@nowcrew/daemon 0.6.80-beta.2 → 0.6.80-beta.3

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 (31) hide show
  1. package/dist/computer-cli.js +3 -12
  2. package/dist/computer-service.js +12 -0
  3. package/dist/daemon-local-home.js +23 -0
  4. package/dist/daemon-update-eligibility.js +7 -4
  5. package/dist/local-executor.js +7 -10
  6. package/dist/main.js +5 -12
  7. package/dist/managed-service-lifecycle.js +6 -2
  8. package/dist/package-manager-env.js +75 -0
  9. package/dist/project-skills/capability.js +109 -0
  10. package/dist/project-skills/controller-convergence.js +57 -0
  11. package/dist/project-skills/projection-set-switch.js +419 -0
  12. package/dist/project-skills/projection-state-domain.js +170 -0
  13. package/dist/project-skills/projection-state-store.js +842 -0
  14. package/dist/project-skills/projection-state-transaction.js +322 -0
  15. package/dist/project-skills/projection-state.js +3 -0
  16. package/dist/project-skills/runtime-root-bootstrap.js +47 -0
  17. package/dist/project-skills/runtime-root-domain.js +268 -0
  18. package/dist/project-skills/runtime-root-gc.js +293 -0
  19. package/dist/project-skills/runtime-root-lease-artifact.js +46 -0
  20. package/dist/project-skills/runtime-root-leases.js +487 -0
  21. package/dist/project-skills/runtime-root-source-identity.js +60 -0
  22. package/dist/project-skills/runtime-root-startup.js +49 -0
  23. package/dist/project-skills/runtime-root-state-artifact-domain.js +143 -0
  24. package/dist/project-skills/runtime-root-state-index.js +356 -0
  25. package/dist/project-skills/runtime-root-store.js +722 -0
  26. package/dist/project-skills/serve-capability.js +28 -0
  27. package/dist/project-skills/serve-startup.js +37 -0
  28. package/dist/runner.js +1 -1
  29. package/dist/runtime-env.js +21 -0
  30. package/dist/serve.js +1 -1
  31. package/package.json +9 -10
@@ -3,7 +3,7 @@ import { homedir } from "node:os";
3
3
  import { posix, resolve, win32 } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { assertProfileAgentsRootUnique, daemonHome, inspectProfileAgentsRoot, listProfiles, loadProfile, PROFILE_AGENTS_ROOT_CONFLICT_MESSAGE, ProfileAgentsRootConflictError, profileIsPrivate, profilePath, publicProfile, removeProfile, resolveAgentsRoot, saveProfile, windowsDpapiProtector, } from "./computer-profile.js";
6
- import { buildServiceSpec, doctorService, hardenWindowsProfile, installService, readServiceDescriptor, serviceAction, serviceStatus, systemCommandRunner, uninstallService, upgradeDaemon, windowsProfileIsPrivate, } from "./computer-service.js";
6
+ import { buildServiceSpec, doctorService, hardenWindowsProfile, installService, readServiceDescriptor, serviceAction, serviceStatus, systemCommandRunner, uninstallService, upgradeDaemon, windowsProfileIsPrivate, storageServiceEnvironment, } from "./computer-service.js";
7
7
  import { releaseTagForServerUrl, assertReleaseTagForServerUrl } from "./automatic-install-target.js";
8
8
  import { normalizeDaemonNpmRegistry } from "./npm-registry.js";
9
9
  import { npmInvocation } from "./npm-command.js";
@@ -89,17 +89,6 @@ function translatedError(error, td) {
89
89
  }
90
90
  return td(error.message);
91
91
  }
92
- function storageServiceEnvironment(env) {
93
- return {
94
- ...(env.NOWWORK_HOME === undefined ? {} : { NOWWORK_HOME: env.NOWWORK_HOME }),
95
- ...(env.NOWWORK_ENV_TAG === undefined ? {} : { NOWWORK_ENV_TAG: env.NOWWORK_ENV_TAG }),
96
- ...(env.NOWWORK_STORAGE_POLICY_PATH === undefined
97
- ? {} : { NOWWORK_STORAGE_POLICY_PATH: env.NOWWORK_STORAGE_POLICY_PATH }),
98
- ...(env.NOWWORK_LOCAL_HOME === undefined ? {} : { NOWWORK_LOCAL_HOME: env.NOWWORK_LOCAL_HOME }),
99
- ...(env.NOWWORK_LOCAL_HOME_MODE === undefined && env.NOWWORK_STORAGE_MODE === undefined
100
- ? {} : { NOWWORK_LOCAL_HOME_MODE: env.NOWWORK_LOCAL_HOME_MODE ?? env.NOWWORK_STORAGE_MODE }),
101
- };
102
- }
103
92
  export async function runComputerCommand(argv, overrides = {}) {
104
93
  const command = argv[0];
105
94
  if (!command || !COMPUTER_COMMANDS.has(command))
@@ -195,6 +184,8 @@ export async function runComputerCommand(argv, overrides = {}) {
195
184
  platform: deps.platform,
196
185
  explicit: deps.env.NOWWORK_LOCAL_HOME,
197
186
  mode: configuredLocalHomeMode,
187
+ ...(resolvedProfile === undefined ? {} : { profile: resolvedProfile }),
188
+ ...(home === undefined ? {} : { daemonHome: home }),
198
189
  });
199
190
  const profileStorage = {
200
191
  platform: deps.platform,
@@ -32,6 +32,18 @@ export const systemCommandRunner = async (command, args) => {
32
32
  };
33
33
  }
34
34
  };
35
+ /** Build the non-secret storage environment persisted in managed services. */
36
+ export function storageServiceEnvironment(env) {
37
+ return {
38
+ ...(env.NOWWORK_HOME === undefined ? {} : { NOWWORK_HOME: env.NOWWORK_HOME }),
39
+ ...(env.NOWWORK_ENV_TAG === undefined ? {} : { NOWWORK_ENV_TAG: env.NOWWORK_ENV_TAG }),
40
+ ...(env.NOWWORK_STORAGE_POLICY_PATH === undefined
41
+ ? {} : { NOWWORK_STORAGE_POLICY_PATH: env.NOWWORK_STORAGE_POLICY_PATH }),
42
+ ...(env.NOWWORK_LOCAL_HOME === undefined ? {} : { NOWWORK_LOCAL_HOME: env.NOWWORK_LOCAL_HOME }),
43
+ ...(env.NOWWORK_LOCAL_HOME_MODE === undefined && env.NOWWORK_STORAGE_MODE === undefined
44
+ ? {} : { NOWWORK_LOCAL_HOME_MODE: env.NOWWORK_LOCAL_HOME_MODE ?? env.NOWWORK_STORAGE_MODE }),
45
+ };
46
+ }
35
47
  function serviceIdentitySuffix(profileHome, userHome) {
36
48
  if (profileHome === undefined || resolve(profileHome) === resolve(userHome, ".crew", "daemon"))
37
49
  return "";
@@ -4,6 +4,13 @@ import { posix, win32 } from "node:path";
4
4
  const PROFILE = /^[a-z0-9][a-z0-9_-]{0,47}$/u;
5
5
  const CONTROL = /[\0\r\n]/u;
6
6
  const pathsFor = (platform) => platform === "win32" ? win32 : posix;
7
+ function samePath(left, right, platform) {
8
+ const normalizedLeft = pathsFor(platform).resolve(left);
9
+ const normalizedRight = pathsFor(platform).resolve(right);
10
+ return platform === "win32"
11
+ ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase()
12
+ : normalizedLeft === normalizedRight;
13
+ }
7
14
  export function parseDaemonLocalHomeMode(value) {
8
15
  if (value === undefined || value.trim() === "")
9
16
  return "auto";
@@ -86,6 +93,22 @@ export function resolveDaemonLocalStateRoot(input) {
86
93
  return legacyRoot;
87
94
  if (input.explicit !== undefined || mode === "nowwork")
88
95
  return nowworkRoot;
96
+ // Managed service descriptors created before local-home was persisted carry
97
+ // only --daemon-home. Do not treat every --daemon-home as legacy: migrated
98
+ // services also use that argument, but point it at ~/.nowwork/config.
99
+ // Prefer the descriptor's exact home when it identifies either layout.
100
+ if (input.daemonHome !== undefined && input.profile !== undefined) {
101
+ const nowworkDaemonHome = resolution("nowwork", nowworkRoot, input.profile, input.platform, "auto_nowwork_existing").daemonHome;
102
+ const legacyDaemonHome = resolution("legacy", legacyRoot, input.profile, input.platform, "auto_legacy_existing").daemonHome;
103
+ if (samePath(input.daemonHome, nowworkDaemonHome, input.platform))
104
+ return nowworkRoot;
105
+ if (samePath(input.daemonHome, legacyDaemonHome, input.platform))
106
+ return legacyRoot;
107
+ // An arbitrary explicit daemon home has historically implied the legacy
108
+ // local state root. Preserve that behavior unless the descriptor declares
109
+ // a mode explicitly.
110
+ return legacyRoot;
111
+ }
89
112
  const exists = input.exists ?? existsSync;
90
113
  // Once the new root exists, shared state must follow it even while the
91
114
  // legacy root is retained as a rollback source.
@@ -4,7 +4,7 @@ import { realpathSync } from "node:fs";
4
4
  import { homedir } from "node:os";
5
5
  import { daemonHome, loadProfile, resolveAgentsRoot } from "./computer-profile.js";
6
6
  import { builtDaemonEntry } from "./computer-cli.js";
7
- import { buildServiceSpec, readServiceDescriptor, serviceStatus, } from "./computer-service.js";
7
+ import { buildServiceSpec, readServiceDescriptor, storageServiceEnvironment, serviceStatus, } from "./computer-service.js";
8
8
  import { daemonGlobalInstallation } from "./daemon-installation.js";
9
9
  import { daemonInstallationLeaseHeld } from "./daemon-installation-lease.js";
10
10
  import { inspectLegacyServiceInstallation } from "./legacy-service-installation.js";
@@ -21,9 +21,9 @@ export async function managedDaemonCapabilities(eligibility) {
21
21
  return [];
22
22
  }
23
23
  }
24
- function defaults(profileName) {
24
+ function defaults(profileName, selectedLocalHome) {
25
25
  const userHome = homedir();
26
- const localHome = resolveDaemonLocalStateRoot({
26
+ const localHome = selectedLocalHome ?? resolveDaemonLocalStateRoot({
27
27
  userHome,
28
28
  platform: process.platform,
29
29
  explicit: process.env.NOWWORK_LOCAL_HOME,
@@ -31,6 +31,7 @@ function defaults(profileName) {
31
31
  });
32
32
  return {
33
33
  platform: process.platform,
34
+ localHome,
34
35
  profileHome: daemonHome(process.env, profileName),
35
36
  userHome,
36
37
  uid: process.getuid?.(),
@@ -65,7 +66,7 @@ async function registryCoversUpdateScope(services, descriptorPaths, targetDescri
65
66
  return true;
66
67
  }
67
68
  export async function detectDaemonUpdateEligibility(profileName, overrides = {}) {
68
- const deps = { ...defaults(profileName), ...overrides };
69
+ const deps = { ...defaults(profileName, overrides.localHome), ...overrides };
69
70
  if (deps.platform !== "darwin" && deps.platform !== "linux" && deps.platform !== "win32") {
70
71
  return { eligible: false, reason: "unsupported_platform" };
71
72
  }
@@ -92,6 +93,7 @@ export async function detectDaemonUpdateEligibility(profileName, overrides = {})
92
93
  nodePath: deps.nodePath,
93
94
  entryPath: deps.entryPath,
94
95
  profileHome: deps.profileHome,
96
+ environment: storageServiceEnvironment(process.env),
95
97
  });
96
98
  if (deps.platform === "win32") {
97
99
  const scope = await deps.inspectWindowsServiceUpdateScope(spec, installation.npmPrefix)
@@ -169,6 +171,7 @@ export async function detectDaemonUpdateEligibility(profileName, overrides = {})
169
171
  entryPath: deps.entryPath,
170
172
  profileHome: deps.profileHome,
171
173
  legacyServiceId: true,
174
+ environment: storageServiceEnvironment(process.env),
172
175
  });
173
176
  const legacyDescriptor = await deps.readServiceDescriptor(legacySpec).catch(() => null);
174
177
  if (legacyDescriptor !== legacySpec.descriptor) {
@@ -9,7 +9,7 @@ import { DEEPSEEK_CODEX_MODEL, DEEPSEEK_CODEX_REASONING_LEVELS, materializeDeepS
9
9
  import { materializeDefaultCodexHome } from "./runtimes/codex-home.js";
10
10
  import { materializePrivateCodexHome } from "./codex-home-launch.js";
11
11
  import { spawnKimi, KIMI_EFFORT_LEVELS } from "./runtimes/kimi.js";
12
- import { applyDeepSeekHarnessMachineEnv, applyProviderEnv, providerFingerprint, sanitizeEnvVars, } from "./provider-env.js";
12
+ import { applyDeepSeekHarnessMachineEnv, applyProviderEnv, providerFingerprint, } from "./provider-env.js";
13
13
  import { augmentedPath } from "./runtime-path.js";
14
14
  import { extractFinalText, extractRunMeta, normalizeEvent, parseLine, } from "./normalize.js";
15
15
  import { readSession, writeSession, pickResumeId, isNearBudget } from "./session.js";
@@ -33,6 +33,8 @@ import { diffMemoryPruneNotes, evaluateMemoryPrunePostcondition, inspectMemoryPr
33
33
  import { createLocalMemoryTelemetry, logLocalMemoryContextPrepareFailure, logLocalMemoryDiagnosticsFailure, } from "./local-memory-telemetry.js";
34
34
  import { CodexStartupStageParser } from "./codex-startup-stage.js";
35
35
  import { formatRuntimeStartupError } from "./runtime-startup-error.js";
36
+ import { machinePackageManagerEnv, packageManagerEnvFacts } from "./package-manager-env.js";
37
+ import { sanitizeEnvVars } from "./runtime-env.js";
36
38
  import { commitLocalMemoryResultAfterExit, initializeLocalMemoryV2ForWorkspace, isLocalMemoryV2Eligible, localMemoryResultPath as makeLocalMemoryResultPath, renderLocalMemoryResultInstructions, } from "./local-memory/runtime.js";
37
39
  export class RuntimeRunningCallbackTimeoutError extends Error {
38
40
  constructor(runtime, timeoutMs) {
@@ -50,15 +52,6 @@ const MEMORY_PRUNE_DIAGNOSTICS_TIMEOUT_MS = 2_000;
50
52
  const LOCAL_MEMORY_DIAGNOSTICS_TIMEOUT_MS = 250;
51
53
  const CLAUDE_INSTRUCTION_WARNING = "claude_additional_directory_instructions_unverified";
52
54
  const CODEX_COLLABORATION_EXEC_WARNING = "codex_collaboration_disabled_on_exec";
53
- const ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
54
- const RESERVED_ENV = new Set([
55
- "PATH",
56
- "XDG_CONFIG_HOME",
57
- "XDG_DATA_HOME",
58
- "XDG_CACHE_HOME",
59
- "GH_CONFIG_DIR",
60
- "CLOUDSDK_CONFIG",
61
- ]);
62
55
  function withoutClaudeInstructionFlag(env) {
63
56
  if (!(CLAUDE_ADDITIONAL_DIRECTORY_INSTRUCTIONS_ENV in env))
64
57
  return env;
@@ -470,6 +463,9 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
470
463
  const baseEnv = {
471
464
  ...inheritedEnv,
472
465
  ...sanitizeEnvVars(providerConfig.envVars),
466
+ ...(input.trainingProjectContext === undefined
467
+ ? {}
468
+ : machinePackageManagerEnv(inheritedEnv)),
473
469
  ...input.launch.systemEnv,
474
470
  PATH: `${workspace.crewDir}${delimiter}${augmentedPath()}`,
475
471
  CREW_SERVER_URL: input.launch.serverUrl,
@@ -512,6 +508,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
512
508
  qa_runtime_root: childEnv.QA_RUNTIME_ROOT ?? null,
513
509
  qa_runtime_root_present: childEnv.QA_RUNTIME_ROOT !== undefined,
514
510
  inherited_qa_runtime_root_present: inheritedEnv.QA_RUNTIME_ROOT !== undefined,
511
+ ...packageManagerEnvFacts(childEnv),
515
512
  training_root: join(crewHome, "training"),
516
513
  project_context_present: input.projectContext !== undefined,
517
514
  project_ids: JSON.stringify(input.projectContext === undefined
package/dist/main.js CHANGED
@@ -31,6 +31,7 @@ import { runStorageWorkerCommand } from "./storage-worker-command.js";
31
31
  import { probeStorageMigrationRuntime } from "./storage-runtime-readiness.js";
32
32
  import { createStorageTargetConfirmationHandler } from "./storage-target-confirmation.js";
33
33
  import { resolveDaemonLocalStateRoot } from "./daemon-local-home.js";
34
+ import { storageServiceEnvironment } from "./computer-service.js";
34
35
  async function main() {
35
36
  const workerResult = await runStorageWorkerCommand(process.argv.slice(2));
36
37
  if (workerResult !== null) {
@@ -78,9 +79,9 @@ async function main() {
78
79
  userHome: homedir(),
79
80
  platform: process.platform,
80
81
  explicit: process.env.NOWWORK_LOCAL_HOME,
81
- mode: process.env.NOWWORK_LOCAL_HOME_MODE
82
- ?? process.env.NOWWORK_STORAGE_MODE
83
- ?? (values["daemon-home"] === undefined ? undefined : "legacy"),
82
+ mode: process.env.NOWWORK_LOCAL_HOME_MODE ?? process.env.NOWWORK_STORAGE_MODE,
83
+ ...(values.profile === undefined ? {} : { profile: values.profile }),
84
+ ...(values["daemon-home"] === undefined ? {} : { daemonHome: values["daemon-home"] }),
84
85
  });
85
86
  if (values.profile) {
86
87
  const home = daemonHome(process.env, values.profile);
@@ -98,15 +99,7 @@ async function main() {
98
99
  agentsRoot: resolveAgentsRoot(profile.agentsRoot, homedir(), process.platform),
99
100
  nodePath: process.execPath,
100
101
  entryPath: builtDaemonEntry(),
101
- environment: {
102
- ...(process.env.NOWWORK_HOME === undefined ? {} : { NOWWORK_HOME: process.env.NOWWORK_HOME }),
103
- ...(process.env.NOWWORK_ENV_TAG === undefined ? {} : { NOWWORK_ENV_TAG: process.env.NOWWORK_ENV_TAG }),
104
- ...(process.env.NOWWORK_STORAGE_POLICY_PATH === undefined
105
- ? {} : { NOWWORK_STORAGE_POLICY_PATH: process.env.NOWWORK_STORAGE_POLICY_PATH }),
106
- ...(process.env.NOWWORK_LOCAL_HOME === undefined ? {} : { NOWWORK_LOCAL_HOME: process.env.NOWWORK_LOCAL_HOME }),
107
- ...(process.env.NOWWORK_LOCAL_HOME_MODE === undefined && process.env.NOWWORK_STORAGE_MODE === undefined
108
- ? {} : { NOWWORK_LOCAL_HOME_MODE: process.env.NOWWORK_LOCAL_HOME_MODE ?? process.env.NOWWORK_STORAGE_MODE }),
109
- },
102
+ environment: storageServiceEnvironment(process.env),
110
103
  });
111
104
  }
112
105
  applyProfileToEnv(profile, process.env);
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { acquireDaemonInstallationLease } from "./daemon-installation-lease.js";
2
+ import { acquireDaemonInstallationLease, } from "./daemon-installation-lease.js";
3
3
  import { installService, ensureServiceEnabled, readServiceDescriptor, serviceAction, replaceInstalledServiceDescriptor, 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) {
@@ -43,7 +43,7 @@ function sameManagedResource(left, right) {
43
43
  function descriptorDiffIsStorageEnvironmentOnly(previous, next) {
44
44
  if (!next.includes("\n <key>EnvironmentVariables</key>\n"))
45
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");
46
+ const strip = (descriptor) => descriptor.replace(/\n <key>EnvironmentVariables<\/key>\n <dict>\n(?: <key>(?:NOWWORK_HOME|NOWWORK_ENV_TAG|NOWWORK_STORAGE_POLICY_PATH|NOWWORK_LOCAL_HOME|NOWWORK_LOCAL_HOME_MODE)<\/key><string>[^<]*<\/string>\n)+ <\/dict>\n/u, "\n");
47
47
  return strip(previous) === strip(next);
48
48
  }
49
49
  function assertRecordDescribesSpec(input) {
@@ -90,6 +90,7 @@ export async function installManagedService(input) {
90
90
  }
91
91
  const lease = await acquire(input.record.npmPrefix, {
92
92
  ...(input.registry?.userHome === undefined ? {} : { userHome: input.registry.userHome }),
93
+ ...(input.registry?.localHome === undefined ? {} : { localHome: input.registry.localHome }),
93
94
  });
94
95
  try {
95
96
  await installService(input.spec, input.runner);
@@ -136,6 +137,7 @@ export async function installManagedService(input) {
136
137
  }
137
138
  const lease = await acquire(input.record.npmPrefix, {
138
139
  ...(input.registry?.userHome === undefined ? {} : { userHome: input.registry.userHome }),
140
+ ...(input.registry?.localHome === undefined ? {} : { localHome: input.registry.localHome }),
139
141
  });
140
142
  try {
141
143
  const descriptor = await readServiceDescriptor(input.spec);
@@ -170,6 +172,7 @@ export async function uninstallUnregisteredManagedService(input) {
170
172
  const acquire = input.acquireInstallationLease ?? acquireDaemonInstallationLease;
171
173
  const lease = await acquire(input.npmPrefix, {
172
174
  ...(input.registry?.userHome === undefined ? {} : { userHome: input.registry.userHome }),
175
+ ...(input.registry?.localHome === undefined ? {} : { localHome: input.registry.localHome }),
173
176
  });
174
177
  try {
175
178
  await uninstallService(input.spec, input.runner);
@@ -199,6 +202,7 @@ export async function uninstallManagedService(input) {
199
202
  }
200
203
  const lease = await acquire(input.record.npmPrefix, {
201
204
  ...(input.registry?.userHome === undefined ? {} : { userHome: input.registry.userHome }),
205
+ ...(input.registry?.localHome === undefined ? {} : { localHome: input.registry.localHome }),
202
206
  });
203
207
  try {
204
208
  if (descriptor !== null)
@@ -0,0 +1,75 @@
1
+ import { readFileSync } from "node:fs";
2
+ function nonEmpty(value) {
3
+ const normalized = value?.trim();
4
+ return normalized === undefined || normalized === "" ? undefined : normalized;
5
+ }
6
+ function registryUrl(value) {
7
+ const normalized = nonEmpty(value);
8
+ if (normalized === undefined)
9
+ return undefined;
10
+ try {
11
+ const protocol = new URL(normalized).protocol;
12
+ return protocol === "http:" || protocol === "https:" ? normalized : undefined;
13
+ }
14
+ catch {
15
+ return undefined;
16
+ }
17
+ }
18
+ function configuredPath(env) {
19
+ const explicit = env.CREW_PACKAGE_MANAGER_USERCONFIG?.trim();
20
+ if (explicit !== undefined && explicit !== "")
21
+ return explicit;
22
+ return undefined;
23
+ }
24
+ function registryFromNpmrc(path) {
25
+ if (path === undefined)
26
+ return undefined;
27
+ try {
28
+ const line = readFileSync(path, "utf8")
29
+ .split(/\r?\n/u)
30
+ .find((entry) => /^\s*registry\s*=/u.test(entry));
31
+ return registryUrl(line?.replace(/^\s*registry\s*=\s*/u, ""));
32
+ }
33
+ catch {
34
+ return undefined;
35
+ }
36
+ }
37
+ function npmrcHasAuth(path) {
38
+ if (path === undefined)
39
+ return false;
40
+ try {
41
+ return /^\s*(?:(?:\/\/[^=\s]+:)?_(?:auth|authToken|password))\s*=/imu.test(readFileSync(path, "utf8"));
42
+ }
43
+ catch {
44
+ return false;
45
+ }
46
+ }
47
+ /**
48
+ * Keep machine-owned npm credentials available for explicitly authorized
49
+ * training dependency installs. The daemon must opt in with
50
+ * CREW_PACKAGE_MANAGER_USERCONFIG; Agent env cannot reach this path.
51
+ */
52
+ export function machinePackageManagerEnv(env = process.env) {
53
+ const npmUserConfig = configuredPath(env);
54
+ const yarnRegistry = npmUserConfig === undefined
55
+ ? undefined
56
+ : registryUrl(env.YARN_REGISTRY) ?? registryFromNpmrc(npmUserConfig);
57
+ const entries = npmUserConfig === undefined
58
+ ? []
59
+ : [["NPM_CONFIG_USERCONFIG", npmUserConfig]];
60
+ const alwaysAuth = npmrcHasAuth(npmUserConfig) ? "true" : undefined;
61
+ return Object.fromEntries(yarnRegistry === undefined && alwaysAuth === undefined
62
+ ? entries
63
+ : [
64
+ ...entries,
65
+ ...(yarnRegistry === undefined ? [] : [["YARN_REGISTRY", yarnRegistry]]),
66
+ ...(alwaysAuth === undefined ? [] : [["NPM_CONFIG_ALWAYS_AUTH", alwaysAuth]]),
67
+ ]);
68
+ }
69
+ export function packageManagerEnvFacts(env) {
70
+ return {
71
+ npm_userconfig_present: Boolean(env.NPM_CONFIG_USERCONFIG?.trim()),
72
+ yarn_registry_present: Boolean(env.YARN_REGISTRY?.trim()),
73
+ npm_always_auth_present: Boolean(env.NPM_CONFIG_ALWAYS_AUTH?.trim()),
74
+ };
75
+ }
@@ -0,0 +1,109 @@
1
+ import { PROJECT_SKILLS_CAPABILITY, PROJECT_SKILL_PROJECTION_V2_CAPABILITY, } from "./types.js";
2
+ export function projectSkillV2RolloutReady(platform, readiness) {
3
+ const platformReady = platform === "darwin"
4
+ || platform === "linux"
5
+ || (platform === "win32" && readiness.nativeWindowsSmokePassed);
6
+ return platformReady
7
+ && readiness.runtimeRootStorePrimitiveReady
8
+ && readiness.runtimeRootStoreRecoveryReady
9
+ && readiness.leaseRecoveryReady
10
+ && readiness.controllerReady
11
+ && readiness.executionHandlerReady
12
+ && readiness.codexAdapterReady
13
+ && readiness.claudeAdapterReady;
14
+ }
15
+ export function effectiveProjectSkillCapabilities(capabilities, projectSkillsReady, projectSkillV2Ready) {
16
+ const projectSkillCapabilitiesReady = projectSkillsReady && projectSkillV2Ready;
17
+ if (!projectSkillCapabilitiesReady
18
+ && !capabilities.includes(PROJECT_SKILLS_CAPABILITY)
19
+ && !capabilities.includes(PROJECT_SKILL_PROJECTION_V2_CAPABILITY)
20
+ && Object.isFrozen(capabilities))
21
+ return capabilities;
22
+ const filtered = capabilities.filter((capability) => capability !== PROJECT_SKILL_PROJECTION_V2_CAPABILITY
23
+ && (projectSkillCapabilitiesReady || capability !== PROJECT_SKILLS_CAPABILITY));
24
+ if (!projectSkillCapabilitiesReady || !filtered.includes(PROJECT_SKILLS_CAPABILITY)) {
25
+ return Object.freeze(filtered);
26
+ }
27
+ const v1Index = filtered.indexOf(PROJECT_SKILLS_CAPABILITY);
28
+ return Object.freeze([
29
+ ...filtered.slice(0, v1Index + 1),
30
+ PROJECT_SKILL_PROJECTION_V2_CAPABILITY,
31
+ ...filtered.slice(v1Index + 1),
32
+ ]);
33
+ }
34
+ export function withProjectSkillCapabilityStatus(value, status) {
35
+ return {
36
+ ...value,
37
+ projectSkillsStatus: status,
38
+ capabilities: status === "ready"
39
+ ? value.capabilities
40
+ : effectiveProjectSkillCapabilities(value.capabilities, false, false),
41
+ };
42
+ }
43
+ const settleReadinessGate = async (operation) => {
44
+ try {
45
+ return await operation() === true;
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ };
51
+ export async function createProjectSkillConnectionCapabilitySnapshot(input) {
52
+ const runtimeRootStorePrimitiveReady = await settleReadinessGate(input.probeRuntimeRootStorePrimitive);
53
+ const controllerReady = await settleReadinessGate(input.initializeController);
54
+ const recoveryReady = await settleReadinessGate(input.recoverRuntimeRootsAndLeases);
55
+ const readiness = Object.freeze({
56
+ runtimeRootStorePrimitiveReady,
57
+ runtimeRootStoreRecoveryReady: recoveryReady,
58
+ leaseRecoveryReady: recoveryReady,
59
+ controllerReady,
60
+ executionHandlerReady: true,
61
+ codexAdapterReady: true,
62
+ claudeAdapterReady: true,
63
+ nativeWindowsSmokePassed: false,
64
+ });
65
+ const projectSkillsReady = projectSkillV2RolloutReady(input.platform, readiness);
66
+ return Object.freeze({
67
+ capabilities: effectiveProjectSkillCapabilities(input.baseCapabilities, projectSkillsReady, projectSkillsReady),
68
+ projectSkillsReady,
69
+ readiness,
70
+ });
71
+ }
72
+ export async function prepareProjectSkillConnectionCapabilities(input) {
73
+ const snapshot = await createProjectSkillConnectionCapabilitySnapshot({
74
+ ...input,
75
+ baseCapabilities: [...input.baseCapabilities, ...await input.additionalCapabilities()],
76
+ });
77
+ input.applyReadiness(snapshot.projectSkillsReady);
78
+ return snapshot.capabilities;
79
+ }
80
+ export function createProjectSkillControllerReadiness(input) {
81
+ let status = "initializing";
82
+ let initialization = null;
83
+ const ensure = () => {
84
+ if (status === "ready")
85
+ return Promise.resolve(true);
86
+ if (initialization !== null)
87
+ return initialization;
88
+ status = "initializing";
89
+ const attempt = input.initialize().then(() => {
90
+ status = "ready";
91
+ return true;
92
+ }, (error) => {
93
+ status = "unavailable";
94
+ input.onUnavailable(error);
95
+ return false;
96
+ }).finally(() => {
97
+ if (initialization === attempt)
98
+ initialization = null;
99
+ input.onSettled();
100
+ });
101
+ initialization = attempt;
102
+ return attempt;
103
+ };
104
+ return Object.freeze({
105
+ status: () => status,
106
+ ensure,
107
+ applyPreflight: (ready) => { status = ready ? "ready" : "unavailable"; },
108
+ });
109
+ }
@@ -0,0 +1,57 @@
1
+ import { ProjectProjectionError, } from "./reconciler.js";
2
+ export const MAX_REMEMBERED_PROJECT_SKILL_AGENTS = 64;
3
+ const compareBinary = (left, right) => {
4
+ if (left < right)
5
+ return -1;
6
+ if (left > right)
7
+ return 1;
8
+ return 0;
9
+ };
10
+ const copySnapshot = (snapshot) => Object.freeze({
11
+ bindings: Object.freeze(snapshot.bindings.map((binding) => Object.freeze({ ...binding }))),
12
+ generation: snapshot.generation,
13
+ });
14
+ export function createProjectSkillSnapshotEpoch(maxEntries = MAX_REMEMBERED_PROJECT_SKILL_AGENTS) {
15
+ if (!Number.isSafeInteger(maxEntries) || maxEntries < 1) {
16
+ throw new ProjectProjectionError("skill_projection_failed");
17
+ }
18
+ const snapshots = new Map();
19
+ return Object.freeze({
20
+ begin() {
21
+ snapshots.clear();
22
+ },
23
+ remember(handle, snapshot) {
24
+ const previous = snapshots.get(handle);
25
+ if (previous !== undefined && snapshot.generation < previous.generation)
26
+ return;
27
+ if (previous === undefined && snapshots.size >= maxEntries) {
28
+ throw new ProjectProjectionError("skill_projection_failed");
29
+ }
30
+ snapshots.set(handle, copySnapshot(snapshot));
31
+ },
32
+ forget(handle) {
33
+ snapshots.delete(handle);
34
+ },
35
+ entries() {
36
+ return Object.freeze([...snapshots.entries()]
37
+ .sort(([left], [right]) => compareBinary(left, right))
38
+ .map(([handle, snapshot]) => Object.freeze([handle, copySnapshot(snapshot)])));
39
+ },
40
+ });
41
+ }
42
+ const stableErrorCode = (reason) => reason?.code === "skill_projection_snapshot_corrupt"
43
+ ? "skill_projection_snapshot_corrupt"
44
+ : "skill_projection_failed";
45
+ export function toPathPrivateConvergenceResults(entries, results) {
46
+ return Object.freeze(entries.map(([handle], index) => {
47
+ const result = results[index];
48
+ if (result?.status === "fulfilled") {
49
+ return Object.freeze({ handle, status: "fulfilled" });
50
+ }
51
+ return Object.freeze({
52
+ handle,
53
+ status: "rejected",
54
+ errorCode: stableErrorCode(result?.reason),
55
+ });
56
+ }));
57
+ }