01.2 模式路由与入口汇合

关注源码

  • src/main.tsx
  • src/commands.ts
  • src/dialogLaunchers.tsx
  • src/interactiveHelpers.tsx
  • src/replLauncher.tsx
  • src/QueryEngine.ts

入口层要解决的问题

入口层真正处理的不是“启动一个程序”,而是“判断当前会话属于哪一种运行形态,并把它们收敛到同一套运行时”。

常见形态包括:

  • 交互式 REPL
  • 非交互式单次 query
  • resume / restore 会话
  • remote viewer / direct connect
  • assistant / coordinator 等 feature-gated 模式
  • SDK / structured I/O 场景

汇合点

commands.ts 是控制面注册表

commands.ts 把几类来源统一成一个命令池:

  • 内建命令
  • 技能目录命令
  • 插件命令
  • 动态技能
  • MCP skill commands

它的价值不只是“列出命令”,而是统一了:

  • 命令来源
  • 是否允许模型调用
  • remote / bridge 可用性
  • 插件和动态技能的缓存失效

dialogLaunchers.tsx 处理一次性流程分支

很多启动期交互不是 REPL 常驻 UI 的一部分,例如:

  • resume chooser
  • teleport mismatch / resume
  • invalid settings
  • assistant install / session chooser

这些流程被抽到 launcher 里,避免 main.tsx 直接夹带大量 UI 逻辑。

interactiveHelpers.tsx 是交互式路径的门厅

它把交互式模式中的公共动作收束为几类 API:

  • showDialog() / showSetupDialog()
  • showSetupScreens()
  • renderAndRun()
  • exitWithMessage() / exitWithError()

这样入口层可以只决定“是否进入交互模式”,而不用关心具体 Ink 根节点怎么管理。

replLauncher.tsx 是 UI 懒加载边界

replLauncher.tsx 明确把:

  • components/App
  • screens/REPL

延迟到真正进入交互式模式时再加载。这个边界非常实用:

  • 非交互式场景不必引入大体积 UI 模块图。
  • 启动时可以在 UI 装载前先完成配置与策略判断。

模式路由的设计特点

1. 命令/工具在入口阶段就被裁剪

入口不是“什么都装上,后面再拒绝”,而是会按当前模式先做过滤,例如:

  • remote 模式只保留 REMOTE_SAFE_COMMANDS
  • bridge 只允许 BRIDGE_SAFE_COMMANDS
  • simple mode / REPL mode / coordinator mode 直接影响工具池

这让后续运行时不必在每次交互里重复做高层策略判断。

2. 模式很多,但运行时心智模型尽量统一

不同入口最终还是会汇合到少数几个核心对象:

  • messages
  • ToolUseContext
  • AppState
  • query()
  • tasks

也就是说,入口层负责把“多种启动方式”翻译成“同一种运行时语义”。

3. UI 流程和业务启动被刻意拆开

main.tsx 决定是否需要:

  • setup
  • trust
  • dialogs
  • REPL
  • QueryEngine

但真正的 UI 细节放在 launcher/helpers 中。这种拆分让:

  • headless 路径更干净
  • UI 迭代不容易污染启动编排
  • 测试更容易隔离到单独流程

读代码时建议关注

  • commands.tsgetCommands()getSkillToolCommands()filterCommandsForRemoteMode()
  • interactiveHelpers.tsx 如何处理 trust dialog 之前与之后的状态切换。
  • replLauncher.tsx 如何只做一件事:把已装配好的 props 交给 App + REPL。