4.3 monorepo 与 package 地图
本页分析版本earendil-works/pi@c13ffe12026-07-30本章解决什么问题:Pi 仓库里有哪些 package、各自负责什么、谁依赖谁——给你一张读源码前必备的地图。 前置知识:1.3 npm、package.json 与项目结构(monorepo 与 workspaces 的概念在那里首次讲解)。 学习目标:① 说出 7 个 package 的职责;② 画出它们的依赖方向;③ 知道每个 package 大致的体量与阅读优先级。
workspaces:一个仓库,多个包
Pi 根目录的 package.json 用 npm workspaces 声明了这是一个 monorepo(源码事实):
workspacesnpm install 在根目录执行一次,就会为所有子包统一安装依赖;子包之间通过包名互相引用(例如 coding-agent 依赖 @earendil-works/pi-agent-core),npm 会把它们链接到本地目录而不是从 registry 下载。
七个 package 一览
以下体量为锁定版本下 src/ 目录去除测试后的实际统计(源码事实,用 wc -l 统计):
| package | npm 包名 | 职责 | 体量(行) | 本书章节 |
|---|---|---|---|---|
packages/ai | @earendil-works/pi-ai | 统一多 Provider 的 LLM API:消息类型、流式事件、20+ 家 Provider 适配、OAuth 登录 | ≈21,000 | 6.1 / 6.2 |
packages/agent | @earendil-works/pi-agent-core | Agent 运行时:Agent Loop、工具执行、Harness、Session 抽象、Compaction 通用实现 | ≈10,000 | 6.3–6.6 |
packages/coding-agent | @earendil-works/pi-coding-agent | 产品层:pi 命令本体。CLI、TUI 交互模式、RPC/JSON 模式、SDK、扩展/技能/主题系统、内置工具 | ≈55,600 | 第五、六、七部分 |
packages/tui | @earendil-works/pi-tui | 终端 UI 库:组件模型与差分渲染(differential rendering) | ≈12,800 | 6.9 |
packages/server | @earendil-works/pi-server | 实验性本机守护进程:管理多个 RPC 子进程(官方说明其定位为 experimental) | ≈2,000 | 6.10 附带 |
packages/storage/sqlite-node | @earendil-works/pi-storage-sqlite-node | SQLite 会话存储后端 | ≈1,600 | 6.5 附带 |
packages/evals | @earendil-works/pi-evals | 评测脚本集 | ≈1,300 | 不深入 |
ai 不知道什么是工具执行,agent 不知道什么是终端渲染,tui 甚至完全不知道 LLM 的存在。这让每一层都能被单独理解、单独测试、单独替换——第三方 Web UI 能存在,正是因为「界面层」与「Harness 层」被协议隔开。这也是你读源码时的导航原则:想找什么概念,先想它属于哪一层。 依赖方向图
依赖关系全部来自各包 package.json 的 dependencies 字段(源码事实):
图 4.3-1 package 依赖方向
箭头 A → B 表示「A 依赖 B」。越靠下的包越基础:pi-ai 与 pi-tui 不依赖任何本仓库的包;pi-coding-agent 是汇聚点;pi-server 在其之上。虚线为 devDependencies。
读图要点:
- 依赖只朝一个方向流动,没有环。
ai永远不会 importagent,agent永远不会 importcoding-agent。你在下层包里看到的任何符号,都可以放心地在不了解上层的情况下理解。 tui与ai互不相识——终端渲染和模型调用是两个完全独立的世界,在coding-agent里才被组装到一起。bin字段揭示了命令入口(源码事实):pi命令来自 coding-agent 的dist/cli.js。
每个包里有什么
按第五部分将要走的请求路径,预览各包内部的关键目录:
packages/
├── ai/src/
│ ├── types.ts ← 统一消息与事件类型(全书引用最多的文件)
│ ├── models.ts ← Models 集合:stream/complete 等对外 API
│ ├── api/ ← 各协议实现(anthropic-messages、openai-*、…)
│ ├── providers/ ← 20+ 家 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/
├── coding-agent/src/
│ ├── cli.ts → main.ts ← 启动入口(5.1 的主角)
│ ├── core/ ← AgentSession、工具、扩展、技能、设置、compaction
│ ├── modes/interactive/ ← TUI 交互模式
│ ├── modes/rpc/ ← RPC/JSON 模式
│ └── extensions/ ← 内置扩展
├── tui/src/ ← Component 模型、差分渲染、编辑器组件
├── server/src/ ← supervisor + ipc(Unix socket 管理 RPC 子进程)
├── storage/sqlite-node/ ← SessionStorage 的 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/的细节(除非你对终端渲染感兴趣)。
实践任务
目标:不相信书,自己从 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 的箭头一一对应(ai 与 tui 为空数组;agent 只依赖 pi-ai;coding-agent 依赖三个;server 依赖 coding-agent)。
如何判断成功:你能解释为什么 storage 包依赖 agent 而不是反过来。
常见错误:忘了 storage 的路径多一层(packages/storage/sqlite-node)。
本章小结
- Pi 是 npm workspaces monorepo:7 个 package,依赖单向流动、无环。
- 三个核心层:
pi-ai(模型接口)→pi-agent-core(Agent 运行时)→pi-coding-agent(产品);pi-tui独立负责渲染。 - coding-agent 体量最大(≈5.6 万行),但主链路入口清晰:
cli.ts → main.ts。 - Provider 文件与 generated 文件可以跳过不读。
- 关键术语:workspaces、依赖方向、bin 入口。
- 自测问题:①
pi-tui为什么不依赖pi-ai?②pi命令对应哪个文件?③ 想看 Agent Loop 应该去哪个包? - 下一章:4.4 从哪里开始读源码。