@nowcrew/daemon 0.6.24 → 0.6.27
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/dist/codex-home-launch.js +7 -0
- package/dist/codex-home-mode-controller.js +68 -0
- package/dist/computer-profile.js +3 -0
- package/dist/config.js +9 -0
- package/dist/execution-runner.js +1 -0
- package/dist/local-executor.js +5 -4
- package/dist/machine-info.js +1 -0
- package/dist/main.js +0 -0
- package/dist/provider-env.js +3 -1
- package/dist/runner.js +1 -0
- package/dist/serve.js +13 -0
- package/package.json +10 -9
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { awaitWithCancellation } from "./runtime-cancellation.js";
|
|
2
|
+
import { materializeDefaultCodexHome } from "./runtimes/codex-home.js";
|
|
3
|
+
export async function materializePrivateCodexHome(input) {
|
|
4
|
+
if (input.runtime !== "codex" || input.mode !== "private")
|
|
5
|
+
return;
|
|
6
|
+
await awaitWithCancellation((input.materialize ?? materializeDefaultCodexHome)(input.homeDir, input.sourceHome, input.sessionId), input.cancellation);
|
|
7
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { daemonHome, loadProfile, saveProfile } from "./computer-profile.js";
|
|
3
|
+
const MessageSchema = z.object({
|
|
4
|
+
type: z.literal("daemon:codex-home-mode"),
|
|
5
|
+
operationId: z.string().uuid(),
|
|
6
|
+
mode: z.enum(["shared", "private"]),
|
|
7
|
+
}).strict();
|
|
8
|
+
export function createCodexHomeModeController(input) {
|
|
9
|
+
let running = null;
|
|
10
|
+
const handled = new Set();
|
|
11
|
+
const fail = (operationId, errorCode) => {
|
|
12
|
+
input.sendStatus({ type: "daemon:codex-home-mode-status", operationId, status: "failed", errorCode });
|
|
13
|
+
};
|
|
14
|
+
const execute = async (message) => {
|
|
15
|
+
if (input.profileName === undefined) {
|
|
16
|
+
fail(message.operationId, "profile_unavailable");
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (input.isBusy()) {
|
|
20
|
+
fail(message.operationId, "busy");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
input.sendStatus({ type: "daemon:codex-home-mode-status", operationId: message.operationId, status: "saving" });
|
|
24
|
+
try {
|
|
25
|
+
const home = input.profileHome ?? daemonHome();
|
|
26
|
+
const profile = await loadProfile(input.profileName, home);
|
|
27
|
+
await saveProfile({ ...profile, codexHomeMode: message.mode }, home);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
fail(message.operationId, "save_failed");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
input.sendStatus({ type: "daemon:codex-home-mode-status", operationId: message.operationId, status: "restarting" });
|
|
34
|
+
try {
|
|
35
|
+
const serviceSpec = await input.getServiceSpec();
|
|
36
|
+
if (serviceSpec === null) {
|
|
37
|
+
fail(message.operationId, "restart_failed");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const handoff = input.scheduleRestart(serviceSpec);
|
|
41
|
+
void handoff.failed.then(() => fail(message.operationId, "restart_failed"), () => fail(message.operationId, "restart_failed"));
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
fail(message.operationId, "restart_failed");
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
handle: async (raw) => {
|
|
49
|
+
const parsed = MessageSchema.safeParse(raw);
|
|
50
|
+
if (!parsed.success)
|
|
51
|
+
return false;
|
|
52
|
+
if (handled.has(parsed.data.operationId))
|
|
53
|
+
return true;
|
|
54
|
+
if (running !== null) {
|
|
55
|
+
fail(parsed.data.operationId, "busy");
|
|
56
|
+
handled.add(parsed.data.operationId);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
running = execute(parsed.data).finally(() => {
|
|
60
|
+
handled.add(parsed.data.operationId);
|
|
61
|
+
running = null;
|
|
62
|
+
});
|
|
63
|
+
await running;
|
|
64
|
+
return true;
|
|
65
|
+
},
|
|
66
|
+
drain: async () => { await running; },
|
|
67
|
+
};
|
|
68
|
+
}
|
package/dist/computer-profile.js
CHANGED
|
@@ -21,6 +21,7 @@ const ProfileSchema = z.object({
|
|
|
21
21
|
machineToken: MachineTokenSchema,
|
|
22
22
|
agentsRoot: z.string().min(1).optional(),
|
|
23
23
|
runtimePath: z.string().min(1).max(32768).refine((value) => !value.includes("\0") && !value.includes("\n") && !value.includes("\r"), "runtimePath must be a single line").optional(),
|
|
24
|
+
codexHomeMode: z.enum(["shared", "private"]).optional(),
|
|
24
25
|
}).strict();
|
|
25
26
|
const ProfileAgentsRootCandidateSchema = ProfileSchema.pick({
|
|
26
27
|
name: true,
|
|
@@ -217,6 +218,7 @@ export async function saveProfile(input, home = daemonHome(), options = {}) {
|
|
|
217
218
|
serverUrl: profile.serverUrl,
|
|
218
219
|
...(profile.agentsRoot ? { agentsRoot: profile.agentsRoot } : {}),
|
|
219
220
|
...(profile.runtimePath ? { runtimePath: profile.runtimePath } : {}),
|
|
221
|
+
...(profile.codexHomeMode === undefined ? {} : { codexHomeMode: profile.codexHomeMode }),
|
|
220
222
|
machineTokenProtected: { scheme: "dpapi-current-user", ciphertext },
|
|
221
223
|
});
|
|
222
224
|
await atomicPrivateWrite(profilePath(profile.name, home), `${JSON.stringify(stored, null, 2)}\n`, configured.harden);
|
|
@@ -353,4 +355,5 @@ export function applyProfileToEnv(profile, env) {
|
|
|
353
355
|
delete env.CREW_AGENTS_ROOT;
|
|
354
356
|
if (profile.runtimePath)
|
|
355
357
|
env.PATH = profile.runtimePath;
|
|
358
|
+
env.CREW_CODEX_HOME_MODE = profile.codexHomeMode ?? "shared";
|
|
356
359
|
}
|
package/dist/config.js
CHANGED
|
@@ -57,6 +57,14 @@ function defaultCliPath() {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
export const DEFAULT_SERVER_URL = "http://127.0.0.1:3001";
|
|
60
|
+
function codexHomeModeEnv(env) {
|
|
61
|
+
const value = env.CREW_CODEX_HOME_MODE;
|
|
62
|
+
if (value === undefined || value === "shared")
|
|
63
|
+
return "shared";
|
|
64
|
+
if (value === "private")
|
|
65
|
+
return "private";
|
|
66
|
+
throw new ConfigError("CREW_CODEX_HOME_MODE must be shared or private");
|
|
67
|
+
}
|
|
60
68
|
export function loadConfig(env = process.env) {
|
|
61
69
|
const serverUrl = (env.CREW_SERVER_URL ?? DEFAULT_SERVER_URL).replace(/\/+$/, "");
|
|
62
70
|
const machineToken = env.CREW_MACHINE_TOKEN ?? "";
|
|
@@ -84,6 +92,7 @@ export function loadConfig(env = process.env) {
|
|
|
84
92
|
agentsRoot: resolveAgentsRoot(env.CREW_AGENTS_ROOT, homedir()),
|
|
85
93
|
cliPath: env.CREW_CLI_PATH ?? defaultCliPath(),
|
|
86
94
|
runtimeBin: env.CREW_RUNTIME ?? "claude",
|
|
95
|
+
codexHomeMode: codexHomeModeEnv(env),
|
|
87
96
|
dangerous: env.CREW_RUNTIME_SAFE !== "1", // 默认开启 (headless agent 在自有 workspace 内运行)
|
|
88
97
|
resume: env.CREW_RESUME !== "off" && env.CREW_RESUME !== "0", // 默认开启;一键回退现状用 CREW_RESUME=off
|
|
89
98
|
resumeWarmMs: env.CREW_RESUME_WARM_MS != null ? Number(env.CREW_RESUME_WARM_MS) : 3_600_000, // 默认 1h
|
package/dist/execution-runner.js
CHANGED
|
@@ -716,6 +716,7 @@ export async function runExecution(config, input, dependencies) {
|
|
|
716
716
|
agentsRoot: config.agentsRoot,
|
|
717
717
|
cliPath: config.cliPath,
|
|
718
718
|
providerConfig,
|
|
719
|
+
codexHomeMode: config.codexHomeMode,
|
|
719
720
|
...(providerConfig.description ? { description: providerConfig.description } : {}),
|
|
720
721
|
...(spec.reporting.allowBoundImDecision ? {
|
|
721
722
|
systemEnv: {
|
package/dist/local-executor.js
CHANGED
|
@@ -6,6 +6,7 @@ import { CLAUDE_ADDITIONAL_DIRECTORY_INSTRUCTIONS_ENV, isClaudeAdditionalDirecto
|
|
|
6
6
|
import { spawnCodex } from "./runtimes/codex.js";
|
|
7
7
|
import { DEEPSEEK_CODEX_MODEL, DEEPSEEK_CODEX_REASONING_LEVELS, materializeDeepSeekCodexHome, } from "./runtimes/codex-deepseek-config.js";
|
|
8
8
|
import { materializeDefaultCodexHome } from "./runtimes/codex-home.js";
|
|
9
|
+
import { materializePrivateCodexHome } from "./codex-home-launch.js";
|
|
9
10
|
import { spawnKimi, KIMI_EFFORT_LEVELS } from "./runtimes/kimi.js";
|
|
10
11
|
import { applyDeepSeekHarnessMachineEnv, applyProviderEnv, providerFingerprint, } from "./provider-env.js";
|
|
11
12
|
import { augmentedPath } from "./runtime-path.js";
|
|
@@ -348,9 +349,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
348
349
|
const launchSessionId = resumeSessionId ?? (rotated
|
|
349
350
|
? await rotateAgentSession(workspace.sessionDir)
|
|
350
351
|
: workspace.agentSessionId);
|
|
351
|
-
|
|
352
|
-
await awaitWithCancellation((dependencies.materializeDefaultCodexHome ?? materializeDefaultCodexHome)(workspace.homeDir, sourceCodexHome, launchSessionId), dependencies.cancellation);
|
|
353
|
-
}
|
|
352
|
+
await materializePrivateCodexHome({ runtime: runtime.name, homeDir: workspace.homeDir, ...(input.launch.codexHomeMode === undefined ? {} : { mode: input.launch.codexHomeMode }), ...(sourceCodexHome === undefined ? {} : { sourceHome: sourceCodexHome }), ...(launchSessionId === undefined ? {} : { sessionId: launchSessionId }), ...(dependencies.cancellation === undefined ? {} : { cancellation: dependencies.cancellation }), ...(dependencies.materializeDefaultCodexHome === undefined ? {} : { materialize: dependencies.materializeDefaultCodexHome }) });
|
|
354
353
|
const promptContext = {
|
|
355
354
|
workspace: executionWorkspace,
|
|
356
355
|
resuming,
|
|
@@ -470,7 +469,9 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
470
469
|
? { KIMI_MODEL_THINKING_EFFORT: runtime.reasoning }
|
|
471
470
|
: {}),
|
|
472
471
|
};
|
|
473
|
-
const providerEnv = applyProviderEnv(baseEnv, runtime.name,
|
|
472
|
+
const providerEnv = applyProviderEnv(baseEnv, runtime.name, input.launch.codexHomeMode === undefined
|
|
473
|
+
? providerConfig
|
|
474
|
+
: { ...providerConfig, codexHomeMode: input.launch.codexHomeMode }, workspace.homeDir);
|
|
474
475
|
const childEnv = runtime.name === "deepseek-harness"
|
|
475
476
|
? applyDeepSeekHarnessMachineEnv(providerEnv, inheritedEnv)
|
|
476
477
|
: providerEnv;
|
package/dist/machine-info.js
CHANGED
|
@@ -183,6 +183,7 @@ export async function collectMachineHello(agentsRoot, executionLimits, runtimePl
|
|
|
183
183
|
runtimes,
|
|
184
184
|
executionRuntimes,
|
|
185
185
|
capabilities,
|
|
186
|
+
...(dependencies.codexHomeMode === undefined ? {} : { codexHomeMode: dependencies.codexHomeMode }),
|
|
186
187
|
...(dependencies.profileName === undefined ? {} : {
|
|
187
188
|
layoutVersion: 1,
|
|
188
189
|
profileName: dependencies.profileName,
|
package/dist/main.js
CHANGED
|
File without changes
|
package/dist/provider-env.js
CHANGED
|
@@ -50,7 +50,9 @@ export function applyProviderEnv(base, runtime, cfg, homeDir) {
|
|
|
50
50
|
return env;
|
|
51
51
|
}
|
|
52
52
|
if (runtime === "codex") {
|
|
53
|
-
|
|
53
|
+
if (cfg.codexHomeMode === "private")
|
|
54
|
+
return { ...base, CODEX_HOME: join(homeDir, ".codex") };
|
|
55
|
+
return base;
|
|
54
56
|
}
|
|
55
57
|
if (runtime !== "claude" || cfg.provider !== "custom")
|
|
56
58
|
return base;
|
package/dist/runner.js
CHANGED
|
@@ -84,6 +84,7 @@ export async function runAgent(config, input, onActivity = defaultPrint, onConso
|
|
|
84
84
|
agentsRoot: config.agentsRoot,
|
|
85
85
|
cliPath: config.cliPath,
|
|
86
86
|
providerConfig,
|
|
87
|
+
codexHomeMode: config.codexHomeMode,
|
|
87
88
|
...(providerConfig.description ? { description: providerConfig.description } : {}),
|
|
88
89
|
systemEnv: {
|
|
89
90
|
...(providerConfig.fastMode ? { CREW_FAST_MODE: "1" } : {}),
|
package/dist/serve.js
CHANGED
|
@@ -49,6 +49,7 @@ import { parseMemoryPruneTraceId } from "./memory-prune-diagnostics.js";
|
|
|
49
49
|
import { handleRuntimeProbeFrame, probeRuntimeHealth } from "./runtime-health.js";
|
|
50
50
|
import { buildControlPlaneUrl } from "./control-plane-url.js";
|
|
51
51
|
import { createServeMigrationController, handleMigrationControlMessage } from "./daemon-migration-wiring.js";
|
|
52
|
+
import { createCodexHomeModeController } from "./codex-home-mode-controller.js";
|
|
52
53
|
export { buildControlPlaneUrl } from "./control-plane-url.js";
|
|
53
54
|
// normalize.ts 的活动种类 → activity 枚举
|
|
54
55
|
const ACTIVITY_MAP = {
|
|
@@ -121,6 +122,11 @@ export function serve(config, opts = {}) {
|
|
|
121
122
|
}
|
|
122
123
|
catch { /* reconnect */ } },
|
|
123
124
|
});
|
|
125
|
+
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 {
|
|
126
|
+
if (ws?.readyState === WebSocket.OPEN)
|
|
127
|
+
ws.send(JSON.stringify(frame));
|
|
128
|
+
}
|
|
129
|
+
catch { /* reconnect */ } } });
|
|
124
130
|
const projectionCoordinator = createAgentProjectionCoordinator();
|
|
125
131
|
const agentAbilityRuntime = createAgentAbilityRuntime(config.agentsRoot, {
|
|
126
132
|
...opts.agentAbility,
|
|
@@ -299,7 +305,9 @@ export function serve(config, opts = {}) {
|
|
|
299
305
|
...(opts.profileName === undefined ? {} : { profileName: opts.profileName }),
|
|
300
306
|
// First hello must not wait for third-party handshakes; optional transports arrive in the refresh.
|
|
301
307
|
detectExecutable: async (installed) => conservativeExecutionRuntimes(installed),
|
|
308
|
+
additionalCapabilities: async () => [...(await managedDaemonCapabilities(updateEligibility)), ...(opts.profileName === undefined ? [] : ["codex_home_mode_v1"])],
|
|
302
309
|
capabilities: capabilities.machineHello,
|
|
310
|
+
codexHomeMode: config.codexHomeMode,
|
|
303
311
|
});
|
|
304
312
|
runtimeFacts = helloPromise
|
|
305
313
|
.then(async (hello) => {
|
|
@@ -380,6 +388,10 @@ export function serve(config, opts = {}) {
|
|
|
380
388
|
void handleMigrationControlMessage(migrationController, decoded).catch((error) => dslog("daemon.migration_failed", "daemon 布局迁移失败", { level: "ERROR", error_message: error.message }));
|
|
381
389
|
return;
|
|
382
390
|
}
|
|
391
|
+
if (rawType === "daemon:codex-home-mode") {
|
|
392
|
+
void codexHomeModeController.handle(decoded).catch((error) => dslog("daemon.codex_home_mode_failed", "Codex Home 模式切换失败", { level: "ERROR", error_message: error.message }));
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
383
395
|
if (rawType.startsWith("execution:")) {
|
|
384
396
|
const parsedExecution = ServerToDaemonExecutionFrameSchema.safeParse(decoded);
|
|
385
397
|
if (!parsedExecution.success) {
|
|
@@ -1147,6 +1159,7 @@ export function serve(config, opts = {}) {
|
|
|
1147
1159
|
webSocketClosed,
|
|
1148
1160
|
updateController.drain(),
|
|
1149
1161
|
...(migrationController === null ? [] : [migrationController.drain()]),
|
|
1162
|
+
codexHomeModeController.drain(),
|
|
1150
1163
|
executionFrameQueue,
|
|
1151
1164
|
...executionRuns.values(),
|
|
1152
1165
|
...[...legacyRuns.values()].map((run) => run.done),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nowcrew/daemon",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.27",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "crew daemon — 运行在用户机器:拉起/管理 agent 进程,注入 crew CLI,归一化 runtime 事件",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -16,6 +16,14 @@
|
|
|
16
16
|
"publishConfig": {
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"daemon": "pnpm --filter @nowcrew/cli build && tsx src/main.ts",
|
|
21
|
+
"build": "tsc -p tsconfig.json",
|
|
22
|
+
"codex-home:migrate": "tsx src/runtimes/codex-home-migration-cli.ts",
|
|
23
|
+
"prepublishOnly": "pnpm build && node ../scripts/daemon-release-artifact.mjs --strict-registry",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"typecheck": "tsc --noEmit"
|
|
26
|
+
},
|
|
19
27
|
"dependencies": {
|
|
20
28
|
"@agentclientprotocol/sdk": "1.2.1",
|
|
21
29
|
"@nowcrew/cli": "^0.4.13",
|
|
@@ -34,12 +42,5 @@
|
|
|
34
42
|
"tsx": "^4.19.0",
|
|
35
43
|
"typescript": "^5.6.0",
|
|
36
44
|
"vitest": "^2.1.0"
|
|
37
|
-
},
|
|
38
|
-
"scripts": {
|
|
39
|
-
"daemon": "pnpm --filter @nowcrew/cli build && tsx src/main.ts",
|
|
40
|
-
"build": "tsc -p tsconfig.json",
|
|
41
|
-
"codex-home:migrate": "tsx src/runtimes/codex-home-migration-cli.ts",
|
|
42
|
-
"test": "vitest run",
|
|
43
|
-
"typecheck": "tsc --noEmit"
|
|
44
45
|
}
|
|
45
|
-
}
|
|
46
|
+
}
|