diff --git a/app/server/utils/__tests__/spawn.test.ts b/app/server/utils/__tests__/spawn.test.ts index 66164c42..8066a442 100644 --- a/app/server/utils/__tests__/spawn.test.ts +++ b/app/server/utils/__tests__/spawn.test.ts @@ -206,7 +206,7 @@ describe("safeSpawn", () => { expect(result.error).toBe("err_last"); }); - test("stderr keeps the full stderr output", async () => { + test("stderr keeps the captured stderr output", async () => { const result = await safeSpawn({ command: "sh", args: ["-c", "echo err_first >&2 && echo err_last >&2 && exit 1"], @@ -215,6 +215,15 @@ describe("safeSpawn", () => { expect(result.stderr).toBe("err_first\nerr_last"); }); + test("stderr keeps only the last 50 stderr lines", async () => { + const result = await safeSpawn({ + command: "sh", + args: ["-c", "i=1; while [ $i -le 55 ]; do echo err_$i >&2; i=$((i+1)); done; exit 1"], + }); + + expect(result.stderr).toBe(Array.from({ length: 50 }, (_, index) => `err_${index + 6}`).join("\n")); + }); + test("returns exitCode -1 when the command is not found", async () => { const result = await safeSpawn({ command: "this-command-does-not-exist-zerobyte", diff --git a/packages/core/src/utils/spawn.ts b/packages/core/src/utils/spawn.ts index 5d173b23..618ee7ac 100644 --- a/packages/core/src/utils/spawn.ts +++ b/packages/core/src/utils/spawn.ts @@ -62,6 +62,8 @@ export type SpawnResult = { stderr?: string; }; +const MAX_STDERR_LINES = 50; + export function safeSpawn(params: SafeSpawnParamsLines): Promise; export function safeSpawn(params: SafeSpawnParamsRaw): Promise; export function safeSpawn(params: SafeSpawnParams): Promise { @@ -71,7 +73,7 @@ export function safeSpawn(params: SafeSpawnParams): Promise { let lastStdout = ""; let lastStderr = ""; - let stderr = ""; + const stderrLines: string[] = []; return new Promise((resolve) => { const child = spawn(command, args, { @@ -103,7 +105,10 @@ export function safeSpawn(params: SafeSpawnParams): Promise { rlErr.on("line", (line) => { if (onStderr) onStderr(line); - stderr += `${line}\n`; + stderrLines.push(line); + if (stderrLines.length > MAX_STDERR_LINES) { + stderrLines.shift(); + } const trimmed = line.trim(); if (trimmed.length > 0) { lastStderr = line; @@ -118,7 +123,7 @@ export function safeSpawn(params: SafeSpawnParams): Promise { exitCode: -1, summary: lastStdout, error: err.message || lastStderr, - stderr: stderr.trim(), + stderr: stderrLines.join("\n"), }); }); @@ -127,7 +132,7 @@ export function safeSpawn(params: SafeSpawnParams): Promise { exitCode: code ?? -1, summary: lastStdout, error: lastStderr, - stderr: stderr.trim(), + stderr: stderrLines.join("\n"), }); }); });