@nowcrew/daemon 0.6.60 → 0.6.63

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 (46) hide show
  1. package/dist/agent-ability/materializer.js +1 -20
  2. package/dist/automatic-install-target.js +10 -15
  3. package/dist/computer-cli.js +1 -10
  4. package/dist/computer-service.js +1 -41
  5. package/dist/config.js +0 -35
  6. package/dist/daemon-migration-controller.js +18 -16
  7. package/dist/execution-runner.js +0 -1
  8. package/dist/local-executor.js +1 -44
  9. package/dist/machine-info.js +2 -3
  10. package/dist/main.js +0 -83
  11. package/dist/managed-service-lifecycle.js +2 -47
  12. package/dist/managed-service-startup.js +0 -2
  13. package/dist/profile-layout.js +4 -10
  14. package/dist/runner.js +0 -1
  15. package/dist/serve.js +3 -64
  16. package/dist/workspace.js +1 -24
  17. package/package.json +10 -9
  18. package/dist/local-memory/index-budget.js +0 -74
  19. package/dist/local-memory/pending.js +0 -111
  20. package/dist/local-memory/runtime.js +0 -87
  21. package/dist/local-memory/types.js +0 -1
  22. package/dist/local-memory/write.js +0 -448
  23. package/dist/storage-config.js +0 -175
  24. package/dist/storage-control.js +0 -131
  25. package/dist/storage-copy.js +0 -444
  26. package/dist/storage-credential-guard.js +0 -21
  27. package/dist/storage-doctor.js +0 -175
  28. package/dist/storage-identity.js +0 -29
  29. package/dist/storage-layout.js +0 -143
  30. package/dist/storage-manifest.js +0 -440
  31. package/dist/storage-migration-controller.js +0 -417
  32. package/dist/storage-migration-manifest.js +0 -105
  33. package/dist/storage-migration-policy.js +0 -100
  34. package/dist/storage-runtime-controller.js +0 -313
  35. package/dist/storage-runtime-readiness.js +0 -184
  36. package/dist/storage-runtime.js +0 -39
  37. package/dist/storage-safe-fs.js +0 -293
  38. package/dist/storage-serve-adapter.js +0 -65
  39. package/dist/storage-status-delivery.js +0 -201
  40. package/dist/storage-status-replay.js +0 -125
  41. package/dist/storage-target-confirmation.js +0 -129
  42. package/dist/storage-worker-command.js +0 -27
  43. package/dist/storage-worker-executor.js +0 -383
  44. package/dist/storage-worker-handoff.js +0 -151
  45. package/dist/storage-worker-operation.js +0 -283
  46. package/dist/storage-worker-status.js +0 -105
@@ -249,23 +249,6 @@ const verifyCache = async (agentsRoot, ability) => {
249
249
  };
250
250
  const layerRelativePath = (path, layer) => path.startsWith(`${layer}/`) ? path.slice(layer.length + 1) : path;
251
251
  const readManagedCatalog = async (trainingRoot) => JSON.parse(await readFile(join(trainingRoot, "ACTIVE_RELEASE.json"), "utf8"));
252
- const projectedSkillExists = async (trainingRoot, name) => {
253
- const root = join(trainingRoot, "skills", safeAbilityName(name));
254
- if (!await isRealDirectory(root))
255
- return false;
256
- const direct = await lstat(join(root, "SKILL.md")).catch(() => null);
257
- if (direct?.isFile() && !direct.isSymbolicLink())
258
- return true;
259
- for (const entry of await readdir(root, { withFileTypes: true })) {
260
- if (!entry.isDirectory())
261
- continue;
262
- const skill = await lstat(join(root, entry.name, "SKILL.md")).catch(() => null);
263
- if (skill?.isFile() && !skill.isSymbolicLink())
264
- return true;
265
- }
266
- return false;
267
- };
268
- const projectedSkillsComplete = async (trainingRoot, skills) => (await Promise.all(skills.map((skill) => projectedSkillExists(trainingRoot, skill.name)))).every(Boolean);
269
252
  const trainingSwitchJournalPath = (agentRoot) => join(agentRoot, ".training-switch.json");
270
253
  const trainingRollbackRoot = (agentRoot) => join(agentRoot, ".training-rollback");
271
254
  const trainingDiscardRoot = (agentRoot) => join(agentRoot, ".training-discard");
@@ -439,7 +422,6 @@ const activeTrainingRoot = async (agentsRoot, handle, ability) => {
439
422
  && catalog.releaseId === ability.releaseId
440
423
  && catalog.rootCommit === ability.rootCommit
441
424
  && catalog.artifactDigest === ability.artifactDigest
442
- && await projectedSkillsComplete(trainingRoot, ability.skills)
443
425
  ? trainingRoot
444
426
  : null;
445
427
  };
@@ -481,8 +463,7 @@ const materializeWorkspaceTraining = async (agentsRoot, handle, ability) => {
481
463
  }
482
464
  if (previousCatalog.releaseId === ability.releaseId
483
465
  && previousCatalog.artifactDigest === ability.artifactDigest
484
- && previousCatalog.projectionDigest === projectionDigest
485
- && await projectedSkillsComplete(trainingRoot, ability.skills))
466
+ && previousCatalog.projectionDigest === projectionDigest)
486
467
  return trainingRoot;
487
468
  }
488
469
  await mkdir(agentRoot, { recursive: true });
@@ -3,12 +3,12 @@ import { profileLayout, validateLocalInstanceName } from "./profile-layout.js";
3
3
  const PROFILE_NAME = /^[a-z0-9][a-z0-9_-]{0,47}$/;
4
4
  const KNOWN_TARGETS = {
5
5
  "nowwork-in.nowcoder.com": {
6
- environment: "production",
6
+ profile: "in",
7
7
  daemonDirectory: "daemon",
8
8
  agentsDirectory: "agents",
9
9
  },
10
10
  "nowwork-dev.nowcoder.com": {
11
- environment: "dev",
11
+ profile: "dev",
12
12
  daemonDirectory: "daemon-dev",
13
13
  agentsDirectory: "agents-dev",
14
14
  },
@@ -42,37 +42,32 @@ function requireCustomProfile(hostname, value) {
42
42
  if (!PROFILE_NAME.test(value)) {
43
43
  throw new Error("Invalid custom profile; expected a lowercase name of 1-48 letters, digits, '_' or '-'");
44
44
  }
45
- if (value === "in" || value === "dev" || value === "production") {
45
+ if (value === "in" || value === "dev") {
46
46
  throw new Error(`Profile '${value}' is reserved for a known NowWork hostname`);
47
47
  }
48
48
  return value;
49
49
  }
50
50
  function knownTargetForProfile(environment, requestedProfile, input, parsed) {
51
51
  const paths = input.platform === "win32" ? win32 : posix;
52
- // Keep the no-flag target readable by old daemons; any workspace-scoped profile below
53
- // uses the canonical environment token (production, never in).
54
- const legacyProfile = environment === "production" ? "in" : environment;
52
+ const legacyProfile = environment;
55
53
  if (requestedProfile === undefined || requestedProfile === legacyProfile) {
56
54
  return Object.freeze({
57
55
  kind: "known",
58
56
  profile: legacyProfile,
59
57
  serverUrl: normalizedServerUrl(parsed),
60
- daemonHome: paths.join(input.userHome, ".crew", environment === "production" ? "daemon" : "daemon-dev"),
61
- agentsRoot: paths.join(input.userHome, ".crew", environment === "production" ? "agents" : "agents-dev"),
58
+ daemonHome: paths.join(input.userHome, ".crew", environment === "in" ? "daemon" : "daemon-dev"),
59
+ agentsRoot: paths.join(input.userHome, ".crew", environment === "in" ? "agents" : "agents-dev"),
62
60
  npmPrefix: paths.join(input.userHome, ".crew", "daemon-runtimes", legacyProfile),
63
61
  });
64
62
  }
65
63
  if (requestedProfile === "in" || requestedProfile === "dev") {
66
- throw new Error(`Server hostname '${new URL(input.serverUrl).hostname}' requires profile '${environment}'`);
64
+ throw new Error(`Server hostname '${new URL(input.serverUrl).hostname}' requires profile '${legacyProfile}'`);
67
65
  }
68
66
  const prefix = `nw-${environment}-`;
69
- const legacyPrefix = environment === "production" ? "nw-in-" : prefix;
70
- if (!requestedProfile.startsWith(prefix) && !requestedProfile.startsWith(legacyPrefix)) {
67
+ if (!requestedProfile.startsWith(prefix)) {
71
68
  throw new Error(`Server hostname '${new URL(input.serverUrl).hostname}' requires profile '${prefix}<local-name>'`);
72
69
  }
73
- const localInstanceName = validateLocalInstanceName(requestedProfile.startsWith(prefix)
74
- ? requestedProfile.slice(prefix.length)
75
- : requestedProfile.slice(legacyPrefix.length));
70
+ const localInstanceName = validateLocalInstanceName(requestedProfile.slice(prefix.length));
76
71
  const layout = profileLayout({
77
72
  environment,
78
73
  localInstanceName,
@@ -96,7 +91,7 @@ export function resolveAutomaticInstallTarget(input) {
96
91
  const paths = input.platform === "win32" ? win32 : posix;
97
92
  const known = KNOWN_TARGETS[hostname];
98
93
  if (known !== undefined) {
99
- return knownTargetForProfile(known.environment, input.requestedProfile, input, parsed);
94
+ return knownTargetForProfile(known.profile, input.requestedProfile, input, parsed);
100
95
  }
101
96
  const profile = requireCustomProfile(hostname, input.requestedProfile);
102
97
  return Object.freeze({
@@ -82,14 +82,6 @@ function translatedError(error, td) {
82
82
  }
83
83
  return td(error.message);
84
84
  }
85
- function storageServiceEnvironment(env) {
86
- return {
87
- ...(env.NOWWORK_HOME === undefined ? {} : { NOWWORK_HOME: env.NOWWORK_HOME }),
88
- ...(env.NOWWORK_ENV_TAG === undefined ? {} : { NOWWORK_ENV_TAG: env.NOWWORK_ENV_TAG }),
89
- ...(env.NOWWORK_STORAGE_POLICY_PATH === undefined
90
- ? {} : { NOWWORK_STORAGE_POLICY_PATH: env.NOWWORK_STORAGE_POLICY_PATH }),
91
- };
92
- }
93
85
  export async function runComputerCommand(argv, overrides = {}) {
94
86
  const command = argv[0];
95
87
  if (!command || !COMPUTER_COMMANDS.has(command))
@@ -210,7 +202,6 @@ export async function runComputerCommand(argv, overrides = {}) {
210
202
  ...deps,
211
203
  profile: restartProfile.name,
212
204
  profileHome: home,
213
- environment: storageServiceEnvironment(deps.env),
214
205
  })
215
206
  : null;
216
207
  if (spec && restartProfile) {
@@ -281,7 +272,7 @@ export async function runComputerCommand(argv, overrides = {}) {
281
272
  throw new Error(td("Service lifecycle requires the built daemon entry (.js), not a TypeScript development entry"));
282
273
  }
283
274
  const spec = buildServiceSpec({
284
- ...deps, profile: name, profileHome: home, environment: storageServiceEnvironment(deps.env),
275
+ ...deps, profile: name, profileHome: home,
285
276
  });
286
277
  const installation = daemonGlobalInstallation(deps.entryPath, deps.platform);
287
278
  const managedRecord = installation !== null && spec.platform !== "win32"
@@ -53,7 +53,6 @@ export function windowsCommandArg(value) {
53
53
  }
54
54
  export function buildServiceSpec(input) {
55
55
  const { platform, profile, userHome } = input;
56
- const environment = input.environment ?? process.env;
57
56
  if (platform !== "darwin" && platform !== "linux" && platform !== "win32") {
58
57
  throw new Error(`Computer services are not supported on platform '${platform}'`);
59
58
  }
@@ -73,16 +72,6 @@ export function buildServiceSpec(input) {
73
72
  throw new Error("Cannot install launchd service without a numeric uid");
74
73
  const id = `com.nowcrew.daemon.${profile}${identitySuffix}`;
75
74
  const args = daemonCommand.map((arg) => ` <string>${xml(arg)}</string>`).join("\n");
76
- const environmentEntries = [
77
- ["NOWWORK_HOME", environment.NOWWORK_HOME],
78
- ["NOWWORK_ENV_TAG", environment.NOWWORK_ENV_TAG],
79
- ["NOWWORK_STORAGE_POLICY_PATH", environment.NOWWORK_STORAGE_POLICY_PATH],
80
- ].flatMap(([key, value]) => value === undefined
81
- ? []
82
- : [` <key>${key}</key><string>${xml(assertSingleLine(value, key))}</string>`]);
83
- const environmentBlock = environmentEntries.length === 0
84
- ? ""
85
- : ` <key>EnvironmentVariables</key>\n <dict>\n${environmentEntries.join("\n")}\n </dict>\n`;
86
75
  return {
87
76
  platform,
88
77
  profile,
@@ -90,7 +79,7 @@ export function buildServiceSpec(input) {
90
79
  descriptorPath: resolve(userHome, "Library", "LaunchAgents", `${id}.plist`),
91
80
  daemonCommand,
92
81
  managerDomain: `gui/${input.uid}`,
93
- descriptor: `<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0">\n<dict>\n <key>Label</key><string>${xml(id)}</string>\n <key>ProgramArguments</key>\n <array>\n${args}\n </array>\n${environmentBlock} <key>KeepAlive</key><true/>\n <key>ProcessType</key><string>Background</string>\n</dict>\n</plist>\n`,
82
+ descriptor: `<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0">\n<dict>\n <key>Label</key><string>${xml(id)}</string>\n <key>ProgramArguments</key>\n <array>\n${args}\n </array>\n <key>KeepAlive</key><true/>\n <key>ProcessType</key><string>Background</string>\n</dict>\n</plist>\n`,
94
83
  };
95
84
  }
96
85
  if (platform === "linux") {
@@ -277,35 +266,6 @@ export async function installService(spec, runner = systemCommandRunner) {
277
266
  throw error;
278
267
  }
279
268
  }
280
- export async function replaceInstalledServiceDescriptor(spec, previousDescriptor, runner = systemCommandRunner) {
281
- if (spec.platform === "win32" || spec.descriptorPath === null || spec.descriptor === null) {
282
- throw new Error("Installed service descriptor replacement requires launchd or systemd");
283
- }
284
- const current = await readServiceDescriptor(spec);
285
- if (current !== previousDescriptor) {
286
- throw new Error(`Installed descriptor for '${spec.id}' changed before replacement`);
287
- }
288
- const status = await serviceStatus(spec, runner);
289
- if (!status.installed)
290
- throw new Error(`Service '${spec.id}' is not installed`);
291
- if (status.running || status.loaded)
292
- await serviceAction(spec, "stop", runner);
293
- try {
294
- await writePrivateAtomic(spec.descriptorPath, spec.descriptor);
295
- if (spec.platform === "linux") {
296
- await checked(runner, "systemctl", ["--user", "daemon-reload"], "systemd reload");
297
- await checked(runner, "systemctl", ["--user", "enable", spec.id], "systemd enable");
298
- }
299
- if (status.running)
300
- await serviceAction(spec, "start", runner);
301
- }
302
- catch (error) {
303
- await writePrivateAtomic(spec.descriptorPath, previousDescriptor).catch(() => undefined);
304
- if (status.running)
305
- await serviceAction(spec, "start", runner).catch(() => undefined);
306
- throw error;
307
- }
308
- }
309
269
  export async function ensureServiceEnabled(spec, runner = systemCommandRunner) {
310
270
  if (spec.platform === "linux") {
311
271
  if (spec.descriptorPath === null || !await pathExists(spec.descriptorPath)) {
package/dist/config.js CHANGED
@@ -59,40 +59,6 @@ function defaultCliPath() {
59
59
  return resolve(here, "../../cli/dist/main.js");
60
60
  }
61
61
  }
62
- // 临时 dev 验收名单。测试完成后删除此常量,恢复未配置时默认关闭的行为。
63
- const TEMP_LOCAL_MEMORY_V2_TEST_HANDLES = [
64
- "memory-v2-test-a",
65
- "memory-v2-test-b",
66
- "memory-v2-test-c",
67
- ];
68
- function isTemporaryDevServer(serverUrl) {
69
- try {
70
- const hostname = new URL(serverUrl).hostname.toLowerCase();
71
- return hostname === "localhost"
72
- || hostname === "127.0.0.1"
73
- || hostname === "::1"
74
- || hostname === "nowwork-dev.nowcoder.com";
75
- }
76
- catch {
77
- return false;
78
- }
79
- }
80
- function localMemoryV2Handles(env, serverUrl) {
81
- const configured = env.CREW_LOCAL_MEMORY_V2_HANDLES;
82
- if (configured === undefined) {
83
- return isTemporaryDevServer(serverUrl)
84
- ? new Set(TEMP_LOCAL_MEMORY_V2_TEST_HANDLES)
85
- : new Set();
86
- }
87
- const raw = configured.trim();
88
- if (raw === "")
89
- return new Set();
90
- const handles = raw.split(",").map((value) => value.trim()).filter(Boolean);
91
- if (handles.some((handle) => !/^[A-Za-z0-9][A-Za-z0-9._-]*$/u.test(handle))) {
92
- throw new ConfigError("CREW_LOCAL_MEMORY_V2_HANDLES contains an invalid agent handle");
93
- }
94
- return new Set(handles);
95
- }
96
62
  export const DEFAULT_SERVER_URL = "http://127.0.0.1:3001";
97
63
  function codexHomeModeEnv(env) {
98
64
  const value = env.CREW_CODEX_HOME_MODE;
@@ -142,7 +108,6 @@ export function loadConfig(env = process.env) {
142
108
  sessionMaxTurns: env.CREW_SESSION_MAX_TURNS != null ? Number(env.CREW_SESSION_MAX_TURNS) : 30,
143
109
  productName: env.CREW_PRODUCT_NAME ?? "nowwork",
144
110
  agentMemory: loadAgentMemoryConfig(env),
145
- localMemoryV2Handles: localMemoryV2Handles(env, serverUrl),
146
111
  executionLimits,
147
112
  };
148
113
  }
@@ -10,7 +10,6 @@ import { buildServiceSpec, captureExactService, serviceStatus, systemCommandRunn
10
10
  import { readManagedServiceRegistry } from "./managed-service-registry.js";
11
11
  import { uninstallManagedService } from "./managed-service-lifecycle.js";
12
12
  import { resolveAutomaticInstallTarget } from "./automatic-install-target.js";
13
- import { buildStorageMigrationMappings, createSafeServiceManifest, CredentialMaterialError, } from "./storage-migration-manifest.js";
14
13
  const MigrationMessageSchema = z.object({
15
14
  type: z.literal("daemon:migrate"),
16
15
  migrationId: z.string().uuid(),
@@ -48,30 +47,33 @@ async function hashedFiles(root, directory = root) {
48
47
  return files;
49
48
  }
50
49
  async function backupAndCopy(input, id, targetHome, targetAgents, service) {
51
- // Validate and project service identity before creating any backup bytes. A descriptor containing
52
- // a token must stop the migration before it can be copied into the local backup root.
53
- const safeService = createSafeServiceManifest({
54
- spec: service.spec,
55
- snapshot: service.snapshot,
56
- managedRecord: service.managedRecord,
57
- });
58
50
  const backup = migrationBackupRoot(input, id);
59
51
  await mkdir(backup, { recursive: true, mode: 0o700 });
60
52
  await cp(input.currentDaemonHome, resolve(backup, "daemon-home"), { recursive: true, force: false });
61
53
  await cp(input.currentAgentsRoot, resolve(backup, "agents-root"), { recursive: true, force: false });
62
54
  await cp(input.currentDaemonHome, targetHome, { recursive: true, force: false });
63
55
  await cp(input.currentAgentsRoot, targetAgents, { recursive: true, force: false });
64
- const daemonFiles = await hashedFiles(resolve(backup, "daemon-home"));
65
- const agentFiles = await hashedFiles(resolve(backup, "agents-root"));
66
- const mappings = buildStorageMigrationMappings({ daemonFiles, agentFiles });
56
+ const files = [
57
+ ...(await hashedFiles(resolve(backup, "daemon-home"))).map((file) => ({ ...file, path: `daemon-home/${file.path}` })),
58
+ ...(await hashedFiles(resolve(backup, "agents-root"))).map((file) => ({ ...file, path: `agents-root/${file.path}` })),
59
+ ];
67
60
  await writeFile(resolve(backup, "manifest.json"), `${JSON.stringify({
68
- version: 2,
61
+ version: 1,
69
62
  migrationId: id,
70
63
  profile: input.profileName,
71
64
  daemonHome: input.currentDaemonHome,
72
65
  agentsRoot: input.currentAgentsRoot,
73
- service: safeService,
74
- mappings,
66
+ service: {
67
+ id: service.spec.id,
68
+ platform: service.spec.platform,
69
+ descriptorPath: service.spec.descriptorPath,
70
+ descriptor: service.spec.descriptor,
71
+ daemonCommand: service.spec.daemonCommand,
72
+ installed: service.snapshot.installed,
73
+ running: service.snapshot.running,
74
+ managedRecord: service.managedRecord,
75
+ },
76
+ files,
75
77
  }, null, 2)}\n`, { mode: 0o600 });
76
78
  return backup;
77
79
  }
@@ -145,7 +147,7 @@ export function createDaemonMigrationController(input) {
145
147
  }
146
148
  }
147
149
  catch (error) {
148
- sendFailure(message.migrationId, error instanceof CredentialMaterialError ? "credential_material" : "target_conflict", error);
150
+ sendFailure(message.migrationId, "target_conflict", error);
149
151
  return;
150
152
  }
151
153
  input.sendStatus({ type: "daemon:migration-status", migrationId: message.migrationId, status: "backing_up" });
@@ -156,7 +158,7 @@ export function createDaemonMigrationController(input) {
156
158
  }
157
159
  catch (error) {
158
160
  await Promise.allSettled([rm(targetPath, { recursive: true, force: true }), rm(targetAgents, { recursive: true, force: true })]);
159
- sendFailure(message.migrationId, error instanceof CredentialMaterialError ? "credential_material" : "backup_failed", error);
161
+ sendFailure(message.migrationId, "backup_failed", error);
160
162
  return;
161
163
  }
162
164
  const install = input.install ?? installDaemonAutomatically;
@@ -847,7 +847,6 @@ export async function runExecution(config, input, dependencies) {
847
847
  softTokens: config.sessionSoftTokens,
848
848
  maxTurns: config.sessionMaxTurns,
849
849
  },
850
- localMemoryV2Enabled: config.localMemoryV2Handles?.has(spec.agent.handle) === true,
851
850
  };
852
851
  const result = await Promise.race([
853
852
  execute(localInput, callbacks, localDependencies),
@@ -31,7 +31,6 @@ import { diffMemoryPruneNotes, evaluateMemoryPrunePostcondition, inspectMemoryPr
31
31
  import { createLocalMemoryTelemetry, logLocalMemoryContextPrepareFailure, logLocalMemoryDiagnosticsFailure, } from "./local-memory-telemetry.js";
32
32
  import { CodexStartupStageParser } from "./codex-startup-stage.js";
33
33
  import { formatRuntimeStartupError } from "./runtime-startup-error.js";
34
- import { commitLocalMemoryResultAfterExit, initializeLocalMemoryV2ForWorkspace, isLocalMemoryV2Eligible, localMemoryResultPath as makeLocalMemoryResultPath, renderLocalMemoryResultInstructions, } from "./local-memory/runtime.js";
35
34
  function memoryPruneFileFactFields(fileFacts) {
36
35
  const fields = {};
37
36
  for (const [label, fact] of Object.entries(fileFacts)) {
@@ -278,7 +277,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
278
277
  }, "workspace_prepare", error);
279
278
  throw error;
280
279
  }
281
- const localMemoryV2Initialized = await initializeLocalMemoryV2ForWorkspace(workspace, input.localMemoryV2Enabled === true);
282
280
  let materialized = null;
283
281
  let knownAttachmentDirectory = null;
284
282
  const startupReservationState = { current: null };
@@ -288,7 +286,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
288
286
  let memoryPruneFailurePhase = "diagnostics_before";
289
287
  let memoryPruneBeforeSnapshot = null;
290
288
  let memoryPruneSharedWriteKey = null;
291
- let localMemoryResultPath = null;
292
289
  let executionWorkspace = workspace;
293
290
  let localMemoryTelemetry = null;
294
291
  const inheritedEnv = { ...process.env };
@@ -374,6 +371,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
374
371
  nearBudget,
375
372
  ...(input.projectContext === undefined ? {} : { projectContext: input.projectContext }),
376
373
  };
374
+ const systemPrompt = resolvePrompt(input.systemPrompt, promptContext);
377
375
  if (input.attachments && input.attachments.length > 0) {
378
376
  if (dependencies.cancellation?.isRequested())
379
377
  throw new RuntimeCancelledError();
@@ -407,29 +405,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
407
405
  const attachmentPlan = routeRuntimeAttachments(runtime.name, materialized?.attachments ?? []);
408
406
  const wakePrompt = `${resolvePrompt(input.wakePrompt, promptContext)}${attachmentPlan.promptSuffix}`;
409
407
  memoryPruneTraceId = parseMemoryPruneTraceId(wakePrompt);
410
- if (input.localMemoryV2Enabled === true && memoryPruneTraceId !== null
411
- && isLocalMemoryV2Eligible({
412
- enabledForHandle: true,
413
- agentHomePreexisted: workspace.agentHomePreexisted === true,
414
- memorySeedCreated: executionWorkspace.memorySeedCreated === true && (localMemoryV2Initialized || workspace.localMemoryV2Initialized !== false),
415
- localMemoryV2Initialized,
416
- memoryPrune: true,
417
- })) {
418
- localMemoryResultPath = makeLocalMemoryResultPath(executionWorkspace.runDir, input.executionId);
419
- }
420
- const baseSystemPrompt = resolvePrompt(input.systemPrompt, promptContext);
421
- const structuredMemoryInstructions = localMemoryResultPath === null
422
- ? null
423
- : renderLocalMemoryResultInstructions(localMemoryResultPath);
424
- const structuredPromptFits = structuredMemoryInstructions === null
425
- || input.maxSystemPromptBytes === undefined
426
- || Buffer.byteLength(baseSystemPrompt, "utf8") + Buffer.byteLength(structuredMemoryInstructions, "utf8")
427
- <= input.maxSystemPromptBytes;
428
- if (!structuredPromptFits)
429
- localMemoryResultPath = null;
430
- const systemPrompt = localMemoryResultPath === null
431
- ? baseSystemPrompt
432
- : `${baseSystemPrompt}${structuredMemoryInstructions ?? ""}`;
433
408
  try {
434
409
  localMemoryTelemetry = createLocalMemoryTelemetry({
435
410
  executionId: input.executionId,
@@ -917,24 +892,6 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
917
892
  const { exitCode, spawnError, terminationSignal } = runtimeExit;
918
893
  localMemoryTelemetry?.markRuntimeExited(exitCode);
919
894
  memoryPruneRuntimeExitCode = exitCode;
920
- if (localMemoryResultPath !== null) {
921
- const committed = await commitLocalMemoryResultAfterExit({
922
- resultPath: localMemoryResultPath,
923
- agentDir: executionWorkspace.dir,
924
- executionId: input.executionId,
925
- pruneTraceId: memoryPruneTraceId,
926
- exitCode,
927
- });
928
- if (committed.status === "failed") {
929
- dslog("local_memory.commit_failed", "结构化本地记忆候选未落地,保留结果文件", {
930
- level: "WARN",
931
- execution_id: input.executionId,
932
- agent_handle: input.handle,
933
- error_code: committed.error,
934
- result_path: localMemoryResultPath,
935
- });
936
- }
937
- }
938
895
  const errorTail = [
939
896
  stderrTail.trim(),
940
897
  spawnError === undefined
@@ -191,11 +191,10 @@ export async function collectMachineHello(agentsRoot, executionLimits, runtimePl
191
191
  profileName: dependencies.profileName,
192
192
  daemonHome: dependencies.daemonHome ?? daemonHome(),
193
193
  agentsRoot,
194
- ...(dependencies.profileName.match(/^nw-(?:production|in|dev)-([a-z0-9][a-z0-9_-]{0,39})$/u)?.[1] === undefined
194
+ ...(dependencies.profileName.match(/^nw-(?:in|dev)-([a-z0-9][a-z0-9_-]{0,39})$/u)?.[1] === undefined
195
195
  ? {}
196
- : { localInstanceName: dependencies.profileName.match(/^nw-(?:production|in|dev)-([a-z0-9][a-z0-9_-]{0,39})$/u)[1] }),
196
+ : { localInstanceName: dependencies.profileName.match(/^nw-(?:in|dev)-([a-z0-9][a-z0-9_-]{0,39})$/u)[1] }),
197
197
  }),
198
- ...(dependencies.storageIdentity ?? {}),
199
198
  ...(backend.supported ? {
200
199
  executionProtocol: EXECUTION_PROTOCOL,
201
200
  executionLimits: Object.freeze({
package/dist/main.js CHANGED
@@ -7,7 +7,6 @@
7
7
  */
8
8
  import { parseArgs } from "node:util";
9
9
  import { homedir } from "node:os";
10
- import { join } from "node:path";
11
10
  import { loadConfig, ConfigError } from "./config.js";
12
11
  import { detectDaemonLang, formatDaemonText, translateDaemon } from "./i18n.js";
13
12
  import { cliVersion, daemonVersion } from "./machine-info.js";
@@ -22,18 +21,7 @@ import { formatDaemonStartupError } from "./daemon-startup-error.js";
22
21
  import { assertManagedServiceStartup } from "./managed-service-startup.js";
23
22
  import { daemonGlobalInstallation } from "./daemon-installation.js";
24
23
  import { acquireDaemonInstallationLease, runWithDaemonInstallationLease, } from "./daemon-installation-lease.js";
25
- import { loadStorageRuntime } from "./storage-runtime.js";
26
- import { readStorageMigrationReceipt } from "./storage-migration-controller.js";
27
- import { storageMigrationStatusReplay } from "./storage-status-replay.js";
28
- import { runStorageWorkerCommand } from "./storage-worker-command.js";
29
- import { probeStorageMigrationRuntime } from "./storage-runtime-readiness.js";
30
- import { createStorageTargetConfirmationHandler } from "./storage-target-confirmation.js";
31
24
  async function main() {
32
- const workerResult = await runStorageWorkerCommand(process.argv.slice(2));
33
- if (workerResult !== null) {
34
- process.exitCode = workerResult;
35
- return;
36
- }
37
25
  const computerResult = await runComputerCommand(process.argv.slice(2));
38
26
  if (computerResult !== null) {
39
27
  process.exitCode = computerResult;
@@ -69,10 +57,8 @@ async function main() {
69
57
  // 不把 machine token 放进 argv / plist / systemd unit / scheduled task。
70
58
  if (values["daemon-home"])
71
59
  process.env.CREW_DAEMON_HOME = values["daemon-home"];
72
- let activeDaemonHome;
73
60
  if (values.profile) {
74
61
  const home = daemonHome();
75
- activeDaemonHome = home;
76
62
  const profile = await loadProfile(values.profile, home);
77
63
  if (cmd === "serve") {
78
64
  await assertProfileAgentsRootUnique(profile, home, homedir());
@@ -85,12 +71,6 @@ async function main() {
85
71
  agentsRoot: resolveAgentsRoot(profile.agentsRoot, homedir(), process.platform),
86
72
  nodePath: process.execPath,
87
73
  entryPath: builtDaemonEntry(),
88
- environment: {
89
- ...(process.env.NOWWORK_HOME === undefined ? {} : { NOWWORK_HOME: process.env.NOWWORK_HOME }),
90
- ...(process.env.NOWWORK_ENV_TAG === undefined ? {} : { NOWWORK_ENV_TAG: process.env.NOWWORK_ENV_TAG }),
91
- ...(process.env.NOWWORK_STORAGE_POLICY_PATH === undefined
92
- ? {} : { NOWWORK_STORAGE_POLICY_PATH: process.env.NOWWORK_STORAGE_POLICY_PATH }),
93
- },
94
74
  });
95
75
  }
96
76
  applyProfileToEnv(profile, process.env);
@@ -120,72 +100,9 @@ async function main() {
120
100
  const installationLease = installation === null
121
101
  ? null
122
102
  : await acquireDaemonInstallationLease(installation.npmPrefix);
123
- const storageRuntime = values.profile === undefined
124
- ? null
125
- : await loadStorageRuntime({
126
- profile: values.profile,
127
- serverUrl: config.serverUrl,
128
- userHome: homedir(),
129
- platform: process.platform,
130
- environment: process.env,
131
- });
132
- const migrationRuntime = values.profile === undefined || activeDaemonHome === undefined || storageRuntime !== null
133
- ? { ready: false, reason: "not_legacy_source" }
134
- : await probeStorageMigrationRuntime({
135
- profile: values.profile,
136
- serverUrl: config.serverUrl,
137
- currentDaemonHome: activeDaemonHome,
138
- currentAgentsRoot: config.agentsRoot,
139
- userHome: homedir(),
140
- platform: process.platform,
141
- ...(process.getuid === undefined ? {} : { uid: process.getuid() }),
142
- environment: process.env,
143
- });
144
- const targetConfirmation = storageRuntime?.identity.storageMigrationId === undefined
145
- ? null
146
- : createStorageTargetConfirmationHandler({
147
- backupsRoot: storageRuntime.local.backupsRoot,
148
- identity: storageRuntime.identity,
149
- profile: storageRuntime.installation.profile,
150
- completionTimeoutMs: 180_000,
151
- });
152
- const storageMigration = storageRuntime !== null
153
- ? {
154
- ready: false,
155
- statusOutboxPath: join(storageRuntime.local.stateRoot, "storage-status-outbox.json"),
156
- identity: storageRuntime.identity,
157
- ...(targetConfirmation === null ? {} : { confirm: targetConfirmation.handle }),
158
- ...(storageRuntime.identity.storageMigrationId === undefined ? {} : {
159
- replayStatus: async (send) => {
160
- const migrationId = storageRuntime.identity.storageMigrationId;
161
- const receipt = await readStorageMigrationReceipt(storageRuntime.local.backupsRoot, migrationId);
162
- if (receipt === null)
163
- throw new Error(`storage migration receipt not found: ${migrationId}`);
164
- for (const frame of storageMigrationStatusReplay(receipt, {
165
- migrationId,
166
- environment: storageRuntime.identity.storageEnvironment,
167
- workspaceId: storageRuntime.identity.storageWorkspaceId,
168
- profile: storageRuntime.installation.profile,
169
- }))
170
- send(frame);
171
- },
172
- }),
173
- }
174
- : migrationRuntime.ready
175
- ? {
176
- ready: true,
177
- statusOutboxPath: migrationRuntime.runtime.statusOutboxPath,
178
- identity: migrationRuntime.runtime.identity,
179
- controller: migrationRuntime.runtime.controller,
180
- attach: migrationRuntime.runtime.attach,
181
- bindServerIdentity: migrationRuntime.runtime.bindServerIdentity,
182
- replayStatus: migrationRuntime.runtime.replayStatus,
183
- }
184
- : undefined;
185
103
  await runWithDaemonInstallationLease(installationLease, async () => {
186
104
  const service = serve(config, {
187
105
  ...(values.profile === undefined ? {} : { profileName: values.profile }),
188
- ...(storageMigration === undefined ? {} : { storageMigration }),
189
106
  });
190
107
  await runServeLifecycle(service);
191
108
  });
@@ -1,6 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { acquireDaemonInstallationLease } from "./daemon-installation-lease.js";
3
- import { installService, ensureServiceEnabled, readServiceDescriptor, serviceAction, replaceInstalledServiceDescriptor, uninstallService, } from "./computer-service.js";
3
+ import { installService, ensureServiceEnabled, readServiceDescriptor, serviceAction, uninstallService, } from "./computer-service.js";
4
4
  import { ManagedServiceConflictError, ManagedServiceRegistryError, assertRegistryCoversDescriptors, listManagedServiceDescriptorPaths, managedServiceIdentityMatches, managedServiceConflict, normalizeManagedServiceRecord, serviceDescriptorSha256, withManagedServiceRegistryLock, } from "./managed-service-registry.js";
5
5
  function requirePortableManagedSpec(spec) {
6
6
  if ((spec.platform !== "darwin" && spec.platform !== "linux")
@@ -35,17 +35,6 @@ function sameManagedIdentity(left, right) {
35
35
  const { generation: _generation, createdAt: _createdAt, ...expected } = right;
36
36
  return managedServiceIdentityMatches(left, expected);
37
37
  }
38
- function sameManagedResource(left, right) {
39
- const { generation: _leftGeneration, createdAt: _leftCreatedAt, descriptorSha256: _leftHash, ...leftResource } = left;
40
- const { generation: _rightGeneration, createdAt: _rightCreatedAt, descriptorSha256: _rightHash, ...rightResource } = right;
41
- return JSON.stringify(leftResource) === JSON.stringify(rightResource);
42
- }
43
- function descriptorDiffIsStorageEnvironmentOnly(previous, next) {
44
- if (!next.includes("\n <key>EnvironmentVariables</key>\n"))
45
- return false;
46
- const strip = (descriptor) => descriptor.replace(/\n <key>EnvironmentVariables<\/key>\n <dict>\n(?: <key>(?:NOWWORK_HOME|NOWWORK_ENV_TAG|NOWWORK_STORAGE_POLICY_PATH)<\/key><string>[^<]*<\/string>\n)+ <\/dict>\n/u, "\n");
47
- return strip(previous) === strip(next);
48
- }
49
38
  function assertRecordDescribesSpec(input) {
50
39
  requirePortableManagedSpec(input.spec);
51
40
  const { spec, record } = input;
@@ -62,20 +51,8 @@ function assertRecordDescribesSpec(input) {
62
51
  }
63
52
  }
64
53
  export async function installManagedService(input) {
54
+ assertRecordDescribesSpec(input);
65
55
  requirePortableManagedSpec(input.spec);
66
- const requestedDescriptor = input.spec.descriptor;
67
- const expectedDescriptorHash = serviceDescriptorSha256(input.spec.descriptor);
68
- const expectedPath = normalizeManagedServiceRecord({
69
- ...input.record,
70
- descriptorPath: input.spec.descriptorPath,
71
- descriptorSha256: expectedDescriptorHash,
72
- }).descriptorPath;
73
- if (input.record.platform !== input.spec.platform
74
- || input.record.serviceId !== input.spec.id
75
- || input.record.profile !== input.spec.profile
76
- || input.record.descriptorPath !== expectedPath) {
77
- throw new ManagedServiceRegistryError(`Managed service record does not describe '${input.spec.id}'`);
78
- }
79
56
  const acquire = input.acquireInstallationLease ?? acquireDaemonInstallationLease;
80
57
  await withManagedServiceRegistryLock(input.registry ?? {}, async ({ services, replace }) => {
81
58
  const exact = services.find((record) => sameManagedIdentity(record, input.record));
@@ -99,28 +76,6 @@ export async function installManagedService(input) {
99
76
  }
100
77
  return;
101
78
  }
102
- const registered = services.find((record) => record.serviceId === input.record.serviceId
103
- && record.profile === input.record.profile
104
- && record.daemonHome === input.record.daemonHome);
105
- if (registered !== undefined && sameManagedResource(registered, input.record)) {
106
- const current = await readServiceDescriptor(input.spec);
107
- if (current === null || serviceDescriptorSha256(current) !== registered.descriptorSha256) {
108
- throw new ManagedServiceRegistryError(`Installed descriptor for '${input.spec.id}' does not match its registry record`);
109
- }
110
- if (!descriptorDiffIsStorageEnvironmentOnly(current, requestedDescriptor)) {
111
- throw new ManagedServiceRegistryError(`Installed descriptor for '${input.spec.id}' conflicts with the requested identity`);
112
- }
113
- await replaceInstalledServiceDescriptor(input.spec, current, input.runner);
114
- try {
115
- await replace(services.map((record) => record === registered ? input.record : record));
116
- }
117
- catch (error) {
118
- const rollbackSpec = { ...input.spec, descriptor: current };
119
- await replaceInstalledServiceDescriptor(rollbackSpec, requestedDescriptor, input.runner).catch(() => undefined);
120
- throw error;
121
- }
122
- return;
123
- }
124
79
  const conflict = managedServiceConflict(services, input.record);
125
80
  if (conflict !== null) {
126
81
  throw new ManagedServiceConflictError(conflict.resource, input.record, conflict.owner);