@agent-device/proxy 0.21.29-nightly.20261019.75 → 0.21.29
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/process.mjs +118 -88
- package/package.json +1 -1
package/dist/process.mjs
CHANGED
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
import { i as redactDiagnosticData, n as createRequestCanceledError, t as AppError } from "./errors.mjs";
|
|
2
|
-
import crypto from "node:crypto";
|
|
2
|
+
import crypto, { randomUUID } from "node:crypto";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
5
|
import fs from "node:fs";
|
|
6
6
|
import { spawn, spawnSync } from "node:child_process";
|
|
7
7
|
import { Readable } from "node:stream";
|
|
8
8
|
import { pipeline } from "node:stream/promises";
|
|
9
|
+
import os from "node:os";
|
|
10
|
+
//#region ../kernel/src/source-value.ts
|
|
11
|
+
const BOOLEAN_TRUE_VALUES = /* @__PURE__ */ new Set([
|
|
12
|
+
"1",
|
|
13
|
+
"true",
|
|
14
|
+
"yes",
|
|
15
|
+
"on"
|
|
16
|
+
]);
|
|
17
|
+
const BOOLEAN_FALSE_VALUES = /* @__PURE__ */ new Set([
|
|
18
|
+
"0",
|
|
19
|
+
"false",
|
|
20
|
+
"no",
|
|
21
|
+
"off"
|
|
22
|
+
]);
|
|
23
|
+
function parseBooleanLiteral(value) {
|
|
24
|
+
const normalized = value.trim().toLowerCase();
|
|
25
|
+
if (BOOLEAN_TRUE_VALUES.has(normalized)) return true;
|
|
26
|
+
if (BOOLEAN_FALSE_VALUES.has(normalized)) return false;
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
9
29
|
//#region ../host-kit/src/internal/diagnostics.ts
|
|
10
30
|
const diagnosticsStorage = new AsyncLocalStorage();
|
|
11
31
|
function updateDiagnosticsScope(options) {
|
|
@@ -52,6 +72,40 @@ function emitDiagnostic(event) {
|
|
|
52
72
|
if (scope.debug && !scope.logPath && !scope.traceLogPath) process.stderr.write(`[agent-device][diag] ${fileLine}`);
|
|
53
73
|
} catch {}
|
|
54
74
|
}
|
|
75
|
+
function flushDiagnosticsToSessionFile(options = {}) {
|
|
76
|
+
const scope = diagnosticsStorage.getStore();
|
|
77
|
+
if (!scope) return null;
|
|
78
|
+
if (!options.force && !scope.debug && !scope.flushOnSuccess) return null;
|
|
79
|
+
if (scope.events.length === 0) return null;
|
|
80
|
+
const values = sortedScopeSensitiveValues(scope);
|
|
81
|
+
try {
|
|
82
|
+
const logPath = scope.logPath ?? scope.flushedLogPath;
|
|
83
|
+
if (logPath) {
|
|
84
|
+
const pendingEvents = scope.events.slice(scope.liveWrittenEventCount);
|
|
85
|
+
if (pendingEvents.length > 0) appendDiagnosticLine(scope, logPath, `${pendingEvents.map((entry) => JSON.stringify(replaceSensitiveValues(entry, values))).join("\n")}\n`);
|
|
86
|
+
const logRecord = scope.logRecord;
|
|
87
|
+
scope.events = [];
|
|
88
|
+
scope.liveWrittenEventCount = 0;
|
|
89
|
+
return {
|
|
90
|
+
path: logPath,
|
|
91
|
+
...logRecord ? { ref: logRecord } : {}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const sessionDir = sanitizePathPart(scope.session ?? "default");
|
|
95
|
+
const dayDir = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
96
|
+
const baseDir = path.join(os.homedir(), ".agent-device", "logs", sessionDir, dayDir);
|
|
97
|
+
fs.mkdirSync(baseDir, { recursive: true });
|
|
98
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replaceAll(/[:.]/g, "-");
|
|
99
|
+
const filePath = path.join(baseDir, `${timestamp}-${scope.diagnosticId}.ndjson`);
|
|
100
|
+
const lines = scope.events.map((entry) => JSON.stringify(replaceSensitiveValues(entry, values)));
|
|
101
|
+
fs.writeFileSync(filePath, `${lines.join("\n")}\n`);
|
|
102
|
+
scope.flushedLogPath = filePath;
|
|
103
|
+
scope.events = [];
|
|
104
|
+
return { path: filePath };
|
|
105
|
+
} catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
55
109
|
/** Longest-first so an overlapping shorter literal cannot truncate a longer one. */
|
|
56
110
|
function sortedScopeSensitiveValues(scope) {
|
|
57
111
|
scope.sortedSensitiveValues ??= [...scope.sensitiveValues].sort((left, right) => right.length - left.length);
|
|
@@ -66,6 +120,9 @@ function replaceSensitiveValues(value, sensitiveValues) {
|
|
|
66
120
|
if (!value || typeof value !== "object") return value;
|
|
67
121
|
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, replaceSensitiveValues(entry, sensitiveValues)]));
|
|
68
122
|
}
|
|
123
|
+
function sanitizePathPart(value) {
|
|
124
|
+
return value.replaceAll(/[^a-zA-Z0-9._-]/g, "_");
|
|
125
|
+
}
|
|
69
126
|
function appendDiagnosticLine(scope, logPath, line) {
|
|
70
127
|
const dir = path.dirname(logPath);
|
|
71
128
|
if (!scope.ensuredDirectories.has(dir)) {
|
|
@@ -75,28 +132,41 @@ function appendDiagnosticLine(scope, logPath, line) {
|
|
|
75
132
|
fs.appendFileSync(logPath, line, "utf8");
|
|
76
133
|
}
|
|
77
134
|
//#endregion
|
|
78
|
-
//#region ../
|
|
79
|
-
|
|
80
|
-
"
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
135
|
+
//#region ../host-kit/src/internal/exec-trace.ts
|
|
136
|
+
function createExecTraceContext(cmd, timeoutMs, hostEnv) {
|
|
137
|
+
const envTraceEnabled = parseBooleanLiteral(hostEnv?.AGENT_DEVICE_EXEC_TRACE ?? "") === true;
|
|
138
|
+
const meta = getDiagnosticsMeta();
|
|
139
|
+
if (!meta.debug && !envTraceEnabled) return { emit: () => {} };
|
|
140
|
+
if (envTraceEnabled && !meta.flushOnSuccess) updateDiagnosticsScope({ flushOnSuccess: true });
|
|
141
|
+
const started = performance.now();
|
|
142
|
+
const execId = randomUUID();
|
|
143
|
+
let pid;
|
|
144
|
+
let settlementEmitted = false;
|
|
145
|
+
const command = path.basename(cmd);
|
|
146
|
+
const emit = AsyncLocalStorage.bind((event, data = {}) => {
|
|
147
|
+
pid = data.pid ?? pid;
|
|
148
|
+
emitDiagnostic({
|
|
149
|
+
level: "debug",
|
|
150
|
+
phase: "exec_command",
|
|
151
|
+
durationMs: performance.now() - started,
|
|
152
|
+
data: {
|
|
153
|
+
execId,
|
|
154
|
+
command,
|
|
155
|
+
event,
|
|
156
|
+
pid,
|
|
157
|
+
timeoutMs,
|
|
158
|
+
...data
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
if (event === "settled") settlementEmitted = true;
|
|
162
|
+
if (envTraceEnabled && (event === "settled" || event === "close" && settlementEmitted)) flushDiagnosticsToSessionFile();
|
|
163
|
+
});
|
|
164
|
+
emit("spawn_start");
|
|
165
|
+
return { emit };
|
|
95
166
|
}
|
|
96
167
|
//#endregion
|
|
97
168
|
//#region ../host-kit/src/internal/exec.ts
|
|
98
169
|
const BARE_COMMAND_RE = /^[A-Za-z0-9][A-Za-z0-9._+-]*$/;
|
|
99
|
-
const EXEC_DIAGNOSTIC_ARG_LIMIT = 6;
|
|
100
170
|
const commandExecutorOverrideScope = new AsyncLocalStorage();
|
|
101
171
|
async function withoutCommandExecutorOverride(fn) {
|
|
102
172
|
return await commandExecutorOverrideScope.run(void 0, fn);
|
|
@@ -143,7 +213,7 @@ function coerceExecResult(result) {
|
|
|
143
213
|
}
|
|
144
214
|
function runSpawnedCommand(cmd, args, options = {}) {
|
|
145
215
|
const executable = normalizeExecutableCommand(cmd);
|
|
146
|
-
const execTrace = createExecTraceContext();
|
|
216
|
+
const execTrace = createExecTraceContext(cmd, normalizeTimeoutMs(options.timeoutMs), commandHostScope.getStore()?.host.env);
|
|
147
217
|
return new Promise((resolve, reject) => {
|
|
148
218
|
const child = spawn(executable, args, {
|
|
149
219
|
cwd: options.cwd,
|
|
@@ -157,6 +227,7 @@ function runSpawnedCommand(cmd, args, options = {}) {
|
|
|
157
227
|
windowsHide: true,
|
|
158
228
|
shell: false
|
|
159
229
|
});
|
|
230
|
+
child.once("spawn", () => execTrace.emit("spawn", { pid: child.pid }));
|
|
160
231
|
options.onSpawn?.(child);
|
|
161
232
|
let stdout = "";
|
|
162
233
|
const stdoutChunks = options.binaryStdout && options.captureOutput !== false ? [] : void 0;
|
|
@@ -165,20 +236,20 @@ function runSpawnedCommand(cmd, args, options = {}) {
|
|
|
165
236
|
const timeoutMs = normalizeTimeoutMs(options.timeoutMs);
|
|
166
237
|
let timeoutHandle = null;
|
|
167
238
|
let settled = false;
|
|
168
|
-
function finish() {
|
|
239
|
+
function finish(reason) {
|
|
169
240
|
if (settled) return false;
|
|
170
241
|
settled = true;
|
|
171
242
|
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
172
243
|
abort.dispose();
|
|
173
244
|
destroyCommandStreams(child);
|
|
174
|
-
execTrace.
|
|
245
|
+
execTrace.emit("settled", { reason });
|
|
175
246
|
return true;
|
|
176
247
|
}
|
|
177
|
-
function fail(error) {
|
|
178
|
-
if (finish()) reject(error);
|
|
248
|
+
function fail(error, reason) {
|
|
249
|
+
if (finish(reason)) reject(error);
|
|
179
250
|
}
|
|
180
251
|
function settle(code) {
|
|
181
|
-
if (!finish()) return;
|
|
252
|
+
if (!finish(abort.didAbort ? "abort" : didTimeout ? "timeout" : (code ?? 1) !== 0 && !options.allowFailure ? "exit_failure" : "success")) return;
|
|
182
253
|
const finalExitCode = code ?? 1;
|
|
183
254
|
if (!abort.didAbort && didTimeout && timeoutMs) {
|
|
184
255
|
reject(createTimeoutError(executable, cmd, args, timeoutMs, finalExitCode, stdout, stderr));
|
|
@@ -200,8 +271,12 @@ function runSpawnedCommand(cmd, args, options = {}) {
|
|
|
200
271
|
killProcessTree: () => killProcessTree(child, options),
|
|
201
272
|
settle
|
|
202
273
|
});
|
|
203
|
-
const abort = watchCommandAbort(options,
|
|
274
|
+
const abort = watchCommandAbort(options, () => {
|
|
275
|
+
execTrace.emit("abort");
|
|
276
|
+
settlement.requestKill();
|
|
277
|
+
});
|
|
204
278
|
timeoutHandle = timeoutMs ? setTimeout(() => {
|
|
279
|
+
execTrace.emit("deadline");
|
|
205
280
|
didTimeout = true;
|
|
206
281
|
settlement.requestKill();
|
|
207
282
|
}, timeoutMs) : null;
|
|
@@ -210,7 +285,7 @@ function runSpawnedCommand(cmd, args, options = {}) {
|
|
|
210
285
|
writeChildStdin(child, options.stdin).catch((error) => {
|
|
211
286
|
if (abort.didAbort || didTimeout) return;
|
|
212
287
|
if (isEpipeError(error)) return;
|
|
213
|
-
fail(createStdinError(executable, cmd, args, error));
|
|
288
|
+
fail(createStdinError(executable, cmd, args, error), "stdin_error");
|
|
214
289
|
killProcessTree(child, options);
|
|
215
290
|
});
|
|
216
291
|
child.stdout.on("data", (chunk) => {
|
|
@@ -228,10 +303,23 @@ function runSpawnedCommand(cmd, args, options = {}) {
|
|
|
228
303
|
options.onStderrChunk?.(text);
|
|
229
304
|
});
|
|
230
305
|
child.on("error", (err) => {
|
|
231
|
-
|
|
306
|
+
execTrace.emit("error");
|
|
307
|
+
fail(spawnRejectionError(abort, executable, cmd, args, err), "spawn_error");
|
|
308
|
+
});
|
|
309
|
+
child.once("exit", (code, signal) => {
|
|
310
|
+
execTrace.emit("exit", {
|
|
311
|
+
exitCode: code,
|
|
312
|
+
signal
|
|
313
|
+
});
|
|
314
|
+
settlement.recordExit(code);
|
|
315
|
+
});
|
|
316
|
+
child.once("close", (code, signal) => {
|
|
317
|
+
execTrace.emit("close", {
|
|
318
|
+
exitCode: code,
|
|
319
|
+
signal
|
|
320
|
+
});
|
|
321
|
+
settle(code);
|
|
232
322
|
});
|
|
233
|
-
child.once("exit", settlement.recordExit);
|
|
234
|
-
child.once("close", settle);
|
|
235
323
|
});
|
|
236
324
|
}
|
|
237
325
|
function runCmdSync(cmd, args, options = {}) {
|
|
@@ -273,64 +361,6 @@ function runCmdSync(cmd, args, options = {}) {
|
|
|
273
361
|
stdoutBuffer
|
|
274
362
|
};
|
|
275
363
|
}
|
|
276
|
-
function createExecTraceContext() {
|
|
277
|
-
const diagnosticsMeta = getDiagnosticsMeta();
|
|
278
|
-
const diagnosticsDebugEnabled = diagnosticsMeta.debug === true;
|
|
279
|
-
const envTraceEnabled = parseBooleanLiteral(commandHostScope.getStore()?.host.env.AGENT_DEVICE_EXEC_TRACE ?? "") === true;
|
|
280
|
-
if (!diagnosticsDebugEnabled && !envTraceEnabled) return createDisabledExecTraceContext();
|
|
281
|
-
if (envTraceEnabled && diagnosticsMeta.flushOnSuccess !== true) updateDiagnosticsScope({ flushOnSuccess: true });
|
|
282
|
-
const startedAtMs = Date.now();
|
|
283
|
-
let completionEmitted = false;
|
|
284
|
-
return {
|
|
285
|
-
emitForegroundCompletion: (cmd, args) => {
|
|
286
|
-
if (completionEmitted) return;
|
|
287
|
-
completionEmitted = true;
|
|
288
|
-
emitExecCommandDiagnostic({
|
|
289
|
-
cmd,
|
|
290
|
-
args,
|
|
291
|
-
startedAtMs
|
|
292
|
-
});
|
|
293
|
-
},
|
|
294
|
-
emitBackgroundSpawn: (cmd, args) => {
|
|
295
|
-
emitExecCommandDiagnostic({
|
|
296
|
-
cmd,
|
|
297
|
-
args,
|
|
298
|
-
data: { event: "spawn" }
|
|
299
|
-
});
|
|
300
|
-
},
|
|
301
|
-
emitBackgroundCompletion: (cmd, args, event) => {
|
|
302
|
-
if (completionEmitted) return;
|
|
303
|
-
completionEmitted = true;
|
|
304
|
-
emitExecCommandDiagnostic({
|
|
305
|
-
cmd,
|
|
306
|
-
args,
|
|
307
|
-
startedAtMs,
|
|
308
|
-
data: { event }
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
function createDisabledExecTraceContext() {
|
|
314
|
-
return {
|
|
315
|
-
emitForegroundCompletion: () => {},
|
|
316
|
-
emitBackgroundSpawn: () => {},
|
|
317
|
-
emitBackgroundCompletion: () => {}
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
function emitExecCommandDiagnostic(params) {
|
|
321
|
-
const argsPrefix = params.args.slice(0, EXEC_DIAGNOSTIC_ARG_LIMIT);
|
|
322
|
-
emitDiagnostic({
|
|
323
|
-
level: "debug",
|
|
324
|
-
phase: "exec_command",
|
|
325
|
-
durationMs: params.startedAtMs === void 0 ? void 0 : Math.max(0, Date.now() - params.startedAtMs),
|
|
326
|
-
data: {
|
|
327
|
-
command: params.cmd,
|
|
328
|
-
argsPrefix,
|
|
329
|
-
...params.args.length > argsPrefix.length ? { omittedArgCount: params.args.length - argsPrefix.length } : {},
|
|
330
|
-
...params.data ?? {}
|
|
331
|
-
}
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
364
|
function normalizeExecutableCommand(cmd) {
|
|
335
365
|
const candidate = normalizeExecutableLookup(cmd);
|
|
336
366
|
if (!candidate) throw new AppError("INVALID_ARGS", `Invalid executable command: ${JSON.stringify(cmd)}`, {
|