summaryrefslogtreecommitdiff
path: root/packages/cli/src/gemini.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/cli/src/gemini.tsx')
-rw-r--r--packages/cli/src/gemini.tsx138
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);
+});