feat: add DeepSeek Harness runtime
This commit is contained in:
@@ -24,6 +24,11 @@ const {
|
||||
sep
|
||||
} = require('node:path')
|
||||
const { finished } = require('node:stream/promises')
|
||||
const {
|
||||
extractFile,
|
||||
listPackage,
|
||||
statFile
|
||||
} = require('@electron/asar')
|
||||
const { Zip, ZipDeflate } = require('fflate')
|
||||
const { sha256File } = require('./file-hash.cjs')
|
||||
|
||||
@@ -35,6 +40,23 @@ const productName = packageJson.build?.productName ?? packageJson.name
|
||||
const releaseRoot = join(root, 'dist', 'release')
|
||||
const manifestName = 'release-manifest.json'
|
||||
const portableMarkerName = '.goodbuddy-portable.json'
|
||||
const harnessHostEntry =
|
||||
'out/main/deepseek-harness-host-bootstrap.js'
|
||||
const harnessBundleManifest = 'out/main/package.json'
|
||||
const harnessPackageVersions = {
|
||||
'@deepseek-ai/dsh-agent': '0.1.0-rc.6',
|
||||
'@deepseek-ai/dsh-sandbox-windows-acl': '0.1.0-rc.6',
|
||||
'@deepseek-ai/node-addon-landlock-run': '0.1.1',
|
||||
'node-pty': '1.1.0'
|
||||
}
|
||||
const koffiVersion = '3.1.4'
|
||||
const harnessLicenseFiles = [
|
||||
'agent-client-protocol-Apache-2.0.txt',
|
||||
'deepseek-cordis-MIT.txt',
|
||||
'deepseek-harness-MIT.txt',
|
||||
'koffi-MIT.txt',
|
||||
'node-pty-MIT.txt'
|
||||
]
|
||||
const portableRequiredFiles = [
|
||||
`${productName}.exe`,
|
||||
'resources/app.asar',
|
||||
@@ -359,6 +381,326 @@ function assertFile(filePath, description) {
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeAsarEntry(filePath) {
|
||||
return filePath.split('/').join(sep)
|
||||
}
|
||||
|
||||
function asarEntryMetadata(
|
||||
asarPath,
|
||||
entryNames,
|
||||
filePath,
|
||||
description,
|
||||
statAsarFile = statFile
|
||||
) {
|
||||
const entry = normalizeAsarEntry(filePath)
|
||||
if (!entryNames.has(`${sep}${entry}`)) {
|
||||
throw new Error(`${description}缺失:${filePath}`)
|
||||
}
|
||||
return statAsarFile(asarPath, entry)
|
||||
}
|
||||
|
||||
function assertAsarEntry(entryNames, filePath, description) {
|
||||
const entry = normalizeAsarEntry(filePath)
|
||||
if (!entryNames.has(`${sep}${entry}`)) {
|
||||
throw new Error(`${description}缺失:${filePath}`)
|
||||
}
|
||||
}
|
||||
|
||||
function assertBinaryArchitecture(filePath, expected, description) {
|
||||
assertFile(filePath, description)
|
||||
const actual = binaryArchitecture(filePath)
|
||||
if (actual !== expected) {
|
||||
throw new Error(
|
||||
`${description}架构错误:期望 ${expected},实际 ${actual ?? '未知'}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function targetHarnessPaths(options) {
|
||||
const platformName = {
|
||||
windows: 'win32',
|
||||
macos: 'darwin',
|
||||
linux: 'linux'
|
||||
}[options.platform]
|
||||
const koffiPackage = `@koromix/koffi-${platformName}-${options.arch}`
|
||||
const koffiBinary = {
|
||||
windows: `win32_${options.arch}/koffi.node`,
|
||||
macos: `darwin_${options.arch}/koffi.node`,
|
||||
linux: `linux_${options.arch}/koffi.node`
|
||||
}[options.platform]
|
||||
return {
|
||||
koffiPackage,
|
||||
koffiBinary,
|
||||
nodePtyBinary:
|
||||
options.platform === 'linux'
|
||||
? 'build/Release/pty.node'
|
||||
: `prebuilds/${platformName}-${options.arch}/pty.node`,
|
||||
nodePtyDirectory: `${platformName}-${options.arch}`,
|
||||
landlockPackage:
|
||||
options.platform === 'linux'
|
||||
? `@deepseek-ai/node-addon-landlock-run-linux-${options.arch}`
|
||||
: undefined
|
||||
}
|
||||
}
|
||||
|
||||
function verifyHarnessPackage(
|
||||
resources,
|
||||
options,
|
||||
dependencies = {}
|
||||
) {
|
||||
const asarPath = join(resources, 'app.asar')
|
||||
const unpackedRoot = join(resources, 'app.asar.unpacked')
|
||||
const listAsarEntries = dependencies.listPackage ?? listPackage
|
||||
const statAsarFile = dependencies.statFile ?? statFile
|
||||
const extractAsarFile = dependencies.extractFile ?? extractFile
|
||||
const entries = new Set(listAsarEntries(asarPath))
|
||||
const target = targetHarnessPaths(options)
|
||||
|
||||
assertAsarEntry(entries, harnessHostEntry, 'DeepSeek Harness Host')
|
||||
const readJson = (filePath, description) => {
|
||||
const metadata = asarEntryMetadata(
|
||||
asarPath,
|
||||
entries,
|
||||
filePath,
|
||||
description,
|
||||
statAsarFile
|
||||
)
|
||||
if ('files' in metadata || 'link' in metadata) {
|
||||
throw new Error(`${description}类型错误:${filePath}`)
|
||||
}
|
||||
return JSON.parse(
|
||||
extractAsarFile(asarPath, normalizeAsarEntry(filePath))
|
||||
)
|
||||
}
|
||||
const bundleManifest = readJson(
|
||||
harnessBundleManifest,
|
||||
'DeepSeek Harness bundle 元数据'
|
||||
)
|
||||
if (
|
||||
bundleManifest.name !== '@deepseek-ai/dsh-llm' ||
|
||||
bundleManifest.version !==
|
||||
harnessPackageVersions['@deepseek-ai/dsh-agent']
|
||||
) {
|
||||
throw new Error('DeepSeek Harness bundle 元数据错误')
|
||||
}
|
||||
assertFile(
|
||||
join(unpackedRoot, ...harnessBundleManifest.split('/')),
|
||||
'DeepSeek Harness 可执行 bundle 元数据'
|
||||
)
|
||||
assertFile(
|
||||
join(unpackedRoot, ...harnessHostEntry.split('/')),
|
||||
'DeepSeek Harness 可执行 Host'
|
||||
)
|
||||
const harnessLlmChunk = [...entries]
|
||||
.map((entry) => entry.slice(1).split(sep).join('/'))
|
||||
.find((entry) =>
|
||||
/^out\/main\/chunks\/deepseek-harness-llm-[^/]+\.js$/u.test(
|
||||
entry
|
||||
)
|
||||
)
|
||||
if (!harnessLlmChunk) {
|
||||
throw new Error('DeepSeek Harness LLM chunk缺失')
|
||||
}
|
||||
const harnessLlmSource = extractAsarFile(
|
||||
asarPath,
|
||||
normalizeAsarEntry(harnessLlmChunk)
|
||||
).toString('utf8')
|
||||
const requiredChunkNames = new Set([
|
||||
...[
|
||||
...harnessLlmSource.matchAll(
|
||||
/import\(["']\.\/([^/"']+\.js)["']\)/gu
|
||||
)
|
||||
].map((match) => match[1]),
|
||||
...[...entries]
|
||||
.map((entry) => entry.slice(1).split(sep).join('/'))
|
||||
.filter((entry) =>
|
||||
/^out\/main\/chunks\/[^/]+\.js$/u.test(entry)
|
||||
)
|
||||
.map((entry) => entry.slice('out/main/chunks/'.length))
|
||||
])
|
||||
if (requiredChunkNames.size === 0) {
|
||||
throw new Error('DeepSeek Harness LLM lazy chunk closure缺失')
|
||||
}
|
||||
for (const chunkName of requiredChunkNames) {
|
||||
const chunkPath = `out/main/chunks/${chunkName}`
|
||||
const metadata = asarEntryMetadata(
|
||||
asarPath,
|
||||
entries,
|
||||
chunkPath,
|
||||
'DeepSeek Harness module chunk',
|
||||
statAsarFile
|
||||
)
|
||||
if (!('unpacked' in metadata) || !metadata.unpacked) {
|
||||
throw new Error(
|
||||
`DeepSeek Harness module chunk未从 ASAR 解包:${chunkPath}`
|
||||
)
|
||||
}
|
||||
assertFile(
|
||||
join(unpackedRoot, ...chunkPath.split('/')),
|
||||
'DeepSeek Harness 可执行 module chunk'
|
||||
)
|
||||
}
|
||||
for (const [packageName, expectedVersion] of Object.entries(
|
||||
harnessPackageVersions
|
||||
)) {
|
||||
const manifest = readJson(
|
||||
`node_modules/${packageName}/package.json`,
|
||||
`${packageName} 元数据`
|
||||
)
|
||||
if (manifest.version !== expectedVersion) {
|
||||
throw new Error(
|
||||
`${packageName} 版本错误:期望 ${expectedVersion},实际 ${String(manifest.version)}`
|
||||
)
|
||||
}
|
||||
}
|
||||
const targetKoffiManifest = readJson(
|
||||
`node_modules/${target.koffiPackage}/package.json`,
|
||||
`${target.koffiPackage} 元数据`
|
||||
)
|
||||
if (targetKoffiManifest.version !== koffiVersion) {
|
||||
throw new Error(
|
||||
`${target.koffiPackage} 版本错误:期望 ${koffiVersion},实际 ${String(targetKoffiManifest.version)}`
|
||||
)
|
||||
}
|
||||
|
||||
const ptyBinary = join(
|
||||
unpackedRoot,
|
||||
'node_modules',
|
||||
'node-pty',
|
||||
...target.nodePtyBinary.split('/')
|
||||
)
|
||||
const koffiBinary = join(
|
||||
unpackedRoot,
|
||||
'node_modules',
|
||||
...target.koffiPackage.split('/'),
|
||||
...target.koffiBinary.split('/')
|
||||
)
|
||||
assertBinaryArchitecture(
|
||||
ptyBinary,
|
||||
options.arch,
|
||||
'DeepSeek Harness node-pty'
|
||||
)
|
||||
const nodePtyMetadata = asarEntryMetadata(
|
||||
asarPath,
|
||||
entries,
|
||||
`node_modules/node-pty/${target.nodePtyBinary}`,
|
||||
'DeepSeek Harness node-pty 元数据',
|
||||
statAsarFile
|
||||
)
|
||||
const koffiMetadata = asarEntryMetadata(
|
||||
asarPath,
|
||||
entries,
|
||||
`node_modules/${target.koffiPackage}/${target.koffiBinary}`,
|
||||
'DeepSeek Harness Koffi 元数据',
|
||||
statAsarFile
|
||||
)
|
||||
for (const [metadata, description] of [
|
||||
[nodePtyMetadata, 'DeepSeek Harness node-pty'],
|
||||
[koffiMetadata, 'DeepSeek Harness Koffi']
|
||||
]) {
|
||||
if (!('unpacked' in metadata) || !metadata.unpacked) {
|
||||
throw new Error(`${description}未从 ASAR 解包`)
|
||||
}
|
||||
}
|
||||
assertBinaryArchitecture(
|
||||
koffiBinary,
|
||||
options.arch,
|
||||
'DeepSeek Harness Koffi'
|
||||
)
|
||||
|
||||
if (options.platform === 'darwin') {
|
||||
const helper = join(
|
||||
unpackedRoot,
|
||||
'node_modules',
|
||||
'node-pty',
|
||||
'prebuilds',
|
||||
target.nodePtyDirectory,
|
||||
'spawn-helper'
|
||||
)
|
||||
assertFile(helper, 'DeepSeek Harness node-pty spawn-helper')
|
||||
if ((statSync(helper).mode & 0o111) === 0) {
|
||||
throw new Error(
|
||||
`DeepSeek Harness node-pty spawn-helper 不可执行:${helper}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (target.landlockPackage) {
|
||||
const targetLandlockManifest = readJson(
|
||||
`node_modules/${target.landlockPackage}/package.json`,
|
||||
`${target.landlockPackage} 元数据`
|
||||
)
|
||||
if (
|
||||
targetLandlockManifest.version !==
|
||||
harnessPackageVersions[
|
||||
'@deepseek-ai/node-addon-landlock-run'
|
||||
]
|
||||
) {
|
||||
throw new Error(
|
||||
`${target.landlockPackage} 版本错误:期望 ${harnessPackageVersions['@deepseek-ai/node-addon-landlock-run']},实际 ${String(targetLandlockManifest.version)}`
|
||||
)
|
||||
}
|
||||
const launcher = join(
|
||||
unpackedRoot,
|
||||
'node_modules',
|
||||
...target.landlockPackage.split('/'),
|
||||
'bin',
|
||||
'landlock-run'
|
||||
)
|
||||
assertBinaryArchitecture(
|
||||
launcher,
|
||||
options.arch,
|
||||
'DeepSeek Harness Landlock launcher'
|
||||
)
|
||||
const launcherMetadata = asarEntryMetadata(
|
||||
asarPath,
|
||||
entries,
|
||||
`node_modules/${target.landlockPackage}/bin/landlock-run`,
|
||||
'DeepSeek Harness Landlock launcher 元数据',
|
||||
statAsarFile
|
||||
)
|
||||
if (
|
||||
!('unpacked' in launcherMetadata) ||
|
||||
!launcherMetadata.unpacked
|
||||
) {
|
||||
throw new Error(
|
||||
'DeepSeek Harness Landlock launcher 未从 ASAR 解包'
|
||||
)
|
||||
}
|
||||
if ((statSync(launcher).mode & 0o111) === 0) {
|
||||
throw new Error(
|
||||
`DeepSeek Harness Landlock launcher 不可执行:${launcher}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (options.platform === 'windows') {
|
||||
assertAsarEntry(
|
||||
entries,
|
||||
'node_modules/@deepseek-ai/dsh-sandbox-windows-acl/lib/runner.js',
|
||||
'DeepSeek Harness Windows ACL runner'
|
||||
)
|
||||
assertFile(
|
||||
join(
|
||||
unpackedRoot,
|
||||
'node_modules',
|
||||
'@deepseek-ai',
|
||||
'dsh-sandbox-windows-acl',
|
||||
'lib',
|
||||
'runner.js'
|
||||
),
|
||||
'DeepSeek Harness 可执行 Windows ACL runner'
|
||||
)
|
||||
}
|
||||
|
||||
for (const license of harnessLicenseFiles) {
|
||||
assertFile(
|
||||
join(resources, 'licenses', license),
|
||||
'DeepSeek Harness 许可证'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function verifyUnpackedOutput(directory, options) {
|
||||
const definition = platformDefinitions[options.platform]
|
||||
const unpackedDirectory = findUnpackedDirectory(
|
||||
@@ -387,6 +729,7 @@ function verifyUnpackedOutput(directory, options) {
|
||||
join(resources, 'runtimes', 'continue', 'dist', 'index.js'),
|
||||
'Continue Runtime'
|
||||
)
|
||||
verifyHarnessPackage(resources, options)
|
||||
for (const [filePath, label] of [
|
||||
[applicationExecutable, '应用主程序'],
|
||||
[runtimeExecutable, 'OpenCode Runtime']
|
||||
@@ -992,6 +1335,8 @@ module.exports = {
|
||||
parseArguments,
|
||||
platformDefinitions,
|
||||
replaceOutput,
|
||||
verifyHarnessPackage,
|
||||
verifyUnpackedOutput,
|
||||
verifyArtifacts,
|
||||
verifyArtifactSignature,
|
||||
verifyPortableZip,
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
'use strict'
|
||||
|
||||
const {
|
||||
mkdirSync,
|
||||
mkdtempSync
|
||||
} = require('node:fs')
|
||||
const {
|
||||
rm,
|
||||
writeFile
|
||||
} = require('node:fs/promises')
|
||||
const { tmpdir } = require('node:os')
|
||||
const {
|
||||
isAbsolute,
|
||||
join,
|
||||
resolve
|
||||
} = require('node:path')
|
||||
const { app, utilityProcess } = require('electron/main')
|
||||
|
||||
const protocol = 'goodbuddy.deepseek-harness.control'
|
||||
const version = 1
|
||||
const byteProtocol = 'goodbuddy.deepseek-harness.byte-stream'
|
||||
const configuredHostPath =
|
||||
process.env.GOODBUDDY_HARNESS_SMOKE_HOST
|
||||
const hostPath = configuredHostPath
|
||||
? isAbsolute(configuredHostPath)
|
||||
? configuredHostPath
|
||||
: resolve(configuredHostPath)
|
||||
: resolve('out/main/deepseek-harness-host-bootstrap.js')
|
||||
const workspace = mkdtempSync(
|
||||
join(tmpdir(), 'goodbuddy-harness-electron-smoke-')
|
||||
)
|
||||
const dshHome = join(workspace, 'dsh-home')
|
||||
mkdirSync(dshHome)
|
||||
const configuredResultPath =
|
||||
process.env.GOODBUDDY_HARNESS_SMOKE_RESULT
|
||||
const resultPath =
|
||||
configuredResultPath && isAbsolute(configuredResultPath)
|
||||
? configuredResultPath
|
||||
: join(
|
||||
tmpdir(),
|
||||
`goodbuddy-harness-utility-smoke-${process.pid}.json`
|
||||
)
|
||||
|
||||
let child
|
||||
let timeout
|
||||
let stderr = ''
|
||||
let settled = false
|
||||
let transportProbed = false
|
||||
|
||||
void writeFile(
|
||||
resultPath,
|
||||
JSON.stringify({ status: 'checkpoint', stage: 'script-start' }),
|
||||
'utf8'
|
||||
)
|
||||
|
||||
async function checkpoint(stage, detail = '') {
|
||||
await writeFile(
|
||||
resultPath,
|
||||
JSON.stringify({ status: 'checkpoint', stage, detail }),
|
||||
'utf8'
|
||||
)
|
||||
}
|
||||
|
||||
function finish(status, detail = '') {
|
||||
if (settled) {
|
||||
return
|
||||
}
|
||||
settled = true
|
||||
if (timeout) {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
void writeFile(
|
||||
resultPath,
|
||||
JSON.stringify({
|
||||
status,
|
||||
detail: detail.slice(0, 4_096)
|
||||
}),
|
||||
'utf8'
|
||||
)
|
||||
.catch(() => undefined)
|
||||
.finally(() => {
|
||||
child?.kill()
|
||||
void rm(workspace, {
|
||||
recursive: true,
|
||||
force: true,
|
||||
maxRetries: 5,
|
||||
retryDelay: 100
|
||||
})
|
||||
.catch(() => undefined)
|
||||
.finally(() => {
|
||||
if (!configuredResultPath) {
|
||||
console.log(
|
||||
`GoodBuddy packaged Harness smoke: ${status}`
|
||||
)
|
||||
}
|
||||
app.exit(status === 'ready' ? 0 : 1)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function run() {
|
||||
await checkpoint('module-loaded')
|
||||
await app.whenReady()
|
||||
await checkpoint('app-ready')
|
||||
child = utilityProcess.fork(hostPath, [], {
|
||||
cwd: workspace,
|
||||
env: {
|
||||
PATH: process.env.PATH ?? '',
|
||||
Path: process.env.Path ?? '',
|
||||
PATHEXT: process.env.PATHEXT ?? '',
|
||||
SystemRoot: process.env.SystemRoot ?? '',
|
||||
COMSPEC: process.env.COMSPEC ?? '',
|
||||
TEMP: process.env.TEMP ?? '',
|
||||
TMP: process.env.TMP ?? '',
|
||||
USERPROFILE: process.env.USERPROFILE ?? '',
|
||||
APPDATA: process.env.APPDATA ?? '',
|
||||
LOCALAPPDATA: process.env.LOCALAPPDATA ?? '',
|
||||
DSH_HOME: dshHome,
|
||||
DSH_TELEMETRY_DISABLED: '1',
|
||||
OTEL_SDK_DISABLED: 'true'
|
||||
},
|
||||
serviceName: 'GoodBuddy DeepSeek Harness Smoke',
|
||||
stdio: ['ignore', 'ignore', 'pipe'],
|
||||
allowLoadingUnsignedLibraries: false,
|
||||
disclaim: false
|
||||
})
|
||||
await checkpoint('utility-forked', String(child.pid ?? ''))
|
||||
|
||||
child.stderr?.on('data', (chunk) => {
|
||||
stderr = (stderr + String(chunk)).slice(-4_096)
|
||||
})
|
||||
child.on('message', (message) => {
|
||||
if (
|
||||
message?.protocol === protocol &&
|
||||
message.version === version &&
|
||||
message.type === 'ready'
|
||||
) {
|
||||
child.postMessage({
|
||||
protocol: byteProtocol,
|
||||
version,
|
||||
type: 'data',
|
||||
stream: 'stdin',
|
||||
seq: 0,
|
||||
bytes: Buffer.from('{}\n')
|
||||
})
|
||||
return
|
||||
}
|
||||
if (
|
||||
message?.protocol === byteProtocol &&
|
||||
message.version === version &&
|
||||
message.type === 'ack' &&
|
||||
message.stream === 'stdin' &&
|
||||
message.seq === 0
|
||||
) {
|
||||
transportProbed = true
|
||||
finish('ready')
|
||||
return
|
||||
}
|
||||
if (
|
||||
message?.protocol === protocol &&
|
||||
message.version === version &&
|
||||
message.type === 'fatal'
|
||||
) {
|
||||
finish('fatal', String(message.code))
|
||||
}
|
||||
})
|
||||
child.on('exit', (code) => {
|
||||
finish(
|
||||
'exit',
|
||||
`${code}:${stderr.replaceAll(/\s+/gu, ' ').trim()}`
|
||||
)
|
||||
})
|
||||
child.postMessage({
|
||||
protocol,
|
||||
version,
|
||||
type: 'start',
|
||||
config: {
|
||||
workspace,
|
||||
dshHome,
|
||||
baseUrl: 'https://api.deepseek.com',
|
||||
api: 'openai-completions',
|
||||
provider: 'goodbuddy',
|
||||
model: 'deepseek-chat',
|
||||
harnessVersion: '0.1.0-rc.6',
|
||||
sandbox: {
|
||||
provider:
|
||||
process.platform === 'win32'
|
||||
? 'windows-acl'
|
||||
: process.platform === 'darwin'
|
||||
? 'seatbelt'
|
||||
: 'local-linux',
|
||||
enforcement:
|
||||
process.platform === 'win32' ? 'partial' : 'full'
|
||||
},
|
||||
credentialRefs: ['GOODBUDDY_DEEPSEEK_API_KEY'],
|
||||
skillPackages: [],
|
||||
maxFrameBytes: 1024 * 1024
|
||||
}
|
||||
})
|
||||
|
||||
timeout = setTimeout(() => {
|
||||
finish(
|
||||
'timeout',
|
||||
`${transportProbed ? 'transport-probed ' : ''}${stderr.replaceAll(/\s+/gu, ' ').trim()}`
|
||||
)
|
||||
}, 20_000)
|
||||
}
|
||||
|
||||
void run().catch((error) => {
|
||||
finish(
|
||||
'bootstrap-error',
|
||||
error instanceof Error ? error.message : 'unknown error'
|
||||
)
|
||||
})
|
||||
@@ -0,0 +1,149 @@
|
||||
'use strict'
|
||||
|
||||
const { spawn } = require('node:child_process')
|
||||
const {
|
||||
readFile,
|
||||
rm,
|
||||
writeFile
|
||||
} = require('node:fs/promises')
|
||||
const { tmpdir } = require('node:os')
|
||||
const { join, resolve } = require('node:path')
|
||||
|
||||
const electronPath = process.env.GOODBUDDY_HARNESS_SMOKE_ELECTRON
|
||||
? resolve(process.env.GOODBUDDY_HARNESS_SMOKE_ELECTRON)
|
||||
: require('electron')
|
||||
const configuredAppPath =
|
||||
process.env.GOODBUDDY_HARNESS_SMOKE_APP
|
||||
const appPath = configuredAppPath
|
||||
? resolve(configuredAppPath)
|
||||
: resolve('build/smoke-app')
|
||||
const temporaryAppPath =
|
||||
configuredAppPath ||
|
||||
process.env.GOODBUDDY_HARNESS_SMOKE_ELECTRON
|
||||
? undefined
|
||||
: join(
|
||||
tmpdir(),
|
||||
`goodbuddy-harness-smoke-app-${process.pid}`
|
||||
)
|
||||
const resultPath = join(
|
||||
tmpdir(),
|
||||
`goodbuddy-harness-utility-smoke-result-${process.pid}.json`
|
||||
)
|
||||
const profilePath = join(
|
||||
tmpdir(),
|
||||
`goodbuddy-harness-utility-smoke-profile-${process.pid}`
|
||||
)
|
||||
const environment = {
|
||||
...process.env,
|
||||
GOODBUDDY_HARNESS_SMOKE_RESULT: resultPath
|
||||
}
|
||||
delete environment.ELECTRON_RUN_AS_NODE
|
||||
|
||||
function runElectron(applicationPath) {
|
||||
return new Promise((resolveRun, rejectRun) => {
|
||||
const child = spawn(
|
||||
electronPath,
|
||||
[
|
||||
applicationPath,
|
||||
'--no-sandbox',
|
||||
`--user-data-dir=${profilePath}`,
|
||||
'--no-first-run'
|
||||
],
|
||||
{
|
||||
cwd: resolve('.'),
|
||||
env: environment,
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
windowsHide: true
|
||||
}
|
||||
)
|
||||
let output = ''
|
||||
const capture = (chunk) => {
|
||||
output = (output + String(chunk)).slice(-8_192)
|
||||
}
|
||||
child.stdout.on('data', capture)
|
||||
child.stderr.on('data', capture)
|
||||
const timeout = setTimeout(() => {
|
||||
child.kill()
|
||||
rejectRun(
|
||||
new Error(
|
||||
`DeepSeek Harness Electron smoke timed out: ${output.trim()}`
|
||||
)
|
||||
)
|
||||
}, 30_000)
|
||||
child.once('error', (error) => {
|
||||
clearTimeout(timeout)
|
||||
rejectRun(error)
|
||||
})
|
||||
child.once('exit', (code, signal) => {
|
||||
clearTimeout(timeout)
|
||||
resolveRun({ code, signal, output })
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await rm(resultPath, { force: true })
|
||||
await writeFile(
|
||||
resolve('out/main/package.json'),
|
||||
`${JSON.stringify(
|
||||
{
|
||||
name: '@deepseek-ai/dsh-llm',
|
||||
version: '0.1.0-rc.6',
|
||||
private: true,
|
||||
type: 'module'
|
||||
},
|
||||
null,
|
||||
2
|
||||
)}\n`,
|
||||
'utf8'
|
||||
)
|
||||
if (temporaryAppPath) {
|
||||
await rm(temporaryAppPath, {
|
||||
recursive: true,
|
||||
force: true
|
||||
})
|
||||
const { cp, copyFile, mkdir } = require('node:fs/promises')
|
||||
await mkdir(temporaryAppPath, { recursive: true })
|
||||
await cp(resolve('build/smoke-app'), temporaryAppPath, {
|
||||
recursive: true
|
||||
})
|
||||
await copyFile(
|
||||
resolve('build/deepseek-harness-utility-smoke.cjs'),
|
||||
join(temporaryAppPath, 'deepseek-harness-utility-smoke.cjs')
|
||||
)
|
||||
}
|
||||
const execution = await runElectron(
|
||||
temporaryAppPath ?? appPath
|
||||
)
|
||||
let result
|
||||
try {
|
||||
result = JSON.parse(await readFile(resultPath, 'utf8'))
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`DeepSeek Harness Electron smoke produced no valid result (code ${execution.code}, signal ${execution.signal ?? 'none'}): ${execution.output.trim()}`,
|
||||
{ cause: error }
|
||||
)
|
||||
} finally {
|
||||
await Promise.all([
|
||||
rm(resultPath, { force: true }),
|
||||
rm(profilePath, { recursive: true, force: true }),
|
||||
temporaryAppPath
|
||||
? rm(temporaryAppPath, {
|
||||
recursive: true,
|
||||
force: true
|
||||
})
|
||||
: Promise.resolve()
|
||||
])
|
||||
}
|
||||
if (execution.code !== 0 || result.status !== 'ready') {
|
||||
throw new Error(
|
||||
`DeepSeek Harness Electron smoke failed (code ${execution.code}, status ${String(result.status)}): ${String(result.detail ?? execution.output).trim()}`
|
||||
)
|
||||
}
|
||||
console.log('DeepSeek Harness Electron utility smoke: ready')
|
||||
}
|
||||
|
||||
void main().catch((error) => {
|
||||
console.error(error instanceof Error ? error.message : error)
|
||||
process.exitCode = 1
|
||||
})
|
||||
@@ -0,0 +1,165 @@
|
||||
'use strict'
|
||||
|
||||
const { spawn } = require('node:child_process')
|
||||
const {
|
||||
copyFile,
|
||||
mkdir,
|
||||
mkdtemp,
|
||||
readFile,
|
||||
rm,
|
||||
writeFile
|
||||
} = require('node:fs/promises')
|
||||
const { statSync } = require('node:fs')
|
||||
const { tmpdir } = require('node:os')
|
||||
const { join, resolve } = require('node:path')
|
||||
|
||||
const unpackedPath = process.argv[2]
|
||||
? resolve(process.argv[2])
|
||||
: resolve('dist/harness-package-probe/win-unpacked')
|
||||
const executable = join(
|
||||
unpackedPath,
|
||||
process.platform === 'win32' ? 'GoodBuddy.exe' : 'goodbuddy'
|
||||
)
|
||||
const host = join(
|
||||
unpackedPath,
|
||||
'resources',
|
||||
'app.asar.unpacked',
|
||||
'out',
|
||||
'main',
|
||||
'deepseek-harness-host-bootstrap.js'
|
||||
)
|
||||
|
||||
for (const [path, description] of [
|
||||
[executable, 'packaged Electron executable'],
|
||||
[host, 'packaged DeepSeek Harness host']
|
||||
]) {
|
||||
if (!statSync(path, { throwIfNoEntry: false })?.isFile()) {
|
||||
throw new Error(`${description} is missing: ${path}`)
|
||||
}
|
||||
}
|
||||
|
||||
function run(command, args, env) {
|
||||
return new Promise((resolveExit, rejectExit) => {
|
||||
const child = spawn(command, args, {
|
||||
cwd: resolve('.'),
|
||||
env,
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
windowsHide: true
|
||||
})
|
||||
let output = ''
|
||||
const capture = (chunk) => {
|
||||
output = (output + String(chunk)).slice(-8_192)
|
||||
}
|
||||
child.stdout.on('data', capture)
|
||||
child.stderr.on('data', capture)
|
||||
child.once('error', rejectExit)
|
||||
child.once('exit', (exitCode, signal) => {
|
||||
resolveExit({ exitCode, signal, output })
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const root = await mkdtemp(
|
||||
join(tmpdir(), 'goodbuddy-packaged-harness-smoke-')
|
||||
)
|
||||
try {
|
||||
const project = join(root, 'app')
|
||||
const profile = join(root, 'profile')
|
||||
const resultPath = join(root, 'result.json')
|
||||
await mkdir(project, { recursive: true })
|
||||
|
||||
await copyFile(
|
||||
resolve('build/deepseek-harness-utility-smoke.cjs'),
|
||||
join(project, 'deepseek-harness-utility-smoke.cjs')
|
||||
)
|
||||
await writeFile(
|
||||
join(project, 'package.json'),
|
||||
`${JSON.stringify(
|
||||
{
|
||||
name: 'goodbuddy-packaged-harness-smoke',
|
||||
version: '1.0.0',
|
||||
private: true,
|
||||
main: 'deepseek-harness-utility-smoke.cjs'
|
||||
},
|
||||
null,
|
||||
2
|
||||
)}\n`,
|
||||
'utf8'
|
||||
)
|
||||
await writeFile(
|
||||
join(project, 'electron-builder.yml'),
|
||||
[
|
||||
'appId: live.digiman.goodbuddy.harness-smoke',
|
||||
'productName: GoodBuddyHarnessSmoke',
|
||||
'electronVersion: "43.2.0"',
|
||||
'asar: true',
|
||||
'npmRebuild: false',
|
||||
'files:',
|
||||
' - package.json',
|
||||
' - deepseek-harness-utility-smoke.cjs',
|
||||
'win:',
|
||||
' target:',
|
||||
' - dir'
|
||||
].join('\n'),
|
||||
'utf8'
|
||||
)
|
||||
|
||||
const packageArguments = [
|
||||
resolve('node_modules/electron-builder/cli.js'),
|
||||
'--projectDir',
|
||||
project,
|
||||
'--win',
|
||||
'dir',
|
||||
'--x64',
|
||||
'--publish',
|
||||
'never',
|
||||
`--config.directories.output=${join(root, 'dist')}`
|
||||
]
|
||||
if (process.env.GOODBUDDY_ELECTRON_DIST) {
|
||||
packageArguments.push(
|
||||
`--config.electronDist=${resolve(process.env.GOODBUDDY_ELECTRON_DIST)}`
|
||||
)
|
||||
}
|
||||
const packaged = await run(
|
||||
process.execPath,
|
||||
packageArguments,
|
||||
process.env
|
||||
)
|
||||
if (packaged.exitCode !== 0 || packaged.signal) {
|
||||
throw new Error(
|
||||
`Unable to package Harness smoke app: ${packaged.output.trim()}`
|
||||
)
|
||||
}
|
||||
|
||||
const smokeEnvironment = {
|
||||
...process.env,
|
||||
GOODBUDDY_HARNESS_SMOKE_HOST: host,
|
||||
GOODBUDDY_HARNESS_SMOKE_RESULT: resultPath
|
||||
}
|
||||
delete smokeEnvironment.ELECTRON_RUN_AS_NODE
|
||||
const executed = await run(
|
||||
join(root, 'dist', 'win-unpacked', 'GoodBuddyHarnessSmoke.exe'),
|
||||
[`--user-data-dir=${profile}`, '--no-first-run'],
|
||||
smokeEnvironment
|
||||
)
|
||||
const result = JSON.parse(await readFile(resultPath, 'utf8'))
|
||||
if (
|
||||
executed.exitCode !== 0 ||
|
||||
executed.signal ||
|
||||
result.status !== 'ready'
|
||||
) {
|
||||
throw new Error(
|
||||
`Packaged DeepSeek Harness smoke failed (${executed.exitCode}, ${executed.signal ?? 'no signal'}): ${JSON.stringify(result)} ${executed.output.trim()}`
|
||||
)
|
||||
}
|
||||
console.log('Packaged DeepSeek Harness utility smoke: ready')
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true })
|
||||
}
|
||||
}
|
||||
|
||||
void main().catch((error) => {
|
||||
console.error(error instanceof Error ? error.message : error)
|
||||
process.exitCode = 1
|
||||
})
|
||||
@@ -139,6 +139,24 @@ module.exports = async function prepareBundledRuntimes(context) {
|
||||
architecture === 'x64' ? `${architecture}-baseline` : architecture
|
||||
const packageName = `opencode-${packagePlatform}-${suffix}`
|
||||
const projectDir = context.packager.projectDir
|
||||
const projectPackage = JSON.parse(
|
||||
await readFile(join(projectDir, 'package.json'), 'utf8')
|
||||
)
|
||||
await writeFile(
|
||||
join(projectDir, 'out', 'main', 'package.json'),
|
||||
`${JSON.stringify(
|
||||
{
|
||||
name: '@deepseek-ai/dsh-llm',
|
||||
version:
|
||||
projectPackage.dependencies['@deepseek-ai/dsh-llm'],
|
||||
private: true,
|
||||
type: 'module'
|
||||
},
|
||||
null,
|
||||
2
|
||||
)}\n`,
|
||||
'utf8'
|
||||
)
|
||||
const integrity = await lockedIntegrity(projectDir, packageName)
|
||||
const targetDirectory = join(
|
||||
projectDir,
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "goodbuddy-harness-utility-smoke",
|
||||
"private": true,
|
||||
"main": "deepseek-harness-utility-smoke.cjs"
|
||||
}
|
||||
Reference in New Issue
Block a user