@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.
Files changed (2) hide show
  1. package/dist/process.mjs +118 -88
  2. 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 ../kernel/src/source-value.ts
79
- const BOOLEAN_TRUE_VALUES = /* @__PURE__ */ new Set([
80
- "1",
81
- "true",
82
- "yes",
83
- "on"
84
- ]);
85
- const BOOLEAN_FALSE_VALUES = /* @__PURE__ */ new Set([
86
- "0",
87
- "false",
88
- "no",
89
- "off"
90
- ]);
91
- function parseBooleanLiteral(value) {
92
- const normalized = value.trim().toLowerCase();
93
- if (BOOLEAN_TRUE_VALUES.has(normalized)) return true;
94
- if (BOOLEAN_FALSE_VALUES.has(normalized)) return false;
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.emitForegroundCompletion(cmd, args);
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, settlement.requestKill);
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
- fail(spawnRejectionError(abort, executable, cmd, args, err));
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)}`, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-device/proxy",
3
- "version": "0.21.29-nightly.20261019.75",
3
+ "version": "0.21.29",
4
4
  "type": "module",
5
5
  "description": "Transport-neutral authenticating proxy in front of an agent-device daemon.",
6
6
  "license": "MIT",