@ai-agent-forge/plugin-process-exec 0.88.4 → 0.88.6

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/agent-forge.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "packageFormatVersion": 1,
3
3
  "id": "agent-forge.plugin.process-exec",
4
- "pluginVersion": "0.88.4",
4
+ "pluginVersion": "0.88.6",
5
5
  "apiVersion": "1",
6
6
  "minHostVersion": "0.84.4",
7
7
  "capabilityRefs": [
@@ -1 +1 @@
1
- {"version":3,"file":"node-adapter.d.ts","sourceRoot":"","sources":["../src/node-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAG3C,OAAO,EAGN,KAAK,oBAAoB,EAGzB,MAAM,YAAY,CAAC;AAKpB,eAAO,MAAM,gCAAgC,QAAc,CAAC;AAE5D,eAAO,MAAM,iCAAiC,OAAO,CAAC;AAItD,eAAO,MAAM,sCAAsC,MAAM,CAAC;AAE1D,MAAM,WAAW,+BAA+B;IAC/C,iEAAiE;IACjE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,4DAA4D;IAC5D,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CAC9B;AAkBD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAc7G;AAED,+EAA+E;AAC/E,wBAAgB,qBAAqB,CACpC,KAAK,EAAE,MAAM,EACb,YAAY,SAAoC,EAChD,GAAG,CAAC,EAAE,MAAM,GACV,MAAM,CAkBR;AAgMD,oFAAoF;AACpF,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,+BAA+B,GAAG,oBAAoB,CAiL7G","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { isAbsolute } from \"node:path\";\nimport { killProcessTree, sanitizeBinaryOutput } from \"./shell-utils.ts\";\nimport {\n\tPROCESS_EXEC_HOST_ADAPTER_ID_V1,\n\tPROCESS_EXEC_HOST_ADAPTER_VERSION_V1,\n\ttype ProcessExecAdapterV1,\n\ttype ProcessExecRequestV1,\n\ttype ProcessExecResultV1,\n} from \"./types.ts\";\n\nconst DEFAULT_TIMEOUT_MS_V1 = 30_000;\nconst MAX_TIMEOUT_MS_V1 = 120_000;\nconst DEFAULT_OUTPUT_BYTES_V1 = 64 * 1024;\nexport const PROCESS_EXEC_MAX_OUTPUT_BYTES_V1 = 1024 * 1024;\nconst DEFAULT_PREVIEW_BYTES_V1 = 4096;\nexport const PROCESS_EXEC_MAX_PREVIEW_BYTES_V1 = 8192;\nconst MAX_ARGS_V1 = 256;\nconst MAX_COMMAND_CHARS_V1 = 512;\nconst MAX_ARG_CHARS_V1 = 4096;\nexport const PROCESS_EXEC_MAX_OPERATION_ID_CHARS_V1 = 256;\n\nexport interface NodeProcessExecAdapterOptionsV1 {\n\t/** Host-owned working directory; requests cannot override it. */\n\treadonly cwd: string;\n\treadonly env?: Readonly<Record<string, string>>;\n\treadonly defaultTimeoutMs?: number;\n\treadonly maxStdoutBytes?: number;\n\treadonly maxStderrBytes?: number;\n\treadonly previewBytes?: number;\n\t/** Test seam for the host-owned process-tree terminator. */\n\treadonly killProcessTree?: (pid: number) => void;\n\t/** Deterministic host seam; production defaults to Node's shell-free spawn. */\n\treadonly spawn?: typeof spawn;\n}\n\nconst SAFE_ENVIRONMENT_KEYS_V1 = new Set(\n\t[\"PATH\", \"PATHEXT\", \"SystemRoot\", \"SYSTEMROOT\", \"WINDIR\", \"TEMP\", \"TMP\", \"TMPDIR\", \"LANG\", \"LC_ALL\"].map((key) =>\n\t\tkey.toLowerCase(),\n\t),\n);\n\n/** Splits a preview budget into head/tail segments (75% head, rest tail; head wins on tiny budgets). */\nfunction splitPreviewBudgetV1(budget: number): { head: number; tail: number } {\n\tconst head = Math.ceil((budget * 3) / 4);\n\treturn { head, tail: Math.max(0, budget - head) };\n}\n\nfunction omissionMarkerV1(omitted: number, unit: \"bytes\" | \"chars\"): string {\n\treturn `\\n... [${omitted} ${unit} omitted] ...\\n`;\n}\n\n/**\n * Builds a bounded preview from the retained capture. `captured` holds the\n * complete output when `totalBytes <= previewBytes`; otherwise it holds the\n * first `head` bytes followed by the last `tail` bytes of the stream, and the\n * two segments are joined with an omission marker. The marker rides on top of\n * the segment budgets but the assembled preview never exceeds\n * `PROCESS_EXEC_MAX_PREVIEW_BYTES_V1`.\n */\nexport function buildHeadTailPreviewV1(captured: Uint8Array, previewBytes: number, totalBytes: number): string {\n\tif (totalBytes <= previewBytes) return previewText(captured);\n\tconst budget = splitPreviewBudgetV1(previewBytes);\n\tconst headPart = captured.subarray(0, Math.min(budget.head, captured.byteLength));\n\tlet tailBytes = Math.min(budget.tail, Math.max(0, captured.byteLength - headPart.byteLength));\n\tlet marker = omissionMarkerV1(Math.max(0, totalBytes - headPart.byteLength - tailBytes), \"bytes\");\n\twhile (headPart.byteLength + tailBytes + marker.length > PROCESS_EXEC_MAX_PREVIEW_BYTES_V1 && tailBytes > 0) {\n\t\ttailBytes = Math.max(\n\t\t\t0,\n\t\t\tMath.min(tailBytes, PROCESS_EXEC_MAX_PREVIEW_BYTES_V1 - headPart.byteLength - marker.length),\n\t\t);\n\t\tmarker = omissionMarkerV1(Math.max(0, totalBytes - headPart.byteLength - tailBytes), \"bytes\");\n\t}\n\treturn previewText(headPart) + marker + previewText(captured.subarray(captured.byteLength - tailBytes));\n}\n\n/** Redacts credentials and host paths before a preview can reach the model. */\nexport function sanitizeProcessTextV1(\n\tvalue: string,\n\tmaximumChars = PROCESS_EXEC_MAX_PREVIEW_BYTES_V1,\n\tcwd?: string,\n): string {\n\tlet text = sanitizeBinaryOutput(value);\n\tif (cwd !== undefined && cwd !== \"\") text = text.replaceAll(cwd, \"[redacted-path]\");\n\ttext = text\n\t\t.replace(\n\t\t\t/((?:api[-_ ]?key|access[-_ ]?token|auth(?:orization)?|bearer|password|passwd|secret|token)\\s*[:=]\\s*)([^\\s,;]+)/giu,\n\t\t\t\"$1[redacted-secret]\",\n\t\t)\n\t\t.replace(/(?:[A-Za-z]:[\\\\/]|\\\\\\\\|\\/(?:home|Users|private|tmp|var|workspace)\\/)[^\\s\"'<>]*/gu, \"[redacted-path]\");\n\tif (text.length <= maximumChars) return text;\n\tconst head = Math.ceil((maximumChars * 3) / 4);\n\tlet tail = maximumChars - head;\n\tlet marker = omissionMarkerV1(text.length - head - tail, \"chars\");\n\twhile (head + tail + marker.length > maximumChars && tail > 0) {\n\t\ttail = Math.max(0, Math.min(tail, maximumChars - head - marker.length));\n\t\tmarker = omissionMarkerV1(text.length - head - tail, \"chars\");\n\t}\n\treturn text.slice(0, head) + marker + (tail > 0 ? text.slice(text.length - tail) : \"\");\n}\n\nfunction hostEnvironment(explicit: Readonly<Record<string, string>> | undefined): NodeJS.ProcessEnv {\n\tconst inherited: NodeJS.ProcessEnv = {};\n\tfor (const [key, value] of Object.entries(process.env)) {\n\t\tif (value !== undefined && SAFE_ENVIRONMENT_KEYS_V1.has(key.toLowerCase())) inherited[key] = value;\n\t}\n\treturn { ...inherited, ...(explicit ?? {}) };\n}\n\nfunction positiveBound(value: number | undefined, fallback: number, maximum: number, label: string): number {\n\tconst result = value ?? fallback;\n\tif (!Number.isSafeInteger(result) || result < 1 || result > maximum)\n\t\tthrow new Error(`${label} must be a positive integer at most ${maximum}`);\n\treturn result;\n}\n\nfunction inputError(operationId: string, message: string): ProcessExecResultV1 {\n\treturn {\n\t\tversion: 1,\n\t\toperationId,\n\t\tstatus: \"failed\",\n\t\tsummary: \"Process request was invalid\",\n\t\tstdoutBytes: 0,\n\t\tstderrBytes: 0,\n\t\tstdoutPreview: \"\",\n\t\tstderrPreview: \"\",\n\t\tstdoutTruncated: false,\n\t\tstderrTruncated: false,\n\t\terror: { version: 1, code: \"invalid_input\", message },\n\t};\n}\n\nfunction cancelled(operationId: string): ProcessExecResultV1 {\n\treturn {\n\t\tversion: 1,\n\t\toperationId,\n\t\tstatus: \"cancelled\",\n\t\tsummary: \"Process execution was cancelled\",\n\t\tstdoutBytes: 0,\n\t\tstderrBytes: 0,\n\t\tstdoutPreview: \"\",\n\t\tstderrPreview: \"\",\n\t\tstdoutTruncated: false,\n\t\tstderrTruncated: false,\n\t\terror: { version: 1, code: \"cancelled\", message: \"Process execution was cancelled\" },\n\t};\n}\n\nfunction validateRequest(request: ProcessExecRequestV1): string | undefined {\n\tif (request.version !== 1) return \"version must be 1\";\n\tif (\n\t\ttypeof request.operationId !== \"string\" ||\n\t\trequest.operationId.trim() === \"\" ||\n\t\trequest.operationId.length > PROCESS_EXEC_MAX_OPERATION_ID_CHARS_V1\n\t)\n\t\treturn \"operationId is invalid\";\n\tif (\n\t\ttypeof request.command !== \"string\" ||\n\t\trequest.command.trim() === \"\" ||\n\t\trequest.command.length > MAX_COMMAND_CHARS_V1 ||\n\t\t/[\\u0000\\r\\n]/u.test(request.command)\n\t)\n\t\treturn \"command is invalid\";\n\tif (!Array.isArray(request.args) || request.args.length > MAX_ARGS_V1) return \"args are invalid\";\n\tif (\n\t\trequest.args.some((arg) => typeof arg !== \"string\" || arg.length > MAX_ARG_CHARS_V1 || /[\\u0000\\r\\n]/u.test(arg))\n\t)\n\t\treturn \"args are invalid\";\n\tif (\n\t\trequest.timeoutMs !== undefined &&\n\t\t(!Number.isSafeInteger(request.timeoutMs) || request.timeoutMs < 1 || request.timeoutMs > MAX_TIMEOUT_MS_V1)\n\t)\n\t\treturn `timeoutMs must be a positive integer at most ${MAX_TIMEOUT_MS_V1}`;\n\treturn undefined;\n}\n\nfunction previewText(bytes: Uint8Array): string {\n\treturn new TextDecoder().decode(bytes);\n}\n\ninterface PreviewCaptureV1 {\n\treadonly head: Uint8Array;\n\treadonly tail: Uint8Array;\n\theadLength: number;\n\ttailLength: number;\n}\n\n/** Retains the first head-budget and last tail-budget bytes of a stream for head+tail previews. */\nfunction createPreviewCaptureV1(previewBytes: number): PreviewCaptureV1 {\n\tconst budget = splitPreviewBudgetV1(previewBytes);\n\treturn { head: new Uint8Array(budget.head), tail: new Uint8Array(budget.tail), headLength: 0, tailLength: 0 };\n}\n\nfunction capturePreviewChunkV1(capture: PreviewCaptureV1, chunk: Uint8Array): void {\n\tconst headCopy = Math.min(chunk.byteLength, capture.head.byteLength - capture.headLength);\n\tcapture.head.set(chunk.subarray(0, headCopy), capture.headLength);\n\tcapture.headLength += headCopy;\n\tconst tailSize = capture.tail.byteLength;\n\tif (tailSize === 0) return;\n\tif (chunk.byteLength >= tailSize) {\n\t\tcapture.tail.set(chunk.subarray(chunk.byteLength - tailSize));\n\t\tcapture.tailLength = tailSize;\n\t\treturn;\n\t}\n\tcapture.tail.copyWithin(0, chunk.byteLength);\n\tcapture.tail.set(chunk, tailSize - chunk.byteLength);\n\tcapture.tailLength = Math.min(tailSize, capture.tailLength + chunk.byteLength);\n}\n\n/** Materializes the retained bytes: the full output when it fit the budget, else head followed by tail. */\nfunction previewCaptureBytesV1(capture: PreviewCaptureV1, totalBytes: number): Uint8Array {\n\tconst capacity = capture.head.byteLength + capture.tail.byteLength;\n\tif (totalBytes <= capture.head.byteLength) return capture.head.subarray(0, totalBytes);\n\tif (totalBytes <= capacity) {\n\t\tconst tailFrom = totalBytes - capture.head.byteLength;\n\t\tconst captured = new Uint8Array(totalBytes);\n\t\tcaptured.set(capture.head);\n\t\tcaptured.set(capture.tail.subarray(capture.tailLength - tailFrom, capture.tailLength), capture.headLength);\n\t\treturn captured;\n\t}\n\tconst captured = new Uint8Array(capacity);\n\tcaptured.set(capture.head.subarray(0, capture.headLength));\n\tcaptured.set(capture.tail.subarray(0, capture.tailLength), capture.headLength);\n\treturn captured;\n}\n\nfunction result(\n\trequest: ProcessExecRequestV1,\n\tstatus: ProcessExecResultV1[\"status\"],\n\tstdout: Uint8Array,\n\tstderr: Uint8Array,\n\tstdoutBytes: number,\n\tstderrBytes: number,\n\tpreviewBytes: number,\n\ttermination?: \"timeout\" | \"output_budget_exceeded\" | \"spawn_failed\" | \"exit_nonzero\" | \"cancelled\",\n\tcode?: number,\n\tcwd?: string,\n): ProcessExecResultV1 {\n\t// The byte-level head/tail split is authoritative; sanitize only redacts and\n\t// acts as the absolute-size circuit breaker at the contract cap.\n\tconst stdoutPreview = sanitizeProcessTextV1(\n\t\tbuildHeadTailPreviewV1(stdout, previewBytes, stdoutBytes),\n\t\tPROCESS_EXEC_MAX_PREVIEW_BYTES_V1,\n\t\tcwd,\n\t);\n\tconst stderrPreview = sanitizeProcessTextV1(\n\t\tbuildHeadTailPreviewV1(stderr, previewBytes, stderrBytes),\n\t\tPROCESS_EXEC_MAX_PREVIEW_BYTES_V1,\n\t\tcwd,\n\t);\n\tconst errorCode = termination;\n\tconst error =\n\t\terrorCode === undefined\n\t\t\t? undefined\n\t\t\t: {\n\t\t\t\t\tversion: 1 as const,\n\t\t\t\t\tcode: errorCode,\n\t\t\t\t\tmessage:\n\t\t\t\t\t\terrorCode === \"exit_nonzero\"\n\t\t\t\t\t\t\t? \"Process exited with a non-zero status\"\n\t\t\t\t\t\t\t: errorCode === \"cancelled\"\n\t\t\t\t\t\t\t\t? \"Process execution was cancelled\"\n\t\t\t\t\t\t\t\t: \"Process execution failed\",\n\t\t\t\t};\n\treturn {\n\t\tversion: 1,\n\t\toperationId: request.operationId,\n\t\tstatus,\n\t\tsummary:\n\t\t\tstatus === \"succeeded\"\n\t\t\t\t? \"Process completed successfully\"\n\t\t\t\t: termination === \"cancelled\"\n\t\t\t\t\t? \"Process execution was cancelled\"\n\t\t\t\t\t: termination === \"timeout\"\n\t\t\t\t\t\t? \"Process execution timed out\"\n\t\t\t\t\t\t: termination === \"output_budget_exceeded\"\n\t\t\t\t\t\t\t? \"Process output exceeded the host budget\"\n\t\t\t\t\t\t\t: termination === \"spawn_failed\"\n\t\t\t\t\t\t\t\t? \"Process could not be started\"\n\t\t\t\t\t\t\t\t: \"Process exited with a non-zero status\",\n\t\t...(code === undefined ? {} : { exitCode: code }),\n\t\tstdoutBytes,\n\t\tstderrBytes,\n\t\tstdoutPreview,\n\t\tstderrPreview,\n\t\tstdoutTruncated: stdoutBytes > previewBytes,\n\t\tstderrTruncated: stderrBytes > previewBytes,\n\t\t...(error === undefined ? {} : { error }),\n\t};\n}\n\n/** Node host adapter with shell-free argv execution and bounded output previews. */\nexport function createNodeProcessExecAdapterV1(options: NodeProcessExecAdapterOptionsV1): ProcessExecAdapterV1 {\n\tif (typeof options.cwd !== \"string\" || !isAbsolute(options.cwd)) throw new Error(\"cwd must be an absolute path\");\n\tconst defaultTimeoutMs = positiveBound(\n\t\toptions.defaultTimeoutMs,\n\t\tDEFAULT_TIMEOUT_MS_V1,\n\t\tMAX_TIMEOUT_MS_V1,\n\t\t\"defaultTimeoutMs\",\n\t);\n\tconst maxStdoutBytes = positiveBound(\n\t\toptions.maxStdoutBytes,\n\t\tDEFAULT_OUTPUT_BYTES_V1,\n\t\tPROCESS_EXEC_MAX_OUTPUT_BYTES_V1,\n\t\t\"maxStdoutBytes\",\n\t);\n\tconst maxStderrBytes = positiveBound(\n\t\toptions.maxStderrBytes,\n\t\tDEFAULT_OUTPUT_BYTES_V1,\n\t\tPROCESS_EXEC_MAX_OUTPUT_BYTES_V1,\n\t\t\"maxStderrBytes\",\n\t);\n\tconst previewBytes = positiveBound(\n\t\toptions.previewBytes,\n\t\tDEFAULT_PREVIEW_BYTES_V1,\n\t\tPROCESS_EXEC_MAX_PREVIEW_BYTES_V1,\n\t\t\"previewBytes\",\n\t);\n\tconst env = hostEnvironment(options.env);\n\tconst spawnProcess = options.spawn ?? spawn;\n\tconst terminateTree = options.killProcessTree ?? killProcessTree;\n\n\treturn {\n\t\tadapterId: PROCESS_EXEC_HOST_ADAPTER_ID_V1,\n\t\tadapterVersion: PROCESS_EXEC_HOST_ADAPTER_VERSION_V1,\n\t\trun: (request, signal) => {\n\t\t\tconst operationId =\n\t\t\t\ttypeof request.operationId === \"string\" && request.operationId.trim() !== \"\"\n\t\t\t\t\t? request.operationId\n\t\t\t\t\t: \"invalid-process-operation\";\n\t\t\tconst invalid = validateRequest(request);\n\t\t\tif (invalid !== undefined) return Promise.resolve(inputError(operationId, invalid));\n\t\t\tif (signal?.aborted) return Promise.resolve(cancelled(operationId));\n\t\t\tconst timeoutMs = request.timeoutMs ?? defaultTimeoutMs;\n\t\t\treturn new Promise<ProcessExecResultV1>((resolve) => {\n\t\t\t\tlet child: ReturnType<typeof spawn>;\n\t\t\t\ttry {\n\t\t\t\t\tchild = spawnProcess(request.command, [...request.args], {\n\t\t\t\t\t\tcwd: options.cwd,\n\t\t\t\t\t\t...(env === undefined ? {} : { env }),\n\t\t\t\t\t\tshell: false,\n\t\t\t\t\t\twindowsHide: true,\n\t\t\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\tresolve(\n\t\t\t\t\t\tresult(\n\t\t\t\t\t\t\trequest,\n\t\t\t\t\t\t\t\"failed\",\n\t\t\t\t\t\t\tnew Uint8Array(),\n\t\t\t\t\t\t\tnew Uint8Array(),\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\tpreviewBytes,\n\t\t\t\t\t\t\t\"spawn_failed\",\n\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\toptions.cwd,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlet stdoutBytes = 0;\n\t\t\t\tlet stderrBytes = 0;\n\t\t\t\tconst stdoutCapture = createPreviewCaptureV1(previewBytes);\n\t\t\t\tconst stderrCapture = createPreviewCaptureV1(previewBytes);\n\t\t\t\tlet termination:\n\t\t\t\t\t| \"timeout\"\n\t\t\t\t\t| \"output_budget_exceeded\"\n\t\t\t\t\t| \"spawn_failed\"\n\t\t\t\t\t| \"exit_nonzero\"\n\t\t\t\t\t| \"cancelled\"\n\t\t\t\t\t| undefined;\n\t\t\t\tlet settled = false;\n\t\t\t\tlet exited = false;\n\t\t\t\tlet terminationRequested = false;\n\t\t\t\tlet forceKillTimer: ReturnType<typeof setTimeout> | undefined;\n\t\t\t\tlet hardFinishTimer: ReturnType<typeof setTimeout> | undefined;\n\t\t\t\tconst forceTerminate = (): void => {\n\t\t\t\t\tif (exited) return;\n\t\t\t\t\tif (typeof child.pid === \"number\" && child.pid > 0) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tterminateTree(child.pid);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// The host cleanup boundary is best-effort; close/error still settles the operation.\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (!child.killed) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tchild.kill(\"SIGKILL\");\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// The close/error event remains authoritative.\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tconst armForceTermination = (): void => {\n\t\t\t\t\tif (forceKillTimer !== undefined) return;\n\t\t\t\t\tforceKillTimer = setTimeout(() => {\n\t\t\t\t\t\tforceTerminate();\n\t\t\t\t\t\thardFinishTimer = setTimeout(() => finalize(null), 500);\n\t\t\t\t\t}, 2000);\n\t\t\t\t};\n\t\t\t\tconst terminate = (reason: \"timeout\" | \"output_budget_exceeded\" | \"spawn_failed\" | \"cancelled\"): void => {\n\t\t\t\t\tif (termination === undefined) termination = reason;\n\t\t\t\t\tif (!terminationRequested) {\n\t\t\t\t\t\tterminationRequested = true;\n\t\t\t\t\t\tif (!child.killed) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t// The force cleanup timer still runs.\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tarmForceTermination();\n\t\t\t\t};\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tterminate(\"cancelled\");\n\t\t\t\t};\n\t\t\t\tconst finalize = (code: number | null): void => {\n\t\t\t\t\tif (settled) return;\n\t\t\t\t\tsettled = true;\n\t\t\t\t\texited = true;\n\t\t\t\t\tclearTimeout(timeout);\n\t\t\t\t\tif (forceKillTimer !== undefined) clearTimeout(forceKillTimer);\n\t\t\t\t\tif (hardFinishTimer !== undefined) clearTimeout(hardFinishTimer);\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\tif (signal?.aborted && termination === undefined) termination = \"cancelled\";\n\t\t\t\t\tif (termination === undefined && code !== 0) termination = \"exit_nonzero\";\n\t\t\t\t\tconst status =\n\t\t\t\t\t\ttermination === undefined ? \"succeeded\" : termination === \"cancelled\" ? \"cancelled\" : \"failed\";\n\t\t\t\t\tresolve(\n\t\t\t\t\t\tresult(\n\t\t\t\t\t\t\trequest,\n\t\t\t\t\t\t\tstatus,\n\t\t\t\t\t\t\tpreviewCaptureBytesV1(stdoutCapture, stdoutBytes),\n\t\t\t\t\t\t\tpreviewCaptureBytesV1(stderrCapture, stderrBytes),\n\t\t\t\t\t\t\tstdoutBytes,\n\t\t\t\t\t\t\tstderrBytes,\n\t\t\t\t\t\t\tpreviewBytes,\n\t\t\t\t\t\t\ttermination,\n\t\t\t\t\t\t\tcode ?? undefined,\n\t\t\t\t\t\t\toptions.cwd,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t};\n\t\t\t\tconst timeout = setTimeout(() => terminate(\"timeout\"), timeoutMs);\n\t\t\t\tif (signal !== undefined) signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\tchild.stdout?.on(\"data\", (chunk: Buffer | Uint8Array) => {\n\t\t\t\t\tconst bytes = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);\n\t\t\t\t\tstdoutBytes += bytes.byteLength;\n\t\t\t\t\tcapturePreviewChunkV1(stdoutCapture, bytes);\n\t\t\t\t\tif (stdoutBytes > maxStdoutBytes) terminate(\"output_budget_exceeded\");\n\t\t\t\t});\n\t\t\t\tchild.stderr?.on(\"data\", (chunk: Buffer | Uint8Array) => {\n\t\t\t\t\tconst bytes = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);\n\t\t\t\t\tstderrBytes += bytes.byteLength;\n\t\t\t\t\tcapturePreviewChunkV1(stderrCapture, bytes);\n\t\t\t\t\tif (stderrBytes > maxStderrBytes) terminate(\"output_budget_exceeded\");\n\t\t\t\t});\n\t\t\t\tchild.once(\"error\", () => {\n\t\t\t\t\tterminate(\"spawn_failed\");\n\t\t\t\t});\n\t\t\t\tchild.once(\"close\", (code) => {\n\t\t\t\t\texited = true;\n\t\t\t\t\tfinalize(code);\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"node-adapter.d.ts","sourceRoot":"","sources":["../src/node-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAG3C,OAAO,EAGN,KAAK,oBAAoB,EAGzB,MAAM,YAAY,CAAC;AAKpB,eAAO,MAAM,gCAAgC,QAAc,CAAC;AAE5D,eAAO,MAAM,iCAAiC,OAAO,CAAC;AAItD,eAAO,MAAM,sCAAsC,MAAM,CAAC;AAE1D,MAAM,WAAW,+BAA+B;IAC/C,iEAAiE;IACjE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,4DAA4D;IAC5D,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CAC9B;AAkBD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAc7G;AAED,+EAA+E;AAC/E,wBAAgB,qBAAqB,CACpC,KAAK,EAAE,MAAM,EACb,YAAY,SAAoC,EAChD,GAAG,CAAC,EAAE,MAAM,GACV,MAAM,CAkBR;AAgMD,oFAAoF;AACpF,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,+BAA+B,GAAG,oBAAoB,CAoL7G","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { isAbsolute } from \"node:path\";\nimport { killProcessTree, sanitizeBinaryOutput } from \"./shell-utils.ts\";\nimport {\n\tPROCESS_EXEC_HOST_ADAPTER_ID_V1,\n\tPROCESS_EXEC_HOST_ADAPTER_VERSION_V1,\n\ttype ProcessExecAdapterV1,\n\ttype ProcessExecRequestV1,\n\ttype ProcessExecResultV1,\n} from \"./types.ts\";\n\nconst DEFAULT_TIMEOUT_MS_V1 = 30_000;\nconst MAX_TIMEOUT_MS_V1 = 120_000;\nconst DEFAULT_OUTPUT_BYTES_V1 = 64 * 1024;\nexport const PROCESS_EXEC_MAX_OUTPUT_BYTES_V1 = 1024 * 1024;\nconst DEFAULT_PREVIEW_BYTES_V1 = 4096;\nexport const PROCESS_EXEC_MAX_PREVIEW_BYTES_V1 = 8192;\nconst MAX_ARGS_V1 = 256;\nconst MAX_COMMAND_CHARS_V1 = 512;\nconst MAX_ARG_CHARS_V1 = 4096;\nexport const PROCESS_EXEC_MAX_OPERATION_ID_CHARS_V1 = 256;\n\nexport interface NodeProcessExecAdapterOptionsV1 {\n\t/** Host-owned working directory; requests cannot override it. */\n\treadonly cwd: string;\n\treadonly env?: Readonly<Record<string, string>>;\n\treadonly defaultTimeoutMs?: number;\n\treadonly maxStdoutBytes?: number;\n\treadonly maxStderrBytes?: number;\n\treadonly previewBytes?: number;\n\t/** Test seam for the host-owned process-tree terminator. */\n\treadonly killProcessTree?: (pid: number) => void;\n\t/** Deterministic host seam; production defaults to Node's shell-free spawn. */\n\treadonly spawn?: typeof spawn;\n}\n\nconst SAFE_ENVIRONMENT_KEYS_V1 = new Set(\n\t[\"PATH\", \"PATHEXT\", \"SystemRoot\", \"SYSTEMROOT\", \"WINDIR\", \"TEMP\", \"TMP\", \"TMPDIR\", \"LANG\", \"LC_ALL\"].map((key) =>\n\t\tkey.toLowerCase(),\n\t),\n);\n\n/** Splits a preview budget into head/tail segments (75% head, rest tail; head wins on tiny budgets). */\nfunction splitPreviewBudgetV1(budget: number): { head: number; tail: number } {\n\tconst head = Math.ceil((budget * 3) / 4);\n\treturn { head, tail: Math.max(0, budget - head) };\n}\n\nfunction omissionMarkerV1(omitted: number, unit: \"bytes\" | \"chars\"): string {\n\treturn `\\n... [${omitted} ${unit} omitted] ...\\n`;\n}\n\n/**\n * Builds a bounded preview from the retained capture. `captured` holds the\n * complete output when `totalBytes <= previewBytes`; otherwise it holds the\n * first `head` bytes followed by the last `tail` bytes of the stream, and the\n * two segments are joined with an omission marker. The marker rides on top of\n * the segment budgets but the assembled preview never exceeds\n * `PROCESS_EXEC_MAX_PREVIEW_BYTES_V1`.\n */\nexport function buildHeadTailPreviewV1(captured: Uint8Array, previewBytes: number, totalBytes: number): string {\n\tif (totalBytes <= previewBytes) return previewText(captured);\n\tconst budget = splitPreviewBudgetV1(previewBytes);\n\tconst headPart = captured.subarray(0, Math.min(budget.head, captured.byteLength));\n\tlet tailBytes = Math.min(budget.tail, Math.max(0, captured.byteLength - headPart.byteLength));\n\tlet marker = omissionMarkerV1(Math.max(0, totalBytes - headPart.byteLength - tailBytes), \"bytes\");\n\twhile (headPart.byteLength + tailBytes + marker.length > PROCESS_EXEC_MAX_PREVIEW_BYTES_V1 && tailBytes > 0) {\n\t\ttailBytes = Math.max(\n\t\t\t0,\n\t\t\tMath.min(tailBytes, PROCESS_EXEC_MAX_PREVIEW_BYTES_V1 - headPart.byteLength - marker.length),\n\t\t);\n\t\tmarker = omissionMarkerV1(Math.max(0, totalBytes - headPart.byteLength - tailBytes), \"bytes\");\n\t}\n\treturn previewText(headPart) + marker + previewText(captured.subarray(captured.byteLength - tailBytes));\n}\n\n/** Redacts credentials and host paths before a preview can reach the model. */\nexport function sanitizeProcessTextV1(\n\tvalue: string,\n\tmaximumChars = PROCESS_EXEC_MAX_PREVIEW_BYTES_V1,\n\tcwd?: string,\n): string {\n\tlet text = sanitizeBinaryOutput(value);\n\tif (cwd !== undefined && cwd !== \"\") text = text.replaceAll(cwd, \"[redacted-path]\");\n\ttext = text\n\t\t.replace(\n\t\t\t/((?:api[-_ ]?key|access[-_ ]?token|auth(?:orization)?|bearer|password|passwd|secret|token)\\s*[:=]\\s*)([^\\s,;]+)/giu,\n\t\t\t\"$1[redacted-secret]\",\n\t\t)\n\t\t.replace(/(?:[A-Za-z]:[\\\\/]|\\\\\\\\|\\/(?:home|Users|private|tmp|var|workspace)\\/)[^\\s\"'<>]*/gu, \"[redacted-path]\");\n\tif (text.length <= maximumChars) return text;\n\tconst head = Math.ceil((maximumChars * 3) / 4);\n\tlet tail = maximumChars - head;\n\tlet marker = omissionMarkerV1(text.length - head - tail, \"chars\");\n\twhile (head + tail + marker.length > maximumChars && tail > 0) {\n\t\ttail = Math.max(0, Math.min(tail, maximumChars - head - marker.length));\n\t\tmarker = omissionMarkerV1(text.length - head - tail, \"chars\");\n\t}\n\treturn text.slice(0, head) + marker + (tail > 0 ? text.slice(text.length - tail) : \"\");\n}\n\nfunction hostEnvironment(explicit: Readonly<Record<string, string>> | undefined): NodeJS.ProcessEnv {\n\tconst inherited: NodeJS.ProcessEnv = {};\n\tfor (const [key, value] of Object.entries(process.env)) {\n\t\tif (value !== undefined && SAFE_ENVIRONMENT_KEYS_V1.has(key.toLowerCase())) inherited[key] = value;\n\t}\n\treturn { ...inherited, ...(explicit ?? {}) };\n}\n\nfunction positiveBound(value: number | undefined, fallback: number, maximum: number, label: string): number {\n\tconst result = value ?? fallback;\n\tif (!Number.isSafeInteger(result) || result < 1 || result > maximum)\n\t\tthrow new Error(`${label} must be a positive integer at most ${maximum}`);\n\treturn result;\n}\n\nfunction inputError(operationId: string, message: string): ProcessExecResultV1 {\n\treturn {\n\t\tversion: 1,\n\t\toperationId,\n\t\tstatus: \"failed\",\n\t\tsummary: \"Process request was invalid\",\n\t\tstdoutBytes: 0,\n\t\tstderrBytes: 0,\n\t\tstdoutPreview: \"\",\n\t\tstderrPreview: \"\",\n\t\tstdoutTruncated: false,\n\t\tstderrTruncated: false,\n\t\terror: { version: 1, code: \"invalid_input\", message },\n\t};\n}\n\nfunction cancelled(operationId: string): ProcessExecResultV1 {\n\treturn {\n\t\tversion: 1,\n\t\toperationId,\n\t\tstatus: \"cancelled\",\n\t\tsummary: \"Process execution was cancelled\",\n\t\tstdoutBytes: 0,\n\t\tstderrBytes: 0,\n\t\tstdoutPreview: \"\",\n\t\tstderrPreview: \"\",\n\t\tstdoutTruncated: false,\n\t\tstderrTruncated: false,\n\t\terror: { version: 1, code: \"cancelled\", message: \"Process execution was cancelled\" },\n\t};\n}\n\nfunction validateRequest(request: ProcessExecRequestV1): string | undefined {\n\tif (request.version !== 1) return \"version must be 1\";\n\tif (\n\t\ttypeof request.operationId !== \"string\" ||\n\t\trequest.operationId.trim() === \"\" ||\n\t\trequest.operationId.length > PROCESS_EXEC_MAX_OPERATION_ID_CHARS_V1\n\t)\n\t\treturn \"operationId is invalid\";\n\tif (\n\t\ttypeof request.command !== \"string\" ||\n\t\trequest.command.trim() === \"\" ||\n\t\trequest.command.length > MAX_COMMAND_CHARS_V1 ||\n\t\t/[\\u0000\\r\\n]/u.test(request.command)\n\t)\n\t\treturn \"command is invalid\";\n\tif (!Array.isArray(request.args) || request.args.length > MAX_ARGS_V1) return \"args are invalid\";\n\tif (\n\t\trequest.args.some((arg) => typeof arg !== \"string\" || arg.length > MAX_ARG_CHARS_V1 || /[\\u0000\\r\\n]/u.test(arg))\n\t)\n\t\treturn \"args are invalid\";\n\tif (\n\t\trequest.timeoutMs !== undefined &&\n\t\t(!Number.isSafeInteger(request.timeoutMs) || request.timeoutMs < 1 || request.timeoutMs > MAX_TIMEOUT_MS_V1)\n\t)\n\t\treturn `timeoutMs must be a positive integer at most ${MAX_TIMEOUT_MS_V1}`;\n\treturn undefined;\n}\n\nfunction previewText(bytes: Uint8Array): string {\n\treturn new TextDecoder().decode(bytes);\n}\n\ninterface PreviewCaptureV1 {\n\treadonly head: Uint8Array;\n\treadonly tail: Uint8Array;\n\theadLength: number;\n\ttailLength: number;\n}\n\n/** Retains the first head-budget and last tail-budget bytes of a stream for head+tail previews. */\nfunction createPreviewCaptureV1(previewBytes: number): PreviewCaptureV1 {\n\tconst budget = splitPreviewBudgetV1(previewBytes);\n\treturn { head: new Uint8Array(budget.head), tail: new Uint8Array(budget.tail), headLength: 0, tailLength: 0 };\n}\n\nfunction capturePreviewChunkV1(capture: PreviewCaptureV1, chunk: Uint8Array): void {\n\tconst headCopy = Math.min(chunk.byteLength, capture.head.byteLength - capture.headLength);\n\tcapture.head.set(chunk.subarray(0, headCopy), capture.headLength);\n\tcapture.headLength += headCopy;\n\tconst tailSize = capture.tail.byteLength;\n\tif (tailSize === 0) return;\n\tif (chunk.byteLength >= tailSize) {\n\t\tcapture.tail.set(chunk.subarray(chunk.byteLength - tailSize));\n\t\tcapture.tailLength = tailSize;\n\t\treturn;\n\t}\n\tcapture.tail.copyWithin(0, chunk.byteLength);\n\tcapture.tail.set(chunk, tailSize - chunk.byteLength);\n\tcapture.tailLength = Math.min(tailSize, capture.tailLength + chunk.byteLength);\n}\n\n/** Materializes the retained bytes: the full output when it fit the budget, else head followed by tail. */\nfunction previewCaptureBytesV1(capture: PreviewCaptureV1, totalBytes: number): Uint8Array {\n\tconst capacity = capture.head.byteLength + capture.tail.byteLength;\n\tif (totalBytes <= capture.head.byteLength) return capture.head.subarray(0, totalBytes);\n\tif (totalBytes <= capacity) {\n\t\tconst tailFrom = totalBytes - capture.head.byteLength;\n\t\tconst captured = new Uint8Array(totalBytes);\n\t\tcaptured.set(capture.head);\n\t\tcaptured.set(capture.tail.subarray(capture.tailLength - tailFrom, capture.tailLength), capture.headLength);\n\t\treturn captured;\n\t}\n\tconst captured = new Uint8Array(capacity);\n\tcaptured.set(capture.head.subarray(0, capture.headLength));\n\tcaptured.set(capture.tail.subarray(0, capture.tailLength), capture.headLength);\n\treturn captured;\n}\n\nfunction result(\n\trequest: ProcessExecRequestV1,\n\tstatus: ProcessExecResultV1[\"status\"],\n\tstdout: Uint8Array,\n\tstderr: Uint8Array,\n\tstdoutBytes: number,\n\tstderrBytes: number,\n\tpreviewBytes: number,\n\ttermination?: \"timeout\" | \"output_budget_exceeded\" | \"spawn_failed\" | \"exit_nonzero\" | \"cancelled\",\n\tcode?: number,\n\tcwd?: string,\n): ProcessExecResultV1 {\n\t// The byte-level head/tail split is authoritative; sanitize only redacts and\n\t// acts as the absolute-size circuit breaker at the contract cap.\n\tconst stdoutPreview = sanitizeProcessTextV1(\n\t\tbuildHeadTailPreviewV1(stdout, previewBytes, stdoutBytes),\n\t\tPROCESS_EXEC_MAX_PREVIEW_BYTES_V1,\n\t\tcwd,\n\t);\n\tconst stderrPreview = sanitizeProcessTextV1(\n\t\tbuildHeadTailPreviewV1(stderr, previewBytes, stderrBytes),\n\t\tPROCESS_EXEC_MAX_PREVIEW_BYTES_V1,\n\t\tcwd,\n\t);\n\tconst errorCode = termination;\n\tconst error =\n\t\terrorCode === undefined\n\t\t\t? undefined\n\t\t\t: {\n\t\t\t\t\tversion: 1 as const,\n\t\t\t\t\tcode: errorCode,\n\t\t\t\t\tmessage:\n\t\t\t\t\t\terrorCode === \"exit_nonzero\"\n\t\t\t\t\t\t\t? \"Process exited with a non-zero status\"\n\t\t\t\t\t\t\t: errorCode === \"cancelled\"\n\t\t\t\t\t\t\t\t? \"Process execution was cancelled\"\n\t\t\t\t\t\t\t\t: \"Process execution failed\",\n\t\t\t\t};\n\treturn {\n\t\tversion: 1,\n\t\toperationId: request.operationId,\n\t\tstatus,\n\t\tsummary:\n\t\t\tstatus === \"succeeded\"\n\t\t\t\t? \"Process completed successfully\"\n\t\t\t\t: termination === \"cancelled\"\n\t\t\t\t\t? \"Process execution was cancelled\"\n\t\t\t\t\t: termination === \"timeout\"\n\t\t\t\t\t\t? \"Process execution timed out\"\n\t\t\t\t\t\t: termination === \"output_budget_exceeded\"\n\t\t\t\t\t\t\t? \"Process output exceeded the host budget\"\n\t\t\t\t\t\t\t: termination === \"spawn_failed\"\n\t\t\t\t\t\t\t\t? \"Process could not be started\"\n\t\t\t\t\t\t\t\t: \"Process exited with a non-zero status\",\n\t\t...(code === undefined ? {} : { exitCode: code }),\n\t\tstdoutBytes,\n\t\tstderrBytes,\n\t\tstdoutPreview,\n\t\tstderrPreview,\n\t\tstdoutTruncated: stdoutBytes > previewBytes,\n\t\tstderrTruncated: stderrBytes > previewBytes,\n\t\t...(error === undefined ? {} : { error }),\n\t};\n}\n\n/** Node host adapter with shell-free argv execution and bounded output previews. */\nexport function createNodeProcessExecAdapterV1(options: NodeProcessExecAdapterOptionsV1): ProcessExecAdapterV1 {\n\tif (typeof options.cwd !== \"string\" || !isAbsolute(options.cwd)) throw new Error(\"cwd must be an absolute path\");\n\tconst defaultTimeoutMs = positiveBound(\n\t\toptions.defaultTimeoutMs,\n\t\tDEFAULT_TIMEOUT_MS_V1,\n\t\tMAX_TIMEOUT_MS_V1,\n\t\t\"defaultTimeoutMs\",\n\t);\n\tconst maxStdoutBytes = positiveBound(\n\t\toptions.maxStdoutBytes,\n\t\tDEFAULT_OUTPUT_BYTES_V1,\n\t\tPROCESS_EXEC_MAX_OUTPUT_BYTES_V1,\n\t\t\"maxStdoutBytes\",\n\t);\n\tconst maxStderrBytes = positiveBound(\n\t\toptions.maxStderrBytes,\n\t\tDEFAULT_OUTPUT_BYTES_V1,\n\t\tPROCESS_EXEC_MAX_OUTPUT_BYTES_V1,\n\t\t\"maxStderrBytes\",\n\t);\n\tconst previewBytes = positiveBound(\n\t\toptions.previewBytes,\n\t\tDEFAULT_PREVIEW_BYTES_V1,\n\t\tPROCESS_EXEC_MAX_PREVIEW_BYTES_V1,\n\t\t\"previewBytes\",\n\t);\n\tconst env = hostEnvironment(options.env);\n\tconst spawnProcess = options.spawn ?? spawn;\n\tconst terminateTree = options.killProcessTree ?? killProcessTree;\n\n\treturn {\n\t\tadapterId: PROCESS_EXEC_HOST_ADAPTER_ID_V1,\n\t\tadapterVersion: PROCESS_EXEC_HOST_ADAPTER_VERSION_V1,\n\t\trun: (request, signal) => {\n\t\t\tconst operationId =\n\t\t\t\ttypeof request.operationId === \"string\" && request.operationId.trim() !== \"\"\n\t\t\t\t\t? request.operationId\n\t\t\t\t\t: \"invalid-process-operation\";\n\t\t\tconst invalid = validateRequest(request);\n\t\t\tif (invalid !== undefined) return Promise.resolve(inputError(operationId, invalid));\n\t\t\tif (signal?.aborted) return Promise.resolve(cancelled(operationId));\n\t\t\tconst timeoutMs = request.timeoutMs ?? defaultTimeoutMs;\n\t\t\treturn new Promise<ProcessExecResultV1>((resolve) => {\n\t\t\t\tlet child: ReturnType<typeof spawn>;\n\t\t\t\ttry {\n\t\t\t\t\tchild = spawnProcess(request.command, [...request.args], {\n\t\t\t\t\t\tcwd: options.cwd,\n\t\t\t\t\t\t...(env === undefined ? {} : { env }),\n\t\t\t\t\t\tshell: false,\n\t\t\t\t\t\twindowsHide: true,\n\t\t\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\tresolve(\n\t\t\t\t\t\tresult(\n\t\t\t\t\t\t\trequest,\n\t\t\t\t\t\t\t\"failed\",\n\t\t\t\t\t\t\tnew Uint8Array(),\n\t\t\t\t\t\t\tnew Uint8Array(),\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\tpreviewBytes,\n\t\t\t\t\t\t\t\"spawn_failed\",\n\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\toptions.cwd,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlet stdoutBytes = 0;\n\t\t\t\tlet stderrBytes = 0;\n\t\t\t\tconst stdoutCapture = createPreviewCaptureV1(previewBytes);\n\t\t\t\tconst stderrCapture = createPreviewCaptureV1(previewBytes);\n\t\t\t\tlet termination:\n\t\t\t\t\t| \"timeout\"\n\t\t\t\t\t| \"output_budget_exceeded\"\n\t\t\t\t\t| \"spawn_failed\"\n\t\t\t\t\t| \"exit_nonzero\"\n\t\t\t\t\t| \"cancelled\"\n\t\t\t\t\t| undefined;\n\t\t\t\tlet settled = false;\n\t\t\t\tlet terminationRequested = false;\n\t\t\t\tlet treeTerminationAttempted = false;\n\t\t\t\tlet forceKillTimer: ReturnType<typeof setTimeout> | undefined;\n\t\t\t\tlet hardFinishTimer: ReturnType<typeof setTimeout> | undefined;\n\t\t\t\tconst forceTerminate = (): void => {\n\t\t\t\t\tif (treeTerminationAttempted) return;\n\t\t\t\t\ttreeTerminationAttempted = true;\n\t\t\t\t\tif (typeof child.pid === \"number\" && child.pid > 0) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tterminateTree(child.pid);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// The host cleanup boundary is best-effort; close/error still settles the operation.\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (!child.killed) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tchild.kill(\"SIGKILL\");\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// The close/error event remains authoritative.\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tconst armForceTermination = (): void => {\n\t\t\t\t\tif (forceKillTimer !== undefined) return;\n\t\t\t\t\tforceKillTimer = setTimeout(() => {\n\t\t\t\t\t\tforceTerminate();\n\t\t\t\t\t\thardFinishTimer = setTimeout(() => finalize(null), 500);\n\t\t\t\t\t}, 2000);\n\t\t\t\t};\n\t\t\t\tconst terminate = (reason: \"timeout\" | \"output_budget_exceeded\" | \"spawn_failed\" | \"cancelled\"): void => {\n\t\t\t\t\tif (termination === undefined) termination = reason;\n\t\t\t\t\tif (!terminationRequested) {\n\t\t\t\t\t\tterminationRequested = true;\n\t\t\t\t\t\tif (!child.killed) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t// The force cleanup timer still runs.\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tarmForceTermination();\n\t\t\t\t};\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tterminate(\"cancelled\");\n\t\t\t\t};\n\t\t\t\tconst finalize = (code: number | null): void => {\n\t\t\t\t\tif (settled) return;\n\t\t\t\t\tsettled = true;\n\t\t\t\t\tclearTimeout(timeout);\n\t\t\t\t\tif (forceKillTimer !== undefined) clearTimeout(forceKillTimer);\n\t\t\t\t\tif (hardFinishTimer !== undefined) clearTimeout(hardFinishTimer);\n\t\t\t\t\t// Once termination was requested, exactly one process-tree kill must\n\t\t\t\t\t// still be attempted even when the direct child closed before the\n\t\t\t\t\t// force-kill timer fired (grandchildren may outlive it).\n\t\t\t\t\tif (terminationRequested) forceTerminate();\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\tif (signal?.aborted && termination === undefined) termination = \"cancelled\";\n\t\t\t\t\tif (termination === undefined && code !== 0) termination = \"exit_nonzero\";\n\t\t\t\t\tconst status =\n\t\t\t\t\t\ttermination === undefined ? \"succeeded\" : termination === \"cancelled\" ? \"cancelled\" : \"failed\";\n\t\t\t\t\tresolve(\n\t\t\t\t\t\tresult(\n\t\t\t\t\t\t\trequest,\n\t\t\t\t\t\t\tstatus,\n\t\t\t\t\t\t\tpreviewCaptureBytesV1(stdoutCapture, stdoutBytes),\n\t\t\t\t\t\t\tpreviewCaptureBytesV1(stderrCapture, stderrBytes),\n\t\t\t\t\t\t\tstdoutBytes,\n\t\t\t\t\t\t\tstderrBytes,\n\t\t\t\t\t\t\tpreviewBytes,\n\t\t\t\t\t\t\ttermination,\n\t\t\t\t\t\t\tcode ?? undefined,\n\t\t\t\t\t\t\toptions.cwd,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t};\n\t\t\t\tconst timeout = setTimeout(() => terminate(\"timeout\"), timeoutMs);\n\t\t\t\tif (signal !== undefined) signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\tchild.stdout?.on(\"data\", (chunk: Buffer | Uint8Array) => {\n\t\t\t\t\tconst bytes = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);\n\t\t\t\t\tstdoutBytes += bytes.byteLength;\n\t\t\t\t\tcapturePreviewChunkV1(stdoutCapture, bytes);\n\t\t\t\t\tif (stdoutBytes > maxStdoutBytes) terminate(\"output_budget_exceeded\");\n\t\t\t\t});\n\t\t\t\tchild.stderr?.on(\"data\", (chunk: Buffer | Uint8Array) => {\n\t\t\t\t\tconst bytes = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);\n\t\t\t\t\tstderrBytes += bytes.byteLength;\n\t\t\t\t\tcapturePreviewChunkV1(stderrCapture, bytes);\n\t\t\t\t\tif (stderrBytes > maxStderrBytes) terminate(\"output_budget_exceeded\");\n\t\t\t\t});\n\t\t\t\tchild.once(\"error\", () => {\n\t\t\t\t\tterminate(\"spawn_failed\");\n\t\t\t\t});\n\t\t\t\tchild.once(\"close\", (code) => {\n\t\t\t\t\tfinalize(code);\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t};\n}\n"]}
@@ -255,13 +255,14 @@ export function createNodeProcessExecAdapterV1(options) {
255
255
  const stderrCapture = createPreviewCaptureV1(previewBytes);
256
256
  let termination;
257
257
  let settled = false;
258
- let exited = false;
259
258
  let terminationRequested = false;
259
+ let treeTerminationAttempted = false;
260
260
  let forceKillTimer;
261
261
  let hardFinishTimer;
262
262
  const forceTerminate = () => {
263
- if (exited)
263
+ if (treeTerminationAttempted)
264
264
  return;
265
+ treeTerminationAttempted = true;
265
266
  if (typeof child.pid === "number" && child.pid > 0) {
266
267
  try {
267
268
  terminateTree(child.pid);
@@ -310,12 +311,16 @@ export function createNodeProcessExecAdapterV1(options) {
310
311
  if (settled)
311
312
  return;
312
313
  settled = true;
313
- exited = true;
314
314
  clearTimeout(timeout);
315
315
  if (forceKillTimer !== undefined)
316
316
  clearTimeout(forceKillTimer);
317
317
  if (hardFinishTimer !== undefined)
318
318
  clearTimeout(hardFinishTimer);
319
+ // Once termination was requested, exactly one process-tree kill must
320
+ // still be attempted even when the direct child closed before the
321
+ // force-kill timer fired (grandchildren may outlive it).
322
+ if (terminationRequested)
323
+ forceTerminate();
319
324
  signal?.removeEventListener("abort", onAbort);
320
325
  if (signal?.aborted && termination === undefined)
321
326
  termination = "cancelled";
@@ -345,7 +350,6 @@ export function createNodeProcessExecAdapterV1(options) {
345
350
  terminate("spawn_failed");
346
351
  });
347
352
  child.once("close", (code) => {
348
- exited = true;
349
353
  finalize(code);
350
354
  });
351
355
  });
@@ -1 +1 @@
1
- {"version":3,"file":"node-adapter.js","sourceRoot":"","sources":["../src/node-adapter.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { isAbsolute } from \"node:path\";\nimport { killProcessTree, sanitizeBinaryOutput } from \"./shell-utils.ts\";\nimport {\n\tPROCESS_EXEC_HOST_ADAPTER_ID_V1,\n\tPROCESS_EXEC_HOST_ADAPTER_VERSION_V1,\n\ttype ProcessExecAdapterV1,\n\ttype ProcessExecRequestV1,\n\ttype ProcessExecResultV1,\n} from \"./types.ts\";\n\nconst DEFAULT_TIMEOUT_MS_V1 = 30_000;\nconst MAX_TIMEOUT_MS_V1 = 120_000;\nconst DEFAULT_OUTPUT_BYTES_V1 = 64 * 1024;\nexport const PROCESS_EXEC_MAX_OUTPUT_BYTES_V1 = 1024 * 1024;\nconst DEFAULT_PREVIEW_BYTES_V1 = 4096;\nexport const PROCESS_EXEC_MAX_PREVIEW_BYTES_V1 = 8192;\nconst MAX_ARGS_V1 = 256;\nconst MAX_COMMAND_CHARS_V1 = 512;\nconst MAX_ARG_CHARS_V1 = 4096;\nexport const PROCESS_EXEC_MAX_OPERATION_ID_CHARS_V1 = 256;\n\nexport interface NodeProcessExecAdapterOptionsV1 {\n\t/** Host-owned working directory; requests cannot override it. */\n\treadonly cwd: string;\n\treadonly env?: Readonly<Record<string, string>>;\n\treadonly defaultTimeoutMs?: number;\n\treadonly maxStdoutBytes?: number;\n\treadonly maxStderrBytes?: number;\n\treadonly previewBytes?: number;\n\t/** Test seam for the host-owned process-tree terminator. */\n\treadonly killProcessTree?: (pid: number) => void;\n\t/** Deterministic host seam; production defaults to Node's shell-free spawn. */\n\treadonly spawn?: typeof spawn;\n}\n\nconst SAFE_ENVIRONMENT_KEYS_V1 = new Set(\n\t[\"PATH\", \"PATHEXT\", \"SystemRoot\", \"SYSTEMROOT\", \"WINDIR\", \"TEMP\", \"TMP\", \"TMPDIR\", \"LANG\", \"LC_ALL\"].map((key) =>\n\t\tkey.toLowerCase(),\n\t),\n);\n\n/** Splits a preview budget into head/tail segments (75% head, rest tail; head wins on tiny budgets). */\nfunction splitPreviewBudgetV1(budget: number): { head: number; tail: number } {\n\tconst head = Math.ceil((budget * 3) / 4);\n\treturn { head, tail: Math.max(0, budget - head) };\n}\n\nfunction omissionMarkerV1(omitted: number, unit: \"bytes\" | \"chars\"): string {\n\treturn `\\n... [${omitted} ${unit} omitted] ...\\n`;\n}\n\n/**\n * Builds a bounded preview from the retained capture. `captured` holds the\n * complete output when `totalBytes <= previewBytes`; otherwise it holds the\n * first `head` bytes followed by the last `tail` bytes of the stream, and the\n * two segments are joined with an omission marker. The marker rides on top of\n * the segment budgets but the assembled preview never exceeds\n * `PROCESS_EXEC_MAX_PREVIEW_BYTES_V1`.\n */\nexport function buildHeadTailPreviewV1(captured: Uint8Array, previewBytes: number, totalBytes: number): string {\n\tif (totalBytes <= previewBytes) return previewText(captured);\n\tconst budget = splitPreviewBudgetV1(previewBytes);\n\tconst headPart = captured.subarray(0, Math.min(budget.head, captured.byteLength));\n\tlet tailBytes = Math.min(budget.tail, Math.max(0, captured.byteLength - headPart.byteLength));\n\tlet marker = omissionMarkerV1(Math.max(0, totalBytes - headPart.byteLength - tailBytes), \"bytes\");\n\twhile (headPart.byteLength + tailBytes + marker.length > PROCESS_EXEC_MAX_PREVIEW_BYTES_V1 && tailBytes > 0) {\n\t\ttailBytes = Math.max(\n\t\t\t0,\n\t\t\tMath.min(tailBytes, PROCESS_EXEC_MAX_PREVIEW_BYTES_V1 - headPart.byteLength - marker.length),\n\t\t);\n\t\tmarker = omissionMarkerV1(Math.max(0, totalBytes - headPart.byteLength - tailBytes), \"bytes\");\n\t}\n\treturn previewText(headPart) + marker + previewText(captured.subarray(captured.byteLength - tailBytes));\n}\n\n/** Redacts credentials and host paths before a preview can reach the model. */\nexport function sanitizeProcessTextV1(\n\tvalue: string,\n\tmaximumChars = PROCESS_EXEC_MAX_PREVIEW_BYTES_V1,\n\tcwd?: string,\n): string {\n\tlet text = sanitizeBinaryOutput(value);\n\tif (cwd !== undefined && cwd !== \"\") text = text.replaceAll(cwd, \"[redacted-path]\");\n\ttext = text\n\t\t.replace(\n\t\t\t/((?:api[-_ ]?key|access[-_ ]?token|auth(?:orization)?|bearer|password|passwd|secret|token)\\s*[:=]\\s*)([^\\s,;]+)/giu,\n\t\t\t\"$1[redacted-secret]\",\n\t\t)\n\t\t.replace(/(?:[A-Za-z]:[\\\\/]|\\\\\\\\|\\/(?:home|Users|private|tmp|var|workspace)\\/)[^\\s\"'<>]*/gu, \"[redacted-path]\");\n\tif (text.length <= maximumChars) return text;\n\tconst head = Math.ceil((maximumChars * 3) / 4);\n\tlet tail = maximumChars - head;\n\tlet marker = omissionMarkerV1(text.length - head - tail, \"chars\");\n\twhile (head + tail + marker.length > maximumChars && tail > 0) {\n\t\ttail = Math.max(0, Math.min(tail, maximumChars - head - marker.length));\n\t\tmarker = omissionMarkerV1(text.length - head - tail, \"chars\");\n\t}\n\treturn text.slice(0, head) + marker + (tail > 0 ? text.slice(text.length - tail) : \"\");\n}\n\nfunction hostEnvironment(explicit: Readonly<Record<string, string>> | undefined): NodeJS.ProcessEnv {\n\tconst inherited: NodeJS.ProcessEnv = {};\n\tfor (const [key, value] of Object.entries(process.env)) {\n\t\tif (value !== undefined && SAFE_ENVIRONMENT_KEYS_V1.has(key.toLowerCase())) inherited[key] = value;\n\t}\n\treturn { ...inherited, ...(explicit ?? {}) };\n}\n\nfunction positiveBound(value: number | undefined, fallback: number, maximum: number, label: string): number {\n\tconst result = value ?? fallback;\n\tif (!Number.isSafeInteger(result) || result < 1 || result > maximum)\n\t\tthrow new Error(`${label} must be a positive integer at most ${maximum}`);\n\treturn result;\n}\n\nfunction inputError(operationId: string, message: string): ProcessExecResultV1 {\n\treturn {\n\t\tversion: 1,\n\t\toperationId,\n\t\tstatus: \"failed\",\n\t\tsummary: \"Process request was invalid\",\n\t\tstdoutBytes: 0,\n\t\tstderrBytes: 0,\n\t\tstdoutPreview: \"\",\n\t\tstderrPreview: \"\",\n\t\tstdoutTruncated: false,\n\t\tstderrTruncated: false,\n\t\terror: { version: 1, code: \"invalid_input\", message },\n\t};\n}\n\nfunction cancelled(operationId: string): ProcessExecResultV1 {\n\treturn {\n\t\tversion: 1,\n\t\toperationId,\n\t\tstatus: \"cancelled\",\n\t\tsummary: \"Process execution was cancelled\",\n\t\tstdoutBytes: 0,\n\t\tstderrBytes: 0,\n\t\tstdoutPreview: \"\",\n\t\tstderrPreview: \"\",\n\t\tstdoutTruncated: false,\n\t\tstderrTruncated: false,\n\t\terror: { version: 1, code: \"cancelled\", message: \"Process execution was cancelled\" },\n\t};\n}\n\nfunction validateRequest(request: ProcessExecRequestV1): string | undefined {\n\tif (request.version !== 1) return \"version must be 1\";\n\tif (\n\t\ttypeof request.operationId !== \"string\" ||\n\t\trequest.operationId.trim() === \"\" ||\n\t\trequest.operationId.length > PROCESS_EXEC_MAX_OPERATION_ID_CHARS_V1\n\t)\n\t\treturn \"operationId is invalid\";\n\tif (\n\t\ttypeof request.command !== \"string\" ||\n\t\trequest.command.trim() === \"\" ||\n\t\trequest.command.length > MAX_COMMAND_CHARS_V1 ||\n\t\t/[\\u0000\\r\\n]/u.test(request.command)\n\t)\n\t\treturn \"command is invalid\";\n\tif (!Array.isArray(request.args) || request.args.length > MAX_ARGS_V1) return \"args are invalid\";\n\tif (\n\t\trequest.args.some((arg) => typeof arg !== \"string\" || arg.length > MAX_ARG_CHARS_V1 || /[\\u0000\\r\\n]/u.test(arg))\n\t)\n\t\treturn \"args are invalid\";\n\tif (\n\t\trequest.timeoutMs !== undefined &&\n\t\t(!Number.isSafeInteger(request.timeoutMs) || request.timeoutMs < 1 || request.timeoutMs > MAX_TIMEOUT_MS_V1)\n\t)\n\t\treturn `timeoutMs must be a positive integer at most ${MAX_TIMEOUT_MS_V1}`;\n\treturn undefined;\n}\n\nfunction previewText(bytes: Uint8Array): string {\n\treturn new TextDecoder().decode(bytes);\n}\n\ninterface PreviewCaptureV1 {\n\treadonly head: Uint8Array;\n\treadonly tail: Uint8Array;\n\theadLength: number;\n\ttailLength: number;\n}\n\n/** Retains the first head-budget and last tail-budget bytes of a stream for head+tail previews. */\nfunction createPreviewCaptureV1(previewBytes: number): PreviewCaptureV1 {\n\tconst budget = splitPreviewBudgetV1(previewBytes);\n\treturn { head: new Uint8Array(budget.head), tail: new Uint8Array(budget.tail), headLength: 0, tailLength: 0 };\n}\n\nfunction capturePreviewChunkV1(capture: PreviewCaptureV1, chunk: Uint8Array): void {\n\tconst headCopy = Math.min(chunk.byteLength, capture.head.byteLength - capture.headLength);\n\tcapture.head.set(chunk.subarray(0, headCopy), capture.headLength);\n\tcapture.headLength += headCopy;\n\tconst tailSize = capture.tail.byteLength;\n\tif (tailSize === 0) return;\n\tif (chunk.byteLength >= tailSize) {\n\t\tcapture.tail.set(chunk.subarray(chunk.byteLength - tailSize));\n\t\tcapture.tailLength = tailSize;\n\t\treturn;\n\t}\n\tcapture.tail.copyWithin(0, chunk.byteLength);\n\tcapture.tail.set(chunk, tailSize - chunk.byteLength);\n\tcapture.tailLength = Math.min(tailSize, capture.tailLength + chunk.byteLength);\n}\n\n/** Materializes the retained bytes: the full output when it fit the budget, else head followed by tail. */\nfunction previewCaptureBytesV1(capture: PreviewCaptureV1, totalBytes: number): Uint8Array {\n\tconst capacity = capture.head.byteLength + capture.tail.byteLength;\n\tif (totalBytes <= capture.head.byteLength) return capture.head.subarray(0, totalBytes);\n\tif (totalBytes <= capacity) {\n\t\tconst tailFrom = totalBytes - capture.head.byteLength;\n\t\tconst captured = new Uint8Array(totalBytes);\n\t\tcaptured.set(capture.head);\n\t\tcaptured.set(capture.tail.subarray(capture.tailLength - tailFrom, capture.tailLength), capture.headLength);\n\t\treturn captured;\n\t}\n\tconst captured = new Uint8Array(capacity);\n\tcaptured.set(capture.head.subarray(0, capture.headLength));\n\tcaptured.set(capture.tail.subarray(0, capture.tailLength), capture.headLength);\n\treturn captured;\n}\n\nfunction result(\n\trequest: ProcessExecRequestV1,\n\tstatus: ProcessExecResultV1[\"status\"],\n\tstdout: Uint8Array,\n\tstderr: Uint8Array,\n\tstdoutBytes: number,\n\tstderrBytes: number,\n\tpreviewBytes: number,\n\ttermination?: \"timeout\" | \"output_budget_exceeded\" | \"spawn_failed\" | \"exit_nonzero\" | \"cancelled\",\n\tcode?: number,\n\tcwd?: string,\n): ProcessExecResultV1 {\n\t// The byte-level head/tail split is authoritative; sanitize only redacts and\n\t// acts as the absolute-size circuit breaker at the contract cap.\n\tconst stdoutPreview = sanitizeProcessTextV1(\n\t\tbuildHeadTailPreviewV1(stdout, previewBytes, stdoutBytes),\n\t\tPROCESS_EXEC_MAX_PREVIEW_BYTES_V1,\n\t\tcwd,\n\t);\n\tconst stderrPreview = sanitizeProcessTextV1(\n\t\tbuildHeadTailPreviewV1(stderr, previewBytes, stderrBytes),\n\t\tPROCESS_EXEC_MAX_PREVIEW_BYTES_V1,\n\t\tcwd,\n\t);\n\tconst errorCode = termination;\n\tconst error =\n\t\terrorCode === undefined\n\t\t\t? undefined\n\t\t\t: {\n\t\t\t\t\tversion: 1 as const,\n\t\t\t\t\tcode: errorCode,\n\t\t\t\t\tmessage:\n\t\t\t\t\t\terrorCode === \"exit_nonzero\"\n\t\t\t\t\t\t\t? \"Process exited with a non-zero status\"\n\t\t\t\t\t\t\t: errorCode === \"cancelled\"\n\t\t\t\t\t\t\t\t? \"Process execution was cancelled\"\n\t\t\t\t\t\t\t\t: \"Process execution failed\",\n\t\t\t\t};\n\treturn {\n\t\tversion: 1,\n\t\toperationId: request.operationId,\n\t\tstatus,\n\t\tsummary:\n\t\t\tstatus === \"succeeded\"\n\t\t\t\t? \"Process completed successfully\"\n\t\t\t\t: termination === \"cancelled\"\n\t\t\t\t\t? \"Process execution was cancelled\"\n\t\t\t\t\t: termination === \"timeout\"\n\t\t\t\t\t\t? \"Process execution timed out\"\n\t\t\t\t\t\t: termination === \"output_budget_exceeded\"\n\t\t\t\t\t\t\t? \"Process output exceeded the host budget\"\n\t\t\t\t\t\t\t: termination === \"spawn_failed\"\n\t\t\t\t\t\t\t\t? \"Process could not be started\"\n\t\t\t\t\t\t\t\t: \"Process exited with a non-zero status\",\n\t\t...(code === undefined ? {} : { exitCode: code }),\n\t\tstdoutBytes,\n\t\tstderrBytes,\n\t\tstdoutPreview,\n\t\tstderrPreview,\n\t\tstdoutTruncated: stdoutBytes > previewBytes,\n\t\tstderrTruncated: stderrBytes > previewBytes,\n\t\t...(error === undefined ? {} : { error }),\n\t};\n}\n\n/** Node host adapter with shell-free argv execution and bounded output previews. */\nexport function createNodeProcessExecAdapterV1(options: NodeProcessExecAdapterOptionsV1): ProcessExecAdapterV1 {\n\tif (typeof options.cwd !== \"string\" || !isAbsolute(options.cwd)) throw new Error(\"cwd must be an absolute path\");\n\tconst defaultTimeoutMs = positiveBound(\n\t\toptions.defaultTimeoutMs,\n\t\tDEFAULT_TIMEOUT_MS_V1,\n\t\tMAX_TIMEOUT_MS_V1,\n\t\t\"defaultTimeoutMs\",\n\t);\n\tconst maxStdoutBytes = positiveBound(\n\t\toptions.maxStdoutBytes,\n\t\tDEFAULT_OUTPUT_BYTES_V1,\n\t\tPROCESS_EXEC_MAX_OUTPUT_BYTES_V1,\n\t\t\"maxStdoutBytes\",\n\t);\n\tconst maxStderrBytes = positiveBound(\n\t\toptions.maxStderrBytes,\n\t\tDEFAULT_OUTPUT_BYTES_V1,\n\t\tPROCESS_EXEC_MAX_OUTPUT_BYTES_V1,\n\t\t\"maxStderrBytes\",\n\t);\n\tconst previewBytes = positiveBound(\n\t\toptions.previewBytes,\n\t\tDEFAULT_PREVIEW_BYTES_V1,\n\t\tPROCESS_EXEC_MAX_PREVIEW_BYTES_V1,\n\t\t\"previewBytes\",\n\t);\n\tconst env = hostEnvironment(options.env);\n\tconst spawnProcess = options.spawn ?? spawn;\n\tconst terminateTree = options.killProcessTree ?? killProcessTree;\n\n\treturn {\n\t\tadapterId: PROCESS_EXEC_HOST_ADAPTER_ID_V1,\n\t\tadapterVersion: PROCESS_EXEC_HOST_ADAPTER_VERSION_V1,\n\t\trun: (request, signal) => {\n\t\t\tconst operationId =\n\t\t\t\ttypeof request.operationId === \"string\" && request.operationId.trim() !== \"\"\n\t\t\t\t\t? request.operationId\n\t\t\t\t\t: \"invalid-process-operation\";\n\t\t\tconst invalid = validateRequest(request);\n\t\t\tif (invalid !== undefined) return Promise.resolve(inputError(operationId, invalid));\n\t\t\tif (signal?.aborted) return Promise.resolve(cancelled(operationId));\n\t\t\tconst timeoutMs = request.timeoutMs ?? defaultTimeoutMs;\n\t\t\treturn new Promise<ProcessExecResultV1>((resolve) => {\n\t\t\t\tlet child: ReturnType<typeof spawn>;\n\t\t\t\ttry {\n\t\t\t\t\tchild = spawnProcess(request.command, [...request.args], {\n\t\t\t\t\t\tcwd: options.cwd,\n\t\t\t\t\t\t...(env === undefined ? {} : { env }),\n\t\t\t\t\t\tshell: false,\n\t\t\t\t\t\twindowsHide: true,\n\t\t\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\tresolve(\n\t\t\t\t\t\tresult(\n\t\t\t\t\t\t\trequest,\n\t\t\t\t\t\t\t\"failed\",\n\t\t\t\t\t\t\tnew Uint8Array(),\n\t\t\t\t\t\t\tnew Uint8Array(),\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\tpreviewBytes,\n\t\t\t\t\t\t\t\"spawn_failed\",\n\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\toptions.cwd,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlet stdoutBytes = 0;\n\t\t\t\tlet stderrBytes = 0;\n\t\t\t\tconst stdoutCapture = createPreviewCaptureV1(previewBytes);\n\t\t\t\tconst stderrCapture = createPreviewCaptureV1(previewBytes);\n\t\t\t\tlet termination:\n\t\t\t\t\t| \"timeout\"\n\t\t\t\t\t| \"output_budget_exceeded\"\n\t\t\t\t\t| \"spawn_failed\"\n\t\t\t\t\t| \"exit_nonzero\"\n\t\t\t\t\t| \"cancelled\"\n\t\t\t\t\t| undefined;\n\t\t\t\tlet settled = false;\n\t\t\t\tlet exited = false;\n\t\t\t\tlet terminationRequested = false;\n\t\t\t\tlet forceKillTimer: ReturnType<typeof setTimeout> | undefined;\n\t\t\t\tlet hardFinishTimer: ReturnType<typeof setTimeout> | undefined;\n\t\t\t\tconst forceTerminate = (): void => {\n\t\t\t\t\tif (exited) return;\n\t\t\t\t\tif (typeof child.pid === \"number\" && child.pid > 0) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tterminateTree(child.pid);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// The host cleanup boundary is best-effort; close/error still settles the operation.\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (!child.killed) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tchild.kill(\"SIGKILL\");\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// The close/error event remains authoritative.\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tconst armForceTermination = (): void => {\n\t\t\t\t\tif (forceKillTimer !== undefined) return;\n\t\t\t\t\tforceKillTimer = setTimeout(() => {\n\t\t\t\t\t\tforceTerminate();\n\t\t\t\t\t\thardFinishTimer = setTimeout(() => finalize(null), 500);\n\t\t\t\t\t}, 2000);\n\t\t\t\t};\n\t\t\t\tconst terminate = (reason: \"timeout\" | \"output_budget_exceeded\" | \"spawn_failed\" | \"cancelled\"): void => {\n\t\t\t\t\tif (termination === undefined) termination = reason;\n\t\t\t\t\tif (!terminationRequested) {\n\t\t\t\t\t\tterminationRequested = true;\n\t\t\t\t\t\tif (!child.killed) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t// The force cleanup timer still runs.\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tarmForceTermination();\n\t\t\t\t};\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tterminate(\"cancelled\");\n\t\t\t\t};\n\t\t\t\tconst finalize = (code: number | null): void => {\n\t\t\t\t\tif (settled) return;\n\t\t\t\t\tsettled = true;\n\t\t\t\t\texited = true;\n\t\t\t\t\tclearTimeout(timeout);\n\t\t\t\t\tif (forceKillTimer !== undefined) clearTimeout(forceKillTimer);\n\t\t\t\t\tif (hardFinishTimer !== undefined) clearTimeout(hardFinishTimer);\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\tif (signal?.aborted && termination === undefined) termination = \"cancelled\";\n\t\t\t\t\tif (termination === undefined && code !== 0) termination = \"exit_nonzero\";\n\t\t\t\t\tconst status =\n\t\t\t\t\t\ttermination === undefined ? \"succeeded\" : termination === \"cancelled\" ? \"cancelled\" : \"failed\";\n\t\t\t\t\tresolve(\n\t\t\t\t\t\tresult(\n\t\t\t\t\t\t\trequest,\n\t\t\t\t\t\t\tstatus,\n\t\t\t\t\t\t\tpreviewCaptureBytesV1(stdoutCapture, stdoutBytes),\n\t\t\t\t\t\t\tpreviewCaptureBytesV1(stderrCapture, stderrBytes),\n\t\t\t\t\t\t\tstdoutBytes,\n\t\t\t\t\t\t\tstderrBytes,\n\t\t\t\t\t\t\tpreviewBytes,\n\t\t\t\t\t\t\ttermination,\n\t\t\t\t\t\t\tcode ?? undefined,\n\t\t\t\t\t\t\toptions.cwd,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t};\n\t\t\t\tconst timeout = setTimeout(() => terminate(\"timeout\"), timeoutMs);\n\t\t\t\tif (signal !== undefined) signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\tchild.stdout?.on(\"data\", (chunk: Buffer | Uint8Array) => {\n\t\t\t\t\tconst bytes = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);\n\t\t\t\t\tstdoutBytes += bytes.byteLength;\n\t\t\t\t\tcapturePreviewChunkV1(stdoutCapture, bytes);\n\t\t\t\t\tif (stdoutBytes > maxStdoutBytes) terminate(\"output_budget_exceeded\");\n\t\t\t\t});\n\t\t\t\tchild.stderr?.on(\"data\", (chunk: Buffer | Uint8Array) => {\n\t\t\t\t\tconst bytes = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);\n\t\t\t\t\tstderrBytes += bytes.byteLength;\n\t\t\t\t\tcapturePreviewChunkV1(stderrCapture, bytes);\n\t\t\t\t\tif (stderrBytes > maxStderrBytes) terminate(\"output_budget_exceeded\");\n\t\t\t\t});\n\t\t\t\tchild.once(\"error\", () => {\n\t\t\t\t\tterminate(\"spawn_failed\");\n\t\t\t\t});\n\t\t\t\tchild.once(\"close\", (code) => {\n\t\t\t\t\texited = true;\n\t\t\t\t\tfinalize(code);\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"node-adapter.js","sourceRoot":"","sources":["../src/node-adapter.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { isAbsolute } from \"node:path\";\nimport { killProcessTree, sanitizeBinaryOutput } from \"./shell-utils.ts\";\nimport {\n\tPROCESS_EXEC_HOST_ADAPTER_ID_V1,\n\tPROCESS_EXEC_HOST_ADAPTER_VERSION_V1,\n\ttype ProcessExecAdapterV1,\n\ttype ProcessExecRequestV1,\n\ttype ProcessExecResultV1,\n} from \"./types.ts\";\n\nconst DEFAULT_TIMEOUT_MS_V1 = 30_000;\nconst MAX_TIMEOUT_MS_V1 = 120_000;\nconst DEFAULT_OUTPUT_BYTES_V1 = 64 * 1024;\nexport const PROCESS_EXEC_MAX_OUTPUT_BYTES_V1 = 1024 * 1024;\nconst DEFAULT_PREVIEW_BYTES_V1 = 4096;\nexport const PROCESS_EXEC_MAX_PREVIEW_BYTES_V1 = 8192;\nconst MAX_ARGS_V1 = 256;\nconst MAX_COMMAND_CHARS_V1 = 512;\nconst MAX_ARG_CHARS_V1 = 4096;\nexport const PROCESS_EXEC_MAX_OPERATION_ID_CHARS_V1 = 256;\n\nexport interface NodeProcessExecAdapterOptionsV1 {\n\t/** Host-owned working directory; requests cannot override it. */\n\treadonly cwd: string;\n\treadonly env?: Readonly<Record<string, string>>;\n\treadonly defaultTimeoutMs?: number;\n\treadonly maxStdoutBytes?: number;\n\treadonly maxStderrBytes?: number;\n\treadonly previewBytes?: number;\n\t/** Test seam for the host-owned process-tree terminator. */\n\treadonly killProcessTree?: (pid: number) => void;\n\t/** Deterministic host seam; production defaults to Node's shell-free spawn. */\n\treadonly spawn?: typeof spawn;\n}\n\nconst SAFE_ENVIRONMENT_KEYS_V1 = new Set(\n\t[\"PATH\", \"PATHEXT\", \"SystemRoot\", \"SYSTEMROOT\", \"WINDIR\", \"TEMP\", \"TMP\", \"TMPDIR\", \"LANG\", \"LC_ALL\"].map((key) =>\n\t\tkey.toLowerCase(),\n\t),\n);\n\n/** Splits a preview budget into head/tail segments (75% head, rest tail; head wins on tiny budgets). */\nfunction splitPreviewBudgetV1(budget: number): { head: number; tail: number } {\n\tconst head = Math.ceil((budget * 3) / 4);\n\treturn { head, tail: Math.max(0, budget - head) };\n}\n\nfunction omissionMarkerV1(omitted: number, unit: \"bytes\" | \"chars\"): string {\n\treturn `\\n... [${omitted} ${unit} omitted] ...\\n`;\n}\n\n/**\n * Builds a bounded preview from the retained capture. `captured` holds the\n * complete output when `totalBytes <= previewBytes`; otherwise it holds the\n * first `head` bytes followed by the last `tail` bytes of the stream, and the\n * two segments are joined with an omission marker. The marker rides on top of\n * the segment budgets but the assembled preview never exceeds\n * `PROCESS_EXEC_MAX_PREVIEW_BYTES_V1`.\n */\nexport function buildHeadTailPreviewV1(captured: Uint8Array, previewBytes: number, totalBytes: number): string {\n\tif (totalBytes <= previewBytes) return previewText(captured);\n\tconst budget = splitPreviewBudgetV1(previewBytes);\n\tconst headPart = captured.subarray(0, Math.min(budget.head, captured.byteLength));\n\tlet tailBytes = Math.min(budget.tail, Math.max(0, captured.byteLength - headPart.byteLength));\n\tlet marker = omissionMarkerV1(Math.max(0, totalBytes - headPart.byteLength - tailBytes), \"bytes\");\n\twhile (headPart.byteLength + tailBytes + marker.length > PROCESS_EXEC_MAX_PREVIEW_BYTES_V1 && tailBytes > 0) {\n\t\ttailBytes = Math.max(\n\t\t\t0,\n\t\t\tMath.min(tailBytes, PROCESS_EXEC_MAX_PREVIEW_BYTES_V1 - headPart.byteLength - marker.length),\n\t\t);\n\t\tmarker = omissionMarkerV1(Math.max(0, totalBytes - headPart.byteLength - tailBytes), \"bytes\");\n\t}\n\treturn previewText(headPart) + marker + previewText(captured.subarray(captured.byteLength - tailBytes));\n}\n\n/** Redacts credentials and host paths before a preview can reach the model. */\nexport function sanitizeProcessTextV1(\n\tvalue: string,\n\tmaximumChars = PROCESS_EXEC_MAX_PREVIEW_BYTES_V1,\n\tcwd?: string,\n): string {\n\tlet text = sanitizeBinaryOutput(value);\n\tif (cwd !== undefined && cwd !== \"\") text = text.replaceAll(cwd, \"[redacted-path]\");\n\ttext = text\n\t\t.replace(\n\t\t\t/((?:api[-_ ]?key|access[-_ ]?token|auth(?:orization)?|bearer|password|passwd|secret|token)\\s*[:=]\\s*)([^\\s,;]+)/giu,\n\t\t\t\"$1[redacted-secret]\",\n\t\t)\n\t\t.replace(/(?:[A-Za-z]:[\\\\/]|\\\\\\\\|\\/(?:home|Users|private|tmp|var|workspace)\\/)[^\\s\"'<>]*/gu, \"[redacted-path]\");\n\tif (text.length <= maximumChars) return text;\n\tconst head = Math.ceil((maximumChars * 3) / 4);\n\tlet tail = maximumChars - head;\n\tlet marker = omissionMarkerV1(text.length - head - tail, \"chars\");\n\twhile (head + tail + marker.length > maximumChars && tail > 0) {\n\t\ttail = Math.max(0, Math.min(tail, maximumChars - head - marker.length));\n\t\tmarker = omissionMarkerV1(text.length - head - tail, \"chars\");\n\t}\n\treturn text.slice(0, head) + marker + (tail > 0 ? text.slice(text.length - tail) : \"\");\n}\n\nfunction hostEnvironment(explicit: Readonly<Record<string, string>> | undefined): NodeJS.ProcessEnv {\n\tconst inherited: NodeJS.ProcessEnv = {};\n\tfor (const [key, value] of Object.entries(process.env)) {\n\t\tif (value !== undefined && SAFE_ENVIRONMENT_KEYS_V1.has(key.toLowerCase())) inherited[key] = value;\n\t}\n\treturn { ...inherited, ...(explicit ?? {}) };\n}\n\nfunction positiveBound(value: number | undefined, fallback: number, maximum: number, label: string): number {\n\tconst result = value ?? fallback;\n\tif (!Number.isSafeInteger(result) || result < 1 || result > maximum)\n\t\tthrow new Error(`${label} must be a positive integer at most ${maximum}`);\n\treturn result;\n}\n\nfunction inputError(operationId: string, message: string): ProcessExecResultV1 {\n\treturn {\n\t\tversion: 1,\n\t\toperationId,\n\t\tstatus: \"failed\",\n\t\tsummary: \"Process request was invalid\",\n\t\tstdoutBytes: 0,\n\t\tstderrBytes: 0,\n\t\tstdoutPreview: \"\",\n\t\tstderrPreview: \"\",\n\t\tstdoutTruncated: false,\n\t\tstderrTruncated: false,\n\t\terror: { version: 1, code: \"invalid_input\", message },\n\t};\n}\n\nfunction cancelled(operationId: string): ProcessExecResultV1 {\n\treturn {\n\t\tversion: 1,\n\t\toperationId,\n\t\tstatus: \"cancelled\",\n\t\tsummary: \"Process execution was cancelled\",\n\t\tstdoutBytes: 0,\n\t\tstderrBytes: 0,\n\t\tstdoutPreview: \"\",\n\t\tstderrPreview: \"\",\n\t\tstdoutTruncated: false,\n\t\tstderrTruncated: false,\n\t\terror: { version: 1, code: \"cancelled\", message: \"Process execution was cancelled\" },\n\t};\n}\n\nfunction validateRequest(request: ProcessExecRequestV1): string | undefined {\n\tif (request.version !== 1) return \"version must be 1\";\n\tif (\n\t\ttypeof request.operationId !== \"string\" ||\n\t\trequest.operationId.trim() === \"\" ||\n\t\trequest.operationId.length > PROCESS_EXEC_MAX_OPERATION_ID_CHARS_V1\n\t)\n\t\treturn \"operationId is invalid\";\n\tif (\n\t\ttypeof request.command !== \"string\" ||\n\t\trequest.command.trim() === \"\" ||\n\t\trequest.command.length > MAX_COMMAND_CHARS_V1 ||\n\t\t/[\\u0000\\r\\n]/u.test(request.command)\n\t)\n\t\treturn \"command is invalid\";\n\tif (!Array.isArray(request.args) || request.args.length > MAX_ARGS_V1) return \"args are invalid\";\n\tif (\n\t\trequest.args.some((arg) => typeof arg !== \"string\" || arg.length > MAX_ARG_CHARS_V1 || /[\\u0000\\r\\n]/u.test(arg))\n\t)\n\t\treturn \"args are invalid\";\n\tif (\n\t\trequest.timeoutMs !== undefined &&\n\t\t(!Number.isSafeInteger(request.timeoutMs) || request.timeoutMs < 1 || request.timeoutMs > MAX_TIMEOUT_MS_V1)\n\t)\n\t\treturn `timeoutMs must be a positive integer at most ${MAX_TIMEOUT_MS_V1}`;\n\treturn undefined;\n}\n\nfunction previewText(bytes: Uint8Array): string {\n\treturn new TextDecoder().decode(bytes);\n}\n\ninterface PreviewCaptureV1 {\n\treadonly head: Uint8Array;\n\treadonly tail: Uint8Array;\n\theadLength: number;\n\ttailLength: number;\n}\n\n/** Retains the first head-budget and last tail-budget bytes of a stream for head+tail previews. */\nfunction createPreviewCaptureV1(previewBytes: number): PreviewCaptureV1 {\n\tconst budget = splitPreviewBudgetV1(previewBytes);\n\treturn { head: new Uint8Array(budget.head), tail: new Uint8Array(budget.tail), headLength: 0, tailLength: 0 };\n}\n\nfunction capturePreviewChunkV1(capture: PreviewCaptureV1, chunk: Uint8Array): void {\n\tconst headCopy = Math.min(chunk.byteLength, capture.head.byteLength - capture.headLength);\n\tcapture.head.set(chunk.subarray(0, headCopy), capture.headLength);\n\tcapture.headLength += headCopy;\n\tconst tailSize = capture.tail.byteLength;\n\tif (tailSize === 0) return;\n\tif (chunk.byteLength >= tailSize) {\n\t\tcapture.tail.set(chunk.subarray(chunk.byteLength - tailSize));\n\t\tcapture.tailLength = tailSize;\n\t\treturn;\n\t}\n\tcapture.tail.copyWithin(0, chunk.byteLength);\n\tcapture.tail.set(chunk, tailSize - chunk.byteLength);\n\tcapture.tailLength = Math.min(tailSize, capture.tailLength + chunk.byteLength);\n}\n\n/** Materializes the retained bytes: the full output when it fit the budget, else head followed by tail. */\nfunction previewCaptureBytesV1(capture: PreviewCaptureV1, totalBytes: number): Uint8Array {\n\tconst capacity = capture.head.byteLength + capture.tail.byteLength;\n\tif (totalBytes <= capture.head.byteLength) return capture.head.subarray(0, totalBytes);\n\tif (totalBytes <= capacity) {\n\t\tconst tailFrom = totalBytes - capture.head.byteLength;\n\t\tconst captured = new Uint8Array(totalBytes);\n\t\tcaptured.set(capture.head);\n\t\tcaptured.set(capture.tail.subarray(capture.tailLength - tailFrom, capture.tailLength), capture.headLength);\n\t\treturn captured;\n\t}\n\tconst captured = new Uint8Array(capacity);\n\tcaptured.set(capture.head.subarray(0, capture.headLength));\n\tcaptured.set(capture.tail.subarray(0, capture.tailLength), capture.headLength);\n\treturn captured;\n}\n\nfunction result(\n\trequest: ProcessExecRequestV1,\n\tstatus: ProcessExecResultV1[\"status\"],\n\tstdout: Uint8Array,\n\tstderr: Uint8Array,\n\tstdoutBytes: number,\n\tstderrBytes: number,\n\tpreviewBytes: number,\n\ttermination?: \"timeout\" | \"output_budget_exceeded\" | \"spawn_failed\" | \"exit_nonzero\" | \"cancelled\",\n\tcode?: number,\n\tcwd?: string,\n): ProcessExecResultV1 {\n\t// The byte-level head/tail split is authoritative; sanitize only redacts and\n\t// acts as the absolute-size circuit breaker at the contract cap.\n\tconst stdoutPreview = sanitizeProcessTextV1(\n\t\tbuildHeadTailPreviewV1(stdout, previewBytes, stdoutBytes),\n\t\tPROCESS_EXEC_MAX_PREVIEW_BYTES_V1,\n\t\tcwd,\n\t);\n\tconst stderrPreview = sanitizeProcessTextV1(\n\t\tbuildHeadTailPreviewV1(stderr, previewBytes, stderrBytes),\n\t\tPROCESS_EXEC_MAX_PREVIEW_BYTES_V1,\n\t\tcwd,\n\t);\n\tconst errorCode = termination;\n\tconst error =\n\t\terrorCode === undefined\n\t\t\t? undefined\n\t\t\t: {\n\t\t\t\t\tversion: 1 as const,\n\t\t\t\t\tcode: errorCode,\n\t\t\t\t\tmessage:\n\t\t\t\t\t\terrorCode === \"exit_nonzero\"\n\t\t\t\t\t\t\t? \"Process exited with a non-zero status\"\n\t\t\t\t\t\t\t: errorCode === \"cancelled\"\n\t\t\t\t\t\t\t\t? \"Process execution was cancelled\"\n\t\t\t\t\t\t\t\t: \"Process execution failed\",\n\t\t\t\t};\n\treturn {\n\t\tversion: 1,\n\t\toperationId: request.operationId,\n\t\tstatus,\n\t\tsummary:\n\t\t\tstatus === \"succeeded\"\n\t\t\t\t? \"Process completed successfully\"\n\t\t\t\t: termination === \"cancelled\"\n\t\t\t\t\t? \"Process execution was cancelled\"\n\t\t\t\t\t: termination === \"timeout\"\n\t\t\t\t\t\t? \"Process execution timed out\"\n\t\t\t\t\t\t: termination === \"output_budget_exceeded\"\n\t\t\t\t\t\t\t? \"Process output exceeded the host budget\"\n\t\t\t\t\t\t\t: termination === \"spawn_failed\"\n\t\t\t\t\t\t\t\t? \"Process could not be started\"\n\t\t\t\t\t\t\t\t: \"Process exited with a non-zero status\",\n\t\t...(code === undefined ? {} : { exitCode: code }),\n\t\tstdoutBytes,\n\t\tstderrBytes,\n\t\tstdoutPreview,\n\t\tstderrPreview,\n\t\tstdoutTruncated: stdoutBytes > previewBytes,\n\t\tstderrTruncated: stderrBytes > previewBytes,\n\t\t...(error === undefined ? {} : { error }),\n\t};\n}\n\n/** Node host adapter with shell-free argv execution and bounded output previews. */\nexport function createNodeProcessExecAdapterV1(options: NodeProcessExecAdapterOptionsV1): ProcessExecAdapterV1 {\n\tif (typeof options.cwd !== \"string\" || !isAbsolute(options.cwd)) throw new Error(\"cwd must be an absolute path\");\n\tconst defaultTimeoutMs = positiveBound(\n\t\toptions.defaultTimeoutMs,\n\t\tDEFAULT_TIMEOUT_MS_V1,\n\t\tMAX_TIMEOUT_MS_V1,\n\t\t\"defaultTimeoutMs\",\n\t);\n\tconst maxStdoutBytes = positiveBound(\n\t\toptions.maxStdoutBytes,\n\t\tDEFAULT_OUTPUT_BYTES_V1,\n\t\tPROCESS_EXEC_MAX_OUTPUT_BYTES_V1,\n\t\t\"maxStdoutBytes\",\n\t);\n\tconst maxStderrBytes = positiveBound(\n\t\toptions.maxStderrBytes,\n\t\tDEFAULT_OUTPUT_BYTES_V1,\n\t\tPROCESS_EXEC_MAX_OUTPUT_BYTES_V1,\n\t\t\"maxStderrBytes\",\n\t);\n\tconst previewBytes = positiveBound(\n\t\toptions.previewBytes,\n\t\tDEFAULT_PREVIEW_BYTES_V1,\n\t\tPROCESS_EXEC_MAX_PREVIEW_BYTES_V1,\n\t\t\"previewBytes\",\n\t);\n\tconst env = hostEnvironment(options.env);\n\tconst spawnProcess = options.spawn ?? spawn;\n\tconst terminateTree = options.killProcessTree ?? killProcessTree;\n\n\treturn {\n\t\tadapterId: PROCESS_EXEC_HOST_ADAPTER_ID_V1,\n\t\tadapterVersion: PROCESS_EXEC_HOST_ADAPTER_VERSION_V1,\n\t\trun: (request, signal) => {\n\t\t\tconst operationId =\n\t\t\t\ttypeof request.operationId === \"string\" && request.operationId.trim() !== \"\"\n\t\t\t\t\t? request.operationId\n\t\t\t\t\t: \"invalid-process-operation\";\n\t\t\tconst invalid = validateRequest(request);\n\t\t\tif (invalid !== undefined) return Promise.resolve(inputError(operationId, invalid));\n\t\t\tif (signal?.aborted) return Promise.resolve(cancelled(operationId));\n\t\t\tconst timeoutMs = request.timeoutMs ?? defaultTimeoutMs;\n\t\t\treturn new Promise<ProcessExecResultV1>((resolve) => {\n\t\t\t\tlet child: ReturnType<typeof spawn>;\n\t\t\t\ttry {\n\t\t\t\t\tchild = spawnProcess(request.command, [...request.args], {\n\t\t\t\t\t\tcwd: options.cwd,\n\t\t\t\t\t\t...(env === undefined ? {} : { env }),\n\t\t\t\t\t\tshell: false,\n\t\t\t\t\t\twindowsHide: true,\n\t\t\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\tresolve(\n\t\t\t\t\t\tresult(\n\t\t\t\t\t\t\trequest,\n\t\t\t\t\t\t\t\"failed\",\n\t\t\t\t\t\t\tnew Uint8Array(),\n\t\t\t\t\t\t\tnew Uint8Array(),\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\tpreviewBytes,\n\t\t\t\t\t\t\t\"spawn_failed\",\n\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\toptions.cwd,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlet stdoutBytes = 0;\n\t\t\t\tlet stderrBytes = 0;\n\t\t\t\tconst stdoutCapture = createPreviewCaptureV1(previewBytes);\n\t\t\t\tconst stderrCapture = createPreviewCaptureV1(previewBytes);\n\t\t\t\tlet termination:\n\t\t\t\t\t| \"timeout\"\n\t\t\t\t\t| \"output_budget_exceeded\"\n\t\t\t\t\t| \"spawn_failed\"\n\t\t\t\t\t| \"exit_nonzero\"\n\t\t\t\t\t| \"cancelled\"\n\t\t\t\t\t| undefined;\n\t\t\t\tlet settled = false;\n\t\t\t\tlet terminationRequested = false;\n\t\t\t\tlet treeTerminationAttempted = false;\n\t\t\t\tlet forceKillTimer: ReturnType<typeof setTimeout> | undefined;\n\t\t\t\tlet hardFinishTimer: ReturnType<typeof setTimeout> | undefined;\n\t\t\t\tconst forceTerminate = (): void => {\n\t\t\t\t\tif (treeTerminationAttempted) return;\n\t\t\t\t\ttreeTerminationAttempted = true;\n\t\t\t\t\tif (typeof child.pid === \"number\" && child.pid > 0) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tterminateTree(child.pid);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// The host cleanup boundary is best-effort; close/error still settles the operation.\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (!child.killed) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tchild.kill(\"SIGKILL\");\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// The close/error event remains authoritative.\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tconst armForceTermination = (): void => {\n\t\t\t\t\tif (forceKillTimer !== undefined) return;\n\t\t\t\t\tforceKillTimer = setTimeout(() => {\n\t\t\t\t\t\tforceTerminate();\n\t\t\t\t\t\thardFinishTimer = setTimeout(() => finalize(null), 500);\n\t\t\t\t\t}, 2000);\n\t\t\t\t};\n\t\t\t\tconst terminate = (reason: \"timeout\" | \"output_budget_exceeded\" | \"spawn_failed\" | \"cancelled\"): void => {\n\t\t\t\t\tif (termination === undefined) termination = reason;\n\t\t\t\t\tif (!terminationRequested) {\n\t\t\t\t\t\tterminationRequested = true;\n\t\t\t\t\t\tif (!child.killed) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t// The force cleanup timer still runs.\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tarmForceTermination();\n\t\t\t\t};\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tterminate(\"cancelled\");\n\t\t\t\t};\n\t\t\t\tconst finalize = (code: number | null): void => {\n\t\t\t\t\tif (settled) return;\n\t\t\t\t\tsettled = true;\n\t\t\t\t\tclearTimeout(timeout);\n\t\t\t\t\tif (forceKillTimer !== undefined) clearTimeout(forceKillTimer);\n\t\t\t\t\tif (hardFinishTimer !== undefined) clearTimeout(hardFinishTimer);\n\t\t\t\t\t// Once termination was requested, exactly one process-tree kill must\n\t\t\t\t\t// still be attempted even when the direct child closed before the\n\t\t\t\t\t// force-kill timer fired (grandchildren may outlive it).\n\t\t\t\t\tif (terminationRequested) forceTerminate();\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\tif (signal?.aborted && termination === undefined) termination = \"cancelled\";\n\t\t\t\t\tif (termination === undefined && code !== 0) termination = \"exit_nonzero\";\n\t\t\t\t\tconst status =\n\t\t\t\t\t\ttermination === undefined ? \"succeeded\" : termination === \"cancelled\" ? \"cancelled\" : \"failed\";\n\t\t\t\t\tresolve(\n\t\t\t\t\t\tresult(\n\t\t\t\t\t\t\trequest,\n\t\t\t\t\t\t\tstatus,\n\t\t\t\t\t\t\tpreviewCaptureBytesV1(stdoutCapture, stdoutBytes),\n\t\t\t\t\t\t\tpreviewCaptureBytesV1(stderrCapture, stderrBytes),\n\t\t\t\t\t\t\tstdoutBytes,\n\t\t\t\t\t\t\tstderrBytes,\n\t\t\t\t\t\t\tpreviewBytes,\n\t\t\t\t\t\t\ttermination,\n\t\t\t\t\t\t\tcode ?? undefined,\n\t\t\t\t\t\t\toptions.cwd,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t};\n\t\t\t\tconst timeout = setTimeout(() => terminate(\"timeout\"), timeoutMs);\n\t\t\t\tif (signal !== undefined) signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\tchild.stdout?.on(\"data\", (chunk: Buffer | Uint8Array) => {\n\t\t\t\t\tconst bytes = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);\n\t\t\t\t\tstdoutBytes += bytes.byteLength;\n\t\t\t\t\tcapturePreviewChunkV1(stdoutCapture, bytes);\n\t\t\t\t\tif (stdoutBytes > maxStdoutBytes) terminate(\"output_budget_exceeded\");\n\t\t\t\t});\n\t\t\t\tchild.stderr?.on(\"data\", (chunk: Buffer | Uint8Array) => {\n\t\t\t\t\tconst bytes = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);\n\t\t\t\t\tstderrBytes += bytes.byteLength;\n\t\t\t\t\tcapturePreviewChunkV1(stderrCapture, bytes);\n\t\t\t\t\tif (stderrBytes > maxStderrBytes) terminate(\"output_budget_exceeded\");\n\t\t\t\t});\n\t\t\t\tchild.once(\"error\", () => {\n\t\t\t\t\tterminate(\"spawn_failed\");\n\t\t\t\t});\n\t\t\t\tchild.once(\"close\", (code) => {\n\t\t\t\t\tfinalize(code);\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t};\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-agent-forge/plugin-process-exec",
3
- "version": "0.88.4",
3
+ "version": "0.88.6",
4
4
  "description": "Process execution capability plugin for Agent Forge (full-control process_exec tool)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "typebox": "1.3.27",
48
- "@ai-agent-forge/plugin-sdk": "^0.88.4"
48
+ "@ai-agent-forge/plugin-sdk": "^0.88.6"
49
49
  },
50
50
  "devDependencies": {
51
51
  "shx": "0.4.0",
package/plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "agent-forge.plugin.process-exec",
3
- "version": "0.88.4",
3
+ "version": "0.88.6",
4
4
  "apiVersion": "1-draft",
5
5
  "entry": "./dist/entry.js",
6
6
  "hostVersion": ">=0.84.4",