



























@@ -236,6 +236,8 @@ export type SpawnResult = {
236236pid?: number;
237237stdout: string;
238238stderr: string;
239+stdoutTruncatedBytes?: number;
240+stderrTruncatedBytes?: number;
239241code: number | null;
240242signal: NodeJS.Signals | null;
241243killed: boolean;
@@ -252,10 +254,55 @@ export type CommandOptions = {
252254windowsVerbatimArguments?: boolean;
253255noOutputTimeoutMs?: number;
254256signal?: AbortSignal;
257+maxOutputBytes?: number;
255258};
256259257260const WINDOWS_CLOSE_STATE_SETTLE_TIMEOUT_MS = 250;
258261const WINDOWS_CLOSE_STATE_POLL_MS = 10;
262+const DEFAULT_COMMAND_OUTPUT_MAX_BYTES = 16 * 1024 * 1024;
263+264+type CapturedOutputBuffers = {
265+chunks: Buffer[];
266+bytes: number;
267+truncatedBytes: number;
268+};
269+270+function normalizeMaxOutputBytes(value: number | undefined): number {
271+if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
272+return DEFAULT_COMMAND_OUTPUT_MAX_BYTES;
273+}
274+return Math.max(1, Math.floor(value));
275+}
276+277+function appendCapturedOutput(
278+capture: CapturedOutputBuffers,
279+chunk: Buffer | string,
280+maxBytes: number,
281+): void {
282+const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
283+if (buffer.byteLength >= maxBytes) {
284+capture.chunks = [Buffer.from(buffer.subarray(buffer.byteLength - maxBytes))];
285+capture.truncatedBytes += capture.bytes + buffer.byteLength - maxBytes;
286+capture.bytes = maxBytes;
287+return;
288+}
289+290+capture.chunks.push(buffer);
291+capture.bytes += buffer.byteLength;
292+while (capture.bytes > maxBytes && capture.chunks.length > 0) {
293+const first = capture.chunks[0];
294+const overflow = capture.bytes - maxBytes;
295+if (first.byteLength <= overflow) {
296+capture.chunks.shift();
297+capture.bytes -= first.byteLength;
298+capture.truncatedBytes += first.byteLength;
299+} else {
300+capture.chunks[0] = Buffer.from(first.subarray(overflow));
301+capture.bytes -= overflow;
302+capture.truncatedBytes += overflow;
303+}
304+}
305+}
259306260307export function resolveProcessExitCode(params: {
261308explicitCode: number | null | undefined;
@@ -353,8 +400,9 @@ export async function runCommandWithTimeout(
353400});
354401// Spawn with inherited stdin (TTY) so interactive tools stay usable when needed.
355402return await new Promise((resolve, reject) => {
356-const stdoutChunks: Buffer[] = [];
357-const stderrChunks: Buffer[] = [];
403+const stdoutCapture: CapturedOutputBuffers = { chunks: [], bytes: 0, truncatedBytes: 0 };
404+const stderrCapture: CapturedOutputBuffers = { chunks: [], bytes: 0, truncatedBytes: 0 };
405+const maxOutputBytes = normalizeMaxOutputBytes(options.maxOutputBytes);
358406const windowsEncoding = resolveWindowsConsoleEncoding();
359407let settled = false;
360408let timedOut = false;
@@ -443,11 +491,11 @@ export async function runCommandWithTimeout(
443491}
444492445493child.stdout?.on("data", (d) => {
446-stdoutChunks.push(Buffer.isBuffer(d) ? d : Buffer.from(d));
494+appendCapturedOutput(stdoutCapture, d, maxOutputBytes);
447495armNoOutputTimer();
448496});
449497child.stderr?.on("data", (d) => {
450-stderrChunks.push(Buffer.isBuffer(d) ? d : Buffer.from(d));
498+appendCapturedOutput(stderrCapture, d, maxOutputBytes);
451499armNoOutputTimer();
452500});
453501child.on("error", (err) => {
@@ -512,13 +560,15 @@ export async function runCommandWithTimeout(
512560resolve({
513561pid: child.pid ?? undefined,
514562stdout: decodeWindowsOutputBuffer({
515-buffer: Buffer.concat(stdoutChunks),
563+buffer: Buffer.concat(stdoutCapture.chunks, stdoutCapture.bytes),
516564 windowsEncoding,
517565}),
518566stderr: decodeWindowsOutputBuffer({
519-buffer: Buffer.concat(stderrChunks),
567+buffer: Buffer.concat(stderrCapture.chunks, stderrCapture.bytes),
520568 windowsEncoding,
521569}),
570+stdoutTruncatedBytes: stdoutCapture.truncatedBytes || undefined,
571+stderrTruncatedBytes: stderrCapture.truncatedBytes || undefined,
522572code: normalizedCode,
523573signal: resolvedSignal,
524574killed: child.killed,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。