Skip to content

CLI 入口与分发

源码版本v0.73.1

main.tspi 命令的进程入口。它把命令行参数解析成 AppMode,装配 AgentSessionRuntime,再分派给三种运行模式:交互式 TUI、一次性 print、JSON-RPC。除了模式分派,它还负责迁移、session 解析、模型选择、文件型参数预处理。可以理解成「把 shell 调用翻译成 runtime 调用」的前置层。

职责

  1. 解析与分派:parseArgsArgs,resolveAppMode 算出 AppMode,据此选 runRpcMode / InteractiveMode / runPrintMode。见 packages/coding-agent/src/main.ts:98-109packages/coding-agent/src/main.ts:673-726
  2. session 解析:--continue--resume--session <id|path>--fork <id>--no-session 之间的冲突校验和路径解析,把 session 标识符定位到磁盘上的 .jsonl。见 packages/coding-agent/src/main.ts:188-212packages/coding-agent/src/main.ts:147-172
  3. 服务装配:调 createAgentSessionServices + createAgentSessionRuntime,把 cwd、agentDir、authStorage、扩展/技能/主题路径串起来。见 packages/coding-agent/src/main.ts:522-560
  4. 预运行短路:pi --versionpi --exportpi package ...pi config ... 不进 runtime,直接 process.exit。见 packages/coding-agent/src/main.ts:431-478

设计动机

为什么把入口单独成文件?因为 CLI 解析、session 选择、迁移这些逻辑只发生一次,且只在进程启动时发生。把这些塞进 createAgentSession(SDK 入口)会污染 SDK 的复用性——SDK 调用方通常是已有 runtime 上下文的程序,不需要解析 argv,也不需要进程级 stdout 接管。main.ts 因此只承担「进程级杂事」:stdout 接管、信号处理、migrations、版本检查、fork 校验,剩下的都委托给 SDK 和 runtime 工厂。

关键文件

resolveAppMode 优先看 --mode 标志,再退化到 stdin 是否 TTY,这样管道用法 echo x | pi 自动走 print 模式:

typescript
// packages/coding-agent/src/main.ts:98-109
function resolveAppMode(parsed: Args, stdinIsTTY: boolean): AppMode {
	if (parsed.mode === "rpc") {
		return "rpc";
	}
	if (parsed.mode === "json") {
		return "json";
	}
	if (parsed.print || !stdinIsTTY) {
		return "print";
	}
	return "interactive";
}

三岔分派处直接用 appMode 选分支,print 与 json 共用 runPrintMode,差异在 toPrintOutputMode(appMode):

typescript
// packages/coding-agent/src/main.ts:673-726
if (appMode === "rpc") {
	printTimings();
	await runRpcMode(runtime);
} else if (appMode === "interactive") {
	// ...
	const interactiveMode = new InteractiveMode(runtime, {
		migratedProviders,
		modelFallbackMessage,
		initialMessage,
		initialImages,
		initialMessages: parsed.messages,
		verbose: parsed.verbose,
	});
	// ...
	await interactiveMode.run();
} else {
	printTimings();
	const exitCode = await runPrintMode(runtime, {
		mode: toPrintOutputMode(appMode),
		messages: parsed.messages,
		initialMessage,
		initialImages,
	});
	// ...
}

数据流

argv 进来后的分派路径:

边界与失败

  • fork 冲突:--fork 不能与 --session--continue--resume--no-session 同时用,见 packages/coding-agent/src/main.ts:188-202
  • stdout 接管:非 interactive 模式调 takeOverStdout(),把 console.log 重定向到内部管道,避免 TUI 转义污染 print/rpc 输出,退出时 restoreStdout
  • session cwd 缺失:--session 指向别的项目时,interactive 会弹 prompt 让用户选,print/rpc 直接报错退出,见 packages/coding-agent/src/main.ts:502-514
  • benchmark 模式:startupBenchmark 时只跑 interactiveMode.init() 后立刻 stop(),不进主循环。

小结

main.ts 是薄壳:把 argv 和 stdin 翻译成 runtime 工厂闭包,再交给三种模式之一。runtime 工厂内部怎么装配看 createAgentSession 装配,交互模式细节看 TUI 交互模式,非交互模式看 print 与 rpc 模式