4.3 monorepo 与 package 地图
本页分析版本earendil-works/pi@16787ad2026-09-21本章解决什么问题:Pi 仓库里有哪些 package、各自负责什么、谁依赖谁——给你一张读源码前必备的地图。 前置知识:1.3 npm、package.json 与项目结构(monorepo 与 workspaces 的概念在那里首次讲解)。 学习目标:① 说出各个 package 的职责,分清主线包与实验性的包;② 画出它们的依赖方向;③ 知道每个 package 大致的体量与阅读优先级。
workspaces:一个仓库,多个包
Pi 根目录的 package.json 用 npm workspaces 声明了这是一个 monorepo(源码事实):
workspacesnpm install 在根目录执行一次,就会为所有子包统一安装依赖;子包之间通过包名互相引用(例如 coding-agent 依赖 @earendil-works/pi-agent-core),npm 会把它们链接到本地目录而不是从 registry 下载。
package 一览
锁定版本下共有 12 个 package:packages/ 下除 session-backends 以外的 11 个目录各是一个包,再加上嵌套一层的 packages/session-backends/sqlite-node(session-backends 目录本身只是分组,不是包)。以下体量为 src/ 目录去除测试后的实际统计(源码事实,用 wc -l 统计 .ts 文件)。按与本书主线的关系分成两组,先看主线包和它们直接依赖的底层包:
| package | npm 包名 | 职责 | 体量(行) | 本书章节 |
|---|---|---|---|---|
packages/ai | @earendil-works/pi-ai | 统一多 Provider 的 LLM API:消息类型、流式事件、41 个内置 Provider 适配(按 builtinProviders() 的条目数)、OAuth 登录 | ≈25,100 | 6.1 / 6.2 |
packages/agent | @earendil-works/pi-agent-core | Agent 运行时:Agent Loop、工具执行、持久化的 AgentHarness 与 Session 抽象、Compaction 通用实现 | ≈33,500 | 6.3–6.6 |
packages/coding-agent | @earendil-works/pi-coding-agent | 产品层:pi 命令本体。CLI、TUI 交互模式、RPC/JSON 模式、SDK、扩展/技能/主题系统、内置工具 | ≈74,800 | 第五、六、七部分 |
packages/tui | @earendil-works/pi-tui | 终端 UI 库:组件模型与差分渲染(differential rendering),另有全屏(alternate screen)渲染器 | ≈18,300 | 6.9 |
packages/telemetry | @earendil-works/pi-telemetry | 与厂商无关的遥测(telemetry)接口约定:span、事件与带类型的 schema,本身不带任何上报后端 | ≈900 | 不深入 |
再看其余七个。它们大多服务于一套官方标为 experimental(实验性)的远程会话架构,本书只在必要处提到:
| package | npm 包名 | 职责 | 体量(行) | 本书章节 |
|---|---|---|---|---|
packages/chord | @earendil-works/chord | 通用的应用组装运行时:插件、服务、可复制状态与远程服务边界。README 说明它不依赖任何 Pi 包,也不算 Pi 专用的包 | ≈7,600 | 不深入 |
packages/protocol | @earendil-works/pi-protocol | 远程 pi 会话的传输中立协议:CBOR 编码与字节流分帧 | ≈900 | 不深入 |
packages/client | @earendil-works/pi-client | 上述协议的客户端 | ≈1,100 | 不深入 |
packages/server | @earendil-works/pi-server | 实验性本机服务端:把请求路由到持久化的 Session 与 AgentHarness(包描述写明 experimental) | ≈2,000 | 6.10 附带 |
packages/durable | @earendil-works/pi-durable | 持久化的对话、任务与文档运行时(README 称为 Pico runtime),目前只公开记录约定与内存存储实现 | ≈800 | 不深入 |
packages/session-backends/sqlite-node | @earendil-works/pi-session-backend-sqlite-node | 基于 Node 内置 node:sqlite 的 Session 存储后端 | ≈2,000 | 6.5 附带 |
packages/evals | @earendil-works/pi-evals | 评测脚本集(private,不发布到 npm) | ≈1,400 | 不深入 |
ai 不知道什么是工具执行,agent 不知道什么是终端渲染,tui 甚至完全不知道 LLM 的存在。这让每一层都能被单独理解、单独测试、单独替换——第三方 Web UI 能存在,正是因为「界面层」与「Harness 层」被协议隔开。这也是你读源码时的导航原则:想找什么概念,先想它属于哪一层。 依赖方向图
依赖关系全部来自各包 package.json 的 dependencies 字段(源码事实):
图 4.3-1 package 依赖方向
箭头 A → B 表示「A 依赖 B」,箭头指向的一方更基础:pi-tui、pi-telemetry 与 chord 不依赖任何本仓库的包;pi-ai 只依赖 pi-telemetry;pi-coding-agent 是主线的汇聚点。虚线为 devDependencies(pi-coding-agent 还以 dev 依赖引用 pi-protocol,为免线条过密未画出)。
读图要点:
- 依赖只朝一个方向流动,没有环。
ai永远不会 importagent,agent永远不会 importcoding-agent。你在下层包里看到的任何符号,都可以放心地在不了解上层的情况下理解。 tui与ai互不相识——终端渲染和模型调用是两个完全独立的世界,在coding-agent里才被组装到一起。- 主线只有一条:
pi-ai→pi-agent-core→pi-coding-agent,加上旁边的pi-tui。pi-telemetry与chord是被主线包依赖的底层积木;protocol、client、server、durable 属于实验性的远程会话架构。从源码结构看,coding-agent里用到 chord、protocol、client、server 的代码都集中在src/experimental/与src/cli/experimental/下,pi-durable目前没有被其他包引用。 bin字段揭示了命令入口(源码事实):pi命令来自 coding-agent 的dist/bundle/cli.js。
每个包里有什么
按第五部分将要走的请求路径,预览各包内部的关键目录:
packages/
├── ai/src/
│ ├── types.ts ← 统一消息与事件类型(全书引用最多的文件)
│ ├── models.ts ← Models 集合:stream/complete 等对外 API
│ ├── api/ ← 各协议实现(anthropic-messages、openai-*、…)
│ ├── providers/ ← 40 余个内置 Provider 的注册与模型清单
│ ├── auth/ ← API Key / OAuth 凭据
│ └── utils/event-stream.ts ← 流式事件的缓冲与分发
├── agent/src/
│ ├── agent-loop.ts ← Agent Loop 本体(6.3 的主角)
│ ├── agent.ts ← 有状态的 Agent 类
│ └── harness/ ← AgentHarness、session/、tools/、compaction/、runtime/
├── coding-agent/src/
│ ├── cli.ts → main.ts ← 启动入口(5.1 的主角)
│ ├── core/ ← AgentSession、工具、扩展、技能、设置、compaction
│ ├── modes/interactive/ ← TUI 交互模式
│ ├── modes/rpc/ ← RPC/JSON 模式
│ └── extensions/ ← 内置扩展
├── tui/src/ ← Component 模型、差分渲染、编辑器组件、全屏渲染器
├── telemetry/src/ ← 遥测接口约定与内存参考实现
├── chord/ protocol/ client/ server/ durable/ ← 实验性远程会话架构
├── session-backends/sqlite-node/ ← Session 存储的 SQLite 实现
└── evals/ ← 评测不要现在就试图记住所有文件——这张目录树是后续章节的路标,每次深入一个模块时回来对一下位置即可。
哪些先读、哪些跳过
按学习价值排序(分析解释,依据是依赖层级与第五部分的主链路):
- 必读主线:
ai/src/types.ts→agent/src/agent-loop.ts→coding-agent/src/main.ts+core/agent-session.ts。第五部分沿这条线走。 - 按需深入:
ai/src/api/anthropic-messages.ts(看一个 Provider 如何实现协议)、coding-agent/src/core/tools/、core/extensions/。 - 暂时不读:
ai/src/providers/下几十个 Provider 文件(结构高度重复,看懂一个即可)、*.generated.ts(脚本生成的模型数据,不是手写代码)、evals/、tui/的细节(除非你对终端渲染感兴趣),以及 chord、protocol、client、server、durable 这组实验性的包。
实践任务
目标:不相信书,自己从 package.json 里把依赖图挖出来。
步骤:在 Pi 仓库根目录运行:
for p in ai agent coding-agent tui server; do
echo "== $p =="
node -e "const d=require('./packages/$p/package.json'); console.log(Object.keys(d.dependencies||{}).filter(x=>x.startsWith('@earendil')))"
done预期现象:输出与图 4.3-1 的实线箭头一一对应(tui 为空数组;ai 只依赖 pi-telemetry;agent 依赖 chord、pi-ai、pi-telemetry;coding-agent 依赖 chord、pi-agent-core、pi-ai、pi-tui 四个;server 依赖 chord、pi-agent-core、pi-protocol)。
如何判断成功:你能解释为什么 SQLite 会话后端依赖 agent 而不是反过来。
常见错误:想顺手查 SQLite 后端时忘了它的路径多一层(packages/session-backends/sqlite-node)。
本章小结
- Pi 是 npm workspaces monorepo:12 个 package,依赖单向流动、无环;其中一组(chord、protocol、client、server、durable)服务于实验性的远程会话架构。
- 三个核心层:
pi-ai(模型接口)→pi-agent-core(Agent 运行时)→pi-coding-agent(产品);pi-tui独立负责渲染。 - coding-agent 体量最大(≈7.5 万行),但主链路入口清晰:
cli.ts → main.ts。 - Provider 文件与 generated 文件可以跳过不读。
- 关键术语:workspaces、依赖方向、bin 入口。
- 自测问题:①
pi-tui为什么不依赖pi-ai?②pi命令对应哪个文件?③ 想看 Agent Loop 应该去哪个包? - 下一章:4.4 从哪里开始读源码。
✅ 自测问题参考答案先自己回答,再点开对照
- 因为
pi-tui是一个纯终端 UI 库——组件模型加差分渲染,它的工作只是把内容画到终端上,完全不需要知道 LLM 的存在。终端渲染和模型调用是两个互不相识的世界,它们只在pi-coding-agent这一层才被组装到一起。这也保证了依赖单向流动、无环:越基础的包知道的越少(pi-tui不依赖本仓库的任何包,pi-ai也只依赖同样处在底层的pi-telemetry)。 - 对应
packages/coding-agent——它的package.json里bin字段把命令名pi映射到dist/bundle/cli.js,源码入口是packages/coding-agent/src/cli.ts(它再转到main.ts,那是 5.1 的主角)。注意bin指向的是dist而不是src:用户运行的是编译、再打包后的 JavaScript。 - 去
packages/agent(包名@earendil-works/pi-agent-core),本体是packages/agent/src/agent-loop.ts,6.3 会精读它。判断方法就是那条导航原则——先想这个概念属于哪一层:Agent Loop 属于 Agent 运行时,既不属于模型接口层(ai),也不属于产品层(coding-agent)。