@nowcrew/daemon 0.6.91 → 0.6.92
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.
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { delimiter, join } from "node:path";
|
|
1
|
+
import { delimiter, join, resolve } from "node:path";
|
|
2
2
|
import { environmentForExecution } from "./agent-environments.js";
|
|
3
3
|
import { augmentedPath } from "./runtime-path.js";
|
|
4
4
|
import { sanitizeEnvVars } from "./runtime-env.js";
|
|
@@ -10,6 +10,7 @@ export function buildLocalRuntimeEnvironment(context) {
|
|
|
10
10
|
const { input, workspace, executionWorkspace, inheritedEnv, providerConfig, crewHome, contextThreadId, nativeThreadContextPath, reportContext, runtime } = context;
|
|
11
11
|
const agentEnvironment = sanitizeEnvVars(providerConfig.environmentPlan === undefined ? providerConfig.envVars
|
|
12
12
|
: environmentForExecution(providerConfig.environmentPlan, workspace.dir, input.projectContext?.primary?.root ?? executionWorkspace.runDir, input.projectContext?.primary?.projectId));
|
|
13
|
+
const boundImDecisionFile = input.launch.systemEnv?.CREW_BOUND_IM_DECISION_FILE;
|
|
13
14
|
return mergeRuntimeEnvironments([
|
|
14
15
|
inheritedEnv, agentEnvironment,
|
|
15
16
|
input.trainingProjectContext === undefined ? {} : machinePackageManagerEnv(inheritedEnv),
|
|
@@ -35,6 +36,10 @@ export function buildLocalRuntimeEnvironment(context) {
|
|
|
35
36
|
QA_REPORT_DELIVERY_CONTEXT: JSON.stringify(reportContext),
|
|
36
37
|
CREW_TASK_DIR: executionWorkspace.runDir,
|
|
37
38
|
CREW_TASK_LOG: executionWorkspace.workLogPath,
|
|
39
|
+
// CLI 可以从 Agent 主目录或项目目录调用,标记始终写回完成阶段读取的任务目录。
|
|
40
|
+
...(boundImDecisionFile ? {
|
|
41
|
+
CREW_BOUND_IM_DECISION_FILE: resolve(executionWorkspace.runDir, boundImDecisionFile),
|
|
42
|
+
} : {}),
|
|
38
43
|
...(input.wakeMessageId ? { CREW_WAKE_MESSAGE_ID: input.wakeMessageId } : {}),
|
|
39
44
|
XDG_CONFIG_HOME: join(workspace.homeDir, ".config"),
|
|
40
45
|
XDG_DATA_HOME: join(workspace.homeDir, ".local", "share"),
|
|
@@ -31,6 +31,7 @@ export function createMachineEnvironmentProvider(options = {}) {
|
|
|
31
31
|
let lastFailureTime = -Infinity;
|
|
32
32
|
let stopped = false;
|
|
33
33
|
let unsafeCleanup = false;
|
|
34
|
+
let isCleanupComplete;
|
|
34
35
|
let flight;
|
|
35
36
|
let controller;
|
|
36
37
|
let captureEpoch = 0;
|
|
@@ -50,8 +51,19 @@ export function createMachineEnvironmentProvider(options = {}) {
|
|
|
50
51
|
return module.captureWindowsMachineEnvironment(request);
|
|
51
52
|
});
|
|
52
53
|
const refresh = async (force, signal) => {
|
|
53
|
-
if (!selected.source || stopped
|
|
54
|
+
if (!selected.source || stopped)
|
|
54
55
|
return snapshot;
|
|
56
|
+
if (unsafeCleanup) {
|
|
57
|
+
try {
|
|
58
|
+
if (!isCleanupComplete?.())
|
|
59
|
+
return snapshot;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return snapshot;
|
|
63
|
+
}
|
|
64
|
+
unsafeCleanup = false;
|
|
65
|
+
isCleanupComplete = undefined;
|
|
66
|
+
}
|
|
55
67
|
if (!force && now() - lastFailureTime < (options.backoffMs ?? 30_000))
|
|
56
68
|
return snapshot;
|
|
57
69
|
const attemptedAt = new Date(now()).toISOString();
|
|
@@ -64,7 +76,7 @@ export function createMachineEnvironmentProvider(options = {}) {
|
|
|
64
76
|
return snapshot;
|
|
65
77
|
publish(snapshot.env, { state: "loading", attemptedAt, errorCode: null });
|
|
66
78
|
capturing = true;
|
|
67
|
-
const deadline = Date.now() + (options.captureTimeoutMs ?? (selected.source === "windows-registry" ? 8_000 :
|
|
79
|
+
const deadline = Date.now() + (options.captureTimeoutMs ?? (selected.source === "windows-registry" ? 8_000 : 10_000));
|
|
68
80
|
const capture = selected.source === "windows-registry" ? captureWindows : options.captureShell ?? captureShellMachineEnvironment;
|
|
69
81
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
70
82
|
try {
|
|
@@ -111,8 +123,10 @@ export function createMachineEnvironmentProvider(options = {}) {
|
|
|
111
123
|
}
|
|
112
124
|
catch (error) {
|
|
113
125
|
const errorCode = error instanceof MachineEnvironmentCaptureError ? error.code : selected.source === "windows-registry" ? "windows_environment_unavailable" : "shell_capture_failed";
|
|
114
|
-
if (errorCode === "shell_cleanup_failed")
|
|
126
|
+
if (errorCode === "shell_cleanup_failed") {
|
|
115
127
|
unsafeCleanup = true;
|
|
128
|
+
isCleanupComplete = error instanceof MachineEnvironmentCaptureError ? error.isCleanupComplete : undefined;
|
|
129
|
+
}
|
|
116
130
|
lastFailureTime = now();
|
|
117
131
|
return publish(lastGood?.env ?? baseline, { state: lastGood ? "stale" : "fallback", errorCode, attemptedAt });
|
|
118
132
|
}
|
|
@@ -129,7 +143,7 @@ export function createMachineEnvironmentProvider(options = {}) {
|
|
|
129
143
|
}
|
|
130
144
|
return flight;
|
|
131
145
|
}
|
|
132
|
-
if (stopped ||
|
|
146
|
+
if (stopped || selected.source === null)
|
|
133
147
|
return Promise.resolve(snapshot);
|
|
134
148
|
controller = new AbortController();
|
|
135
149
|
flight = refresh(acquireOptions?.force === true, controller.signal).finally(() => { flight = undefined; controller = undefined; });
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export class MachineEnvironmentCaptureError extends Error {
|
|
2
2
|
code;
|
|
3
|
-
|
|
3
|
+
isCleanupComplete;
|
|
4
|
+
constructor(code, isCleanupComplete) {
|
|
4
5
|
super(code);
|
|
5
6
|
this.code = code;
|
|
7
|
+
this.isCleanupComplete = isCleanupComplete;
|
|
6
8
|
this.name = "MachineEnvironmentCaptureError";
|
|
7
9
|
}
|
|
8
10
|
}
|
|
@@ -44,7 +44,11 @@ export async function runMachineEnvironmentProcess(options) {
|
|
|
44
44
|
stdio[collectFd] = "pipe";
|
|
45
45
|
if (options.metadataFd !== undefined)
|
|
46
46
|
stdio[options.metadataFd] = "pipe";
|
|
47
|
-
|
|
47
|
+
// launchd Background 的 I/O 策略会让 shell 初始化停滞;只调整短命采集子进程。
|
|
48
|
+
// taskpolicy 原地 exec,保持同一 PID/进程组、环境和私有管道。
|
|
49
|
+
const file = process.platform === "darwin" ? "/usr/sbin/taskpolicy" : options.file;
|
|
50
|
+
const args = process.platform === "darwin" ? ["-a", "--", options.file, ...options.args] : [...options.args];
|
|
51
|
+
const child = spawn(file, args, { cwd: options.cwd, env: options.env, detached: true, stdio });
|
|
48
52
|
const chunks = [];
|
|
49
53
|
let size = 0;
|
|
50
54
|
const metadata = [];
|
|
@@ -102,6 +106,22 @@ export async function runMachineEnvironmentProcess(options) {
|
|
|
102
106
|
try {
|
|
103
107
|
await cleanupOnce();
|
|
104
108
|
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
const pid = child.pid;
|
|
111
|
+
if (error instanceof MachineEnvironmentCaptureError && error.code === "shell_cleanup_failed" && pid !== undefined) {
|
|
112
|
+
// 后续只验证旧进程组消失,不对可能复用的 PID 再发送终止信号。
|
|
113
|
+
throw new MachineEnvironmentCaptureError("shell_cleanup_failed", () => {
|
|
114
|
+
try {
|
|
115
|
+
process.kill(-pid, 0);
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
catch (cause) {
|
|
119
|
+
return cause.code === "ESRCH";
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
105
125
|
finally {
|
|
106
126
|
for (const stream of child.stdio)
|
|
107
127
|
stream?.destroy();
|