@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.
- package/dist/computer-cli.js +3 -12
- package/dist/computer-service.js +12 -0
- package/dist/daemon-local-home.js +23 -0
- package/dist/daemon-update-eligibility.js +7 -4
- package/dist/local-executor.js +7 -10
- package/dist/main.js +5 -12
- package/dist/managed-service-lifecycle.js +6 -2
- package/dist/package-manager-env.js +75 -0
- package/dist/project-skills/capability.js +109 -0
- package/dist/project-skills/controller-convergence.js +57 -0
- package/dist/project-skills/projection-set-switch.js +419 -0
- package/dist/project-skills/projection-state-domain.js +170 -0
- package/dist/project-skills/projection-state-store.js +842 -0
- package/dist/project-skills/projection-state-transaction.js +322 -0
- package/dist/project-skills/projection-state.js +3 -0
- package/dist/project-skills/runtime-root-bootstrap.js +47 -0
- package/dist/project-skills/runtime-root-domain.js +268 -0
- package/dist/project-skills/runtime-root-gc.js +293 -0
- package/dist/project-skills/runtime-root-lease-artifact.js +46 -0
- package/dist/project-skills/runtime-root-leases.js +487 -0
- package/dist/project-skills/runtime-root-source-identity.js +60 -0
- package/dist/project-skills/runtime-root-startup.js +49 -0
- package/dist/project-skills/runtime-root-state-artifact-domain.js +143 -0
- package/dist/project-skills/runtime-root-state-index.js +356 -0
- package/dist/project-skills/runtime-root-store.js +722 -0
- package/dist/project-skills/serve-capability.js +28 -0
- package/dist/project-skills/serve-startup.js +37 -0
- package/dist/runner.js +1 -1
- package/dist/runtime-env.js +21 -0
- package/dist/serve.js +1 -1
- package/package.json +9 -10
package/dist/computer-cli.js
CHANGED
|
@@ -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,
|
package/dist/computer-service.js
CHANGED
|
@@ -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) {
|
package/dist/local-executor.js
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
83
|
-
|
|
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
|
+
}
|