






















@@ -28,6 +28,7 @@ const CODEX_APP_SERVER_PARSE_LOG_MAX = 500;
2828const CODEX_APP_SERVER_PARSE_BUFFER_MAX = 1_000_000;
2929const CODEX_APP_SERVER_PARSE_BUFFER_MAX_LINES = 1_000;
3030const CODEX_DYNAMIC_TOOL_SERVER_REQUEST_TIMEOUT_MS = 30_000;
31+const CODEX_APP_SERVER_STDERR_TAIL_MAX = 2_000;
31323233type PendingRequest = {
3334method: string;
@@ -76,6 +77,8 @@ export class CodexAppServerClient {
7677private nextId = 1;
7778private initialized = false;
7879private closed = false;
80+private closeError: Error | undefined;
81+private stderrTail = "";
7982private pendingParse:
8083| {
8184text: string;
@@ -89,20 +92,18 @@ export class CodexAppServerClient {
8992this.lines = createInterface({ input: child.stdout });
9093this.lines.on("line", (line) => this.handleLine(line));
9194child.stderr.on("data", (chunk: Buffer | string) => {
92-const text = chunk.toString("utf8").trim();
93-if (text) {
94-embeddedAgentLog.debug(`codex app-server stderr: ${text}`);
95+const text = chunk.toString("utf8");
96+this.stderrTail = appendBoundedTail(this.stderrTail, text, CODEX_APP_SERVER_STDERR_TAIL_MAX);
97+const trimmed = text.trim();
98+if (trimmed) {
99+embeddedAgentLog.debug(`codex app-server stderr: ${trimmed}`);
95100}
96101});
97102child.once("error", (error) =>
98103this.closeWithError(error instanceof Error ? error : new Error(String(error))),
99104);
100105child.once("exit", (code, signal) => {
101-this.closeWithError(
102-new Error(
103-`codex app-server exited: code=${formatExitValue(code)} signal=${formatExitValue(signal)}`,
104-),
105-);
106+this.closeWithError(buildCodexAppServerExitError(code, signal, this.stderrTail));
106107});
107108// Guard against unhandled EPIPE / write-after-close errors on the stdin
108109// stream. When the child process terminates abruptly the pipe can break
@@ -171,7 +172,7 @@ export class CodexAppServerClient {
171172): Promise<T> {
172173options ??= {};
173174if (this.closed) {
174-return Promise.reject(new Error("codex app-server client is closed"));
175+return Promise.reject(this.closeError ?? new Error("codex app-server client is closed"));
175176}
176177if (options.signal?.aborted) {
177178return Promise.reject(new Error(`${method} aborted`));
@@ -451,6 +452,7 @@ export class CodexAppServerClient {
451452return false;
452453}
453454this.closed = true;
455+this.closeError = error;
454456this.lines.close();
455457this.rejectPendingRequests(error);
456458return true;
@@ -596,12 +598,31 @@ function redactCodexAppServerLinePreview(value: string): string {
596598.replace(
597599/("(?:api_?key|authorization|token|access_token|refresh_token)"\s*:\s*")([^"]+)(")/gi,
598600"$1<redacted>$3",
601+)
602+.replace(
603+/\b([a-z0-9_]*(?:api_?key|authorization|access_token|refresh_token|token))(\s*=\s*)(["']?)[^\s"']+(\3)/gi,
604+"$1$2$3<redacted>$4",
599605);
600606return redacted.length > CODEX_APP_SERVER_PARSE_LOG_MAX
601607 ? `${redacted.slice(0, CODEX_APP_SERVER_PARSE_LOG_MAX)}...`
602608 : redacted;
603609}
604610611+function appendBoundedTail(current: string, next: string, maxLength: number): string {
612+const combined = `${current}${next}`;
613+return combined.length > maxLength ? combined.slice(combined.length - maxLength) : combined;
614+}
615+616+function buildCodexAppServerExitError(code: unknown, signal: unknown, stderrTail: string): Error {
617+const stderrPreview = redactCodexAppServerLinePreview(stderrTail);
618+const suffix = stderrPreview ? ` stderr=${JSON.stringify(stderrPreview)}` : "";
619+return new Error(
620+`codex app-server exited: code=${formatExitValue(code)} signal=${formatExitValue(
621+ signal,
622+ )}${suffix}`,
623+);
624+}
625+605626function shouldBufferCodexAppServerParseFailure(value: string, error: unknown): boolean {
606627if (!value.startsWith("{") && !value.startsWith("[")) {
607628return false;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。