fix: cap pending process output

This commit is contained in:
Peter Steinberger
2026-01-17 08:18:27 +00:00
parent eef3df9fa5
commit d8b463d0b3
4 changed files with 166 additions and 0 deletions

View File

@@ -4,6 +4,7 @@ import type { ProcessSession } from "./bash-process-registry.js";
import {
addSession,
appendOutput,
drainSession,
listFinishedSessions,
markBackgrounded,
markExited,
@@ -23,9 +24,12 @@ describe("bash process registry", () => {
startedAt: Date.now(),
cwd: "/tmp",
maxOutputChars: 10,
pendingMaxOutputChars: 30_000,
totalOutputChars: 0,
pendingStdout: [],
pendingStderr: [],
pendingStdoutChars: 0,
pendingStderrChars: 0,
aggregated: "",
tail: "",
exited: false,
@@ -43,6 +47,105 @@ describe("bash process registry", () => {
expect(session.truncated).toBe(true);
});
it("caps pending output to avoid runaway polls", () => {
const session: ProcessSession = {
id: "sess",
command: "echo test",
child: { pid: 123 } as ChildProcessWithoutNullStreams,
startedAt: Date.now(),
cwd: "/tmp",
maxOutputChars: 100_000,
pendingMaxOutputChars: 20_000,
totalOutputChars: 0,
pendingStdout: [],
pendingStderr: [],
pendingStdoutChars: 0,
pendingStderrChars: 0,
aggregated: "",
tail: "",
exited: false,
exitCode: undefined,
exitSignal: undefined,
truncated: false,
backgrounded: true,
};
addSession(session);
const payload = `${"a".repeat(70_000)}${"b".repeat(20_000)}`;
appendOutput(session, "stdout", payload);
const drained = drainSession(session);
expect(drained.stdout).toBe("b".repeat(20_000));
expect(session.pendingStdout).toHaveLength(0);
expect(session.pendingStdoutChars).toBe(0);
expect(session.truncated).toBe(true);
});
it("respects max output cap when pending cap is larger", () => {
const session: ProcessSession = {
id: "sess",
command: "echo test",
child: { pid: 123 } as ChildProcessWithoutNullStreams,
startedAt: Date.now(),
cwd: "/tmp",
maxOutputChars: 5_000,
pendingMaxOutputChars: 30_000,
totalOutputChars: 0,
pendingStdout: [],
pendingStderr: [],
pendingStdoutChars: 0,
pendingStderrChars: 0,
aggregated: "",
tail: "",
exited: false,
exitCode: undefined,
exitSignal: undefined,
truncated: false,
backgrounded: true,
};
addSession(session);
appendOutput(session, "stdout", "x".repeat(10_000));
const drained = drainSession(session);
expect(drained.stdout.length).toBe(5_000);
expect(session.truncated).toBe(true);
});
it("caps stdout and stderr independently", () => {
const session: ProcessSession = {
id: "sess",
command: "echo test",
child: { pid: 123 } as ChildProcessWithoutNullStreams,
startedAt: Date.now(),
cwd: "/tmp",
maxOutputChars: 100,
pendingMaxOutputChars: 10,
totalOutputChars: 0,
pendingStdout: [],
pendingStderr: [],
pendingStdoutChars: 0,
pendingStderrChars: 0,
aggregated: "",
tail: "",
exited: false,
exitCode: undefined,
exitSignal: undefined,
truncated: false,
backgrounded: true,
};
addSession(session);
appendOutput(session, "stdout", "a".repeat(6));
appendOutput(session, "stdout", "b".repeat(6));
appendOutput(session, "stderr", "c".repeat(12));
const drained = drainSession(session);
expect(drained.stdout).toBe("a".repeat(4) + "b".repeat(6));
expect(drained.stderr).toBe("c".repeat(10));
expect(session.truncated).toBe(true);
});
it("only persists finished sessions when backgrounded", () => {
const session: ProcessSession = {
id: "sess",
@@ -51,9 +154,12 @@ describe("bash process registry", () => {
startedAt: Date.now(),
cwd: "/tmp",
maxOutputChars: 100,
pendingMaxOutputChars: 30_000,
totalOutputChars: 0,
pendingStdout: [],
pendingStderr: [],
pendingStdoutChars: 0,
pendingStderrChars: 0,
aggregated: "",
tail: "",
exited: false,