@nowcrew/daemon 0.6.17 → 0.6.19

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.
@@ -5,6 +5,7 @@ import { prepareWorkspace, rotateAgentSession, safeKey, } from "./workspace.js";
5
5
  import { CLAUDE_ADDITIONAL_DIRECTORY_INSTRUCTIONS_ENV, isClaudeAdditionalDirectoryInstructionsSupported, probeClaudeVersion, resolveOrderedUniqueClaudeDirectories, spawnClaude, } from "./runtimes/claude.js";
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
+ import { materializeDefaultCodexHome } from "./runtimes/codex-home.js";
8
9
  import { spawnKimi, KIMI_EFFORT_LEVELS } from "./runtimes/kimi.js";
9
10
  import { applyDeepSeekHarnessMachineEnv, applyProviderEnv, providerFingerprint, } from "./provider-env.js";
10
11
  import { augmentedPath } from "./runtime-path.js";
@@ -257,6 +258,11 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
257
258
  let memoryPruneSharedWriteKey = null;
258
259
  let executionWorkspace = workspace;
259
260
  let localMemoryTelemetry = null;
261
+ const inheritedEnv = { ...process.env };
262
+ for (const key of Object.keys(inheritedEnv)) {
263
+ if (key.startsWith("CREW_AGENT_MEMORY_"))
264
+ delete inheritedEnv[key];
265
+ }
260
266
  try {
261
267
  if (isDeepSeekCodex && !providerConfig.providerApiKey) {
262
268
  throw new Error("DeepSeek API key is not configured for this Agent");
@@ -264,6 +270,11 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
264
270
  if (isDeepSeekCodex) {
265
271
  await awaitWithCancellation((dependencies.materializeDeepSeekCodexHome ?? materializeDeepSeekCodexHome)(workspace.homeDir), dependencies.cancellation);
266
272
  }
273
+ else if (runtime.name === "codex") {
274
+ const sourceCodexHome = inheritedEnv.CODEX_HOME
275
+ ?? (inheritedEnv.HOME === undefined ? undefined : join(inheritedEnv.HOME, ".codex"));
276
+ await awaitWithCancellation((dependencies.materializeDefaultCodexHome ?? materializeDefaultCodexHome)(workspace.homeDir, sourceCodexHome), dependencies.cancellation);
277
+ }
267
278
  const supportsNativeResume = runtime.name === "claude"
268
279
  || (dependencies.launchRuntime !== undefined && runtimeCapability(runtime.name).nativeResume);
269
280
  const storedCurrentPrior = input.session.enabled && supportsNativeResume
@@ -422,11 +433,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
422
433
  }
423
434
  }
424
435
  memoryPruneFailurePhase = "runtime_prepare";
425
- const inheritedEnv = { ...process.env };
426
- for (const key of Object.keys(inheritedEnv)) {
427
- if (key.startsWith("CREW_AGENT_MEMORY_"))
428
- delete inheritedEnv[key];
429
- }
430
436
  const baseEnv = {
431
437
  ...inheritedEnv,
432
438
  ...sanitizeEnvVars(providerConfig.envVars),
@@ -49,6 +49,9 @@ export function applyProviderEnv(base, runtime, cfg, homeDir) {
49
49
  env[DEEPSEEK_CODEX_KEY_ENV] = cfg.providerApiKey;
50
50
  return env;
51
51
  }
52
+ if (runtime === "codex") {
53
+ return { ...base, CODEX_HOME: join(homeDir, ".codex") };
54
+ }
52
55
  if (runtime !== "claude" || cfg.provider !== "custom")
53
56
  return base;
54
57
  const env = { ...base };
@@ -37,6 +37,7 @@ const MAX_TRANSIENT_TURN_RETRIES = 2;
37
37
  const TRANSIENT_TURN_RETRY_DELAY_MS = 15_000;
38
38
  const TRANSIENT_TURN_RETRY_BACKOFF_FACTOR = 3;
39
39
  const PROCESS_TREE_STOP_TIMEOUT_MS = 1_000;
40
+ const USER_INPUT_UNAVAILABLE_MESSAGE = "NowWork cannot collect interactive user input in this headless run; ask the user in the final response instead of assuming an answer.";
40
41
  export function codexAppServerArgs(projectRootMarkers) {
41
42
  return [
42
43
  ...(projectRootMarkers === undefined
@@ -197,8 +198,9 @@ export function codexApprovalResponse(method, permission) {
197
198
  || method === "item/fileChange/requestApproval") {
198
199
  return { decision: permission === "full_access" ? "accept" : "decline" };
199
200
  }
200
- if (method === "item/tool/requestUserInput")
201
- return { answers: {} };
201
+ if (method === "item/tool/requestUserInput") {
202
+ return { error: { code: -32002, message: USER_INPUT_UNAVAILABLE_MESSAGE } };
203
+ }
202
204
  return null;
203
205
  }
204
206
  /** Translate app-server v2 notifications into the daemon's existing runtime event contract. */
@@ -321,6 +323,10 @@ class CodexRpcClient {
321
323
  error: { code: -32001, message: `NowCrew denied unsupported request ${message.method}` },
322
324
  });
323
325
  }
326
+ else if (result.error !== undefined && typeof result.error === "object" && result.error !== null) {
327
+ process.stderr.write("[codex-app-server] interactive user input is unavailable; returned a truthful RPC error\n");
328
+ this.write({ jsonrpc: "2.0", id: message.id, error: result.error });
329
+ }
324
330
  else {
325
331
  this.write({ jsonrpc: "2.0", id: message.id, result });
326
332
  }
@@ -0,0 +1,50 @@
1
+ import { chmod, lstat, mkdir, symlink } from "node:fs/promises";
2
+ import { join, resolve } from "node:path";
3
+ const PRIVATE_CODEX_HOME = ".codex";
4
+ const SHARED_CODEX_ENTRIES = [
5
+ { name: "auth.json", type: "file" },
6
+ { name: "sessions", type: "dir" },
7
+ { name: "session_index.jsonl", type: "file" },
8
+ ];
9
+ export function defaultCodexHome(homeDir) {
10
+ return resolve(homeDir, PRIVATE_CODEX_HOME);
11
+ }
12
+ async function linkIfMissing(source, target, type) {
13
+ try {
14
+ await lstat(target);
15
+ return;
16
+ }
17
+ catch (error) {
18
+ if (error.code !== "ENOENT")
19
+ throw error;
20
+ }
21
+ try {
22
+ await lstat(source);
23
+ }
24
+ catch (error) {
25
+ if (error.code === "ENOENT")
26
+ return;
27
+ throw error;
28
+ }
29
+ try {
30
+ await symlink(source, target, type);
31
+ }
32
+ catch (error) {
33
+ if (error.code !== "EEXIST")
34
+ throw error;
35
+ }
36
+ }
37
+ /**
38
+ * Isolate Codex's high-volume runtime state per Agent while preserving the
39
+ * machine login and native resume files used by existing sessions.
40
+ */
41
+ export async function materializeDefaultCodexHome(agentHome, sourceHome) {
42
+ const codexHome = defaultCodexHome(agentHome);
43
+ await mkdir(codexHome, { recursive: true, mode: 0o700 });
44
+ await chmod(codexHome, 0o700);
45
+ const source = sourceHome === undefined ? null : resolve(sourceHome);
46
+ if (source === null || source === codexHome)
47
+ return codexHome;
48
+ await Promise.all(SHARED_CODEX_ENTRIES.map(({ name, type }) => linkIfMissing(join(source, name), join(codexHome, name), type)));
49
+ return codexHome;
50
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nowcrew/daemon",
3
- "version": "0.6.17",
3
+ "version": "0.6.19",
4
4
  "type": "module",
5
5
  "description": "crew daemon — 运行在用户机器:拉起/管理 agent 进程,注入 crew CLI,归一化 runtime 事件",
6
6
  "license": "Apache-2.0",