小龙虾(OpenClaw)源码分析2:CLI启动链路,从一条命令说起
文章目录
上一篇我们把地图铺开了,这一篇开始走主链路第一站:CLI启动。
毕竟无论你是跑openclaw gateway还是openclaw agent,第一步都绕不开CLI入口。
先看两个入口文件
这个项目里和启动强相关的入口,先盯两个就够了:
src/entry.tssrc/cli/run-main.ts
我自己的理解是:
entry.ts负责“程序级起步动作”(环境、参数预处理、快速路径、异常兜底)run-main.ts负责“CLI主流程调度”(解析参数、注册命令、执行命令)
entry.ts在干嘛
entry.ts里有几段很关键的逻辑:
- 只在主模块执行(避免被import时重复启动)
- 处理
--no-color这类环境行为 - 尝试respawn(某些场景会子进程重启CLI)
- 最终进入
runMainOrRootHelp
换句话说,它把“启动姿势”先调整好,再把执行权交给真正的CLI主流程。
run-main.ts才是命令分发核心
src/cli/run-main.ts函数runCli()里,主流程大概是这样:
- 规范化argv(含Windows兼容)
- 处理
--profile/--container这类全局参数 - 加载
.env和运行时环境 - 处理root help/version快速路径
- 执行路由
tryRouteCli - 构建program并注册命令(核心命令 + 插件命令)
program.parseAsync(...)真正执行
这段代码的设计我觉得很实用:
先做一层“轻路由/快路径”再进入完整命令解析,启动体验会更干脆。
一个典型执行过程
比如你执行:
openclaw gateway --port 18789 --verbose
大致会经历:
entry.ts接管进程级初始化- 转到
runCli()做参数预处理 - 命令注册阶段识别到
gateway - Commander解析并进入对应命令处理逻辑
- 后续才会进入Gateway启动流程(这是下一篇重点)
为什么要先注册核心命令,再按需注册插件命令
run-main.ts里可以看到,它会根据参数判断注册策略,比如只注册主命令、或补充插件命令。
这样做的好处是:
- help输出更准确
- 非必要插件命令不会过早拉起
- 启动性能和可维护性更平衡
从工程角度讲,这是一种“按需装配”的CLI架构思路。
读这部分源码的建议
你可以按下面顺序读,效率更高:
src/entry.tssrc/cli/run-main.tssrc/cli/program/build-program.tssrc/cli/program/command-registry.ts
不要一上来就陷入所有命令实现细节,先把“命令是如何被定位和执行的”这件事跑通。
小结
这一篇我们搞清楚了三件事:
- OpenClaw CLI不是一层薄壳,而是分层启动
entry.ts偏进程和环境,run-main.ts偏命令调度- 命令注册是按需策略,不是无脑全量加载
下一篇进入Gateway启动内幕,把控制平面是怎么一点点挂起来的讲清楚。
