mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-09-09 04:02:35 +00:00
115 lines
4.9 KiB
TypeScript
115 lines
4.9 KiB
TypeScript
/** Build and launch the unpackaged Electron shell against the current workspace. */
|
|
|
|
import { spawn } from 'node:child_process'
|
|
import { existsSync, readFileSync } from 'node:fs'
|
|
import { createRequire } from 'node:module'
|
|
import { join, resolve } from 'node:path'
|
|
import { parseArgs } from 'node:util'
|
|
import { DESKTOP_HOST_PROTOCOL_VERSION } from '../src/host-protocol.ts'
|
|
import type { DesktopRelease } from '../src/release.ts'
|
|
import { prepareDevelopmentProject } from './development-project.ts'
|
|
|
|
const APP_ROOT = resolve(import.meta.dirname, '..')
|
|
const REPOSITORY_ROOT = resolve(APP_ROOT, '..', '..')
|
|
const BUILD_ROOT = join(APP_ROOT, '.desktop-build')
|
|
const DEVELOPMENT_ROOT = join(BUILD_ROOT, 'development')
|
|
|
|
interface PackageManifest {
|
|
readonly version?: string
|
|
}
|
|
|
|
function packageVersion(path: string, subject: string): string {
|
|
const manifest = JSON.parse(readFileSync(path, 'utf8')) as PackageManifest
|
|
if (typeof manifest.version !== 'string') throw new Error(`desktop development: ${subject} has no version`)
|
|
return manifest.version
|
|
}
|
|
|
|
function debugPort(name: string, fallback: number): number {
|
|
const value = process.env[name]
|
|
if (value === undefined || value === '') return fallback
|
|
const port = Number(value)
|
|
if (!Number.isSafeInteger(port) || port < 1 || port > 65_535) {
|
|
throw new Error(`desktop development: ${name} must be an integer from 1 through 65535`)
|
|
}
|
|
return port
|
|
}
|
|
|
|
async function run(command: string, args: readonly string[], cwd: string, environment = process.env): Promise<void> {
|
|
await new Promise<void>((resolvePromise, reject) => {
|
|
const child = spawn(command, args, { cwd, env: environment, stdio: 'inherit' })
|
|
child.once('error', reject)
|
|
child.once('exit', (code, signal) => {
|
|
if (code === 0) resolvePromise()
|
|
else reject(new Error(`desktop development: ${args.join(' ')} exited with ${String(code ?? signal)}`))
|
|
})
|
|
})
|
|
}
|
|
|
|
async function runPackageScript(script: string, cwd: string): Promise<void> {
|
|
const packageManager = process.env.npm_execpath
|
|
if (packageManager === undefined || packageManager === '') {
|
|
throw new Error('desktop development: invoke this launcher through pnpm run dev:desktop or start:desktop')
|
|
}
|
|
await run(process.execPath, [packageManager, 'run', script], cwd)
|
|
}
|
|
|
|
async function launchElectron(projectDir: string): Promise<void> {
|
|
const require = createRequire(import.meta.url)
|
|
const electron: unknown = require('electron')
|
|
if (typeof electron !== 'string') throw new Error('desktop development: electron executable is unavailable')
|
|
const mainPort = debugPort('DSH_DESKTOP_MAIN_INSPECT_PORT', 9229)
|
|
const rendererPort = debugPort('DSH_DESKTOP_RENDERER_DEBUG_PORT', 9222)
|
|
const hostPort = debugPort('DSH_DESKTOP_HOST_INSPECT_PORT', 9230)
|
|
const home = resolve(process.env.DSH_HOME ?? join(DEVELOPMENT_ROOT, 'home'))
|
|
const userData = join(DEVELOPMENT_ROOT, 'electron-user-data')
|
|
const environment: NodeJS.ProcessEnv = {
|
|
...process.env,
|
|
DSH_HOME: home,
|
|
DSH_DESKTOP_DEV_PROJECT_DIR: projectDir,
|
|
DSH_DESKTOP_HOST_INSPECT_PORT: String(hostPort),
|
|
DSH_DESKTOP_NODE_BINARY: process.execPath,
|
|
DSH_DESKTOP_OPEN_DEVTOOLS: process.env.DSH_DESKTOP_OPEN_DEVTOOLS ?? '1',
|
|
ELECTRON_ENABLE_LOGGING: process.env.ELECTRON_ENABLE_LOGGING ?? '1',
|
|
}
|
|
console.log(`desktop development: DSH_HOME=${home}`)
|
|
console.log(`desktop development: inspectors main=${String(mainPort)}, renderer=${String(rendererPort)}, host=${String(hostPort)}`)
|
|
await run(electron, [
|
|
`--inspect=127.0.0.1:${String(mainPort)}`,
|
|
`--remote-debugging-port=${String(rendererPort)}`,
|
|
`--user-data-dir=${userData}`,
|
|
APP_ROOT,
|
|
], APP_ROOT, environment)
|
|
}
|
|
|
|
async function main(): Promise<void> {
|
|
const { values } = parseArgs({ options: { 'skip-build': { type: 'boolean', default: false } } })
|
|
if (!values['skip-build']) {
|
|
await runPackageScript('build', REPOSITORY_ROOT)
|
|
await runPackageScript('build', APP_ROOT)
|
|
}
|
|
for (const path of [join(APP_ROOT, 'lib', 'main.js'), join(REPOSITORY_ROOT, 'apps', 'cli', 'lib', 'desktop-host.js')]) {
|
|
if (!existsSync(path)) throw new Error(`desktop development: missing built artifact ${path}`)
|
|
}
|
|
const version = packageVersion(join(APP_ROOT, 'package.json'), 'desktop package')
|
|
const pnpmVersion = packageVersion(join(APP_ROOT, 'node_modules', 'pnpm', 'package.json'), 'pnpm package')
|
|
const release: DesktopRelease = {
|
|
schemaVersion: 1,
|
|
version,
|
|
hostProtocolVersion: DESKTOP_HOST_PROTOCOL_VERSION,
|
|
nodeVersion: process.versions.node,
|
|
pnpmVersion,
|
|
}
|
|
const projectDir = prepareDevelopmentProject({
|
|
projectDir: join(DEVELOPMENT_ROOT, 'project'),
|
|
cliDir: join(REPOSITORY_ROOT, 'apps', 'cli'),
|
|
dependencyDir: join(REPOSITORY_ROOT, 'node_modules', '.pnpm', 'node_modules'),
|
|
release,
|
|
})
|
|
await launchElectron(projectDir)
|
|
}
|
|
|
|
main().catch((error: unknown) => {
|
|
console.error(error instanceof Error ? error.message : error)
|
|
process.exitCode = 1
|
|
})
|