feat(cli): route non-interactive output to stderr (#5624)

This commit is contained in:
Allen Hutchison 2025-08-05 16:11:21 -07:00 committed by GitHub
parent 268627469b
commit 2141b39c3d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 41 additions and 23 deletions

View File

@ -70,6 +70,7 @@ describe('runNonInteractive', () => {
getIdeMode: vi.fn().mockReturnValue(false), getIdeMode: vi.fn().mockReturnValue(false),
getFullContext: vi.fn().mockReturnValue(false), getFullContext: vi.fn().mockReturnValue(false),
getContentGeneratorConfig: vi.fn().mockReturnValue({}), getContentGeneratorConfig: vi.fn().mockReturnValue({}),
getDebugMode: vi.fn().mockReturnValue(false),
} as unknown as Config; } as unknown as Config;
}); });

View File

@ -17,28 +17,37 @@ import {
import { Content, Part, FunctionCall } from '@google/genai'; import { Content, Part, FunctionCall } from '@google/genai';
import { parseAndFormatApiError } from './ui/utils/errorParsing.js'; import { parseAndFormatApiError } from './ui/utils/errorParsing.js';
import { ConsolePatcher } from './ui/utils/ConsolePatcher.js';
export async function runNonInteractive( export async function runNonInteractive(
config: Config, config: Config,
input: string, input: string,
prompt_id: string, prompt_id: string,
): Promise<void> { ): Promise<void> {
await config.initialize(); const consolePatcher = new ConsolePatcher({
// Handle EPIPE errors when the output is piped to a command that closes early. stderr: true,
process.stdout.on('error', (err: NodeJS.ErrnoException) => { debugMode: config.getDebugMode(),
if (err.code === 'EPIPE') {
// Exit gracefully if the pipe is closed.
process.exit(0);
}
}); });
const geminiClient = config.getGeminiClient();
const toolRegistry: ToolRegistry = await config.getToolRegistry();
const abortController = new AbortController();
let currentMessages: Content[] = [{ role: 'user', parts: [{ text: input }] }];
let turnCount = 0;
try { try {
await config.initialize();
consolePatcher.patch();
// Handle EPIPE errors when the output is piped to a command that closes early.
process.stdout.on('error', (err: NodeJS.ErrnoException) => {
if (err.code === 'EPIPE') {
// Exit gracefully if the pipe is closed.
process.exit(0);
}
});
const geminiClient = config.getGeminiClient();
const toolRegistry: ToolRegistry = await config.getToolRegistry();
const abortController = new AbortController();
let currentMessages: Content[] = [
{ role: 'user', parts: [{ text: input }] },
];
let turnCount = 0;
while (true) { while (true) {
turnCount++; turnCount++;
if ( if (
@ -133,6 +142,7 @@ export async function runNonInteractive(
); );
process.exit(1); process.exit(1);
} finally { } finally {
consolePatcher.cleanup();
if (isTelemetrySdkInitialized()) { if (isTelemetrySdkInitialized()) {
await shutdownTelemetry(); await shutdownTelemetry();
} }

View File

@ -8,8 +8,9 @@ import util from 'util';
import { ConsoleMessageItem } from '../types.js'; import { ConsoleMessageItem } from '../types.js';
interface ConsolePatcherParams { interface ConsolePatcherParams {
onNewMessage: (message: Omit<ConsoleMessageItem, 'id'>) => void; onNewMessage?: (message: Omit<ConsoleMessageItem, 'id'>) => void;
debugMode: boolean; debugMode: boolean;
stderr?: boolean;
} }
export class ConsolePatcher { export class ConsolePatcher {
@ -46,16 +47,22 @@ export class ConsolePatcher {
originalMethod: (...args: unknown[]) => void, originalMethod: (...args: unknown[]) => void,
) => ) =>
(...args: unknown[]) => { (...args: unknown[]) => {
if (this.params.debugMode) { if (this.params.stderr) {
originalMethod.apply(console, args); if (type !== 'debug' || this.params.debugMode) {
} this.originalConsoleError(this.formatArgs(args));
}
} else {
if (this.params.debugMode) {
originalMethod.apply(console, args);
}
if (type !== 'debug' || this.params.debugMode) { if (type !== 'debug' || this.params.debugMode) {
this.params.onNewMessage({ this.params.onNewMessage?.({
type, type,
content: this.formatArgs(args), content: this.formatArgs(args),
count: 1, count: 1,
}); });
}
} }
}; };
} }