@nowcrew/daemon 0.6.89 → 0.6.90
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 +64 -3
- package/dist/agent-ability/materializer.js +21 -10
- package/dist/agent-environment-contract.js +55 -0
- package/dist/agent-environment-control.js +24 -0
- package/dist/agent-environment-execution.js +98 -0
- package/dist/agent-environments.js +275 -0
- package/dist/automatic-daemon-installer.js +49 -1
- package/dist/computer-main.js +18 -0
- package/dist/computer-profile.js +17 -4
- package/dist/computer-setup-client.js +318 -0
- package/dist/computer-setup-orchestration.js +203 -0
- package/dist/computer-setup.js +244 -0
- package/dist/config.js +30 -12
- package/dist/console-formatter.js +3 -3
- package/dist/console.js +15 -2
- package/dist/daemon-update-eligibility.js +1 -1
- package/dist/execution-journal.js +9 -6
- package/dist/execution-protocol.js +1 -0
- package/dist/execution-runner.js +13 -8
- package/dist/i18n.js +19 -0
- package/dist/list-models.js +12 -12
- package/dist/local-execution-lease.js +22 -0
- package/dist/local-executor.js +57 -84
- package/dist/local-memory/candidate.js +43 -0
- package/dist/local-memory/index-budget.js +8 -0
- package/dist/local-memory/inventory-cli.js +61 -0
- package/dist/local-memory/inventory-preview.js +159 -0
- package/dist/local-memory/inventory-source.js +159 -0
- package/dist/local-memory/pending-match.js +29 -0
- package/dist/local-memory/pending-query.js +100 -0
- package/dist/local-memory/pending-schema.js +29 -0
- package/dist/local-memory/pending-source.js +126 -0
- package/dist/local-memory/pending.js +34 -84
- package/dist/local-memory/prompt.js +19 -12
- package/dist/local-memory/read-cli.js +97 -12
- package/dist/local-memory/rollout.js +82 -0
- package/dist/local-memory/runtime.js +23 -17
- package/dist/local-memory/work-log-execution.js +31 -0
- package/dist/local-memory/work-log.js +58 -0
- package/dist/local-memory/write.js +27 -58
- package/dist/local-runtime-environment.js +50 -0
- package/dist/machine-environment-cache.js +147 -0
- package/dist/machine-environment-contract.js +10 -0
- package/dist/machine-environment-control.js +27 -0
- package/dist/machine-environment-policy.js +75 -0
- package/dist/machine-environment-process.js +110 -0
- package/dist/machine-info.js +70 -25
- package/dist/machine-runtime-environment.js +189 -0
- package/dist/machine-shell-environment-capture.js +135 -0
- package/dist/machine-windows-environment-capture.js +215 -0
- package/dist/machine-windows-environment-script.js +158 -0
- package/dist/main.js +4 -69
- package/dist/marketplace-command.js +21 -0
- package/dist/marketplace-controller.js +80 -0
- package/dist/marketplace-protocol.js +38 -0
- package/dist/marketplace-remote.js +48 -0
- package/dist/marketplace-runner.js +235 -0
- package/dist/marketplace-serve.js +17 -0
- package/dist/marketplace-source.js +103 -0
- package/dist/message-file-preview.js +246 -0
- package/dist/pre-admission-rejection.js +64 -0
- package/dist/project-skills/execution-adapter.js +3 -2
- package/dist/project-skills/runtime-launch.js +14 -0
- package/dist/provider-env.js +26 -15
- package/dist/runner.js +17 -4
- package/dist/runtime-environment-merge.js +16 -0
- package/dist/runtime-health-handler.js +10 -0
- package/dist/runtime-health.js +3 -1
- package/dist/runtime-path.js +22 -12
- package/dist/runtime-probe.js +35 -6
- package/dist/runtimes/acp-probe-transport.js +69 -0
- package/dist/runtimes/claude-models.js +20 -9
- package/dist/runtimes/deepseek-harness-launch.js +10 -0
- package/dist/runtimes/deepseek-harness-profile.js +92 -0
- package/dist/runtimes/deepseek-harness-runner.js +226 -0
- package/dist/runtimes/deepseek-harness.js +76 -15
- package/dist/runtimes/harness-probe-retry.js +42 -0
- package/dist/runtimes/hermes-models.js +2 -1
- package/dist/runtimes/kimi-acp-runner.js +78 -13
- package/dist/runtimes/opencode.js +2 -2
- package/dist/serve.js +88 -176
- package/dist/storage-identity.js +0 -3
- package/dist/storage-serve-adapter.js +6 -72
- package/dist/supervised-runtime.js +17 -2
- package/dist/token.js +0 -1
- package/dist/workspace-control-handler.js +27 -0
- package/dist/workspace-fs.js +66 -15
- package/dist/workspace.js +1 -1
- package/package.json +13 -12
- package/dist/codex-home-launch.js +0 -7
- package/dist/codex-home-mode-controller.js +0 -68
- package/dist/daemon-migration-controller.js +0 -211
- package/dist/daemon-migration-wiring.js +0 -22
- package/dist/runtimes/codex-home-migration-cli.js +0 -26
- package/dist/runtimes/codex-home-migration.js +0 -112
- package/dist/runtimes/codex-home.js +0 -236
- 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 -241
- 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 -447
- package/dist/storage-worker-handoff.js +0 -151
- package/dist/storage-worker-operation.js +0 -294
- package/dist/storage-worker-status.js +0 -259
package/dist/serve.js
CHANGED
|
@@ -1,21 +1,19 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/** 常驻控制面桥接;按请求拉起本地 runtime。 */
|
|
2
|
+
import { handleAgentEnvironmentFrame } from "./agent-environment-control.js";
|
|
3
|
+
import { handleMachineEnvironmentFrame } from "./machine-environment-control.js";
|
|
4
|
+
import { createMachineRuntimeEnvironment } from "./machine-runtime-environment.js";
|
|
5
5
|
import { WebSocket } from "ws";
|
|
6
|
+
import { persistPreAdmissionRejection, rejectQueuedAdmission } from "./pre-admission-rejection.js";
|
|
6
7
|
import { join } from "node:path";
|
|
7
8
|
import { randomUUID } from "node:crypto";
|
|
8
9
|
import { dslog, setSlogDefaults, drainSpool, flushSlog } from "./slog.js";
|
|
9
10
|
import { mergeRunAgentResults, reportScheduledStartFailure, runAgent } from "./runner.js";
|
|
10
11
|
import { buildOriginDecisionRetryPrompt, buildScheduledPrompt } from "./prompt.js";
|
|
11
|
-
import {
|
|
12
|
+
import { daemonCapabilities, daemonCapabilityBindings, } from "./machine-info.js";
|
|
12
13
|
import { reconcileExecutionJournal } from "./execution-recovery.js";
|
|
13
14
|
import { initReleaseSlog, reportDaemonConnectionClose, reportReleaseError } from "./release-channel-guard.js";
|
|
14
|
-
import {
|
|
15
|
-
import { listWorkspace, readWorkspaceFile } from "./workspace-fs.js";
|
|
16
|
-
import { listSkills } from "./skills.js";
|
|
15
|
+
import { handleWorkspaceControlRequest } from "./workspace-control-handler.js";
|
|
17
16
|
import { inspectRaftWorkspace, importRaftWorkspace } from "./workspace-import.js";
|
|
18
|
-
import { listRuntimeModels } from "./list-models.js";
|
|
19
17
|
import { normalizeScheduledContext } from "./scheduled-report.js";
|
|
20
18
|
import { formatDaemonLogLine } from "./log-format.js";
|
|
21
19
|
import { reportAgentRunComplete } from "./scheduled-run-report.js";
|
|
@@ -49,12 +47,10 @@ import { createProjectSkillsReconciler, } from "./project-skills/reconciler.js";
|
|
|
49
47
|
import { initializedProjectSkillsReconciler } from "./project-skills/initialized-reconciler.js";
|
|
50
48
|
import { parseMemoryPruneTraceId } from "./memory-prune-diagnostics.js";
|
|
51
49
|
import { probeRuntimeHealth } from "./runtime-health.js";
|
|
52
|
-
import {
|
|
50
|
+
import { createServeMarketplace } from "./marketplace-serve.js";
|
|
53
51
|
import { buildControlPlaneUrl } from "./control-plane-url.js";
|
|
54
|
-
import { createServeMigrationController, handleMigrationControlMessage } from "./daemon-migration-wiring.js";
|
|
55
|
-
import { createCodexHomeModeController } from "./codex-home-mode-controller.js";
|
|
56
52
|
import { resolveDaemonLocalStateRootFromEnv } from "./daemon-local-home.js";
|
|
57
|
-
import {
|
|
53
|
+
import { bindStorageServerIdentity, createStorageIdentityAdmission, } from "./storage-serve-adapter.js";
|
|
58
54
|
export { buildControlPlaneUrl } from "./control-plane-url.js";
|
|
59
55
|
// normalize.ts 的活动种类 → activity 枚举
|
|
60
56
|
const ACTIVITY_MAP = {
|
|
@@ -80,9 +76,6 @@ export function serve(config, opts = {}) {
|
|
|
80
76
|
?? (config.agentMemory === null ? undefined : createAgentMemoryBridge(config.agentMemory));
|
|
81
77
|
const executionTelemetry = createExecutionTelemetryJournal(config.agentsRoot);
|
|
82
78
|
const executeProtocol = opts.execution?.runExecution ?? runExecution;
|
|
83
|
-
let detectedExecutionRuntimes = [], executionRuntimesKnown = false;
|
|
84
|
-
let runtimeFacts = null;
|
|
85
|
-
const runtimeProbe = createRuntimeProbeCoordinator();
|
|
86
79
|
const localHome = opts.localHome ?? resolveDaemonLocalStateRootFromEnv();
|
|
87
80
|
const hostCoordinator = opts.execution?.hostCoordinator
|
|
88
81
|
?? createHostExecutionCoordinator({
|
|
@@ -127,28 +120,14 @@ export function serve(config, opts = {}) {
|
|
|
127
120
|
catch { /* reconnect/timeout reconciliation handles a lost status frame */ }
|
|
128
121
|
},
|
|
129
122
|
});
|
|
130
|
-
|
|
131
|
-
const
|
|
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
|
-
});
|
|
141
|
-
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
|
-
if (ws?.readyState === WebSocket.OPEN)
|
|
143
|
-
ws.send(JSON.stringify(frame));
|
|
144
|
-
}
|
|
145
|
-
catch { /* reconnect */ } } });
|
|
123
|
+
const marketplace = createServeMarketplace(config, localSlots, hostCoordinator);
|
|
124
|
+
const storageIdentityAdmission = createStorageIdentityAdmission(opts.storageRuntime);
|
|
146
125
|
const projectionCoordinator = createAgentProjectionCoordinator();
|
|
147
126
|
const { projectRegistry, projectWorkspaceRegistry, projectDependencyProvisioner } = createProjectRegistryWiring(config.agentsRoot, opts.execution?.dependencies);
|
|
148
127
|
const agentAbilityRuntime = createAgentAbilityRuntime(config.agentsRoot, {
|
|
149
128
|
...opts.agentAbility,
|
|
150
129
|
coordinator: projectionCoordinator,
|
|
151
|
-
isAdmissionClosed:
|
|
130
|
+
isAdmissionClosed: storageIdentityAdmission.isAdmissionClosed,
|
|
152
131
|
});
|
|
153
132
|
let projectSkillsController;
|
|
154
133
|
const projectSkillsReconciler = opts.projectSkills?.reconciler ?? createProjectSkillsReconciler({
|
|
@@ -169,7 +148,7 @@ export function serve(config, opts = {}) {
|
|
|
169
148
|
}
|
|
170
149
|
catch { /* 下一次 ready 或项目操作会重新发送完整快照 */ }
|
|
171
150
|
},
|
|
172
|
-
isAdmissionClosed:
|
|
151
|
+
isAdmissionClosed: storageIdentityAdmission.isAdmissionClosed,
|
|
173
152
|
reconcile: (handle, bindings) => projectSkillsReconciler.reconcile(handle, bindings),
|
|
174
153
|
});
|
|
175
154
|
const logRegistryFailure = (error) => dslog("project_skills.registry_failed", "本机项目注册表读取失败,本次不上报项目清单(能力不受影响)", {
|
|
@@ -177,7 +156,7 @@ export function serve(config, opts = {}) {
|
|
|
177
156
|
});
|
|
178
157
|
const machineHelloPublisher = createMachineHelloPublisher({
|
|
179
158
|
effective: (hello) => {
|
|
180
|
-
const configuredIdentity = opts.
|
|
159
|
+
const configuredIdentity = opts.storageRuntime?.identity;
|
|
181
160
|
const currentIdentity = typeof configuredIdentity === "function"
|
|
182
161
|
? configuredIdentity()
|
|
183
162
|
: configuredIdentity;
|
|
@@ -185,25 +164,19 @@ export function serve(config, opts = {}) {
|
|
|
185
164
|
},
|
|
186
165
|
onSent: (hello) => log(`📤 已上报机器信息: ${hello.hostname} · ${hello.os} · installed=[${hello.runtimes.join(",")}] · executable=[${hello.executionRuntimes.join(",")}] · agents=[${hello.agentHandles.join(",")}]`),
|
|
187
166
|
});
|
|
167
|
+
const machineRuntime = createMachineRuntimeEnvironment({
|
|
168
|
+
config,
|
|
169
|
+
...(opts.machineEnvironment === undefined ? {} : { provider: opts.machineEnvironment }),
|
|
170
|
+
...(opts.machineInfo === undefined ? {} : { machineInfo: opts.machineInfo }),
|
|
171
|
+
...(opts.profileName === undefined ? {} : { profileName: opts.profileName }),
|
|
172
|
+
additionalCapabilities: async () => managedDaemonCapabilities(updateEligibility),
|
|
173
|
+
publish: machineHelloPublisher.publish,
|
|
174
|
+
onError: (code, context) => dslog("execution.runtime_detection_failed", "机器环境或 runtime 探测失败", { level: "ERROR", error_code: code, ...context }),
|
|
175
|
+
});
|
|
176
|
+
machineRuntime.prewarm();
|
|
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)
|
|
@@ -237,6 +210,11 @@ export function serve(config, opts = {}) {
|
|
|
237
210
|
return;
|
|
238
211
|
}
|
|
239
212
|
safeExecutionSend(frame);
|
|
213
|
+
if (frame.type === "execution:rejected" && frame.preAdmissionProof) {
|
|
214
|
+
const entry = await executionJournal.get(frame.executionId);
|
|
215
|
+
if (entry?.completion)
|
|
216
|
+
completionRetransmitter.track(entry.completion);
|
|
217
|
+
}
|
|
240
218
|
};
|
|
241
219
|
const replayExecutionTelemetry = async () => {
|
|
242
220
|
const frames = await executionTelemetry.replay();
|
|
@@ -312,67 +290,29 @@ export function serve(config, opts = {}) {
|
|
|
312
290
|
if (stopped)
|
|
313
291
|
return;
|
|
314
292
|
const wsUrl = (opts.buildControlPlaneUrl ?? buildControlPlaneUrl)(config.serverUrl, config.machineToken, process.platform, undefined, capabilities.controlPlaneUrl);
|
|
315
|
-
|
|
293
|
+
const connectionSocket = createWebSocket(wsUrl);
|
|
294
|
+
ws = connectionSocket;
|
|
295
|
+
const sendToConnection = (frame) => {
|
|
296
|
+
try {
|
|
297
|
+
if (!stopped && ws === connectionSocket && connectionSocket.readyState === WebSocket.OPEN)
|
|
298
|
+
connectionSocket.send(JSON.stringify(frame));
|
|
299
|
+
}
|
|
300
|
+
catch { /* 旧连接请求由服务端超时处理。 */ }
|
|
301
|
+
};
|
|
316
302
|
let connectionEpochToken;
|
|
317
|
-
|
|
303
|
+
connectionSocket.on("open", () => {
|
|
318
304
|
connectionEpochToken = projectSkillsController.beginConnectionEpoch();
|
|
319
|
-
|
|
320
|
-
const openedSocket =
|
|
305
|
+
storageIdentityAdmission.beginIdentityBinding();
|
|
306
|
+
const openedSocket = connectionSocket;
|
|
307
|
+
const connectionId = randomUUID();
|
|
321
308
|
backoff = 1000;
|
|
322
309
|
connectedAt = Date.now();
|
|
323
310
|
log(`🔌 已连接控制面 ${config.serverUrl}`);
|
|
324
|
-
dslog("daemon.ws_open", "已连接控制面", { server_url: config.serverUrl });
|
|
311
|
+
dslog("daemon.ws_open", "已连接控制面", { server_url: config.serverUrl, connection_id: connectionId });
|
|
325
312
|
// 连上了才有机会把离线期间(断连原因/退出前)落盘的日志补传上去
|
|
326
313
|
void drainSpool();
|
|
327
314
|
// 上报本机信息 (hostname/os/daemon 版本/已装 runtimes)
|
|
328
|
-
|
|
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
|
-
const { detectInstalled, detectExecutable = detectExecutionRuntimesWithSignal } = opts.machineInfo ?? {};
|
|
335
|
-
const helloPromise = (opts.machineInfo?.collect ?? collectMachineHello)(config.agentsRoot, config.executionLimits, process.platform, {
|
|
336
|
-
...(detectInstalled ? { detectInstalled } : {}),
|
|
337
|
-
...(opts.profileName === undefined ? {} : { profileName: opts.profileName }),
|
|
338
|
-
// First hello must not wait for third-party handshakes; optional transports arrive in the refresh.
|
|
339
|
-
detectExecutable: async (installed) => conservativeExecutionRuntimes(installed),
|
|
340
|
-
additionalCapabilities: async () => [...(await managedDaemonCapabilities(updateEligibility)), ...(opts.profileName === undefined ? [] : ["codex_home_mode_v1"])],
|
|
341
|
-
capabilities: capabilities.machineHello,
|
|
342
|
-
codexHomeMode: config.codexHomeMode,
|
|
343
|
-
});
|
|
344
|
-
runtimeFacts = helloPromise
|
|
345
|
-
.then(async (hello) => {
|
|
346
|
-
if (hello.executionProtocol === undefined) {
|
|
347
|
-
executionRuntimesKnown = true;
|
|
348
|
-
return [];
|
|
349
|
-
}
|
|
350
|
-
const detected = await runtimeProbe.detect(hello.runtimes, detectExecutable);
|
|
351
|
-
executionRuntimesKnown = true;
|
|
352
|
-
await storageReplay;
|
|
353
|
-
if (ws === openedSocket && openedSocket.readyState === WebSocket.OPEN
|
|
354
|
-
&& detected.some((runtime) => !hello.executionRuntimes.includes(runtime))) {
|
|
355
|
-
detectedExecutionRuntimes = detected;
|
|
356
|
-
machineHelloPublisher.publish({ ...hello, executionRuntimes: [...detected] }, openedSocket);
|
|
357
|
-
}
|
|
358
|
-
return detected;
|
|
359
|
-
})
|
|
360
|
-
.catch((error) => {
|
|
361
|
-
executionRuntimesKnown = true;
|
|
362
|
-
dslog("execution.runtime_detection_failed", "runtime 探测失败", {
|
|
363
|
-
level: "ERROR", error_message: error.message,
|
|
364
|
-
});
|
|
365
|
-
return [];
|
|
366
|
-
});
|
|
367
|
-
void helloPromise
|
|
368
|
-
.then(async (hello) => {
|
|
369
|
-
await storageReplay;
|
|
370
|
-
if (ws !== openedSocket || openedSocket.readyState !== WebSocket.OPEN)
|
|
371
|
-
return;
|
|
372
|
-
detectedExecutionRuntimes = hello.executionRuntimes;
|
|
373
|
-
machineHelloPublisher.publish(hello, openedSocket);
|
|
374
|
-
})
|
|
375
|
-
.catch(() => { });
|
|
315
|
+
machineRuntime.open(openedSocket, { capabilities: capabilities.machineHello, beforePublish: Promise.resolve(), connectionId });
|
|
376
316
|
opts.onOpen?.(ws);
|
|
377
317
|
void sendSnapshot(`reconnect-${randomUUID()}`);
|
|
378
318
|
void executionJournal.replay()
|
|
@@ -401,8 +341,8 @@ export function serve(config, opts = {}) {
|
|
|
401
341
|
});
|
|
402
342
|
});
|
|
403
343
|
});
|
|
404
|
-
|
|
405
|
-
if (stopped)
|
|
344
|
+
connectionSocket.on("message", async (data) => {
|
|
345
|
+
if (stopped || ws !== connectionSocket)
|
|
406
346
|
return;
|
|
407
347
|
let decoded;
|
|
408
348
|
try {
|
|
@@ -414,8 +354,10 @@ export function serve(config, opts = {}) {
|
|
|
414
354
|
if (typeof decoded !== "object" || decoded === null)
|
|
415
355
|
return;
|
|
416
356
|
const rawType = "type" in decoded && typeof decoded.type === "string" ? decoded.type : "";
|
|
417
|
-
if (
|
|
357
|
+
if (rawType === "machine:environment:get" || rawType === "machine:environment:refresh") {
|
|
358
|
+
await handleMachineEnvironmentFrame(decoded, machineRuntime.provider, sendToConnection);
|
|
418
359
|
return;
|
|
360
|
+
}
|
|
419
361
|
if (rawType === "daemon:update" || rawType === "daemon:restart") {
|
|
420
362
|
void updateController.handle(decoded).catch((error) => {
|
|
421
363
|
dslog("daemon.control_failed", "daemon 控制操作处理失败", {
|
|
@@ -424,19 +366,6 @@ export function serve(config, opts = {}) {
|
|
|
424
366
|
});
|
|
425
367
|
return;
|
|
426
368
|
}
|
|
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
|
-
if (rawType === "daemon:codex-home-mode") {
|
|
432
|
-
void codexHomeModeController.handle(decoded).catch((error) => dslog("daemon.codex_home_mode_failed", "Codex Home 模式切换失败", { level: "ERROR", error_message: error.message }));
|
|
433
|
-
return;
|
|
434
|
-
}
|
|
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
369
|
if (rawType.startsWith("execution:")) {
|
|
441
370
|
const parsedExecution = ServerToDaemonExecutionFrameSchema.safeParse(decoded);
|
|
442
371
|
if (!parsedExecution.success) {
|
|
@@ -498,10 +427,10 @@ export function serve(config, opts = {}) {
|
|
|
498
427
|
return;
|
|
499
428
|
}
|
|
500
429
|
const spec = frame;
|
|
501
|
-
if (
|
|
430
|
+
if (storageIdentityAdmission.isAdmissionClosed()) {
|
|
502
431
|
safeExecutionSend(ExecutionRejectedSchema.parse({
|
|
503
432
|
type: "execution:rejected", protocolVersion: 1, executionId: spec.executionId,
|
|
504
|
-
reason: "resource_limit", message: "Storage
|
|
433
|
+
reason: "resource_limit", message: "Storage identity binding is not complete",
|
|
505
434
|
at: new Date().toISOString(),
|
|
506
435
|
}));
|
|
507
436
|
return;
|
|
@@ -560,18 +489,8 @@ export function serve(config, opts = {}) {
|
|
|
560
489
|
: spec.context.scheduledRunId === undefined ? "normal" : "scheduled";
|
|
561
490
|
let reservation = sharedSlots.reserve(spec.agent.handle, "execution", executionClass);
|
|
562
491
|
if (!reservation.accepted) {
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
execution_id: spec.executionId,
|
|
566
|
-
agent_handle: spec.agent.handle,
|
|
567
|
-
execution_class: executionClass,
|
|
568
|
-
...reservation.facts,
|
|
569
|
-
});
|
|
570
|
-
safeExecutionSend(ExecutionRejectedSchema.parse({
|
|
571
|
-
type: "execution:rejected", protocolVersion: 1, executionId: spec.executionId,
|
|
572
|
-
reason: "resource_limit", message: "Local machine execution queue is full",
|
|
573
|
-
at: new Date().toISOString(),
|
|
574
|
-
}));
|
|
492
|
+
await rejectQueuedAdmission({ journal: executionJournal, knownExecutionHashes, executionRuns,
|
|
493
|
+
report: reportExecutionFrame, replay: sendJournalStatus }, spec, executionClass, reservation.facts);
|
|
575
494
|
return;
|
|
576
495
|
}
|
|
577
496
|
knownExecutionHashes.set(spec.executionId, hash);
|
|
@@ -710,9 +629,7 @@ export function serve(config, opts = {}) {
|
|
|
710
629
|
let availableRuntimes;
|
|
711
630
|
try {
|
|
712
631
|
availableRuntimes = opts.execution?.availableRuntimes?.()
|
|
713
|
-
??
|
|
714
|
-
? detectedExecutionRuntimes
|
|
715
|
-
: await (runtimeFacts ?? Promise.resolve(detectedExecutionRuntimes)));
|
|
632
|
+
?? await machineRuntime.availableRuntimes(spec.runtime.name);
|
|
716
633
|
}
|
|
717
634
|
catch (error) {
|
|
718
635
|
cleanupExecutionReservation("runtime_detection_failed");
|
|
@@ -729,6 +646,7 @@ export function serve(config, opts = {}) {
|
|
|
729
646
|
}
|
|
730
647
|
const execution = executeProtocol(config, spec, {
|
|
731
648
|
...opts.execution?.dependencies,
|
|
649
|
+
machineEnvironment: machineRuntime.provider,
|
|
732
650
|
capabilities: capabilities.executionRunner,
|
|
733
651
|
projectSkills: opts.execution?.dependencies?.projectSkills ?? initializedProjectSkills,
|
|
734
652
|
abilityRelease: opts.execution?.dependencies?.abilityRelease ?? agentAbilityRuntime.materializer,
|
|
@@ -741,6 +659,13 @@ export function serve(config, opts = {}) {
|
|
|
741
659
|
...reservation.facts,
|
|
742
660
|
},
|
|
743
661
|
report: reportExecutionFrame,
|
|
662
|
+
confirmPreAdmission: async (rejection) => {
|
|
663
|
+
const prior = await executionJournal.get(spec.executionId);
|
|
664
|
+
if (prior !== null || stopped || knownExecutionHashes.get(spec.executionId) !== hash
|
|
665
|
+
|| executionRuns.get(spec.executionId) !== execution)
|
|
666
|
+
return false;
|
|
667
|
+
return await persistPreAdmissionRejection(executionJournal, spec, rejection) !== null;
|
|
668
|
+
},
|
|
744
669
|
startupGate: runtimeStartupGate,
|
|
745
670
|
startupTimeoutMs: config.executionLimits.startupTimeoutMs,
|
|
746
671
|
onRuntimePhase: (phase, runtime) => {
|
|
@@ -812,17 +737,17 @@ export function serve(config, opts = {}) {
|
|
|
812
737
|
// ready 帧带 server 视角的 machineId/workspaceId → 作为后续所有日志的默认关联键
|
|
813
738
|
const r = msg;
|
|
814
739
|
setSlogDefaults({ machine_id: r.machineId, workspace_id: r.workspaceId });
|
|
815
|
-
if (opts.
|
|
740
|
+
if (opts.storageRuntime !== undefined && typeof r.workspaceId === "string" && typeof r.machineId === "string") {
|
|
816
741
|
const bindingSocket = ws;
|
|
817
|
-
|
|
742
|
+
bindStorageServerIdentity(opts.storageRuntime, { workspaceId: r.workspaceId, machineId: r.machineId }, {
|
|
818
743
|
onBound: () => {
|
|
819
744
|
if (bindingSocket !== ws || bindingSocket?.readyState !== WebSocket.OPEN)
|
|
820
745
|
return;
|
|
821
|
-
|
|
746
|
+
storageIdentityAdmission.completeIdentityBinding();
|
|
822
747
|
machineHelloPublisher.republish();
|
|
823
748
|
},
|
|
824
749
|
onError: (error) => {
|
|
825
|
-
dslog("daemon.storage_identity_bind_failed", "
|
|
750
|
+
dslog("daemon.storage_identity_bind_failed", "存储 Workspace 身份绑定失败", { level: "ERROR", error_message: error.message });
|
|
826
751
|
if (bindingSocket === ws && bindingSocket?.readyState === WebSocket.OPEN)
|
|
827
752
|
bindingSocket.close(1011, "storage identity binding failed");
|
|
828
753
|
},
|
|
@@ -867,15 +792,17 @@ export function serve(config, opts = {}) {
|
|
|
867
792
|
return;
|
|
868
793
|
}
|
|
869
794
|
if (msg.type === "runtime-health:probe") {
|
|
870
|
-
const result = await
|
|
795
|
+
const result = await machineRuntime.health(msg, opts.runtimeHealth?.probe);
|
|
871
796
|
if (result !== null) {
|
|
872
|
-
|
|
873
|
-
ws?.send(JSON.stringify({ type: "fs:result", ...result }));
|
|
874
|
-
}
|
|
875
|
-
catch { /* server 会超时 */ }
|
|
797
|
+
sendToConnection({ type: "fs:result", ...result });
|
|
876
798
|
}
|
|
877
799
|
return;
|
|
878
800
|
}
|
|
801
|
+
if (marketplace.handle(msg, frame => { try {
|
|
802
|
+
ws?.send(JSON.stringify(frame));
|
|
803
|
+
}
|
|
804
|
+
catch { /* server 会按请求超时处理 */ } }))
|
|
805
|
+
return;
|
|
879
806
|
if (await agentAbilityRuntime.tryHandleControlMessage(msg, serverCapabilities, (frame) => ws?.send(JSON.stringify(frame))))
|
|
880
807
|
return;
|
|
881
808
|
// 导入 raft agent 工作区:inspect 反填 name/description;import 复制用户内容
|
|
@@ -901,31 +828,16 @@ export function serve(config, opts = {}) {
|
|
|
901
828
|
}
|
|
902
829
|
return;
|
|
903
830
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
try {
|
|
910
|
-
ws?.send(JSON.stringify({ type: "fs:result", reqId: req.reqId, ...r }));
|
|
911
|
-
}
|
|
912
|
-
catch { /* 忽略 */ }
|
|
913
|
-
};
|
|
914
|
-
try {
|
|
915
|
-
const data2 = req.type === "fs:list" ? await listWorkspace(root, req.path)
|
|
916
|
-
: req.type === "fs:read" ? await readWorkspaceFile(root, req.path)
|
|
917
|
-
: req.type === "skills:list" ? await listSkills(req.runtime)
|
|
918
|
-
: { models: await listRuntimeModels(req.type === "probe-models" ? (req.runtime ?? "") : "") };
|
|
919
|
-
reply({ ok: true, data: data2 });
|
|
920
|
-
}
|
|
921
|
-
catch (e) {
|
|
922
|
-
reply({ ok: false, error: e.message });
|
|
923
|
-
}
|
|
831
|
+
if (await handleAgentEnvironmentFrame(msg, config.agentsRoot, sendToConnection))
|
|
832
|
+
return;
|
|
833
|
+
const fileResult = await handleWorkspaceControlRequest(msg, config.agentsRoot, projectWorkspaceRegistry, machineRuntime.models);
|
|
834
|
+
if (fileResult) {
|
|
835
|
+
sendToConnection(fileResult);
|
|
924
836
|
return;
|
|
925
837
|
}
|
|
926
838
|
if (msg.type !== "agent:start")
|
|
927
839
|
return; // ready/error 等忽略
|
|
928
|
-
if (
|
|
840
|
+
if (storageIdentityAdmission.isAdmissionClosed())
|
|
929
841
|
return;
|
|
930
842
|
// 任务键:scheduled run 用 runId(每次运行独立 cwd/work-log;overlap 由 server 端 skip_if_running 管,
|
|
931
843
|
// daemon 的 running 去重只兜底"同一 run 重复投递");普通唤醒仍是 线程锚点 ?? 频道。
|
|
@@ -1128,6 +1040,7 @@ export function serve(config, opts = {}) {
|
|
|
1128
1040
|
...(!scheduled && msg.wake?.seq !== undefined ? { wakeContextUpToSeq: msg.wake.seq } : {}),
|
|
1129
1041
|
...(attemptWake ? { wake: attemptWake } : {}),
|
|
1130
1042
|
}, reportActivity, reportConsole, {
|
|
1043
|
+
machineEnvironment: machineRuntime.provider,
|
|
1131
1044
|
cancellation: controller.cancellation,
|
|
1132
1045
|
startupGate: runtimeStartupGate,
|
|
1133
1046
|
startupTimeoutMs: config.executionLimits.startupTimeoutMs,
|
|
@@ -1290,8 +1203,9 @@ export function serve(config, opts = {}) {
|
|
|
1290
1203
|
void flushSlog(); // 每轮收尾冲一次,保证 run.end 尽快可查
|
|
1291
1204
|
}
|
|
1292
1205
|
});
|
|
1293
|
-
|
|
1294
|
-
|
|
1206
|
+
connectionSocket.on("close", (code) => {
|
|
1207
|
+
machineRuntime.close(connectionSocket);
|
|
1208
|
+
if (stopped || ws !== connectionSocket)
|
|
1295
1209
|
return;
|
|
1296
1210
|
// server 能力随连接失效;下个连接的 ready 帧重新声明(防降级重连后沿用过期能力)。
|
|
1297
1211
|
serverCapabilities = new Set();
|
|
@@ -1315,9 +1229,9 @@ export function serve(config, opts = {}) {
|
|
|
1315
1229
|
}, backoff);
|
|
1316
1230
|
backoff = Math.min(backoff * 2, maxBackoff);
|
|
1317
1231
|
});
|
|
1318
|
-
|
|
1232
|
+
connectionSocket.on("error", () => {
|
|
1319
1233
|
if (!stopped)
|
|
1320
|
-
|
|
1234
|
+
connectionSocket.close();
|
|
1321
1235
|
});
|
|
1322
1236
|
}
|
|
1323
1237
|
const ready = (async () => {
|
|
@@ -1355,9 +1269,7 @@ export function serve(config, opts = {}) {
|
|
|
1355
1269
|
stopPromise = (async () => {
|
|
1356
1270
|
stopped = true;
|
|
1357
1271
|
concurrencyTelemetry.stop();
|
|
1358
|
-
runtimeProbe.stop();
|
|
1359
1272
|
completionRetransmitter.stop();
|
|
1360
|
-
storageStatusDelivery.stop();
|
|
1361
1273
|
const deadline = createShutdownDeadline(shutdownTimeoutMs);
|
|
1362
1274
|
if (reconnectTimer !== null) {
|
|
1363
1275
|
clearTimeout(reconnectTimer);
|
|
@@ -1365,10 +1277,10 @@ export function serve(config, opts = {}) {
|
|
|
1365
1277
|
}
|
|
1366
1278
|
const webSocketClosed = closeWebSocketWithinDeadline(ws, deadline.signal);
|
|
1367
1279
|
const pending = [
|
|
1280
|
+
machineRuntime.stop(),
|
|
1368
1281
|
webSocketClosed,
|
|
1369
1282
|
updateController.drain(),
|
|
1370
|
-
|
|
1371
|
-
codexHomeModeController.drain(),
|
|
1283
|
+
marketplace.stop(),
|
|
1372
1284
|
executionFrameQueue,
|
|
1373
1285
|
...executionRuns.values(),
|
|
1374
1286
|
...[...legacyRuns.values()].map((run) => run.done),
|
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
|
}
|
package/dist/token.js
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** 控制面文件/技能查看的按需处理,独立于执行生命周期。 */
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { readSharedMessageFile } from "./message-file-preview.js";
|
|
4
|
+
import { listWorkspace, readWorkspaceFile } from "./workspace-fs.js";
|
|
5
|
+
import { listSkills } from "./skills.js";
|
|
6
|
+
import { listRuntimeModels } from "./list-models.js";
|
|
7
|
+
export async function handleWorkspaceControlRequest(raw, agentsRoot, registry, models = listRuntimeModels) {
|
|
8
|
+
if (!raw || typeof raw !== "object")
|
|
9
|
+
return null;
|
|
10
|
+
const request = raw;
|
|
11
|
+
if (!["fs:list", "fs:read", "fs:read-shared", "skills:list", "probe-models"].includes(request.type))
|
|
12
|
+
return null;
|
|
13
|
+
if (typeof request.reqId !== "string" || request.reqId.length > 128)
|
|
14
|
+
return null;
|
|
15
|
+
try {
|
|
16
|
+
const root = join(agentsRoot, request.handle);
|
|
17
|
+
const data = request.type === "fs:read-shared" ? await readSharedMessageFile(agentsRoot, raw, registry)
|
|
18
|
+
: request.type === "fs:list" ? await listWorkspace(root, request.path)
|
|
19
|
+
: request.type === "fs:read" ? await readWorkspaceFile(root, request.path)
|
|
20
|
+
: request.type === "skills:list" ? await listSkills(request.runtime)
|
|
21
|
+
: { models: await models(request.runtime ?? "") };
|
|
22
|
+
return { type: "fs:result", reqId: request.reqId, ok: true, data };
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
return { type: "fs:result", reqId: request.reqId, ok: false, error: request.type === "probe-models" ? "runtime_model_probe_failed" : error.message };
|
|
26
|
+
}
|
|
27
|
+
}
|