数据截至 (上游 commit b084ab075ba2)
适配 25 家 CLI:RuntimeAgentDef 与双向 MCP
30 秒导读: Open Design 自己不调模型,它调别人的编码 agent CLI——Claude Code、Codex、Gemini CLI、OpenCode……一共 25 家。这 25 家的命令行参数、prompt 投递方式、输出流格式没有任何两家相同。本章讲的就是这一层:一份纯声明式的
RuntimeAgentDef如何把它们抹平成同一个接口,以及 daemon 如何反过来把自己变成一台 MCP server 接给它们。
本章只讲适配层。一次 run 从按下回车到产物落盘的主线在 01-run-lifecycle;prompt 文本本身怎么拼在 03-prompt-composition;失败分类与重试也在 01-run-lifecycle。
1. 这是什么(零基础也能懂)
一句话定义: 一个把 25 个终端 AI 编码工具"翻译"成同一套调用协议的兼容层。
它要解决的问题
假设你要写一个桌面 App,用户在里面点"生成一个落地页",你去调用用户本机已经装好的 AI 编码 CLI 来干活。
麻烦在于——每一家的用法都不一样。同样是"给它一段 prompt、让它流式吐结果",你要写出来的命令是这样的:
| CLI | 实际命令形状 | prompt 怎么给 | 输出长什么样 |
|---|---|---|---|
| Claude Code | claude -p --input-format stream-json --output-format stream-json --verbose | stdin,包成一行 JSONL | Anthropic 风格的 content_block_delta JSONL |
| Codex | codex exec --json --skip-git-repo-check --sandbox workspace-write | stdin 裸文本 | 自家的 item.completed / turn.completed JSONL |
| Gemini CLI | gemini --output-format stream-json --yolo | stdin 裸文本 | 又一种 JSONL 方言 |
| Antigravity | agy --log-file /tmp/x -p - | stdin 裸文本 | 纯文本,出错时什么都不打印 |
| Grok Build | grok --prompt-file /tmp/prompt.md | 临时文件 | 纯文本 |
| DeepSeek | deepseek exec --auto "<整段 prompt>" | argv 位置参数 | 纯文本 |
六家六种写法,还有十九家没列。而且这些差异不是"风格问题"——写错一个 flag,进程会在读到 prompt 之前就 exit 2。
它做了什么
这一层负责四件事:
- 声明:每家 CLI 写成一个
RuntimeAgentDef对象——bin 名、argv 怎么拼、prompt 走哪条路、输出是哪种方言。 - 探测:在用户机器上找到那个可执行文件(PATH 不够就翻 Homebrew、nvm、macOS App Bundle),跑
--version确认能启动,跑--help确认它支不支持某个可选 flag。 - 翻译:把各家五花八门的输出流解析成同一套 UI 事件(
text_delta/tool_use/usage/turn_end)。 - 反向供能:把 Open Design 自己的能力(创建产物、列技能、发起 run)作为 MCP 工具接回给这些 CLI。
一句话直觉
把它想成打印机驱动。 应用层只会说"打印这一页",25 个驱动各自知道自家打印机的指令集。RuntimeAgentDef 就是驱动的声明格式,registry.ts 就是驱动列表。