mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-28 22:14:52 -07:00
fix: prevent /copy crash on Windows by skipping /dev/tty (#15657)
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
This commit is contained in:
@@ -98,6 +98,9 @@ describe('commandUtils', () => {
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
|
// Reset platform to default for test isolation
|
||||||
|
mockProcess.platform = 'darwin';
|
||||||
|
|
||||||
// Dynamically import and set up spawn mock
|
// Dynamically import and set up spawn mock
|
||||||
const { spawn } = await import('node:child_process');
|
const { spawn } = await import('node:child_process');
|
||||||
mockSpawn = spawn as Mock;
|
mockSpawn = spawn as Mock;
|
||||||
@@ -354,6 +357,35 @@ describe('commandUtils', () => {
|
|||||||
expect(tty.write).not.toHaveBeenCalled();
|
expect(tty.write).not.toHaveBeenCalled();
|
||||||
expect(tty.end).not.toHaveBeenCalled();
|
expect(tty.end).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('skips /dev/tty on Windows and uses stderr fallback for OSC-52', async () => {
|
||||||
|
mockProcess.platform = 'win32';
|
||||||
|
const stderrStream = makeWritable({ isTTY: true });
|
||||||
|
Object.defineProperty(process, 'stderr', {
|
||||||
|
value: stderrStream,
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set SSH environment to trigger OSC-52 path
|
||||||
|
process.env['SSH_CONNECTION'] = '1';
|
||||||
|
|
||||||
|
await copyToClipboard('windows-ssh-test');
|
||||||
|
|
||||||
|
expect(mockFs.createWriteStream).not.toHaveBeenCalled();
|
||||||
|
expect(stderrStream.write).toHaveBeenCalled();
|
||||||
|
expect(mockClipboardyWrite).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses clipboardy on native Windows without SSH/WSL', async () => {
|
||||||
|
mockProcess.platform = 'win32';
|
||||||
|
mockClipboardyWrite.mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
await copyToClipboard('windows-native-test');
|
||||||
|
|
||||||
|
// Fallback to clipboardy and not /dev/tty
|
||||||
|
expect(mockClipboardyWrite).toHaveBeenCalledWith('windows-native-test');
|
||||||
|
expect(mockFs.createWriteStream).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getUrlOpenCommand', () => {
|
describe('getUrlOpenCommand', () => {
|
||||||
|
|||||||
@@ -66,13 +66,19 @@ const SCREEN_DCS_CHUNK_SIZE = 240;
|
|||||||
type TtyTarget = { stream: Writable; closeAfter: boolean } | null;
|
type TtyTarget = { stream: Writable; closeAfter: boolean } | null;
|
||||||
|
|
||||||
const pickTty = (): TtyTarget => {
|
const pickTty = (): TtyTarget => {
|
||||||
// Prefer the controlling TTY to avoid interleaving escape sequences with piped stdout.
|
// /dev/tty is only available on Unix-like systems (Linux, macOS, BSD, etc.)
|
||||||
try {
|
if (process.platform !== 'win32') {
|
||||||
const devTty = fs.createWriteStream('/dev/tty');
|
// Prefer the controlling TTY to avoid interleaving escape sequences with piped stdout.
|
||||||
return { stream: devTty, closeAfter: true };
|
try {
|
||||||
} catch {
|
const devTty = fs.createWriteStream('/dev/tty');
|
||||||
// fall through
|
// Prevent unhandled 'error' events from crashing the process.
|
||||||
|
devTty.on('error', () => {});
|
||||||
|
return { stream: devTty, closeAfter: true };
|
||||||
|
} catch {
|
||||||
|
// fall through - /dev/tty not accessible
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.stderr?.isTTY)
|
if (process.stderr?.isTTY)
|
||||||
return { stream: process.stderr, closeAfter: false };
|
return { stream: process.stderr, closeAfter: false };
|
||||||
if (process.stdout?.isTTY)
|
if (process.stdout?.isTTY)
|
||||||
|
|||||||
Reference in New Issue
Block a user