源码地址:github.com/google-gemi...
对于 gemini-cli 这样的 Node.js 项目,"入口"有两个层面:
- 可执行文件入口:当你在终端里输入
gemini命令时,系统实际运行的是哪个文件。 - 源码入口:在
src目录中,哪个是包含main函数或启动逻辑的"根"文件。
. 可执行文件入口 (package.json)
这个项目的"可执行"配置在 packages/cli/package.json 文件中。如果你打开这个文件,会找到一个 bin 字段:
json
{
"bin": {
"gemini": "dist/index.js"
}
}
- 文件:
packages/cli/package.json - 入口:
dist/index.js - 作用: 这告诉
npm(或系统),当用户在命令行中运行gemini时,应该执行packages/cli/dist/index.js这个 JavaScript 文件。 - 注意:
dist目录是编译后的产物。我们真正应该看的是它对应的源码。
2. 项目的编译过程
打包执行命令
json
{
"scripts": {
"build": "node ../../scripts/build_package.js",
}
}
build_package.js文件
arduino
// build typescript files
execSync('tsc --build', { stdio: 'inherit' });
process.exit(0);
tsc 构建参数
json
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"jsx": "react-jsx",
"lib": ["DOM", "DOM.Iterable", "ES2023"],
"types": ["node", "vitest/globals"]
},
"include": [
"index.ts",
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.json",
"./package.json"
],
"exclude": ["node_modules", "dist"],
"references": [{ "path": "../core" }]
}
入口文件是 index.ts
typescript
import './src/gemini.js';
import { main } from './src/gemini.js';
import { debugLogger, FatalError } from '@google/gemini-cli-core';
// --- Global Entry Point ---
main().catch((error) => {
if (error instanceof FatalError) {
let errorMessage = error.message;
if (!process.env['NO_COLOR']) {
errorMessage = `\x1b[31m${errorMessage}\x1b[0m`;
}
debugLogger.error(errorMessage);
process.exit(error.exitCode);
}
debugLogger.error('An unexpected critical error occurred:');
if (error instanceof Error) {
debugLogger.error(error.stack);
} else {
debugLogger.error(String(error));
}
process.exit(1);
});
index执行的启动方法是./src/gemini.js 的main方法。结论是 入口文件和方法是 gemini.tsx的main方法
3. 源码入口 (gemini.tsx)
dist/index.js 文件是由 TypeScript 源码编译而来的。根据项目的设计文档和构建逻辑,它的源文件是:
- 文件:
packages/cli/src/gemini.tsx - 方法: 这个文件本身就是入口。它是一个 React (Ink) 应用程序的起点。
当你打开 gemini.tsx,你不会看到一个传统的 main() 函数。相反,你会看到它使用 Ink(一个用于在终端中构建 React 应用的库)来渲染整个 CLI 界面。
文件的底部通常会有一个这样的调用:
scss
// (在 gemini.tsx 文件的末尾)
render(React.createElement(App, parsedArgs));
这个 render 函数就是整个程序的起点。它做了几件关键的事情:
- 解析参数: 它使用
yargs(一个命令行参数解析库) 来解析你输入的所有内容(比如-p "..."或--history)。 - 渲染
App组件: 它将解析后的参数 (parsedArgs) 传递给一个名为App的 React 根组件。 - 启动:
App组件(位于packages/cli/src/App.tsx)接管一切,它会检查你是否处于交互模式(REPL)还是只运行一次性提示,并调用packages/core中的GeminiClient来开始 ReAct 循环。
总结一下:
真正的入口是
packages/cli/src/gemini.tsx。它负责解析命令行参数,然后使用
render()函数启动 React (Ink) 界面 (App.tsx),从而拉起了整个应用。
我们接下来是想深入看看 gemini.tsx 如何解析参数,还是想直接跳到 App.tsx 看看它如何处理交互式会话和非交互式提示的?