mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-09-09 04:02:35 +00:00
110 lines
4.0 KiB
TypeScript
110 lines
4.0 KiB
TypeScript
/** Prepare the disposable npm-project view used by an unpackaged Electron shell. */
|
|
|
|
import {
|
|
existsSync,
|
|
lstatSync,
|
|
mkdirSync,
|
|
readFileSync,
|
|
readdirSync,
|
|
realpathSync,
|
|
rmSync,
|
|
symlinkSync,
|
|
unlinkSync,
|
|
} from 'node:fs'
|
|
import { dirname, join } from 'node:path'
|
|
import { createDevelopmentProjectMetadata } from '../src/project-manager.ts'
|
|
import type { DesktopRelease } from '../src/release.ts'
|
|
|
|
interface PackageManifest {
|
|
readonly name?: string
|
|
readonly version?: string
|
|
}
|
|
|
|
/** Inputs whose locations differ between the launcher and isolated tests. */
|
|
export interface DevelopmentProjectOptions {
|
|
/** Directory replaced with the generated development project. */
|
|
readonly projectDir: string
|
|
/** Current workspace's `apps/cli` package directory. */
|
|
readonly cliDir: string
|
|
/** pnpm's workspace-wide virtual-hoist directory. */
|
|
readonly dependencyDir: string
|
|
/** Release identity written into the disposable project metadata. */
|
|
readonly release: DesktopRelease
|
|
}
|
|
|
|
function readManifest(path: string): PackageManifest {
|
|
return JSON.parse(readFileSync(path, 'utf8')) as PackageManifest
|
|
}
|
|
|
|
function removeOwnedPath(path: string): void {
|
|
let stat: ReturnType<typeof lstatSync>
|
|
try {
|
|
stat = lstatSync(path)
|
|
} catch (error) {
|
|
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return
|
|
throw error
|
|
}
|
|
if (stat.isSymbolicLink()) {
|
|
unlinkSync(path)
|
|
return
|
|
}
|
|
if (stat.isDirectory()) {
|
|
rmSync(path, { recursive: true })
|
|
return
|
|
}
|
|
unlinkSync(path)
|
|
}
|
|
|
|
function linkDirectory(source: string, destination: string): void {
|
|
mkdirSync(dirname(destination), { recursive: true })
|
|
symlinkSync(realpathSync(source), destination, process.platform === 'win32' ? 'junction' : 'dir')
|
|
}
|
|
|
|
function mirrorDependencyLinks(sourceRoot: string, destinationRoot: string): void {
|
|
for (const entry of readdirSync(sourceRoot, { withFileTypes: true })) {
|
|
if (entry.name === '.bin') continue
|
|
const source = join(sourceRoot, entry.name)
|
|
if (entry.name.startsWith('@') && (entry.isDirectory() || entry.isSymbolicLink())) {
|
|
mkdirSync(join(destinationRoot, entry.name), { recursive: true })
|
|
for (const scoped of readdirSync(source, { withFileTypes: true })) {
|
|
if (!scoped.isDirectory() && !scoped.isSymbolicLink()) continue
|
|
linkDirectory(join(source, scoped.name), join(destinationRoot, entry.name, scoped.name))
|
|
}
|
|
continue
|
|
}
|
|
if (entry.isDirectory() || entry.isSymbolicLink()) linkDirectory(source, join(destinationRoot, entry.name))
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Replace one disposable project with links to the current built workspace.
|
|
* @param options - Project destination, CLI package, and release identity.
|
|
* @returns the absolute project directory supplied by the caller.
|
|
*/
|
|
export function prepareDevelopmentProject(options: DevelopmentProjectOptions): string {
|
|
const cliManifest = readManifest(join(options.cliDir, 'package.json'))
|
|
if (cliManifest.name !== '@deepseek-ai/dsh' || cliManifest.version !== options.release.version) {
|
|
throw new Error(
|
|
`desktop development: apps/cli must be @deepseek-ai/dsh@${options.release.version}, found `
|
|
+ `${String(cliManifest.name)}@${String(cliManifest.version)}`,
|
|
)
|
|
}
|
|
if (!existsSync(options.dependencyDir)) {
|
|
throw new Error('desktop development: workspace dependency links are missing; run pnpm install')
|
|
}
|
|
const desktopHost = join(options.cliDir, 'lib', 'desktop-host.js')
|
|
if (!existsSync(desktopHost)) {
|
|
throw new Error('desktop development: apps/cli/lib/desktop-host.js is missing; run pnpm run build')
|
|
}
|
|
|
|
removeOwnedPath(options.projectDir)
|
|
createDevelopmentProjectMetadata(options.projectDir, options.release)
|
|
const destinationModules = join(options.projectDir, 'node_modules')
|
|
mkdirSync(destinationModules, { recursive: true })
|
|
mirrorDependencyLinks(options.dependencyDir, destinationModules)
|
|
const dshLink = join(destinationModules, '@deepseek-ai', 'dsh')
|
|
removeOwnedPath(dshLink)
|
|
linkDirectory(options.cliDir, dshLink)
|
|
return options.projectDir
|
|
}
|