@nowcrew/daemon 0.6.88-beta.14 → 0.6.88-beta.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/dist/daemon-update-eligibility.js +1 -1
- package/dist/local-executor.js +4 -2
- package/dist/machine-info.js +16 -4
- package/dist/main.js +4 -69
- package/dist/project-skills/execution-adapter.js +3 -2
- package/dist/project-skills/runtime-launch.js +14 -0
- package/dist/provider-env.js +3 -4
- package/dist/runtimes/deepseek-harness-profile.js +92 -0
- package/dist/runtimes/deepseek-harness-runner.js +224 -0
- package/dist/runtimes/deepseek-harness.js +61 -10
- package/dist/runtimes/kimi-acp-runner.js +5 -5
- package/dist/serve.js +14 -62
- package/dist/storage-identity.js +0 -3
- package/dist/storage-serve-adapter.js +6 -72
- package/dist/supervised-runtime.js +17 -2
- package/package.json +1 -1
- package/dist/daemon-migration-controller.js +0 -211
- package/dist/daemon-migration-wiring.js +0 -22
- package/dist/storage-control.js +0 -212
- package/dist/storage-copy.js +0 -628
- package/dist/storage-credential-guard.js +0 -21
- package/dist/storage-doctor.js +0 -228
- package/dist/storage-manifest.js +0 -1027
- package/dist/storage-migration-controller.js +0 -440
- package/dist/storage-migration-manifest.js +0 -105
- package/dist/storage-migration-policy.js +0 -100
- package/dist/storage-runtime-controller.js +0 -381
- package/dist/storage-runtime-readiness.js +0 -240
- package/dist/storage-safe-fs.js +0 -302
- package/dist/storage-status-delivery.js +0 -224
- package/dist/storage-status-replay.js +0 -140
- package/dist/storage-target-confirmation.js +0 -129
- package/dist/storage-worker-command.js +0 -37
- package/dist/storage-worker-executor.js +0 -449
- package/dist/storage-worker-handoff.js +0 -151
- package/dist/storage-worker-operation.js +0 -294
- package/dist/storage-worker-status.js +0 -259
|
@@ -1,20 +1,71 @@
|
|
|
1
1
|
import spawn from "cross-spawn";
|
|
2
2
|
import { isAbsolute } from "node:path";
|
|
3
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { prepareHarnessProfile } from "./deepseek-harness-profile.js";
|
|
3
7
|
import { probeKimiAcp } from "./kimi-acp-runner.js";
|
|
4
8
|
export const DEEPSEEK_HARNESS_CONFIG_ENV = "NOWCREW_DEEPSEEK_HARNESS_CONFIG";
|
|
9
|
+
export const DEEPSEEK_HARNESS_PATCH_ENV = "NOWCREW_DEEPSEEK_HARNESS_PATCH";
|
|
10
|
+
export function deepSeekHarnessLauncher(env = process.env) {
|
|
11
|
+
const configPath = env[DEEPSEEK_HARNESS_CONFIG_ENV]?.trim();
|
|
12
|
+
const patchPath = env[DEEPSEEK_HARNESS_PATCH_ENV]?.trim();
|
|
13
|
+
if (configPath && !isAbsolute(configPath))
|
|
14
|
+
throw new Error(`${DEEPSEEK_HARNESS_CONFIG_ENV} must be an absolute path`);
|
|
15
|
+
if (patchPath && !isAbsolute(patchPath))
|
|
16
|
+
throw new Error(`${DEEPSEEK_HARNESS_PATCH_ENV} must be an absolute path`);
|
|
17
|
+
if (configPath && patchPath)
|
|
18
|
+
throw new Error("DeepSeek Harness legacy config and profile patch are mutually exclusive");
|
|
19
|
+
return configPath
|
|
20
|
+
? { kind: "legacy", bin: "dsh-acp-demo", configPath }
|
|
21
|
+
: { kind: "profile", bin: "dsh", ...(patchPath ? { patchPath } : {}) };
|
|
22
|
+
}
|
|
5
23
|
export function deepSeekHarnessConfigPath(env = process.env) {
|
|
6
24
|
const path = env[DEEPSEEK_HARNESS_CONFIG_ENV]?.trim();
|
|
7
25
|
return path && isAbsolute(path) ? path : null;
|
|
8
26
|
}
|
|
9
27
|
/** The published DeepSeek Harness ACP executable speaks JSON-RPC directly on stdio. */
|
|
10
|
-
export function probeDeepSeekHarnessAcp(options, spawnProcess = spawn) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
28
|
+
export async function probeDeepSeekHarnessAcp(options, spawnProcess = spawn) {
|
|
29
|
+
if (options.signal?.aborted)
|
|
30
|
+
return false;
|
|
31
|
+
let root;
|
|
32
|
+
try {
|
|
33
|
+
const launcher = options.configPath === undefined
|
|
34
|
+
? deepSeekHarnessLauncher()
|
|
35
|
+
: deepSeekHarnessLauncher({
|
|
36
|
+
[DEEPSEEK_HARNESS_CONFIG_ENV]: options.configPath,
|
|
37
|
+
});
|
|
38
|
+
if (launcher.kind === "legacy")
|
|
39
|
+
return await probeKimiAcp({
|
|
40
|
+
...options,
|
|
41
|
+
provider: "deepseek-harness",
|
|
42
|
+
configPath: launcher.configPath,
|
|
43
|
+
effectivePermission: "sandboxed",
|
|
44
|
+
}, spawnProcess);
|
|
45
|
+
root = await mkdtemp(join(tmpdir(), "nowwork-dsh-probe-"));
|
|
46
|
+
const profile = await prepareHarnessProfile({
|
|
47
|
+
stateRoot: root,
|
|
48
|
+
cwd: root,
|
|
49
|
+
systemPrompt: "",
|
|
50
|
+
skillRoots: [],
|
|
51
|
+
effectivePermission: "sandboxed",
|
|
52
|
+
env: process.env,
|
|
53
|
+
...(launcher.patchPath ? { patchPath: launcher.patchPath } : {}),
|
|
54
|
+
});
|
|
55
|
+
return await probeKimiAcp({
|
|
56
|
+
...options,
|
|
57
|
+
provider: "deepseek-harness",
|
|
58
|
+
effectivePermission: "sandboxed",
|
|
59
|
+
launchArgs: profile.args,
|
|
60
|
+
launchEnv: profile.env,
|
|
61
|
+
launchCwd: root,
|
|
62
|
+
}, spawnProcess);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
if (root)
|
|
69
|
+
await rm(root, { recursive: true, force: true }).catch(() => undefined);
|
|
70
|
+
}
|
|
20
71
|
}
|
|
@@ -198,10 +198,10 @@ async function stopChild(child) {
|
|
|
198
198
|
export async function probeKimiAcp(options, spawnProcess = spawn) {
|
|
199
199
|
const provider = options.provider ?? "kimi";
|
|
200
200
|
const permission = options.effectivePermission ?? "sandboxed";
|
|
201
|
-
const child = spawnProcess(options.bin, providerArgs(provider, options.configPath), {
|
|
202
|
-
cwd: process.cwd(),
|
|
201
|
+
const child = spawnProcess(options.bin, options.launchArgs ?? providerArgs(provider, options.configPath), {
|
|
202
|
+
cwd: options.launchCwd ?? process.cwd(),
|
|
203
203
|
// probe 在 daemon 自身 PATH 下运行,补上用户级 CLI 目录,与 which 探测保持一致。
|
|
204
|
-
env: { ...providerEnv(provider, permission), PATH: augmentedPath() },
|
|
204
|
+
env: { ...(options.launchEnv ?? providerEnv(provider, permission)), PATH: augmentedPath() },
|
|
205
205
|
stdio: ["pipe", "pipe", "pipe"],
|
|
206
206
|
});
|
|
207
207
|
if (child.stdin === null || child.stdout === null || child.stderr === null)
|
|
@@ -217,12 +217,12 @@ export async function probeKimiAcp(options, spawnProcess = spawn) {
|
|
|
217
217
|
try {
|
|
218
218
|
const stream = ndJsonStream(Writable.toWeb(child.stdin), Readable.toWeb(child.stdout));
|
|
219
219
|
const connected = app.connectWith(stream, async (context) => {
|
|
220
|
-
await context.request(methods.agent.initialize, {
|
|
220
|
+
const initialized = await context.request(methods.agent.initialize, {
|
|
221
221
|
protocolVersion: PROTOCOL_VERSION,
|
|
222
222
|
clientCapabilities: {},
|
|
223
223
|
clientInfo: { name: "nowcrew-daemon", version: "1" },
|
|
224
224
|
});
|
|
225
|
-
return
|
|
225
|
+
return initialized.protocolVersion === PROTOCOL_VERSION;
|
|
226
226
|
});
|
|
227
227
|
const result = await Promise.race([
|
|
228
228
|
connected,
|
package/dist/serve.js
CHANGED
|
@@ -51,10 +51,9 @@ import { parseMemoryPruneTraceId } from "./memory-prune-diagnostics.js";
|
|
|
51
51
|
import { probeRuntimeHealth } from "./runtime-health.js";
|
|
52
52
|
import { handleRuntimeHealthProbeFrame } from "./runtime-health-handler.js";
|
|
53
53
|
import { buildControlPlaneUrl } from "./control-plane-url.js";
|
|
54
|
-
import { createServeMigrationController, handleMigrationControlMessage } from "./daemon-migration-wiring.js";
|
|
55
54
|
import { createCodexHomeModeController } from "./codex-home-mode-controller.js";
|
|
56
55
|
import { resolveDaemonLocalStateRootFromEnv } from "./daemon-local-home.js";
|
|
57
|
-
import {
|
|
56
|
+
import { bindStorageServerIdentity, createStorageIdentityAdmission, } from "./storage-serve-adapter.js";
|
|
58
57
|
export { buildControlPlaneUrl } from "./control-plane-url.js";
|
|
59
58
|
// normalize.ts 的活动种类 → activity 枚举
|
|
60
59
|
const ACTIVITY_MAP = {
|
|
@@ -127,17 +126,7 @@ export function serve(config, opts = {}) {
|
|
|
127
126
|
catch { /* reconnect/timeout reconciliation handles a lost status frame */ }
|
|
128
127
|
},
|
|
129
128
|
});
|
|
130
|
-
|
|
131
|
-
const migrationController = createServeMigrationController({
|
|
132
|
-
config,
|
|
133
|
-
...(opts.profileName === undefined ? {} : { profileName: opts.profileName }),
|
|
134
|
-
isBusy: () => executionRuns.size > 0 || legacyRuns.size > 0,
|
|
135
|
-
sendStatus: (frame) => { try {
|
|
136
|
-
if (ws?.readyState === WebSocket.OPEN)
|
|
137
|
-
ws.send(JSON.stringify(frame));
|
|
138
|
-
}
|
|
139
|
-
catch { /* reconnect */ } },
|
|
140
|
-
});
|
|
129
|
+
const storageIdentityAdmission = createStorageIdentityAdmission(opts.storageRuntime);
|
|
141
130
|
const codexHomeModeController = createCodexHomeModeController({ ...(opts.profileName === undefined ? {} : { profileName: opts.profileName }), scheduleRestart: opts.update?.scheduleRestart ?? scheduleServiceRestart, getServiceSpec: async () => { const eligibility = await updateEligibility(); return eligibility.eligible ? eligibility.serviceSpec : null; }, isBusy: () => executionRuns.size > 0 || legacyRuns.size > 0, sendStatus: (frame) => { try {
|
|
142
131
|
if (ws?.readyState === WebSocket.OPEN)
|
|
143
132
|
ws.send(JSON.stringify(frame));
|
|
@@ -148,7 +137,7 @@ export function serve(config, opts = {}) {
|
|
|
148
137
|
const agentAbilityRuntime = createAgentAbilityRuntime(config.agentsRoot, {
|
|
149
138
|
...opts.agentAbility,
|
|
150
139
|
coordinator: projectionCoordinator,
|
|
151
|
-
isAdmissionClosed:
|
|
140
|
+
isAdmissionClosed: storageIdentityAdmission.isAdmissionClosed,
|
|
152
141
|
});
|
|
153
142
|
let projectSkillsController;
|
|
154
143
|
const projectSkillsReconciler = opts.projectSkills?.reconciler ?? createProjectSkillsReconciler({
|
|
@@ -169,7 +158,7 @@ export function serve(config, opts = {}) {
|
|
|
169
158
|
}
|
|
170
159
|
catch { /* 下一次 ready 或项目操作会重新发送完整快照 */ }
|
|
171
160
|
},
|
|
172
|
-
isAdmissionClosed:
|
|
161
|
+
isAdmissionClosed: storageIdentityAdmission.isAdmissionClosed,
|
|
173
162
|
reconcile: (handle, bindings) => projectSkillsReconciler.reconcile(handle, bindings),
|
|
174
163
|
});
|
|
175
164
|
const logRegistryFailure = (error) => dslog("project_skills.registry_failed", "本机项目注册表读取失败,本次不上报项目清单(能力不受影响)", {
|
|
@@ -177,7 +166,7 @@ export function serve(config, opts = {}) {
|
|
|
177
166
|
});
|
|
178
167
|
const machineHelloPublisher = createMachineHelloPublisher({
|
|
179
168
|
effective: (hello) => {
|
|
180
|
-
const configuredIdentity = opts.
|
|
169
|
+
const configuredIdentity = opts.storageRuntime?.identity;
|
|
181
170
|
const currentIdentity = typeof configuredIdentity === "function"
|
|
182
171
|
? configuredIdentity()
|
|
183
172
|
: configuredIdentity;
|
|
@@ -187,23 +176,7 @@ export function serve(config, opts = {}) {
|
|
|
187
176
|
});
|
|
188
177
|
const initializedProjectSkills = initializedProjectSkillsReconciler(() => projectSkillsController.ensureScanned(), projectSkillsReconciler);
|
|
189
178
|
const configuredCapabilities = opts.machineInfo?.capabilities ?? daemonCapabilities(process.platform);
|
|
190
|
-
const
|
|
191
|
-
const storageStatusDelivery = createServeStorageStatusDelivery(opts.storageMigration, {
|
|
192
|
-
onPersistenceError: (error) => dslog("daemon.storage_status_outbox_failed", "存储迁移状态 outbox 持久化失败", { level: "ERROR", error_message: error.message }),
|
|
193
|
-
transmit: (frame) => { try {
|
|
194
|
-
if (ws?.readyState === WebSocket.OPEN)
|
|
195
|
-
ws.send(JSON.stringify(frame));
|
|
196
|
-
}
|
|
197
|
-
catch { /* retry */ } },
|
|
198
|
-
});
|
|
199
|
-
const storageMigrationRuntime = attachStorageMigrationRuntime(opts.storageMigration, {
|
|
200
|
-
isBusy: () => executionRuns.size > 0 || legacyRuns.size > 0,
|
|
201
|
-
waitForExecutionFrames: () => executionFrameQueue,
|
|
202
|
-
flushLocalState: flushSlog,
|
|
203
|
-
sendStatus: storageStatusDelivery.send,
|
|
204
|
-
});
|
|
205
|
-
storageMigrationAdmissionClosed = storageMigrationRuntime.isAdmissionClosed;
|
|
206
|
-
const prepareCapabilities = async () => daemonCapabilityBindings(process.platform, [...storageCapabilities, ...await managedDaemonCapabilities(updateEligibility)]);
|
|
179
|
+
const prepareCapabilities = async () => daemonCapabilityBindings(process.platform, [...configuredCapabilities, ...await managedDaemonCapabilities(updateEligibility)]);
|
|
207
180
|
const safeExecutionSend = (frame) => {
|
|
208
181
|
try {
|
|
209
182
|
if (ws?.readyState !== WebSocket.OPEN)
|
|
@@ -316,7 +289,7 @@ export function serve(config, opts = {}) {
|
|
|
316
289
|
let connectionEpochToken;
|
|
317
290
|
ws.on("open", () => {
|
|
318
291
|
connectionEpochToken = projectSkillsController.beginConnectionEpoch();
|
|
319
|
-
|
|
292
|
+
storageIdentityAdmission.beginIdentityBinding();
|
|
320
293
|
const openedSocket = ws;
|
|
321
294
|
backoff = 1000;
|
|
322
295
|
connectedAt = Date.now();
|
|
@@ -325,12 +298,6 @@ export function serve(config, opts = {}) {
|
|
|
325
298
|
// 连上了才有机会把离线期间(断连原因/退出前)落盘的日志补传上去
|
|
326
299
|
void drainSpool();
|
|
327
300
|
// 上报本机信息 (hostname/os/daemon 版本/已装 runtimes)
|
|
328
|
-
storageStatusDelivery.replayPending();
|
|
329
|
-
const storageReplay = replayStorageMigrationStatus(opts.storageMigration, openedSocket, () => ws === openedSocket, (error) => {
|
|
330
|
-
dslog("daemon.storage_status_replay_failed", "存储迁移状态重放失败", {
|
|
331
|
-
level: "ERROR", error_message: error.message,
|
|
332
|
-
});
|
|
333
|
-
}, storageStatusDelivery.send);
|
|
334
301
|
const { detectInstalled, detectExecutable = detectExecutionRuntimesWithSignal } = opts.machineInfo ?? {};
|
|
335
302
|
const helloPromise = (opts.machineInfo?.collect ?? collectMachineHello)(config.agentsRoot, config.executionLimits, process.platform, {
|
|
336
303
|
...(detectInstalled ? { detectInstalled } : {}),
|
|
@@ -349,7 +316,6 @@ export function serve(config, opts = {}) {
|
|
|
349
316
|
}
|
|
350
317
|
const detected = await runtimeProbe.detect(hello.runtimes, detectExecutable);
|
|
351
318
|
executionRuntimesKnown = true;
|
|
352
|
-
await storageReplay;
|
|
353
319
|
if (ws === openedSocket && openedSocket.readyState === WebSocket.OPEN
|
|
354
320
|
&& detected.some((runtime) => !hello.executionRuntimes.includes(runtime))) {
|
|
355
321
|
detectedExecutionRuntimes = detected;
|
|
@@ -366,7 +332,6 @@ export function serve(config, opts = {}) {
|
|
|
366
332
|
});
|
|
367
333
|
void helloPromise
|
|
368
334
|
.then(async (hello) => {
|
|
369
|
-
await storageReplay;
|
|
370
335
|
if (ws !== openedSocket || openedSocket.readyState !== WebSocket.OPEN)
|
|
371
336
|
return;
|
|
372
337
|
detectedExecutionRuntimes = hello.executionRuntimes;
|
|
@@ -414,8 +379,6 @@ export function serve(config, opts = {}) {
|
|
|
414
379
|
if (typeof decoded !== "object" || decoded === null)
|
|
415
380
|
return;
|
|
416
381
|
const rawType = "type" in decoded && typeof decoded.type === "string" ? decoded.type : "";
|
|
417
|
-
if (storageStatusDelivery.acknowledge(decoded))
|
|
418
|
-
return;
|
|
419
382
|
if (rawType === "daemon:update" || rawType === "daemon:restart") {
|
|
420
383
|
void updateController.handle(decoded).catch((error) => {
|
|
421
384
|
dslog("daemon.control_failed", "daemon 控制操作处理失败", {
|
|
@@ -424,19 +387,10 @@ export function serve(config, opts = {}) {
|
|
|
424
387
|
});
|
|
425
388
|
return;
|
|
426
389
|
}
|
|
427
|
-
if (rawType === "daemon:migrate") {
|
|
428
|
-
void handleMigrationControlMessage(migrationController, decoded).catch((error) => dslog("daemon.migration_failed", "daemon 布局迁移失败", { level: "ERROR", error_message: error.message }));
|
|
429
|
-
return;
|
|
430
|
-
}
|
|
431
390
|
if (rawType === "daemon:codex-home-mode") {
|
|
432
391
|
void codexHomeModeController.handle(decoded).catch((error) => dslog("daemon.codex_home_mode_failed", "Codex Home 模式切换失败", { level: "ERROR", error_message: error.message }));
|
|
433
392
|
return;
|
|
434
393
|
}
|
|
435
|
-
if (handleStorageMigrationControl(opts.storageMigration, rawType, decoded, storageStatusDelivery.send, (kind, error) => {
|
|
436
|
-
dslog(kind === "migration" ? "daemon.storage_migration_failed" : "daemon.storage_migration_confirmation_failed", kind === "migration" ? "存储迁移控制操作失败" : "存储迁移 hello 确认处理失败", { level: "ERROR", error_message: error.message });
|
|
437
|
-
})) {
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
440
394
|
if (rawType.startsWith("execution:")) {
|
|
441
395
|
const parsedExecution = ServerToDaemonExecutionFrameSchema.safeParse(decoded);
|
|
442
396
|
if (!parsedExecution.success) {
|
|
@@ -498,10 +452,10 @@ export function serve(config, opts = {}) {
|
|
|
498
452
|
return;
|
|
499
453
|
}
|
|
500
454
|
const spec = frame;
|
|
501
|
-
if (
|
|
455
|
+
if (storageIdentityAdmission.isAdmissionClosed()) {
|
|
502
456
|
safeExecutionSend(ExecutionRejectedSchema.parse({
|
|
503
457
|
type: "execution:rejected", protocolVersion: 1, executionId: spec.executionId,
|
|
504
|
-
reason: "resource_limit", message: "Storage
|
|
458
|
+
reason: "resource_limit", message: "Storage identity binding is not complete",
|
|
505
459
|
at: new Date().toISOString(),
|
|
506
460
|
}));
|
|
507
461
|
return;
|
|
@@ -812,17 +766,17 @@ export function serve(config, opts = {}) {
|
|
|
812
766
|
// ready 帧带 server 视角的 machineId/workspaceId → 作为后续所有日志的默认关联键
|
|
813
767
|
const r = msg;
|
|
814
768
|
setSlogDefaults({ machine_id: r.machineId, workspace_id: r.workspaceId });
|
|
815
|
-
if (opts.
|
|
769
|
+
if (opts.storageRuntime !== undefined && typeof r.workspaceId === "string" && typeof r.machineId === "string") {
|
|
816
770
|
const bindingSocket = ws;
|
|
817
|
-
|
|
771
|
+
bindStorageServerIdentity(opts.storageRuntime, { workspaceId: r.workspaceId, machineId: r.machineId }, {
|
|
818
772
|
onBound: () => {
|
|
819
773
|
if (bindingSocket !== ws || bindingSocket?.readyState !== WebSocket.OPEN)
|
|
820
774
|
return;
|
|
821
|
-
|
|
775
|
+
storageIdentityAdmission.completeIdentityBinding();
|
|
822
776
|
machineHelloPublisher.republish();
|
|
823
777
|
},
|
|
824
778
|
onError: (error) => {
|
|
825
|
-
dslog("daemon.storage_identity_bind_failed", "
|
|
779
|
+
dslog("daemon.storage_identity_bind_failed", "存储 Workspace 身份绑定失败", { level: "ERROR", error_message: error.message });
|
|
826
780
|
if (bindingSocket === ws && bindingSocket?.readyState === WebSocket.OPEN)
|
|
827
781
|
bindingSocket.close(1011, "storage identity binding failed");
|
|
828
782
|
},
|
|
@@ -925,7 +879,7 @@ export function serve(config, opts = {}) {
|
|
|
925
879
|
}
|
|
926
880
|
if (msg.type !== "agent:start")
|
|
927
881
|
return; // ready/error 等忽略
|
|
928
|
-
if (
|
|
882
|
+
if (storageIdentityAdmission.isAdmissionClosed())
|
|
929
883
|
return;
|
|
930
884
|
// 任务键:scheduled run 用 runId(每次运行独立 cwd/work-log;overlap 由 server 端 skip_if_running 管,
|
|
931
885
|
// daemon 的 running 去重只兜底"同一 run 重复投递");普通唤醒仍是 线程锚点 ?? 频道。
|
|
@@ -1357,7 +1311,6 @@ export function serve(config, opts = {}) {
|
|
|
1357
1311
|
concurrencyTelemetry.stop();
|
|
1358
1312
|
runtimeProbe.stop();
|
|
1359
1313
|
completionRetransmitter.stop();
|
|
1360
|
-
storageStatusDelivery.stop();
|
|
1361
1314
|
const deadline = createShutdownDeadline(shutdownTimeoutMs);
|
|
1362
1315
|
if (reconnectTimer !== null) {
|
|
1363
1316
|
clearTimeout(reconnectTimer);
|
|
@@ -1367,7 +1320,6 @@ export function serve(config, opts = {}) {
|
|
|
1367
1320
|
const pending = [
|
|
1368
1321
|
webSocketClosed,
|
|
1369
1322
|
updateController.drain(),
|
|
1370
|
-
...(migrationController === null ? [] : [migrationController.drain()]),
|
|
1371
1323
|
codexHomeModeController.drain(),
|
|
1372
1324
|
executionFrameQueue,
|
|
1373
1325
|
...executionRuns.values(),
|
package/dist/storage-identity.js
CHANGED
|
@@ -39,8 +39,5 @@ export function storageHelloIdentity(input) {
|
|
|
39
39
|
daemonRootFingerprint: fingerprint("daemon-root", layout.daemonRoot),
|
|
40
40
|
agentRootFingerprint: fingerprint("agent-root", layout.agentItemsRoot),
|
|
41
41
|
...(ownership === undefined ? {} : { storageOwnership: ownership }),
|
|
42
|
-
...(installation.storageMigrationId === undefined
|
|
43
|
-
? {}
|
|
44
|
-
: { storageMigrationId: installation.storageMigrationId }),
|
|
45
42
|
});
|
|
46
43
|
}
|
|
@@ -1,55 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
if (options === undefined)
|
|
5
|
-
return configured;
|
|
6
|
-
return Object.freeze([...new Set([
|
|
7
|
-
...configured,
|
|
8
|
-
STORAGE_MIGRATION_STATUS_CAPABILITY,
|
|
9
|
-
...(options.ready ? [STORAGE_MIGRATION_CAPABILITY] : []),
|
|
10
|
-
])]);
|
|
1
|
+
export function bindStorageServerIdentity(options, identity, callbacks) {
|
|
2
|
+
void Promise.resolve().then(() => options.bindServerIdentity?.(identity))
|
|
3
|
+
.then(callbacks.onBound).catch(callbacks.onError);
|
|
11
4
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if (bind === undefined) {
|
|
15
|
-
if (options?.ready === true)
|
|
16
|
-
callbacks.onBound();
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
void Promise.resolve().then(() => bind(identity)).then(callbacks.onBound).catch(callbacks.onError);
|
|
20
|
-
}
|
|
21
|
-
export function createServeStorageStatusDelivery(options, dependencies) {
|
|
22
|
-
return createStorageStatusDelivery({
|
|
23
|
-
...(options?.statusOutboxPath === undefined ? {} : { outboxPath: options.statusOutboxPath }),
|
|
24
|
-
...(options?.statusRetryMs === undefined ? {} : { retryMs: options.statusRetryMs }),
|
|
25
|
-
...dependencies,
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
export function attachStorageMigrationRuntime(options, dependencies) {
|
|
29
|
-
let migrationAdmissionClosed = false;
|
|
5
|
+
/** 新连接必须重新验证 Server 身份,不能沿用上一次连接的执行准入。 */
|
|
6
|
+
export function createStorageIdentityAdmission(options) {
|
|
30
7
|
let identityAdmissionClosed = options?.bindServerIdentity !== undefined;
|
|
31
|
-
if (options?.ready === true) {
|
|
32
|
-
options.attach?.({
|
|
33
|
-
gate: {
|
|
34
|
-
isBusy: dependencies.isBusy,
|
|
35
|
-
closeAndFlush: async () => {
|
|
36
|
-
migrationAdmissionClosed = true;
|
|
37
|
-
await dependencies.waitForExecutionFrames();
|
|
38
|
-
if (dependencies.isBusy()) {
|
|
39
|
-
migrationAdmissionClosed = false;
|
|
40
|
-
throw Object.assign(new Error("active execution entered before storage migration quiesce"), {
|
|
41
|
-
code: "runtime_busy",
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
await dependencies.flushLocalState();
|
|
45
|
-
},
|
|
46
|
-
reopen: () => { migrationAdmissionClosed = false; },
|
|
47
|
-
},
|
|
48
|
-
sendStatus: dependencies.sendStatus,
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
8
|
return Object.freeze({
|
|
52
|
-
isAdmissionClosed: () =>
|
|
9
|
+
isAdmissionClosed: () => identityAdmissionClosed,
|
|
53
10
|
beginIdentityBinding: () => {
|
|
54
11
|
if (options?.bindServerIdentity !== undefined)
|
|
55
12
|
identityAdmissionClosed = true;
|
|
@@ -57,26 +14,3 @@ export function attachStorageMigrationRuntime(options, dependencies) {
|
|
|
57
14
|
completeIdentityBinding: () => { identityAdmissionClosed = false; },
|
|
58
15
|
});
|
|
59
16
|
}
|
|
60
|
-
export function replayStorageMigrationStatus(options, socket, isCurrent, onError, deliver) {
|
|
61
|
-
return options?.replayStatus?.((frame) => {
|
|
62
|
-
if (!isCurrent())
|
|
63
|
-
return;
|
|
64
|
-
if (deliver !== undefined)
|
|
65
|
-
deliver(frame);
|
|
66
|
-
else if (socket.readyState === socket.OPEN)
|
|
67
|
-
socket.send(JSON.stringify(frame));
|
|
68
|
-
}).catch(onError) ?? Promise.resolve();
|
|
69
|
-
}
|
|
70
|
-
export function handleStorageMigrationControl(options, rawType, raw, sendStatus, onError) {
|
|
71
|
-
if (rawType === "daemon:storage-doctor" || rawType === "daemon:storage-migrate") {
|
|
72
|
-
if (options?.ready === true)
|
|
73
|
-
void options.controller.handle(raw).catch((error) => onError("migration", error));
|
|
74
|
-
return true;
|
|
75
|
-
}
|
|
76
|
-
if (rawType !== "daemon:storage-migration-confirmed")
|
|
77
|
-
return false;
|
|
78
|
-
const confirm = options?.ready === false ? options.confirm : undefined;
|
|
79
|
-
if (confirm !== undefined)
|
|
80
|
-
void confirm(raw, sendStatus).catch((error) => onError("confirmation", error));
|
|
81
|
-
return true;
|
|
82
|
-
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { fileURLToPath } from "node:url";
|
|
2
2
|
import { startDormantSupervisor, } from "./execution-supervisor.js";
|
|
3
3
|
import { buildClaudeLaunch } from "./runtimes/claude.js";
|
|
4
|
-
import { DEEPSEEK_HARNESS_CONFIG_ENV, deepSeekHarnessConfigPath, } from "./runtimes/deepseek-harness.js";
|
|
4
|
+
import { DEEPSEEK_HARNESS_CONFIG_ENV, deepSeekHarnessConfigPath, deepSeekHarnessLauncher, } from "./runtimes/deepseek-harness.js";
|
|
5
5
|
import { RuntimeCancelledError } from "./runtime-cancellation.js";
|
|
6
6
|
export function supervisorLaunch(request) {
|
|
7
7
|
const common = {
|
|
@@ -82,7 +82,22 @@ export function supervisorLaunch(request) {
|
|
|
82
82
|
if (request.runtime === "deepseek-harness") {
|
|
83
83
|
if (request.resume)
|
|
84
84
|
throw new Error("DeepSeek Harness ACP does not support session resume");
|
|
85
|
-
const
|
|
85
|
+
const launcher = deepSeekHarnessLauncher(request.env);
|
|
86
|
+
if (launcher.kind === "profile") {
|
|
87
|
+
return {
|
|
88
|
+
command: process.execPath,
|
|
89
|
+
args: [fileURLToPath(new URL("./runtimes/deepseek-harness-runner.js", import.meta.url)),
|
|
90
|
+
"--bin", request.bin, ...(launcher.patchPath ? ["--patch", launcher.patchPath] : [])],
|
|
91
|
+
cwd: request.cwd, env: request.env,
|
|
92
|
+
stdinText: JSON.stringify({ systemPrompt: request.systemPrompt, wakePrompt: request.wakePrompt,
|
|
93
|
+
stateRoot: request.harnessStateRoot ?? request.cwd, skillRoots: request.harnessSkillRoots ?? [],
|
|
94
|
+
effectivePermission: request.effectivePermission }),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if ((request.harnessSkillRoots?.length ?? 0) > 0) {
|
|
98
|
+
throw new Error("DeepSeek Harness bound Skills require the modern dsh ACP profile");
|
|
99
|
+
}
|
|
100
|
+
const configPath = deepSeekHarnessConfigPath(request.env);
|
|
86
101
|
if (configPath === null) {
|
|
87
102
|
throw new Error(`${DEEPSEEK_HARNESS_CONFIG_ENV} must be an absolute path`);
|
|
88
103
|
}
|