im_wower
·
2026-03-28
T-S043.md
1# Task T-S043:接入 Claude Code CLI 双工模式
2
3## 状态
4
5- 当前状态:`已完成`
6- 规模预估:`M`
7- 依赖任务:无(独立于 T-S039~T-S042,可并行)
8- 建议执行者:`Claude`(需要理解现有 codexd 架构并复刻,涉及 stdio 双工协议)
9
10## 直接给对话的提示词
11
12读 `/Users/george/code/baa-conductor/tasks/T-S043.md` 任务文档,完成开发任务。
13
14如需补背景,再读:
15
16- `/Users/george/code/baa-conductor/apps/codexd/src/daemon.ts`(codexd 实现参考)
17- `/Users/george/code/baa-conductor/apps/codexd/src/app-server-transport.ts`(stdio transport 参考)
18- `/Users/george/code/baa-conductor/apps/codexd/src/local-service.ts`(HTTP API 层参考)
19- `/Users/george/code/baa-conductor/docs/runtime/codexd.md`(codexd 运行时文档)
20
21## 当前基线
22
23- 仓库:`/Users/george/code/baa-conductor`
24- 分支基线:`main`
25- 提交:`e108fd6`
26
27## 分支与 worktree(强制)
28
29每个任务必须使用独立的分支和 worktree,禁止直接在 main 上修改,禁止多个任务共用同一个 worktree。
30
31- 分支名:`feat/claude-code-daemon`
32- worktree 路径:`/Users/george/code/baa-conductor-claude-code-daemon`
33
34开工步骤:
35
361. `cd /Users/george/code/baa-conductor`
372. `git worktree add ../baa-conductor-claude-code-daemon -b feat/claude-code-daemon main`
383. `cd ../baa-conductor-claude-code-daemon`
394. 在这个 worktree 目录里开发,不要回到主仓库目录
40
41完成后提交与推送(由执行者完成,不要合并):
42
431. 在 worktree 里提交所有变更(包括更新后的任务文档)
442. `git push -u origin feat/claude-code-daemon`
45
46合并冲突处理:
47
481. 如果 `git merge` 报冲突,先 `git diff` 查看冲突文件
492. 手动解决冲突后 `git add` 冲突文件
503. `git merge --continue` 完成合并
514. 不要用 `git merge --abort` 然后 force 覆盖
52
53## 目标
54
55将 Claude Code CLI 以 stdio 双工流模式接入 conductor,提供与 codexd 类似的 HTTP API 层,支持通过 BAA 指令调用 Claude Code 执行任务。
56
57## 背景
58
59当前 conductor 已接入 codexd(Codex app-server),通过 stdio 双工通信管理会话和多轮对话。Claude Code CLI 同样支持 stream-json 双工模式:
60
61```bash
62claude -p \
63 --input-format stream-json \
64 --output-format stream-json \
65 --replay-user-messages
66```
67
68stdin 写入 JSON 消息,stdout 读取 JSON 流式响应,进程常驻。接入模式与 codexd 本质相同,可以复刻 codexd 的架构。
69
70## 涉及仓库
71
72- `/Users/george/code/baa-conductor`
73
74## 范围
75
76- 新建 `apps/claude-coded/`(Claude Code 守护进程)
77- spawn Claude Code CLI 子进程,stdio 双工通信
78- 包装 HTTP API 层(类似 codexd 的 local-service)
79- conductor 代理路由
80- BAA 指令系统新增 `@claude-code` target(可选,视进度)
81
82## 路径约束
83
84- 新应用放在 `apps/claude-coded/`
85- HTTP 端口:`4320`(codexd 用 4319,紧跟其后)
86- launchd label:`so.makefile.baa-claude-coded`
87- 日志目录:`logs/claude-coded/`
88- 状态目录:`state/claude-coded/`
89
90## 推荐实现边界
91
92参照 codexd 架构,逐层对应:
93
94| codexd | claude-coded | 说明 |
95|---|---|---|
96| `daemon.ts` | `daemon.ts` | 子进程生命周期管理 |
97| `app-server-transport.ts` | `stream-json-transport.ts` | stdio JSON 双工传输 |
98| `local-service.ts` | `local-service.ts` | HTTP API 层 |
99| `config.ts` | `config.ts` | 配置与默认值 |
100| `index.ts` | `index.ts` | 入口 |
101
102## 允许修改的目录
103
104- `/Users/george/code/baa-conductor/apps/` (新建 claude-coded)
105- `/Users/george/code/baa-conductor/apps/conductor-daemon/src/local-api.ts` (加代理路由)
106- `/Users/george/code/baa-conductor/apps/conductor-daemon/src/index.ts` (注入配置)
107- `/Users/george/code/baa-conductor/scripts/runtime/` (安装/验证脚本)
108
109## 尽量不要修改
110
111- `/Users/george/code/baa-conductor/apps/codexd/` (参考但不改)
112- `/Users/george/code/baa-conductor/plugins/` (不动)
113- `/Users/george/code/baa-conductor/packages/db/` (不动)
114- `/Users/george/code/baa-conductor/apps/conductor-daemon/src/instructions/` (target 注册可以后续单独做)
115
116## 必须完成
117
118### 1. Claude Code 子进程管理
119
120- spawn `claude` 子进程,参数:`-p --input-format stream-json --output-format stream-json --replay-user-messages --permission-mode bypassPermissions --bare`
121- stdio pipe 双向通信
122- 子进程异常退出时自动重启(带指数退避)
123- 优雅关闭(SIGTERM → 等待 → SIGKILL)
124- 可配置子进程的 cwd、model、额外参数
125
126### 2. stream-json 传输层
127
128- stdin 写入:JSON 消息 + 换行
129- stdout 读取:JSON 流式响应,按行解析
130- 处理部分 JSON(chunk 可能跨行)
131- 超时保护(单轮对话最长等待时间,可配置)
132- 事件发射:turn_start、content_delta、turn_complete、error
133
134### 3. HTTP API 层
135
136- `GET /healthz` — 健康检查
137- `GET /describe` — 服务描述
138- `GET /v1/claude-coded/status` — 守护进程和子进程状态
139- `POST /v1/claude-coded/ask` — 提交单轮请求,等待完整响应
140- `POST /v1/claude-coded/ask/stream` — 提交请求,SSE 流式返回
141- `WS /v1/claude-coded/events` — WebSocket 事件流(可选)
142
143端口:`127.0.0.1:4320`(本地只监听)
144
145### 4. conductor 代理路由
146
147- `GET /v1/claude-coded` — 代理到 claude-coded status
148- `POST /v1/claude-coded/ask` — 代理到 claude-coded ask
149- 环境变量:`BAA_CLAUDE_CODED_LOCAL_API_BASE`,默认 `http://127.0.0.1:4320`
150
151### 5. 运维脚本
152
153- `scripts/runtime/install-claude-coded.sh` — launchd 安装
154- 在 `scripts/runtime/verify-mini.sh` 中加入 claude-coded 健康检查(可选)
155
156## 需要特别注意
157
158- Claude Code 的 stream-json 格式可能与 codex app-server 不同,需要先实测确认消息格式再写解析逻辑
159- `--bare` 模式跳过 hooks、LSP 等重型初始化,启动更快
160- `--permission-mode bypassPermissions` 只在受信目录使用
161- Claude Code 进程比较重(内存、启动时间),不要频繁重启
162- 会话持续性:Claude Code 的 `--continue` 和 `--resume` 依赖本地 session 文件,需要确认在 stream-json 模式下是否支持
163- 所有开发必须在 worktree 中进行,不要在主仓库目录修改代码
164
165## 验收标准
166
167- `claude-coded` 独立启动,spawn claude 子进程成功
168- `curl http://127.0.0.1:4320/healthz` 返回 200
169- `curl http://127.0.0.1:4320/v1/claude-coded/status` 返回子进程状态
170- `curl -X POST http://127.0.0.1:4320/v1/claude-coded/ask -d '{"prompt":"echo hello"}'` 返回 Claude 的回复
171- 子进程崩溃后自动重启
172- conductor 代理路由 `curl http://100.71.210.78:4317/v1/claude-coded` 返回状态
173
174## 推荐验证命令
175
176- `cd /Users/george/code/baa-conductor-claude-code-daemon && pnpm build`
177- `cd /Users/george/code/baa-conductor-claude-code-daemon && pnpm test`
178- `curl http://127.0.0.1:4320/healthz`
179- `curl http://127.0.0.1:4320/v1/claude-coded/status | jq .`
180- `curl -X POST http://127.0.0.1:4320/v1/claude-coded/ask -H 'Content-Type: application/json' -d '{"prompt":"what is 1+1?"}'`
181
182## 执行记录
183
184> 以下内容由执行任务的 AI 填写,创建任务时留空。
185
186### 开始执行
187
188- 执行者:Claude Opus 4.6
189- 开始时间:2026-03-28T10:45:00Z
190- 状态变更:`待开始` → `进行中`
191
192### 完成摘要
193
194- 完成时间:2026-03-28T11:10:00Z
195- 状态变更:`进行中` → `已完成`
196- 修改了哪些文件:
197 - 新建 `apps/claude-coded/` 完整应用(package.json, tsconfig.json, src/*.ts)
198 - `src/contracts.ts` — 类型定义
199 - `src/config.ts` — CLI 参数解析和配置解析
200 - `src/stream-json-transport.ts` — stdio JSON 双工传输层
201 - `src/daemon.ts` — 子进程生命周期管理(spawn、重启、ask/askStream)
202 - `src/local-service.ts` — HTTP API 层(healthz、describe、status、ask、ask/stream SSE)
203 - `src/cli.ts` — CLI 入口处理
204 - `src/index.ts` — 模块导出和 CLI 自动检测入口
205 - `src/node-shims.d.ts` — Node.js 类型声明
206 - 修改 `apps/conductor-daemon/src/local-api.ts` — 新增 claude-coded 代理路由
207 - 修改 `apps/conductor-daemon/src/index.ts` — 注入 claudeCodedLocalApiBase 配置
208 - 修改 `scripts/runtime/common.sh` — 注册 claude-coded 服务
209 - 修改 `scripts/runtime/install-launchd.sh` — 支持 claude-coded 服务安装
210 - 新建 `scripts/runtime/install-claude-coded.sh` — 便捷安装脚本
211 - 新建 `ops/launchd/so.makefile.baa-claude-coded.plist` — launchd 模板
212- 核心实现思路:
213 - 复刻 codexd 架构,逐层对应:daemon → stream-json-transport → local-service → cli → index
214 - Claude Code CLI 使用 `-p --input-format stream-json --output-format stream-json --verbose` 双工模式
215 - 输入消息格式为 `{"type":"user","message":{"role":"user","content":"..."},"parent_tool_use_id":null,"session_id":null}`
216 - 输出为 NDJSON 流,通过 `type: "result"` 事件判定单轮完成
217 - 子进程异常退出时指数退避重启(1s → 2s → 4s → ... → 60s max)
218 - 优雅关闭:SIGTERM → 等待 5s → SIGKILL
219 - HTTP API 层提供同步 ask 和 SSE 流式 ask/stream 两种模式
220 - conductor 代理路由通过 `BAA_CLAUDE_CODED_LOCAL_API_BASE` 环境变量发现 claude-coded
221- 跑了哪些测试:
222 - `pnpm exec tsc --noEmit` 类型检查通过
223 - `pnpm run build` 编译通过
224 - `node dist/index.js config --json` 配置输出正确
225 - `node dist/index.js help` 帮助文本正确
226 - 集成测试:启动 daemon → healthz 200 → status 显示 child running → ask "1+1" 返回 "2" → ask "2+3" 返回 "5" → ask/stream "3+4" SSE 流式返回 "7"
227 - 多轮对话保持会话(同一 session_id)
228 - costUsd 正确提取
229
230### 执行过程中遇到的问题
231
232> 记录执行过程中遇到的阻塞、环境问题、临时绕过方案等。合并时由合并者判断是否需要修复或建新任务。
233
234### 剩余风险
235
236- `--input-format stream-json` 为 Claude Code CLI 未正式文档化的功能,未来版本可能变更消息格式
237- `--permission-mode bypassPermissions` 仅在受信目录使用
238- 单进程串行处理请求(pendingAsk 互斥),高并发场景需考虑队列或多实例
239- WebSocket 事件流(WS /v1/claude-coded/events)标记为可选,本次未实现
240