diff options
Diffstat (limited to 'packages/cli/src/gemini.tsx')
| -rw-r--r-- | packages/cli/src/gemini.tsx | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/packages/cli/src/gemini.tsx b/packages/cli/src/gemini.tsx new file mode 100644 index 00000000..11875593 --- /dev/null +++ b/packages/cli/src/gemini.tsx @@ -0,0 +1,138 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { render } from 'ink'; +import { App } from './ui/App.js'; +import { loadCliConfig } from './config/config.js'; +import { readStdin } from './utils/readStdin.js'; +import { GeminiClient } from '@gemini-code/server'; +import { readPackageUp } from 'read-package-up'; +import { fileURLToPath } from 'node:url'; +import { dirname } from 'node:path'; +import { sandbox_command, start_sandbox } from './utils/sandbox.js'; +import { loadSettings } from './config/settings.js'; +import { themeManager } from './ui/themes/theme-manager.js'; +import { getStartupWarnings } from './utils/startupWarnings.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +async function main() { + const settings = loadSettings(process.cwd()); + const config = await loadCliConfig(settings.merged); + + // warn about deprecated environment variables + if (process.env.GEMINI_CODE_MODEL) { + console.warn('GEMINI_CODE_MODEL is deprecated. Use GEMINI_MODEL instead.'); + process.env.GEMINI_MODEL = process.env.GEMINI_CODE_MODEL; + } + if (process.env.GEMINI_CODE_SANDBOX) { + console.warn( + 'GEMINI_CODE_SANDBOX is deprecated. Use GEMINI_SANDBOX instead.', + ); + process.env.GEMINI_SANDBOX = process.env.GEMINI_CODE_SANDBOX; + } + if (process.env.GEMINI_CODE_SANDBOX_IMAGE) { + console.warn( + 'GEMINI_CODE_SANDBOX_IMAGE is deprecated. Use GEMINI_SANDBOX_IMAGE_NAME instead.', + ); + process.env.GEMINI_SANDBOX_IMAGE = process.env.GEMINI_CODE_SANDBOX_IMAGE; + } + + if (settings.merged.theme) { + if (!themeManager.setActiveTheme(settings.merged.theme)) { + // If the theme is not found during initial load, log a warning and continue. + // The useThemeCommand hook in App.tsx will handle opening the dialog. + console.warn(`Warning: Theme "${settings.merged.theme}" not found.`); + } + } + + // hop into sandbox if we are outside and sandboxing is enabled + if (!process.env.SANDBOX) { + const sandbox = sandbox_command(config.getSandbox()); + if (sandbox) { + await start_sandbox(sandbox); + process.exit(0); + } + } + + let input = config.getQuestion(); + const startupWarnings = await getStartupWarnings(); + + // Render UI, passing necessary config values. Check that there is no command line question. + if (process.stdin.isTTY && input?.length === 0) { + const readUpResult = await readPackageUp({ cwd: __dirname }); + const cliVersion = + process.env.CLI_VERSION || readUpResult?.packageJson.version || 'unknown'; + + render( + <React.StrictMode> + <App + config={config} + settings={settings} + cliVersion={cliVersion} + startupWarnings={startupWarnings} + /> + </React.StrictMode>, + ); + return; + } + // If not a TTY, read from stdin + // This is for cases where the user pipes input directly into the command + if (!process.stdin.isTTY) { + input += await readStdin(); + } + if (!input) { + console.error('No input provided via stdin.'); + process.exit(1); + } + + // If not a TTY and we have initial input, process it directly + const geminiClient = new GeminiClient(config); + const chat = await geminiClient.startChat(); + try { + for await (const event of geminiClient.sendMessageStream(chat, [ + { text: input }, + ])) { + if (event.type === 'content') { + process.stdout.write(event.value); + } + // We might need to handle other event types later, but for now, just content. + } + process.stdout.write('\n'); // Add a newline at the end + process.exit(0); + } catch (error) { + console.error('Error processing piped input:', error); + process.exit(1); + } +} + +// --- Global Unhandled Rejection Handler --- +process.on('unhandledRejection', (reason, _promise) => { + // Log other unexpected unhandled rejections as critical errors + console.error('========================================='); + console.error('CRITICAL: Unhandled Promise Rejection!'); + console.error('========================================='); + console.error('Reason:', reason); + console.error('Stack trace may follow:'); + if (!(reason instanceof Error)) { + console.error(reason); + } + // Exit for genuinely unhandled errors + process.exit(1); +}); + +// --- Global Entry Point --- +main().catch((error) => { + console.error('An unexpected critical error occurred:'); + if (error instanceof Error) { + console.error(error.message); + } else { + console.error(String(error)); + } + process.exit(1); +}); |
