@nowcrew/daemon 0.5.26 → 0.5.28
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/package.json +1 -1
- package/dist/attachments.js +0 -196
- package/dist/bound-im-decision.js +0 -22
- package/dist/completion-retransmitter.js +0 -77
- package/dist/computer-cli.js +0 -274
- package/dist/computer-profile-lock.js +0 -395
- package/dist/computer-profile.js +0 -364
- package/dist/computer-service.js +0 -358
- package/dist/config.js +0 -82
- package/dist/console-collapse.js +0 -13
- package/dist/console-formatter.js +0 -77
- package/dist/console-payload.js +0 -73
- package/dist/console.js +0 -329
- package/dist/daemon-startup-error.js +0 -30
- package/dist/execution-backend.js +0 -44
- package/dist/execution-event-limit.js +0 -64
- package/dist/execution-journal-lock.js +0 -421
- package/dist/execution-journal.js +0 -716
- package/dist/execution-protocol.js +0 -342
- package/dist/execution-recovery.js +0 -95
- package/dist/execution-runner.js +0 -659
- package/dist/execution-supervisor-child.js +0 -236
- package/dist/execution-supervisor.js +0 -302
- package/dist/execution-telemetry-journal.js +0 -71
- package/dist/external-output.js +0 -114
- package/dist/i18n.js +0 -64
- package/dist/json-result.js +0 -27
- package/dist/list-models.js +0 -92
- package/dist/local-executor.js +0 -439
- package/dist/log-format.js +0 -10
- package/dist/machine-info.js +0 -124
- package/dist/main.js +0 -118
- package/dist/normalize.js +0 -170
- package/dist/origin-decision.js +0 -44
- package/dist/platform.js +0 -8
- package/dist/prompt.js +0 -307
- package/dist/provider-env.js +0 -90
- package/dist/runner.js +0 -234
- package/dist/runtime-cancellation.js +0 -74
- package/dist/runtime-capabilities.js +0 -43
- package/dist/runtime-path.js +0 -60
- package/dist/runtimes/claude.js +0 -51
- package/dist/runtimes/codex-app-server-runner.js +0 -340
- package/dist/runtimes/codex-deepseek-catalog.js +0 -7
- package/dist/runtimes/codex-deepseek-config.js +0 -50
- package/dist/runtimes/codex.js +0 -53
- package/dist/runtimes/kimi-acp-runner.js +0 -364
- package/dist/runtimes/kimi.js +0 -45
- package/dist/runtimes/progress-watchdog.js +0 -26
- package/dist/scheduled-report.js +0 -51
- package/dist/scheduled-run-report.js +0 -57
- package/dist/serve-lifecycle.js +0 -82
- package/dist/serve.js +0 -868
- package/dist/session.js +0 -82
- package/dist/shared-execution-slots.js +0 -68
- package/dist/shutdown-deadline.js +0 -32
- package/dist/skill-preview.js +0 -21
- package/dist/skills.js +0 -56
- package/dist/slog.js +0 -228
- package/dist/supervised-runtime.js +0 -104
- package/dist/token.js +0 -24
- package/dist/unified-diff.js +0 -84
- package/dist/websocket-shutdown.js +0 -53
- package/dist/win32-job-object.js +0 -193
- package/dist/workspace-fs.js +0 -80
- package/dist/workspace-import.js +0 -127
- package/dist/workspace.js +0 -148
package/dist/local-executor.js
DELETED
|
@@ -1,439 +0,0 @@
|
|
|
1
|
-
import { createInterface } from "node:readline";
|
|
2
|
-
import { rm, writeFile } from "node:fs/promises";
|
|
3
|
-
import { delimiter, join } from "node:path";
|
|
4
|
-
import { prepareWorkspace, rotateAgentSession, } from "./workspace.js";
|
|
5
|
-
import { spawnClaude } from "./runtimes/claude.js";
|
|
6
|
-
import { spawnCodex } from "./runtimes/codex.js";
|
|
7
|
-
import { DEEPSEEK_CODEX_MODEL, DEEPSEEK_CODEX_REASONING_LEVELS, materializeDeepSeekCodexHome, } from "./runtimes/codex-deepseek-config.js";
|
|
8
|
-
import { spawnKimi, KIMI_EFFORT_LEVELS } from "./runtimes/kimi.js";
|
|
9
|
-
import { applyProviderEnv, providerFingerprint } from "./provider-env.js";
|
|
10
|
-
import { augmentedPath } from "./runtime-path.js";
|
|
11
|
-
import { extractFinalText, extractRunMeta, normalizeEvent, parseLine, } from "./normalize.js";
|
|
12
|
-
import { readSession, writeSession, pickResumeId, isNearBudget } from "./session.js";
|
|
13
|
-
import { createConsoleFormatter } from "./console-formatter.js";
|
|
14
|
-
import { capMemoryForInject, capWorkLogForInject } from "./prompt.js";
|
|
15
|
-
import { decodeExternalOutputEvent, extractExternalAnswer, ExternalAnswerDecoder, stripExternalAnswerMarkers, } from "./external-output.js";
|
|
16
|
-
import { cleanupMaterializedAttachments as cleanupAttachments, executionAttachmentDirectory, materializeAttachments, } from "./attachments.js";
|
|
17
|
-
import { routeRuntimeAttachments, runtimeCapability, } from "./runtime-capabilities.js";
|
|
18
|
-
import { awaitWithCancellation, RuntimeCancelledError, } from "./runtime-cancellation.js";
|
|
19
|
-
function truncateUtf8(value, maxBytes) {
|
|
20
|
-
if (maxBytes <= 0)
|
|
21
|
-
return "";
|
|
22
|
-
let bytes = 0;
|
|
23
|
-
let end = 0;
|
|
24
|
-
for (const character of value) {
|
|
25
|
-
const size = Buffer.byteLength(character, "utf8");
|
|
26
|
-
if (bytes + size > maxBytes)
|
|
27
|
-
break;
|
|
28
|
-
bytes += size;
|
|
29
|
-
end += character.length;
|
|
30
|
-
}
|
|
31
|
-
return value.slice(0, end);
|
|
32
|
-
}
|
|
33
|
-
/** v1 server instructions opt in to daemon-owned paths and bounded local workspace context. */
|
|
34
|
-
export function withLocalExecutionFacts(serverPrompt, maxBytes) {
|
|
35
|
-
return ({ workspace, resuming }) => {
|
|
36
|
-
const memory = capMemoryForInject(workspace.memory);
|
|
37
|
-
const workLog = resuming ? "" : capWorkLogForInject(workspace.workLog);
|
|
38
|
-
const localFacts = `\n\n## Local execution facts
|
|
39
|
-
- $CREW_HOME: ${workspace.dir}
|
|
40
|
-
- $CREW_TASK_LOG: ${workspace.workLogPath}${memory
|
|
41
|
-
? `\n\n## Injected MEMORY.md (bounded local context)\n${memory}`
|
|
42
|
-
: ""}${workLog
|
|
43
|
-
? `\n\n## Injected work-log (bounded local context)\n${workLog}`
|
|
44
|
-
: ""}`;
|
|
45
|
-
const remaining = maxBytes - Buffer.byteLength(serverPrompt, "utf8");
|
|
46
|
-
return `${serverPrompt}${truncateUtf8(localFacts, remaining)}`;
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
const STDERR_TAIL_CAP = 2_000;
|
|
50
|
-
const ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
51
|
-
const RESERVED_ENV = new Set([
|
|
52
|
-
"PATH",
|
|
53
|
-
"XDG_CONFIG_HOME",
|
|
54
|
-
"XDG_DATA_HOME",
|
|
55
|
-
"XDG_CACHE_HOME",
|
|
56
|
-
"GH_CONFIG_DIR",
|
|
57
|
-
"CLOUDSDK_CONFIG",
|
|
58
|
-
]);
|
|
59
|
-
export function sanitizeEnvVars(raw) {
|
|
60
|
-
if (!raw)
|
|
61
|
-
return {};
|
|
62
|
-
return Object.fromEntries(Object.entries(raw).filter(([key, value]) => typeof value === "string"
|
|
63
|
-
&& ENV_KEY_RE.test(key)
|
|
64
|
-
&& !key.toUpperCase().startsWith("CREW_")
|
|
65
|
-
&& !RESERVED_ENV.has(key.toUpperCase())));
|
|
66
|
-
}
|
|
67
|
-
export function awaitExit(child) {
|
|
68
|
-
return new Promise((resolve) => {
|
|
69
|
-
let settled = false;
|
|
70
|
-
const settle = (result) => {
|
|
71
|
-
if (settled)
|
|
72
|
-
return;
|
|
73
|
-
settled = true;
|
|
74
|
-
resolve(result);
|
|
75
|
-
};
|
|
76
|
-
child.on("error", (error) => settle({ exitCode: -1, spawnError: error.message }));
|
|
77
|
-
child.on("close", (code, signal) => settle(code === null
|
|
78
|
-
? { exitCode: 128, terminationSignal: signal ?? "unknown" }
|
|
79
|
-
: { exitCode: code }));
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
export function exitActivity(runtime, exitCode, stderrTail) {
|
|
83
|
-
if ((runtime === "codex" || runtime === "kimi" || exitCode === -1) && exitCode !== 0) {
|
|
84
|
-
return {
|
|
85
|
-
kind: "error",
|
|
86
|
-
label: "运行出错",
|
|
87
|
-
detail: `${runtime} exited with code ${exitCode}${stderrTail ? `: ${stderrTail}` : ""}`,
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
if (runtime === "kimi" && exitCode === 0)
|
|
91
|
-
return { kind: "done", label: "本轮结束" };
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
function wrapChild(child) {
|
|
95
|
-
return {
|
|
96
|
-
...(child.pid === undefined ? {} : { pid: child.pid }),
|
|
97
|
-
stdout: child.stdout,
|
|
98
|
-
stderr: child.stderr,
|
|
99
|
-
exit: awaitExit(child),
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
async function launchLegacyRuntime(request) {
|
|
103
|
-
const common = {
|
|
104
|
-
bin: request.bin,
|
|
105
|
-
cwd: request.cwd,
|
|
106
|
-
env: request.env,
|
|
107
|
-
dangerous: request.effectivePermission === "full_access",
|
|
108
|
-
effectivePermission: request.effectivePermission,
|
|
109
|
-
...(request.model === undefined ? {} : { model: request.model }),
|
|
110
|
-
...(request.reasoning === undefined ? {} : { reasoning: request.reasoning }),
|
|
111
|
-
};
|
|
112
|
-
if (request.runtime === "claude") {
|
|
113
|
-
return wrapChild(spawnClaude({
|
|
114
|
-
...common,
|
|
115
|
-
systemPromptPath: request.systemPromptPath,
|
|
116
|
-
wakePrompt: request.wakePrompt,
|
|
117
|
-
...(request.sessionId === undefined ? {} : {
|
|
118
|
-
sessionId: request.sessionId,
|
|
119
|
-
resume: request.resume,
|
|
120
|
-
}),
|
|
121
|
-
}));
|
|
122
|
-
}
|
|
123
|
-
if (request.runtime === "codex") {
|
|
124
|
-
return wrapChild(spawnCodex({
|
|
125
|
-
...common,
|
|
126
|
-
wakePrompt: `${request.systemPrompt}\n\n${request.wakePrompt}`,
|
|
127
|
-
...(request.imagePaths === undefined ? {} : { imagePaths: request.imagePaths }),
|
|
128
|
-
}));
|
|
129
|
-
}
|
|
130
|
-
return wrapChild(spawnKimi({
|
|
131
|
-
bin: request.bin,
|
|
132
|
-
cwd: request.cwd,
|
|
133
|
-
env: request.env,
|
|
134
|
-
effectivePermission: request.effectivePermission,
|
|
135
|
-
wakePrompt: `${request.systemPrompt}\n\n${request.wakePrompt}`,
|
|
136
|
-
...(request.model === undefined ? {} : { model: request.model }),
|
|
137
|
-
}));
|
|
138
|
-
}
|
|
139
|
-
function resolvePrompt(prompt, context) {
|
|
140
|
-
return typeof prompt === "string" ? prompt : prompt(context);
|
|
141
|
-
}
|
|
142
|
-
const nativeSessionLeaseTails = new Map();
|
|
143
|
-
async function withKeyedLease(key, operation, cancellation) {
|
|
144
|
-
const predecessor = nativeSessionLeaseTails.get(key) ?? Promise.resolve();
|
|
145
|
-
let release;
|
|
146
|
-
const current = new Promise((resolve) => { release = resolve; });
|
|
147
|
-
const tail = predecessor.then(() => current);
|
|
148
|
-
nativeSessionLeaseTails.set(key, tail);
|
|
149
|
-
try {
|
|
150
|
-
await awaitWithCancellation(predecessor, cancellation);
|
|
151
|
-
return await operation();
|
|
152
|
-
}
|
|
153
|
-
finally {
|
|
154
|
-
release();
|
|
155
|
-
void tail.then(() => {
|
|
156
|
-
if (nativeSessionLeaseTails.get(key) === tail)
|
|
157
|
-
nativeSessionLeaseTails.delete(key);
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
export async function executeLocal(input, callbacks = {}, dependencies = {}) {
|
|
162
|
-
const stableProtocolRuntime = dependencies.launchRuntime !== undefined;
|
|
163
|
-
const supportsNativeResume = input.runtime.name === "claude"
|
|
164
|
-
|| (stableProtocolRuntime && runtimeCapability(input.runtime.name).nativeResume);
|
|
165
|
-
if (!supportsNativeResume || !input.session.enabled) {
|
|
166
|
-
return executeLocalUnlocked(input, callbacks, dependencies);
|
|
167
|
-
}
|
|
168
|
-
const leaseKey = JSON.stringify([
|
|
169
|
-
input.launch.agentsRoot,
|
|
170
|
-
input.handle,
|
|
171
|
-
input.keyMode ?? "legacy",
|
|
172
|
-
input.resumeKey ?? input.taskKey ?? "",
|
|
173
|
-
]);
|
|
174
|
-
return withKeyedLease(leaseKey, () => executeLocalUnlocked(input, callbacks, dependencies), dependencies.cancellation);
|
|
175
|
-
}
|
|
176
|
-
async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
177
|
-
const providerConfig = input.launch.providerConfig ?? {};
|
|
178
|
-
const { runtime } = input;
|
|
179
|
-
const isDeepSeekCodex = runtime.name === "codex" && providerConfig.provider === "deepseek";
|
|
180
|
-
const currentModel = isDeepSeekCodex ? DEEPSEEK_CODEX_MODEL : runtime.model ?? null;
|
|
181
|
-
const launchModel = isDeepSeekCodex ? DEEPSEEK_CODEX_MODEL : runtime.model;
|
|
182
|
-
const launchReasoning = isDeepSeekCodex
|
|
183
|
-
? runtime.reasoning && DEEPSEEK_CODEX_REASONING_LEVELS
|
|
184
|
-
.includes(runtime.reasoning)
|
|
185
|
-
? runtime.reasoning
|
|
186
|
-
: "high"
|
|
187
|
-
: runtime.reasoning;
|
|
188
|
-
const providerFp = providerFingerprint(runtime.name, providerConfig);
|
|
189
|
-
const workspace = await awaitWithCancellation(prepareWorkspace({
|
|
190
|
-
agentsRoot: input.launch.agentsRoot,
|
|
191
|
-
handle: input.handle,
|
|
192
|
-
cliPath: input.launch.cliPath,
|
|
193
|
-
executionId: input.executionId,
|
|
194
|
-
...(input.keyMode === undefined ? {} : { keyMode: input.keyMode }),
|
|
195
|
-
...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }),
|
|
196
|
-
...(input.resumeKey === undefined ? {} : { resumeKey: input.resumeKey }),
|
|
197
|
-
...(input.launch.description ? { description: input.launch.description } : {}),
|
|
198
|
-
}), dependencies.cancellation);
|
|
199
|
-
let materialized = null;
|
|
200
|
-
let knownAttachmentDirectory = null;
|
|
201
|
-
try {
|
|
202
|
-
if (isDeepSeekCodex && !providerConfig.providerApiKey) {
|
|
203
|
-
throw new Error("DeepSeek API key is not configured for this Agent");
|
|
204
|
-
}
|
|
205
|
-
if (isDeepSeekCodex) {
|
|
206
|
-
await awaitWithCancellation((dependencies.materializeDeepSeekCodexHome ?? materializeDeepSeekCodexHome)(workspace.homeDir), dependencies.cancellation);
|
|
207
|
-
}
|
|
208
|
-
const supportsNativeResume = runtime.name === "claude"
|
|
209
|
-
|| (dependencies.launchRuntime !== undefined && runtimeCapability(runtime.name).nativeResume);
|
|
210
|
-
const prior = input.session.enabled && supportsNativeResume
|
|
211
|
-
? await readSession(workspace.sessionDir)
|
|
212
|
-
: null;
|
|
213
|
-
const resumeSessionId = supportsNativeResume && workspace.sessionResume
|
|
214
|
-
? pickResumeId(prior, Date.now(), input.session.warmMs, currentModel, input.session.budgetTokens, input.session.maxTurns, providerFp)
|
|
215
|
-
: null;
|
|
216
|
-
const resuming = resumeSessionId !== null;
|
|
217
|
-
const rotatedForBudget = !resuming && supportsNativeResume && workspace.sessionResume
|
|
218
|
-
&& pickResumeId(prior, Date.now(), input.session.warmMs, currentModel, 0, 0, providerFp) !== null;
|
|
219
|
-
const nearBudget = resuming && isNearBudget(prior, input.session.softTokens);
|
|
220
|
-
const rotated = Boolean(workspace.agentSessionId && workspace.sessionResume && !resuming);
|
|
221
|
-
// Codex and Kimi choose their own id on a fresh start, so the native id persisted in
|
|
222
|
-
// .crew-session.json can differ from the bootstrap id in .session. Resume the former.
|
|
223
|
-
const launchSessionId = resumeSessionId ?? (rotated
|
|
224
|
-
? await rotateAgentSession(workspace.sessionDir)
|
|
225
|
-
: workspace.agentSessionId);
|
|
226
|
-
const promptContext = {
|
|
227
|
-
workspace,
|
|
228
|
-
resuming,
|
|
229
|
-
rotatedForBudget,
|
|
230
|
-
nearBudget,
|
|
231
|
-
};
|
|
232
|
-
const systemPrompt = resolvePrompt(input.systemPrompt, promptContext);
|
|
233
|
-
if (input.attachments && input.attachments.length > 0) {
|
|
234
|
-
if (dependencies.cancellation?.isRequested())
|
|
235
|
-
throw new RuntimeCancelledError();
|
|
236
|
-
knownAttachmentDirectory = executionAttachmentDirectory(workspace.runDir, input.executionId);
|
|
237
|
-
const controller = new AbortController();
|
|
238
|
-
const materialization = Promise.resolve().then(() => (dependencies.materializeAttachments ?? materializeAttachments)({
|
|
239
|
-
serverUrl: input.launch.serverUrl,
|
|
240
|
-
token: input.launch.token,
|
|
241
|
-
runDir: workspace.runDir,
|
|
242
|
-
executionId: input.executionId,
|
|
243
|
-
attachments: input.attachments,
|
|
244
|
-
signal: controller.signal,
|
|
245
|
-
}));
|
|
246
|
-
dependencies.cancellation?.register(async () => {
|
|
247
|
-
controller.abort(new RuntimeCancelledError());
|
|
248
|
-
await materialization.then(() => undefined, () => undefined);
|
|
249
|
-
});
|
|
250
|
-
try {
|
|
251
|
-
materialized = await materialization;
|
|
252
|
-
}
|
|
253
|
-
catch (error) {
|
|
254
|
-
if (dependencies.cancellation?.isRequested())
|
|
255
|
-
throw new RuntimeCancelledError();
|
|
256
|
-
throw error;
|
|
257
|
-
}
|
|
258
|
-
if (dependencies.cancellation?.isRequested()) {
|
|
259
|
-
await dependencies.cancellation.waitForStop();
|
|
260
|
-
throw new RuntimeCancelledError();
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
const attachmentPlan = routeRuntimeAttachments(runtime.name, materialized?.attachments ?? []);
|
|
264
|
-
const wakePrompt = `${resolvePrompt(input.wakePrompt, promptContext)}${attachmentPlan.promptSuffix}`;
|
|
265
|
-
await awaitWithCancellation(writeFile(workspace.systemPromptPath, systemPrompt, "utf8"), dependencies.cancellation);
|
|
266
|
-
const baseEnv = {
|
|
267
|
-
...process.env,
|
|
268
|
-
...sanitizeEnvVars(providerConfig.envVars),
|
|
269
|
-
...input.launch.systemEnv,
|
|
270
|
-
PATH: `${workspace.crewDir}${delimiter}${augmentedPath()}`,
|
|
271
|
-
CREW_SERVER_URL: input.launch.serverUrl,
|
|
272
|
-
CREW_TOKEN: input.launch.token,
|
|
273
|
-
CREW_CHANNEL: input.channelId,
|
|
274
|
-
CREW_HOME: workspace.dir,
|
|
275
|
-
CREW_TASK_LOG: workspace.workLogPath,
|
|
276
|
-
...(input.wakeMessageId ? { CREW_WAKE_MESSAGE_ID: input.wakeMessageId } : {}),
|
|
277
|
-
XDG_CONFIG_HOME: join(workspace.homeDir, ".config"),
|
|
278
|
-
XDG_DATA_HOME: join(workspace.homeDir, ".local", "share"),
|
|
279
|
-
XDG_CACHE_HOME: join(workspace.homeDir, ".cache"),
|
|
280
|
-
GH_CONFIG_DIR: join(workspace.homeDir, ".config", "gh"),
|
|
281
|
-
CLOUDSDK_CONFIG: join(workspace.homeDir, ".config", "gcloud"),
|
|
282
|
-
...(runtime.name === "kimi" && runtime.reasoning
|
|
283
|
-
&& KIMI_EFFORT_LEVELS.includes(runtime.reasoning)
|
|
284
|
-
? { KIMI_MODEL_THINKING_EFFORT: runtime.reasoning }
|
|
285
|
-
: {}),
|
|
286
|
-
};
|
|
287
|
-
const childEnv = applyProviderEnv(baseEnv, runtime.name, providerConfig, workspace.homeDir);
|
|
288
|
-
const launchRuntime = dependencies.launchRuntime ?? launchLegacyRuntime;
|
|
289
|
-
if (dependencies.cancellation?.isRequested())
|
|
290
|
-
throw new RuntimeCancelledError();
|
|
291
|
-
const child = await launchRuntime({
|
|
292
|
-
runtime: runtime.name,
|
|
293
|
-
bin: runtime.name,
|
|
294
|
-
cwd: workspace.runDir,
|
|
295
|
-
systemPromptPath: workspace.systemPromptPath,
|
|
296
|
-
systemPrompt,
|
|
297
|
-
wakePrompt,
|
|
298
|
-
env: childEnv,
|
|
299
|
-
effectivePermission: input.effectivePermission,
|
|
300
|
-
...(launchModel === undefined ? {} : { model: launchModel }),
|
|
301
|
-
...(launchReasoning === undefined ? {} : { reasoning: launchReasoning }),
|
|
302
|
-
...(launchSessionId === null ? {} : { sessionId: launchSessionId }),
|
|
303
|
-
resume: resuming,
|
|
304
|
-
...(attachmentPlan.nativeImagePaths.length > 0
|
|
305
|
-
? { imagePaths: attachmentPlan.nativeImagePaths }
|
|
306
|
-
: {}),
|
|
307
|
-
});
|
|
308
|
-
if (child.cancel !== undefined) {
|
|
309
|
-
dependencies.cancellation?.register(child.cancel);
|
|
310
|
-
}
|
|
311
|
-
if (dependencies.cancellation?.isRequested()) {
|
|
312
|
-
await dependencies.cancellation.waitForStop();
|
|
313
|
-
throw new RuntimeCancelledError();
|
|
314
|
-
}
|
|
315
|
-
const activities = [];
|
|
316
|
-
let sessionId = launchSessionId;
|
|
317
|
-
let usage;
|
|
318
|
-
let observedModel = currentModel;
|
|
319
|
-
let finalText = null;
|
|
320
|
-
let sentViaCrew = false;
|
|
321
|
-
const externalOutput = new ExternalAnswerDecoder();
|
|
322
|
-
// 每轮独立:tool_use/result 关联状态不能跨 execution 泄漏。
|
|
323
|
-
const consoleFormatter = createConsoleFormatter();
|
|
324
|
-
const readline = createInterface({ input: child.stdout });
|
|
325
|
-
readline.on("line", (line) => {
|
|
326
|
-
const event = parseLine(line);
|
|
327
|
-
if (!event)
|
|
328
|
-
return;
|
|
329
|
-
const meta = extractRunMeta(event);
|
|
330
|
-
if (meta.sessionId)
|
|
331
|
-
sessionId = meta.sessionId;
|
|
332
|
-
if (meta.usage)
|
|
333
|
-
usage = meta.usage;
|
|
334
|
-
if (meta.model)
|
|
335
|
-
observedModel = meta.model;
|
|
336
|
-
for (const activity of normalizeEvent(event)) {
|
|
337
|
-
if (activity.kind === "sending")
|
|
338
|
-
sentViaCrew = true;
|
|
339
|
-
activities.push(activity);
|
|
340
|
-
callbacks.onActivity?.(activity);
|
|
341
|
-
}
|
|
342
|
-
const extracted = extractFinalText(event);
|
|
343
|
-
if (extracted) {
|
|
344
|
-
const incremental = typeof event === "object" && event !== null
|
|
345
|
-
&& "type" in event && event.type === "kimi.acp.text_delta";
|
|
346
|
-
finalText = incremental ? `${finalText ?? ""}${extracted}` : extracted;
|
|
347
|
-
}
|
|
348
|
-
for (const text of decodeExternalOutputEvent(runtime.name, event, externalOutput)) {
|
|
349
|
-
callbacks.onExternalOutput?.(text);
|
|
350
|
-
}
|
|
351
|
-
for (const chunk of consoleFormatter.format(event))
|
|
352
|
-
callbacks.onConsole?.(chunk);
|
|
353
|
-
});
|
|
354
|
-
let stderrTail = "";
|
|
355
|
-
child.stderr.on("data", (data) => {
|
|
356
|
-
process.stderr.write(data);
|
|
357
|
-
stderrTail = (stderrTail + String(data)).slice(-STDERR_TAIL_CAP);
|
|
358
|
-
});
|
|
359
|
-
let runtimeExit;
|
|
360
|
-
try {
|
|
361
|
-
runtimeExit = await awaitWithCancellation(child.exit, dependencies.cancellation);
|
|
362
|
-
}
|
|
363
|
-
catch (error) {
|
|
364
|
-
if (error instanceof RuntimeCancelledError) {
|
|
365
|
-
await dependencies.cancellation?.waitForStop();
|
|
366
|
-
}
|
|
367
|
-
throw error;
|
|
368
|
-
}
|
|
369
|
-
const { exitCode, spawnError, terminationSignal } = runtimeExit;
|
|
370
|
-
const errorTail = [
|
|
371
|
-
stderrTail.trim(),
|
|
372
|
-
spawnError,
|
|
373
|
-
terminationSignal ? `terminated by ${terminationSignal}` : undefined,
|
|
374
|
-
].filter(Boolean).join(" ").trim();
|
|
375
|
-
const finish = exitActivity(runtime.name, exitCode, errorTail);
|
|
376
|
-
if (finish) {
|
|
377
|
-
activities.push(finish);
|
|
378
|
-
callbacks.onActivity?.(finish);
|
|
379
|
-
if (finish.kind === "error") {
|
|
380
|
-
callbacks.onConsole?.({ stream: "error", text: `✖ ${finish.detail ?? finish.label}` });
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
if (input.session.enabled && supportsNativeResume && sessionId) {
|
|
384
|
-
const contextTokens = usage
|
|
385
|
-
? usage.inputTokens + usage.cacheReadTokens + usage.cacheCreationTokens
|
|
386
|
-
: (resuming ? prior?.contextTokens : undefined);
|
|
387
|
-
await writeSession(workspace.sessionDir, {
|
|
388
|
-
sessionId,
|
|
389
|
-
lastRunAt: Date.now(),
|
|
390
|
-
turns: resuming ? (prior?.turns ?? 0) + 1 : 1,
|
|
391
|
-
model: currentModel,
|
|
392
|
-
providerFingerprint: providerFp,
|
|
393
|
-
lastExitOk: exitCode === 0,
|
|
394
|
-
...(contextTokens === undefined ? {} : { contextTokens }),
|
|
395
|
-
});
|
|
396
|
-
}
|
|
397
|
-
return {
|
|
398
|
-
workspaceRunDir: workspace.runDir,
|
|
399
|
-
exitCode,
|
|
400
|
-
...(terminationSignal === undefined ? {} : { terminationSignal }),
|
|
401
|
-
activities,
|
|
402
|
-
...(usage === undefined ? {} : { usage }),
|
|
403
|
-
model: observedModel,
|
|
404
|
-
runtime: runtime.name,
|
|
405
|
-
resumed: resuming,
|
|
406
|
-
sessionId,
|
|
407
|
-
errorMessage: errorTail || null,
|
|
408
|
-
finalText: input.captureFinal && finalText !== null
|
|
409
|
-
? stripExternalAnswerMarkers(finalText)
|
|
410
|
-
: null,
|
|
411
|
-
externalAnswer: input.captureFinal && finalText !== null
|
|
412
|
-
? extractExternalAnswer(finalText)
|
|
413
|
-
: null,
|
|
414
|
-
sentViaCrew,
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
finally {
|
|
418
|
-
const attachmentDirectories = new Set([
|
|
419
|
-
...(knownAttachmentDirectory === null ? [] : [knownAttachmentDirectory]),
|
|
420
|
-
...(materialized === null ? [] : [materialized.directory]),
|
|
421
|
-
]);
|
|
422
|
-
for (const directory of attachmentDirectories) {
|
|
423
|
-
try {
|
|
424
|
-
await (dependencies.cleanupMaterializedAttachments ?? cleanupAttachments)(directory);
|
|
425
|
-
}
|
|
426
|
-
catch (error) {
|
|
427
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
428
|
-
process.stderr.write(`[execution] failed to remove attachments ${directory}: ${detail}\n`);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
try {
|
|
432
|
-
await rm(workspace.systemPromptPath, { force: true });
|
|
433
|
-
}
|
|
434
|
-
catch (error) {
|
|
435
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
436
|
-
process.stderr.write(`[execution] failed to remove prompt ${workspace.systemPromptPath}: ${detail}\n`);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
package/dist/log-format.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export function formatDaemonLogLine(message, date = new Date(), offsetMinutes = date.getTimezoneOffset()) {
|
|
2
|
-
const local = new Date(date.getTime() - offsetMinutes * 60_000);
|
|
3
|
-
const pad = (value, width = 2) => String(value).padStart(width, "0");
|
|
4
|
-
const sign = offsetMinutes <= 0 ? "+" : "-";
|
|
5
|
-
const offset = Math.abs(offsetMinutes);
|
|
6
|
-
const timestamp = `${local.getUTCFullYear()}-${pad(local.getUTCMonth() + 1)}-${pad(local.getUTCDate())}` +
|
|
7
|
-
` ${pad(local.getUTCHours())}:${pad(local.getUTCMinutes())}:${pad(local.getUTCSeconds())}.${pad(local.getUTCMilliseconds(), 3)}` +
|
|
8
|
-
` ${sign}${pad(Math.floor(offset / 60))}:${pad(offset % 60)}`;
|
|
9
|
-
return `[${timestamp}] ${message}`;
|
|
10
|
-
}
|
package/dist/machine-info.js
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 采集本机信息上报给控制面 (machine:hello):hostname / os / daemon 版本 / 已装 runtimes。
|
|
3
|
-
* runtimes 探测靠 `which <bin>`(win32 用 `where`),只报真实可执行的 CLI(用于展示 Detected Runtimes)。
|
|
4
|
-
*/
|
|
5
|
-
import { hostname, arch, platform as osPlatform } from "node:os";
|
|
6
|
-
import { lookupCmd } from "./platform.js";
|
|
7
|
-
import { augmentedPath } from "./runtime-path.js";
|
|
8
|
-
import { execFile } from "node:child_process";
|
|
9
|
-
import { promisify } from "node:util";
|
|
10
|
-
import { readFileSync } from "node:fs";
|
|
11
|
-
import { readdir } from "node:fs/promises";
|
|
12
|
-
import { fileURLToPath } from "node:url";
|
|
13
|
-
import { createRequire } from "node:module";
|
|
14
|
-
import { dirname, resolve } from "node:path";
|
|
15
|
-
import { executableRuntimes } from "./runtime-capabilities.js";
|
|
16
|
-
import { executionBackendCapability } from "./execution-backend.js";
|
|
17
|
-
import { probeKimiAcp } from "./runtimes/kimi-acp-runner.js";
|
|
18
|
-
export { executableRuntimes } from "./runtime-capabilities.js";
|
|
19
|
-
const execFileP = promisify(execFile);
|
|
20
|
-
export const DAEMON_CAPABILITIES = [
|
|
21
|
-
"scheduled_job_v1",
|
|
22
|
-
"reply_origin_v1",
|
|
23
|
-
"origin_decision_v1",
|
|
24
|
-
"execution_telemetry_ack_v1",
|
|
25
|
-
"execution_external_output_v1",
|
|
26
|
-
"execution_attachments_v1",
|
|
27
|
-
"execution_answer_stream_v1",
|
|
28
|
-
];
|
|
29
|
-
export const EXECUTION_PROTOCOL = Object.freeze({ min: 1, max: 1 });
|
|
30
|
-
/** 候选 runtime CLI:展示名 → 可执行文件名。 */
|
|
31
|
-
const RUNTIME_BINS = [
|
|
32
|
-
["claude", "claude"],
|
|
33
|
-
["codex", "codex"],
|
|
34
|
-
["cursor", "cursor-agent"],
|
|
35
|
-
["gemini", "gemini"],
|
|
36
|
-
["opencode", "opencode"],
|
|
37
|
-
["copilot", "copilot"],
|
|
38
|
-
["kimi", "kimi"],
|
|
39
|
-
["pi", "pi"],
|
|
40
|
-
];
|
|
41
|
-
async function isInstalled(bin) {
|
|
42
|
-
try {
|
|
43
|
-
// 用增强 PATH 探测:daemon 继承的 PATH 可能缺 ~/.local/bin 等用户级目录(Claude 原生安装器落点)。
|
|
44
|
-
await execFileP(lookupCmd(), [bin], { env: { ...process.env, PATH: augmentedPath() } });
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
/** 并发探测所有候选 runtime,返回已安装的展示名列表。 */
|
|
52
|
-
export async function detectRuntimes() {
|
|
53
|
-
const checks = await Promise.all(RUNTIME_BINS.map(async ([name, bin]) => ((await isInstalled(bin)) ? name : null)));
|
|
54
|
-
return checks.filter((x) => x !== null);
|
|
55
|
-
}
|
|
56
|
-
async function supportsKimiAcp() {
|
|
57
|
-
return probeKimiAcp({ bin: "kimi" });
|
|
58
|
-
}
|
|
59
|
-
/** Runtime adapters that can satisfy the durable protocol-v1 process contract. */
|
|
60
|
-
export async function detectExecutionRuntimes(installed = detectRuntimes(), kimiAcpProbe = supportsKimiAcp) {
|
|
61
|
-
const present = await installed;
|
|
62
|
-
const supported = executableRuntimes(present).filter((runtime) => runtime !== "kimi");
|
|
63
|
-
if (present.includes("kimi") && await kimiAcpProbe())
|
|
64
|
-
supported.push("kimi");
|
|
65
|
-
return supported;
|
|
66
|
-
}
|
|
67
|
-
export function daemonVersion() {
|
|
68
|
-
try {
|
|
69
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
70
|
-
const pkg = JSON.parse(readFileSync(resolve(here, "../package.json"), "utf8"));
|
|
71
|
-
return pkg.version ?? "0.0.0";
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
return "0.0.0";
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
/** 读取同装的 @nowcrew/cli 版本(daemon 的依赖,npx 场景与 daemon 一起安装);解析失败返回 "0.0.0"。 */
|
|
78
|
-
export function cliVersion() {
|
|
79
|
-
try {
|
|
80
|
-
const req = createRequire(import.meta.url);
|
|
81
|
-
const pkg = JSON.parse(readFileSync(req.resolve("@nowcrew/cli/package.json"), "utf8"));
|
|
82
|
-
return pkg.version ?? "0.0.0";
|
|
83
|
-
}
|
|
84
|
-
catch {
|
|
85
|
-
return "0.0.0";
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
/** 列出 agentsRoot 下的 agent handle(每个子目录 = 一个 agent),跳过隐藏目录;读不到则返回 []。 */
|
|
89
|
-
async function listAgentHandles(agentsRoot) {
|
|
90
|
-
try {
|
|
91
|
-
const entries = await readdir(agentsRoot, { withFileTypes: true });
|
|
92
|
-
return entries
|
|
93
|
-
.filter((e) => e.isDirectory() && !e.name.startsWith("."))
|
|
94
|
-
.map((e) => e.name.trim().toLowerCase())
|
|
95
|
-
.filter((h) => h.length > 0);
|
|
96
|
-
}
|
|
97
|
-
catch {
|
|
98
|
-
return [];
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
export async function collectMachineHello(agentsRoot, executionLimits, runtimePlatform = process.platform, dependencies = {}) {
|
|
102
|
-
const [runtimes, agentHandles] = await Promise.all([
|
|
103
|
-
(dependencies.detectInstalled ?? detectRuntimes)(),
|
|
104
|
-
listAgentHandles(agentsRoot),
|
|
105
|
-
]);
|
|
106
|
-
const backend = executionBackendCapability(runtimePlatform, dependencies.jobObjectProbe);
|
|
107
|
-
const executionRuntimes = backend.supported
|
|
108
|
-
? await (dependencies.detectExecutable ?? detectExecutionRuntimes)(runtimes)
|
|
109
|
-
: [];
|
|
110
|
-
return {
|
|
111
|
-
type: "machine:hello",
|
|
112
|
-
hostname: hostname(),
|
|
113
|
-
os: `${osPlatform()} ${arch()}`,
|
|
114
|
-
daemonVersion: daemonVersion(),
|
|
115
|
-
runtimes,
|
|
116
|
-
executionRuntimes,
|
|
117
|
-
capabilities: DAEMON_CAPABILITIES,
|
|
118
|
-
...(backend.supported ? {
|
|
119
|
-
executionProtocol: EXECUTION_PROTOCOL,
|
|
120
|
-
executionLimits: Object.freeze({ ...executionLimits }),
|
|
121
|
-
} : {}),
|
|
122
|
-
agentHandles,
|
|
123
|
-
};
|
|
124
|
-
}
|