1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
/**
* @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 }],
new AbortController().signal,
)) {
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);
});
|