【免费下载链接】ai-devkit
The control plane for AI coding agents.
ai-devkit 是管理多个 AI coding agent 的控制平面(control plane),它的核心痛点是:每个客户端都要扫进程、写状态、刷新列表,各自为战、互相打架。为此项目用 Rust 写了一个名为devkitd的守护进程(daemon),通过Unix Socket上的JSON-RPC协议对外服务。本文将通俗拆解 devkitd 的通信协议、SQLite 事件日志、进程发现循环和自动启动机制,帮你理解这套守护进程架构的设计取舍。
1. 先讲背景:没有守护进程时有多痛 😩
ai-devkit 需要同时追踪claude、codex、gemini、copilot、opencode、pi、devin等一堆 AI 编码代理的进程状态。早期每个 TypeScript 客户端每次调用都自己干这些事:
- 跑一遍
ps扫全系统进程; - 各自写本地状态文件;
- 控制台每 3 秒盲轮询一次列表,哪怕什么都没变。
问题很明显:多个客户端同时扫进程、同时写状态,容易竞态;轮询浪费 CPU;UI 感知延迟高。解法就是引入一个常驻的协调者——devkitd。
它的职责边界被刻意画得很清晰(见 2026-10-07-feature-rust-daemon.md):
| 谁负责 | 内容 |
|---|---|
| devkitd(Rust) | 共享状态、事件分发、进程发现、IPC |
| TypeScript 客户端 | harness 会话文件解析、消息格式化、TUI 渲染 |
daemon 永远不需要知道"什么是 codex 的 session 文件",它只认识 pid、cwd、命令和事件。这个边界正是它"能用 Rust 写、却不用把整层 TS adapter 拖过去"的关键。
2. 为什么是 Rust,而不是继续用 Node/TS?
devkitd 是一个单个静态 Rust 二进制,常驻在后台。选择 Rust 的理由很实际:
- 常驻 + 高频:它每 2 秒扫一次进程、持续服务多个客户端,Node 进程长期跑着吃内存更重;
- 零运行环境依赖:静态编译的二进制(Linux 还是 musl-static,一份构建通吃 glibc 和 Alpine),用户机器上装不装 Node 版本都对它没影响;
- 并发模型贴合:基于
tokio异步框架,"每连接一个任务" + 阻塞线程池跑扫描,一个慢操作不会卡住其他连接(见 server.rs); - 可复用的核心库:协议、存储、发现逻辑都放在 devkit-core,二进制 crate 只剩
main.rs+server.rs两层薄壳。
工程上它和 JS 部分共存于同一个 monorepo:rust/是独立 Cargo workspace,通过 Nx 的nx:run-commands目标接入构建流程(cargo build、cargo clippy都挂到了 rust/project.json)。
3. 通信协议拆解:Unix Socket 上线程 JSON-RPC
3.1 为什么选 Unix Socket 而不是 TCP?
IPC 契约写在 proto.rs 里,传输层的设计取舍如下:
- 监听位置:
~/.ai-devkit/daemon.sock,文件权限0600(只有属主可读写); - 同用户强校验:Linux 上用
SO_PEERCRED检查连接对端 uid,跨用户连接直接拒绝(见 server.rs); - 明确拒绝 gRPC / localhost HTTP:没有任何远程调用者,监听 TCP 对"同用户协调总线"来说是安全倒退——本地状态就不该暴露到网络上。
一句话:这是纯本地、同用户的通信,Unix Socket 走内核不经过网络栈,配合 uid 校验,安全面最小。
3.2 报文格式:一行一个 JSON
帧格式极其朴素——每行一个 JSON 对象,双向通用:
| 帧类型 | 形状 |
|---|---|
| 请求 | {"id":1, "method":"agent.list", "params":{}} |
| 响应 | {"id":1, "result":[...]}或{"id":1, "error":"unknown method: xxx"} |
| 事件帧 | {"event":{"seq":3, "ts":1728..., "kind":"agent.appeared", "payload":{...}}} |
字段缺失就省略、绝不发null,这让 TS 客户端解析特别省心(客户端实现在 client.ts)。
3.3 只有 5 个方法,够用就是最好的 API
| 方法 | 作用 |
|---|---|
ping | 探活,返回{pong, startedAt} |
daemon.status | 查版本、启动时间、socket 路径 |
agent.list | 返回完全归属过的 agent 列表(权威数据源) |
subscribe | 订阅事件流,可带afterSeq(补发)和liveOnly(只看新事件) |
shutdown | 优雅退出,响应冲刷完毕后才 exit |
注意设计文档里还提到一个被砍掉的registryKV 表——因为读的时候要拿 daemon 行和文件行做合并,"两个存储合并比只用文件更糟",于是直接删除。这种"敢删 API"的克制,正是协议保持简单的关键。
4. 状态层:SQLite 独家写入 + 只追加事件日志
devkitd 用rusqlite(bundled,不依赖系统库)打开~/.ai-devkit/daemon.db,开启 WAL 日志模式,并且daemon 是唯一写入者(实现见 store.rs)。两张表:
events(seq, ts, kind, payload)—— 只追加事件日志,seq自增。它既是 pub/sub 的地基,也是审计轨迹;agents(pid, ppid, tty, command, cwd, ...)—— 进程发现快照,每次扫描做 diff。
三个值得新手注意的工程细节:
- 有界日志:事件保留最近 10000 条,每 512 次 emit 顺带做一次清理和 WAL checkpoint,热路径永远是"单条 insert";
- 崩溃自愈:db 损坏时不硬死——隔离成
daemon.db.corrupt-<时间戳>后重建,避免 autostart 陷入"启动→崩溃→再启动"的死循环(见 server.rs); - pid 复用陷阱:进程身份是
(pid, start_time_ms)二元组,否则 OS 复用 pid 会让死进程"静默续命"。store 层有专门测试覆盖这个场景(store.rs)。
5. 事件订阅:补发 + 实时流的无缺口拼接
subscribe是最有设计含量的方法,时序上有三个小心机:
客户端 → subscribe{afterSeq} daemon → ① 先注册 broadcast 订阅(挡住"注册前"的漏洞窗口) daemon → ② 从 SQLite 重放 seq > afterSeq 的持久化事件 daemon → ③ 返回 {subscribed:true},此后推送实时帧 daemon → ④ 实时帧若 seq ≤ 已重放水位,丢弃(防重复)效果是at-least-once 语义:重连、甚至 daemon 重启后补发都成立,而重叠的 seq 会被水位线去重(端到端测试见 server.rs)。如果 1024 事件的广播缓冲溢出,daemon 会显式发一个subscription.lagged帧提示客户端"历史丢了,请重新拉状态"——宁可明说,不静默丢帧。
6. 发现循环:2 秒一轮的全系统扫描
发现逻辑在 discover.rs,规则简单粗暴但讲究防御:
- 每 2 秒跑一次
ps -axo pid=,ppid=,tty=,command=,按可执行文件名匹配 harness 列表(claude、codex、gemini、devin…); - cwd 直接读
/proc/<pid>/cwd,一次系统调用,不用 spawnlsof/pwdx; - 所有外部命令带10 秒硬超时,超时直接 SIGKILL——卡死的
ps永远不允许冻结发现循环; - 每轮扫描和上一轮快照 diff,产出
agent.appeared/agent.disappeared两类事件。
ps失败时保留上一份快照而不是清空——避免"一次故障 → 所有 agent 消失 → 幽灵事件风暴"。
7. tmux 式自动启动:不手动拉起,用时才有
用户几乎不需要显式启动 daemon。TS 客户端的 autostart.ts 实现了tmux 风格的隐式拉起:
- 先试连 socket,连通就直接用;
- 连不上,用
O_EXCL独占锁文件抢占"唯一启动者"身份(超过 30 秒的陈旧锁会被回收,防止崩溃的启动者永久卡死); - 以 detached 方式拉起
devkitd serve,日志追加到~/.ai-devkit/daemon.log(1MiB 自动轮转); - 轮询 socket 直到就绪(≤3 秒)。
还有一个很妙的决定:socket 存在即存活,不用 PID 文件——从根上消灭了"陈旧 PID +kill(pid,0)"这一整类经典 bug。想要开机自启可再跑devkitd install写一个 systemd--user单元(main.rs)。
找不到二进制怎么办?binary.ts 按DEVKITD_BIN环境变量 → npm 平台包@ai-devkit/devkitd-<平台>-<架构>→~/.ai-devkit/bin/devkitd→ 开发目录rust/target/*/devkitd逐级解析,全部落空就安静降级回"无 daemon"的旧行为——用户永远不会被一个可选组件卡死。
8. Rust 与 TypeScript 如何不"各说各话"
双语言最怕协议漂移。ai-devkit 的做法是单一事实来源在 Rust 侧:
proto.rs里的Request/Response/Event都派生了ts-rs宏;cargo test -p devkit-core时自动把 TS 类型重新生成到 packages/daemon-client/src/gen/;- 生成物是 check-in 的,纯 TS 构建永远不需要 cargo;而 CI 一跑 cargo test,类型若有漂移就会弄脏工作树,立刻暴露(整体方案见 2026-10-08-feature-devkitd-monorepo.md)。
9. 客户端真实收益:从 3 秒盲轮询到事件驱动
以最直观的控制台为例(useAgentList.ts + agentListSubscription.ts):
| 无 daemon | 有 daemon |
|---|---|
| 每 3 秒无脑重拉列表 | 挂载时subscribe,agent.appeared/disappeared到达即刷新 |
| 变化感知延迟最高 3s | 毫秒级感知 |
| 每次调用都扫一遍进程 | 轮询降为 60s 慢速兜底 |
| — | 订阅失败自动退回原 3s 轮询,零行为退化 |
架构文档特别强调了一条"诚实的分工":daemon 只提供失效信号("东西变了"),内容仍由客户端的manager.listAgents拉取——事件管时机,不管内容。这样 harness 解析知识留客户端,协议就永远不用为某个 harness 的格式变动而改协议。
10. 日常怎么用:daemon 管理命令
平时完全不用管它(自动拉起)。需要时用 daemon 子命令:
ai-devkit daemon status # 是否在跑、socket 路径、版本 ai-devkit daemon start # 确保 daemon 运行(其实用时也会自动拉) ai-devkit daemon stop # 优雅停止 ai-devkit daemon logs -n 50 # 查看最近 50 行日志 ai-devkit daemon install # 安装 systemd --user 单元实现开机自启status还会主动探测 socket:连得通才是listening,只剩残留文件则报stale socket file (daemon down)——不让陈旧文件骗过诊断(main.rs)。
11. 总结:这套架构的三条可复用经验
✅协调者只做协调:devkitd 不碰 harness 解析、不碰 git、不碰网络监听。职责窄,协议才稳。
✅本地 IPC 用 Unix Socket + JSON-RPC:无远程需求时,TCP/gRPC 是纯负担;一行一 JSON 的帧格式 +seq水位线,用最少的零件实现了可补发的事件流。
✅优雅降级是守护进程的生存底线:找不到二进制 → 退回旧轮询;ps 失败 → 保留旧快照;db 损坏 → 隔离重建;锁卡死 → 30s 回收。每个故障路径都有兜底,可选组件才敢做"隐形"。
如果你想深入源码,建议按这条路径读:proto.rs(协议)→ store.rs(存储)→ server.rs(服务与订阅)→ autostart.ts(拉起),配合设计文档 2026-10-07-feature-rust-daemon.md 和实现笔记 2026-10-07-feature-rust-daemon.md,一两个小时就能把整条链路走通。
【免费下载链接】ai-devkit
The control plane for AI coding agents.
相关推荐
DLSS Swapper完整指南:批量替换游戏DLSS DLL版本
DLSS Swapper完整指南:批量替换游戏DLSS DLL版本 刚入手的游戏支持DLSS 3,但内置版本还停在2.5,一开画面就发糊,官方补丁却迟迟不见踪影
桌面应用UniClipboard 守护进程 uniclipd 架构解析:为什么 GUI 与 daemon 必须严格分离
UniClipboard 守护进程 uniclipd 架构解析:为什么 GUI 与 daemon 必须严格分离 UniClipboard 是一款主打 跨设备剪贴
microduck 机器人整体架构解析:七个守护进程、Unix Socket 控制面与更新安全路径
microduck 机器人整体架构解析:七个守护进程、Unix Socket 控制面与更新安全路径 本文基于 docs/design/architecture.
机器人嵌入式具身智能智能硬件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考