fix: increase output limits for truncating collector (#575)
This Pull Request addresses an issue where the output of commands executed in the raw-exec utility was being truncated due to restrictive limits on the number of lines and bytes collected. The truncation caused the message [Output truncated: too many lines or bytes] to appear when processing large outputs, which could hinder the functionality of the CLI. Changes Made Increased the maximum output limits in the [createTruncatingCollector](https://github.com/openai/codex/pull/575) utility: Bytes: Increased from 10 KB to 100 KB. Lines: Increased from 256 lines to 1024 lines. Installed the @types/node package to resolve missing type definitions for [NodeJS](https://github.com/openai/codex/pull/575) and [Buffer](https://github.com/openai/codex/pull/575). Verified and fixed any related errors in the [createTruncatingCollector](https://github.com/openai/codex/pull/575) implementation. Issue Solved: This PR ensures that larger outputs can be processed without truncation, improving the usability of the CLI for commands that generate extensive output. https://github.com/openai/codex/issues/509 --------- Co-authored-by: Michael Bolin <bolinfest@gmail.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { exec as rawExec } from "../src/utils/agent/sandbox/raw-exec.js";
|
||||
import { describe, it, expect } from "vitest";
|
||||
import type { AppConfig } from "src/utils/config.js";
|
||||
|
||||
// Import the low‑level exec implementation so we can verify that AbortSignal
|
||||
// correctly terminates a spawned process. We bypass the higher‑level wrappers
|
||||
@@ -12,9 +13,13 @@ describe("exec cancellation", () => {
|
||||
// Spawn a node process that would normally run for 5 seconds before
|
||||
// printing anything. We should abort long before that happens.
|
||||
const cmd = ["node", "-e", "setTimeout(() => console.log('late'), 5000);"];
|
||||
|
||||
const config: AppConfig = {
|
||||
model: "test-model",
|
||||
instructions: "test-instructions",
|
||||
};
|
||||
const start = Date.now();
|
||||
const promise = rawExec(cmd, {}, abortController.signal);
|
||||
|
||||
const promise = rawExec(cmd, {}, config, abortController.signal);
|
||||
|
||||
// Abort almost immediately.
|
||||
abortController.abort();
|
||||
@@ -36,9 +41,14 @@ describe("exec cancellation", () => {
|
||||
it("allows the process to finish when not aborted", async () => {
|
||||
const abortController = new AbortController();
|
||||
|
||||
const config: AppConfig = {
|
||||
model: "test-model",
|
||||
instructions: "test-instructions",
|
||||
};
|
||||
|
||||
const cmd = ["node", "-e", "console.log('finished')"];
|
||||
|
||||
const result = await rawExec(cmd, {}, abortController.signal);
|
||||
const result = await rawExec(cmd, {}, config, abortController.signal);
|
||||
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.stdout.trim()).toBe("finished");
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import type * as fsType from "fs";
|
||||
|
||||
import { loadConfig, saveConfig } from "../src/utils/config.js"; // parent import first
|
||||
import {
|
||||
loadConfig,
|
||||
saveConfig,
|
||||
DEFAULT_SHELL_MAX_BYTES,
|
||||
DEFAULT_SHELL_MAX_LINES,
|
||||
} from "../src/utils/config.js";
|
||||
import { AutoApprovalMode } from "../src/utils/auto-approval-mode.js";
|
||||
import { tmpdir } from "os";
|
||||
import { join } from "path";
|
||||
@@ -275,3 +280,84 @@ test("handles empty user instructions when saving with project doc separator", (
|
||||
});
|
||||
expect(loadedConfig.instructions).toBe("");
|
||||
});
|
||||
|
||||
test("loads default shell config when not specified", () => {
|
||||
// Setup config without shell settings
|
||||
memfs[testConfigPath] = JSON.stringify(
|
||||
{
|
||||
model: "mymodel",
|
||||
},
|
||||
null,
|
||||
2,
|
||||
);
|
||||
memfs[testInstructionsPath] = "test instructions";
|
||||
|
||||
// Load config and verify default shell settings
|
||||
const loadedConfig = loadConfig(testConfigPath, testInstructionsPath, {
|
||||
disableProjectDoc: true,
|
||||
});
|
||||
|
||||
// Check shell settings were loaded with defaults
|
||||
expect(loadedConfig.tools).toBeDefined();
|
||||
expect(loadedConfig.tools?.shell).toBeDefined();
|
||||
expect(loadedConfig.tools?.shell?.maxBytes).toBe(DEFAULT_SHELL_MAX_BYTES);
|
||||
expect(loadedConfig.tools?.shell?.maxLines).toBe(DEFAULT_SHELL_MAX_LINES);
|
||||
});
|
||||
|
||||
test("loads and saves custom shell config", () => {
|
||||
// Setup config with custom shell settings
|
||||
const customMaxBytes = 12_410;
|
||||
const customMaxLines = 500;
|
||||
|
||||
memfs[testConfigPath] = JSON.stringify(
|
||||
{
|
||||
model: "mymodel",
|
||||
tools: {
|
||||
shell: {
|
||||
maxBytes: customMaxBytes,
|
||||
maxLines: customMaxLines,
|
||||
},
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
);
|
||||
memfs[testInstructionsPath] = "test instructions";
|
||||
|
||||
// Load config and verify custom shell settings
|
||||
const loadedConfig = loadConfig(testConfigPath, testInstructionsPath, {
|
||||
disableProjectDoc: true,
|
||||
});
|
||||
|
||||
// Check shell settings were loaded correctly
|
||||
expect(loadedConfig.tools?.shell?.maxBytes).toBe(customMaxBytes);
|
||||
expect(loadedConfig.tools?.shell?.maxLines).toBe(customMaxLines);
|
||||
|
||||
// Modify shell settings and save
|
||||
const updatedMaxBytes = 20_000;
|
||||
const updatedMaxLines = 1_000;
|
||||
|
||||
const updatedConfig = {
|
||||
...loadedConfig,
|
||||
tools: {
|
||||
shell: {
|
||||
maxBytes: updatedMaxBytes,
|
||||
maxLines: updatedMaxLines,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
saveConfig(updatedConfig, testConfigPath, testInstructionsPath);
|
||||
|
||||
// Verify saved config contains updated shell settings
|
||||
expect(memfs[testConfigPath]).toContain(`"maxBytes": ${updatedMaxBytes}`);
|
||||
expect(memfs[testConfigPath]).toContain(`"maxLines": ${updatedMaxLines}`);
|
||||
|
||||
// Load again and verify updated values
|
||||
const reloadedConfig = loadConfig(testConfigPath, testInstructionsPath, {
|
||||
disableProjectDoc: true,
|
||||
});
|
||||
|
||||
expect(reloadedConfig.tools?.shell?.maxBytes).toBe(updatedMaxBytes);
|
||||
expect(reloadedConfig.tools?.shell?.maxLines).toBe(updatedMaxLines);
|
||||
});
|
||||
|
||||
@@ -5,12 +5,12 @@ import { describe, it, expect, vi } from "vitest";
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
import { exec as rawExec } from "../src/utils/agent/sandbox/raw-exec.js";
|
||||
|
||||
import type { AppConfig } from "../src/utils/config.js";
|
||||
describe("rawExec – invalid command handling", () => {
|
||||
it("resolves with non‑zero exit code when executable is missing", async () => {
|
||||
const cmd = ["definitely-not-a-command-1234567890"];
|
||||
|
||||
const result = await rawExec(cmd, {});
|
||||
const config = { model: "any", instructions: "" } as AppConfig;
|
||||
const result = await rawExec(cmd, {}, config);
|
||||
|
||||
expect(result.exitCode).not.toBe(0);
|
||||
expect(result.stderr.length).toBeGreaterThan(0);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { exec as rawExec } from "../src/utils/agent/sandbox/raw-exec.js";
|
||||
import type { AppConfig } from "src/utils/config.js";
|
||||
|
||||
// Regression test: When cancelling an in‑flight `rawExec()` the implementation
|
||||
// must terminate *all* processes that belong to the spawned command – not just
|
||||
@@ -27,13 +28,17 @@ describe("rawExec – abort kills entire process group", () => {
|
||||
// Bash script: spawn `sleep 30` in background, print its PID, then wait.
|
||||
const script = "sleep 30 & pid=$!; echo $pid; wait $pid";
|
||||
const cmd = ["bash", "-c", script];
|
||||
const config: AppConfig = {
|
||||
model: "test-model",
|
||||
instructions: "test-instructions",
|
||||
};
|
||||
|
||||
// Start a bash shell that:
|
||||
// - spawns a background `sleep 30`
|
||||
// - prints the PID of the `sleep`
|
||||
// - waits for `sleep` to exit
|
||||
const { stdout, exitCode } = await (async () => {
|
||||
const p = rawExec(cmd, {}, abortController.signal);
|
||||
const p = rawExec(cmd, {}, config, abortController.signal);
|
||||
|
||||
// Give Bash a tiny bit of time to start and print the PID.
|
||||
await new Promise((r) => setTimeout(r, 100));
|
||||
|
||||
Reference in New Issue
Block a user