@nowcrew/daemon 0.6.80-beta.1 → 0.6.80
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/agent-ability/resolver.js +10 -7
- package/dist/computer-cli.js +3 -0
- package/dist/computer-service.js +2 -0
- package/dist/execution-protocol.js +1 -0
- package/dist/execution-runner.js +9 -3
- package/dist/local-execution-prompt.js +3 -2
- package/dist/local-executor.js +59 -35
- package/dist/machine-info.js +3 -0
- package/dist/main.js +3 -0
- package/dist/provider-env.js +21 -3
- package/dist/runner.js +1 -1
- package/dist/runtimes/codex-app-server-runner.js +12 -4
- package/dist/runtimes/codex-deepseek-config.js +17 -7
- package/dist/runtimes/codex.js +3 -0
- package/dist/self-training-workspace.js +15 -0
- package/dist/supervised-runtime.js +1 -0
- package/dist/workspace.js +13 -10
- package/package.json +1 -1
- package/dist/package-manager-env.js +0 -75
- package/dist/runtime-env.js +0 -21
|
@@ -10,8 +10,8 @@ import { PROJECT_MANIFEST_SOURCE_PATH, ProjectDependencyManifestSchema, } from "
|
|
|
10
10
|
import { normalizeRepositoryIdentity } from "../repository-identity.js";
|
|
11
11
|
const runFile = promisify(execFile);
|
|
12
12
|
const MAX_FILES = 5_000;
|
|
13
|
-
const
|
|
14
|
-
const
|
|
13
|
+
export const AGENT_ABILITY_MAX_TOTAL_BYTES = 200 * 1024 * 1024;
|
|
14
|
+
export const AGENT_ABILITY_MAX_FILE_BYTES = 100 * 1024 * 1024;
|
|
15
15
|
const MAX_INSTRUCTIONS_BYTES = 96 * 1024;
|
|
16
16
|
const MAX_MEMORY_BYTES = 64 * 1024;
|
|
17
17
|
const AUTHORING_ONLY_ROOTS = new Set([".claude"]);
|
|
@@ -125,10 +125,11 @@ export const collectAbilityFiles = async (root) => {
|
|
|
125
125
|
if (info.isDirectory())
|
|
126
126
|
await visit(absolute);
|
|
127
127
|
else if (info.isFile()) {
|
|
128
|
-
if (info.size >
|
|
128
|
+
if (info.size > AGENT_ABILITY_MAX_FILE_BYTES) {
|
|
129
129
|
throw new Error(`ability_file_size_exceeded:${relative(root, absolute)}`);
|
|
130
|
+
}
|
|
130
131
|
totalBytes += info.size;
|
|
131
|
-
if (totalBytes >
|
|
132
|
+
if (totalBytes > AGENT_ABILITY_MAX_TOTAL_BYTES)
|
|
132
133
|
throw new Error("ability_total_size_exceeded");
|
|
133
134
|
if (files.length >= MAX_FILES)
|
|
134
135
|
throw new Error("ability_file_count_exceeded");
|
|
@@ -262,7 +263,7 @@ export const validateAbilityDependencyDeclarations = (manifest, lock) => {
|
|
|
262
263
|
throw new Error("ability_lock_has_undeclared_dependency");
|
|
263
264
|
}
|
|
264
265
|
};
|
|
265
|
-
const
|
|
266
|
+
export const scanAbilityFiles = (files, manifest) => {
|
|
266
267
|
const findings = [];
|
|
267
268
|
const secrets = [
|
|
268
269
|
/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/u,
|
|
@@ -270,7 +271,9 @@ const scan = (files, manifest) => {
|
|
|
270
271
|
/\bAK(?:ID|IA)[A-Z0-9]{12,}\b/u,
|
|
271
272
|
];
|
|
272
273
|
const executable = new Set([".exe", ".dll", ".dylib", ".so", ".bin", ".class", ".jar"]);
|
|
273
|
-
const allowedBinaryDocuments = new Set([
|
|
274
|
+
const allowedBinaryDocuments = new Set([
|
|
275
|
+
".pdf", ".png", ".jpg", ".jpeg", ".gif", ".webp", ".xlsx", ".xls", ".docx", ".pptx", ".ttf", ".otf",
|
|
276
|
+
]);
|
|
274
277
|
for (const file of files) {
|
|
275
278
|
let text;
|
|
276
279
|
try {
|
|
@@ -396,7 +399,7 @@ export async function resolveAbilityRelease(agentsRoot, command) {
|
|
|
396
399
|
}
|
|
397
400
|
const externalPaths = await verifyExternalDependencies(cacheRoot, resolved.root, lock);
|
|
398
401
|
files = await collectAbilityFiles(resolved.root);
|
|
399
|
-
const security =
|
|
402
|
+
const security = scanAbilityFiles(files, manifest);
|
|
400
403
|
const kinds = new Set(lock.assets.map((asset) => asset.kind));
|
|
401
404
|
const memory = [];
|
|
402
405
|
for (const [layer, mounts] of [["release-policy", manifest.spec.memory.policy], ["release-seed", manifest.spec.memory.seeds]]) {
|
package/dist/computer-cli.js
CHANGED
|
@@ -95,6 +95,9 @@ function storageServiceEnvironment(env) {
|
|
|
95
95
|
...(env.NOWWORK_ENV_TAG === undefined ? {} : { NOWWORK_ENV_TAG: env.NOWWORK_ENV_TAG }),
|
|
96
96
|
...(env.NOWWORK_STORAGE_POLICY_PATH === undefined
|
|
97
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 }),
|
|
98
101
|
};
|
|
99
102
|
}
|
|
100
103
|
export async function runComputerCommand(argv, overrides = {}) {
|
package/dist/computer-service.js
CHANGED
|
@@ -80,6 +80,8 @@ export function buildServiceSpec(input) {
|
|
|
80
80
|
["NOWWORK_HOME", environment.NOWWORK_HOME],
|
|
81
81
|
["NOWWORK_ENV_TAG", environment.NOWWORK_ENV_TAG],
|
|
82
82
|
["NOWWORK_STORAGE_POLICY_PATH", environment.NOWWORK_STORAGE_POLICY_PATH],
|
|
83
|
+
["NOWWORK_LOCAL_HOME", environment.NOWWORK_LOCAL_HOME],
|
|
84
|
+
["NOWWORK_LOCAL_HOME_MODE", environment.NOWWORK_LOCAL_HOME_MODE],
|
|
83
85
|
].flatMap(([key, value]) => value === undefined
|
|
84
86
|
? []
|
|
85
87
|
: [` <key>${key}</key><string>${xml(assertSingleLine(value, key))}</string>`]);
|
|
@@ -200,6 +200,7 @@ export const ExecutionStartSchema = z.object({
|
|
|
200
200
|
language: z.enum(["zh", "en"]),
|
|
201
201
|
systemPrompt: z.string(),
|
|
202
202
|
wakePrompt: z.string(),
|
|
203
|
+
maintenancePurpose: z.literal("self_training").optional(),
|
|
203
204
|
}).strict(),
|
|
204
205
|
runtime: z.object({
|
|
205
206
|
name: RuntimeSchema,
|
package/dist/execution-runner.js
CHANGED
|
@@ -567,8 +567,9 @@ export async function runExecution(config, input, dependencies) {
|
|
|
567
567
|
projectContext = await cancellable(resolveProjectContext(projectSnapshot, projectWorkspaceRegistry), dependencies.cancellation);
|
|
568
568
|
sessionContextFingerprint = projectSessionContextFingerprint(spec.runtime.name, projectSnapshot);
|
|
569
569
|
}
|
|
570
|
+
const selfTraining = spec.instructions.maintenancePurpose === "self_training";
|
|
570
571
|
let recalledMemory = "";
|
|
571
|
-
if (spec.agent.memoryEnabled === true && dependencies.agentMemory !== undefined) {
|
|
572
|
+
if (!selfTraining && spec.agent.memoryEnabled === true && dependencies.agentMemory !== undefined) {
|
|
572
573
|
try {
|
|
573
574
|
recalledMemory = await cancellable(dependencies.agentMemory.recall(spec.agent.handle, spec.instructions.wakePrompt, spec.executionId, spec.agent.memoryAgentId ?? null), dependencies.cancellation);
|
|
574
575
|
}
|
|
@@ -827,7 +828,10 @@ export async function runExecution(config, input, dependencies) {
|
|
|
827
828
|
const baseSystemPromptBudget = spec.agent.abilityRelease === undefined
|
|
828
829
|
? systemPromptBudget
|
|
829
830
|
: Math.max(0, Math.min(systemPromptBudget, Math.max(32 * 1024, systemPromptBudget - 168 * 1024)));
|
|
830
|
-
const systemPromptWithLocalFacts = withLocalExecutionFacts(spec.instructions.systemPrompt, baseSystemPromptBudget, {
|
|
831
|
+
const systemPromptWithLocalFacts = withLocalExecutionFacts(spec.instructions.systemPrompt, baseSystemPromptBudget, {
|
|
832
|
+
...(selfTraining ? { isolateLocalMemory: true } : {}),
|
|
833
|
+
collaborationTools: config.codexCollaborationTools && spec.runtime.name === "codex",
|
|
834
|
+
});
|
|
831
835
|
const boundedSystemPrompt = (context) => appendAgentMemoryContext(typeof systemPromptWithLocalFacts === "string"
|
|
832
836
|
? systemPromptWithLocalFacts
|
|
833
837
|
: systemPromptWithLocalFacts(context), recalledMemory, baseSystemPromptBudget);
|
|
@@ -876,12 +880,13 @@ export async function runExecution(config, input, dependencies) {
|
|
|
876
880
|
} : {}),
|
|
877
881
|
},
|
|
878
882
|
session: {
|
|
879
|
-
enabled: config.resume,
|
|
883
|
+
enabled: selfTraining ? false : config.resume,
|
|
880
884
|
warmMs: config.resumeWarmMs,
|
|
881
885
|
budgetTokens: config.sessionBudgetTokens,
|
|
882
886
|
softTokens: config.sessionSoftTokens,
|
|
883
887
|
maxTurns: config.sessionMaxTurns,
|
|
884
888
|
},
|
|
889
|
+
...(selfTraining ? { maintenancePurpose: "self_training" } : {}),
|
|
885
890
|
localMemoryV2Enabled: config.localMemoryV2Handles?.has(spec.agent.handle) === true,
|
|
886
891
|
};
|
|
887
892
|
const result = await Promise.race([
|
|
@@ -1048,6 +1053,7 @@ export async function runExecution(config, input, dependencies) {
|
|
|
1048
1053
|
}
|
|
1049
1054
|
await dependencies.journal.complete(spec.executionId, completion);
|
|
1050
1055
|
if (completion.outcome === "succeeded"
|
|
1056
|
+
&& spec.instructions.maintenancePurpose !== "self_training"
|
|
1051
1057
|
&& spec.agent.memoryEnabled === true
|
|
1052
1058
|
&& memoryCaptureFinalText !== null
|
|
1053
1059
|
&& dependencies.agentMemory !== undefined) {
|
|
@@ -47,12 +47,13 @@ function takeCompletePromptSections(sections, maxBytes) {
|
|
|
47
47
|
/** v1 server instructions opt in to daemon-owned paths and bounded local workspace context. */
|
|
48
48
|
export function withLocalExecutionFacts(serverPrompt, maxBytes, options = {}) {
|
|
49
49
|
return ({ workspace, resuming, projectContext, trainingProjectContext }) => {
|
|
50
|
-
const memory = capMemoryForInject(workspace.memory);
|
|
50
|
+
const memory = options.isolateLocalMemory ? "" : capMemoryForInject(workspace.memory);
|
|
51
51
|
const workLog = resuming ? "" : capWorkLogForInject(workspace.workLog);
|
|
52
52
|
const collaborationPrompt = options.collaborationTools === true ? buildCollaborationToolsPrompt() : "";
|
|
53
53
|
const mandatoryWorkspacePrompt = `${buildRuntimeWorkspacePrompt(projectContext)}${buildTrainingGitProjectPrompt(trainingProjectContext)}`;
|
|
54
|
+
const crewHome = options.isolateLocalMemory ? workspace.runDir : workspace.dir;
|
|
54
55
|
const localFactSections = [`\n\n## Local execution facts
|
|
55
|
-
- $CREW_HOME: ${
|
|
56
|
+
- $CREW_HOME: ${crewHome}
|
|
56
57
|
- $CREW_TASK_LOG: ${workspace.workLogPath}`,
|
|
57
58
|
...(memory ? [`\n\n## Injected MEMORY.md (bounded local context)\n${memory}`] : []),
|
|
58
59
|
...(workLog ? [`\n\n## Injected work-log (bounded local context)\n${workLog}`] : []),
|
package/dist/local-executor.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createInterface } from "node:readline";
|
|
2
2
|
import { readFile, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import { delimiter, join } from "node:path";
|
|
4
|
+
import { cleanupSelfTrainingRunDir } from "./self-training-workspace.js";
|
|
4
5
|
import { prepareWorkspace, rotateAgentSession, safeKey, } from "./workspace.js";
|
|
5
6
|
import { CLAUDE_ADDITIONAL_DIRECTORY_INSTRUCTIONS_ENV, isClaudeAdditionalDirectoryInstructionsSupported, probeClaudeVersion, spawnClaude, } from "./runtimes/claude.js";
|
|
6
7
|
import { spawnCodex } from "./runtimes/codex.js";
|
|
@@ -8,7 +9,7 @@ import { DEEPSEEK_CODEX_MODEL, DEEPSEEK_CODEX_REASONING_LEVELS, materializeDeepS
|
|
|
8
9
|
import { materializeDefaultCodexHome } from "./runtimes/codex-home.js";
|
|
9
10
|
import { materializePrivateCodexHome } from "./codex-home-launch.js";
|
|
10
11
|
import { spawnKimi, KIMI_EFFORT_LEVELS } from "./runtimes/kimi.js";
|
|
11
|
-
import { applyDeepSeekHarnessMachineEnv, applyProviderEnv, providerFingerprint, } from "./provider-env.js";
|
|
12
|
+
import { applyDeepSeekHarnessMachineEnv, applyProviderEnv, providerFingerprint, sanitizeEnvVars, } from "./provider-env.js";
|
|
12
13
|
import { augmentedPath } from "./runtime-path.js";
|
|
13
14
|
import { extractFinalText, extractRunMeta, normalizeEvent, parseLine, } from "./normalize.js";
|
|
14
15
|
import { readSession, writeSession, pickResumeId, isNearBudget } from "./session.js";
|
|
@@ -32,8 +33,6 @@ import { diffMemoryPruneNotes, evaluateMemoryPrunePostcondition, inspectMemoryPr
|
|
|
32
33
|
import { createLocalMemoryTelemetry, logLocalMemoryContextPrepareFailure, logLocalMemoryDiagnosticsFailure, } from "./local-memory-telemetry.js";
|
|
33
34
|
import { CodexStartupStageParser } from "./codex-startup-stage.js";
|
|
34
35
|
import { formatRuntimeStartupError } from "./runtime-startup-error.js";
|
|
35
|
-
import { machinePackageManagerEnv, packageManagerEnvFacts } from "./package-manager-env.js";
|
|
36
|
-
import { sanitizeEnvVars } from "./runtime-env.js";
|
|
37
36
|
import { commitLocalMemoryResultAfterExit, initializeLocalMemoryV2ForWorkspace, isLocalMemoryV2Eligible, localMemoryResultPath as makeLocalMemoryResultPath, renderLocalMemoryResultInstructions, } from "./local-memory/runtime.js";
|
|
38
37
|
export class RuntimeRunningCallbackTimeoutError extends Error {
|
|
39
38
|
constructor(runtime, timeoutMs) {
|
|
@@ -51,6 +50,15 @@ const MEMORY_PRUNE_DIAGNOSTICS_TIMEOUT_MS = 2_000;
|
|
|
51
50
|
const LOCAL_MEMORY_DIAGNOSTICS_TIMEOUT_MS = 250;
|
|
52
51
|
const CLAUDE_INSTRUCTION_WARNING = "claude_additional_directory_instructions_unverified";
|
|
53
52
|
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
|
+
]);
|
|
54
62
|
function withoutClaudeInstructionFlag(env) {
|
|
55
63
|
if (!(CLAUDE_ADDITIONAL_DIRECTORY_INSTRUCTIONS_ENV in env))
|
|
56
64
|
return env;
|
|
@@ -111,6 +119,7 @@ async function launchLegacyRuntime(request) {
|
|
|
111
119
|
effectivePermission: request.effectivePermission,
|
|
112
120
|
...(request.model === undefined ? {} : { model: request.model }),
|
|
113
121
|
...(request.reasoning === undefined ? {} : { reasoning: request.reasoning }),
|
|
122
|
+
...(request.networkAccess === undefined ? {} : { networkAccess: request.networkAccess }),
|
|
114
123
|
};
|
|
115
124
|
if (request.runtime === "claude") {
|
|
116
125
|
const additionalDirectories = request.claudeAdditionalDirectories
|
|
@@ -200,6 +209,7 @@ export async function executeLocal(input, callbacks = {}, dependencies = {}) {
|
|
|
200
209
|
async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
201
210
|
const providerConfig = input.launch.providerConfig ?? {};
|
|
202
211
|
const { runtime } = input;
|
|
212
|
+
const isolatedMaintenance = input.maintenancePurpose === "self_training";
|
|
203
213
|
const isDeepSeekCodex = runtime.name === "codex" && providerConfig.provider === "deepseek";
|
|
204
214
|
const currentModel = isDeepSeekCodex ? DEEPSEEK_CODEX_MODEL : runtime.model ?? null;
|
|
205
215
|
const launchModel = isDeepSeekCodex ? DEEPSEEK_CODEX_MODEL : runtime.model;
|
|
@@ -224,6 +234,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
224
234
|
? {}
|
|
225
235
|
: { sessionContextFingerprint: input.sessionContextFingerprint }),
|
|
226
236
|
...(input.launch.description ? { description: input.launch.description } : {}),
|
|
237
|
+
...(isolatedMaintenance ? { skipLocalMemory: true } : {}),
|
|
227
238
|
}), dependencies.cancellation);
|
|
228
239
|
}
|
|
229
240
|
catch (error) {
|
|
@@ -254,12 +265,23 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
254
265
|
}
|
|
255
266
|
const sourceCodexHome = inheritedEnv.CODEX_HOME
|
|
256
267
|
?? (inheritedEnv.HOME === undefined ? undefined : join(inheritedEnv.HOME, ".codex"));
|
|
268
|
+
let materializedDeepSeekHome;
|
|
257
269
|
try {
|
|
258
270
|
if (isDeepSeekCodex && !providerConfig.providerApiKey) {
|
|
259
271
|
throw new Error("DeepSeek API key is not configured for this Agent");
|
|
260
272
|
}
|
|
261
273
|
if (isDeepSeekCodex) {
|
|
262
|
-
|
|
274
|
+
const providerBaseUrl = providerConfig.providerBaseUrl
|
|
275
|
+
? new URL(providerConfig.providerBaseUrl, input.launch.serverUrl).toString()
|
|
276
|
+
: undefined;
|
|
277
|
+
const materialize = dependencies.materializeDeepSeekCodexHome ?? materializeDeepSeekCodexHome;
|
|
278
|
+
const materializeOptions = {
|
|
279
|
+
...(providerBaseUrl === undefined ? {} : { baseUrl: providerBaseUrl }),
|
|
280
|
+
...(isolatedMaintenance ? { selfTraining: true } : {}),
|
|
281
|
+
};
|
|
282
|
+
materializedDeepSeekHome = await awaitWithCancellation(providerBaseUrl === undefined && !isolatedMaintenance
|
|
283
|
+
? materialize(workspace.homeDir)
|
|
284
|
+
: materialize(workspace.homeDir, materializeOptions), dependencies.cancellation);
|
|
263
285
|
}
|
|
264
286
|
const supportsNativeResume = runtime.name === "claude"
|
|
265
287
|
|| (dependencies.launchRuntime !== undefined && runtimeCapability(runtime.name).nativeResume);
|
|
@@ -387,29 +409,30 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
387
409
|
const systemPrompt = localMemoryResultPath === null
|
|
388
410
|
? baseSystemPrompt
|
|
389
411
|
: `${baseSystemPrompt}${structuredMemoryInstructions ?? ""}`;
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
412
|
+
if (!isolatedMaintenance)
|
|
413
|
+
try {
|
|
414
|
+
localMemoryTelemetry = createLocalMemoryTelemetry({
|
|
415
|
+
executionId: input.executionId,
|
|
416
|
+
agentHandle: input.handle,
|
|
417
|
+
...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }),
|
|
418
|
+
runtime: runtime.name,
|
|
419
|
+
stableProtocolRuntime: dependencies.launchRuntime !== undefined,
|
|
420
|
+
agentDir: executionWorkspace.dir,
|
|
421
|
+
runDir: executionWorkspace.runDir,
|
|
422
|
+
isMemoryPrune: memoryPruneTraceId !== null,
|
|
423
|
+
diagnosticsTimeoutMs: dependencies.localMemoryDiagnosticsTimeoutMs
|
|
424
|
+
?? LOCAL_MEMORY_DIAGNOSTICS_TIMEOUT_MS,
|
|
425
|
+
});
|
|
426
|
+
await localMemoryTelemetry.captureBefore();
|
|
427
|
+
}
|
|
428
|
+
catch (error) {
|
|
429
|
+
localMemoryTelemetry = null;
|
|
430
|
+
logLocalMemoryDiagnosticsFailure({
|
|
431
|
+
executionId: input.executionId,
|
|
432
|
+
agentHandle: input.handle,
|
|
433
|
+
...(input.taskKey === undefined ? {} : { taskKey: input.taskKey }),
|
|
434
|
+
}, "initialize", error);
|
|
435
|
+
}
|
|
413
436
|
if (memoryPruneTraceId !== null) {
|
|
414
437
|
memoryPruneSharedWriteKey = JSON.stringify([input.launch.agentsRoot, input.handle]);
|
|
415
438
|
const activePruneCount = (activeMemoryPrunes.get(memoryPruneSharedWriteKey) ?? 0) + 1;
|
|
@@ -443,18 +466,16 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
443
466
|
}
|
|
444
467
|
}
|
|
445
468
|
memoryPruneFailurePhase = "runtime_prepare";
|
|
469
|
+
const crewHome = isolatedMaintenance ? executionWorkspace.runDir : workspace.dir;
|
|
446
470
|
const baseEnv = {
|
|
447
471
|
...inheritedEnv,
|
|
448
472
|
...sanitizeEnvVars(providerConfig.envVars),
|
|
449
|
-
...(input.trainingProjectContext === undefined
|
|
450
|
-
? {}
|
|
451
|
-
: machinePackageManagerEnv(inheritedEnv)),
|
|
452
473
|
...input.launch.systemEnv,
|
|
453
474
|
PATH: `${workspace.crewDir}${delimiter}${augmentedPath()}`,
|
|
454
475
|
CREW_SERVER_URL: input.launch.serverUrl,
|
|
455
476
|
CREW_TOKEN: input.launch.token,
|
|
456
477
|
CREW_CHANNEL: input.channelId,
|
|
457
|
-
CREW_HOME:
|
|
478
|
+
CREW_HOME: crewHome,
|
|
458
479
|
// Keep the daemon-selected storage root authoritative for runtimes.
|
|
459
480
|
// In profile-backed NowWork sessions the daemon config may differ from
|
|
460
481
|
// the daemon process environment, so inheriting CREW_AGENTS_ROOT is not
|
|
@@ -464,7 +485,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
464
485
|
...(input.launch.qaReportRoot === undefined ? {} : { QA_REPORT_HUB: input.launch.qaReportRoot }),
|
|
465
486
|
// Expose only the training root; ability-specific workspace layout stays
|
|
466
487
|
// owned by the training package rather than the NowWork daemon.
|
|
467
|
-
QA_RUNTIME_ROOT: join(
|
|
488
|
+
QA_RUNTIME_ROOT: join(crewHome, "training"),
|
|
468
489
|
CREW_TASK_DIR: executionWorkspace.runDir,
|
|
469
490
|
CREW_TASK_LOG: executionWorkspace.workLogPath,
|
|
470
491
|
...(input.wakeMessageId ? { CREW_WAKE_MESSAGE_ID: input.wakeMessageId } : {}),
|
|
@@ -480,7 +501,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
480
501
|
};
|
|
481
502
|
const providerEnv = applyProviderEnv(baseEnv, runtime.name, input.launch.codexHomeMode === undefined
|
|
482
503
|
? providerConfig
|
|
483
|
-
: { ...providerConfig, codexHomeMode: input.launch.codexHomeMode }, workspace.homeDir);
|
|
504
|
+
: { ...providerConfig, codexHomeMode: input.launch.codexHomeMode }, workspace.homeDir, materializedDeepSeekHome);
|
|
484
505
|
const childEnv = runtime.name === "deepseek-harness"
|
|
485
506
|
? applyDeepSeekHarnessMachineEnv(providerEnv, inheritedEnv)
|
|
486
507
|
: providerEnv;
|
|
@@ -491,8 +512,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
491
512
|
qa_runtime_root: childEnv.QA_RUNTIME_ROOT ?? null,
|
|
492
513
|
qa_runtime_root_present: childEnv.QA_RUNTIME_ROOT !== undefined,
|
|
493
514
|
inherited_qa_runtime_root_present: inheritedEnv.QA_RUNTIME_ROOT !== undefined,
|
|
494
|
-
|
|
495
|
-
training_root: join(workspace.dir, "training"),
|
|
515
|
+
training_root: join(crewHome, "training"),
|
|
496
516
|
project_context_present: input.projectContext !== undefined,
|
|
497
517
|
project_ids: JSON.stringify(input.projectContext === undefined
|
|
498
518
|
? []
|
|
@@ -624,6 +644,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
624
644
|
...(input.projectContext === undefined ? {} : {
|
|
625
645
|
projectContext: input.projectContext,
|
|
626
646
|
}),
|
|
647
|
+
...(isolatedMaintenance && runtime.name === "codex" ? { networkAccess: true } : {}),
|
|
627
648
|
...(input.projectContext === undefined && workspaceRoots.length === 0
|
|
628
649
|
? {}
|
|
629
650
|
: { workspaceRoots: Object.freeze(workspaceRoots) }),
|
|
@@ -1107,5 +1128,8 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
1107
1128
|
const detail = error instanceof Error ? error.message : String(error);
|
|
1108
1129
|
process.stderr.write(`[execution] failed to remove prompt ${workspace.systemPromptPath}: ${detail}\n`);
|
|
1109
1130
|
}
|
|
1131
|
+
if (isolatedMaintenance) {
|
|
1132
|
+
await cleanupSelfTrainingRunDir(join(workspace.dir, "tasks"), executionWorkspace.runDir);
|
|
1133
|
+
}
|
|
1110
1134
|
}
|
|
1111
1135
|
}
|
package/dist/machine-info.js
CHANGED
|
@@ -25,7 +25,9 @@ export { executableRuntimes } from "./runtime-capabilities.js";
|
|
|
25
25
|
const execFileP = promisify(execFile);
|
|
26
26
|
export const PROJECT_WORKSPACES_CAPABILITY = "project_workspaces_v1";
|
|
27
27
|
export const TRAINING_PROJECT_DEPENDENCIES_CAPABILITY = "training_project_dependencies_v1";
|
|
28
|
+
export const SELF_TRAINING_EXECUTION_CAPABILITY = "self_training_execution_v1";
|
|
28
29
|
export const EXECUTION_LARGE_ATTACHMENTS_CAPABILITY = "execution_attachments_300m_v1";
|
|
30
|
+
export const PROJECT_SKILL_PROJECTION_CAPABILITY = "project_skill_projection_v2";
|
|
29
31
|
export const NATIVE_PROJECT_WORKSPACE_READINESS = Object.freeze({
|
|
30
32
|
resolverReady: true,
|
|
31
33
|
nativeRuntimeAdaptersReady: true,
|
|
@@ -42,6 +44,7 @@ export const DAEMON_CAPABILITIES = Object.freeze([
|
|
|
42
44
|
"execution_machine_queue_v1",
|
|
43
45
|
"execution_agent_memory_policy_v1",
|
|
44
46
|
"execution_agent_memory_mapping_v1",
|
|
47
|
+
SELF_TRAINING_EXECUTION_CAPABILITY,
|
|
45
48
|
"project_skills_v1",
|
|
46
49
|
"project_skill_projection_v2",
|
|
47
50
|
PROJECT_WORKSPACES_CAPABILITY,
|
package/dist/main.js
CHANGED
|
@@ -103,6 +103,9 @@ async function main() {
|
|
|
103
103
|
...(process.env.NOWWORK_ENV_TAG === undefined ? {} : { NOWWORK_ENV_TAG: process.env.NOWWORK_ENV_TAG }),
|
|
104
104
|
...(process.env.NOWWORK_STORAGE_POLICY_PATH === undefined
|
|
105
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 }),
|
|
106
109
|
},
|
|
107
110
|
});
|
|
108
111
|
}
|
package/dist/provider-env.js
CHANGED
|
@@ -11,6 +11,24 @@
|
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
import { DEEPSEEK_CODEX_KEY_ENV, deepSeekCodexHome, } from "./runtimes/codex-deepseek-config.js";
|
|
13
13
|
import { DEEPSEEK_HARNESS_CONFIG_ENV } from "./runtimes/deepseek-harness.js";
|
|
14
|
+
const ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
15
|
+
const RESERVED_ENV = new Set([
|
|
16
|
+
"PATH",
|
|
17
|
+
"XDG_CONFIG_HOME",
|
|
18
|
+
"XDG_DATA_HOME",
|
|
19
|
+
"XDG_CACHE_HOME",
|
|
20
|
+
"GH_CONFIG_DIR",
|
|
21
|
+
"CLOUDSDK_CONFIG",
|
|
22
|
+
]);
|
|
23
|
+
/** Keep user-provided variables away from daemon-owned runtime and credential fields. */
|
|
24
|
+
export function sanitizeEnvVars(raw) {
|
|
25
|
+
if (!raw)
|
|
26
|
+
return {};
|
|
27
|
+
return Object.fromEntries(Object.entries(raw).filter(([key, value]) => typeof value === "string"
|
|
28
|
+
&& ENV_KEY_RE.test(key)
|
|
29
|
+
&& !key.toUpperCase().startsWith("CREW_")
|
|
30
|
+
&& !RESERVED_ENV.has(key.toUpperCase())));
|
|
31
|
+
}
|
|
14
32
|
/** custom 模式下必须从继承 env 中剔除的全局路由/凭证变量(避免与注入值叠加或抢优先级)。 */
|
|
15
33
|
const CONFLICTING_ENV = [
|
|
16
34
|
"ANTHROPIC_BASE_URL",
|
|
@@ -39,12 +57,12 @@ const CONFLICTING_CODEX_ENV = [
|
|
|
39
57
|
DEEPSEEK_CODEX_KEY_ENV,
|
|
40
58
|
];
|
|
41
59
|
/** 返回叠加了 provider 隔离的新 env(不改入参);不支持的 runtime/provider 组合原样返回。 */
|
|
42
|
-
export function applyProviderEnv(base, runtime, cfg, homeDir) {
|
|
60
|
+
export function applyProviderEnv(base, runtime, cfg, homeDir, deepSeekHomeOverride) {
|
|
43
61
|
if (runtime === "codex" && cfg.provider === "deepseek") {
|
|
44
62
|
const env = { ...base };
|
|
45
63
|
for (const key of CONFLICTING_CODEX_ENV)
|
|
46
64
|
delete env[key];
|
|
47
|
-
env.CODEX_HOME = deepSeekCodexHome(homeDir);
|
|
65
|
+
env.CODEX_HOME = deepSeekHomeOverride ?? deepSeekCodexHome(homeDir);
|
|
48
66
|
if (cfg.providerApiKey)
|
|
49
67
|
env[DEEPSEEK_CODEX_KEY_ENV] = cfg.providerApiKey;
|
|
50
68
|
return env;
|
|
@@ -100,7 +118,7 @@ export function applyDeepSeekHarnessMachineEnv(base, machineEnv) {
|
|
|
100
118
|
*/
|
|
101
119
|
export function providerFingerprint(runtime, cfg) {
|
|
102
120
|
if (runtime === "codex" && cfg.provider === "deepseek") {
|
|
103
|
-
return `deepseek|${cfg.providerApiKey ? "key" : "nokey"}`;
|
|
121
|
+
return `deepseek|${cfg.providerBaseUrl ?? "default"}|${cfg.providerApiKey ? "key" : "nokey"}`;
|
|
104
122
|
}
|
|
105
123
|
if (runtime !== "claude" || cfg.provider !== "custom")
|
|
106
124
|
return null;
|
package/dist/runner.js
CHANGED
|
@@ -12,7 +12,7 @@ import { readBoundImDecisionFile, resetBoundImDecisionFile } from "./bound-im-de
|
|
|
12
12
|
import { launchSupervisedRuntime } from "./supervised-runtime.js";
|
|
13
13
|
import { awaitWithCancellation, } from "./runtime-cancellation.js";
|
|
14
14
|
export { awaitExit, exitActivity } from "./local-executor.js";
|
|
15
|
-
export { sanitizeEnvVars } from "./
|
|
15
|
+
export { sanitizeEnvVars } from "./provider-env.js";
|
|
16
16
|
const ICON = {
|
|
17
17
|
init: "🟢", text: "💬", reading: "📖", sending: "📨", checking: "🔎",
|
|
18
18
|
claiming: "📌", crew: "⚙️", tool: "🛠️", tool_result: "↩️", done: "✅", error: "❌",
|
|
@@ -21,9 +21,14 @@ const RunnerInputSchema = z.object({
|
|
|
21
21
|
cwd: z.string().min(1).optional(),
|
|
22
22
|
workspaceRoots: z.array(z.string().min(1)).optional(),
|
|
23
23
|
skillRoots: z.array(z.string().min(1)).optional(),
|
|
24
|
+
networkAccess: z.boolean().optional(),
|
|
24
25
|
codexCollaborationTools: z.boolean().optional(),
|
|
25
26
|
resume: z.boolean(),
|
|
26
|
-
}).strict()
|
|
27
|
+
}).strict().superRefine((input, ctx) => {
|
|
28
|
+
if (input.networkAccess === true && input.effectivePermission !== "workspace_write") {
|
|
29
|
+
ctx.addIssue({ code: "custom", message: "network_access_requires_workspace_write" });
|
|
30
|
+
}
|
|
31
|
+
});
|
|
27
32
|
const RPC_TIMEOUT_MS = 30_000;
|
|
28
33
|
const INITIALIZE_RPC_TIMEOUT_MS = 60_000;
|
|
29
34
|
const ERROR_MESSAGE_CAP = 2_000;
|
|
@@ -565,18 +570,18 @@ async function runCodexAppServerAttempt(bin, input, attempt, initializeTimeoutMs
|
|
|
565
570
|
...(input.model === undefined ? {} : { model: input.model }),
|
|
566
571
|
};
|
|
567
572
|
let thread;
|
|
568
|
-
const
|
|
573
|
+
const workspaceContractRequired = input.workspaceRoots !== undefined;
|
|
569
574
|
try {
|
|
570
575
|
thread = input.resume && input.sessionId !== undefined
|
|
571
576
|
? await rpc.request("thread/resume", { threadId: input.sessionId, ...threadParams })
|
|
572
577
|
: await rpc.request("thread/start", threadParams);
|
|
573
578
|
}
|
|
574
579
|
catch (error) {
|
|
575
|
-
if (
|
|
580
|
+
if (workspaceContractRequired)
|
|
576
581
|
throw codexProjectWorkspaceUnsupported();
|
|
577
582
|
throw error;
|
|
578
583
|
}
|
|
579
|
-
if (
|
|
584
|
+
if (workspaceContractRequired) {
|
|
580
585
|
thread = assertCodexProjectWorkspaceResponse(thread, {
|
|
581
586
|
cwd: threadCwd,
|
|
582
587
|
workspaceRoots: input.workspaceRoots ?? [],
|
|
@@ -613,6 +618,9 @@ async function runCodexAppServerAttempt(bin, input, attempt, initializeTimeoutMs
|
|
|
613
618
|
...(input.reasoning === undefined || input.reasoning === "default"
|
|
614
619
|
? {}
|
|
615
620
|
: { effort: input.reasoning }),
|
|
621
|
+
...(input.networkAccess === true ? {
|
|
622
|
+
sandboxPolicy: { type: "workspaceWrite", networkAccess: true },
|
|
623
|
+
} : {}),
|
|
616
624
|
});
|
|
617
625
|
logStage(activeStage, "ok", attempt, stageStartedAt);
|
|
618
626
|
turnId = started.turn.id;
|
|
@@ -7,10 +7,20 @@ export const DEEPSEEK_CODEX_BASE_URL = "https://api.deepseek.com/";
|
|
|
7
7
|
export const DEEPSEEK_CODEX_KEY_ENV = "NOWCREW_DEEPSEEK_API_KEY";
|
|
8
8
|
export const DEEPSEEK_CODEX_REASONING_LEVELS = ["low", "high", "max"];
|
|
9
9
|
const PRIVATE_CODEX_HOME = ".codex-deepseek";
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const SELF_TRAINING_PRIVATE_CODEX_HOME = ".codex-deepseek-self-training";
|
|
11
|
+
export function deepSeekCodexHome(homeDir, selfTraining = false) {
|
|
12
|
+
return resolve(homeDir, selfTraining ? SELF_TRAINING_PRIVATE_CODEX_HOME : PRIVATE_CODEX_HOME);
|
|
12
13
|
}
|
|
13
|
-
|
|
14
|
+
function normalizedBaseUrl(value) {
|
|
15
|
+
const url = new URL(value);
|
|
16
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
17
|
+
throw new Error("DeepSeek provider base URL must use HTTP(S)");
|
|
18
|
+
}
|
|
19
|
+
if (!url.pathname.endsWith("/"))
|
|
20
|
+
url.pathname = `${url.pathname}/`;
|
|
21
|
+
return url.toString();
|
|
22
|
+
}
|
|
23
|
+
export function renderDeepSeekConfig(codexHome, baseUrl = DEEPSEEK_CODEX_BASE_URL) {
|
|
14
24
|
const catalogPath = join(codexHome, "models.json");
|
|
15
25
|
return [
|
|
16
26
|
`model = ${JSON.stringify(DEEPSEEK_CODEX_MODEL)}`,
|
|
@@ -20,7 +30,7 @@ export function renderDeepSeekConfig(codexHome) {
|
|
|
20
30
|
"",
|
|
21
31
|
"[model_providers.deepseek]",
|
|
22
32
|
'name = "deepseek"',
|
|
23
|
-
`base_url = ${JSON.stringify(
|
|
33
|
+
`base_url = ${JSON.stringify(normalizedBaseUrl(baseUrl))}`,
|
|
24
34
|
'wire_api = "responses"',
|
|
25
35
|
`env_key = ${JSON.stringify(DEEPSEEK_CODEX_KEY_ENV)}`,
|
|
26
36
|
"",
|
|
@@ -40,11 +50,11 @@ async function writePrivateAtomic(path, content) {
|
|
|
40
50
|
await rm(temporary, { force: true });
|
|
41
51
|
}
|
|
42
52
|
}
|
|
43
|
-
export async function materializeDeepSeekCodexHome(homeDir) {
|
|
44
|
-
const codexHome = deepSeekCodexHome(homeDir);
|
|
53
|
+
export async function materializeDeepSeekCodexHome(homeDir, options = {}) {
|
|
54
|
+
const codexHome = deepSeekCodexHome(homeDir, options.selfTraining === true);
|
|
45
55
|
await mkdir(codexHome, { recursive: true, mode: 0o700 });
|
|
46
56
|
await chmod(codexHome, 0o700);
|
|
47
57
|
await writePrivateAtomic(join(codexHome, "models.json"), DEEPSEEK_CODEX_CATALOG);
|
|
48
|
-
await writePrivateAtomic(join(codexHome, "config.toml"), renderDeepSeekConfig(codexHome));
|
|
58
|
+
await writePrivateAtomic(join(codexHome, "config.toml"), renderDeepSeekConfig(codexHome, options.baseUrl));
|
|
49
59
|
return codexHome;
|
|
50
60
|
}
|
package/dist/runtimes/codex.js
CHANGED
|
@@ -36,6 +36,9 @@ export function buildCodexArgs(input) {
|
|
|
36
36
|
else if (input.effectivePermission === "full_access" || (input.effectivePermission === undefined && input.dangerous)) {
|
|
37
37
|
args.push("--dangerously-bypass-approvals-and-sandbox");
|
|
38
38
|
}
|
|
39
|
+
if (input.effectivePermission === "workspace_write" && input.networkAccess === true) {
|
|
40
|
+
args.push("-c", "sandbox_workspace_write.network_access=true");
|
|
41
|
+
}
|
|
39
42
|
for (const imagePath of input.imagePaths ?? [])
|
|
40
43
|
args.push("--image", imagePath);
|
|
41
44
|
// `-` instructs codex exec to read the prompt from stdin. Keeping the complete prompt out of argv
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { rm } from "node:fs/promises";
|
|
2
|
+
import { isAbsolute, relative, sep } from "node:path";
|
|
3
|
+
/** Delete only a concrete child of this Agent's tasks root; never follow an ambiguous target. */
|
|
4
|
+
export async function cleanupSelfTrainingRunDir(tasksRoot, runDir) {
|
|
5
|
+
const child = relative(tasksRoot, runDir);
|
|
6
|
+
if (child === "" || isAbsolute(child) || child === ".." || child.startsWith(`..${sep}`))
|
|
7
|
+
return;
|
|
8
|
+
try {
|
|
9
|
+
await rm(runDir, { recursive: true, force: true });
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
13
|
+
process.stderr.write(`[execution] failed to remove self-training run dir ${runDir}: ${detail}\n`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -46,6 +46,7 @@ export function supervisorLaunch(request) {
|
|
|
46
46
|
effectivePermission: request.effectivePermission,
|
|
47
47
|
...(request.model === undefined ? {} : { model: request.model }),
|
|
48
48
|
...(request.reasoning === undefined ? {} : { reasoning: request.reasoning }),
|
|
49
|
+
...(request.networkAccess === undefined ? {} : { networkAccess: request.networkAccess }),
|
|
49
50
|
...(request.sessionId === undefined ? {} : { sessionId: request.sessionId }),
|
|
50
51
|
...(request.imagePaths === undefined ? {} : { imagePaths: request.imagePaths }),
|
|
51
52
|
...(request.agentRoot === undefined ? {} : {
|
package/dist/workspace.js
CHANGED
|
@@ -59,12 +59,13 @@ export async function prepareWorkspace(input) {
|
|
|
59
59
|
const crewDir = join(dir, ".crew");
|
|
60
60
|
await mkdir(crewDir, { recursive: true });
|
|
61
61
|
// 预先创建明细目录,让收尾流程可以按功能创建 notes/<topic>.md。
|
|
62
|
-
|
|
62
|
+
if (!input.skipLocalMemory)
|
|
63
|
+
await mkdir(join(dir, "notes"), { recursive: true });
|
|
63
64
|
// MEMORY.md:首次创建"持久身份 + 知识索引"骨架,之后由 agent 自己维护
|
|
64
65
|
const memoryPath = join(dir, "MEMORY.md");
|
|
65
|
-
const memorySeedCreated = !(await exists(memoryPath));
|
|
66
|
+
const memorySeedCreated = !input.skipLocalMemory && !(await exists(memoryPath));
|
|
66
67
|
const localMemoryV2Initialized = await hasLocalMemoryV2Marker(dir);
|
|
67
|
-
if (memorySeedCreated) {
|
|
68
|
+
if (!input.skipLocalMemory && memorySeedCreated) {
|
|
68
69
|
try {
|
|
69
70
|
await writeFile(memoryPath, memorySeed(input.handle, input.description), "utf8");
|
|
70
71
|
}
|
|
@@ -73,13 +74,15 @@ export async function prepareWorkspace(input) {
|
|
|
73
74
|
throw error;
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
|
-
let memory;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
77
|
+
let memory = "";
|
|
78
|
+
if (!input.skipLocalMemory) {
|
|
79
|
+
try {
|
|
80
|
+
memory = await readFile(memoryPath, "utf8");
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
logMemoryPrepareFailure(input, "memory_read", error);
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
83
86
|
}
|
|
84
87
|
// 系统提示词按 execution 隔离;省略 identity 的兼容调用每次使用随机路径。
|
|
85
88
|
const promptDir = join(crewDir, "prompts");
|
package/package.json
CHANGED
|
@@ -1,75 +0,0 @@
|
|
|
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
|
-
}
|
package/dist/runtime-env.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
const ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
2
|
-
export const RESERVED_ENV = new Set([
|
|
3
|
-
"PATH",
|
|
4
|
-
"XDG_CONFIG_HOME",
|
|
5
|
-
"XDG_DATA_HOME",
|
|
6
|
-
"XDG_CACHE_HOME",
|
|
7
|
-
"GH_CONFIG_DIR",
|
|
8
|
-
"CLOUDSDK_CONFIG",
|
|
9
|
-
"NPM_CONFIG_USERCONFIG",
|
|
10
|
-
"YARN_REGISTRY",
|
|
11
|
-
"NPM_CONFIG_ALWAYS_AUTH",
|
|
12
|
-
]);
|
|
13
|
-
/** 用户自定义环境只能补充普通变量,不能覆盖 daemon 维护的运行时契约。 */
|
|
14
|
-
export function sanitizeEnvVars(raw) {
|
|
15
|
-
if (!raw)
|
|
16
|
-
return {};
|
|
17
|
-
return Object.fromEntries(Object.entries(raw).filter(([key, value]) => typeof value === "string"
|
|
18
|
-
&& ENV_KEY_RE.test(key)
|
|
19
|
-
&& !key.toUpperCase().startsWith("CREW_")
|
|
20
|
-
&& !RESERVED_ENV.has(key.toUpperCase())));
|
|
21
|
-
}
|