refactor(ptc): 复用 embedded 载体的既有 process.js 启动
This commit is contained in:
1 parent
4ac6a28e68
commit
41ef0fcaa9
10 files changed
+31
-52
No files matched your search
@@ -24,8 +24,8 @@
|
||||
en: 2a9fa3292bf07355
|
||||
zh: db0b5684820915eb
|
||||
/deepseek-ai-dsh-ptc-runtime-node/use-this-package/in-memory-modes:
|
||||
en: 22c1d0a06eaebe80
|
||||
zh: af1c8f0e815906c9
|
||||
en: 41c54a052af70659
|
||||
zh: becb41300547089e
|
||||
/deepseek-ai-dsh-ptc-runtime-node/use-this-package/deadlines-and-cancellation:
|
||||
en: d383ec7aeab8afec
|
||||
zh: 7c9c49e205fbbcd8
|
||||
@@ -39,8 +39,8 @@
|
||||
en: f27d477e3ccb10de
|
||||
zh: 0f14a4899bae6c39
|
||||
/deepseek-ai-dsh-ptc-runtime-node/understand-the-implementation/source-and-built-bootstraps:
|
||||
en: 24a5864c1552c69a
|
||||
zh: a5406693d86d6183
|
||||
en: b944263ce0b1d200
|
||||
zh: c25a99f01a0d9af5
|
||||
/deepseek-ai-dsh-ptc-runtime-node/understand-the-implementation/runner-import-handling:
|
||||
en: e2458b4653a620bb
|
||||
zh: 4ae26bd1e6a69430
|
||||
|
||||
@@ -91,7 +91,7 @@ The `persistent-in-memory` mode and Script mode with `scriptPersistence: true` r
|
||||
executionMode: persistent-in-memory
|
||||
```
|
||||
|
||||
Save the patch and pass its path to `dsh headless --patch <path>` in a composition with PTC tool presentation. The `per-call` mode and Script mode with `scriptPersistence: false` retain no program memory between calls. Changing the configuration reloads the provider and releases its execution resources. Packaged single-executable hosts use their embedded carrier by default; the worker selects `persistent-process.js` for retained execution. Remote script deployments use a `node-script` launch with `launch.bootstrapPath` pointing to this package's built `persistent-process.js`.
|
||||
Save the patch and pass its path to `dsh headless --patch <path>` in a composition with PTC tool presentation. The `per-call` mode and Script mode with `scriptPersistence: false` retain no program memory between calls. Changing the configuration reloads the provider and releases its execution resources. Packaged single-executable hosts use their embedded carrier by default; the host sends the selected program format and retention flag with each evaluation, so the embedded `process.js` bootstrap serves both per-call and retained execution. Remote script deployments use a `node-script` launch with `launch.bootstrapPath` pointing to this package's built `persistent-process.js`.
|
||||
|
||||
In REPL mode, each live Agent keeps its own process and top-level variables, functions, and imported modules across `run_code` calls and turns. Use `await import(...)`, `console.log(...)`, and a final JSON expression; top-level `return` is not supported. Assignments to undeclared names create retained globals under Node REPL’s non-strict evaluation. `_` and `_error` are ordinary retained globals, without interactive REPL result setters. Node REPL declaration rules apply, including errors on redeclaring existing `let`, `const`, or `class` names; assign to a mutable binding or choose a new name. A leading `{ ... }` is a statement block and can contain top-level await and temporary declarations; parenthesize an object expression to return it. Retained functions and saved tool methods call the current evaluation's bindings. All tool calls must be awaited; callbacks from a completed evaluation cannot invoke tools in a later evaluation.
|
||||
|
||||
@@ -137,7 +137,7 @@ Per-call completion leaves unfinished binding waits pending and stops binding ca
|
||||
|
||||
### Source and built bootstraps
|
||||
|
||||
Source execution loads an erasable-only bootstrap closure without relying on sibling built exports; its evaluator modules and installed Acorn and birpc dependencies must be available in the execution world. Built script execution uses `process.js` for per-call programs or `persistent-process.js` for retained execution; both delegate to the same loop with a REPL default for hosts that omit program syntax. Embedded carriers launch their private worker through `launch.executable` and set `DSH_PTC_RUNTIME_NODE=persistent` for retained execution. A remote script deployment supplies the matching `launch.bootstrapPath`. `resolveLaunch()` selects the local default during construction; an explicit remote invocation never depends on whether the client itself is packaged. A host path is never assumed to name the same remote file.
|
||||
Source execution loads an erasable-only bootstrap closure without relying on sibling built exports; its evaluator modules and installed Acorn and birpc dependencies must be available in the execution world. Built script execution uses `process.js` for every mode; `persistent-process.js` delegates to the same loop with a REPL default for hosts that omit program syntax. Embedded carriers launch their private worker through `launch.executable` and carry the program format and retention flag in each evaluation. A remote script deployment supplies the matching `launch.bootstrapPath`. `resolveLaunch()` selects the local default during construction; an explicit remote invocation never depends on whether the client itself is packaged. A host path is never assumed to name the same remote file.
|
||||
|
||||
Source containing `await` is checked for top-level await before native compilation, which can otherwise treat `await` as an identifier. Acorn parses an async wrapper, and acorn-walk traverses it using Node's REPL visitors, declaration hoisting and character-position edits. Computed class-method keys are traversed while method bodies remain separate evaluations. The transform boxes the final expression; the evaluator unwraps and awaits its value. Source that needs no lowering uses native compilation; submissions without `await` retain native syntax diagnostics. Syntax errors preserve the parser message and available source location and caret, without compiler or transformer stack frames. Dynamic `import()` expressions remain native and use the compiling Script's `importModuleDynamically` option. Both private process bundles include Acorn, acorn-walk and birpc; source execution imports the installed packages.
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ profile 编辑器可从 `@deepseek-ai/dsh-ptc-runtime-node/configuration` 导入
|
||||
executionMode: persistent-in-memory
|
||||
```
|
||||
|
||||
保存补丁,在使用 PTC 工具展示的配置中通过 `dsh headless --patch <path>` 加载。`per-call` 模式和 `scriptPersistence: false` 的 Script 模式不跨调用保留程序内存。修改配置会重载提供方并释放其执行资源。单文件打包宿主默认使用内嵌载体,保留模式由 worker 选择 `persistent-process.js`;远端脚本部署使用 `node-script` 启动方式,并将 `launch.bootstrapPath` 指向本包构建出的 `persistent-process.js`。
|
||||
保存补丁,在使用 PTC 工具展示的配置中通过 `dsh headless --patch <path>` 加载。`per-call` 模式和 `scriptPersistence: false` 的 Script 模式不跨调用保留程序内存。修改配置会重载提供方并释放其执行资源。单文件打包宿主默认使用内嵌载体;宿主在每次执行中发送所选的程序格式和保留标志,因此内嵌的 `process.js` bootstrap 同时服务单次执行和保留执行。远端脚本部署使用 `node-script` 启动方式,并将 `launch.bootstrapPath` 指向本包构建出的 `persistent-process.js`。
|
||||
|
||||
在 REPL 模式下,每个存活 Agent 保留独立进程,跨 `run_code` 调用和轮次保留顶层变量、函数及导入模块。使用 `await import(...)`、`console.log(...)` 和末尾 JSON 表达式;不支持顶层 `return`。Node REPL 使用非严格模式,给未声明的名称赋值会创建保留的全局变量。`_` 和 `_error` 是普通的保留全局变量,不使用交互式 REPL 的结果设置器。声明遵循 Node REPL 规则,包括重复声明已有 `let`、`const` 或 `class` 名称时报错;应给可变绑定赋值或选择新名称。开头的 `{ ... }` 按语句块执行,可以包含顶层 await 和临时声明;需要返回对象表达式时,用圆括号包裹。保留的函数和工具方法引用调用当前执行的绑定。必须等待所有工具调用;已完成执行的回调不能在后续执行中调用工具。
|
||||
|
||||
@@ -137,7 +137,7 @@ Host 通过有序控制管道上的 birpc 调用子进程的 `run()`。同一时
|
||||
|
||||
### 源代码与构建后 bootstrap
|
||||
|
||||
源代码执行加载仅含可擦除语法的 bootstrap 依赖,不依赖同级包的构建后导出;执行世界中必须提供求值器模块和已安装的 Acorn 与 birpc 依赖。构建后脚本对一次执行使用 `process.js`,对保留执行使用 `persistent-process.js`;两者委托同一个循环,供未指定程序语法的宿主默认使用 REPL。内嵌载体通过 `launch.executable` 启动私有 worker,并在保留模式下设置 `DSH_PTC_RUNTIME_NODE=persistent`。远端脚本部署提供对应的 `launch.bootstrapPath`。`resolveLaunch()` 在构造时选择本地默认值;显式的远端调用不依赖客户端自身是否打包。不会假设 Host 路径对应同一个远程文件。
|
||||
源代码执行加载仅含可擦除语法的 bootstrap 依赖,不依赖同级包的构建后导出;执行世界中必须提供求值器模块和已安装的 Acorn 与 birpc 依赖。构建后脚本对所有模式使用 `process.js`;`persistent-process.js` 委托同一个循环,并为未指定程序语法的宿主提供 REPL 默认值。内嵌载体通过 `launch.executable` 启动私有 worker,并在每次执行中携带程序格式和保留标志。远端脚本部署提供对应的 `launch.bootstrapPath`。`resolveLaunch()` 在构造时选择本地默认值;显式的远端调用不依赖客户端自身是否打包。不会假设 Host 路径对应同一个远程文件。
|
||||
|
||||
包含 `await` 的源码会在原生编译前检查顶层 await,否则原生编译可能将 `await` 当作标识符。Acorn 解析包住源码的异步函数,acorn-walk 沿用 Node REPL 的 visitor、声明提升和按字符位置修改源码的方式进行遍历。类方法的计算属性名参与遍历,方法体仍独立求值。转换包装末尾表达式,求值器解包并等待其值。无需转换的源码使用原生编译;不含 `await` 的提交保留原生语法诊断。语法错误保留解析器消息、可用的源码位置与光标,不包含编译器或转换器的调用帧。动态 `import()` 表达式保持原生形式,使用编译该 Script 时的 `importModuleDynamically` 选项。两个私有进程 bundle 都包含 Acorn、acorn-walk 和 birpc;源码执行导入已安装的包。
|
||||
|
||||
|
||||
@@ -27,10 +27,6 @@
|
||||
"types": "./lib/types/process-entry.d.ts",
|
||||
"default": "./lib/process.js"
|
||||
},
|
||||
"./persistent-process": {
|
||||
"types": "./lib/types/persistent-process-entry.d.ts",
|
||||
"default": "./lib/persistent-process.js"
|
||||
},
|
||||
"./locale/*.json": "./locale/*.json"
|
||||
},
|
||||
"files": [
|
||||
|
||||
@@ -291,7 +291,7 @@ export class ProgramWorker {
|
||||
.filter(key => !STARTUP_ENVIRONMENT_NAMES.has(key.toUpperCase()) && key.toUpperCase() !== 'ELECTRON_RUN_AS_NODE')
|
||||
.map(key => [key, undefined]))
|
||||
if (packaged) {
|
||||
env.DSH_PTC_RUNTIME_NODE = this.retainsState ? 'persistent' : '1'
|
||||
env.DSH_PTC_RUNTIME_NODE = '1'
|
||||
env.NODE_OPTIONS = `${heapFlag} --experimental-vm-modules`
|
||||
}
|
||||
const handle = this.ctx.subprocess.spawn({ argv: this.confined?.argv ?? argv, cwd, env, stdio: { stdin: 'ignore', stdout: 'pipe', stderr: 'pipe', control: 'pipe' }, graceMs: this.config.graceMs })
|
||||
|
||||
@@ -5,16 +5,16 @@ import { Context } from '@deepseek-ai/cordis'
|
||||
import { describe, expect, it, onTestFinished } from 'vitest'
|
||||
import { mountRuntime } from './setup.ts'
|
||||
|
||||
const runtimePath = process.env.DSH_PACKAGED_RUNTIME
|
||||
const runtimePath = process.env.DSH_PACKAGED_RUNTIME ?? ''
|
||||
|
||||
describe.skipIf(runtimePath === undefined)('packaged embedded carrier', () => {
|
||||
describe.skipIf(runtimePath === '')('packaged embedded carrier', () => {
|
||||
it('retains state across evaluations through the packaged runtime', async () => {
|
||||
const cwd = await mkdtemp(join(tmpdir(), 'dsh-packaged-ptc-'))
|
||||
const ctx = new Context()
|
||||
onTestFinished(async () => { await ctx.fiber.dispose(); await rm(cwd, { recursive: true, force: true }) })
|
||||
const { runtime } = await mountRuntime(ctx, {
|
||||
executionMode: 'persistent-in-memory',
|
||||
launch: { kind: 'embedded', executable: runtimePath as string },
|
||||
launch: { kind: 'embedded', executable: runtimePath },
|
||||
})
|
||||
const owner = {}
|
||||
const run = (program: string) =>
|
||||
@@ -22,10 +22,21 @@ describe.skipIf(runtimePath === undefined)('packaged embedded carrier', () => {
|
||||
|
||||
expect((await run('var retained = 40; retained')).value).toBe(40)
|
||||
expect((await run('retained += 2; retained')).value).toBe(42)
|
||||
// pkg exposes the packaged tree at this URL; the CUA provider prompt uses the same scheme.
|
||||
const bridge = await run('var bridge = await import("file:///snapshot/node/node_modules/@deepseek-ai/dsh-cua/lib/bridge.js"); typeof bridge.connectCua')
|
||||
expect(bridge.value).toBe('function')
|
||||
await runtime.release(owner)
|
||||
expect((await run('typeof retained')).value).toBe('undefined')
|
||||
}, 120_000)
|
||||
|
||||
it('does not retain state without an owner', async () => {
|
||||
const cwd = await mkdtemp(join(tmpdir(), 'dsh-packaged-ptc-'))
|
||||
const ctx = new Context()
|
||||
onTestFinished(async () => { await ctx.fiber.dispose(); await rm(cwd, { recursive: true, force: true }) })
|
||||
const { runtime } = await mountRuntime(ctx, {
|
||||
launch: { kind: 'embedded', executable: runtimePath },
|
||||
})
|
||||
const run = (program: string) =>
|
||||
runtime.run(runtime.resolve({ program, bindings: [], cwd }))
|
||||
|
||||
expect((await run('var retained = 40; return retained')).value).toBe(40)
|
||||
expect((await run('return typeof retained')).value).toBe('undefined')
|
||||
}, 120_000)
|
||||
})
|
||||
@@ -501,22 +501,6 @@ describe('Node runtime host failures', () => {
|
||||
expect(Object.fromEntries(Object.entries(spec?.env ?? {}).filter(([, value]) => value !== undefined)))
|
||||
.toEqual({ DSH_PTC_RUNTIME_NODE: '1', NODE_OPTIONS: '--max-old-space-size=512 --experimental-vm-modules' })
|
||||
})
|
||||
|
||||
it('selects the persistent embedded bootstrap for retained state', async () => {
|
||||
const h = await setup({
|
||||
executionMode: 'persistent-in-memory',
|
||||
maxMessageBytes: 2048,
|
||||
launch: { kind: 'embedded', executable: '/remote/helper' },
|
||||
})
|
||||
h.onBoot(() => { h.complete({ value: encodePtcJsonWire(1) }) })
|
||||
const owner = {}
|
||||
expect((await h.start({ program: '1', bindings: [], stateOwner: owner })).error).toBeUndefined()
|
||||
const spec = h.spawn.mock.calls[0]?.[0]
|
||||
expect(spec?.env?.DSH_PTC_RUNTIME_NODE).toBe('persistent')
|
||||
expect(spec?.argv).toEqual([process.execPath, '2048'])
|
||||
expect(Object.fromEntries(Object.entries(spec?.env ?? {}).filter(([, value]) => value !== undefined)))
|
||||
.toEqual({ DSH_PTC_RUNTIME_NODE: 'persistent', NODE_OPTIONS: '--max-old-space-size=512 --experimental-vm-modules' })
|
||||
})
|
||||
})
|
||||
|
||||
it('keeps failed per-call cleanup registered until process observation succeeds', async () => {
|
||||
|
||||
@@ -34,7 +34,7 @@ it('resolves the local carrier once while preserving an explicit remote script c
|
||||
expect(resolveLaunch()).toEqual({ kind: 'node-script', executable: process.execPath })
|
||||
})
|
||||
|
||||
it('selects embedded and script carriers without a persistent-only launch restriction', () => {
|
||||
it('selects embedded and script bootstrap arguments', () => {
|
||||
expect(bootstrapArgs(noMapping, { kind: 'embedded', executable: '/remote/helper' }, 2048))
|
||||
.toEqual(['2048'])
|
||||
expect(bootstrapArgs(noMapping, { kind: 'node-script', executable: '/remote/node', bootstrapPath: '/remote/persistent-process.js' }, 2048))
|
||||
|
||||
@@ -27,15 +27,9 @@ export async function runSshHelperRuntime(): Promise<void> {
|
||||
|
||||
const selection = process.env.DSH_SUBPROCESS_RUNNER
|
||||
Reflect.deleteProperty(process.env, 'DSH_SUBPROCESS_RUNNER')
|
||||
if (process.env.DSH_PTC_RUNTIME_NODE !== undefined) {
|
||||
const mode = process.env.DSH_PTC_RUNTIME_NODE
|
||||
if (process.env.DSH_PTC_RUNTIME_NODE === '1') {
|
||||
Reflect.deleteProperty(process.env, 'DSH_PTC_RUNTIME_NODE')
|
||||
if (mode !== '1' && mode !== 'persistent') {
|
||||
throw new Error('DSH_PTC_RUNTIME_NODE must be 1 or persistent')
|
||||
}
|
||||
await import(mode === 'persistent'
|
||||
? '@deepseek-ai/dsh-ptc-runtime-node/persistent-process'
|
||||
: '@deepseek-ai/dsh-ptc-runtime-node/process')
|
||||
await import('@deepseek-ai/dsh-ptc-runtime-node/process')
|
||||
} else if (selection !== undefined) {
|
||||
const { runSelectedSubprocessRunner } = await import('@deepseek-ai/dsh-subprocess-local/runner')
|
||||
await runSelectedSubprocessRunner(selection)
|
||||
|
||||
@@ -84,15 +84,9 @@ if (resource !== undefined) {
|
||||
} else if (aclRunner !== undefined && process.argv[2] === aclRunner) {
|
||||
process.argv.splice(1, 1)
|
||||
await import('@deepseek-ai/dsh-sandbox-windows-acl/runner')
|
||||
} else if (process.env.DSH_PTC_RUNTIME_NODE !== undefined) {
|
||||
const mode = process.env.DSH_PTC_RUNTIME_NODE
|
||||
} else if (process.env.DSH_PTC_RUNTIME_NODE === '1') {
|
||||
Reflect.deleteProperty(process.env, 'DSH_PTC_RUNTIME_NODE')
|
||||
if (mode !== '1' && mode !== 'persistent') {
|
||||
throw new Error('DSH_PTC_RUNTIME_NODE must be 1 or persistent')
|
||||
}
|
||||
await import(mode === 'persistent'
|
||||
? '@deepseek-ai/dsh-ptc-runtime-node/persistent-process'
|
||||
: '@deepseek-ai/dsh-ptc-runtime-node/process')
|
||||
await import('@deepseek-ai/dsh-ptc-runtime-node/process')
|
||||
} else if (selection === undefined) {
|
||||
// A DSH_PRIMARY_RUNTIME set or inherited from the environment, including the empty opt-out, takes precedence over the
|
||||
// downloaded cache.
|
||||
|
||||
Reference in new issue
Block a user