@nowcrew/daemon 0.5.27 → 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.
Files changed (77) hide show
  1. package/package.json +2 -2
  2. package/dist/attachments.js +0 -196
  3. package/dist/bound-im-decision.js +0 -22
  4. package/dist/completion-retransmitter.js +0 -77
  5. package/dist/computer-cli.js +0 -274
  6. package/dist/computer-profile-lock.js +0 -395
  7. package/dist/computer-profile.js +0 -364
  8. package/dist/computer-service.js +0 -358
  9. package/dist/config.js +0 -82
  10. package/dist/console-collapse.js +0 -13
  11. package/dist/console-formatter.js +0 -77
  12. package/dist/console-payload.js +0 -73
  13. package/dist/console.js +0 -329
  14. package/dist/daemon-startup-error.js +0 -30
  15. package/dist/execution-backend.js +0 -44
  16. package/dist/execution-event-limit.js +0 -64
  17. package/dist/execution-journal-lock.js +0 -421
  18. package/dist/execution-journal.js +0 -716
  19. package/dist/execution-protocol.js +0 -342
  20. package/dist/execution-recovery.js +0 -95
  21. package/dist/execution-runner.js +0 -659
  22. package/dist/execution-supervisor-child.js +0 -236
  23. package/dist/execution-supervisor.js +0 -316
  24. package/dist/execution-telemetry-journal.js +0 -71
  25. package/dist/external-output.js +0 -114
  26. package/dist/i18n.js +0 -64
  27. package/dist/json-result.js +0 -27
  28. package/dist/list-models.js +0 -92
  29. package/dist/local-executor.js +0 -439
  30. package/dist/log-format.js +0 -10
  31. package/dist/machine-info.js +0 -124
  32. package/dist/main.js +0 -118
  33. package/dist/normalize.js +0 -170
  34. package/dist/origin-decision.js +0 -44
  35. package/dist/platform.js +0 -8
  36. package/dist/prompt.js +0 -307
  37. package/dist/provider-env.js +0 -90
  38. package/dist/remote/claude-bridge.js +0 -402
  39. package/dist/remote/claude-channel.js +0 -164
  40. package/dist/remote/codex-client.js +0 -408
  41. package/dist/remote/codex-runtime.js +0 -77
  42. package/dist/remote/config.js +0 -83
  43. package/dist/remote/gateway.js +0 -572
  44. package/dist/remote/protocol.js +0 -178
  45. package/dist/remote/remote-cli.js +0 -233
  46. package/dist/remote/session-discovery.js +0 -249
  47. package/dist/remote/wrapper.js +0 -40
  48. package/dist/runner.js +0 -234
  49. package/dist/runtime-cancellation.js +0 -74
  50. package/dist/runtime-capabilities.js +0 -43
  51. package/dist/runtime-path.js +0 -60
  52. package/dist/runtimes/claude.js +0 -51
  53. package/dist/runtimes/codex-app-server-runner.js +0 -344
  54. package/dist/runtimes/codex-deepseek-catalog.js +0 -7
  55. package/dist/runtimes/codex-deepseek-config.js +0 -50
  56. package/dist/runtimes/codex.js +0 -53
  57. package/dist/runtimes/kimi-acp-runner.js +0 -364
  58. package/dist/runtimes/kimi.js +0 -45
  59. package/dist/runtimes/progress-watchdog.js +0 -26
  60. package/dist/scheduled-report.js +0 -51
  61. package/dist/scheduled-run-report.js +0 -57
  62. package/dist/serve-lifecycle.js +0 -82
  63. package/dist/serve.js +0 -868
  64. package/dist/session.js +0 -82
  65. package/dist/shared-execution-slots.js +0 -68
  66. package/dist/shutdown-deadline.js +0 -32
  67. package/dist/skill-preview.js +0 -21
  68. package/dist/skills.js +0 -56
  69. package/dist/slog.js +0 -228
  70. package/dist/supervised-runtime.js +0 -104
  71. package/dist/token.js +0 -24
  72. package/dist/unified-diff.js +0 -84
  73. package/dist/websocket-shutdown.js +0 -53
  74. package/dist/win32-job-object.js +0 -193
  75. package/dist/workspace-fs.js +0 -80
  76. package/dist/workspace-import.js +0 -127
  77. package/dist/workspace.js +0 -148
@@ -1,40 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
3
- function optionValue(args, long, short) {
4
- for (let index = 0; index < args.length; index += 1) {
5
- const value = args[index];
6
- if (value.startsWith(`${long}=`))
7
- return value.slice(long.length + 1);
8
- if (value === long || (short && value === short)) {
9
- const next = args[index + 1];
10
- return next && !next.startsWith("-") ? next : null;
11
- }
12
- }
13
- return null;
14
- }
15
- export function resolveClaudeWrapperSession(args, createId = randomUUID) {
16
- if (args.includes("--continue") || args.includes("-c")) {
17
- throw new Error("NowCrew remote requires an explicit session ID; use --resume <session-id> instead of --continue");
18
- }
19
- if (args.includes("--fork-session")) {
20
- throw new Error("NowCrew remote cannot identify a fork chosen inside Claude; start a new session or resume without --fork-session");
21
- }
22
- const sessionId = optionValue(args, "--session-id") ?? optionValue(args, "--resume", "-r") ?? createId();
23
- if (!UUID.test(sessionId)) {
24
- throw new Error("Claude remote sessions require a UUID in --session-id or --resume");
25
- }
26
- const hasSessionSelector = args.some((value) => value === "--session-id" || value.startsWith("--session-id=")
27
- || value === "--resume" || value.startsWith("--resume=") || value === "-r");
28
- return {
29
- sessionId,
30
- args: hasSessionSelector ? [...args] : ["--session-id", sessionId, ...args],
31
- };
32
- }
33
- export function buildCodexWrappedInvocation(userArgs, socketPath = "") {
34
- const hasRemote = userArgs.some((value) => value === "--remote" || value.startsWith("--remote="));
35
- return {
36
- bin: "codex",
37
- args: hasRemote ? [...userArgs] : ["--remote", socketPath ? `unix://${socketPath}` : "unix://", ...userArgs],
38
- env: process.env,
39
- };
40
- }
package/dist/runner.js DELETED
@@ -1,234 +0,0 @@
1
- /** Legacy agent orchestration around the business-neutral local executor. */
2
- import { randomUUID } from "node:crypto";
3
- import { join } from "node:path";
4
- import { mintAgentToken } from "./token.js";
5
- import { buildSystemPrompt, buildWakePrompt, capMemoryForInject, capWorkLogForInject } from "./prompt.js";
6
- import { deliverScheduledReport, } from "./scheduled-report.js";
7
- import { executeLocal, } from "./local-executor.js";
8
- import { ReasoningSchema } from "./execution-protocol.js";
9
- import { readOriginDecisionFile, resetOriginDecisionFile } from "./origin-decision.js";
10
- import { readBoundImDecisionFile, resetBoundImDecisionFile } from "./bound-im-decision.js";
11
- import { launchSupervisedRuntime } from "./supervised-runtime.js";
12
- import { awaitWithCancellation, } from "./runtime-cancellation.js";
13
- export { awaitExit, exitActivity, sanitizeEnvVars } from "./local-executor.js";
14
- const ICON = {
15
- init: "🟢", text: "💬", reading: "📖", sending: "📨", checking: "🔎",
16
- claiming: "📌", crew: "⚙️", tool: "🛠️", tool_result: "↩️", done: "✅", error: "❌",
17
- };
18
- function runtimeName(value) {
19
- if (value === "claude" || value === "codex" || value === "kimi")
20
- return value;
21
- throw new Error(`unsupported runtime: ${value}`);
22
- }
23
- export async function runAgent(config, input, onActivity = defaultPrint, onConsole = () => { }, dependencies = {}) {
24
- const credential = await awaitWithCancellation((dependencies.mintAgentToken ?? mintAgentToken)(config.serverUrl, config.machineToken, input.handle, input.displayName, {
25
- ...(input.wakeMessageId ? { wakeThreadRoot: input.wakeMessageId } : {}),
26
- ...(input.wakeContextUpToSeq === undefined ? {} : { wakeContextUpToSeq: input.wakeContextUpToSeq }),
27
- ...(input.runId ? { agentRunId: input.runId } : {}),
28
- }), dependencies.cancellation);
29
- const providerConfig = credential.config ?? {};
30
- const runtime = runtimeName(providerConfig.runtime ?? config.runtimeBin);
31
- const reasoning = ReasoningSchema.safeParse(providerConfig.reasoning);
32
- const baseWake = input.wake ?? buildWakePrompt(input.channelId);
33
- const executionId = input.runId ?? `legacy-${randomUUID()}`;
34
- const originDecisionFileName = input.wakeOrigin
35
- ? `.origin-decision-${executionId}.json`
36
- : null;
37
- const boundImDecisionFileName = input.scheduled?.externalNotificationPolicy === "agent_decides"
38
- ? `.bound-im-decision-${executionId}.json`
39
- : null;
40
- const local = await (dependencies.executeLocal ?? executeLocal)({
41
- executionId,
42
- handle: input.handle,
43
- channelId: input.channelId,
44
- ...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }),
45
- ...(input.wakeMessageId === undefined ? {} : { wakeMessageId: input.wakeMessageId }),
46
- systemPrompt: ({ workspace, resuming }) => buildSystemPrompt({
47
- handle: input.handle,
48
- channelId: input.channelId,
49
- agentId: credential.agentId,
50
- homeDir: workspace.dir,
51
- productName: config.productName,
52
- platform: process.platform,
53
- ...(input.scheduled ? { scheduledOutputPolicy: input.scheduled.outputPolicy } : {}),
54
- ...(input.scheduled?.externalNotificationPolicy
55
- ? { scheduledExternalNotificationPolicy: input.scheduled.externalNotificationPolicy }
56
- : {}),
57
- ...(input.wakeOrigin ? { wakeOrigin: input.wakeOrigin } : {}),
58
- memory: capMemoryForInject(workspace.memory),
59
- ...(resuming ? {} : { workLog: capWorkLogForInject(workspace.workLog) }),
60
- }),
61
- wakePrompt: ({ resuming, rotatedForBudget, nearBudget }) => {
62
- const distillHint = nearBudget
63
- ? "\n(注意:本会话上下文已接近预算,稍后将轮换重启。本轮结束前,把当前状态——阶段/关键结论/下一步/卡点/关键文件路径——完整更新到 $CREW_TASK_LOG,下轮将以它为基础继续。)"
64
- : "";
65
- if (resuming) {
66
- return `(继续之前的会话:频道历史与你的进度已在上下文里,不必从头重读;要新消息用 \`crew message read --channel ${input.channelId}\` 增量拉即可。)${distillHint}\n\n${baseWake}`;
67
- }
68
- return rotatedForBudget
69
- ? `(上下文已轮换:之前的会话过大已重启。你的 work-log 已注入系统提示词末尾,以它为基础继续;频道/线程历史用 crew 命令按需拉取,不必全量重读。)\n\n${baseWake}`
70
- : baseWake;
71
- },
72
- runtime: {
73
- name: runtime,
74
- ...(providerConfig.model ? { model: providerConfig.model } : {}),
75
- ...(reasoning.success ? { reasoning: reasoning.data } : {}),
76
- },
77
- effectivePermission: config.dangerous ? "full_access" : "workspace_write",
78
- captureFinal: true,
79
- launch: {
80
- serverUrl: config.serverUrl,
81
- token: credential.token,
82
- agentId: credential.agentId,
83
- agentsRoot: config.agentsRoot,
84
- cliPath: config.cliPath,
85
- providerConfig,
86
- ...(providerConfig.description ? { description: providerConfig.description } : {}),
87
- systemEnv: {
88
- ...(providerConfig.fastMode ? { CREW_FAST_MODE: "1" } : {}),
89
- ...(input.scheduled ? { CREW_SCHEDULE_OUTPUT_POLICY: input.scheduled.outputPolicy } : {}),
90
- ...(originDecisionFileName ? {
91
- CREW_WAKE_ORIGIN: input.wakeOrigin,
92
- CREW_ORIGIN_DECISION_FILE: originDecisionFileName,
93
- } : {}),
94
- ...(boundImDecisionFileName ? {
95
- CREW_BOUND_IM_DECISION_FILE: boundImDecisionFileName,
96
- } : {}),
97
- },
98
- },
99
- session: {
100
- enabled: config.resume,
101
- warmMs: config.resumeWarmMs,
102
- budgetTokens: config.sessionBudgetTokens,
103
- softTokens: config.sessionSoftTokens,
104
- maxTurns: config.sessionMaxTurns,
105
- },
106
- }, { onActivity, onConsole }, {
107
- launchRuntime: dependencies.launchRuntime ?? ((request) => launchSupervisedRuntime(request, dependencies.startSupervisor, dependencies.cancellation, dependencies.platform)),
108
- ...(dependencies.cancellation === undefined ? {} : { cancellation: dependencies.cancellation }),
109
- });
110
- const activities = [...local.activities];
111
- if (!input.scheduled && (runtime === "codex" || runtime === "kimi")
112
- && local.exitCode === 0 && !local.sentViaCrew && local.finalText) {
113
- const sent = await sendAgentMessage(config.serverUrl, credential.token, input.channelId, {
114
- content: local.finalText,
115
- force: true,
116
- ...(input.wakeMessageId ? { thread: input.wakeMessageId } : {}),
117
- });
118
- if (sent.delivered) {
119
- const activity = { kind: "sending", label: "发消息", detail: `${runtime} final answer fallback` };
120
- activities.push(activity);
121
- onActivity(activity);
122
- }
123
- else if (sent.status === 202) {
124
- process.stderr.write(`${runtime} fallback reply was held as draft (HTTP 202) — not visible in channel\n`);
125
- }
126
- else {
127
- process.stderr.write(`${runtime} fallback send failed (HTTP ${sent.status})\n`);
128
- }
129
- }
130
- if (local.usage) {
131
- const usage = local.usage;
132
- process.stdout.write(`📊 tokens: in=${usage.inputTokens} out=${usage.outputTokens} cache_read=${usage.cacheReadTokens} cache_create=${usage.cacheCreationTokens}`
133
- + `${usage.costUsd != null ? ` cost=$${usage.costUsd.toFixed(4)}` : ""} ${local.resumed ? "(resumed)" : "(fresh)"}\n`);
134
- }
135
- const boundImDecisionPath = boundImDecisionFileName
136
- ? join(local.workspaceRunDir, boundImDecisionFileName)
137
- : null;
138
- const selectedBoundImDecision = boundImDecisionPath
139
- ? await readBoundImDecisionFile(boundImDecisionPath)
140
- : null;
141
- if (boundImDecisionPath)
142
- await resetBoundImDecisionFile(boundImDecisionPath);
143
- const boundImDecision = boundImDecisionPath
144
- ? selectedBoundImDecision?.decision ?? "silent"
145
- : undefined;
146
- const report = input.scheduled
147
- ? await deliverScheduledReport({
148
- policy: boundImDecision === "notify" ? "always_report" : input.scheduled.outputPolicy,
149
- title: input.scheduled.title,
150
- exitCode: local.exitCode,
151
- finalText: local.finalText,
152
- errorMessage: local.errorMessage,
153
- send: (content) => sendAgentMessage(config.serverUrl, credential.token, input.channelId, {
154
- content,
155
- force: true,
156
- ...(boundImDecision === "notify" ? { notifyBoundIm: true } : {}),
157
- }),
158
- })
159
- : undefined;
160
- const originDecisionPath = originDecisionFileName
161
- ? join(local.workspaceRunDir, originDecisionFileName)
162
- : null;
163
- const originDecision = originDecisionPath ? await readOriginDecisionFile(originDecisionPath) : null;
164
- if (originDecisionPath)
165
- await resetOriginDecisionFile(originDecisionPath);
166
- return {
167
- exitCode: local.exitCode,
168
- activities,
169
- model: local.model,
170
- runtime,
171
- resumed: local.resumed,
172
- sessionId: local.sessionId,
173
- errorMessage: local.errorMessage,
174
- ...(local.usage ? { usage: local.usage } : {}),
175
- ...(report ? { report } : {}),
176
- ...(boundImDecision ? { boundImDecision } : {}),
177
- ...(input.wakeOrigin ? {
178
- originDecision: originDecision?.decision ?? "missing",
179
- ...(originDecision?.decision === "silent" && originDecision.reason
180
- ? { originDecisionReason: originDecision.reason }
181
- : {}),
182
- } : {}),
183
- };
184
- }
185
- export async function reportScheduledStartFailure(config, input) {
186
- const credential = await mintAgentToken(config.serverUrl, config.machineToken, input.handle);
187
- return deliverScheduledReport({
188
- policy: input.scheduled.outputPolicy,
189
- title: input.scheduled.title,
190
- exitCode: -1,
191
- finalText: null,
192
- errorMessage: input.errorMessage,
193
- send: (content) => sendAgentMessage(config.serverUrl, credential.token, input.channelId, {
194
- content,
195
- force: true,
196
- }),
197
- });
198
- }
199
- export function mergeRunAgentResults(results) {
200
- const final = results[results.length - 1];
201
- if (!final)
202
- throw new Error("cannot merge empty run results");
203
- const usages = results.flatMap((result) => result.usage ? [result.usage] : []);
204
- const usage = usages.length > 0 ? {
205
- inputTokens: usages.reduce((sum, item) => sum + item.inputTokens, 0),
206
- outputTokens: usages.reduce((sum, item) => sum + item.outputTokens, 0),
207
- cacheReadTokens: usages.reduce((sum, item) => sum + item.cacheReadTokens, 0),
208
- cacheCreationTokens: usages.reduce((sum, item) => sum + item.cacheCreationTokens, 0),
209
- ...(usages.some((item) => item.costUsd != null)
210
- ? { costUsd: usages.reduce((sum, item) => sum + (item.costUsd ?? 0), 0) }
211
- : {}),
212
- } : undefined;
213
- return {
214
- ...final,
215
- activities: results.flatMap((result) => result.activities),
216
- ...(usage ? { usage } : {}),
217
- };
218
- }
219
- function defaultPrint(activity) {
220
- const icon = ICON[activity.kind] ?? "·";
221
- const detail = activity.detail ? ` ${activity.detail.replace(/\s+/g, " ").slice(0, 120)}` : "";
222
- process.stdout.write(`${icon} ${activity.label}${detail}\n`);
223
- }
224
- export async function sendAgentMessage(serverUrl, token, channelId, body) {
225
- const response = await fetch(`${serverUrl}/agent/channels/${encodeURIComponent(channelId)}/messages`, {
226
- method: "POST",
227
- headers: {
228
- authorization: `Bearer ${token}`,
229
- "content-type": "application/json",
230
- },
231
- body: JSON.stringify(body),
232
- });
233
- return { delivered: response.status === 201, status: response.status };
234
- }
@@ -1,74 +0,0 @@
1
- export class RuntimeCancelledError extends Error {
2
- constructor(message = "Runtime launch cancelled") {
3
- super(message);
4
- this.name = "RuntimeCancelledError";
5
- }
6
- }
7
- export async function awaitWithCancellation(promise, cancellation) {
8
- if (cancellation === undefined)
9
- return promise;
10
- if (cancellation.isRequested())
11
- throw new RuntimeCancelledError();
12
- const result = await Promise.race([
13
- promise,
14
- cancellation.requested.then(() => { throw new RuntimeCancelledError(); }),
15
- ]);
16
- if (cancellation.isRequested())
17
- throw new RuntimeCancelledError();
18
- return result;
19
- }
20
- export function createRuntimeCancellation() {
21
- let requested = false;
22
- let resolveRequested;
23
- const requestedPromise = new Promise((resolve) => { resolveRequested = resolve; });
24
- const registrations = new Map();
25
- const startRegisteredStops = () => {
26
- if (!requested)
27
- return;
28
- for (const [cancel, stopPromise] of registrations) {
29
- if (stopPromise !== null)
30
- continue;
31
- const started = Promise.resolve().then(cancel);
32
- registrations.set(cancel, started);
33
- void started.catch(() => undefined);
34
- }
35
- };
36
- const waitForStop = async () => {
37
- if (!requested)
38
- return;
39
- while (true) {
40
- startRegisteredStops();
41
- const registrationCount = registrations.size;
42
- const results = await Promise.allSettled([...registrations.values()].filter((value) => value !== null));
43
- if (registrations.size !== registrationCount)
44
- continue;
45
- const failures = results.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
46
- if (failures.length === 1)
47
- throw failures[0];
48
- if (failures.length > 1)
49
- throw new AggregateError(failures, "Runtime cancellation failed");
50
- return;
51
- }
52
- };
53
- const cancellation = {
54
- isRequested: () => requested,
55
- requested: requestedPromise,
56
- register: (next) => {
57
- if (registrations.has(next))
58
- return;
59
- registrations.set(next, null);
60
- startRegisteredStops();
61
- },
62
- waitForStop,
63
- };
64
- return {
65
- cancellation,
66
- request: () => {
67
- if (requested)
68
- return;
69
- requested = true;
70
- resolveRequested();
71
- startRegisteredStops();
72
- },
73
- };
74
- }
@@ -1,43 +0,0 @@
1
- export const LOCAL_EXECUTION_RUNTIMES = ["claude", "codex", "kimi"];
2
- export const LOCAL_RUNTIME_CAPABILITIES = Object.freeze({
3
- claude: Object.freeze({
4
- transport: "claude-stream-json",
5
- nativeResume: true,
6
- systemPromptTransport: "file",
7
- }),
8
- codex: Object.freeze({
9
- transport: "codex-app-server",
10
- nativeResume: true,
11
- systemPromptTransport: "protocol",
12
- }),
13
- kimi: Object.freeze({
14
- transport: "kimi-acp",
15
- nativeResume: true,
16
- systemPromptTransport: "protocol",
17
- }),
18
- });
19
- export function runtimeCapability(runtime) {
20
- return LOCAL_RUNTIME_CAPABILITIES[runtime];
21
- }
22
- const LOCAL_EXECUTION_RUNTIME_SET = new Set(LOCAL_EXECUTION_RUNTIMES);
23
- export function executableRuntimes(installed) {
24
- return [...new Set(installed)].filter((runtime) => LOCAL_EXECUTION_RUNTIME_SET.has(runtime));
25
- }
26
- export function routeRuntimeAttachments(runtime, attachments) {
27
- if (attachments.length === 0)
28
- return { nativeImagePaths: [], promptSuffix: "" };
29
- const lines = attachments.map((attachment) => `- ${JSON.stringify(attachment.path)} (${attachment.mime}, ${attachment.sizeBytes} bytes)`);
30
- return {
31
- nativeImagePaths: runtime === "codex"
32
- ? attachments.filter((attachment) => attachment.mime.startsWith("image/"))
33
- .map((attachment) => attachment.path)
34
- : [],
35
- promptSuffix: [
36
- "",
37
- "",
38
- "## Files attached to the triggering message",
39
- "These files were downloaded into the local execution workspace. Open/read the relevant files before describing their contents; do not infer contents from filenames alone.",
40
- ...lines,
41
- ].join("\n"),
42
- };
43
- }
@@ -1,60 +0,0 @@
1
- /**
2
- * daemon 被非登录/非交互进程(sh -c / pnpm script / tmux / 后台转发)拉起时,继承的 PATH 常退化为系统
3
- * 默认,缺用户级 CLI 目录——尤其 Claude 官方原生安装器默认的 `~/.local/bin`。结果 `which claude` 探测
4
- * 落空、真正 spawn 也 ENOENT。Kimi 官方安装器同样只把 `~/.kimi-code/bin` 写入 shell rc。
5
- * 这里在探测与启动前把常见安装目录补进 PATH,保证「扫得到 = 起得来」。
6
- *
7
- * codex 装在 `/usr/local/bin`(系统默认 PATH 本就含之)所以不受影响;本模块对已在 PATH 中的目录是无操作。
8
- */
9
- import { existsSync } from "node:fs";
10
- import { win32, posix } from "node:path";
11
- const pathApi = (platform) => (platform === "win32" ? win32 : posix);
12
- /** 该平台常见 CLI 安装目录候选(是否真实存在稍后由 augmentedPath 校验)。 */
13
- export function commonBinDirs(env = process.env, platform = process.platform) {
14
- const p = pathApi(platform);
15
- if (platform === "win32") {
16
- const dirs = [];
17
- if (env.USERPROFILE) {
18
- dirs.push(p.join(env.USERPROFILE, ".kimi-code", "bin"), // Kimi 官方安装器默认落点
19
- p.join(env.USERPROFILE, ".local", "bin"));
20
- }
21
- if (env.APPDATA)
22
- dirs.push(p.join(env.APPDATA, "npm"));
23
- if (env.LOCALAPPDATA)
24
- dirs.push(p.join(env.LOCALAPPDATA, "Microsoft", "WindowsApps"));
25
- return dirs;
26
- }
27
- const dirs = [];
28
- const home = env.HOME;
29
- if (home) {
30
- dirs.push(p.join(home, ".kimi-code", "bin"), // Kimi 官方安装器默认落点
31
- p.join(home, ".local", "bin"), // Claude 官方原生安装器默认落点
32
- p.join(home, ".claude", "local"));
33
- }
34
- dirs.push("/opt/homebrew/bin", "/usr/local/bin"); // Apple Silicon brew / Intel brew & npm 全局
35
- if (home) {
36
- dirs.push(p.join(home, ".npm-global", "bin"), p.join(home, ".bun", "bin"), p.join(home, ".deno", "bin"));
37
- }
38
- return dirs;
39
- }
40
- /**
41
- * 在 `env.PATH` 基础上,把候选目录里「真实存在且尚未在 PATH 中」的去重后 prepend,返回新的 PATH 字符串。
42
- * 用户装的目录优先命中(prepend);全部已存在或都不在磁盘上时,原样返回 `env.PATH`。
43
- */
44
- export function augmentedPath(env = process.env, deps = {}, platform = process.platform) {
45
- const dirExists = deps.dirExists ?? existsSync;
46
- const sep = pathApi(platform).delimiter;
47
- const current = env.PATH ?? "";
48
- const existing = new Set(current.split(sep).filter((entry) => entry.length > 0));
49
- const extra = [];
50
- for (const dir of commonBinDirs(env, platform)) {
51
- if (existing.has(dir) || extra.includes(dir))
52
- continue;
53
- if (!dirExists(dir))
54
- continue;
55
- extra.push(dir);
56
- }
57
- if (extra.length === 0)
58
- return current;
59
- return current.length > 0 ? `${extra.join(sep)}${sep}${current}` : extra.join(sep);
60
- }
@@ -1,51 +0,0 @@
1
- /**
2
- * Claude Code runtime 适配:print + stream-json 模式,headless 驱动。
3
- */
4
- // cross-spawn:win32 上 npm CLI 是 .cmd shim,node 原生 spawn 不带 shell 无法执行(ENOENT/EINVAL)
5
- import spawn from "cross-spawn";
6
- // Claude Code 原生 --effort 档位(claude 2.1.196 实测:--help 与非法值告警均枚举这五档)。
7
- // 白名单外的值(含 "default" 与 codex 专属档)回落 CLAUDE_DEFAULT_EFFORT,脏数据不影响启动。
8
- export const CLAUDE_EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
9
- // 未配置/非法档位时的默认思考强度:medium 开启原生 thinking(终端透传要展示思考过程),
10
- // 又不至于 high/max 的 token 开销;agent 配置白名单档位可覆盖。
11
- export const CLAUDE_DEFAULT_EFFORT = "medium";
12
- export function buildClaudeArgs(input) {
13
- const args = [
14
- "--print",
15
- "--verbose",
16
- "--output-format",
17
- "stream-json",
18
- "--include-partial-messages",
19
- "--append-system-prompt-file",
20
- input.systemPromptPath,
21
- ];
22
- if (input.model)
23
- args.push("--model", input.model);
24
- const effort = input.reasoning && CLAUDE_EFFORT_LEVELS.includes(input.reasoning)
25
- ? input.reasoning
26
- : CLAUDE_DEFAULT_EFFORT;
27
- args.push("--effort", effort);
28
- if (input.sessionId) {
29
- args.push(input.resume ? "--resume" : "--session-id", input.sessionId);
30
- }
31
- if (input.effectivePermission === undefined) {
32
- if (input.dangerous)
33
- args.push("--dangerously-skip-permissions");
34
- }
35
- else if (input.effectivePermission === "full_access") {
36
- args.push("--dangerously-skip-permissions");
37
- }
38
- else {
39
- args.push("--permission-mode", input.effectivePermission === "sandboxed" ? "plan" : "acceptEdits");
40
- }
41
- args.push(input.wakePrompt);
42
- return args;
43
- }
44
- export function spawnClaude(input) {
45
- // stdio 固定 ignore/pipe/pipe,stdout/stderr 必为 Readable;cross-spawn 类型不带该细化,断言之
46
- return spawn(input.bin, buildClaudeArgs(input), {
47
- cwd: input.cwd,
48
- env: input.env,
49
- stdio: ["ignore", "pipe", "pipe"],
50
- });
51
- }