fix(e2e): cap pty transcript output

This commit is contained in:
Vincent Koc
2026-06-01 11:49:54 +02:00
parent 75ebf1c870
commit dac33c8ecb
2 changed files with 64 additions and 14 deletions

View File

@@ -5,6 +5,7 @@ import { spawn } from "@lydell/node-pty";
import { readPositiveIntEnv } from "./env-limits.mjs";
const [logPath, command, ...args] = process.argv.slice(2);
const OUTPUT_MAX_BYTES = readPositiveIntEnv("OPENCLAW_E2E_PTY_OUTPUT_MAX_BYTES", 16 * 1024 * 1024);
if (!logPath || !command) {
console.error("usage: run-with-pty.mjs <log-path> <command> [args...]");
@@ -21,10 +22,37 @@ const pty = spawn(command, args, {
});
let exiting = false;
const outputLimitMarker = `\n[run-with-pty output truncated after ${OUTPUT_MAX_BYTES} bytes]\n`;
const outputState = {
bytes: 0,
truncated: false,
};
function writeCappedOutput(data) {
if (outputState.truncated) {
return;
}
const buffer = Buffer.from(data);
const remainingBytes = OUTPUT_MAX_BYTES - outputState.bytes;
if (buffer.byteLength <= remainingBytes) {
outputState.bytes += buffer.byteLength;
log.write(buffer);
process.stdout.write(buffer);
return;
}
if (remainingBytes > 0) {
const head = buffer.subarray(0, remainingBytes);
log.write(head);
process.stdout.write(head);
}
outputState.bytes = OUTPUT_MAX_BYTES;
outputState.truncated = true;
log.write(outputLimitMarker);
process.stdout.write(outputLimitMarker);
}
pty.onData((data) => {
log.write(data);
process.stdout.write(data);
writeCappedOutput(data);
});
pty.onExit(({ exitCode, signal }) => {

View File

@@ -11,19 +11,18 @@ const scriptPath = path.join(repoRoot, "scripts/e2e/lib/run-with-pty.mjs");
function runPtyProbe(
logPath: string,
env: Record<string, string> = {},
command: string[] = [
"/bin/bash",
"-lc",
'printf "prompt\\n"; IFS= read -r value; printf "got:%s\\n" "$value"',
],
input = "abc\n",
): Promise<{ code: number | null; stdout: string; stderr: string }> {
return new Promise((resolve, reject) => {
const child = spawn(
process.execPath,
[
scriptPath,
logPath,
"/bin/bash",
"-lc",
'printf "prompt\\n"; IFS= read -r value; printf "got:%s\\n" "$value"',
],
{ env: { ...process.env, ...env }, stdio: ["pipe", "pipe", "pipe"] },
);
const child = spawn(process.execPath, [scriptPath, logPath, ...command], {
env: { ...process.env, ...env },
stdio: ["pipe", "pipe", "pipe"],
});
let stdout = "";
let stderr = "";
const timeout = setTimeout(() => {
@@ -47,7 +46,7 @@ function runPtyProbe(
clearTimeout(timeout);
resolve({ code, stdout, stderr });
});
child.stdin.end("abc\n");
child.stdin.end(input);
});
}
@@ -81,4 +80,27 @@ describe("run-with-pty", () => {
await rm(tempRoot, { recursive: true, force: true });
}
});
it("caps noisy PTY output in stdout and transcript logs", async () => {
const tempRoot = await mkdtemp(path.join(os.tmpdir(), "openclaw-run-with-pty-"));
const logPath = path.join(tempRoot, "pty.log");
try {
const result = await runPtyProbe(
logPath,
{ OPENCLAW_E2E_PTY_OUTPUT_MAX_BYTES: "64" },
[process.execPath, "-e", "process.stdout.write('x'.repeat(2048))"],
"",
);
const log = await readFile(logPath, "utf8");
const marker = "[run-with-pty output truncated after 64 bytes]";
expect(result).toMatchObject({ code: 0, stderr: "" });
expect(result.stdout).toContain(marker);
expect(log).toContain(marker);
expect(result.stdout.length).toBeLessThan(512);
expect(log.length).toBeLessThan(512);
} finally {
await rm(tempRoot, { recursive: true, force: true });
}
});
});