From cfac19e77257217309c6e55d7b7123586b7807f7 Mon Sep 17 00:00:00 2001 From: Spencer Date: Thu, 9 Apr 2026 18:10:40 +0000 Subject: [PATCH] fix: resolve childProcessFallback on close instead of exit to prevent stdout truncation, and fix recursive file search in tests --- .../run_shell_command_file_stream.test.ts | 80 +++++++++++-------- .../src/services/shellExecutionService.ts | 2 +- 2 files changed, 48 insertions(+), 34 deletions(-) diff --git a/integration-tests/run_shell_command_file_stream.test.ts b/integration-tests/run_shell_command_file_stream.test.ts index 9fbea9499d..69a608eb71 100644 --- a/integration-tests/run_shell_command_file_stream.test.ts +++ b/integration-tests/run_shell_command_file_stream.test.ts @@ -50,25 +50,27 @@ describe('run_shell_command streaming to file regression', () => { let savedFilePath = ''; const tmpdir = path.join(rig.homeDir!, '.gemini', 'tmp'); if (fs.existsSync(tmpdir)) { - const files = fs.readdirSync(tmpdir, { - recursive: true, - withFileTypes: true, - }); - for (const file of files) { - if (file.isFile() && file.name.endsWith('.txt')) { - // In Node 20+, recursive readdir returns Dirent objects where `parentPath` is the directory path, - // but sometimes `path` is used in older Node. fallback: - const parentDir = - (file as { parentPath?: string }).parentPath ?? - (file as { path?: string }).path ?? - tmpdir; - const p = path.join(parentDir, file.name); - const stat = fs.statSync(p); - if (Date.now() - stat.mtimeMs < 60000 && stat.size >= 20000000) { - savedFilePath = p; - break; + const findFiles = (dir: string): string[] => { + let results: string[] = []; + const list = fs.readdirSync(dir, { withFileTypes: true }); + for (const file of list) { + const fullPath = path.join(dir, file.name); + if (file.isDirectory()) { + results = results.concat(findFiles(fullPath)); + } else if (file.isFile() && file.name.endsWith('.txt')) { + results.push(fullPath); } } + return results; + }; + + const files = findFiles(tmpdir); + for (const p of files) { + const stat = fs.statSync(p); + if (Date.now() - stat.mtimeMs < 60000 && stat.size >= 20000000) { + savedFilePath = p; + break; + } } } @@ -117,24 +119,36 @@ describe('run_shell_command streaming to file regression', () => { let savedFilePath = ''; const tmpdir = path.join(rig.homeDir!, '.gemini', 'tmp'); if (fs.existsSync(tmpdir)) { - const files = fs.readdirSync(tmpdir, { - recursive: true, - withFileTypes: true, - }); - for (const file of files) { - if (file.isFile() && file.name.endsWith('.txt')) { - const parentDir = - (file as { parentPath?: string }).parentPath ?? - (file as { path?: string }).path ?? - tmpdir; - const p = path.join(parentDir, file.name); - const stat = fs.statSync(p); - // Look for file >= 20MB (since we expect 50MB, but allowing margin for the bug) - if (Date.now() - stat.mtimeMs < 60000 && stat.size >= 20000000) { - savedFilePath = p; - break; + const findFiles = (dir: string): string[] => { + let results: string[] = []; + const list = fs.readdirSync(dir, { withFileTypes: true }); + for (const file of list) { + const fullPath = path.join(dir, file.name); + if (file.isDirectory()) { + results = results.concat(findFiles(fullPath)); + } else if (file.isFile() && file.name.endsWith('.txt')) { + results.push(fullPath); } } + return results; + }; + + const files = findFiles(tmpdir); + const fileStats = files.map((p) => ({ + p, + size: fs.statSync(p).size, + age: Date.now() - fs.statSync(p).mtimeMs, + })); + for (const p of files) { + const stat = fs.statSync(p); + // Look for file >= 20MB (since we expect 50MB, but allowing margin for the bug) + if (Date.now() - stat.mtimeMs < 60000 && stat.size >= 20000000) { + savedFilePath = p; + break; + } + } + if (!savedFilePath) { + console.error('Available files:', JSON.stringify(fileStats, null, 2)); } } diff --git a/packages/core/src/services/shellExecutionService.ts b/packages/core/src/services/shellExecutionService.ts index c2697a19ac..090bce19f3 100644 --- a/packages/core/src/services/shellExecutionService.ts +++ b/packages/core/src/services/shellExecutionService.ts @@ -899,7 +899,7 @@ export class ShellExecutionService { abortSignal.addEventListener('abort', abortHandler, { once: true }); - child.on('exit', (code, signal) => { + child.on('close', (code, signal) => { handleExit(code, signal); });