mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-14 16:10:59 -07:00
(fix): Respect ctrl+c signal for aborting execution in NonInteractive mode (#11478)
This commit is contained in:
@@ -30,6 +30,7 @@ import {
|
||||
} from '@google/gemini-cli-core';
|
||||
|
||||
import type { Content, Part } from '@google/genai';
|
||||
import readline from 'node:readline';
|
||||
|
||||
import { handleSlashCommand } from './nonInteractiveCliCommands.js';
|
||||
import { ConsolePatcher } from './ui/utils/ConsolePatcher.js';
|
||||
@@ -82,9 +83,95 @@ export async function runNonInteractive({
|
||||
? new StreamJsonFormatter()
|
||||
: null;
|
||||
|
||||
const abortController = new AbortController();
|
||||
|
||||
// Track cancellation state
|
||||
let isAborting = false;
|
||||
let cancelMessageTimer: NodeJS.Timeout | null = null;
|
||||
|
||||
// Setup stdin listener for Ctrl+C detection
|
||||
let stdinWasRaw = false;
|
||||
let rl: readline.Interface | null = null;
|
||||
|
||||
const setupStdinCancellation = () => {
|
||||
// Only setup if stdin is a TTY (user can interact)
|
||||
if (!process.stdin.isTTY) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save original raw mode state
|
||||
stdinWasRaw = process.stdin.isRaw || false;
|
||||
|
||||
// Enable raw mode to capture individual keypresses
|
||||
process.stdin.setRawMode(true);
|
||||
process.stdin.resume();
|
||||
|
||||
// Setup readline to emit keypress events
|
||||
rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
escapeCodeTimeout: 0,
|
||||
});
|
||||
readline.emitKeypressEvents(process.stdin, rl);
|
||||
|
||||
// Listen for Ctrl+C
|
||||
const keypressHandler = (
|
||||
str: string,
|
||||
key: { name?: string; ctrl?: boolean },
|
||||
) => {
|
||||
// Detect Ctrl+C: either ctrl+c key combo or raw character code 3
|
||||
if ((key && key.ctrl && key.name === 'c') || str === '\u0003') {
|
||||
// Only handle once
|
||||
if (isAborting) {
|
||||
return;
|
||||
}
|
||||
|
||||
isAborting = true;
|
||||
|
||||
// Only show message if cancellation takes longer than 200ms
|
||||
// This reduces verbosity for fast cancellations
|
||||
cancelMessageTimer = setTimeout(() => {
|
||||
process.stderr.write('\nCancelling...\n');
|
||||
}, 200);
|
||||
|
||||
abortController.abort();
|
||||
// Note: Don't exit here - let the abort flow through the system
|
||||
// and trigger handleCancellationError() which will exit with proper code
|
||||
}
|
||||
};
|
||||
|
||||
process.stdin.on('keypress', keypressHandler);
|
||||
};
|
||||
|
||||
const cleanupStdinCancellation = () => {
|
||||
// Clear any pending cancel message timer
|
||||
if (cancelMessageTimer) {
|
||||
clearTimeout(cancelMessageTimer);
|
||||
cancelMessageTimer = null;
|
||||
}
|
||||
|
||||
// Cleanup readline and stdin listeners
|
||||
if (rl) {
|
||||
rl.close();
|
||||
rl = null;
|
||||
}
|
||||
|
||||
// Remove keypress listener
|
||||
process.stdin.removeAllListeners('keypress');
|
||||
|
||||
// Restore stdin to original state
|
||||
if (process.stdin.isTTY) {
|
||||
process.stdin.setRawMode(stdinWasRaw);
|
||||
process.stdin.pause();
|
||||
}
|
||||
};
|
||||
|
||||
let errorToHandle: unknown | undefined;
|
||||
try {
|
||||
consolePatcher.patch();
|
||||
|
||||
// Setup stdin cancellation listener
|
||||
setupStdinCancellation();
|
||||
|
||||
coreEvents.on(CoreEvent.UserFeedback, handleUserFeedback);
|
||||
coreEvents.drainFeedbackBacklog();
|
||||
|
||||
@@ -108,8 +195,6 @@ export async function runNonInteractive({
|
||||
});
|
||||
}
|
||||
|
||||
const abortController = new AbortController();
|
||||
|
||||
let query: Part[] | undefined;
|
||||
|
||||
if (isSlashCommand(input)) {
|
||||
@@ -336,6 +421,9 @@ export async function runNonInteractive({
|
||||
} catch (error) {
|
||||
errorToHandle = error;
|
||||
} finally {
|
||||
// Cleanup stdin cancellation before other cleanup
|
||||
cleanupStdinCancellation();
|
||||
|
||||
consolePatcher.cleanup();
|
||||
coreEvents.off(CoreEvent.UserFeedback, handleUserFeedback);
|
||||
if (isTelemetrySdkInitialized()) {
|
||||
|
||||
Reference in New Issue
Block a user