workspace 全景 · 153 个成员怎么分层

codex-rs 的 members 列了 153 个 crate,按职责可以归成十二组:入口与终端界面、app-server 与传输、内核、工具与执行、沙箱与安全、扩展机制等。依赖方向很清楚:前端只认 app-server,app-server 驱动 codex-core,codex-core 再把工具、沙箱、MCP、模型调用、持久化分给几十个卫星 crate;TUI 甚至不直接依赖 codex-core。

作者 David更新于 第 3 篇(共 57 篇)

workspace 全景 · 153 个成员怎么分层

打开 codex-rs/Cargo.toml,[workspace] 的 members 列了 153 项。第一次看会晕,但它们不是平铺的:按职责能归成十来组,依赖方向也很清楚。这一篇给出一张分层地图,后面任何一个功能,都能先在这张图上定位到 crate,再翻进去读。

代码量沿用专栏封面的口径:只数 .rs 文件的物理行,去掉 tests.rs、*_tests.rs、tests/ 目录与 #[cfg(test)] 块。153 个成员合计 4,770 个 .rs 文件、约 190 万行,去掉测试约 82 万行。

目录与命名

153 个成员里,108 个直接放在 codex-rs/ 下,其余是三个子目录:

  • utils/:26 个小工具库,如 utils/absolute-path、utils/pty、utils/stream-parser;
  • ext/:16 个扩展 crate,建在扩展接口 codex-extension-api(就在 ext/extension-api)之上,如 ext/goal、ext/memories、ext/mcp;
  • 另有 memories/read、memories/write 和测试辅助 exec-server/tests/support。

crate 名的规则写在根目录 AGENTS.md 第一条:目录名加 codex- 前缀,core 目录就是 codex-core。这条规矩不靠自觉,CI 脚本会逐个核对:

def expected_package_name(path: Path) -> str | None:
    parts = path.relative_to(CARGO_RS_ROOT).parts
    if len(parts) == 2 and parts[1] == "Cargo.toml":
        directory = parts[0]
        return TOP_LEVEL_NAME_EXCEPTIONS.get(
            directory,
            directory if directory.startswith("codex-") else f"codex-{directory}",
        )
    if len(parts) == 3 and parts[0] == "utils" and parts[2] == "Cargo.toml":
        directory = parts[1]
        return UTILITY_NAME_EXCEPTIONS.get(directory, f"codex-utils-{directory}")
    return None

(.github/scripts/verify_cargo_workspace_manifests.py:211)

顶层目录是 codex-<目录名>(目录本身已带 codex- 前缀的,如 codex-api、codex-mcp,原样保留),utils/ 下是 codex-utils-<目录名>;登记在案的例外只有两个:windows-sandbox-rs 叫 codex-windows-sandbox,utils/path-utils 叫 codex-utils-path。ext/ 不在检查范围内,名字多是 codex-<名>-extension 的形式,例如 ext/goal 是 codex-goal-extension、ext/items 是 codex-extension-items,搜代码时要留意。

members 并不是全部。chatgpt、message-history、windows-sandbox-rs 三个目录没列在里面,但它们是 workspace 目录内的 path 依赖,Cargo 会自动把它们算作成员,测试辅助 crate core_test_support、app_test_support 也一样。其中 Windows 沙箱 windows-sandbox-rs 本身就有约 1.9 万行非测试代码,上面的总数没有计入它们。

按职责分成十二组

下面的分组是本文按职责划的,不是仓库里的正式分类,但每个成员都恰好落在一组里:

分组成员数非测试代码代表成员
入口与终端界面10约 24.2 万行tui、cli、exec、cloud-tasks、file-search、实时语音的 realtime-webrtc 与 voice-host
app-server 与传输12约 8.4 万行app-server、app-server-protocol、app-server-transport、app-server-daemon、app-server-client、uds
内核7约 13.8 万行core、protocol、core-api、context-fragments、prompts、history
工具与执行11约 5.7 万行exec-server、tools、shell-command、apply-patch、file-system、code-mode 一族
沙箱、网络与安全10约 3.8 万行network-proxy、linux-sandbox、sandboxing、bwrap、execpolicy、guardian-context
扩展机制30约 11.3 万行core-plugins、rmcp-client、codex-mcp、hooks、skills、plugin、ext/ 下 16 个、memories/*、external-agent-migration
模型与认证16约 2.9 万行login、codex-api、codex-client、model-provider、models-manager、ollama、lmstudio
配置5约 2.1 万行config、features、cloud-config
持久化5约 4.5 万行thread-store、state、rollout、rollout-trace
遥测与反馈5约 1.5 万行analytics、otel、feedback
云端后端客户端4约 0.5 万行backend-client、codex-backend-openapi-models、cloud-tasks-client
基础库与工程辅助38约 2.9 万行utils/ 下 26 个、http-client、arg0、install-context、git-utils、test-binary-support

两个数字值得多看一眼。“入口与终端界面”一组占了近三成代码,几乎全是 tui 一个 crate;“扩展机制”成员最多,这是后面第 5 部分的地盘。

依赖怎么流

按各 crate Cargo.toml 的 [dependencies](含平台相关依赖,不含 dev-dependencies)把主干画出来,箭头指向被依赖的一方,图里只画了主干边:

图表加载中…

从图里能读出几条贯穿全书的事实。

TUI 不依赖 codex-core。 codex-rs/tui/Cargo.toml 的依赖里有 codex-app-server-client、codex-app-server-protocol、codex-app-server-daemon,却没有 codex-core:会话里的一切都要变成对 app-server 的请求。还没迁完的几条启动与配置路径,经客户端 crate 里的一个过渡模块转手:

/// Transitional access to core-only embedded app-server types.
///
/// New TUI behavior should prefer the app-server protocol methods. This
/// module exists so clients can remove a direct `codex-core` dependency
/// while legacy startup/config paths are migrated to RPCs.
pub mod legacy_core {
    pub mod config {
        pub use codex_core::config::*;

        pub mod edit {
            pub use codex_core::config::edit::*;
        }
    }
}

(codex-rs/app-server-client/src/lib.rs:71)

codex-exec 还直接依赖 codex-core,但它的运行同样经由进程内的 app-server。这个“前端只认 app-server”的结构,是一条消息的生命周期和整个第 1 部分的出发点。

codex-core 是依赖的枢纽。 它的 [dependencies] 里有 68 个本仓库 crate;153 个成员中有 23 个直接依赖它,直接依赖 codex-protocol 的则有 76 个。codex-protocol 定义了内核与外界之间的词汇:提交给内核的 Op、内核发出的 EventMsg、与模型往来的 ResponseItem,几乎人人都要用。

卫星 crate 是刻意拆出来的。 AGENTS.md 专门有一节要求抵制往 codex-core 里加代码(见读源码之前),于是工具规格(codex-tools)、上下文片段(codex-context-fragments)、提示词模板(codex-prompts)、扩展接口(codex-extension-api)都各自成 crate。扩展 crate 的依赖方向也值得注意:codex-extension-api 本身不依赖 codex-core;ext/ 下需要内核能力的扩展(goal、memories、mcp、web-search 等)依赖 codex-core,由 app-server 在 codex-rs/app-server/src/extensions.rs 里装配;codex-core 自己只直接依赖其中少数几个,如 codex-skills-extension、codex-guardian-reviewer。

Linux 沙箱是反着挂的。 不是 codex-sandboxing 依赖 codex-linux-sandbox,而是反过来:沙箱助手是 codex-arg0 在程序名为 codex-linux-sandbox 时调起的独立入口(见上一篇),它的 bubblewrap 由 codex-bwrap 从 codex-rs/vendor/bubblewrap 下的 C 源码编出来。

最大的几个 crate

crate非测试代码做什么本专栏
codex-tui205,740 行终端界面第 6 部分
codex-core113,796 行agent 内核:线程、Session、run_turn、工具路由与编排第 2、3 部分
codex-app-server43,575 行所有前端共用的服务端第 1 部分
codex-exec-server26,595 行命令执行与文件操作服务,可以跑在另一台机器上exec-server
codex-core-plugins22,632 行插件与 marketplace插件
codex-app-server-protocol21,360 行app-server 协议类型与 schema 生成v2 协议
codex-cli20,670 行命令行入口与各子命令本部分
codex-protocol20,256 行内核协议 Op、EventMsg、ResponseItem 等全书
codex-config17,443 行分层配置加载配置系统
codex-thread-store16,649 行线程存储会话持久化
codex-network-proxy16,437 行联网权限的代理网络代理
codex-rmcp-client15,562 行MCP 客户端的传输与 OAuthMCP 客户端

另一头是一批小 crate:前端与服务端之间的整个客户端层 codex-app-server-client 不到 1,900 行,codex-arg0 约 540 行;最小的 codex-collaboration-mode-templates 只有两行 Rust,用 include_str! 把 default.md、plan.md 两份协作模式模板编进二进制。

作为参照,Grok Build 的 workspace 是 96 个 crate,OpenCode 是 36 个 TypeScript 包。Codex 的成员数最多,从上面的分组看,很大一部分来自内核周边拆出来的小 crate 和 utils/ 下的工具库。


上一篇:从源码构建与运行 · Cargo、Bazel 与 npm 包装 · 下一篇:读源码之前 · 仓库的工程约定

本页目录