Monorepo 全景
36 个 workspace 包的职责与规模、schema → protocol → server 的依赖方向、V1 与 V2 各住在哪些包,以及撑起这一切的关键外部依赖。
工作区
根 package.json 声明 packageManager 为 bun@1.3.14,工作区是 packages/*、packages/console/*、packages/stats/*、packages/sdk/js 与 packages/slack,合计 36 个包。版本统一钉在 workspaces.catalog 里,各包用 catalog: 引用;patchedDependencies 还给 effect、solid-js 和一批 @ai-sdk/* 打了补丁(见根目录 patches/)。类型检查走 bun turbo typecheck,根目录的 test 脚本直接 exit 1——测试只能进各包目录跑。
规模
按各包 src/ 下的 TS/TSX 统计(不含测试与 .d.ts),与 agent 直接相关的包如下:
| 包 | 行数 | 职责 |
|---|---|---|
packages/opencode | 81180 | 命令 opencode:CLI、现役 HTTP 服务端、V1 会话主循环、工具、提供方、配置、插件宿主 |
packages/core | 32970 | Effect 化的新内核:V2 会话、事件、数据库与迁移、LayerNode、models.dev;V1 契约的 facade |
packages/tui | 27035 | 终端界面:OpenTUI + SolidJS,领域数据只经 SDK 获取 |
packages/llm | 9533 | Schema 优先的模型协议层 |
packages/codemode | 6878 | Code Mode:受限的代码执行,只能经 schema 描述的工具与外界交互 |
packages/client | 4643 | 由 HttpApi 生成的新客户端(Promise 与 Effect 两个入口) |
packages/schema | 3387 | 浏览器安全的 wire 与存储契约 |
packages/effect-drizzle-sqlite | 3238 | Effect 与 Drizzle 的 SQLite 适配 |
packages/server | 1682 | 新 HttpApi 的具体实现:handlers、中间件、鉴权 |
packages/plugin | 1612 | 插件 API @opencode-ai/plugin |
packages/protocol | 1582 | /api/... 端点契约 |
packages/cli | 670 | 新 CLI,可执行文件名 lildax |
packages/sdk/js | 30090 | 旧 JS SDK,主体是生成代码 |
packages/sdk-next | 68 | 组合 Client、Core、Server 的新 SDK |
其余是产品外围:packages/app(Web 界面,SolidJS,约 13.9 万行)、ui 与 session-ui(共享组件)、desktop(Electron)、web(Astro Starlight 官网与文档)、console/*(opencode.ai 控制台)、stats/*、enterprise、function、slack、storybook,以及测试基础设施 http-recorder(录制与回放 HTTP 流量)和客户端代码生成器 httpapi-codegen。
依赖方向
根 AGENTS.md 把方向写成了规则:
Keep runtime dependencies directed from Schema to Core and Protocol, then from Core and Protocol to Server. Client runtime code may depend on Schema and Protocol but never Core or Server;
sdk-nextcomposes Client, Core, and Server.
照各包 package.json 的运行时依赖画出来(只画 agent 相关的包):
三处值得留意:
- 清单与 import 不完全一致。
packages/opencode/package.json把 core 列在devDependencies,源码却大量 import@opencode-ai/core/...。读依赖关系要看 import,不能只看清单。 - TUI 与 core 的边界还没收干净。
specs/tui-package.md的目标是 TUI 只以 SDK 为边界、不依赖 core,十个迁移小节都已勾选完成;但 v1.18.30 的packages/tui仍依赖 core,源码里还有十几处从 core 取Global、Flag、InstallationVersion之类基础设施的 import(如packages/tui/src/context/sdk.tsx:3)。领域数据则确实只走 SDK。 - 两条 SDK 线。旧的
packages/sdk/js由openapi.json生成(./packages/sdk/js/script/build.ts),TUI、插件、run命令用的都是它;新的packages/client从@opencode-ai/server/api这份 HttpApi 定义生成(在packages/client里跑bun run generate),sdk-next在其上组合。
V1 与 V2 各住在哪
| V1(现役) | V2(迁移中) | |
|---|---|---|
| 会话主循环 | packages/opencode/src/session:prompt.ts、llm.ts、processor.ts | packages/core/src/session:execution、runner、run-coordinator.ts、context-epoch.ts |
| HTTP 路由 | packages/opencode/src/server/routes/instance/httpapi:/session/... 等 | packages/protocol 定义、packages/server 实现:/api/... |
| 客户端 | packages/sdk/js | packages/client、packages/sdk-next |
| CLI | opencode | lildax(守护进程模式) |
| 数据契约 | packages/schema/src/v1(SessionV1、PermissionV1 等) | packages/schema 根导出的当前契约 |
两代并不是各跑各的。V1 的持久化已经交给 core:Session.updatePart 发布的 message.part.updated 是带 durable 声明的事件,由 core 的 SessionProjector 写进 SQLite 的 PartTable(packages/core/src/session/projector.ts:310)。现役服务端也已经挂上了 V2:HttpApiApp.createRoutes 把旧路由与 serverRoutes 合在一起,并提供换上本地执行实现的 SessionV2 层(packages/opencode/src/server/routes/instance/httpapi/server.ts:276,删减后):
return Layer.mergeAll(
rootApiRoutes,
eventApiRoutes,
ptyConnectApiRoutes,
instanceRoutes,
serverRoutes,
docRoute,
uiRoute,
).pipe(
// ...
Layer.provide(
AppNodeBuilderV1.build(SessionV2.node, [
[LocationServiceMap.node, locationServiceMapV2],
[SessionExecution.node, SessionExecutionLocal.node],
]),
),
// ...
)serverRoutes 就是 HttpApiBuilder.layer(Api),Api 来自 @opencode-ai/server/api(同文件第 177 行)。一个 opencode serve 进程同时提供旧的 /session/... 与新的 /api/...。V2 内核见 V2 会话内核。
存储同样收拢到 core:SQLite + Drizzle,表定义分散在 packages/core/src 下的 *.sql.ts 与 sql.ts 里(packages/core/drizzle.config.ts 两种文件名都收),迁移在 packages/core/src/database/migration/,数据库默认是全局数据目录下的 opencode.db,可用 OPENCODE_DB 改(packages/core/src/database/database.ts:44)。旧的 packages/opencode/src/storage/db.ts 已删除,细节见存储:SQLite 与事件投影。
关键外部依赖
| 依赖 | 版本 | 用在哪 |
|---|---|---|
effect、@effect/platform-node | 4.0.0-beta.83 | 全仓的服务组织、Schema、HTTP 服务端(effect/unstable/httpapi) |
ai 与 @ai-sdk/* | ai 6.0.168 | 默认的模型调用链路;opencode 与 core 都直接依赖一批 provider 包 |
@opentui/core、@opentui/solid、@opentui/keymap | 0.4.5 | TUI 渲染、布局与键位 |
solid-js | 1.9.10 | TUI 与 Web 界面的响应式 |
drizzle-orm | 1.0.0-rc.2 | SQLite 表定义与查询(core 另依赖 @effect/sql-sqlite-bun) |
tree-sitter-bash、tree-sitter-powershell、web-tree-sitter | 0.25.x | Shell 工具解析命令(packages/opencode/src/tool/shell.ts) |
vscode-jsonrpc | 8.2.1 | LSP 客户端(packages/opencode/src/lsp/client.ts) |
@parcel/watcher | 2.5.1 | 文件监听(packages/core/src/filesystem/watcher.ts) |
@modelcontextprotocol/sdk | 1.29.0 | MCP 客户端 |
@agentclientprotocol/sdk | 0.21.0 | opencode acp |
bonjour-service | 1.3.0 | mDNS 服务发布(packages/opencode/src/server/mdns.ts) |
yargs | 18.0.0 | 旧 CLI 的命令解析 |
一个细节:服务端没有用 Hono 或 Express,而是 Effect 自带的 effect/unstable/httpapi 加 @effect/platform-node 的 Node HTTP 服务器——路由、校验、OpenAPI 生成都出自同一份 HttpApi 定义。这是后面HTTP 服务端一篇的主角。
下一篇:怎么读这份源码——Effect 的 Service、Layer 与 LayerNode,实验开关,以及一条推荐的阅读路线。