主要面向用户的启动入口
这一层是用户敲下 codex 后最先到达的“总服务台”。main.rs 先看用户想做什么:开终端界面、跑一次性任务、登录、云任务、远程服务、桌面应用、沙箱调试或体检。各个 cli.rs 像点菜单,规定能填哪些选项;命令文件像分诊员,把参数交给真正干活的模块。doctor、archive、mcp、apply 等则把常用维护操作串成可直接使用的流程。
根 CLI 路由
这些文件定义主要的 Codex 命令界面,以及将用户引导到该阶段主要启动路径的共享 CLI 类型。
cli/src/lib.rs源码 ↗
这个文件主要解决两个问题。第一,它告诉命令行解析工具 clap(一个把用户输入的命令行参数变成 Rust 结构体的库)该怎么理解几个沙箱命令:macOS 的 Seatbelt、Linux 的 Landlock、Windows 的受限沙箱。比如用户可以传权限配置名、配置 profile、工作目录、是否包含托管配置,以及真正要放进沙箱里运行的命令。第二,它把登录、登出和各平台沙箱运行函数重新导出,让别的地方不用知道这些函数原本藏在哪个子模块里。这里基本不真正执行命令,而是定义“菜单”和“表格”:用户输入什么参数、哪些参数互相依赖、哪些参数可以重复出现。比较特别的是 macOS 的 Seatbelt 命令还允许指定 Unix socket 路径,并会把相对路径转成绝对路径,避免后面执行时路径含糊不清。
parse_allow_unix_socket_path69–72 ↗
fn parse_allow_unix_socket_path(raw: &str) -> Result<AbsolutePathBuf, String>
作用:这个函数专门检查并整理用户通过 --allow-unix-socket 传进来的路径。它会把用户写的路径变成绝对路径,避免后续沙箱判断 socket 访问权限时因为“相对路径从哪里算起”而出错。
数据流:输入是一段用户在命令行里写的路径字符串。函数把这段字符串交给 AbsolutePathBuf::relative_to_current_dir,也就是按当前目录为基准,把相对路径补成绝对路径;如果成功,就输出整理好的绝对路径对象;如果失败,就输出一段带有原始路径和错误原因的提示文字。它本身不改全局状态,只负责把一段文字路径变成更可靠的路径值。
调用关系:它被 clap 当作 --allow-unix-socket 参数的专用转换器使用。也就是说,用户启动 Seatbelt 沙箱命令并传这个参数时,命令行解析阶段会先调用它;它再把具体的路径转换工作交给 relative_to_current_dir。转换成功后,结果会进入 SeatbeltCommand.allow_unix_sockets,供后面的沙箱运行逻辑使用。
调用图:调用 1 个内部函数(relative_to_current_dir)。
cli/src/main.rs源码 ↗
可以把这个文件理解成 Codex 的“前台接待和调度台”。用户敲下 codex ... 后,程序先在这里读懂参数,合并全局配置、功能开关和子命令自己的设置,再决定下一步该交给谁做。没有它,很多功能模块虽然存在,但用户的命令无法被正确解释,也容易出现把只适合交互界面的参数传给后台命令这类混乱。它还负责一些收尾工作,比如打印退出提示、显示会话恢复命令、执行自动更新、在错误时用正确退出码结束。这个文件不只启动主界面,也守住边界:哪些命令能用远程模式,哪些能用严格配置,哪些危险操作必须确认,都在这里先检查。 这个文件就像 codex 命令的“前台接待”。用户在终端里输入 codex resume、codex app-server、--remote 之类的内容后,它要判断用户想做什么、参数放在哪里、哪些组合合法,最后把这些信息整理成内部配置。这里有几类关键工作:一是把远程地址和认证令牌凑成可连接的端点,避免把密钥用在不安全地址上;二是把 resume、fork、archive 这些子命令自己的参数合并回交互式界面的配置里;三是生成 shell 补全;四是用大量测试固定命令行行为,防止以后改代码时把老用户习惯、权限限制或安全检查弄坏。
SessionTuiCli::augment_args401–403 ↗
fn augment_args(cmd: clap::Command) -> clap::Command
作用:给会话相关命令补上交互界面的通用命令行参数,同时规定 prompt 和 last 不能一起用。这样用户不会一边指定新提示词,一边又要求恢复最后会话造成歧义。
数据流:输入一个 clap 命令定义 → 先套用普通 TUI 的参数规则 → 再把 prompt 参数改成和 last 冲突 → 输出更新后的命令定义。
调用关系:这是命令行解析阶段用的小包装。它借用 TuiCli::augment_args 的大部分规则,只在会话场景额外加一条防误用限制。
调用图:外部调用 1 个(augment_args)。
SessionTuiCli::augment_args_for_update405–407 ↗
fn augment_args_for_update(cmd: clap::Command) -> clap::Command
作用:在 clap 需要更新已有参数定义时,给会话命令套用同样的参数规则。重点也是避免 prompt 和 last 同时出现。
数据流:输入已有命令定义 → 调用 TUI 的更新版参数扩展 → 修改 prompt 的冲突关系 → 返回改好的定义。
调用关系:它和 SessionTuiCli::augment_args 是同一类工具,只是用于 clap 内部“更新参数”的路径。
调用图:外部调用 1 个(augment_args_for_update)。
SessionTuiCli::from_arg_matches411–413 ↗
fn from_arg_matches(matches: &clap::ArgMatches) -> Result<Self, clap::Error>
作用:把命令行解析出来的结果,转换成会话命令能使用的配置对象。它本质上是给普通 TUI 配置包了一层会话专用外壳。
数据流:输入 clap 解析出的参数匹配结果 → 交给 TuiCli::from_arg_matches 解析 → 成功后包装成 SessionTuiCli,失败就返回参数错误。
调用关系:命令行库在构造 SessionTuiCli 时会用到它。它不重新解析细节,而是复用普通交互界面的解析能力。
调用图:外部调用 1 个(from_arg_matches)。
SessionTuiCli::update_from_arg_matches415–417 ↗
fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> Result<(), clap::Error>
作用:把新的命令行参数更新到已有的会话 TUI 配置里。适合 clap 在多层参数合并时使用。
数据流:输入已有 SessionTuiCli 和新的参数匹配结果 → 把更新动作转交给内部的 TuiCli → 成功则原对象被更新,失败则返回参数错误。
调用关系:这是 SessionTuiCli 作为命令行参数类型时的配套函数,真正干活的是里面的 TuiCli。
parse_socket_path697–700 ↗
fn parse_socket_path(raw: &str) -> Result<AbsolutePathBuf, String>
作用:把用户输入的 socket 路径转成绝对路径。socket 可以理解成程序之间通信用的“本机插座”,路径不明确会导致连错地方或找不到。
数据流:输入一段路径文字 → 按当前目录解析成绝对路径 → 成功返回路径对象,失败返回带原始路径的错误说明。
调用关系:它通常在解析需要连接本地服务 socket 的命令参数时使用,先把路径规范好,再交给后面的通信代码。
调用图:调用 1 个内部函数(relative_to_current_dir)。
format_exit_messages702–728 ↗
fn format_exit_messages(exit_info: AppExitInfo, color_enabled: bool) -> Vec<String>
作用:把 Codex 退出时需要给用户看的补充信息整理成一行行文字。比如本次用了多少 token,或者如何继续这个会话。
数据流:输入退出信息和是否支持彩色输出 → 检查是否有 token 用量、恢复命令、会话 ID → 生成字符串列表,不直接打印。
调用关系:handle_app_exit 会调用它来准备收尾提示。测试也会检查它在不同退出场景下是否给出正确文字。
调用图:被 7 处调用(handle_app_exit, format_exit_messages_applies_color_when_enabled, format_exit_messages_includes_resume_hint_for_fatal_exit, format_exit_messages_includes_resume_hint_without_color, format_exit_messages_includes_session_id_for_fatal_exit_without_resume_hint, format_exit_messages_names_picker_item_when_thread_has_name, format_exit_messages_skips_zero_usage);外部调用 3 个(new, format!, matches!)。
handle_app_exit731–753 ↗
fn handle_app_exit(exit_info: AppExitInfo) -> anyhow::Result<()>
作用:处理交互界面结束后的收尾。它负责打印错误、显示恢复提示、必要时执行更新,并在致命错误时用失败状态退出程序。
数据流:输入应用退出信息 → 判断是不是严重错误 → 打印错误和 format_exit_messages 生成的提示 → 如果严重就退出进程,否则按需运行更新动作 → 返回成功或错误。
调用关系:cli_main 在交互式 TUI 结束后调用它。它把用户能看到的最后提示和进程退出码统一收口。
调用图:调用 2 个内部函数(format_exit_messages, run_update_action);被 1 处调用(cli_main);外部调用 5 个(eprintln!, println!, stdout, exit, on)。
run_update_action756–796 ↗
fn run_update_action(action: UpdateAction) -> anyhow::Result<()>
作用:实际执行 Codex 的更新命令。它会根据安装方式和操作系统选择正确的外部命令来跑。
数据流:输入一个更新动作 → 打印将要执行的命令 → 在 Windows 或非 Windows 上用不同方式启动外部进程 → 检查退出状态 → 成功打印完成提示,失败返回错误。
调用关系:handle_app_exit 可在退出时触发它,run_update_command 也会直接调用它。它是“决定更新后真正开跑”的那一步。
调用图:调用 3 个内部函数(normalize_for_wsl, command_args, command_str);被 2 处调用(handle_app_exit, run_update_command);外部调用 3 个(bail!, new, println!)。
run_update_command798–815 ↗
fn run_update_command() -> anyhow::Result<()>
作用:处理用户输入的 codex update。它先确认当前构建版本能不能更新,再判断 Codex 是怎么安装的。
数据流:没有业务输入 → 调试版直接报错 → 发布版尝试获取合适的更新动作 → 找到就交给 run_update_action,找不到就提示手动更新。
调用关系:cli_main 在匹配到 update 子命令时调用它。它负责选择更新方案,执行细节交给 run_update_action。
调用图:调用 1 个内部函数(run_update_action);被 1 处调用(cli_main);外部调用 2 个(bail!, get_update_action)。
run_execpolicycheck817–819 ↗
run_session_archive_cli_command821–852 ↗
async fn run_session_archive_cli_command(
action: codex_tui::SessionArchiveAction,
cmd: SessionArchiveCommand,
mut interactive: TuiCli,
root_config_overrides: CliConfigOverrides,
r
作用:统一处理归档、删除、取消归档这类会话整理命令。它把交互配置、远程端点和目标会话拼好,再交给 TUI 模块执行。
数据流:输入动作、会话命令、交互配置、全局配置、远程信息和程序路径 → 合并配置 → 解析远程连接信息 → 调用会话归档命令 → 返回要打印给用户的文字。
调用关系:cli_main 在 archive、delete、unarchive 分支都会调用它。它避免三种命令重复写同样的准备流程。
调用图:调用 2 个内部函数(finalize_session_archive_interactive, resolve_remote_endpoint);被 1 处调用(cli_main);外部调用 1 个(run_session_archive_command)。
delete_action854–863 ↗
fn delete_action(target: &str, force: bool) -> anyhow::Result<codex_tui::SessionArchiveAction>
作用:根据用户是否加了 --force,决定删除会话时要不要再确认。它还防止用户用名字强制删除,因为名字可能不唯一。
数据流:输入删除目标和是否强制 → 如果强制但目标不是会话 UUID 就报错 → 生成“跳过确认”或“需要提示确认”的删除动作 → 返回动作。
调用关系:cli_main 在 delete 子命令中先调用它,再把结果交给 run_session_archive_cli_command。测试也会验证强制删除必须用 UUID。
调用图:调用 1 个内部函数(from_string);被 2 处调用(cli_main, delete_force_requires_uuid);外部调用 2 个(bail!, Delete)。
run_debug_app_server_command865–873 ↗
async fn run_debug_app_server_command(cmd: DebugAppServerCommand) -> anyhow::Result<()>
作用:运行 app-server 相关的调试命令。这里展示的路径是发送一条 V2 测试消息到应用服务器。
数据流:输入调试命令 → 根据子命令取当前 Codex 可执行文件路径 → 调用测试客户端发送用户消息 → 返回发送结果。
调用关系:cli_main 在 debug app-server 下调用它。它把调试入口连接到 app-server 测试客户端。
调用图:被 1 处调用(cli_main);外部调用 2 个(send_message_v2, current_exe)。
FeatureToggles::to_overrides901–912 ↗
fn to_overrides(&self) -> anyhow::Result<Vec<String>>
作用:把命令行里的 --enable 和 --disable 功能开关,转换成配置覆盖项。这样后面的所有命令都能像读配置文件一样读到它们。
数据流:读取对象里的启用列表和禁用列表 → 逐个校验功能名是否存在 → 生成 features.xxx=true/false 这种配置字符串 → 返回列表。
调用关系:cli_main 启动后很早调用它,把功能开关并入全局配置覆盖。校验工作交给 FeatureToggles::validate_feature。
调用图:外部调用 3 个(validate_feature, new, format!)。
FeatureToggles::validate_feature914–920 ↗
fn validate_feature(feature: &str) -> anyhow::Result<()>
作用:检查用户写的功能开关名字是不是 Codex 认识的。拼错或不存在就立刻报错,避免悄悄无效。
数据流:输入功能名 → 查询是否是已知功能键 → 是就通过,不是就返回“未知功能开关”的错误。
调用关系:FeatureToggles::to_overrides、enable_feature_in_config 和 disable_feature_in_config 都会用它把关。
调用图:被 2 处调用(disable_feature_in_config, enable_feature_in_config);外部调用 2 个(bail!, is_known_feature_key)。
stage_str945–953 ↗
fn stage_str(stage: Stage) -> &'static str
作用:把功能阶段这种内部枚举,变成用户看得懂的英文短语。比如稳定、实验中、已废弃。
数据流:输入一个阶段值 → 按类型匹配 → 输出固定文字。
调用关系:cli_main 在 features list 打印功能列表时用它,让表格里显示友好的阶段名称。
调用图:被 1 处调用(cli_main)。
main955–961 ↗
fn main() -> anyhow::Result<()>
作用:这是整个 Codex 命令行程序真正的入口。操作系统启动程序后,最先进入这里。
数据流:启动时读取远程控制禁用状态 → 设置按程序名分发的入口包装 → 最终调用异步的 cli_main → 返回程序整体结果。
调用关系:它是最外层壳子,具体命令解析和分发都交给 cli_main。
调用图:外部调用 2 个(take_remote_control_disabled_env, arg0_dispatch_or_else)。
cli_main963–1647 ↗
async fn cli_main(
arg0_paths: Arg0DispatchPaths,
remote_control_disabled: bool,
) -> anyhow::Result<()>
作用:这是命令行的总调度函数。它读懂用户输入的子命令,然后把任务交给对应模块,比如 TUI、exec、登录、服务、沙箱、调试或功能开关。
数据流:输入程序路径信息和远程控制状态 → 解析命令行 → 合并全局配置、功能开关、远程参数和子命令参数 → 按子命令分支执行对应功能 → 最后返回成功或错误。
调用关系:main 调用它。它是本文件的中心枢纽,调用大量辅助函数做参数检查、配置合并和收尾,也把真正业务交给各个外部模块。
调用图:调用 45 个内部函数(run_apply_command, run_app, delete_action, disable_feature_in_config, run_doctor, enable_feature_in_config, finalize_fork_interactive, finalize_resume_interactive, handle_app_exit, loader_overrides_for_profile (+15 more));外部调用 28 个(default, try_parse_from, with_capacity, bail!, clone, parse, Review, app_server_control_socket_path, run_main_with_transport_options, bootstrap (+15 more))。
profile_v2_for_subcommand1649–1674 ↗
fn profile_v2_for_subcommand(
interactive: &'a TuiCli,
subcommand: &Subcommand,
) -> anyhow::Result<Option<&'a ProfileV2Name>>
作用:检查 --profile 这个配置档参数能不能用于当前子命令。配置档只适合会真正加载运行配置的命令,不适合所有命令。
数据流:输入交互配置和子命令 → 如果没指定 profile 就返回空 → 如果子命令支持就返回 profile 名 → 不支持就报错说明允许范围。
调用关系:cli_main 在执行子命令前调用它,测试辅助也会用它。它防止用户把配置档传给无意义的命令。
调用图:被 2 处调用(cli_main, profile_v2_for_args);外部调用 1 个(bail!)。
run_exec_server_command1676–1723 ↗
async fn run_exec_server_command(
cmd: ExecServerCommand,
arg0_paths: &Arg0DispatchPaths,
root_config_overrides: &CliConfigOverrides,
strict_config: bool,
) -> anyhow::Result<()>
作用:启动执行服务器,可以是本地监听,也可以注册到远程环境。执行服务器负责替其他组件运行命令。
数据流:输入 exec-server 命令、程序路径、配置覆盖和严格配置开关 → 准备运行时路径 → 如果是远程模式就加载配置和认证并注册远程环境 → 否则启动本地服务监听。
调用关系:cli_main 在 exec-server 子命令下调用它。远程认证交给 load_exec_server_remote_auth_provider,配置读取交给 load_exec_server_config。
调用图:调用 4 个内部函数(load_exec_server_config, load_exec_server_remote_auth_provider, new, new);被 1 处调用(cli_main);外部调用 2 个(run_main, run_remote_environment)。
load_exec_server_remote_auth_provider1725–1757 ↗
async fn load_exec_server_remote_auth_provider(
config: &codex_core::config::Config,
base_url: &str,
use_agent_identity_auth: bool,
) -> anyhow::Result<codex_api::SharedAuthProvider>
作用:为远程 exec-server 准备认证提供者。认证提供者可以理解成“之后请求时怎么证明你是谁”的对象。
数据流:输入配置、远程地址和是否使用 agent identity → 如果用 agent identity 就从环境变量读令牌并转换认证 → 否则加载登录/API key 认证并检查是否允许 → API key 还会校验远程主机安全性 → 返回共享认证提供者。
调用关系:run_exec_server_command 只在远程模式下调用它。它会调用认证加载、认证类型判断和远程地址校验函数。
调用图:调用 4 个内部函数(is_supported_exec_server_remote_auth, load_exec_server_remote_auth, validate_api_key_remote_host, from_agent_identity_jwt);被 1 处调用(run_exec_server_command);外部调用 3 个(bail!, read_codex_access_token_from_env, auth_provider_from_auth)。
is_supported_exec_server_remote_auth1759–1761 ↗
fn is_supported_exec_server_remote_auth(auth: &CodexAuth) -> bool
作用:判断某种认证方式能不能用于远程 exec-server 注册。当前普通 ChatGPT 登录和 API key 可以,其他方式默认不行。
数据流:输入一个认证对象 → 检查它是不是 ChatGPT 认证或 API key 认证 → 返回布尔值。
调用关系:load_exec_server_remote_auth_provider 用它做白名单检查,避免不合适的认证方式被误用。
调用图:调用 2 个内部函数(is_api_key_auth, is_chatgpt_auth);被 1 处调用(load_exec_server_remote_auth_provider)。
validate_api_key_remote_host1763–1795 ↗
fn validate_api_key_remote_host(base_url: &str) -> anyhow::Result<()>
作用:限制 API key 认证能发往哪些远程地址,防止把密钥送到危险网站。规则是只允许 OpenAI 的 HTTPS 域名,或本机回环地址。
数据流:输入远程 URL 字符串 → 解析协议和主机 → 判断是不是 localhost、本机 IP、openai.com 或 openai.org 及其子域名 → 不符合就报错,符合就通过。
调用关系:load_exec_server_remote_auth_provider 在使用 API key 认证时调用它。相关测试会验证 HTTP OpenAI 域名和伪装后缀会被拒绝。
调用图:被 3 处调用(load_exec_server_remote_auth_provider, exec_server_remote_api_key_auth_rejects_http_openai_domain, exec_server_remote_api_key_auth_rejects_suffix_spoof);外部调用 2 个(bail!, parse)。
load_exec_server_config1797–1809 ↗
async fn load_exec_server_config(
root_config_overrides: &CliConfigOverrides,
strict_config: bool,
) -> anyhow::Result<codex_core::config::Config>
作用:为 exec-server 加载 Codex 配置。它会把命令行 -c key=value 这类覆盖项也算进去。
数据流:输入全局配置覆盖和是否严格配置 → 解析覆盖项 → 用配置构建器加载完整配置 → 返回配置对象。
调用关系:run_exec_server_command 在远程模式和严格本地模式下调用它。它是 exec-server 进入正式运行前的配置入口。
调用图:调用 1 个内部函数(parse_overrides);被 1 处调用(run_exec_server_command);外部调用 1 个(default)。
load_exec_server_remote_auth1811–1830 ↗
async fn load_exec_server_remote_auth(
config: &codex_core::config::Config,
missing_auth_error: &'static str,
) -> anyhow::Result<codex_login::CodexAuth>
作用:从当前配置里加载远程 exec-server 需要的登录信息。如果一开始没读到,会重新加载一次再判断。
数据流:输入配置和缺少认证时的错误文字 → 创建认证管理器 → 尝试读取认证 → 没有就 reload 后再读 → 仍没有就报错 → 返回认证对象。
调用关系:load_exec_server_remote_auth_provider 调用它取得基础认证,再转换成可共享的认证提供者。
调用图:调用 1 个内部函数(shared_from_config);被 1 处调用(load_exec_server_remote_auth_provider)。
enable_feature_in_config1832–1842 ↗
async fn enable_feature_in_config(feature: &str) -> anyhow::Result<()>
作用:把某个功能开关写入用户的 config.toml,设置为启用。这样以后不用每次命令行都写 --enable。
数据流:输入功能名 → 校验功能名 → 找到 Codex 配置目录 → 修改配置文件中的功能开关为 true → 打印成功消息 → 如果是开发中功能,再打印风险提醒。
调用关系:cli_main 在 features enable 子命令下调用它。它复用 FeatureToggles::validate_feature 和开发中功能提醒函数。
调用图:调用 4 个内部函数(validate_feature, maybe_print_under_development_feature_warning, new, find_codex_home);被 1 处调用(cli_main);外部调用 1 个(println!)。
disable_feature_in_config1844–1853 ↗
async fn disable_feature_in_config(feature: &str) -> anyhow::Result<()>
作用:把某个功能开关写入用户配置,设置为关闭。用于长期禁用某个功能。
数据流:输入功能名 → 校验功能名 → 找到 Codex 配置目录 → 修改配置文件中的功能开关为 false → 打印成功消息。
调用关系:cli_main 在 features disable 子命令下调用它。它和启用函数是一对,只是写入的值相反。
调用图:调用 3 个内部函数(validate_feature, new, find_codex_home);被 1 处调用(cli_main);外部调用 1 个(println!)。
loader_overrides_for_profile1855–1869 ↗
fn loader_overrides_for_profile(
profile_v2: Option<&ProfileV2Name>,
) -> anyhow::Result<LoaderOverrides>
作用:根据用户指定的 profile,生成配置加载器的覆盖设置。profile 可以理解成一套独立配置档。
数据流:输入可选 profile 名 → 如果有,就找到 Codex 主目录并算出该 profile 的配置文件路径 → 返回带路径和 profile 名的加载覆盖;如果没有就返回默认覆盖。
调用关系:cli_main 在 mcp、sandbox 等路径会用它,run_debug_prompt_input_command 也用它来按同一配置档构造调试输入。
调用图:调用 2 个内部函数(find_codex_home, resolve_profile_v2_config_path);被 2 处调用(cli_main, run_debug_prompt_input_command);外部调用 2 个(default, default)。
maybe_print_under_development_feature_warning1871–1884 ↗
fn maybe_print_under_development_feature_warning(codex_home: &std::path::Path, feature: &str)
作用:当用户启用仍在开发中的功能时,打印提醒。意思是这个功能可能还不稳定,出问题不要意外。
数据流:输入 Codex 主目录和功能名 → 在功能清单里查找 → 如果不是开发中阶段就什么也不做 → 如果是,就把配置文件路径放进警告文字并输出到错误流。
调用关系:enable_feature_in_config 在成功启用功能后调用它。它只负责提醒,不阻止用户启用。
调用图:被 1 处调用(enable_feature_in_config);外部调用 3 个(join, eprintln!, matches!)。
run_debug_trace_reduce_command1886–1897 ↗
async fn run_debug_trace_reduce_command(cmd: DebugTraceReduceCommand) -> anyhow::Result<()>
作用:把调试 trace 包重放并压缩成一个更容易查看的状态 JSON 文件。trace 可以理解成程序运行过程的记录包。
数据流:输入 trace 包路径和可选输出路径 → 没给输出就默认写到 trace 包目录下 → 重放 trace 得到简化状态 → 转成漂亮的 JSON → 写文件并打印输出路径。
调用关系:cli_main 在 debug trace-reduce 下调用它。它把复杂调试记录变成可读文件,方便排查问题。
调用图:被 1 处调用(cli_main);外部调用 4 个(replay_bundle, println!, to_vec_pretty, write)。
run_debug_prompt_input_command1899–1974 ↗
async fn run_debug_prompt_input_command(
cmd: DebugPromptInputCommand,
root_config_overrides: CliConfigOverrides,
interactive: TuiCli,
arg0_paths: Arg0DispatchPaths,
) -> anyhow::Resul
作用:生成并打印 Codex 真正会发送给核心逻辑的 prompt 输入。它用于调试:看看命令行提示词、图片、配置和用户说明合在一起后长什么样。
数据流:输入调试命令、配置覆盖、交互参数和程序路径 → 加载 profile 和配置 → 合并 web search、模型、沙箱、审批、图片、文字提示等信息 → 构造 prompt 输入对象 → 以格式化 JSON 打印。
调用关系:cli_main 在 debug prompt-input 下调用它。它会用 loader_overrides_for_profile 准备配置,并把最终构造交给核心库的 build_prompt_input。
调用图:调用 3 个内部函数(loader_overrides_for_profile, new, parse_overrides);被 1 处调用(cli_main);外部调用 7 个(new, default, new, build_prompt_input, default, println!, String)。
run_debug_models_command1976–2001 ↗
async fn run_debug_models_command(
cmd: DebugModelsCommand,
root_config_overrides: CliConfigOverrides,
) -> anyhow::Result<()>
作用:打印模型目录的原始 JSON。可以选择只看内置模型列表,也可以联网或读缓存获取真实模型目录。
数据流:输入调试模型命令和配置覆盖 → 如果指定 bundled 就取内置模型响应 → 否则加载配置和认证,创建模型管理器并取模型目录 → 把结果写到标准输出。
调用关系:cli_main 在 debug models 下调用它。它连接配置、认证和模型管理模块,输出给调试者检查。
调用图:调用 2 个内部函数(shared_from_config, parse_overrides);被 1 处调用(cli_main);外部调用 6 个(build_models_manager, bundled_models_response, default, println!, to_writer, stdout)。
run_debug_clear_memories_command2003–2033 ↗
async fn run_debug_clear_memories_command(
root_config_overrides: &CliConfigOverrides,
) -> anyhow::Result<()>
作用:清除 Codex 的记忆数据,用于调试或重置状态。记忆包括数据库里的状态和配置目录下的记忆文件夹内容。
数据流:输入配置覆盖 → 加载配置 → 找到记忆数据库路径 → 清除 SQLite 记忆数据 → 清空记忆目录内容 → 打印清理结果。
调用关系:cli_main 在 debug clear-memories 下调用它。它把数据库清理和文件夹清理两个动作串起来。
调用图:调用 2 个内部函数(clear_memory_data_in_sqlite_home, parse_overrides);被 1 处调用(cli_main);外部调用 5 个(clear_memory_roots_contents, memories_db_path, default, format!, println!)。
prepend_config_flags2037–2042 ↗
fn prepend_config_flags(
subcommand_config_overrides: &mut CliConfigOverrides,
cli_config_overrides: CliConfigOverrides,
)
作用:把全局配置覆盖项放到子命令自己的配置覆盖项前面。这样全局 -c 参数能统一传到子命令里。
数据流:输入子命令配置覆盖对象和全局覆盖对象 → 调用对象方法把全局项插到前面 → 直接修改子命令配置对象,没有单独返回值。
调用关系:cli_main 在很多子命令前调用它,恢复、分叉和会话归档的准备函数也会用它。
调用图:调用 1 个内部函数(prepend_root_overrides);被 4 处调用(cli_main, finalize_fork_interactive, finalize_resume_interactive, finalize_session_archive_interactive)。
reject_remote_mode_for_subcommand2044–2060 ↗
fn reject_remote_mode_for_subcommand(
remote: Option<&str>,
remote_auth_token_env: Option<&str>,
subcommand: &str,
) -> anyhow::Result<()>
作用:阻止不支持远程交互模式的子命令使用 --remote 或 --remote-auth-token-env。这些参数只适合交互式 TUI。
数据流:输入远程地址、远程令牌环境变量名和子命令名称 → 如果发现远程参数就返回清楚的错误 → 没有就通过。
调用关系:cli_main 在大量非交互子命令前调用它。其他更细的拒绝函数也会复用它。
调用图:被 5 处调用(cli_main, reject_remote_mode_for_app_server_subcommand, reject_remote_auth_token_env_for_non_interactive_subcommands, reject_remote_flag_for_remote_control, reject_remote_mode_for_non_interactive_subcommands);外部调用 1 个(bail!)。
reject_root_strict_config_for_subcommand2062–2076 ↗
fn reject_root_strict_config_for_subcommand(
strict_config: bool,
subcommand: &Option<Subcommand>,
) -> anyhow::Result<()>
作用:检查全局 --strict-config 是否能用于当前子命令。严格配置会拒绝未知配置项,但不是每个命令都会加载完整配置。
数据流:输入严格开关和可选子命令 → 没开严格就直接通过 → 开了就查该子命令是否支持 → 不支持则报错,支持则通过。
调用关系:cli_main 在分发前调用它。它把“哪些命令不支持严格配置”的判断交给 unsupported_subcommand_name_for_strict_config。
调用图:调用 2 个内部函数(reject_strict_config_for_unsupported_subcommand, unsupported_subcommand_name_for_strict_config);被 3 处调用(cli_main, root_strict_config_is_rejected_for_unsupported_subcommands, root_strict_config_is_supported_for_exec_server)。
unsupported_subcommand_name_for_strict_config2090–2127 ↗
fn unsupported_subcommand_name_for_strict_config(
subcommand: &Option<Subcommand>,
) -> Option<&'static str>
作用:列出哪些子命令不支持全局严格配置,并给出用于错误消息的命令名。
数据流:输入可选子命令 → 按子命令类型匹配 → 支持严格配置的返回空,不支持的返回命令名称。
调用关系:reject_root_strict_config_for_subcommand 用它来决定是否报错。app-server 的细分名称会交给 app_server_subcommand_name 生成。
调用图:调用 1 个内部函数(app_server_subcommand_name);被 1 处调用(reject_root_strict_config_for_subcommand)。
reject_strict_config_for_app_server_subcommand2129–2140 ↗
fn reject_strict_config_for_app_server_subcommand(
strict_config: bool,
subcommand: Option<&AppServerSubcommand>,
) -> anyhow::Result<()>
作用:专门检查 app-server 的子命令能不能用严格配置。直接启动 app-server 可以,但很多管理/生成类子命令不需要也不支持。
数据流:输入严格开关和 app-server 子命令 → 如果没有子命令就通过 → 有子命令则生成名称并调用通用拒绝函数 → 返回通过或错误。
调用关系:cli_main 在处理 app-server 命令时调用它。它复用 reject_strict_config_for_unsupported_subcommand。
调用图:调用 2 个内部函数(app_server_subcommand_name, reject_strict_config_for_unsupported_subcommand);被 2 处调用(cli_main, app_server_subcommands_reject_strict_config)。
reject_strict_config_for_unsupported_subcommand2142–2150 ↗
fn reject_strict_config_for_unsupported_subcommand(
strict_config: bool,
subcommand: &str,
) -> anyhow::Result<()>
作用:如果某个不支持严格配置的命令收到了 --strict-config,就给出明确错误。
数据流:输入严格开关和子命令名称 → 严格开关为真就报错 → 否则通过。
调用关系:reject_root_strict_config_for_subcommand 和 reject_strict_config_for_app_server_subcommand 都把最终报错交给它。
调用图:被 2 处调用(reject_root_strict_config_for_subcommand, reject_strict_config_for_app_server_subcommand);外部调用 1 个(bail!)。
reject_remote_mode_for_app_server_subcommand2152–2159 ↗
fn reject_remote_mode_for_app_server_subcommand(
remote: Option<&str>,
remote_auth_token_env: Option<&str>,
subcommand: Option<&AppServerSubcommand>,
) -> anyhow::Result<()>
作用:专门检查 app-server 相关子命令是否误用了远程交互参数。它会把具体 app-server 子命令名写进错误。
数据流:输入远程参数和 app-server 子命令 → 先算出用户可读的子命令名称 → 调用通用远程参数拒绝函数 → 返回通过或错误。
调用关系:cli_main 在 app-server 分支调用它。它复用 app_server_subcommand_name 和 reject_remote_mode_for_subcommand。
调用图:调用 2 个内部函数(app_server_subcommand_name, reject_remote_mode_for_subcommand);被 4 处调用(cli_main, reject_remote_auth_token_env_for_app_server_generate_internal_json_schema, reject_remote_auth_token_env_for_app_server_proxy, reject_remote_auth_token_env_for_app_server_version)。
app_server_subcommand_name2161–2185 ↗
fn app_server_subcommand_name(subcommand: Option<&AppServerSubcommand>) -> &'static str
作用:把 app-server 的内部子命令类型转换成用户看到的命令字符串。这样错误消息能准确说出是哪条命令。
数据流:输入可选 app-server 子命令 → 按不同子命令和 daemon 动作匹配 → 返回固定命令名字符串。
调用关系:多个检查函数都会用它生成错误提示里的命令名,包括远程模式检查和严格配置检查。
调用图:被 3 处调用(reject_remote_mode_for_app_server_subcommand, reject_strict_config_for_app_server_subcommand, unsupported_subcommand_name_for_strict_config)。
print_app_server_daemon_output2187–2191 ↗
async fn print_app_server_daemon_output(command: AppServerLifecycleCommand) -> anyhow::Result<()>
作用:执行 app-server 守护进程的生命周期命令,并把结果用 JSON 打印出来。守护进程就是在后台长期运行的服务。
数据流:输入启动、停止、重启、版本等生命周期命令 → 调用 daemon 模块执行 → 把返回结果转成一行 JSON → 打印。
调用关系:cli_main 在 app-server daemon 的多个子命令下调用它。它统一了这些命令的输出格式。
print_app_server_remote_control_output2193–2199 ↗
async fn print_app_server_remote_control_output(
mode: AppServerRemoteControlMode,
) -> anyhow::Result<()>
作用:开启或关闭 app-server 的远程控制设置,并把结果用 JSON 打印出来。
数据流:输入远程控制模式 → 调用 daemon 模块保存/切换设置 → 把结果转成 JSON → 打印。
调用关系:cli_main 在 enable-remote-control 和 disable-remote-control 下调用它。它让这两个管理命令输出格式一致。
调用图:被 1 处调用(cli_main);外部调用 2 个(set_remote_control, println!)。
read_remote_auth_token_from_env_var_with2201–2215 ↗
fn read_remote_auth_token_from_env_var_with(
env_var_name: &str,
get_var: F,
) -> anyhow::Result<String>
作用:从指定环境变量读取远程认证令牌,并确保它不是空的。环境变量就是操作系统给程序的一小段外部配置。
数据流:输入环境变量名和一个读取函数 → 读取变量 → 去掉前后空白 → 如果不存在或为空就报错 → 返回令牌字符串。
调用关系:read_remote_auth_token_from_env_var 会用真实环境变量读取函数调用它,测试则可以传假的读取函数来验证各种情况。
调用图:被 4 处调用(read_remote_auth_token_from_env_var, read_remote_auth_token_from_env_var_rejects_empty_values, read_remote_auth_token_from_env_var_reports_missing_values, read_remote_auth_token_from_env_var_trims_values);外部调用 1 个(bail!)。
read_remote_auth_token_from_env_var2217–2219 ↗
fn read_remote_auth_token_from_env_var(env_var_name: &str) -> anyhow::Result<String>
作用:从真实系统环境变量中读取远程认证令牌。它是实际运行时使用的简化入口。
数据流:输入环境变量名 → 调用 read_remote_auth_token_from_env_var_with 并传入 std::env::var → 返回清理后的令牌或错误。
调用关系:resolve_remote_endpoint 会调用它来取得远程 TUI 连接需要的认证令牌。
调用图:调用 1 个内部函数(read_remote_auth_token_from_env_var_with);被 1 处调用(resolve_remote_endpoint)。
run_interactive_tui2221–2298 ↗
async fn run_interactive_tui(
mut interactive: TuiCli,
remote: Option<String>,
remote_auth_token_env: Option<String>,
arg0_paths: Arg0DispatchPaths,
) -> std::io::Result<AppExitInfo>
作用:启动 Codex 的交互式终端界面,也就是用户平时聊天和操作的主界面。它还处理终端不合适、远程连接参数错误、本地数据库损坏等启动问题。
数据流:输入 TUI 配置、远程信息和程序路径 → 规范化提示词换行 → 检查终端是否可用,必要时向用户确认 → 解析远程端点 → 调用 TUI 主程序 → 如果本地状态数据库损坏且可恢复,就自动备份后重试 → 最终返回退出信息或 I/O 错误。
调用关系:cli_main 在无子命令、resume、fork 等交互路径调用它。它把真正界面运行交给 codex_tui::run_main,但自己负责启动前检查和数据库恢复兜底。
调用图:调用 4 个内部函数(confirm, is_remote_auth_usage_error, resolve_remote_endpoint, fatal);被 1 处调用(cli_main);外部调用 14 个(new, terminal_info, eprintln!, format!, backup_files_for_fresh_start, confirm_fresh_start_rebuild, is_auto_backup_recoverable, is_locked, print_auto_backup_start, print_diagnostic_guidance (+4 more))。
resolve_remote_endpoint2300–2333 ↗
fn resolve_remote_endpoint(
remote: Option<String>,
remote_auth_token_env: Option<String>,
) -> std::io::Result<Option<codex_tui::RemoteAppServerEndpoint>>
作用:把用户给的远程地址和可选的认证令牌环境变量,整理成真正可用的远程连接信息。它也会拦住不安全或不完整的用法,比如只给令牌变量却没给远程地址。
数据流:输入是 --remote 的地址字符串和 --remote-auth-token-env 的环境变量名;它先解析远程地址,再在需要时读取环境变量里的令牌,并确认地址类型支持令牌;输出是可选的远程端点,或返回一条说明哪里用错了的错误。
调用关系:交互式 TUI 和会话归档命令在准备连接远程 app server 时会调用它;它把读取令牌的细活交给 read_remote_auth_token_from_env_var,把地址安全判断交给 TUI 相关工具。
调用图:调用 1 个内部函数(read_remote_auth_token_from_env_var);被 2 处调用(run_interactive_tui, run_session_archive_cli_command);外部调用 2 个(other, remote_addr_supports_auth_token)。
is_remote_auth_usage_error2335–2338 ↗
fn is_remote_auth_usage_error(err: &std::io::Error) -> bool
作用:判断一个错误是不是远程认证参数用法错误。这样上层可以给用户更合适的提示,而不是当成普通故障处理。
数据流:输入是一条 IO 错误;它把错误转成文字,看开头是不是关于 --remote-auth-token-env 的要求;输出 true 或 false。
调用关系:交互式 TUI 启动时会用它识别 resolve_remote_endpoint 产生的参数用错错误。
调用图:被 1 处调用(run_interactive_tui);外部调用 1 个(to_string)。
confirm2340–2347 ↗
fn confirm(prompt: &str) -> std::io::Result<bool>
作用:在终端里问用户一个是/否问题。只有用户输入 y 或 yes,才算同意。
数据流:输入是一段提示文字;它打印提示,从标准输入读一行,去掉前后空白并比较答案;输出布尔值,表示用户是否确认。
调用关系:交互式流程里需要人工确认危险操作或不可逆操作时会调用它。
调用图:被 1 处调用(run_interactive_tui);外部调用 3 个(new, eprintln!, stdin)。
finalize_resume_interactive2350–2382 ↗
fn finalize_resume_interactive(
mut interactive: TuiCli,
root_config_overrides: CliConfigOverrides,
session_id: Option<String>,
last: bool,
show_all: bool,
include_non_interact
作用:把 codex resume 的参数整理成普通交互式界面能理解的一份配置。它解决的是:继续会话既像一个子命令,又要复用主界面的所有参数。
数据流:输入包括已解析的交互式配置、根级配置覆盖、会话 ID、是否继续最近会话、是否显示全部、是否包含非交互会话,以及 resume 自己的参数;它决定要不要打开会话选择器、是否把位置参数当提示词,并合并各种覆盖;输出最终的 TuiCli 配置。
调用关系:cli_main 和测试辅助 tests::finalize_resume_from_args 会调用它;它把参数合并交给 merge_interactive_cli_flags,把根级配置插到前面交给 prepend_config_flags。
调用图:调用 2 个内部函数(merge_interactive_cli_flags, prepend_config_flags);被 2 处调用(cli_main, finalize_resume_from_args)。
finalize_fork_interactive2385–2415 ↗
fn finalize_fork_interactive(
mut interactive: TuiCli,
root_config_overrides: CliConfigOverrides,
session_id: Option<String>,
last: bool,
show_all: bool,
mut fork_cli: TuiCli,
作用:把 codex fork 的参数整理成交互式界面配置,用来从旧会话分叉出新会话。它和 resume 很像,但设置的是 fork 相关开关。
数据流:输入是交互式基础配置、根级覆盖、可选会话 ID、是否使用最近会话、是否显示全部,以及 fork 子命令参数;它决定是否弹选择器、是否把参数改当提示词,并合并子命令选项;输出最终 TuiCli。
调用关系:cli_main 和测试辅助 tests::finalize_fork_from_args 会调用它;它复用 merge_interactive_cli_flags 和 prepend_config_flags 完成合并。
调用图:调用 2 个内部函数(merge_interactive_cli_flags, prepend_config_flags);被 2 处调用(cli_main, finalize_fork_from_args)。
finalize_session_archive_interactive2417–2437 ↗
fn finalize_session_archive_interactive(
mut interactive: TuiCli,
root_config_overrides: CliConfigOverrides,
archive_cli: SessionArchiveConfigOverrides,
) -> TuiCli
作用:把 codex archive 的参数合并进交互式配置,用来定位和归档某个会话。它保证 archive 子命令自己的目录、模型、严格配置等选项能覆盖默认值。
数据流:输入是交互式配置、根级配置覆盖、archive 子命令覆盖;它应用共享参数、打开严格配置、追加配置覆盖,再把根级覆盖放到合适位置;输出更新后的 TuiCli。
调用关系:归档命令运行和测试辅助 tests::finalize_archive_from_args 会调用它;它只把配置前置这一步交给 prepend_config_flags。
调用图:调用 1 个内部函数(prepend_config_flags);被 2 处调用(run_session_archive_cli_command, finalize_archive_from_args)。
merge_interactive_cli_flags2442–2473 ↗
fn merge_interactive_cli_flags(interactive: &mut TuiCli, subcommand_cli: TuiCli)
作用:把 resume 或 fork 子命令里的交互式参数合并到主交互式配置里。这样用户可以在子命令后面继续写 -m、-C、--search 等熟悉选项。
数据流:输入是一份可修改的主配置和一份子命令配置;它把共享参数、审批策略、联网搜索、严格配置、提示词和配置覆盖逐项搬过去;结果是主配置被原地更新。
调用关系:finalize_resume_interactive 和 finalize_fork_interactive 都依赖它,专门处理两类命令共同的参数合并规则。
调用图:被 2 处调用(finalize_fork_interactive, finalize_resume_interactive)。
print_completion2475–2479 ↗
tests::exec_server_remote_auth_accepts_api_key_auth2490–2494 ↗
fn exec_server_remote_auth_accepts_api_key_auth()
作用:确认 exec-server 的远程认证接受 API key。API key 是一串密钥,用来证明调用者有权限。
数据流:它造出一个测试 API key 认证对象,传给支持性检查函数;预期结果是检查通过。
调用关系:这是安全规则的单元测试,保护 is_supported_exec_server_remote_auth 的允许列表不被误改。
调用图:调用 1 个内部函数(from_api_key);外部调用 1 个(assert!)。
tests::exec_server_remote_api_key_auth_accepts_https_openai_domains2497–2506 ↗
fn exec_server_remote_api_key_auth_accepts_https_openai_domains()
作用:确认 API key 远程认证允许 HTTPS 的 OpenAI 域名。HTTPS 是加密网页连接,能避免密钥明文泄露。
数据流:输入是一组 openai.com 和 openai.org 的 HTTPS 地址;逐个交给校验函数;每个都应返回成功。
调用关系:它测试 validate_api_key_remote_host 的正向规则,保证官方域名能用。
调用图:外部调用 1 个(assert!)。
tests::exec_server_remote_api_key_auth_accepts_http_loopback2509–2517 ↗
fn exec_server_remote_api_key_auth_accepts_http_loopback()
作用:确认本机回环地址可以用 HTTP。回环地址就是只连自己电脑的地址,风险比公网低。
数据流:输入是 localhost、127.0.0.1、::1 这几种本机 HTTP 地址;逐个校验;结果都应成功。
调用关系:它补充测试 validate_api_key_remote_host,保证本地开发和测试场景不被 HTTPS 限制挡住。
调用图:外部调用 1 个(assert!)。
tests::exec_server_remote_api_key_auth_rejects_http_openai_domain2520–2533 ↗
fn exec_server_remote_api_key_auth_rejects_http_openai_domain()
作用:确认 OpenAI 域名如果用未加密 HTTP 会被拒绝。这样可以防止 API key 在网络上传明文。
数据流:输入是 HTTP 的 OpenAI 子域名;校验函数应返回错误;测试再比对错误文字是否明确。
调用关系:它守住 validate_api_key_remote_host 的安全底线。
调用图:调用 1 个内部函数(validate_api_key_remote_host);外部调用 1 个(assert_eq!)。
tests::exec_server_remote_api_key_auth_rejects_suffix_spoof2536–2544 ↗
fn exec_server_remote_api_key_auth_rejects_suffix_spoof()
作用:确认假冒后缀域名会被拒绝,比如看起来像 openai.org 但其实不是。这样避免钓鱼域名骗走密钥。
数据流:输入是 service.openai.org.evil.example;校验应失败;输出错误文字必须说明只允许官方域名或本机地址。
调用关系:它测试 validate_api_key_remote_host 的域名判断不能只做简单字符串包含。
调用图:调用 1 个内部函数(validate_api_key_remote_host);外部调用 1 个(assert_eq!)。
tests::finalize_resume_from_args2546–2578 ↗
fn finalize_resume_from_args(args: &[&str]) -> TuiCli
作用:测试用的小帮手:从一组命令行文字解析出 resume,并调用真正的整理函数。这样后面的测试不用重复写解析样板代码。
数据流:输入是模拟的命令行参数数组;它解析成 MultitoolCli,取出 resume 子命令和配置;输出最终 TuiCli。
调用关系:很多 resume 行为测试都调用它;它把核心检查交给 finalize_resume_interactive。
调用图:调用 1 个内部函数(finalize_resume_interactive);外部调用 2 个(try_parse_from, unreachable!)。
tests::finalize_fork_from_args2580–2603 ↗
fn finalize_fork_from_args(args: &[&str]) -> TuiCli
作用:测试用的小帮手:从命令行文字解析出 fork,并整理成最终交互式配置。
数据流:输入是参数数组;它解析 CLI,取出 fork 子命令字段;输出 finalize_fork_interactive 生成的 TuiCli。
调用关系:fork 相关测试复用它,避免每个测试都手写解析和拆字段。
调用图:调用 1 个内部函数(finalize_fork_interactive);外部调用 2 个(try_parse_from, unreachable!)。
tests::finalize_archive_from_args2605–2629 ↗
fn finalize_archive_from_args(args: &[&str]) -> (String, TuiCli, InteractiveRemoteOptions)
作用:测试用的小帮手:解析 archive 命令,并拿到归档目标、交互式配置和远程选项。
数据流:输入是参数数组;它解析 CLI,取出 archive 子命令;输出目标字符串、整理后的 TuiCli、远程参数。
调用关系:archive 参数合并测试用它;它把配置整理交给 finalize_session_archive_interactive。
调用图:调用 1 个内部函数(finalize_session_archive_interactive);外部调用 2 个(try_parse_from, unreachable!)。
tests::profile_v2_for_args2631–2641 ↗
fn profile_v2_for_args(args: &[&str]) -> anyhow::Result<Option<String>>
作用:测试用的小帮手:判断给定命令行最终会使用哪个新版配置 profile。profile 可以理解成一套命名配置。
数据流:输入是参数数组;它解析 CLI,如果没有子命令就读交互式配置,否则调用子命令 profile 判断;输出可选 profile 名或错误。
调用关系:profile 相关测试调用它;它把复杂判断交给 profile_v2_for_subcommand。
调用图:调用 1 个内部函数(profile_v2_for_subcommand);外部调用 1 个(try_parse_from)。
tests::profile_v2_is_rejected_for_config_management_subcommands2644–2646 ↗
fn profile_v2_is_rejected_for_config_management_subcommands()
作用:确认配置管理类命令不能使用新版 profile 参数。因为这类命令本身就在改配置,用 profile 可能造成歧义。
数据流:输入是一条带 --profile 的 features list 命令;辅助函数应返回错误;测试断言确实失败。
调用关系:它通过 tests::profile_v2_for_args 覆盖 profile 限制规则。
调用图:外部调用 1 个(assert!)。
tests::profile_v2_is_allowed_for_runtime_subcommands2649–2674 ↗
fn profile_v2_is_allowed_for_runtime_subcommands()
作用:确认运行类命令可以使用新版 profile。运行类命令只是按某套配置执行,不会修改配置本身。
数据流:输入多组带 --profile work 的命令;逐个解析并读取 profile;输出都应是 work。
调用关系:它和拒绝测试配套,说明 profile_v2_for_subcommand 哪些命令能放行。
调用图:外部调用 1 个(assert_eq!)。
tests::import_remains_an_interactive_prompt2677–2682 ↗
fn import_remains_an_interactive_prompt()
作用:确认 codex import 仍被当作普通交互提示词,而不是子命令。这样避免破坏已有用户把 import 当 prompt 的用法。
数据流:输入 codex import;解析后应没有子命令,交互式 prompt 是 import。
调用关系:它保护命令行解析规则,防止新增子命令时误占用用户输入。
调用图:外部调用 3 个(assert!, assert_eq!, try_parse_from)。
tests::profile_v2_rejects_non_plain_names_at_parse_time2685–2689 ↗
fn profile_v2_rejects_non_plain_names_at_parse_time()
作用:确认 profile 名不能包含类似路径的斜杠。这样避免用户输入被误当成文件路径或嵌套名字。
数据流:输入 --profile nested/work;解析阶段就应失败。
调用关系:它测试 clap 参数解析层面的 profile 名校验。
调用图:外部调用 1 个(assert!)。
tests::exec_resume_last_accepts_prompt_positional2692–2707 ↗
fn exec_resume_last_accepts_prompt_positional()
作用:确认非交互 exec resume --last 后面的一个位置参数会被当作提示词。这样用户能继续最近会话并追加新请求。
数据流:输入 exec resume 命令和 2+2;解析后 last 为真、session_id 为空、prompt 是 2+2。
调用关系:它保护 exec resume 的特殊位置参数规则。
调用图:外部调用 4 个(assert!, assert_eq!, try_parse_from, panic!)。
tests::exec_resume_accepts_output_flags_after_subcommand2710–2741 ↗
fn exec_resume_accepts_output_flags_after_subcommand()
作用:确认 exec resume 子命令后面仍能写输出文件和输出 schema 参数。schema 是约束输出格式的说明文件。
数据流:输入包含 session、-o、--output-schema 和提示词;解析后输出路径、schema 路径、会话 ID、prompt 都应正确。
调用关系:它测试 exec 子命令参数可以在 resume 后继续生效。
调用图:外部调用 3 个(assert_eq!, try_parse_from, panic!)。
tests::dangerous_bypass_conflicts_with_approval_policy2744–2754 ↗
fn dangerous_bypass_conflicts_with_approval_policy()
作用:确认“跳过审批和沙箱”的危险开关不能和普通审批策略同时用。否则权限语义会互相打架。
数据流:输入两个冲突权限参数;解析应失败,错误类型是参数冲突。
调用关系:它保护 clap 参数冲突配置,避免用户同时表达两个矛盾要求。
调用图:外部调用 2 个(assert_eq!, try_parse_from)。
tests::app_server_from_args2756–2762 ↗
fn app_server_from_args(args: &[&str]) -> AppServerCommand
作用:测试用的小帮手:从命令行参数里取出 app-server 子命令配置。
数据流:输入参数数组;它解析 CLI 并拆出 AppServerCommand;输出该命令配置。
调用关系:app-server 解析测试大量复用它。
调用图:外部调用 2 个(try_parse_from, unreachable!)。
tests::default_app_server_socket_path2764–2768 ↗
fn default_app_server_socket_path() -> AbsolutePathBuf
作用:测试用的小帮手:算出 app-server 默认 Unix socket 路径。Unix socket 可以理解成本机进程之间通信的一根本地管道。
数据流:它读取 codex home 目录,再调用 app-server 工具生成默认 socket 路径;输出绝对路径。
调用关系:app-server listen 相关测试用它对比默认值。
调用图:调用 1 个内部函数(find_codex_home);外部调用 1 个(app_server_control_socket_path)。
tests::debug_prompt_input_parses_prompt_and_images2771–2794 ↗
fn debug_prompt_input_parses_prompt_and_images()
作用:确认 debug prompt-input 能同时解析文字提示和图片路径。图片参数支持逗号分隔。
数据流:输入 prompt 和两张图片路径;解析后 prompt 是 hello,images 是两个 PathBuf。
调用关系:它测试 debug 子命令的参数拆分规则。
调用图:外部调用 3 个(assert_eq!, try_parse_from, panic!)。
tests::debug_models_parses_bundled_flag2797–2809 ↗
fn debug_models_parses_bundled_flag()
作用:确认 debug models 能识别 --bundled 开关。这个开关表示查看内置模型信息。
数据流:输入 debug models --bundled;解析后 bundled 字段应为 true。
调用关系:它保护 debug models 子命令的布尔参数。
调用图:外部调用 3 个(assert!, try_parse_from, panic!)。
tests::responses_subcommand_is_not_registered2812–2819 ↗
tests::help_from_args2821–2825 ↗
fn help_from_args(args: &[&str]) -> String
作用:测试用的小帮手:拿到某组参数触发的帮助文本。它确认帮助是正常短路显示,而不是解析失败。
数据流:输入参数数组;解析应返回 DisplayHelp 错误;输出错误里的帮助字符串。
调用关系:插件帮助文案测试调用它来检查 Usage 行。
调用图:外部调用 2 个(assert_eq!, try_parse_from)。
tests::plugin_marketplace_help_uses_plugin_namespace2828–2844 ↗
fn plugin_marketplace_help_uses_plugin_namespace()
作用:确认 marketplace 命令的帮助文本显示在 codex plugin marketplace 命名空间下。这样用户看到的路径和真实用法一致。
数据流:输入 marketplace 及其子命令的 help 参数;取出帮助文本;检查 Usage 文案包含正确前缀。
调用关系:它依赖 tests::help_from_args,保护插件市场命令迁移后的帮助体验。
调用图:外部调用 2 个(assert!, help_from_args)。
tests::plugin_marketplace_add_parses_under_plugin2847–2853 ↗
fn plugin_marketplace_add_parses_under_plugin()
作用:确认 plugin marketplace add 会被解析成 plugin 子命令。
数据流:输入添加 marketplace 插件的命令;解析后 subcommand 应匹配 Plugin。
调用关系:它测试 marketplace add 不再是顶层命令,而归入 plugin。
调用图:外部调用 2 个(assert!, try_parse_from)。
tests::plugin_marketplace_upgrade_parses_under_plugin2856–2862 ↗
fn plugin_marketplace_upgrade_parses_under_plugin()
作用:确认 plugin marketplace upgrade 归在 plugin 子命令下面。
数据流:输入升级命令;解析后 subcommand 应是 Plugin。
调用关系:它和其他 marketplace 测试一起固定插件命令层级。
调用图:外部调用 2 个(assert!, try_parse_from)。
tests::plugin_add_parses_under_plugin2865–2877 ↗
fn plugin_add_parses_under_plugin()
作用:确认 plugin add 命令能正常解析,并支持 marketplace 参数。
数据流:输入 plugin add sample --marketplace debug;解析后应进入 Plugin 子命令。
调用关系:它测试普通插件添加路径。
调用图:外部调用 2 个(assert!, try_parse_from)。
tests::plugin_list_parses_under_plugin2880–2886 ↗
fn plugin_list_parses_under_plugin()
作用:确认 plugin list 命令能正常解析,并能带 marketplace 参数。
数据流:输入 plugin list --marketplace debug;解析后应进入 Plugin 子命令。
调用关系:它保护插件列表命令的解析入口。
调用图:外部调用 2 个(assert!, try_parse_from)。
tests::plugin_remove_parses_under_plugin2889–2901 ↗
fn plugin_remove_parses_under_plugin()
作用:确认 plugin remove 命令能正常解析,并能指定 marketplace。
数据流:输入 plugin remove sample --marketplace debug;解析后应进入 Plugin 子命令。
调用关系:它保护插件移除命令的解析入口。
调用图:外部调用 2 个(assert!, try_parse_from)。
tests::update_parses_as_update_subcommand2904–2907 ↗
fn update_parses_as_update_subcommand()
作用:确认 codex update 被识别成更新子命令。
数据流:输入 codex update;解析后 subcommand 应是 Update。
调用关系:它保护顶层 update 命令注册。
调用图:外部调用 2 个(assert!, try_parse_from)。
tests::archive_merges_scoped_tui_flags2910–2942 ↗
fn archive_merges_scoped_tui_flags()
作用:确认 archive 子命令后面的交互式参数会覆盖到最终配置里。比如工作目录、模型、profile 和严格配置。
数据流:输入一条带多种 archive 参数的命令;辅助函数整理后;检查目标、远程地址、模型、profile、目录和危险开关都正确。
调用关系:它通过 tests::finalize_archive_from_args 测试 finalize_session_archive_interactive 的合并效果。
调用图:外部调用 3 个(assert!, assert_eq!, finalize_archive_from_args)。
tests::delete_force_requires_uuid2945–2953 ↗
fn delete_force_requires_uuid()
作用:确认强制删除只能用于 UUID 形式的会话 ID,不能用于普通名字。UUID 是一串标准格式的唯一编号。
数据流:先用 UUID 调删除动作,应成功;再用名字加 force,应返回需要交互确认的错误。
调用关系:它测试 delete_action 的安全限制,避免误删同名会话。
调用图:调用 1 个内部函数(delete_action);外部调用 2 个(assert!, assert_eq!)。
tests::sandbox_parses_permissions_profile2957–2974 ↗
fn sandbox_parses_permissions_profile()
作用:确认 sandbox 命令能解析权限 profile。权限 profile 是一套预设的沙箱权限。
数据流:输入 sandbox --permissions-profile :workspace -- echo;解析后 profile 是 :workspace,命令是 echo。
调用关系:它保护 sandbox 子命令的长参数。
调用图:外部调用 3 个(assert_eq!, try_parse_from, panic!)。
tests::sandbox_parses_permissions_profile_short_alias2978–2989 ↗
fn sandbox_parses_permissions_profile_short_alias()
作用:确认 sandbox 的权限 profile 短参数 -P 可用。
数据流:输入 sandbox -P :workspace -- echo;解析后权限 profile 和命令都应正确。
调用关系:它和长参数测试一起保护用户快捷写法。
调用图:外部调用 3 个(assert_eq!, try_parse_from, panic!)。
tests::sandbox_parses_config_profile2993–3004 ↗
fn sandbox_parses_config_profile()
作用:确认 sandbox 能解析配置 profile。这里的 profile 是选择一套 codex 配置。
数据流:输入 sandbox --profile work -- echo;解析后 config_profile 是 work,命令是 echo。
调用关系:它测试 sandbox 对运行配置的支持。
调用图:外部调用 3 个(assert_eq!, try_parse_from, panic!)。
tests::sandbox_rejects_explicit_profile_controls_without_profile3008–3013 ↗
fn sandbox_rejects_explicit_profile_controls_without_profile()
作用:确认 sandbox 不能单独使用需要 profile 才有意义的控制参数。比如只给 -C 没有 profile 会缺少必要上下文。
数据流:输入 sandbox -C /tmp;解析应失败,错误类型是缺少必需参数。
调用关系:它保护 sandbox 参数组合规则。
调用图:外部调用 2 个(assert_eq!, try_parse_from)。
tests::plugin_marketplace_remove_parses_under_plugin3016–3022 ↗
fn plugin_marketplace_remove_parses_under_plugin()
作用:确认 plugin marketplace remove 被解析到 plugin 子命令下。
数据流:输入 marketplace remove 命令;解析后 subcommand 应匹配 Plugin。
调用关系:它补充 marketplace remove 的命令层级测试。
调用图:外部调用 2 个(assert!, try_parse_from)。
tests::marketplace_no_longer_parses_at_top_level3025–3037 ↗
fn marketplace_no_longer_parses_at_top_level()
作用:确认 marketplace 不再能作为顶层命令使用。用户必须走 plugin marketplace。
数据流:输入顶层 marketplace add、upgrade、remove;每个解析结果都应是错误。
调用关系:它保护命令迁移后的旧入口关闭状态。
调用图:外部调用 2 个(assert!, try_parse_from)。
tests::full_auto_no_longer_parses_at_top_level3040–3044 ↗
fn full_auto_no_longer_parses_at_top_level()
作用:确认顶层 --full-auto 已不再接受。这个旧开关被新的沙箱参数替代。
数据流:输入 codex --full-auto;解析应失败。
调用关系:它测试废弃参数在顶层已经移除。
调用图:外部调用 2 个(assert!, try_parse_from)。
tests::exec_full_auto_reports_migration_path3047–3058 ↗
fn exec_full_auto_reports_migration_path()
作用:确认 exec 里遇到旧 --full-auto 时还能给出迁移提示。这样老用户知道该换成什么参数。
数据流:输入 codex exec --full-auto summarize;解析成功后读取警告文字;输出应提示改用 --sandbox workspace-write。
调用关系:它测试 exec 子命令保留兼容提示,而不是静默失败。
调用图:外部调用 3 个(assert_eq!, try_parse_from, panic!)。
tests::sandbox_full_auto_no_longer_parses3061–3065 ↗
fn sandbox_full_auto_no_longer_parses()
作用:确认 sandbox 子命令不再接受旧 --full-auto。
数据流:输入 sandbox --full-auto;解析应失败。
调用关系:它和 exec 的迁移提示测试一起定义旧参数在不同命令里的处理方式。
调用图:外部调用 2 个(assert!, try_parse_from)。
tests::sample_exit_info3067–3083 ↗
fn sample_exit_info(conversation_id: Option<&str>, thread_name: Option<&str>) -> AppExitInfo
作用:测试用的小帮手:构造一份退出信息,带少量 token 用量和可选会话信息。token 可以理解成模型处理文字的计量单位。
数据流:输入可选会话 ID 和线程名;它生成 token 用量、线程 ID、继续会话提示和退出原因;输出 AppExitInfo。
调用关系:退出消息格式化测试复用它来减少重复构造代码。
调用图:外部调用 2 个(default, resume_hint)。
tests::format_exit_messages_skips_zero_usage3086–3096 ↗
fn format_exit_messages_skips_zero_usage()
作用:确认没有 token 用量、也没有继续提示时,不打印多余退出消息。
数据流:输入一份空用量退出信息;格式化后应得到空行列表。
调用关系:它测试 format_exit_messages 的安静退出行为。
调用图:调用 1 个内部函数(format_exit_messages);外部调用 2 个(assert!, default)。
tests::format_exit_messages_includes_session_id_for_fatal_exit_without_resume_hint3099–3112 ↗
fn format_exit_messages_includes_session_id_for_fatal_exit_without_resume_hint()
作用:确认严重错误退出且没有继续提示时,仍会打印会话 ID。这样用户还能反馈或排查这个会话。
数据流:输入带线程 ID、无 resume_hint、fatal 退出原因的信息;格式化后输出一行 Session ID。
调用关系:它覆盖 format_exit_messages 的故障场景。
调用图:调用 2 个内部函数(format_exit_messages, from_string);外部调用 3 个(assert_eq!, default, Fatal)。
tests::format_exit_messages_includes_resume_hint_for_fatal_exit3115–3130 ↗
fn format_exit_messages_includes_resume_hint_for_fatal_exit()
作用:确认即使是严重错误退出,只要有继续提示,也会告诉用户如何恢复会话。
数据流:输入带 token 用量和会话 ID 的 fatal 退出信息;格式化后输出用量和 resume 命令提示。
调用关系:它用 tests::sample_exit_info 构造数据,测试 format_exit_messages。
调用图:调用 1 个内部函数(format_exit_messages);外部调用 3 个(assert_eq!, sample_exit_info, Fatal)。
tests::format_exit_messages_includes_resume_hint_without_color3133–3147 ↗
fn format_exit_messages_includes_resume_hint_without_color()
作用:确认普通退出时会显示 token 用量和无颜色的继续会话提示。
数据流:输入带会话 ID 的退出信息,关闭颜色;输出两行纯文本消息。
调用关系:它固定 format_exit_messages 在非彩色终端下的文字。
调用图:调用 1 个内部函数(format_exit_messages);外部调用 2 个(assert_eq!, sample_exit_info)。
tests::format_exit_messages_applies_color_when_enabled3150–3158 ↗
fn format_exit_messages_applies_color_when_enabled()
作用:确认开启颜色时,继续会话提示会带终端颜色控制码。颜色码是终端用来改变文字颜色的特殊字符。
数据流:输入同样的退出信息并开启颜色;输出两行,第二行应包含青色控制码。
调用关系:它测试 format_exit_messages 的彩色显示分支。
调用图:调用 1 个内部函数(format_exit_messages);外部调用 3 个(assert!, assert_eq!, sample_exit_info)。
tests::format_exit_messages_names_picker_item_when_thread_has_name3161–3174 ↗
fn format_exit_messages_names_picker_item_when_thread_has_name()
作用:确认有线程名字时,继续提示会告诉用户在选择器里选哪个名字。这样比只给长 UUID 更友好。
数据流:输入带会话 ID 和名字 my-thread 的退出信息;输出包含“运行 resume 然后选择 my-thread”的提示。
调用关系:它测试 resume hint 对命名会话的展示方式。
调用图:调用 1 个内部函数(format_exit_messages);外部调用 2 个(assert_eq!, sample_exit_info)。
tests::resume_model_flag_applies_when_no_root_flags3177–3185 ↗
fn resume_model_flag_applies_when_no_root_flags()
作用:确认 resume -m 能设置模型,即使根命令没有模型参数。
数据流:输入 codex resume -m gpt-5.1-test;整理后模型字段应为该值,并打开 resume 选择器。
调用关系:它通过 tests::finalize_resume_from_args 检查 resume 参数合并。
调用图:外部调用 3 个(assert!, assert_eq!, finalize_resume_from_args)。
tests::resume_picker_logic_none_and_not_last3188–3194 ↗
fn resume_picker_logic_none_and_not_last()
作用:确认只输入 resume 时会打开会话选择器。
数据流:输入 codex resume;整理后 picker 为真,last 为假,没有指定 session_id。
调用关系:它测试 finalize_resume_interactive 的默认选择逻辑。
调用图:外部调用 3 个(assert!, assert_eq!, finalize_resume_from_args)。
tests::resume_picker_logic_last3197–3203 ↗
fn resume_picker_logic_last()
作用:确认 resume --last 不打开选择器,而是继续最近会话。
数据流:输入 codex resume --last;整理后 picker 为假,last 为真,没有 session_id。
调用关系:它测试 resume 最近会话分支。
调用图:外部调用 3 个(assert!, assert_eq!, finalize_resume_from_args)。
tests::resume_last_accepts_prompt_positional3206–3218 ↗
fn resume_last_accepts_prompt_positional()
作用:确认 resume --last 后面跟一个位置参数时,这个参数是提示词,不是会话 ID。
数据流:输入 resume --last 加一段提示;整理后 last 为真、session_id 为空、prompt 是那段提示。
调用关系:它测试 finalize_resume_interactive 对位置参数的重新解释。
调用图:外部调用 3 个(assert!, assert_eq!, finalize_resume_from_args)。
tests::resume_last_rejects_explicit_session_and_prompt3221–3227 ↗
fn resume_last_rejects_explicit_session_and_prompt()
作用:确认 resume --last 不能同时写明确会话 ID 和提示词。因为 --last 已经说明要最近会话。
数据流:输入 resume --last 1234 continue here;解析应失败,错误类型是参数冲突。
调用关系:它保护 clap 对 resume 参数数量和冲突的限制。
调用图:外部调用 2 个(assert_eq!, try_parse_from)。
tests::resume_picker_logic_with_session_id3230–3236 ↗
fn resume_picker_logic_with_session_id()
作用:确认给了会话 ID 时,不打开选择器,也不使用最近会话。
数据流:输入 resume 1234;整理后 session_id 是 1234,picker 和 last 都为假。
调用关系:它测试 resume 指定会话分支。
调用图:外部调用 3 个(assert!, assert_eq!, finalize_resume_from_args)。
tests::resume_with_session_id_accepts_prompt_positional3239–3247 ↗
fn resume_with_session_id_accepts_prompt_positional()
作用:确认指定会话 ID 后还能再给一段提示词。
数据流:输入 resume 1234 continue here;整理后 session_id 是 1234,prompt 是 continue here。
调用关系:它测试 resume 的两个位置参数规则。
调用图:外部调用 3 个(assert!, assert_eq!, finalize_resume_from_args)。
tests::resume_all_flag_sets_show_all3250–3254 ↗
fn resume_all_flag_sets_show_all()
作用:确认 resume --all 会让选择器显示全部会话。
数据流:输入 resume --all;整理后 picker 为真,resume_show_all 为真。
调用关系:它测试 resume 选择器过滤开关。
调用图:外部调用 2 个(assert!, finalize_resume_from_args)。
tests::resume_include_non_interactive_flag_sets_source_filter_override3257–3263 ↗
fn resume_include_non_interactive_flag_sets_source_filter_override()
作用:确认 resume --include-non-interactive 会包含非交互式会话。
数据流:输入该参数;整理后选择器打开,并设置 include_non_interactive 为真。
调用关系:它测试 resume 会话来源过滤规则。
调用图:外部调用 2 个(assert!, finalize_resume_from_args)。
tests::resume_merges_option_flags3266–3320 ↗
fn resume_merges_option_flags()
作用:确认 resume 子命令里的常用选项都会合并进交互式配置。包括模型、profile、目录、沙箱、审批、搜索和图片。
数据流:输入带大量 resume 参数的命令;整理后逐项检查配置字段和图片列表;会话 ID 也应保留。
调用关系:它全面覆盖 merge_interactive_cli_flags 在 resume 场景下的效果。
调用图:外部调用 4 个(assert!, assert_eq!, assert_matches!, finalize_resume_from_args)。
tests::resume_merges_dangerously_bypass_flag3323–3336 ↗
fn resume_merges_dangerously_bypass_flag()
作用:确认 resume 能接收“跳过审批和沙箱”的危险开关。
数据流:输入 resume 加危险开关;整理后对应布尔字段为真,并保持默认选择器逻辑。
调用关系:它测试危险权限参数能从 resume 子命令传入主配置。
调用图:外部调用 3 个(assert!, assert_eq!, finalize_resume_from_args)。
tests::resume_merges_bypass_hook_trust_flag3339–3348 ↗
fn resume_merges_bypass_hook_trust_flag()
作用:确认 resume 能接收“跳过 hook 信任检查”的危险开关。hook 是程序运行前后触发的小脚本。
数据流:输入 resume 加 bypass hook trust;整理后 bypass_hook_trust 为真。
调用关系:它测试 resume 子命令的安全相关参数合并。
调用图:外部调用 3 个(assert!, assert_eq!, finalize_resume_from_args)。
tests::fork_picker_logic_none_and_not_last3351–3357 ↗
fn fork_picker_logic_none_and_not_last()
作用:确认只输入 fork 时会打开会话选择器。
数据流:输入 codex fork;整理后 fork_picker 为真,fork_last 为假,无 session_id。
调用关系:它测试 finalize_fork_interactive 的默认选择逻辑。
调用图:外部调用 3 个(assert!, assert_eq!, finalize_fork_from_args)。
tests::fork_picker_logic_last3360–3366 ↗
fn fork_picker_logic_last()
作用:确认 fork --last 会从最近会话分叉,不打开选择器。
数据流:输入 fork --last;整理后 fork_last 为真,picker 为假。
调用关系:它测试 fork 最近会话分支。
调用图:外部调用 3 个(assert!, assert_eq!, finalize_fork_from_args)。
tests::fork_last_accepts_prompt_positional3369–3380 ↗
fn fork_last_accepts_prompt_positional()
作用:确认 fork --last 后面的位置参数会被当作提示词。
数据流:输入 fork --last 加提示;整理后没有 session_id,prompt 是该提示。
调用关系:它测试 finalize_fork_interactive 对位置参数的特殊处理。
调用图:外部调用 3 个(assert!, assert_eq!, finalize_fork_from_args)。
tests::fork_last_rejects_explicit_session_and_prompt3383–3389 ↗
fn fork_last_rejects_explicit_session_and_prompt()
作用:确认 fork --last 不能同时给会话 ID 和提示词。
数据流:输入 fork --last 1234 continue here;解析应失败,错误类型是参数冲突。
调用关系:它保护 fork 的参数冲突规则。
调用图:外部调用 2 个(assert_eq!, try_parse_from)。
tests::fork_picker_logic_with_session_id3392–3398 ↗
fn fork_picker_logic_with_session_id()
作用:确认给了会话 ID 时,fork 不打开选择器。
数据流:输入 fork 1234;整理后 session_id 是 1234,picker 和 last 都为假。
调用关系:它测试 fork 指定会话分支。
调用图:外部调用 3 个(assert!, assert_eq!, finalize_fork_from_args)。
tests::fork_with_session_id_accepts_prompt_positional3401–3409 ↗
fn fork_with_session_id_accepts_prompt_positional()
作用:确认 fork 指定会话 ID 后还可以给提示词。
数据流:输入 fork 1234 continue here;整理后 session_id 和 prompt 都正确。
调用关系:它测试 fork 的两个位置参数规则。
调用图:外部调用 3 个(assert!, assert_eq!, finalize_fork_from_args)。
tests::fork_all_flag_sets_show_all3412–3416 ↗
fn fork_all_flag_sets_show_all()
作用:确认 fork --all 会让选择器显示全部会话。
数据流:输入 fork --all;整理后 picker 为真,fork_show_all 为真。
调用关系:它测试 fork 选择器过滤开关。
调用图:外部调用 2 个(assert!, finalize_fork_from_args)。
tests::app_server_analytics_default_disabled_without_flag3419–3427 ↗
fn app_server_analytics_default_disabled_without_flag()
作用:确认 app-server 默认不启用 analytics,也不启用远程控制,并使用标准输入输出通信。
数据流:输入 codex app-server;解析后检查 analytics、remote_control 和 listen 默认值。
调用关系:它通过 tests::app_server_from_args 固定 app-server 默认行为。
调用图:外部调用 3 个(assert!, assert_eq!, app_server_from_args)。
tests::app_server_remote_control_startup_flag_enables_remote_control3430–3433 ↗
fn app_server_remote_control_startup_flag_enables_remote_control()
作用:确认 app-server 的 --remote-control 开关会打开远程控制。
数据流:输入 app-server --remote-control;解析后 remote_control 应为 true。
调用关系:它测试 app-server 启动参数。
调用图:外部调用 2 个(assert!, app_server_from_args)。
tests::app_server_analytics_default_enabled_with_flag3436–3440 ↗
fn app_server_analytics_default_enabled_with_flag()
作用:确认 app-server 显式加开关后会默认启用 analytics。
数据流:输入 app-server --analytics-default-enabled;解析后 analytics_default_enabled 为 true。
调用关系:它测试 app-server 的统计开关解析。
调用图:外部调用 2 个(assert!, app_server_from_args)。
tests::strict_config_parses_for_supported_commands3443–3476 ↗
fn strict_config_parses_for_supported_commands()
作用:确认支持严格配置的命令能解析 --strict-config。严格配置表示遇到未知或不合法配置时更严格地报错。
数据流:输入交互式、mcp-server、review、exec-server 等命令;解析后对应 strict_config 字段都应为 true。
调用关系:它测试多个命令的严格配置参数入口。
调用图:外部调用 3 个(assert!, assert_matches!, try_parse_from)。
tests::root_strict_config_is_supported_for_exec_server3479–3485 ↗
fn root_strict_config_is_supported_for_exec_server()
作用:确认根级 --strict-config 可以和 exec-server 一起用。
数据流:输入 codex --strict-config exec-server;解析后调用拒绝检查;结果应成功。
调用关系:它测试 reject_root_strict_config_for_subcommand 对 exec-server 的放行规则。
调用图:调用 1 个内部函数(reject_root_strict_config_for_subcommand);外部调用 1 个(try_parse_from)。
tests::root_strict_config_is_rejected_for_unsupported_subcommands3488–3514 ↗
fn root_strict_config_is_rejected_for_unsupported_subcommands()
作用:确认不支持的子命令会拒绝根级 --strict-config。
数据流:输入 mcp list 和 remote-control 两种命令;拒绝检查应返回错误,并包含对应命令名。
调用关系:它测试 reject_root_strict_config_for_subcommand 的拒绝分支。
调用图:调用 1 个内部函数(reject_root_strict_config_for_subcommand);外部调用 2 个(assert_eq!, try_parse_from)。
tests::app_server_subcommands_reject_strict_config3517–3530 ↗
fn app_server_subcommands_reject_strict_config()
作用:确认 app-server 的某些子命令不支持 --strict-config。
数据流:输入 app-server --strict-config proxy;调用 app-server 子命令拒绝检查;应返回包含 proxy 的错误。
调用关系:它测试 reject_strict_config_for_app_server_subcommand。
调用图:调用 1 个内部函数(reject_strict_config_for_app_server_subcommand);外部调用 2 个(assert_eq!, app_server_from_args)。
tests::reject_remote_flag_for_remote_control3533–3549 ↗
fn reject_remote_flag_for_remote_control()
作用:确认 remote-control 命令不能使用根级 --remote。因为它本身就是远程控制相关入口,混用会造成含义混乱。
数据流:输入带 --remote 的 remote-control;解析后调用拒绝检查;应返回包含 remote-control 的错误。
调用关系:它测试 reject_remote_mode_for_subcommand 对非交互命令的限制。
调用图:调用 1 个内部函数(reject_remote_mode_for_subcommand);外部调用 4 个(assert!, assert_eq!, try_parse_from, panic!)。
tests::remote_flag_parses_for_interactive_root3552–3556 ↗
fn remote_flag_parses_for_interactive_root()
作用:确认交互式根命令可以解析 --remote。
数据流:输入 codex --remote unix://codex.sock;解析后 remote 字段应保存该地址。
调用关系:它测试远程 TUI 的根级参数入口。
调用图:外部调用 2 个(assert_eq!, try_parse_from)。
tests::remote_auth_token_env_flag_parses_for_interactive_root3559–3572 ↗
fn remote_auth_token_env_flag_parses_for_interactive_root()
作用:确认交互式根命令可以解析认证令牌环境变量参数。
数据流:输入 remote-auth-token-env 和 websocket remote;解析后环境变量名字段应正确。
调用关系:它测试远程认证参数在交互式入口上的解析。
调用图:外部调用 2 个(assert_eq!, try_parse_from)。
tests::remote_flag_parses_for_resume_subcommand3575–3585 ↗
fn remote_flag_parses_for_resume_subcommand()
作用:确认 resume 子命令也能解析自己的 --remote 参数。
数据流:输入 codex resume --remote unix://codex.sock;解析后 resume 的 remote 字段应保存该地址。
调用关系:它测试远程 TUI 参数在 resume 下的作用域。
调用图:外部调用 3 个(assert_eq!, try_parse_from, panic!)。
tests::reject_remote_mode_for_non_interactive_subcommands3588–3599 ↗
fn reject_remote_mode_for_non_interactive_subcommands()
作用:确认非交互命令不能使用 --remote。
数据流:直接把 remote 地址和命令名 exec 交给拒绝检查;应返回说明只支持交互式 TUI 的错误。
调用关系:它测试 reject_remote_mode_for_subcommand 的通用保护。
调用图:调用 1 个内部函数(reject_remote_mode_for_subcommand);外部调用 1 个(assert!)。
tests::reject_remote_auth_token_env_for_non_interactive_subcommands3602–3613 ↗
fn reject_remote_auth_token_env_for_non_interactive_subcommands()
作用:确认非交互命令不能使用 --remote-auth-token-env。
数据流:输入没有 remote 但有令牌环境变量名和命令名 exec;拒绝检查应返回只支持交互式 TUI 的错误。
调用关系:它和 remote 地址拒绝测试一起保护远程模式边界。
调用图:调用 1 个内部函数(reject_remote_mode_for_subcommand);外部调用 1 个(assert!)。
tests::reject_remote_auth_token_env_for_app_server_generate_internal_json_schema3616–3628 ↗
fn reject_remote_auth_token_env_for_app_server_generate_internal_json_schema()
作用:确认 app-server 的生成内部 JSON schema 子命令不能用远程认证令牌参数。JSON schema 是描述 JSON 数据结构的说明书。
数据流:构造 generate-internal-json-schema 子命令和令牌环境变量;拒绝检查应返回包含该子命令名的错误。
调用关系:它测试 reject_remote_mode_for_app_server_subcommand 对 app-server 非交互子命令的限制。
调用图:调用 1 个内部函数(reject_remote_mode_for_app_server_subcommand);外部调用 3 个(from, assert!, GenerateInternalJsonSchema)。
tests::read_remote_auth_token_from_env_var_reports_missing_values3631–3637 ↗
fn read_remote_auth_token_from_env_var_reports_missing_values()
作用:确认读取远程认证令牌时,如果环境变量不存在,会给出明确错误。
数据流:输入变量名和一个模拟的读取函数,模拟 NotPresent;结果应是错误,文字包含未设置。
调用关系:它测试 read_remote_auth_token_from_env_var_with 的缺失变量分支。
调用图:调用 1 个内部函数(read_remote_auth_token_from_env_var_with);外部调用 1 个(assert!)。
tests::read_remote_auth_token_from_env_var_trims_values3640–3647 ↗
fn read_remote_auth_token_from_env_var_trims_values()
作用:确认环境变量里的令牌会去掉前后空白。
数据流:模拟读取到带空格的 bearer-token;函数返回去空白后的 bearer-token。
调用关系:它测试 read_remote_auth_token_from_env_var_with 的清理行为。
调用图:调用 1 个内部函数(read_remote_auth_token_from_env_var_with);外部调用 1 个(assert_eq!)。
tests::read_remote_auth_token_from_env_var_rejects_empty_values3650–3656 ↗
fn read_remote_auth_token_from_env_var_rejects_empty_values()
作用:确认环境变量只有空白时会被当成空令牌并拒绝。
数据流:模拟读取到空白字符;函数应返回错误,文字包含 empty。
调用关系:它保护远程认证不能带空密钥。
调用图:调用 1 个内部函数(read_remote_auth_token_from_env_var_with);外部调用 1 个(assert!)。
tests::app_server_listen_websocket_url_parses3659–3669 ↗
fn app_server_listen_websocket_url_parses()
作用:确认 app-server 能解析 websocket 监听地址。WebSocket 是一种长连接网络协议,常用于实时通信。
数据流:输入 --listen ws://127.0.0.1:4500;解析后 listen 应是 WebSocket,并带对应绑定地址。
调用关系:它测试 app-server 监听传输类型解析。
调用图:外部调用 2 个(assert_eq!, app_server_from_args)。
tests::app_server_listen_stdio_url_parses3672–3679 ↗
fn app_server_listen_stdio_url_parses()
作用:确认 app-server 能解析 stdio:// 监听方式。stdio 表示通过标准输入输出通信。
数据流:输入 --listen stdio://;解析后 listen 应是 Stdio。
调用关系:它测试 app-server 的标准输入输出传输配置。
调用图:外部调用 2 个(assert_eq!, app_server_from_args)。
tests::app_server_stdio_flag_parses3682–3685 ↗
fn app_server_stdio_flag_parses()
作用:确认旧式或快捷的 --stdio 开关能被解析。
数据流:输入 app-server --stdio;解析后 stdio 字段为 true。
调用关系:它测试 app-server 的 stdio 标志。
调用图:外部调用 2 个(assert!, app_server_from_args)。
tests::app_server_stdio_flag_conflicts_with_listen3688–3698 ↗
fn app_server_stdio_flag_conflicts_with_listen()
作用:确认 --stdio 不能和 --listen 同时使用。两者都在指定通信方式,放一起会冲突。
数据流:输入同时带两个参数;解析应失败,错误类型是参数冲突。
调用关系:它保护 app-server 监听参数的互斥规则。
调用图:外部调用 2 个(assert_eq!, try_parse_from)。
tests::app_server_listen_unix_socket_url_parses3701–3710 ↗
fn app_server_listen_unix_socket_url_parses()
作用:确认 --listen unix:// 会使用默认 Unix socket 路径。
数据流:输入 app-server --listen unix://;解析后 listen 是 UnixSocket,路径等于默认 socket 路径。
调用关系:它使用 tests::default_app_server_socket_path 对比默认值。
调用图:外部调用 2 个(assert_eq!, app_server_from_args)。
tests::app_server_listen_unix_socket_path_parses3713–3724 ↗
fn app_server_listen_unix_socket_path_parses()
作用:确认 --listen unix:///tmp/codex.sock 能解析指定 socket 文件路径。
数据流:输入带绝对路径的 unix URL;解析后 listen 是 UnixSocket,路径是 /tmp/codex.sock。
调用关系:它测试 app-server 本地 socket 路径解析。
调用图:外部调用 2 个(assert_eq!, app_server_from_args)。
tests::app_server_listen_off_parses3727–3730 ↗
fn app_server_listen_off_parses()
作用:确认 app-server 能解析 --listen off,表示不监听。
数据流:输入 --listen off;解析后 listen 应是 Off。
调用关系:它测试关闭监听的配置入口。
调用图:外部调用 2 个(assert_eq!, app_server_from_args)。
tests::app_server_listen_invalid_url_fails_to_parse3733–3737 ↗
fn app_server_listen_invalid_url_fails_to_parse()
作用:确认不支持的监听 URL 会解析失败。
数据流:输入 --listen http://foo;解析结果应是错误。
调用关系:它保护 app-server 只接受已定义的传输协议。
调用图:外部调用 2 个(assert!, try_parse_from)。
tests::app_server_proxy_subcommand_parses3740–3748 ↗
fn app_server_proxy_subcommand_parses()
作用:确认 app-server proxy 子命令能解析。
数据流:输入 codex app-server proxy;解析后子命令应是 Proxy,且没有指定 socket 路径。
调用关系:它测试 app-server proxy 入口注册。
调用图:外部调用 2 个(assert!, app_server_from_args)。
tests::app_server_daemon_subcommands_parse3751–3812 ↗
fn app_server_daemon_subcommands_parse()
作用:确认 app-server daemon 下的一组管理命令都能解析。daemon 是后台常驻服务。
数据流:输入 bootstrap、start、restart、enable/disable remote-control、stop、version 等命令;每个都应匹配对应枚举分支。
调用关系:它集中测试 app-server daemon 的命令树。
调用图:外部调用 1 个(assert!)。
tests::app_server_proxy_sock_path_parses3815–3828 ↗
fn app_server_proxy_sock_path_parses()
作用:确认 app-server proxy 的 --sock 参数能解析相对路径。
数据流:输入 proxy --sock codex.sock;解析后 socket_path 是基于当前目录解析出的绝对路径。
调用关系:它测试 proxy 子命令的 socket 路径参数。
调用图:外部调用 3 个(assert_eq!, app_server_from_args, panic!)。
tests::reject_remote_auth_token_env_for_app_server_proxy3831–3840 ↗
fn reject_remote_auth_token_env_for_app_server_proxy()
作用:确认 app-server proxy 不能使用远程认证令牌环境变量参数。
数据流:构造 Proxy 子命令和令牌变量名;拒绝检查应返回包含 app-server proxy 的错误。
调用关系:它测试 reject_remote_mode_for_app_server_subcommand 对 proxy 的限制。
调用图:调用 1 个内部函数(reject_remote_mode_for_app_server_subcommand);外部调用 2 个(assert!, Proxy)。
tests::reject_remote_auth_token_env_for_app_server_version3843–3854 ↗
fn reject_remote_auth_token_env_for_app_server_version()
作用:确认 app-server daemon version 不能使用远程认证令牌环境变量参数。
数据流:构造 daemon version 子命令和令牌变量名;拒绝检查应返回包含该命令名的错误。
调用关系:它测试 app-server daemon 子命令的远程参数限制。
调用图:调用 1 个内部函数(reject_remote_mode_for_app_server_subcommand);外部调用 2 个(assert!, Daemon)。
tests::app_server_capability_token_flags_parse3857–3877 ↗
fn app_server_capability_token_flags_parse()
作用:确认 app-server 的 capability-token WebSocket 认证参数能解析。capability token 可以理解成一张“持有即有权限”的通行证。
数据流:输入 ws-auth capability-token 和 token 文件路径;解析后认证模式和文件路径字段都应正确。
调用关系:它测试 app-server WebSocket 认证配置。
调用图:外部调用 2 个(assert_eq!, app_server_from_args)。
tests::app_server_signed_bearer_flags_parse3880–3909 ↗
fn app_server_signed_bearer_flags_parse()
作用:确认 app-server 的 signed-bearer-token 认证参数能解析。signed bearer token 是带签名的持有者令牌,可验证来源和有效性。
数据流:输入认证模式、共享密钥文件、issuer、audience、最大时钟偏差;解析后各字段应正确。
调用关系:它测试更复杂的 WebSocket 认证配置解析。
调用图:外部调用 2 个(assert_eq!, app_server_from_args)。
tests::app_server_rejects_removed_insecure_non_loopback_flag3912–3919 ↗
fn app_server_rejects_removed_insecure_non_loopback_flag()
作用:确认已移除的不安全非本机 WebSocket 放行参数不能再解析。
数据流:输入 --allow-unauthenticated-non-loopback-ws;解析应失败。
调用关系:它保护安全策略,避免无认证公网 WebSocket 被重新打开。
调用图:外部调用 2 个(assert!, try_parse_from)。
tests::features_enable_parses_feature_name3922–3932 ↗
fn features_enable_parses_feature_name()
作用:确认 features enable 能解析要启用的功能名。
数据流:输入 features enable unified_exec;解析后 feature 字段应是 unified_exec。
调用关系:它测试功能开关管理命令的启用入口。
调用图:外部调用 3 个(assert_eq!, try_parse_from, panic!)。
tests::features_disable_parses_feature_name3935–3945 ↗
fn features_disable_parses_feature_name()
作用:确认 features disable 能解析要关闭的功能名。
数据流:输入 features disable shell_tool;解析后 feature 字段应是 shell_tool。
调用关系:它测试功能开关管理命令的关闭入口。
调用图:外部调用 3 个(assert_eq!, try_parse_from, panic!)。
tests::feature_toggles_known_features_generate_overrides3948–3961 ↗
fn feature_toggles_known_features_generate_overrides()
作用:确认已知功能开关会转换成配置覆盖项。配置覆盖项就是类似 key=value 的临时设置。
数据流:输入 enable web_search_request 和 disable unified_exec;转换后输出两条 features.xxx=true/false 字符串。
调用关系:它测试 FeatureToggles::to_overrides 的正常路径。
调用图:外部调用 2 个(assert_eq!, vec!)。
tests::feature_toggles_accept_legacy_linux_sandbox_flag3964–3974 ↗
fn feature_toggles_accept_legacy_linux_sandbox_flag()
作用:确认旧的 Linux 沙箱功能名仍被接受。这样老配置或老命令不会突然坏掉。
数据流:输入 enable use_linux_sandbox_bwrap;转换后输出对应 true 覆盖项。
调用关系:它测试功能开关对历史名字的兼容。
调用图:外部调用 3 个(new, assert_eq!, vec!)。
tests::feature_toggles_accept_removed_image_detail_original_flag3977–3987 ↗
fn feature_toggles_accept_removed_image_detail_original_flag()
作用:确认已移除的 image_detail_original 功能名仍能被接受。
数据流:输入 enable image_detail_original;转换后输出对应 true 覆盖项。
调用关系:它保护旧命令或旧脚本的兼容性。
调用图:外部调用 3 个(new, assert_eq!, vec!)。
tests::feature_toggles_unknown_feature_errors3990–3999 ↗
fn feature_toggles_unknown_feature_errors()
作用:确认未知功能名会报错,而不是悄悄生成无效配置。
数据流:输入 enable does_not_exist;转换应失败,错误文字说明未知功能 flag。
调用关系:它测试 FeatureToggles::to_overrides 的错误路径。
调用图:外部调用 3 个(new, assert_eq!, vec!)。
tests::strict_config_with_unknown_enable_errors4002–4005 ↗
fn strict_config_with_unknown_enable_errors()
作用:确认严格配置下,启用未知功能会报错。
数据流:输入 --strict-config --enable does_not_exist;辅助函数返回错误;测试比对错误文字。
调用关系:它通过 tests::strict_config_feature_toggle_error 测试严格模式的功能名校验。
调用图:外部调用 2 个(assert_eq!, strict_config_feature_toggle_error)。
tests::strict_config_with_unknown_disable_errors4008–4011 ↗
fn strict_config_with_unknown_disable_errors()
作用:确认严格配置下,关闭未知功能也会报错。
数据流:输入 --strict-config --disable does_not_exist;转换应失败并提示未知功能。
调用关系:它复用 tests::strict_config_feature_toggle_error。
调用图:外部调用 2 个(assert_eq!, strict_config_feature_toggle_error)。
tests::strict_config_with_compound_enable_errors4014–4022 ↗
fn strict_config_with_compound_enable_errors()
作用:确认严格配置下,带点号的复合功能名如果未知也会报错。
数据流:输入 --enable multi_agent_v2.subagent_usage_hint_text;输出应是未知功能错误。
调用关系:它测试功能开关不会把复杂名字误当成合法配置路径。
调用图:外部调用 2 个(assert_eq!, strict_config_feature_toggle_error)。
tests::strict_config_feature_toggle_error4024–4033 ↗
fn strict_config_feature_toggle_error(args: &[&str]) -> anyhow::Error
作用:测试用的小帮手:在严格配置模式下解析一组功能开关,并返回它产生的错误。
数据流:输入功能开关参数;它前面自动加上 codex 和 --strict-config,解析 CLI,确认严格模式开启,再调用 to_overrides;输出期望中的错误。
调用关系:严格配置下未知功能的多个测试都调用它,实际校验交给 FeatureToggles::to_overrides。
调用图:外部调用 3 个(assert!, try_parse_from, once)。
交互式和任务运行器
这些入口点和架构覆盖用户直接启动的交互式 TUI 和云任务执行界面。
tui/src/cli.rs源码 ↗
这个文件主要解决“用户在命令行里输入的东西,程序该怎么理解”的问题。比如用户可以直接带一个初始提问,也可以要求严格检查配置文件,还可以选择是否启用联网搜索、是否关闭备用屏幕模式。这里用 clap(一个 Rust 命令行参数解析库,负责把命令行文字变成结构化数据)来声明这些选项。Cli 是完整的 TUI 启动参数清单;TuiSharedCliOptions 是一层包装,把多个命令共用的参数接进来,同时给 TUI 加一点额外限制:某个“绕过审批和沙箱”的危险选项,不能和“审批策略”选项同时使用。可以把它理解成入口处的值班员:先看用户带了哪些纸条,再检查有没有互相冲突的要求,最后把干净的启动配置交给后面的程序。
Cli::deref81–83 ↗
fn deref(&self) -> &Self::Target
作用:让 Cli 可以像 SharedCliOptions 一样被读取。这样别的代码想看通用命令行选项时,不用每次都写很长的字段路径。
数据流:进去的是一个 Cli 对象的只读引用 → 它找到里面的 shared 字段,再取出真正的 SharedCliOptions → 出来的是这份通用选项的只读引用,原数据不被改动。
调用关系:这是给 Rust 的 Deref 机制用的。后面的启动代码如果需要读取通用选项,可以把 Cli 当成 SharedCliOptions 来用,省去一层手动拆包。
Cli::deref_mut87–89 ↗
fn deref_mut(&mut self) -> &mut Self::Target
作用:让 Cli 可以像 SharedCliOptions 一样被修改。需要调整通用命令行选项时,调用方不用先手动进入 shared 字段。
数据流:进去的是一个 Cli 对象的可修改引用 → 它定位到 shared.0 里的 SharedCliOptions → 出来的是这份通用选项的可修改引用,调用方随后可以改它。
调用关系:这是 DerefMut 机制的一部分。任何需要在解析后更新通用选项的代码,都可以通过它直接操作 Cli 里的共享选项。
mark_tui_args135–139 ↗
fn mark_tui_args(cmd: clap::Command) -> clap::Command
作用:给 TUI 的命令行规则补上一条安全限制:危险的“绕过审批和沙箱”选项,不能和“审批策略”选项一起用。
数据流:进去的是一个 clap::Command → 它通过 mut_arg 找到 dangerously_bypass_approvals_and_sandbox 这个参数,并给它设置 conflicts_with("approval_policy") → 出来的是带有冲突检查的新命令对象。
调用关系:它被 TuiSharedCliOptions::augment_args 和 TuiSharedCliOptions::augment_args_for_update 调用。也就是说,无论是新建参数规则还是更新参数规则,最后都会经过这里加上同一条 TUI 安全检查。
调用图:被 2 处调用(augment_args, augment_args_for_update);外部调用 1 个(mut_arg)。
tui/src/main.rs源码 ↗
这个文件像一家店的前台:用户先到这里报上自己要做什么,然后前台把信息交给后面的主程序去干活。它先解析命令行参数,也就是用户启动程序时带的选项;还会把一些配置覆盖项合并进真正的参数里,保证用户临时指定的设置能生效。接着它调用 TUI(终端用户界面,也就是在黑框框里操作的界面)的主运行函数。程序结束后,它根据退出原因决定怎么收尾:如果是严重错误,就先打印错误,并让系统知道这次运行失败;如果有会话恢复命令,就提示用户下次怎样继续;如果有 token 用量,也会显示出来。它还会判断终端是否支持颜色,支持就把恢复命令染成醒目的青色。没有这个文件,程序就缺少“从命令行启动到正确退出”的门面。
format_exit_messages13–39 ↗
fn format_exit_messages(exit_info: AppExitInfo, color_enabled: bool) -> Vec<String>
作用:这个函数专门整理程序结束时要显示给用户看的几行文字。它会决定是否显示 token 用量、恢复会话的命令,或者在严重错误时显示会话 ID。
数据流:进去的是一次运行结束后的信息 AppExitInfo,以及一个布尔值,表示终端能不能显示颜色。它先看这次退出是不是严重错误,再取出 token 用量、线程/会话 ID、恢复提示等信息;如果 token 用量不是零,就放进输出列表;如果有恢复命令,就按需加颜色后生成提示;如果没有恢复命令但发生严重错误并且有会话 ID,就把会话 ID 放进去。出来的是一个字符串列表,每个字符串就是之后要打印的一行话;它不改外部状态。
调用关系:它是 main 在程序快结束时使用的小帮手,负责把复杂的退出信息变成普通人能读懂的提示语。它内部只做简单判断和字符串拼接,用到新建列表、格式化文字、判断退出类型这些基础动作,不负责真正运行程序,也不负责打印。
main50–83 ↗
fn main() -> anyhow::Result<()>
作用:这是整个可执行程序真正开始运行的地方。它读取命令行参数,启动 TUI 主程序,最后根据结果打印提示并设置成功或失败的退出状态。
数据流:进去的是操作系统传给程序的启动信息,比如命令行参数和程序名路径。它先通过 arg0_dispatch_or_else 做启动分发,也就是根据程序是以什么名字被调用来决定是否走特殊入口;然后解析参数,把顶层配置覆盖项塞到内部命令参数前面;再调用主运行流程拿到退出信息。之后它检查退出原因:严重错误就往标准错误输出打印 ERROR,正常用户退出就不报错;再判断标准输出是否支持颜色,把退出提示逐行打印。若是严重错误,它会先刷新标准输出,再用退出码 1 结束进程,表示这次运行失败;否则正常返回成功。
调用关系:它站在最外层,是用户启动 tui 程序后最先进入的函数。它把早期分发交给 arg0_dispatch_or_else,把真正的界面运行交给 run_main,把退出文案整理交给 format_exit_messages。可以把它理解成总调度员:自己不做复杂业务,但决定先做什么、后做什么,以及最后怎么向用户和操作系统交代结果。
调用图:外部调用 1 个(arg0_dispatch_or_else)。
exec/src/cli.rs源码 ↗
这个文件主要是在教命令行解析器 clap(一个把终端参数变成 Rust 数据的工具)怎么读 codex exec 的命令。它定义了总入口 Cli,里面有是否严格读取配置、是否跳过 Git 仓库检查、是否输出 JSON、提示词从哪里来等选项。它还定义了两个子命令:resume 用来接着以前的会话聊,review 用来做代码评审。一个重要的小修正是 ResumeArgsRaw 到 ResumeArgs 的转换:当用户写 resume --last <prompt> 时,那个位置参数会被当成“新提示词”,而不是会话 ID,因为普通参数规则表达不出这种条件变化。文件里还把一些共享选项标成全局参数,让它们在子命令后面也能用。
Cli::deref91–93 ↗
fn deref(&self) -> &Self::Target
作用:让 Cli 可以像共享命令行选项 SharedCliOptions 一样被读取。这样别的代码想看通用选项时,不必总是写很长的字段路径。
数据流:输入是一个已经解析好的 Cli;它不复制也不修改任何东西,只是返回里面 shared.0 的只读引用;结果是调用者可以直接读共享选项。
调用关系:它属于 Rust 的 Deref 机制,也就是“自动借用里面那层东西”。后续业务代码拿到 Cli 后,可以自然地访问共享选项,而不用知道它外面包了一层 ExecSharedCliOptions。
Cli::deref_mut97–99 ↗
fn deref_mut(&mut self) -> &mut Self::Target
作用:让 Cli 可以像共享命令行选项一样被修改。需要调整通用选项时,调用者不用手动钻进嵌套字段。
数据流:输入是一个可修改的 Cli;它返回 shared.0 的可修改引用;结果是调用者能改里面的共享选项,Cli 本身也随之变化。
调用关系:它和 Cli::deref 配套,一个管只读,一个管可写。程序在解析后如果还要补默认值或覆盖某些选项,就可以通过这个入口改共享选项。
Cli::removed_full_auto_warning103–111 ↗
fn removed_full_auto_warning(&self) -> Option<&'static str>
作用:检查用户是否用了已经废弃的 --full-auto 参数,并给出该用什么新参数的提醒。这样老用户不会突然一头雾水。
数据流:输入是当前 Cli 里的 removed_full_auto 标记;如果它为真,就返回一段固定警告文字;如果为假,就返回空,表示不用提醒。
调用关系:这个函数通常会在命令行解析完成后被上层流程调用。它不负责打印,只负责判断“该不该警告”和“警告内容是什么”。
mark_exec_global_args157–163 ↗
fn mark_exec_global_args(cmd: clap::Command) -> clap::Command
作用:把几个共享参数标记成全局参数,让它们不只在主命令位置可用,放在子命令后面也能生效。
数据流:输入是一份 clap 命令定义;它找到 model、dangerously_bypass_approvals_and_sandbox、bypass_hook_trust 这几个参数,并把它们的 global 标记打开;输出是修改后的命令定义。
调用关系:它被 ExecSharedCliOptions::augment_args 和 ExecSharedCliOptions::augment_args_for_update 调用。也就是说,不管是新建命令规则还是更新命令规则,都走同一处补丁,避免两边行为不一致。
调用图:被 2 处调用(augment_args, augment_args_for_update);外部调用 1 个(mut_arg)。
ResumeArgs::from226–241 ↗
fn from(raw: ResumeArgsRaw) -> Self
作用:把 clap 直接解析出来的 ResumeArgsRaw 整理成真正好用的 ResumeArgs,尤其修正 --last 时位置参数的含义。
数据流:输入是原始恢复会话参数;如果用户用了 --last 且没有单独写 prompt,它会把原本可能被当成 session_id 的位置参数改当成 prompt;然后连同 last、all、图片列表一起输出成正式的 ResumeArgs。
调用关系:它是 resume 子命令解析后的关键整理步骤。ResumeArgs::from_arg_matches 和 ResumeArgs::update_from_arg_matches 都会先得到原始参数,再通过它变成程序后续真正使用的形状。
ResumeArgs::augment_args245–247 ↗
fn augment_args(cmd: clap::Command) -> clap::Command
作用:告诉 clap:resume 子命令有哪些参数,比如会话 ID、--last、--all、图片和恢复后要发送的提示词。
数据流:输入是一份 clap 命令定义;它把工作交给 ResumeArgsRaw::augment_args,按原始参数结构添加命令行规则;输出是加好规则的命令定义。
调用关系:这是 clap 构建 resume 子命令语法时用的入口。它先按原始形状声明参数,后面再靠 ResumeArgs::from 修正 clap 表达不了的特殊含义。
调用图:外部调用 1 个(augment_args)。
ResumeArgs::augment_args_for_update249–251 ↗
fn augment_args_for_update(cmd: clap::Command) -> clap::Command
作用:在更新已有命令定义时,补上 resume 子命令的参数规则。
数据流:输入是待更新的 clap 命令定义;它调用 ResumeArgsRaw::augment_args_for_update 添加或更新这些参数规则;输出是更新后的命令定义。
调用关系:它用于 clap 的参数规则更新流程。和 ResumeArgs::augment_args 一样,它只负责声明原始参数形状,真正的含义整理留给转换步骤。
调用图:外部调用 1 个(augment_args_for_update)。
ResumeArgs::from_arg_matches255–257 ↗
fn from_arg_matches(matches: &clap::ArgMatches) -> Result<Self, clap::Error>
作用:把用户输入的 resume 参数从 clap 的解析结果变成正式的 ResumeArgs。
数据流:输入是 clap 的 ArgMatches;它先调用 ResumeArgsRaw::from_arg_matches 得到原始结构,再调用 ResumeArgs::from 做含义修正;输出是可供后续恢复会话逻辑使用的参数,或解析错误。
调用关系:这是 resume 子命令从“终端文字”进入程序内部的转换口。它把底层解析交给 clap 生成的原始结构,再接上本文件自己的特殊规则。
调用图:外部调用 1 个(from_arg_matches)。
ResumeArgs::update_from_arg_matches259–262 ↗
fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> Result<(), clap::Error>
作用:用新的 clap 解析结果,重新填充一个已有的 ResumeArgs。
数据流:输入是当前可修改的 ResumeArgs 和新的 ArgMatches;它重新解析出 ResumeArgsRaw,转换成正式 ResumeArgs,然后覆盖当前对象;输出是成功或错误,成功时当前对象已变成新参数对应的状态。
调用关系:它服务于 clap 的对象更新流程。和 ResumeArgs::from_arg_matches 一样,它会经过 ResumeArgs::from,保证 --last 的特殊位置参数规则始终生效。
调用图:外部调用 1 个(from_arg_matches)。
cloud-tasks/src/cli.rs源码 ↗
这个文件主要解决一个很实际的问题:用户在终端里输入命令时,程序怎么知道他想做什么。它用 clap 这个命令行解析工具,把命令拆成几类:提交云端任务、查看任务状态、列出任务、把云端改动应用到本地、查看改动差异。每个命令都有自己的参数,比如任务编号、环境编号、分支名、尝试次数等。这里还特别给一些数字参数加了“门槛”:尝试次数只能是 1 到 4,列表数量只能是 1 到 20。这样可以在程序真正执行网络请求或改本地文件之前,就先把明显错误的输入拦下来,避免后面出现更难懂的报错。
parse_attempts52–61 ↗
fn parse_attempts(input: &str) -> Result<usize, String>
作用:这个函数检查用户填的“尝试次数”是不是合法。它保证这个数字必须是整数,而且只能在 1 到 4 之间,避免用户让云端任务跑出不支持的次数。
数据流:进去的是用户在命令行里输入的一段文字,比如 2 或 abc。函数先尝试把这段文字变成数字;如果根本不是数字,就返回一条好懂的错误信息。变成数字后,它再检查是不是 1、2、3、4 之一;合法就把数字交出去,不合法就返回错误说明。它不改动外部数据,只负责把输入过滤干净。
调用关系:它被 clap 命令行解析器当作参数校验器使用。用户执行提交任务命令里的 --attempts,或者执行应用、查看差异命令里的 --attempt 时,解析器会先把输入交给它检查;只有通过检查,后面的命令执行代码才会拿到这个次数。
parse_limit63–72 ↗
fn parse_limit(input: &str) -> Result<i64, String>
作用:这个函数检查用户填的“最多列出多少个任务”是不是合法。它把列表数量限制在 1 到 20,防止一次请求太多或填了没意义的数字。
数据流:进去的是用户给 --limit 写的文字。函数先把文字转成整数;如果转不了,就返回“必须是 1 到 20 的整数”这类错误。转成功后,它检查这个整数是否落在 1 到 20 之间;符合就返回这个数字,不符合就返回错误。它只做检查和转换,不负责真正去拿任务列表。
调用关系:它服务于列出云端任务的命令。用户运行列表命令并带上 --limit 时,clap 会调用它先确认数量没问题;确认后,后续真正查询任务列表的代码才能放心使用这个限制值。
cloud-tasks/src/lib.rs源码 ↗
可以把这个文件理解成「云端任务控制台」的前台总管。用户输入 codex cloud 后,代码先准备后端连接:设置用户代理、确认已经登录、带上认证信息。然后按用户选择走两条路:如果是 list/status/diff/apply/exec 这类一次性命令,就直接调用云端接口并打印结果;如果没有子命令,就进入终端交互界面(TUI,也就是文字版界面)。界面里会后台加载任务、环境列表和任务详情,同时监听键盘操作,比如刷新、切换环境、新建任务、查看 diff、预检应用和真正应用。为了不让界面卡住,很多慢操作会放到后台任务里,完成后再通过事件通知界面更新。文件里还带了一些小工具,比如解析任务 ID、选择第几次尝试、把状态和 diff 摘要格式化成人能读懂的文字,以及一组测试来保证这些规则不跑偏。
init_backend43–107 ↗
async fn init_backend(user_agent_suffix: &str) -> anyhow::Result<BackendContext>
作用:准备和云端任务服务通话的“电话线”。它会决定服务地址、设置请求标识、检查登录状态,并把认证信息装进 HTTP 客户端里。
数据流:进去的是一个用户代理后缀,用来标明这次请求来自哪个功能;它读取环境变量、登录信息和账户信息,必要时写错误日志;出来的是 BackendContext,里面有可用的后端客户端和基础网址。如果没登录或登录方式不对,它会打印提示并退出进程。
调用关系:所有需要访问云端的入口都会先找它:run_exec_command、run_status_command、run_list_command、run_diff_command、run_apply_command 和 run_main 都靠它拿到后端连接。它内部会调用 load_auth_manager、get_codex_user_agent、append_error_log 等工具完成准备工作。
调用图:调用 5 个内部函数(new, append_error_log, load_auth_manager, set_user_agent_suffix, get_codex_user_agent);被 6 处调用(run_apply_command, run_diff_command, run_exec_command, run_list_command, run_main, run_status_command);外部调用 7 个(new, auth_provider_from_auth, eprintln!, format!, matches!, var, exit)。
RealGitInfo::default_branch_name124–126 ↗
async fn default_branch_name(&self, path: &std::path::Path) -> Option<String>
作用:读取真实 Git 仓库的默认分支名,比如 main 或 master。这让新建云任务时能知道应该基于哪条代码线。
数据流:进去的是一个本地路径;它把路径交给 Git 工具函数查询默认分支;出来的是可能存在的分支名,查不到就返回空。
调用关系:它是 RealGitInfo 对 GitInfoProvider 接口的真实实现,主要服务于 resolve_git_ref_with_git_info;测试里会用 StubGitInfo 替代它,避免真的访问 Git。
调用图:外部调用 1 个(default_branch_name)。
RealGitInfo::current_branch_name128–130 ↗
async fn current_branch_name(&self, path: &std::path::Path) -> Option<String>
作用:读取当前工作目录所在 Git 仓库的当前分支名。用户没手动指定分支时,它通常优先使用这个分支。
数据流:进去的是本地路径;它调用 Git 工具查询当前分支;出来的是分支名或空值。
调用关系:resolve_git_ref_with_git_info 会先问它当前分支是什么;如果没有结果,才继续问默认分支。
调用图:外部调用 1 个(current_branch_name)。
resolve_git_ref133–135 ↗
async fn resolve_git_ref(branch_override: Option<&String>) -> String
作用:决定云端任务要基于哪个 Git 分支。它是给正常运行代码用的简单入口。
数据流:进去的是用户可能传入的分支名;它把真实 Git 查询器 RealGitInfo 交给更通用的 resolve_git_ref_with_git_info;出来的是最终分支名字符串。
调用关系:run_exec_command 会调用它来创建任务;新建任务的交互流程里也会用同样思路确定分支。它把具体判断工作交给 resolve_git_ref_with_git_info。
调用图:调用 1 个内部函数(resolve_git_ref_with_git_info);被 1 处调用(run_exec_command)。
resolve_git_ref_with_git_info137–159 ↗
async fn resolve_git_ref_with_git_info(
branch_override: Option<&String>,
git_info: &impl GitInfoProvider,
) -> String
作用:按优先级挑一个 Git 分支:先用用户指定的,再用当前分支,再用默认分支,最后兜底用 main。这样即使 Git 信息缺失,也不会让任务创建没分支可用。
数据流:进去的是可选的分支覆盖值和一个 Git 信息提供者;它先清理空格并检查覆盖值,再读取当前目录、当前分支和默认分支;出来的是一个一定非空的分支名。
调用关系:resolve_git_ref 调用它做真实判断;多条测试也直接调用它,用假 Git 信息验证各种兜底情况。
调用图:被 6 处调用(resolve_git_ref, branch_override_is_used_when_provided, falls_back_to_current_branch_when_default_is_missing, falls_back_to_main_when_no_git_info_is_available, prefers_current_branch_when_available, trims_override_whitespace);外部调用 3 个(current_branch_name, default_branch_name, current_dir)。
run_exec_command161–184 ↗
async fn run_exec_command(args: crate::cli::ExecCommand) -> anyhow::Result<()>
作用:执行 codex cloud exec:把一段用户请求提交成一个新的云端任务,并打印任务链接。适合脚本或命令行快速创建任务。
数据流:进去的是 exec 子命令参数,包括请求文本、环境、分支和尝试次数;它初始化后端、取得请求内容、解析环境 ID、决定 Git 分支,然后调用云端 create_task;出来是在终端打印的新任务 URL。
调用关系:run_main 在发现用户选择 Exec 子命令时调用它。它依赖 init_backend、resolve_query_input、resolve_environment_id 和 resolve_git_ref,然后把创建动作交给云端客户端。
调用图:调用 5 个内部函数(init_backend, resolve_environment_id, resolve_git_ref, resolve_query_input, task_url);被 1 处调用(run_main);外部调用 2 个(create_task, println!)。
resolve_environment_id186–229 ↗
async fn resolve_environment_id(ctx: &BackendContext, requested: &str) -> anyhow::Result<String>
作用:把用户输入的环境名或环境 ID,变成云端真正认识的环境 ID。这样用户既可以输入机器 ID,也可以输入更好记的标签。
数据流:进去的是后端上下文和用户填写的环境字符串;它先拒绝空字符串,再拉取当前工作区的环境列表,按 ID 精确匹配,或按标签忽略大小写匹配;出来的是唯一的环境 ID,找不到或标签有歧义就报错。
调用关系:run_exec_command 创建任务前会用它确定环境;run_list_command 按环境筛选任务时也会用它。它会调用 env_detect::list_environments,并用 util 构造基础网址和请求头。
调用图:调用 3 个内部函数(list_environments, build_chatgpt_headers, normalize_base_url);被 2 处调用(run_exec_command, run_list_command);外部调用 1 个(anyhow!)。
resolve_query_input231–256 ↗
fn resolve_query_input(query_arg: Option<String>) -> anyhow::Result<String>
作用:取得用户要交给云任务的文字请求。它支持直接从参数拿,也支持从标准输入管道读。
数据流:进去的是可选的命令行 query;如果 query 是普通文本就直接返回,如果是 - 或没有参数且标准输入不是终端,就从 stdin 读完整内容;出来的是请求文本。没有内容或空输入会返回错误。
调用关系:run_exec_command 用它取得 prompt。它会在需要时提示“正在从 stdin 读取”,避免用户误以为程序卡住。
调用图:被 1 处调用(run_exec_command);外部调用 5 个(new, anyhow!, eprintln!, matches!, stdin)。
parse_task_id258–277 ↗
fn parse_task_id(raw: &str) -> anyhow::Result<codex_cloud_tasks_client::TaskId>
作用:从用户输入里提取任务 ID。用户可以粘贴裸 ID,也可以粘贴完整网页链接,它都会尽量识别。
数据流:进去的是原始字符串;它去掉空白、去掉 URL 的片段和查询参数,再取最后一段路径作为 ID;出来的是 TaskId。空字符串或解析后为空会报错。
调用关系:status、diff、apply 三个命令都会先调用它。相关测试 parse_task_id_from_url_and_raw 和 collect_attempt_diffs_includes_sibling_attempts 也验证它能处理 URL 和裸 ID。
调用图:被 5 处调用(run_apply_command, run_diff_command, run_status_command, collect_attempt_diffs_includes_sibling_attempts, parse_task_id_from_url_and_raw);外部调用 2 个(bail!, TaskId)。
cmp_attempt286–298 ↗
fn cmp_attempt(lhs: &AttemptDiffData, rhs: &AttemptDiffData) -> Ordering
作用:给同一个任务的多次尝试排序。它保证第 1 次、第 2 次这类尝试按合理顺序展示和选择。
数据流:进去的是两个 AttemptDiffData;它先比较 placement(尝试位置),没有位置时再比较创建时间;出来的是排序结果。
调用关系:collect_attempt_diffs 收集完多个 diff 后用它排序,之后 select_attempt 才能按用户说的第几次尝试取到正确内容。
collect_attempt_diffs300–341 ↗
async fn collect_attempt_diffs(
backend: &dyn codex_cloud_tasks_client::CloudBackend,
task_id: &codex_cloud_tasks_client::TaskId,
) -> anyhow::Result<Vec<AttemptDiffData>>
作用:收集一个任务可用的所有 diff,包括当前尝试和同一轮里的兄弟尝试。这样用户可以查看或应用不同尝试的结果。
数据流:进去的是后端客户端和任务 ID;它先取任务文本信息,再取当前 diff,如果有 turn_id 就继续拉取兄弟尝试的 diff;最后按 cmp_attempt 排序。出来的是 diff 列表;如果一个 diff 都没有,就报“可能还在运行”。
调用关系:run_diff_command 和 run_apply_command 都先调用它,再交给 select_attempt 选择具体尝试。测试 collect_attempt_diffs_includes_sibling_attempts 用 mock 后端验证它确实会包含兄弟尝试。
调用图:被 3 处调用(run_apply_command, run_diff_command, collect_attempt_diffs_includes_sibling_attempts);外部调用 6 个(new, bail!, get_task_diff, get_task_text, list_sibling_attempts, clone)。
select_attempt343–361 ↗
fn select_attempt(
attempts: &[AttemptDiffData],
attempt: Option<usize>,
) -> anyhow::Result<&AttemptDiffData>
作用:从多次尝试里选出用户要的那一次。它把用户看到的“第 1 次、第 2 次”转换成程序内部从 0 开始的下标。
数据流:进去的是尝试列表和可选的尝试编号;没有指定时默认选第 1 次;它检查编号不能小于 1、不能超过已有数量;出来的是对应的 AttemptDiffData 引用。
调用关系:run_diff_command 用它决定打印哪个 diff,run_apply_command 用它决定应用哪个 diff。测试 select_attempt_validates_bounds 专门检查越界会报错。
调用图:被 3 处调用(run_apply_command, run_diff_command, select_attempt_validates_bounds);外部调用 3 个(bail!, is_empty, len)。
task_status_label363–370 ↗
fn task_status_label(status: &TaskStatus) -> &'static str
作用:把任务状态枚举转换成屏幕上显示的英文标签,比如 READY 或 ERROR。这样状态显示统一,不会到处手写字符串。
数据流:进去的是 TaskStatus;它按不同状态匹配固定文字;出来的是静态字符串。
调用关系:format_task_status_lines 调用它生成任务列表和状态命令里的第一行。
调用图:被 1 处调用(format_task_status_lines)。
summary_line372–409 ↗
fn summary_line(summary: &codex_cloud_tasks_client::DiffSummary, colorize: bool) -> String
作用:把 diff 摘要变成人看的短句,比如 +5/-2 • 3 files。如果终端支持颜色,它还会把新增变绿、删除变红。
数据流:进去的是 DiffSummary 和是否上色的开关;它检查有没有改动,计算新增行、删除行和文件数,并按是否支持颜色生成字符串;出来的是一行摘要文字。
调用关系:format_task_status_lines 会把它作为每个任务展示的第三行。测试通过 format_task_status_lines 间接验证它的输出。
调用图:被 1 处调用(format_task_status_lines);外部调用 1 个(format!)。
format_task_status_lines411–476 ↗
fn format_task_status_lines(
task: &codex_cloud_tasks_client::TaskSummary,
now: chrono::DateTime<Utc>,
colorize: bool,
) -> Vec<String>
作用:把一个任务整理成几行适合终端显示的文字。它把状态、标题、环境、更新时间和 diff 摘要组合起来。
数据流:进去的是任务摘要、当前时间和是否上色;它先生成状态标签,再选择环境标签或 ID,计算相对更新时间,最后加上 diff 摘要;出来的是多行字符串。
调用关系:run_status_command 直接打印它的结果;format_task_list_lines 也用它给每个任务生成内容。两条测试检查有 diff 和无 diff 的显示格式。
调用图:调用 3 个内部函数(summary_line, task_status_label, format_relative_time);被 4 处调用(format_task_list_lines, run_status_command, format_task_status_lines_with_diff_and_label, format_task_status_lines_without_diff_falls_back);外部调用 2 个(new, format!)。
format_task_list_lines478–495 ↗
fn format_task_list_lines(
tasks: &[codex_cloud_tasks_client::TaskSummary],
base_url: &str,
now: chrono::DateTime<Utc>,
colorize: bool,
) -> Vec<String>
作用:把多个任务排版成完整任务列表。每个任务前面会带可打开的网页 URL。
数据流:进去的是任务数组、基础网址、当前时间和颜色开关;它逐个生成任务 URL,再缩进加入 format_task_status_lines 的内容,并在任务之间插空行;出来的是整页文本行。
调用关系:run_list_command 在非 JSON 输出时调用它。测试 format_task_list_lines_formats_urls 验证 URL 和多任务排版是否正确。
调用图:调用 2 个内部函数(format_task_status_lines, task_url);被 2 处调用(run_list_command, format_task_list_lines_formats_urls);外部调用 5 个(new, new, iter, len, format!)。
run_status_command497–511 ↗
async fn run_status_command(args: crate::cli::StatusCommand) -> anyhow::Result<()>
作用:执行 codex cloud status:查看某个云任务当前是不是准备好了。它常用于脚本里判断任务是否完成。
数据流:进去的是 status 子命令参数;它初始化后端、解析任务 ID、拉取任务摘要、格式化并打印状态行;如果任务不是 READY,会用退出码 1 表示还不能继续。
调用关系:run_main 在 Status 子命令分支调用它。它依赖 init_backend、parse_task_id 和 format_task_status_lines。
调用图:调用 3 个内部函数(format_task_status_lines, init_backend, parse_task_id);被 1 处调用(run_main);外部调用 6 个(now, get_task_summary, matches!, println!, exit, on)。
run_list_command513–578 ↗
async fn run_list_command(args: crate::cli::ListCommand) -> anyhow::Result<()>
作用:执行 codex cloud list:列出云端任务。它支持按环境筛选、分页,也支持输出 JSON 给脚本读取。
数据流:进去的是 list 子命令参数;它初始化后端,必要时解析环境 ID,然后请求任务列表;如果指定 JSON,就打印结构化 JSON,否则打印人看的列表和下一页提示;出来是终端输出。
调用关系:run_main 在 List 子命令分支调用它。它会用 resolve_environment_id 处理环境筛选,用 format_task_list_lines 生成普通文本。
调用图:调用 3 个内部函数(format_task_list_lines, init_backend, resolve_environment_id);被 1 处调用(run_main);外部调用 6 个(now, list_tasks, format!, println!, json!, on)。
run_diff_command580–587 ↗
async fn run_diff_command(args: crate::cli::DiffCommand) -> anyhow::Result<()>
作用:执行 codex cloud diff:把某个任务生成的代码改动打印出来。用户可以指定看第几次尝试。
数据流:进去的是 diff 子命令参数;它初始化后端、解析任务 ID、收集所有尝试的 diff、选择目标尝试;出来是在标准输出打印原始 diff 文本。
调用关系:run_main 在 Diff 子命令分支调用它。它把收集交给 collect_attempt_diffs,把选择交给 select_attempt。
调用图:调用 4 个内部函数(collect_attempt_diffs, init_backend, parse_task_id, select_attempt);被 1 处调用(run_main);外部调用 1 个(print!)。
run_apply_command589–608 ↗
async fn run_apply_command(args: crate::cli::ApplyCommand) -> anyhow::Result<()>
作用:执行 codex cloud apply:把某个云任务生成的 diff 应用到当前代码环境。它是把云端结果落到本地的重要一步。
数据流:进去的是 apply 子命令参数;它初始化后端、解析任务 ID、收集并选择 diff,然后调用云端 apply_task;出来是应用结果消息。如果结果不是成功,会用退出码 1 表示失败或部分成功。
调用关系:run_main 在 Apply 子命令分支调用它。它复用 collect_attempt_diffs 和 select_attempt,最后把真正应用动作交给 CloudBackend。
调用图:调用 4 个内部函数(collect_attempt_diffs, init_backend, parse_task_id, select_attempt);被 1 处调用(run_main);外部调用 4 个(apply_task, matches!, println!, exit)。
level_from_status610–616 ↗
fn level_from_status(status: codex_cloud_tasks_client::ApplyStatus) -> app::ApplyResultLevel
作用:把后端返回的应用状态,转换成界面里使用的结果等级。这样 UI 不需要直接认识后端枚举。
数据流:进去的是 ApplyStatus;它按成功、部分成功、错误三种情况映射;出来的是 app::ApplyResultLevel。
调用关系:spawn_preflight 在预检完成后调用它,用来决定弹窗里显示成功、警告还是错误。
调用图:被 1 处调用(spawn_preflight)。
spawn_preflight618–678 ↗
fn spawn_preflight(
app: &mut app::App,
backend: &Arc<dyn codex_cloud_tasks_client::CloudBackend>,
tx: &UnboundedSender<app::AppEvent>,
frame_tx: &UnboundedSender<Instant>,
title:
作用:启动一次“应用前预检”。预检像先试穿衣服:看看 diff 能不能应用、有哪些冲突或跳过的文件,但还不真正改代码。
数据流:进去的是当前 App 状态、后端、事件发送器、刷新帧发送器、任务标题和 ApplyJob;它先检查有没有其他应用或预检正在跑,然后标记忙碌、启动后台任务;后台任务调用 apply_task_preflight,完成后发出 ApplyPreflightFinished 事件。返回值表示是否成功启动。
调用关系:交互界面里用户按应用相关按键时会用它。它把慢的网络预检放进 tokio::spawn,完成后由 run_main 的事件循环接收结果并更新弹窗。
调用图:调用 1 个内部函数(level_from_status);外部调用 8 个(from_millis, now, clone, send, new, apply_task_preflight, format!, spawn)。
spawn_apply680–728 ↗
fn spawn_apply(
app: &mut app::App,
backend: &Arc<dyn codex_cloud_tasks_client::CloudBackend>,
tx: &UnboundedSender<app::AppEvent>,
frame_tx: &UnboundedSender<Instant>,
job: ApplyJ
作用:启动真正的应用操作,把选中的 diff 应用到目标环境。它会防止用户同时启动多个应用。
数据流:进去的是 App 状态、后端、事件发送器、刷新帧发送器和 ApplyJob;它检查是否已有应用或预检在进行,设置忙碌状态,并在后台调用 apply_task;完成后发送 ApplyFinished 事件。返回值表示是否成功启动。
调用关系:交互界面的应用确认弹窗里,用户确认后会调用它。真正的结果不直接在这里处理,而是交给 run_main 的事件循环收到事件后处理。
调用图:外部调用 7 个(from_millis, now, clone, send, apply_task, format!, spawn)。
run_main735–2020 ↗
async fn run_main(cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()>
作用:这是 codex cloud 子命令的主入口。它决定是运行一次性命令,还是打开完整的终端交互界面。
数据流:进去的是解析好的 Cli 参数和一个未使用的沙箱路径;如果有子命令,它分发给对应 run_*_command;否则初始化日志和后端,切换终端到交互模式,创建 App 状态,启动后台加载任务和环境,然后进入键盘事件与后台事件循环。退出前会恢复终端。
调用关系:它位于整个文件的中心:调用 run_exec_command、run_status_command、run_list_command、run_diff_command、run_apply_command,也直接协调 app、ui、env_detect、util 和 CloudBackend。后台任务通过 AppEvent 回到它这里更新界面。
调用图:调用 13 个内部函数(new, load_tasks, autodetect_environment_id, list_environments, init_backend, run_apply_command, run_diff_command, run_exec_command, run_list_command, run_status_command (+3 more));外部调用 21 个(clone, new, try_from_default_env, new, now, from_std, EnvironmentAutodetected, EnvironmentsLoaded, execute!, format! (+11 more))。
conversation_lines2025–2049 ↗
fn conversation_lines(prompt: Option<String>, messages: &[String]) -> Vec<String>
作用:把用户 prompt 和助手消息拼成适合界面显示的普通文本行。它让任务详情页能像聊天记录一样展示内容。
数据流:进去的是可选用户提示和助手消息列表;它先加 user: 标签和用户文本,再加 assistant: 标签和多段助手文本;如果什么都没有,就输出 <no output>。出来的是字符串行数组。
调用关系:run_main 在处理 DetailsMessagesLoaded 和 AttemptsLoaded 事件时调用它,把后端返回的消息转换成 diff overlay 能显示的行。
pretty_lines_from_error2053–2129 ↗
fn pretty_lines_from_error(raw: &str) -> Vec<String>
作用:把又长又难懂的 HTTP 错误,整理成任务详情页里能看懂的几行提示。它尽量告诉用户是没 diff、没消息,还是任务仍在运行。
数据流:进去的是原始错误字符串;它先识别常见错误,再尝试从错误里嵌入的 JSON 解析助手错误码、状态和最新事件;出来的是几行友好的说明,解析失败时会附上一段截短的原始错误。
调用关系:run_main 处理 DetailsFailed 事件时调用它,让错误也能在详情弹层里显示得清楚,而不是把整段底层错误直接丢给用户。
tests::StubGitInfo::new2155–2160 ↗
fn new(default_branch: Option<String>, current_branch: Option<String>) -> Self
作用:创建一个假的 Git 信息提供者,专门给测试用。这样测试不用真的依赖本地 Git 仓库。
数据流:进去的是可选默认分支和可选当前分支;它把这两个值存进 StubGitInfo;出来的是一个可被测试使用的假对象。
调用关系:多条 resolve_git_ref_with_git_info 测试都会调用它,模拟不同 Git 状态。
tests::StubGitInfo::default_branch_name2164–2166 ↗
async fn default_branch_name(&self, _path: &std::path::Path) -> Option<String>
作用:在测试里返回预设的默认分支名。它替代真实 Git 查询。
数据流:进去的路径会被忽略;它返回 StubGitInfo 里保存的 default_branch 克隆值;出来的是可选分支名。
调用关系:resolve_git_ref_with_git_info 的测试通过这个函数控制“默认分支是否存在”。
tests::StubGitInfo::current_branch_name2168–2170 ↗
async fn current_branch_name(&self, _path: &std::path::Path) -> Option<String>
作用:在测试里返回预设的当前分支名。它让测试能稳定验证分支优先级。
数据流:进去的路径会被忽略;它返回 StubGitInfo 里保存的 current_branch 克隆值;出来的是可选分支名。
调用关系:resolve_git_ref_with_git_info 的测试通过这个函数控制“当前分支是否存在”。
tests::branch_override_is_used_when_provided2174–2182 ↗
async fn branch_override_is_used_when_provided()
作用:验证用户明确指定分支时,程序一定使用这个分支。这样不会意外改用 Git 当前分支。
数据流:进去的是测试写死的覆盖分支和没有 Git 信息的 StubGitInfo;它调用 resolve_git_ref_with_git_info;出来的结果必须等于覆盖分支。
调用关系:它直接测试 resolve_git_ref_with_git_info 的最高优先级规则。
调用图:调用 1 个内部函数(resolve_git_ref_with_git_info);外部调用 2 个(assert_eq!, new)。
tests::trims_override_whitespace2185–2193 ↗
async fn trims_override_whitespace()
作用:验证用户输入分支名前后有空格时,程序会自动去掉。这样命令行复制粘贴更宽容。
数据流:进去的是带空格的分支字符串;它调用 resolve_git_ref_with_git_info;出来的结果必须是不带空格的分支名。
调用关系:它保护 resolve_git_ref_with_git_info 的输入清理行为。
调用图:调用 1 个内部函数(resolve_git_ref_with_git_info);外部调用 2 个(assert_eq!, new)。
tests::prefers_current_branch_when_available2196–2207 ↗
async fn prefers_current_branch_when_available()
作用:验证没有手动指定分支时,程序优先使用当前分支,而不是默认分支。
数据流:进去的是一个同时有默认分支和当前分支的 StubGitInfo;它调用 resolve_git_ref_with_git_info;出来必须是当前分支。
调用关系:它确保 resolve_git_ref_with_git_info 的分支选择顺序符合用户直觉。
调用图:调用 1 个内部函数(resolve_git_ref_with_git_info);外部调用 2 个(assert_eq!, new)。
tests::falls_back_to_current_branch_when_default_is_missing2210–2218 ↗
async fn falls_back_to_current_branch_when_default_is_missing()
作用:验证只有当前分支可用时,程序能正常使用当前分支。名字里提到 default 缺失,是为了覆盖兜底场景。
数据流:进去的是没有默认分支、但有当前分支的 StubGitInfo;它调用 resolve_git_ref_with_git_info;出来必须是当前分支。
调用关系:它继续验证 resolve_git_ref_with_git_info 在 Git 信息不完整时仍能给出合理答案。
调用图:调用 1 个内部函数(resolve_git_ref_with_git_info);外部调用 2 个(assert_eq!, new)。
tests::falls_back_to_main_when_no_git_info_is_available2221–2229 ↗
async fn falls_back_to_main_when_no_git_info_is_available()
作用:验证完全拿不到 Git 信息时,程序会兜底使用 main。这样创建任务不会因为分支未知而没有结果。
数据流:进去的是默认分支和当前分支都为空的 StubGitInfo;它调用 resolve_git_ref_with_git_info;出来必须是 main。
调用关系:它保护 resolve_git_ref_with_git_info 的最后一道兜底规则。
调用图:调用 1 个内部函数(resolve_git_ref_with_git_info);外部调用 2 个(assert_eq!, new)。
tests::format_task_status_lines_with_diff_and_label2232–2258 ↗
fn format_task_status_lines_with_diff_and_label()
作用:验证有环境标签、有 diff 摘要的任务,会被格式化成预期的三行文字。
数据流:进去的是测试构造的 READY 任务和当前时间;它调用 format_task_status_lines;出来的行数组要包含状态标题、环境标签时间、增删行摘要。
调用关系:它测试 format_task_status_lines,也间接覆盖 task_status_label、summary_line 和 format_relative_time 的组合效果。
调用图:调用 1 个内部函数(format_task_status_lines);外部调用 3 个(now, assert_eq!, new)。
tests::format_task_status_lines_without_diff_falls_back2261–2283 ↗
fn format_task_status_lines_without_diff_falls_back()
作用:验证没有 diff 的任务会显示 no diff,并在没有环境标签时退回显示环境 ID。
数据流:进去的是 Pending 状态、环境标签为空、diff 摘要为默认值的任务;它调用 format_task_status_lines;出来必须符合预期三行。
调用关系:它保护 format_task_status_lines 在信息不完整时的显示兜底。
调用图:调用 1 个内部函数(format_task_status_lines);外部调用 4 个(now, assert_eq!, default, new)。
tests::format_task_list_lines_formats_urls2286–2336 ↗
fn format_task_list_lines_formats_urls()
作用:验证任务列表输出会包含正确 URL,并且多个任务之间排版正确。
数据流:进去的是两个测试任务、基础网址和当前时间;它调用 format_task_list_lines;出来的行数组必须包含两个任务 URL、缩进状态行和中间空行。
调用关系:它测试 format_task_list_lines,并间接测试每个任务内部仍由 format_task_status_lines 负责排版。
调用图:调用 1 个内部函数(format_task_list_lines);外部调用 3 个(now, assert_eq!, vec!)。
tests::collect_attempt_diffs_includes_sibling_attempts2339–2350 ↗
async fn collect_attempt_diffs_includes_sibling_attempts()
作用:验证收集 diff 时不只拿当前尝试,也会拿兄弟尝试。这样 best-of 或多次尝试不会被漏掉。
数据流:进去的是 MockClient 和从 URL 解析出的任务 ID;它调用 collect_attempt_diffs;出来的尝试列表必须有两个,并且按 placement 排好且 diff 非空。
调用关系:它测试 parse_task_id 和 collect_attempt_diffs 的配合,使用 mock 后端避免真实联网。
调用图:调用 2 个内部函数(collect_attempt_diffs, parse_task_id);外部调用 2 个(assert!, assert_eq!)。
tests::select_attempt_validates_bounds2353–2362 ↗
fn select_attempt_validates_bounds()
作用:验证选择尝试时,合法编号能取到结果,越界编号会报错。
数据流:进去的是只有一个元素的尝试列表;它调用 select_attempt 选择第 1 次和第 2 次;出来第 1 次成功,第 2 次失败。
调用关系:它保护 select_attempt 的边界检查,避免用户输入不存在的尝试编号时程序乱取数据。
调用图:调用 1 个内部函数(select_attempt);外部调用 3 个(assert!, assert_eq!, vec!)。
tests::parse_task_id_from_url_and_raw2365–2372 ↗
fn parse_task_id_from_url_and_raw()
作用:验证任务 ID 解析既支持裸 ID,也支持完整 URL,并且拒绝空输入。
数据流:进去的是裸任务 ID、带查询参数的任务 URL 和空白字符串;它分别调用 parse_task_id;出来前两个解析成功,空白输入失败。
调用关系:它直接测试 parse_task_id,保证 status、diff、apply 这些命令能接受用户常见的粘贴方式。
调用图:调用 1 个内部函数(parse_task_id);外部调用 2 个(assert!, assert_eq!)。
tests::composer_input_renders_typed_characters2376–2401 ↗
fn composer_input_renders_typed_characters()
作用:验证新建任务输入框真的能把用户输入的字符画出来,并且提示栏能显示快捷键。这个测试被标记为忽略,因为很慢。
数据流:进去的是一个新 ComposerInput 和模拟按键 a;它把输入渲染到缓冲区,再检查缓冲区里是否有 a,随后设置提示项并检查底部是否出现 ⌃O env;出来是断言通过或失败。
调用关系:它测试的是交互界面里新建任务输入组件的可见效果,和 run_main 中的新任务编辑流程有关,但默认不会在普通测试里运行。
调用图:调用 1 个内部函数(new);外部调用 7 个(empty, Char, new, new, assert!, panic!, vec!)。
桌面和远程 app-server 启动
这些命令用于打开桌面应用或启动远程控制 app-server 模式,包括特定平台的桌面启动行为。
cli/src/app_cmd.rs源码 ↗
这个文件解决的是:用户在命令行里想用 Codex Desktop 打开某个项目文件夹时,程序怎么知道要打开哪里,以及如果桌面应用还没装该怎么办。AppCommand 描述了这个命令能收哪些输入:一个工作区路径,默认是当前目录;还有一个高级选项,可以指定桌面应用安装包的下载地址。run_app 则是执行入口。它先把用户给的路径尽量转换成“绝对、规范”的路径,这样后面不会因为相对路径弄错地方;如果转换失败,就保留原路径。然后它只在 macOS 和 Windows 上继续执行,把工作区路径和可选下载地址交给 desktop_app 模块。真正的打开应用、发现没安装就安装这些重活,不在这里做。这个文件更像命令行参数和桌面应用启动流程之间的一座小桥。
run_app15–25 ↗
async fn run_app(cmd: AppCommand) -> anyhow::Result<()>
作用:执行“打开 Codex Desktop”这个命令。它先整理用户给的工作区路径,再把任务交给桌面应用启动/安装流程。
数据流:输入是一份 AppCommand,里面有用户指定的路径和可选的下载地址。函数先调用系统的 canonicalize 尝试把路径变成标准的完整路径;如果做不到,就继续用原来的路径。之后在 macOS 或 Windows 上,把整理好的工作区路径和下载地址传给 run_app_open_or_install,最后把对方返回的成功或失败结果原样交回去。
调用关系:它由 cli_main 在用户触发对应命令时调用。它自己不直接打开窗口、下载安装包,而是做完路径准备后,把后续工作交给 run_app_open_or_install;这样命令行层只负责接收和整理输入,桌面应用层负责真正执行。
调用图:调用 1 个内部函数(run_app_open_or_install);被 1 处调用(cli_main);外部调用 1 个(canonicalize)。
cli/src/desktop_app/mod.rs源码 ↗
这个文件像一个“分诊台”。用户想通过命令行打开桌面应用,或者发现没装时先安装应用,但 macOS 和 Windows 的安装、启动方式完全不同。如果让上层到处判断系统类型,代码会很乱,也容易出错。这里用 Rust 的条件编译(编译时按操作系统只保留对应代码)来解决:在 macOS 上只接入 mac 模块,在 Windows 上只接入 windows 模块。外面看到的函数名都叫 run_app_open_or_install,意思是“运行当前系统的打开或安装流程”。它自己不做安装细节,只把工作转交给当前操作系统专用的函数。这样主流程只需要说“帮我打开或安装桌面应用”,不必知道每个平台的规矩。
run_app_open_or_install17–22 ↗
async fn run_app_open_or_install(
workspace: std::path::PathBuf,
download_url_override: Option<String>,
) -> anyhow::Result<()>
作用:这是外部调用桌面应用打开/安装逻辑时用的统一函数。调用者传入工作目录和可选的下载地址,它会根据当前操作系统,把事情交给 macOS 或 Windows 的专门流程去做。
数据流:输入是一条 workspace 路径,表示要在哪个工作区上下文里操作;还有一个可选的 download_url_override,表示是否临时指定应用下载地址。函数读取当前编译目标所对应的系统版本:在 macOS 构建中调用 run_mac_app_open_or_install,在 Windows 构建中调用 run_windows_app_open_or_install。最后把对方执行成功或失败的结果原样返回;它本身不改文件、不下载安装,只负责转交。
调用关系:它由 run_app 在需要启动桌面应用流程时调用。它处在主命令和系统专用实现之间,像中间转接员:上层 run_app 不需要知道平台差异;下层 run_mac_app_open_or_install 或 run_windows_app_open_or_install 才真正处理各自系统上的安装和打开细节。
调用图:调用 2 个内部函数(run_mac_app_open_or_install, run_windows_app_open_or_install);被 1 处调用(run_app)。
cli/src/desktop_app/mac.rs源码 ↗
在 macOS 上,桌面应用通常是一个 .app 文件,安装包常见格式是 .dmg,可以理解成一张临时插上的“虚拟光盘”。这个文件做的事很实际:用户想从命令行打开当前工作区到 Codex Desktop,如果已经安装,就直接用系统的 open 命令打开;如果没找到,就按电脑芯片类型选择合适的下载地址,调用 curl 下载 dmg,用 hdiutil 挂载它,找到里面的 Codex.app,再用 ditto 复制到 Applications 文件夹。最后它会生成一个 codex://... 深度链接,把工作区路径交给桌面应用。它还会尽量清理挂载的 dmg,避免留下临时磁盘。
run_mac_app_open_or_install12–42 ↗
async fn run_mac_app_open_or_install(
workspace: PathBuf,
download_url_override: Option<String>,
) -> anyhow::Result<()>
作用:这是 macOS 打开 Codex Desktop 的总入口。它先看电脑上有没有现成的应用,没有就自动下载并安装,然后把指定工作区交给桌面应用打开。
数据流:进去的是一个工作区路径,以及一个可选的下载地址覆盖值。它先读取本机常见安装位置;如果找到 Codex.app,就直接打开。找不到时,它选择默认或指定的 dmg 下载地址,完成下载安装,最后再打开应用。出来的是成功或失败;过程中可能会下载文件、安装应用、启动桌面程序,并在终端打印进度。
调用关系:它由更上层的 run_app_open_or_install 调用,像总调度员一样先交给 find_existing_codex_app_path 找应用,再按情况交给 download_and_install_codex_to_user_applications 安装,最后交给 open_codex_app 启动应用。
调用图:调用 3 个内部函数(download_and_install_codex_to_user_applications, find_existing_codex_app_path, open_codex_app);被 1 处调用(run_app_open_or_install);外部调用 1 个(eprintln!)。
is_apple_silicon_mac44–64 ↗
fn is_apple_silicon_mac() -> bool
作用:这个函数判断当前 Mac 更像 Apple Silicon 机器,还是传统 Intel 机器。这样下载时才能选对安装包,避免装错架构导致应用跑不起来。
数据流:它读取 Rust 看到的 CPU 架构,也向 macOS 系统询问一些标志位,比如是否在 Rosetta 转译环境里、硬件是否支持 arm64。它把这些信息合并成一个布尔值:是 Apple Silicon 就返回 true,否则返回 false。
调用关系:它服务于 run_mac_app_open_or_install 的下载地址选择。虽然它不负责下载,但它决定该用 ARM64 版本还是 x64 版本,是安装流程前面的分岔判断。
find_existing_codex_app_path66–70 ↗
fn find_existing_codex_app_path() -> Option<PathBuf>
作用:这个函数负责看看 Codex Desktop 是否已经装在常见位置。找到的话,后面就不用重新下载和安装了。
数据流:它先拿到一组候选路径,然后逐个检查这些路径是不是一个文件夹,也就是 macOS 的应用包目录。第一个存在的路径会被返回;都不存在就返回空。
调用关系:它被 run_mac_app_open_or_install 最先调用,用来决定走“直接打开”还是“下载安装”。它自己把候选路径的准备工作交给 candidate_codex_app_paths。
调用图:调用 1 个内部函数(candidate_codex_app_paths);被 1 处调用(run_mac_app_open_or_install)。
candidate_codex_app_paths72–78 ↗
fn candidate_codex_app_paths() -> Vec<PathBuf>
作用:这个函数列出可能安装 Codex.app 的地方。它相当于给搜索动作准备一张“去哪里找”的清单。
数据流:它固定加入系统级的 /Applications/Codex.app,再读取 HOME 环境变量;如果能知道用户家目录,就再加入用户自己的 Applications/Codex.app。输出是一组路径。
调用关系:它被 find_existing_codex_app_path 调用,只负责提供候选地址,不判断是否真的存在。
调用图:被 1 处调用(find_existing_codex_app_path);外部调用 3 个(from, var_os, vec!)。
open_codex_app80–103 ↗
async fn open_codex_app(app_path: &Path, workspace: &Path) -> anyhow::Result<()>
作用:这个函数真正启动 Codex Desktop,并告诉它要打开哪个工作区。它用的是 macOS 自带的 open 命令。
数据流:进去的是 Codex.app 的路径和工作区路径。它先把工作区路径做成 codex://threads/new?... 这样的深度链接,然后运行 open -a 应用路径 链接。如果系统命令成功,就返回成功;如果失败,就把退出状态包装成错误。
调用关系:它被 run_mac_app_open_or_install 在两种情况下调用:已有应用时直接调用,刚安装完也调用。它把生成链接的细节交给 codex_new_thread_url,把实际启动交给系统的 open 命令。
调用图:调用 1 个内部函数(codex_new_thread_url);被 1 处调用(run_mac_app_open_or_install);外部调用 3 个(bail!, new, eprintln!)。
codex_new_thread_url105–111 ↗
fn codex_new_thread_url(workspace: &Path) -> String
作用:这个函数把一个本地工作区路径变成 Codex Desktop 能识别的打开链接。它解决的是“命令行怎么把路径安全地传给桌面应用”的问题。
数据流:进去的是一个文件路径。它把路径转成字符串,再按网址查询参数的规则编码,避免空格、井号等特殊字符把链接弄坏。输出是形如 codex://threads/new?path=... 的字符串。
调用关系:它被 open_codex_app 调用来准备启动参数,也被测试 tests::codex_new_thread_url_encodes_workspace_path 验证编码是否正确。
调用图:被 2 处调用(open_codex_app, codex_new_thread_url_encodes_workspace_path);外部调用 5 个(as_os_str, as_ref, new, format!, new)。
download_and_install_codex_to_user_applications113–146 ↗
async fn download_and_install_codex_to_user_applications(dmg_url: &str) -> anyhow::Result<PathBuf>
作用:这个函数负责完整的下载安装流程。它把下载 dmg、挂载、找到应用、复制安装、卸载 dmg 这些步骤连成一条安全的流水线。
数据流:进去的是 dmg 下载地址。它创建临时目录,把 dmg 下载到里面,挂载这个 dmg,查找里面的 Codex.app,然后尝试安装到 Applications 目录。无论安装成不成功,它都会尽量卸载挂载点。输出是安装好的 Codex.app 路径,或者失败原因。
调用关系:它被 run_mac_app_open_or_install 在本机没有应用时调用。它依次把具体工作交给 download_dmg、mount_dmg、find_codex_app_in_mount、install_codex_app_bundle 和 detach_dmg,自己负责保证顺序和收尾。
调用图:调用 5 个内部函数(detach_dmg, download_dmg, find_codex_app_in_mount, install_codex_app_bundle, mount_dmg);被 1 处调用(run_mac_app_open_or_install);外部调用 2 个(new, eprintln!)。
install_codex_app_bundle148–178 ↗
async fn install_codex_app_bundle(app_in_volume: &Path) -> anyhow::Result<PathBuf>
作用:这个函数把挂载出来的 Codex.app 复制到用户能长期使用的 Applications 文件夹。它会尝试多个安装位置,提高成功率。
数据流:进去的是 dmg 里找到的 app 路径。它先拿到可尝试的 Applications 目录,逐个创建目录;如果目标 Codex.app 已存在,就直接认为可用;否则调用复制命令安装。成功时输出目标路径;所有位置都失败才报错。
调用关系:它由 download_and_install_codex_to_user_applications 在找到 dmg 里的应用后调用。它把候选目录交给 candidate_applications_dirs,把实际复制交给 copy_app_bundle。
调用图:调用 2 个内部函数(candidate_applications_dirs, copy_app_bundle);被 1 处调用(download_and_install_codex_to_user_applications);外部调用 3 个(bail!, eprintln!, create_dir_all)。
candidate_applications_dirs180–184 ↗
fn candidate_applications_dirs() -> anyhow::Result<Vec<PathBuf>>
作用:这个函数列出可以安装应用的 Applications 目录。它先尝试系统级目录,再尝试用户自己的目录。
数据流:它创建一个路径列表,先放入 /Applications,再通过 user_applications_dir 得到 ~/Applications。输出是这两个候选目录,或者因为找不到 HOME 等原因失败。
调用关系:它被 install_codex_app_bundle 调用,为安装步骤提供目的地清单。它自己依赖 user_applications_dir 获取用户级目录。
调用图:调用 1 个内部函数(user_applications_dir);被 1 处调用(install_codex_app_bundle);外部调用 1 个(vec!)。
download_dmg186–205 ↗
async fn download_dmg(url: &str, dest: &Path) -> anyhow::Result<()>
作用:这个函数用 curl 下载 Codex Desktop 的 dmg 安装包。curl 是系统里常用的网络下载工具。
数据流:进去的是下载网址和保存位置。它运行 curl -fL,带 3 次重试和短暂延迟,把文件写到目标路径。命令成功就返回成功;如果下载失败或 curl 启动不了,就返回错误。
调用关系:它由 download_and_install_codex_to_user_applications 在安装流程开头调用。它只负责把安装包拿到本地,后面的挂载和安装交给其他函数。
调用图:被 1 处调用(download_and_install_codex_to_user_applications);外部调用 3 个(bail!, new, eprintln!)。
mount_dmg207–229 ↗
async fn mount_dmg(dmg_path: &Path) -> anyhow::Result<PathBuf>
作用:这个函数把下载好的 dmg 挂载成 macOS 能浏览的临时卷。可以把它想成把一张虚拟光盘插进电脑。
数据流:进去的是 dmg 文件路径。它调用 hdiutil attach -nobrowse -readonly 只读挂载安装包,读取命令输出,然后从输出文字里解析出挂载点,比如 /Volumes/Codex。成功时输出这个挂载路径,失败时带上系统错误信息。
调用关系:它由 download_and_install_codex_to_user_applications 在下载完成后调用。它把解析挂载点的细活交给 parse_hdiutil_attach_mount_point。
调用图:调用 1 个内部函数(parse_hdiutil_attach_mount_point);被 1 处调用(download_and_install_codex_to_user_applications);外部调用 3 个(from_utf8_lossy, bail!, new)。
detach_dmg231–243 ↗
async fn detach_dmg(mount_point: &Path) -> anyhow::Result<()>
作用:这个函数卸载之前挂载的 dmg。它的作用是收拾现场,避免系统里一直留着一个临时安装盘。
数据流:进去的是挂载点路径。它调用 hdiutil detach 挂载点。命令成功就返回成功;失败就返回带状态码的错误。
调用关系:它由 download_and_install_codex_to_user_applications 在安装尝试结束后调用。即使安装阶段出错,这个收尾动作也会尽量执行。
调用图:被 1 处调用(download_and_install_codex_to_user_applications);外部调用 2 个(bail!, new)。
find_codex_app_in_mount245–268 ↗
fn find_codex_app_in_mount(mount_point: &Path) -> anyhow::Result<PathBuf>
作用:这个函数在挂载出来的 dmg 里寻找真正的 .app 应用包。因为不同安装包里应用的位置或名字可能略有不同,所以它做了一个小范围搜索。
数据流:进去的是 dmg 的挂载目录。它先看根目录下有没有 Codex.app;如果没有,就遍历挂载目录下的项目,找第一个扩展名是 .app 且是文件夹的项目。找到就返回路径,找不到就报错。
调用关系:它由 download_and_install_codex_to_user_applications 在 mount_dmg 之后调用。它找到的路径会交给 install_codex_app_bundle 去复制安装。
调用图:被 1 处调用(download_and_install_codex_to_user_applications);外部调用 3 个(join, bail!, read_dir)。
copy_app_bundle270–282 ↗
async fn copy_app_bundle(src_app: &Path, dest_app: &Path) -> anyhow::Result<()>
作用:这个函数用 macOS 的 ditto 命令复制 .app 应用包。.app 看起来像一个文件,其实是个目录,用专门工具复制更稳妥。
数据流:进去的是源 app 路径和目标 app 路径。它运行 ditto 源 目标。复制成功就返回成功;命令失败就返回错误。
调用关系:它被 install_codex_app_bundle 调用,是安装动作里真正把应用放到 Applications 目录的那一步。
调用图:被 1 处调用(install_codex_app_bundle);外部调用 2 个(bail!, new)。
user_applications_dir284–287 ↗
fn user_applications_dir() -> anyhow::Result<PathBuf>
作用:这个函数算出当前用户自己的 Applications 文件夹位置。这个位置通常是 ~/Applications,不用管理员权限也更可能能写入。
数据流:它读取 HOME 环境变量,也就是用户家目录。如果 HOME 存在,就拼出 Applications 子目录并返回;如果 HOME 不存在,就返回错误。
调用关系:它被 candidate_applications_dirs 调用,用来补充系统级 /Applications 之外的备用安装位置。
调用图:被 1 处调用(candidate_applications_dirs);外部调用 2 个(from, var_os)。
parse_hdiutil_attach_mount_point289–301 ↗
fn parse_hdiutil_attach_mount_point(output: &str) -> Option<String>
作用:这个函数从 hdiutil attach 的输出文字里找出 dmg 被挂载到了哪里。它解决的是“系统命令只给一段文本,程序需要从里面抠出路径”的问题。
数据流:进去的是命令输出字符串。它逐行查找包含 /Volumes/ 的行,优先按制表符切分取最后一段;如果没有制表符,就按空白字段找以 /Volumes/ 开头的内容。找到就返回挂载路径字符串,找不到就返回空。
调用关系:它被 mount_dmg 调用,是挂载流程里的解析器。相关测试会检查它能处理普通制表符输出,也能处理挂载点名字里带空格的情况。
调用图:被 1 处调用(mount_dmg)。
tests::parses_mount_point_from_tab_separated_hdiutil_output311–317 ↗
fn parses_mount_point_from_tab_separated_hdiutil_output()
作用:这个测试确认解析器能读懂 hdiutil 常见的制表符分隔输出。它防止以后改代码时把最普通的挂载点解析弄坏。
数据流:进去的是一段模拟的 hdiutil 输出。测试调用解析函数,期望得到 /Volumes/Codex。如果结果不一样,测试就失败。
调用关系:它直接验证 parse_hdiutil_attach_mount_point 的基础情况,给 mount_dmg 依赖的文本解析能力兜底。
调用图:外部调用 1 个(assert_eq!)。
tests::parses_mount_point_with_spaces320–326 ↗
fn parses_mount_point_with_spaces()
作用:这个测试确认挂载点名称里有空格时也能正确解析。很多 macOS 卷名会带空格,所以这是一个容易出错但很重要的情况。
数据流:进去的是一段挂载名为 Codex Installer 的模拟输出。测试期望解析结果完整保留 /Volumes/Codex Installer,而不是只拿到一半。
调用关系:它补充验证 parse_hdiutil_attach_mount_point 的边界情况,避免 mount_dmg 遇到带空格的卷名时失败。
调用图:外部调用 1 个(assert_eq!)。
tests::codex_new_thread_url_encodes_workspace_path329–347 ↗
fn codex_new_thread_url_encodes_workspace_path()
作用:这个测试确认工作区路径会被正确放进 Codex 深度链接里。尤其检查空格和 # 这类特殊字符不会破坏网址。
数据流:进去的是一个包含空格和井号的示例路径。测试先调用 codex_new_thread_url 生成链接,再把链接按 URL 解析,检查协议、主机、路径和查询参数都符合预期。
调用关系:它验证 codex_new_thread_url,而这个函数会被 open_codex_app 用来把工作区传给桌面应用。测试保证启动链接不会因为路径特殊字符而出错。
调用图:调用 1 个内部函数(codex_new_thread_url);外部调用 3 个(new, assert_eq!, parse)。
cli/src/desktop_app/windows.rs源码 ↗
这个文件像一个“Windows 接待员”。用户在命令行里想把当前项目交给 Codex Desktop 时,它先把工作目录路径整理成适合显示的样子,然后用 PowerShell(Windows 自带的命令工具)检查系统里有没有名叫 Codex 的开始菜单应用。找到了,就拼出一个 codex:// 开头的特殊链接,这种链接像“给桌面应用的门铃”,里面带着要打开的文件夹路径,然后让 Windows 打开它。没找到,就打开 Microsoft Store 的安装链接;如果默认安装链接打不开,还会退一步打开网页商店地址。文件里还处理了 Windows 一些奇怪路径前缀,比如 \\?\,让提示文字更像普通人能看懂的路径。底部测试则确保这些路径和链接不会被拼错。
run_windows_app_open_or_install10–31 ↗
async fn run_windows_app_open_or_install(
workspace: PathBuf,
download_url_override: Option<String>,
) -> anyhow::Result<()>
作用:这是这个文件的主流程:给它一个工作目录,它会决定是打开已安装的 Codex Desktop,还是引导用户去安装。它让用户不需要关心应用是否已装、链接怎么拼、安装页在哪里。
数据流:进去的是一个工作目录路径,以及可选的下载地址覆盖值。它先把路径转成显示用文字,再检查 Codex Desktop 是否已安装;如果已安装,就把原始工作目录放进 codex:// 链接里并打开;如果没安装,就打开下载安装地址,必要时再退到 Microsoft Store 网页。出来的结果是成功或错误,同时它会在终端打印提示,并可能启动浏览器或桌面应用。
调用关系:它由更上层的 run_app_open_or_install 在 Windows 场景下调用。它自己不做所有细活,而是把“查是否安装”交给 codex_app_is_installed,把“打开链接”交给 open_url,把“拼桌面应用链接”交给 codex_new_thread_url,把“整理给人看的路径”交给 display_workspace_path。
调用图:调用 4 个内部函数(codex_app_is_installed, codex_new_thread_url, display_workspace_path, open_url);被 1 处调用(run_app_open_or_install);外部调用 2 个(display, eprintln!)。
codex_app_is_installed33–47 ↗
async fn codex_app_is_installed() -> anyhow::Result<bool>
作用:这个函数用来问 Windows:电脑里有没有安装 Codex Desktop。它通过 PowerShell 查询开始菜单应用,而不是靠猜文件夹位置。
数据流:进去没有业务参数,它会启动 powershell.exe,执行查询名为 Codex 的开始菜单应用的命令。命令成功且输出里有 AppID,就返回 true;命令失败或没有输出,就返回 false;如果连 PowerShell 都启动不了,就返回错误。
调用关系:它被 run_windows_app_open_or_install 首先调用,用来决定后面走“打开应用”还是“打开安装器”。它依赖 Windows 的 PowerShell 和 Get-StartApps 命令完成实际查询。
调用图:被 1 处调用(run_windows_app_open_or_install);外部调用 2 个(from_utf8_lossy, new)。
open_url49–64 ↗
async fn open_url(url: &str) -> anyhow::Result<()>
作用:这个函数负责把一个网址或特殊链接交给 Windows 打开。这里的“链接”既可以是普通网页,也可以是 codex:// 这种让桌面应用响应的链接。
数据流:进去的是一个字符串形式的目标地址。它启动 powershell.exe,并让 PowerShell 调用 Start-Process 打开这个目标;如果 Windows 成功接手,就返回成功;如果启动失败或退出状态不对,就返回带说明的错误。
调用关系:它被 run_windows_app_open_or_install 用在两个地方:一是打开 Codex Desktop 的 codex:// 链接,二是打开安装地址或商店网页。它把“怎么让 Windows 打开东西”的底层动作封装起来,让主流程只关心打开什么。
调用图:被 1 处调用(run_windows_app_open_or_install);外部调用 2 个(bail!, new)。
codex_new_thread_url66–71 ↗
fn codex_new_thread_url(workspace: &str) -> String
作用:这个函数把工作目录路径包装成 Codex Desktop 能看懂的启动链接。可以把它想成把“请打开这个文件夹”写进一张给桌面应用的快递单。
数据流:进去的是工作目录路径字符串。它把路径作为 path 参数进行网址编码,也就是把反斜杠、冒号等特殊字符转成链接里安全的写法,然后拼成 codex://threads/new?path=... 这样的字符串。出来的是完整的桌面应用链接。
调用关系:它被 run_windows_app_open_or_install 在确认应用已安装后调用。生成的链接随后交给 open_url,由 Windows 分发给 Codex Desktop。底部两个测试专门检查 Windows 普通路径和带特殊前缀的路径都会被正确编码。
调用图:被 1 处调用(run_windows_app_open_or_install);外部调用 3 个(new, format!, new)。
display_workspace_path73–82 ↗
fn display_workspace_path(workspace: &Path) -> String
作用:这个函数把 Windows 的工作目录路径整理成更适合显示给人的样子。特别是去掉某些 Windows 内部使用的“加长路径”前缀,避免用户看到一串陌生符号。
数据流:进去的是一个路径对象。它先转成文字;如果路径以 \\?\UNC\ 开头,就改回常见的网络共享路径 \\server\share 形式;如果以 \\?\ 开头,就去掉这个前缀;普通路径则原样返回。出来的是用于终端提示的路径字符串,不改变真实文件位置。
调用关系:它被 run_windows_app_open_or_install 用来生成终端里的提示文字。它不参与真正打开应用的链接生成,所以不会偷偷改变传给 Codex Desktop 的实际路径;测试函数分别验证三种路径显示情况。
调用图:被 1 处调用(run_windows_app_open_or_install);外部调用 2 个(display, format!)。
tests::display_workspace_path_removes_windows_extended_prefix92–97 ↗
fn display_workspace_path_removes_windows_extended_prefix()
作用:这个测试确认普通磁盘路径前面的 Windows 扩展前缀 \\?\ 会被去掉。这样终端提示不会把内部格式直接显示给用户。
数据流:进去的是一个带 \\?\ 前缀的示例路径。测试调用 display_workspace_path,然后把结果和期望的普通 C:\ 路径比较;如果不一致,测试失败。
调用关系:它由 Rust 测试运行器在测试阶段调用。它专门保护 display_workspace_path 的一个重要行为,防止以后改代码时又把难懂前缀显示出来。
调用图:外部调用 1 个(assert_eq!)。
tests::display_workspace_path_preserves_unc_prefix100–105 ↗
fn display_workspace_path_preserves_unc_prefix()
作用:这个测试确认网络共享路径不会被错误改坏。UNC 路径就是类似 \\server\share 的 Windows 网络文件夹路径。
数据流:进去的是一个带 \\?\UNC\ 前缀的示例网络路径。测试调用 display_workspace_path,期望它变成常见的 \\server\share\codex 形式;结果不对就失败。
调用关系:它由测试运行器调用,用来守住 display_workspace_path 对网络路径的特殊处理。这个测试很重要,因为网络路径和本机 C 盘路径的前缀规则不一样。
调用图:外部调用 1 个(assert_eq!)。
tests::display_workspace_path_leaves_regular_paths_unchanged108–113 ↗
fn display_workspace_path_leaves_regular_paths_unchanged()
作用:这个测试确认普通 Windows 路径不会被多此一举地改动。也就是说,已经好懂的路径就原样显示。
数据流:进去的是一个普通的 C:\Users\... 路径。测试调用 display_workspace_path,并检查输出和输入文字一致;如果函数误删或误改内容,测试会失败。
调用关系:它在测试阶段运行,补齐 display_workspace_path 的第三种常见情况:普通路径、扩展路径、网络路径都要表现正确。
调用图:外部调用 1 个(assert_eq!)。
tests::codex_new_thread_url_encodes_windows_workspace_path116–121 ↗
fn codex_new_thread_url_encodes_windows_workspace_path()
作用:这个测试确认普通 Windows 工作目录能被安全地放进 codex:// 链接里。重点是冒号和反斜杠这类字符必须被编码,否则链接可能被误读。
数据流:进去的是一个普通 Windows 路径字符串。测试调用 codex_new_thread_url,期望得到 path 参数已经编码好的 codex://threads/new 链接;不一致就失败。
调用关系:它由测试运行器调用,保护 codex_new_thread_url 的链接拼接规则。这个结果之后会被 run_windows_app_open_or_install 交给 open_url 打开。
调用图:外部调用 1 个(assert_eq!)。
tests::codex_new_thread_url_preserves_verbatim_workspace_path124–129 ↗
fn codex_new_thread_url_preserves_verbatim_workspace_path()
作用:这个测试确认带 \\?\ 的原样路径也会被完整放进链接里,而不是被这个函数擅自清理掉。因为显示给人看的路径可以简化,但传给应用的真实路径要保持原样。
数据流:进去的是一个带 Windows 扩展前缀的路径字符串。测试调用 codex_new_thread_url,并检查输出链接里这个前缀被正确编码保留;如果丢了字符或编码错了,测试失败。
调用关系:它在测试阶段运行,专门守住 codex_new_thread_url 和 display_workspace_path 的分工:前者保留真实路径用于打开工作区,后者只整理提示文字。
调用图:外部调用 1 个(assert_eq!)。
cli/src/remote_control_cmd.rs源码 ↗
这个文件像一个前台接待员:用户在命令行输入 remote-control、remote-control start 或 remote-control stop,它负责判断用户想干什么,然后把活交给真正的 app-server 或后台守护进程。守护进程就是一个在后台常驻的小程序,不需要一直占着终端。这个文件还负责把结果说清楚:人看的普通文字,或者机器看的 JSON(一种结构化文本格式)。比较重要的是,默认不带 start/stop 时,它会启动一个临时的前台 app-server,创建私有 socket(本机程序之间通信用的小门),等远程控制真的可用后再告诉用户;如果用户按 Ctrl-C,它会尽量干净地停掉。没有这个文件,命令行用户就很难安全、清楚地开关远程控制,也很难知道到底启动成功、还在连接、出错,还是根本没运行。
RemoteControlCommand::subcommand_name41–47 ↗
fn subcommand_name(&self) -> &'static str
作用:把当前命令对象转换成一个好读的命令名字。这个名字通常用于日志、统计或错误提示,让系统知道用户运行的是 remote-control、start,还是 stop。
数据流:进去的是命令里保存的子命令状态 → 它检查有没有子命令,以及是哪一个 → 出来一个固定的文字名字,不改动任何东西。
调用关系:它是 RemoteControlCommand 这个命令结构上的小工具,帮助外层代码用统一方式称呼这次命令。它不启动服务,也不打印结果,只负责给命令取名。
run59–87 ↗
async fn run(
command: RemoteControlCommand,
arg0_paths: Arg0DispatchPaths,
root_config_overrides: CliConfigOverrides,
) -> anyhow::Result<()>
作用:这是这个文件的主调度函数。命令行主程序把解析好的 remote-control 命令交给它,它决定是前台启动、后台启动,还是停止远程控制。
数据流:进去的是用户命令、程序启动路径信息和配置覆盖项 → 它先按需打印进度,再根据子命令分支调用前台运行、后台守护进程启动,或守护进程停止 → 出来是成功或错误;过程中会在终端输出文字或 JSON。
调用关系:它由 cli_main 调用,是 remote-control 命令进入本文件后的总入口。它自己不做底层启动细节,而是把活分给 run_foreground_remote_control、外部的 ensure_remote_control_ready、外部的 daemon run,并用对应的打印函数收尾。
调用图:调用 4 个内部函数(print_remote_control_progress, print_remote_control_start_output, print_remote_control_stop_output, run_foreground_remote_control);被 1 处调用(cli_main);外部调用 2 个(ensure_remote_control_ready, run)。
print_remote_control_progress89–99 ↗
fn print_remote_control_progress(json: bool, message: &str) -> anyhow::Result<()>
作用:在普通文字模式下打印“正在启动/正在停止”这类进度提示。JSON 模式下它什么都不打印,避免把机器读取的 JSON 搞乱。
数据流:进去的是是否 JSON 输出和一段提示文字 → 如果是 JSON 就直接返回;否则打印文字并刷新标准输出 → 出来是成功或刷新失败的错误。
调用关系:run 在真正开始启动或停止前会调用它。它只管提示用户当前在做什么,不参与远程控制本身。
run_foreground_remote_control101–174 ↗
async fn run_foreground_remote_control(
json: bool,
arg0_paths: Arg0DispatchPaths,
root_config_overrides: CliConfigOverrides,
) -> anyhow::Result<()>
作用:以前台方式启动一个临时 app-server,并打开远程控制。所谓前台方式,就是服务跟着当前终端走,用户按 Ctrl-C 就停。
数据流:进去的是输出格式、程序路径和配置覆盖项 → 它创建一个临时私有 socket 路径,启动 app-server,监听 Ctrl-C,同时等待远程控制变成可用 → 如果可用就打印连接信息并继续等服务运行;如果用户中断或启动失败,就中止服务并返回结果。
调用关系:run 在用户只输入 remote-control、没有 start/stop 时调用它。它把许多零件串起来:foreground_stop_signal 负责 Ctrl-C,wait_for_foreground_remote_control_start 负责等启动结果,wait_for_foreground_remote_control_ready 负责连 socket,print_foreground_ready_output 负责告诉用户,wait_for_foreground_app_server 负责后续等待,abort_foreground_app_server 负责必要时强停。
调用图:调用 7 个内部函数(abort_foreground_app_server, foreground_stop_signal, print_foreground_ready_output, wait_for_foreground_app_server, wait_for_foreground_remote_control_ready, wait_for_foreground_remote_control_start, from_absolute_path);被 1 处调用(run);外部调用 6 个(default, default, run_main_with_transport_options, default, new, spawn)。
foreground_stop_signal176–185 ↗
fn foreground_stop_signal() -> (watch::Receiver<bool>, JoinHandle<()>)
作用:建立一个“停止信号”的小通道,用来把用户按 Ctrl-C 这件事通知给其他异步任务。异步任务可以理解为同时进行的后台小工作。
数据流:进去不需要业务输入 → 它创建一个 watch 通道(一种只保存最新状态的通知通道),再启动一个任务等待 Ctrl-C → 出来是接收端和这个监听任务的句柄;按下 Ctrl-C 后通道里的值会变成 true。
调用关系:run_foreground_remote_control 调用它,让前台 app-server 能被用户中断。之后等待启动和等待运行的函数都会看这个停止信号。
调用图:被 1 处调用(run_foreground_remote_control);外部调用 4 个(eprintln!, ctrl_c, spawn, channel)。
wait_for_foreground_remote_control_start194–213 ↗
async fn wait_for_foreground_remote_control_start(
app_server_task: &mut JoinHandle<std::io::Result<()>>,
ready: impl std::future::Future<Output = anyhow::Result<AppServerRemoteControlReadySta
作用:同时等三件事谁先发生:远程控制准备好、app-server 提前退出、用户要求停止。它把启动阶段的结果归成几种清楚的状态。
数据流:进去的是 app-server 任务、一个“等待远程控制就绪”的 future(还没完成的异步结果),以及停止信号 → 它用 select 同时观察这些事件 → 出来是 Ready、Stopped、ReadyFailed 或 AppServerExited 之一。
调用关系:run_foreground_remote_control 在启动初期调用它。相关测试也直接调用它,确认 Ctrl-C 或 app-server 提前失败时不会卡住。
调用图:被 3 处调用(run_foreground_remote_control, foreground_start_wait_reports_app_server_exit_before_ready, foreground_start_wait_stops_before_ready);外部调用 2 个(pin!, select!)。
wait_for_foreground_app_server215–231 ↗
async fn wait_for_foreground_app_server(
mut app_server_task: JoinHandle<std::io::Result<()>>,
mut stop_rx: watch::Receiver<bool>,
) -> anyhow::Result<()>
作用:在远程控制已经启动后,继续守着前台 app-server。它要么等服务自己结束,要么等用户按 Ctrl-C 后把服务停掉。
数据流:进去的是 app-server 任务和停止信号 → 它同时等任务结束和停止信号 → 如果服务报错就把错误传出去;如果收到停止信号就中止任务;最后返回成功或错误。
调用关系:run_foreground_remote_control 在打印“已就绪”之后调用它。测试 foreground_wait_aborts_app_server_on_stop_signal 用它验证收到停止信号时会及时退出。
调用图:被 2 处调用(run_foreground_remote_control, foreground_wait_aborts_app_server_on_stop_signal);外部调用 1 个(select!)。
wait_for_stop_signal233–238 ↗
async fn wait_for_stop_signal(stop_rx: &mut watch::Receiver<bool>)
作用:等待停止信号变成 true。它是一个小助手,避免其他函数重复写“检查是否已经停止、否则继续等”的代码。
数据流:进去的是停止信号接收端 → 它先看当前值是不是已经 true;如果不是,就一直等到变成 true → 出来时表示已经收到停止请求,不返回额外数据。
调用关系:启动等待和运行等待这类流程会用它来响应 Ctrl-C。它不关心谁要停,只负责等“该停了”这个消息。
调用图:外部调用 2 个(borrow, wait_for)。
foreground_app_server_exited_before_ready240–252 ↗
fn foreground_app_server_exited_before_ready(
result: Result<std::io::Result<()>, tokio::task::JoinError>,
) -> anyhow::Error
作用:把“app-server 还没准备好就退出了”这件事包装成更容易理解的错误。这样用户看到的不是晦涩的任务状态,而是明确的启动失败原因。
数据流:进去的是 app-server 任务结束的结果,可能成功、返回 IO 错误,或者任务本身崩掉 → 它按情况加上上下文说明 → 出来是一个 anyhow 错误对象。
调用关系:wait_for_foreground_remote_control_start 在发现 app-server 比远程控制就绪更早结束时会用它生成错误说明。
abort_foreground_app_server254–257 ↗
async fn abort_foreground_app_server(app_server_task: JoinHandle<std::io::Result<()>>)
作用:强制中止前台 app-server 任务,并短暂等待它真的停下来。它相当于在退出前做一次“别让后台小工作漏在外面”的清理。
数据流:进去的是 app-server 任务句柄 → 它发出 abort 中止请求,然后最多等 1 秒 → 出来不返回具体结果;就算等待超时也不继续纠缠。
调用关系:run_foreground_remote_control 在用户停止、启动失败、打印失败等需要收摊的情况下调用它。wait_for_foreground_app_server 收到停止信号时也会走到这个清理动作。
调用图:被 1 处调用(run_foreground_remote_control);外部调用 2 个(abort, timeout)。
wait_for_foreground_remote_control_ready259–268 ↗
async fn wait_for_foreground_remote_control_ready(
socket_path: AbsolutePathBuf,
) -> anyhow::Result<AppServerRemoteControlReadyStatus>
作用:尝试通过刚创建的本地 socket 打开远程控制,并等它变成可用。socket 可以理解成本机程序之间通话的小门。
数据流:进去的是 socket 的绝对路径 → 它把路径交给外部的 enable_remote_control_on_socket,并带上连接超时和重试间隔 → 出来是远程控制就绪状态,或连接失败的错误。
调用关系:run_foreground_remote_control 把它作为“等待就绪”的任务交给 wait_for_foreground_remote_control_start。它是前台启动流程里确认远程控制真的能用的关键一步。
调用图:调用 1 个内部函数(as_path);被 1 处调用(run_foreground_remote_control);外部调用 1 个(enable_remote_control_on_socket)。
print_remote_control_start_output270–293 ↗
fn print_remote_control_start_output(
output: &AppServerRemoteControlReadyOutput,
json: bool,
) -> anyhow::Result<()>
作用:打印后台守护进程启动远程控制后的结果。它既能输出人看的文字,也能输出给脚本读的 JSON。
数据流:进去的是守护进程返回的远程控制信息和是否 JSON → 它先确认远程控制状态是可启动的;JSON 模式下序列化成一行 JSON,普通模式下打印远程控制说明和 app-server 路径、版本 → 出来是打印成功或错误。
调用关系:run 在 remote-control start 分支拿到 ensure_remote_control_ready 的结果后调用它。它会用 ensure_remote_control_startable 做状态把关,用 remote_control_start_human_lines 和 daemon_app_server_human_lines 组织普通文字。
调用图:调用 3 个内部函数(daemon_app_server_human_lines, ensure_remote_control_startable, remote_control_start_human_lines);被 1 处调用(run);外部调用 1 个(println!)。
print_foreground_ready_output295–313 ↗
fn print_foreground_ready_output(
summary: &AppServerRemoteControlReadyStatus,
json: bool,
) -> anyhow::Result<()>
作用:打印前台模式下远程控制已经就绪的结果。前台模式会额外提醒用户可以按 Ctrl-C 停止。
数据流:进去的是远程控制就绪摘要和是否 JSON → JSON 模式下先检查状态再输出结构化结果;普通模式下生成并打印人类可读的几行文字 → 出来是成功或错误。
调用关系:run_foreground_remote_control 在远程控制准备好后调用它。它和后台打印函数共用 ensure_remote_control_startable 和 remote_control_start_human_lines,保证两种模式对状态的判断一致。
调用图:调用 2 个内部函数(ensure_remote_control_startable, remote_control_start_human_lines);被 1 处调用(run_foreground_remote_control);外部调用 1 个(println!)。
RemoteControlStartJsonOutput::foreground335–344 ↗
fn foreground(summary: &'a AppServerRemoteControlReadyStatus) -> Self
作用:把前台模式的远程控制状态整理成 JSON 输出用的结构。这样脚本或编辑器能稳定读取字段,而不用解析普通文字。
数据流:进去的是前台远程控制就绪摘要 → 它复制状态、服务器名、环境 ID、是否超时等字段,并标记 mode 为 foreground,daemon 字段为空 → 出来是可被序列化成 JSON 的对象。
调用关系:前台 JSON 输出时会用它生成标准格式。它只做数据整理,不检查状态是否可用,状态检查由打印函数先做。
RemoteControlStartJsonOutput::daemon346–356 ↗
fn daemon(output: &'a AppServerRemoteControlReadyOutput) -> Self
作用:把后台守护进程模式的远程控制结果整理成 JSON 输出用的结构。它会同时包含远程控制状态和守护进程启动信息。
数据流:进去的是守护进程返回的完整就绪输出 → 它取出远程控制部分填入通用字段,把 mode 标成 daemon,并把 daemon 启动详情放进去 → 出来是可序列化的 JSON 对象。
调用关系:remote-control start 使用 JSON 输出时会用它。它和 foreground 构造函数保证两种启动方式的 JSON 大体长得一样,方便调用方处理。
remote_control_start_human_message359–376 ↗
fn remote_control_start_human_message(
output: &AppServerRemoteControlReadyStatus,
) -> anyhow::Result<String>
作用:生成一句给人看的启动结果说明,比如“这台机器可以作为某某名称被远程控制”。它会先拒绝明显不可用的状态。
数据流:进去的是远程控制状态摘要 → 它先调用 ensure_remote_control_startable 检查状态,再根据 connected 或 connecting 生成不同文字 → 出来是一句说明,或状态不可用时的错误。
调用关系:它是普通文字输出的核心句子来源。它依赖 ensure_remote_control_startable,保证不会把“出错”或“已禁用”说成启动成功。
调用图:调用 1 个内部函数(ensure_remote_control_startable);外部调用 2 个(format!, unreachable!)。
ensure_remote_control_startable378–395 ↗
fn ensure_remote_control_startable(
output: &AppServerRemoteControlReadyStatus,
) -> anyhow::Result<()>
作用:检查远程控制状态是否适合当作“启动成功或正在连接”来展示。它是防止误报成功的安全闸门。
数据流:进去的是远程控制状态 → 如果状态是 connected 或 connecting 就通过;如果是 errored 或 disabled 就生成明确错误 → 出来是成功确认或错误信息。
调用关系:print_foreground_ready_output、print_remote_control_start_output 和 remote_control_start_human_message 都会用它。它让 JSON 输出和普通文字输出在状态判断上保持一致。
调用图:被 3 处调用(print_foreground_ready_output, print_remote_control_start_output, remote_control_start_human_message);外部调用 1 个(bail!)。
remote_control_start_human_lines403–415 ↗
fn remote_control_start_human_lines(
summary: &AppServerRemoteControlReadyStatus,
mode: RemoteControlHumanOutputMode,
) -> anyhow::Result<Vec<String>>
作用:把远程控制启动结果整理成多行普通文字。前台模式会多加一句“按 Ctrl-C 停止”。
数据流:进去的是远程控制摘要和输出模式 → 它先生成主要说明,再按前台或后台模式决定是否追加停止提示 → 出来是字符串列表,供打印函数逐行输出。
调用关系:print_foreground_ready_output 和 print_remote_control_start_output 都会调用它。它把“内容怎么写”和“什么时候打印”分开,让两个打印入口共用同一套文案。
调用图:被 2 处调用(print_foreground_ready_output, print_remote_control_start_output);外部调用 1 个(vec!)。
daemon_app_server_human_lines417–424 ↗
fn daemon_app_server_human_lines(output: &AppServerRemoteControlStartOutput) -> Vec<String>
作用:生成后台守护进程所使用的 app-server 信息,包括程序路径和版本。这样用户能知道后台实际跑的是哪个 Codex。
数据流:进去的是守护进程启动输出 → 它先取出 app-server 的路径和版本,再组装成三行文字 → 出来是这些文字行;如果版本缺失就显示 unknown。
调用关系:print_remote_control_start_output 在普通文字模式下调用它。它内部把识别具体输出类型的工作交给 daemon_app_server_identity。
调用图:调用 1 个内部函数(daemon_app_server_identity);被 1 处调用(print_remote_control_start_output);外部调用 1 个(vec!)。
daemon_app_server_identity426–439 ↗
fn daemon_app_server_identity(
output: &AppServerRemoteControlStartOutput,
) -> (&std::path::Path, Option<&str>)
作用:从不同形态的守护进程启动输出里取出同样的身份信息:app-server 路径和版本。因为启动可能是 bootstrap,也可能是普通 start,但用户关心的是同一类信息。
数据流:进去的是 AppServerRemoteControlStartOutput,可能是 Bootstrap 或 Start → 它匹配具体类型,拿到 managed_codex_path 和 managed_codex_version → 出来是路径引用和可选版本字符串。
调用关系:daemon_app_server_human_lines 调用它来隐藏输出类型差异。这样上层生成文字时不用关心守护进程到底走了哪种启动路线。
调用图:被 1 处调用(daemon_app_server_human_lines)。
print_remote_control_stop_output441–452 ↗
fn print_remote_control_stop_output(
output: &AppServerLifecycleOutput,
json: bool,
) -> anyhow::Result<()>
作用:打印停止远程控制后的结果。它支持 JSON,也支持普通人能读的简短提示。
数据流:进去的是守护进程生命周期输出和是否 JSON → JSON 模式下直接序列化整个输出;普通模式下生成一句停止结果说明并打印 → 出来是成功或序列化错误。
调用关系:run 在 remote-control stop 分支调用外部 daemon run 停止服务后,会把结果交给它展示。
remote_control_stop_human_message454–468 ↗
fn remote_control_stop_human_message(output: &AppServerLifecycleOutput) -> String
作用:把停止命令的状态变成一句普通话。比如已经停了、原本没运行,或者出现了不太常见的状态。
数据流:进去的是生命周期输出 → 它查看 status 字段,按不同状态选择固定文字或带状态名的说明 → 出来是一句字符串,不改动输入。
调用关系:print_remote_control_stop_output 在普通文字模式下用它生成提示。它只负责措辞,不负责真的停止服务。
调用图:外部调用 1 个(format!)。
tests::remote_control_status478–487 ↗
fn remote_control_status(
status: RemoteControlConnectionStatus,
) -> AppServerRemoteControlReadyStatus
作用:测试用的小工厂函数,用固定的机器名和环境 ID 造一个远程控制状态。这样每个测试不用重复写一大段假数据。
数据流:进去的是连接状态 → 它填入 server_name、environment_id,并根据状态是否 connecting 设置 timed_out → 出来是一个 AppServerRemoteControlReadyStatus 测试对象。
调用关系:多个测试用它制造 connected、connecting、errored、disabled 等场景。它只在测试里帮助准备输入数据。
tests::daemon_ready_output489–510 ↗
fn daemon_ready_output(
status: RemoteControlConnectionStatus,
) -> AppServerRemoteControlReadyOutput
作用:测试用的小工厂函数,造一个像后台守护进程启动成功后返回的完整结果。它包含远程控制状态,也包含 app-server 路径、版本和 pid。
数据流:进去的是远程控制连接状态 → 它创建固定的守护进程输出和远程控制摘要 → 出来是 AppServerRemoteControlReadyOutput 测试对象。
调用关系:JSON 输出、后台文字输出、错误状态检查等测试会用它。它让测试关注要验证的行为,而不是被假数据细节淹没。
调用图:外部调用 2 个(Start, from)。
tests::remote_control_human_start_messages_use_server_name513–544 ↗
fn remote_control_human_start_messages_use_server_name()
作用:检查启动提示文字是否正确使用服务器名,并且错误和禁用状态会报错。它保证用户看到的提示既准确又不会误导。
数据流:进去没有外部输入,测试内部构造不同状态 → 它调用启动文案函数并和期望文字比较 → 结果是测试通过或失败,不产生运行时功能输出。
调用关系:这是 remote_control_start_human_message 和状态检查逻辑的保护网。以后有人改文案或状态处理时,测试会提醒是否破坏了预期。
调用图:外部调用 1 个(assert_eq!)。
tests::remote_control_human_lines_include_foreground_stop_hint_only547–563 ↗
fn remote_control_human_lines_include_foreground_stop_hint_only()
作用:检查只有前台模式才会显示“按 Ctrl-C 停止”的提示,后台模式不会显示。这样用户不会在后台模式看到不适用的操作说明。
数据流:进去没有外部输入,测试先用 remote_control_status 造 connected 状态 → 它分别生成前台和后台文字行并比较期望结果 → 出来是测试是否通过。
调用关系:它直接验证 remote_control_start_human_lines 的模式差异。这个测试防止前台和后台提示被不小心混在一起。
调用图:外部调用 2 个(assert_eq!, remote_control_status)。
tests::daemon_app_server_human_lines_include_path_and_version566–577 ↗
fn daemon_app_server_human_lines_include_path_and_version()
作用:检查后台启动后的普通文字里会包含 app-server 的路径和版本。这样用户能追踪到底运行了哪个程序。
数据流:进去没有外部输入,测试用 daemon_ready_output 造假输出 → 它调用 daemon_app_server_human_lines 并比较三行期望文字 → 出来是测试通过或失败。
调用关系:它保护 daemon_app_server_human_lines 和 daemon_app_server_identity 这条展示链路,避免路径或版本信息丢失。
调用图:外部调用 1 个(assert_eq!)。
tests::remote_control_json_output_marks_foreground_or_daemon580–617 ↗
fn remote_control_json_output_marks_foreground_or_daemon()
作用:检查 JSON 输出会清楚标明 foreground 或 daemon,并包含该有的字段。这样外部工具可以放心按固定格式读取。
数据流:进去没有外部输入,测试分别造前台摘要和后台输出 → 它把 RemoteControlStartJsonOutput 转成 JSON 值,并和期望 JSON 比较 → 出来是测试结果。
调用关系:它验证 RemoteControlStartJsonOutput::foreground 和 RemoteControlStartJsonOutput::daemon 的字段映射。这个测试主要保护机器可读接口不被随意改坏。
调用图:外部调用 3 个(assert_eq!, daemon_ready_output, remote_control_status)。
tests::remote_control_daemon_json_rejects_unstartable_status620–630 ↗
fn remote_control_daemon_json_rejects_unstartable_status()
作用:检查后台 JSON 输出遇到错误状态时不会假装成功输出。它确保脚本不会把 errored 状态误判为可用。
数据流:进去没有外部输入,测试构造一个 errored 的后台输出 → 它调用 print_remote_control_start_output 的 JSON 分支并期待错误 → 出来是测试通过或失败。
调用关系:它保护 print_remote_control_start_output 对 ensure_remote_control_startable 的使用。只要有人绕过状态检查,这个测试就可能失败。
调用图:外部调用 1 个(assert_eq!)。
tests::foreground_wait_aborts_app_server_on_stop_signal633–645 ↗
async fn foreground_wait_aborts_app_server_on_stop_signal()
作用:检查前台等待函数收到停止信号时,会中止一个一直不结束的 app-server 任务,并且不会卡死。
数据流:进去没有外部输入,测试创建一个永远 pending 的任务和一个已经为 true 的停止信号 → 它在 1 秒超时保护内调用 wait_for_foreground_app_server → 出来是函数及时成功返回,或测试失败。
调用关系:它直接调用 wait_for_foreground_app_server。这个测试模拟用户按 Ctrl-C,确认前台运行模式能正常收摊。
调用图:调用 1 个内部函数(wait_for_foreground_app_server);外部调用 4 个(from_secs, spawn, channel, timeout)。
tests::foreground_start_wait_stops_before_ready648–667 ↗
async fn foreground_start_wait_stops_before_ready()
作用:检查启动阶段如果先收到停止信号,而远程控制还没准备好,函数会返回 Stopped,不会一直等。
数据流:进去没有外部输入,测试创建一个不结束的 app-server 任务、一个不完成的 ready future,以及已触发的停止信号 → 它调用 wait_for_foreground_remote_control_start → 出来应是 ForegroundStartupResult::Stopped。
调用关系:它直接测试 wait_for_foreground_remote_control_start 的三选一等待逻辑。这个场景对应用户在启动还没完成时就按 Ctrl-C。
调用图:调用 1 个内部函数(wait_for_foreground_remote_control_start);外部调用 5 个(assert!, from_secs, spawn, channel, timeout)。
tests::foreground_start_wait_reports_app_server_exit_before_ready670–694 ↗
async fn foreground_start_wait_reports_app_server_exit_before_ready()
作用:检查 app-server 在远程控制准备好之前失败退出时,会得到清楚的错误,而不是被当成正常停止。
数据流:进去没有外部输入,测试创建一个立刻返回 IO 错误的 app-server 任务、一个永远不完成的 ready future 和未触发的停止信号 → 它调用 wait_for_foreground_remote_control_start 并检查返回的错误文字 → 出来是测试通过或失败。
调用关系:它直接验证 wait_for_foreground_remote_control_start 在 app-server 先退出时的分支,也间接保护 foreground_app_server_exited_before_ready 生成的错误说明。
调用图:调用 1 个内部函数(wait_for_foreground_remote_control_start);外部调用 7 个(assert_eq!, other, panic!, from_secs, spawn, channel, timeout)。
沙盒和维护命令
这些顶层工具处理沙盒执行和设置、doctor 诊断,以及会话归档生命周期操作。
cli/src/debug_sandbox.rs源码 ↗
沙盒可以理解成给程序临时搭了一个“隔离房间”:它能看到哪些文件、能不能联网、从哪个目录开始运行,都要先说清楚。这个文件先读取配置和命令行覆盖项,再根据系统选择不同的沙盒工具:macOS 用 Seatbelt,Linux 用 Landlock,Windows 用 Windows 沙盒会话。它还会处理网络代理、证书文件可读权限、工作目录和环境变量,最后启动子进程并把输入输出接到当前终端上。一个重要细节是:老配置默认会按“只读”沙盒跑,避免不小心继承更宽的权限;但如果用户明确指定了权限配置,就尊重用户的选择。文件底部的测试专门确认这些配置优先级不会变。
run_command_under_seatbelt79–85 ↗
async fn run_command_under_seatbelt(
_command: SeatbeltCommand,
_codex_linux_sandbox_exe: Option<PathBuf>,
_loader_overrides: LoaderOverrides,
) -> anyhow::Result<()>
作用:这是 macOS Seatbelt 沙盒的外层入口。Seatbelt 是 macOS 自带的沙盒机制,用来限制命令能读写什么、能不能联网。
数据流:进去的是 Seatbelt 命令参数、可选的 Linux 沙盒程序路径、配置加载覆盖项 → 它拆出权限配置、工作目录、是否带上托管配置等信息,并判断托管要求要不要生效 → 然后把这些信息交给统一的沙盒运行函数;如果不是 macOS,就直接报错说这个沙盒不可用。
调用关系:它通常由 CLI 的 Seatbelt 调试命令触发。它自己不真正启动进程,而是先调用 ManagedRequirementsMode::for_profile_invocation 决定配置策略,再把实际执行交给 run_command_under_sandbox。
调用图:调用 2 个内部函数(for_profile_invocation, run_command_under_sandbox);外部调用 1 个(bail!)。
run_command_under_landlock87–119 ↗
async fn run_command_under_landlock(
command: LandlockCommand,
codex_linux_sandbox_exe: Option<PathBuf>,
loader_overrides: LoaderOverrides,
) -> anyhow::Result<()>
作用:这是 Linux Landlock 沙盒的外层入口。Landlock 是 Linux 的权限限制机制,可以让一个命令在受限的文件和网络权限下运行。
数据流:进去的是 Landlock 命令参数、可选的沙盒执行文件路径、配置加载覆盖项 → 它取出权限档案、工作目录和要运行的命令,并计算是否忽略托管要求 → 出来不是直接返回命令结果,而是把整件事交给统一沙盒运行流程,执行完成后返回成功或错误。
调用关系:它是 Linux 调试沙盒命令的起点。它调用 ManagedRequirementsMode::for_profile_invocation 做配置判断,然后调用 run_command_under_sandbox 完成加载配置、生成沙盒参数、启动子进程等后续工作。
调用图:调用 2 个内部函数(for_profile_invocation, run_command_under_sandbox)。
run_command_under_windows_sandbox121–153 ↗
async fn run_command_under_windows_sandbox(
command: WindowsCommand,
codex_linux_sandbox_exe: Option<PathBuf>,
loader_overrides: LoaderOverrides,
) -> anyhow::Result<()>
作用:这是 Windows 沙盒的外层入口。它让同一套调试命令也能在 Windows 的隔离环境中运行。
数据流:进去的是 Windows 沙盒命令参数、可选的 Linux 沙盒路径、配置加载覆盖项 → 它整理出权限档案、工作目录、配置覆盖项和待执行命令 → 然后交给统一的沙盒运行函数,由那里在 Windows 上启动专门的沙盒会话。
调用关系:它对应 Windows 调试沙盒入口。和 Landlock、Seatbelt 入口一样,它先调用 ManagedRequirementsMode::for_profile_invocation,再把真正执行交给 run_command_under_sandbox。
调用图:调用 2 个内部函数(for_profile_invocation, run_command_under_sandbox)。
ManagedRequirementsMode::for_profile_invocation177–186 ↗
fn for_profile_invocation(
permissions_profile: &Option<String>,
include_managed_config: bool,
) -> Self
作用:这个小函数决定“托管要求”要不要跟着本次权限档案一起生效。托管要求可以理解成系统或组织预先规定的额外限制。
数据流:进去的是用户有没有指定权限档案、以及是否明确要求包含托管配置 → 如果用户指定了权限档案但没要求包含托管配置,就返回 Ignore;其他情况返回 Include → 出来的是后续加载配置时要采用的模式。
调用关系:三个沙盒入口都会先问它一次。它的结果会放进 DebugSandboxConfigOptions,最后影响 run_command_under_sandbox 里加载配置时是否忽略托管要求。
调用图:被 3 处调用(run_command_under_landlock, run_command_under_seatbelt, run_command_under_windows_sandbox)。
run_command_under_sandbox189–360 ↗
async fn run_command_under_sandbox(
config_options: DebugSandboxConfigOptions,
command: Vec<String>,
config_overrides: CliConfigOverrides,
codex_linux_sandbox_exe: Option<PathBuf>,
作用:这是本文件的核心流水线:把配置、权限、网络、环境变量和具体平台沙盒组装起来,然后真的启动命令。
数据流:进去的是调试沙盒配置、待运行命令、命令行配置覆盖项、沙盒类型、是否记录拒绝日志等 → 它先加载最终配置,再生成子进程环境变量;如果需要网络代理,就启动代理并把代理信息写进环境;接着按 Seatbelt、Landlock 或 Windows 生成对应沙盒启动参数 → 最后启动子进程,等待它结束,并按退出码退出当前流程。
调用关系:它被 run_command_under_seatbelt、run_command_under_landlock、run_command_under_windows_sandbox 共同调用。中途它会调用 load_debug_sandbox_config 读配置,Windows 时交给 run_command_under_windows_session,macOS/Linux 时交给 spawn_debug_sandbox_child 启动子进程,最后用 handle_exit_status 处理命令退出状态。
调用图:调用 9 个内部函数(load_debug_sandbox_config, run_command_under_windows_session, spawn_debug_sandbox_child, handle_exit_status, create_env, allow_network_for_proxy, create_linux_sandbox_command_args_for_permission_profile, create_seatbelt_command_args, parse_overrides);被 3 处调用(run_command_under_landlock, run_command_under_seatbelt, run_command_under_windows_sandbox);外部调用 6 个(from, bail!, with_managed_mitm_ca_readable_root, eprintln!, default, unreachable!)。
run_command_under_windows_session363–408 ↗
async fn run_command_under_windows_session(
config: &Config,
command: Vec<String>,
cwd: AbsolutePathBuf,
workspace_roots: Vec<AbsolutePathBuf>,
env: std::collections::HashMap<Strin
作用:这个函数专门在 Windows 上启动沙盒会话,并把沙盒里的输入输出转接到当前终端。
数据流:进去的是最终配置、命令、工作目录、工作区根目录和环境变量 → 它把这些打包成 WindowsSandboxSessionRequest,请 Windows 沙盒库创建会话;如果失败就打印错误并退出 1;如果成功,就转发标准输入输出,拿到沙盒进程退出码后直接退出当前进程。
调用关系:它只由 run_command_under_sandbox 在 Windows 分支里调用。因为 Windows 的实现需要模拟继承当前终端,所以它不是返回一个普通子进程,而是直接控制当前进程的退出。
调用图:调用 1 个内部函数(as_path);被 1 处调用(run_command_under_sandbox);外部调用 4 个(forward_sandbox_session_stdio, eprintln!, from_config, exit)。
spawn_debug_sandbox_child410–439 ↗
async fn spawn_debug_sandbox_child(
program: PathBuf,
args: Vec<String>,
arg0: Option<&str>,
cwd: PathBuf,
network_sandbox_policy: NetworkSandboxPolicy,
mut env: std::collectio
作用:这个函数负责真正启动 macOS 或 Linux 的沙盒子进程。可以把它看成最后按下“运行”按钮的地方。
数据流:进去的是要启动的程序路径、参数、工作目录、网络沙盒策略、环境变量,以及一个补充环境变量的小回调 → 它创建 TokioCommand,设置参数、当前目录、清空并重建环境变量,必要时标记网络被禁用,并继承当前终端的输入输出 → 出来的是已经启动的 Child 子进程句柄。
调用关系:它由 run_command_under_sandbox 在 Seatbelt 和 Landlock 分支里调用。上游已经算好所有沙盒参数,它只负责把这些参数安全地交给操作系统启动。
调用图:调用 1 个内部函数(is_enabled);被 1 处调用(run_command_under_sandbox);外部调用 2 个(inherit, new)。
load_debug_sandbox_config441–455 ↗
async fn load_debug_sandbox_config(
cli_overrides: Vec<(String, TomlValue)>,
codex_linux_sandbox_exe: Option<PathBuf>,
options: DebugSandboxConfigOptions,
strict_config: bool,
) -> any
作用:这是加载调试沙盒配置的简化入口。调用者不用关心 Codex 主目录时,就用它。
数据流:进去的是命令行覆盖项、可选的 Linux 沙盒执行文件、调试沙盒选项和是否严格校验配置 → 它把 Codex 主目录设为 None → 出来的是 load_debug_sandbox_config_with_codex_home 构建好的 Config。
调用关系:它由 run_command_under_sandbox 调用,是正式运行时读配置的入口。真正的配置合并规则都在 load_debug_sandbox_config_with_codex_home 里完成。
调用图:调用 1 个内部函数(load_debug_sandbox_config_with_codex_home);被 1 处调用(run_command_under_sandbox)。
load_debug_sandbox_config_with_codex_home457–517 ↗
async fn load_debug_sandbox_config_with_codex_home(
cli_overrides: Vec<(String, TomlValue)>,
codex_linux_sandbox_exe: Option<PathBuf>,
options: DebugSandboxConfigOptions,
codex_home: O
作用:这个函数决定调试沙盒最终该用哪套权限配置。它特别处理了新式“权限档案”和旧式 sandbox_mode 之间的兼容关系。
数据流:进去的是 CLI 覆盖项、沙盒执行文件路径、调试选项、可选 Codex 主目录和严格校验开关 → 如果用户指定了权限档案,它会把它写成 default_permissions 覆盖项;然后先按正常规则加载配置;如果发现配置已经使用权限档案,或用户明确传了旧式 sandbox_mode,就直接返回;否则再加载一次,并强制旧配置默认用只读模式 → 出来的是最终 Config。
调用关系:它被 load_debug_sandbox_config 和多组测试直接调用。它内部依赖 build_debug_sandbox_config_with_loader_overrides 真正构建配置,并用 config_uses_permission_profiles、cli_overrides_use_legacy_sandbox_mode 判断走新规则还是旧规则。
调用图:调用 3 个内部函数(build_debug_sandbox_config_with_loader_overrides, cli_overrides_use_legacy_sandbox_mode, config_uses_permission_profiles);被 8 处调用(load_debug_sandbox_config, debug_sandbox_defaults_legacy_configs_to_read_only, debug_sandbox_honors_active_permission_profiles, debug_sandbox_honors_config_profile_loader_overrides, debug_sandbox_honors_explicit_builtin_permission_profile, debug_sandbox_honors_explicit_legacy_sandbox_mode, debug_sandbox_honors_explicit_named_permission_profile, debug_sandbox_uses_explicit_cwd);外部调用 2 个(default, String)。
build_debug_sandbox_config_with_loader_overrides519–541 ↗
async fn build_debug_sandbox_config_with_loader_overrides(
cli_overrides: Vec<(String, TomlValue)>,
harness_overrides: ConfigOverrides,
codex_home: Option<PathBuf>,
managed_requirement
作用:这个函数把各种配置来源装进 ConfigBuilder,生成真正可用的 Config。ConfigBuilder 可以理解成一步步拼配置的工具。
数据流:进去的是 CLI 覆盖项、测试/运行时覆盖项、可选 Codex 主目录、托管要求模式、加载器覆盖项和严格校验开关 → 它把这些塞进 builder;如果模式是 Ignore,就让加载器忽略托管要求;如果给了 Codex 主目录,也设置备用工作目录 → 出来的是异步 build 得到的 Config,或构建失败的 I/O 错误。
调用关系:它是配置构建的底层帮手,被 load_debug_sandbox_config_with_codex_home 和测试辅助函数调用。上层负责决定用什么规则,它负责把规则实际交给配置系统。
调用图:被 3 处调用(load_debug_sandbox_config_with_codex_home, build_debug_sandbox_config, debug_sandbox_honors_config_profile_loader_overrides);外部调用 2 个(default, matches!)。
config_uses_permission_profiles543–549 ↗
fn config_uses_permission_profiles(config: &Config) -> bool
作用:这个函数检查当前配置是不是已经用了新式权限档案。权限档案就是一组命名的文件和网络权限规则。
数据流:进去的是 Config → 它查看有效配置里有没有 default_permissions 这个键 → 出来是 true 或 false,表示是否启用了权限档案。
调用关系:它由 load_debug_sandbox_config_with_codex_home 调用,用来决定是否需要退回到旧配置的“默认只读”兼容逻辑。
调用图:被 1 处调用(load_debug_sandbox_config_with_codex_home)。
cli_overrides_use_legacy_sandbox_mode551–553 ↗
fn cli_overrides_use_legacy_sandbox_mode(cli_overrides: &[(String, TomlValue)]) -> bool
作用:这个函数检查用户有没有明确使用旧式 sandbox_mode 参数。旧式 sandbox_mode 是以前用来选择只读、工作区可写等沙盒模式的配置项。
数据流:进去的是一组 CLI 覆盖项 → 它逐个看键名是否等于 sandbox_mode → 出来是布尔值,表示用户是否明确选择了旧模式。
调用关系:它由 load_debug_sandbox_config_with_codex_home 调用。这个判断很重要:如果用户明确传了旧参数,系统就尊重它,而不是偷偷改成默认只读。
调用图:被 1 处调用(load_debug_sandbox_config_with_codex_home)。
tests::build_debug_sandbox_config561–577 ↗
async fn build_debug_sandbox_config(
cli_overrides: Vec<(String, TomlValue)>,
harness_overrides: ConfigOverrides,
codex_home: Option<PathBuf>,
managed_requirements_mode
作用:这是测试里用的配置构建快捷函数。它让测试不用每次都手写默认的 LoaderOverrides。
数据流:进去的是测试想设置的 CLI 覆盖项、运行时覆盖项、Codex 主目录、托管要求模式和严格校验开关 → 它补上默认加载器覆盖项 → 出来的是 build_debug_sandbox_config_with_loader_overrides 构建出的 Config。
调用关系:它只在测试中使用,帮助多条测试构造“期望配置”,再和 load_debug_sandbox_config_with_codex_home 的结果对比。
调用图:调用 1 个内部函数(build_debug_sandbox_config_with_loader_overrides);外部调用 1 个(default)。
tests::escape_toml_path579–581 ↗
fn escape_toml_path(path: &std::path::Path) -> String
作用:这个测试辅助函数把文件路径变成可以安全写进 TOML 配置文件的字符串。TOML 是一种常见的配置文件格式。
数据流:进去的是一个路径 → 它把路径转成显示字符串,并把反斜杠转义,避免 Windows 路径破坏 TOML 格式 → 出来的是可写入配置文件的路径文本。
调用关系:它被写测试配置的辅助函数使用。没有它,测试在 Windows 路径里遇到反斜杠时可能写出无效配置。
调用图:外部调用 1 个(display)。
tests::write_permissions_profile_config583–593 ↗
fn write_permissions_profile_config(
codex_home: &TempDir,
docs: &std::path::Path,
private: &std::path::Path,
) -> std::io::Result<()>
作用:这个测试辅助函数在临时 Codex 主目录里写一份权限档案配置。它让测试能模拟用户已经配置好某个权限档案。
数据流:进去的是临时 Codex 主目录、允许读的 docs 路径和禁止读的 private 路径 → 它把配置文件路径定到 codex_home/config.toml → 出来是写文件成功或失败的结果。
调用关系:它被多个测试调用,用来准备测试环境。实际写入动作交给 tests::write_permissions_profile_config_to_path。
调用图:外部调用 2 个(path, write_permissions_profile_config_to_path)。
tests::write_permissions_profile_config_to_path595–615 ↗
fn write_permissions_profile_config_to_path(
config_path: &std::path::Path,
docs: &std::path::Path,
private: &std::path::Path,
) -> std::io::Result<()>
作用:这个测试辅助函数把一份具体的权限档案配置写到指定文件。配置内容规定 docs 可读、private 不可读,并打开网络权限。
数据流:进去的是配置文件路径、docs 路径和 private 路径 → 它先创建 private 目录,再拼出 TOML 配置文本,最后写到文件 → 出来是文件系统操作成功或失败。
调用关系:它被 tests::write_permissions_profile_config 和部分测试直接使用。它生成的配置是后面验证权限档案加载逻辑的基础材料。
调用图:外部调用 3 个(format!, create_dir_all, write)。
tests::debug_sandbox_honors_active_permission_profiles618–676 ↗
async fn debug_sandbox_honors_active_permission_profiles() -> anyhow::Result<()>
作用:这个测试确认:如果用户配置里已经启用了权限档案,调试沙盒会尊重它,而不是改用旧式只读模式。
数据流:进去的是测试创建的临时目录和测试配置 → 它分别构造权限档案配置、旧式只读配置,以及真实加载结果 → 最后断言真实结果等于权限档案配置,并且不同于旧式只读配置。
调用关系:它直接调用 load_debug_sandbox_config_with_codex_home,并用 tests::build_debug_sandbox_config 构造对照组。它保护的是新配置优先于旧兼容默认值的行为。
调用图:调用 1 个内部函数(load_debug_sandbox_config_with_codex_home);外部调用 10 个(default, new, new, assert!, assert_eq!, assert_ne!, build_debug_sandbox_config, write_permissions_profile_config, default, default)。
tests::debug_sandbox_honors_config_profile_loader_overrides679–740 ↗
async fn debug_sandbox_honors_config_profile_loader_overrides() -> anyhow::Result<()>
作用:这个测试确认:如果通过加载器覆盖项指定了另一个配置文件或配置 profile,调试沙盒会按这个指定来读。
数据流:进去的是临时 Codex 主目录、单独的 profile 配置文件和 loader_overrides → 它构造 profile 配置、只读对照配置,再让真实加载流程使用 loader_overrides → 最后断言真实结果等于 profile 配置,而不是只读对照。
调用关系:它调用 build_debug_sandbox_config_with_loader_overrides 和 load_debug_sandbox_config_with_codex_home。这个测试保证配置文件切换功能不会被调试沙盒的默认兼容逻辑覆盖。
调用图:调用 3 个内部函数(build_debug_sandbox_config_with_loader_overrides, load_debug_sandbox_config_with_codex_home, from_absolute_path);外部调用 10 个(default, new, new, assert!, assert_eq!, assert_ne!, build_debug_sandbox_config, write_permissions_profile_config_to_path, default, default)。
tests::debug_sandbox_honors_explicit_legacy_sandbox_mode743–810 ↗
async fn debug_sandbox_honors_explicit_legacy_sandbox_mode() -> anyhow::Result<()>
作用:这个测试确认:用户如果明确传了旧式 sandbox_mode,调试沙盒必须尊重它。
数据流:进去的是一个 CLI 覆盖项 sandbox_mode=workspace-write 和临时 Codex 主目录 → 它构造 workspace-write 配置、只读配置和真实加载结果 → 最后断言真实结果跟 workspace-write 一致;在 Windows 特殊情况下,还考虑该模式可能降级成只读。
调用关系:它直接测试 load_debug_sandbox_config_with_codex_home 里 cli_overrides_use_legacy_sandbox_mode 的分支,防止“默认只读”逻辑误伤用户明确选择。
调用图:调用 1 个内部函数(load_debug_sandbox_config_with_codex_home);外部调用 10 个(default, new, new, assert_eq!, assert_ne!, cfg!, build_debug_sandbox_config, default, default, vec!)。
tests::debug_sandbox_defaults_legacy_configs_to_read_only813–850 ↗
async fn debug_sandbox_defaults_legacy_configs_to_read_only() -> anyhow::Result<()>
作用:这个测试确认:没有新式权限档案、也没有明确旧式 sandbox_mode 时,调试沙盒会默认使用只读模式。
数据流:进去的是空的临时 Codex 主目录 → 它构造一个只读对照配置,再走真实加载流程 → 最后断言真实配置没有使用权限档案,并且文件系统权限等于只读对照。
调用关系:它覆盖 load_debug_sandbox_config_with_codex_home 的兼容兜底逻辑。这个行为很重要,因为它保持了老版本 codex sandbox 的安全默认值。
调用图:调用 1 个内部函数(load_debug_sandbox_config_with_codex_home);外部调用 7 个(default, new, new, assert!, assert_eq!, build_debug_sandbox_config, default)。
tests::debug_sandbox_honors_explicit_builtin_permission_profile853–885 ↗
async fn debug_sandbox_honors_explicit_builtin_permission_profile() -> anyhow::Result<()>
作用:这个测试确认:用户明确指定内置权限档案时,调试沙盒会保留这套内置规则。
数据流:进去的是临时 Codex 主目录和权限档案名 :workspace → 它加载配置,取出实际文件系统沙盒规则,再和内置 workspace_write 权限档案的规则比较 → 出来是断言通过或失败。
调用关系:它直接调用 load_debug_sandbox_config_with_codex_home,并用 ManagedRequirementsMode::Ignore 模拟显式指定权限档案的场景。它保护的是内置权限档案不会被托管或兼容逻辑意外覆盖。
调用图:调用 2 个内部函数(load_debug_sandbox_config_with_codex_home, workspace_write);外部调用 4 个(new, new, assert!, default)。
tests::debug_sandbox_honors_explicit_named_permission_profile888–927 ↗
async fn debug_sandbox_honors_explicit_named_permission_profile() -> anyhow::Result<()>
作用:这个测试确认:用户明确指定自己配置的命名权限档案时,调试沙盒会准确使用它。
数据流:进去的是临时配置,其中定义了 limited-read-test 权限档案 → 它用这个名字加载真实配置,再构造一个直接设置 default_permissions 的期望配置 → 最后比较两者的文件系统沙盒规则是否相同。
调用关系:它调用 write_permissions_profile_config 准备配置,再调用 load_debug_sandbox_config_with_codex_home 和 tests::build_debug_sandbox_config 做对照。它验证显式命名档案的转换逻辑正确。
调用图:调用 1 个内部函数(load_debug_sandbox_config_with_codex_home);外部调用 8 个(new, new, assert_eq!, build_debug_sandbox_config, write_permissions_profile_config, default, default, vec!)。
tests::debug_sandbox_uses_explicit_cwd930–951 ↗
async fn debug_sandbox_uses_explicit_cwd() -> anyhow::Result<()>
作用:这个测试确认:如果调试命令明确指定了工作目录,最终配置会使用这个目录。
数据流:进去的是临时 Codex 主目录、临时工作目录和 :workspace 权限档案 → 它加载配置 → 最后断言配置里的 cwd 等于传入的工作目录。
调用关系:它调用 load_debug_sandbox_config_with_codex_home,专门覆盖 DebugSandboxConfigOptions.cwd 这条路径。这样可以防止沙盒策略用错目录,导致权限范围判断出错。
调用图:调用 1 个内部函数(load_debug_sandbox_config_with_codex_home);外部调用 4 个(new, new, assert_eq!, default)。
cli/src/sandbox_setup.rs源码 ↗
这个文件像一个安装向导。用户在命令行里输入 setup --elevated,再说明要给哪个 Windows 用户配置,代码就会先检查参数是否合规:必须明确是当前用户还是指定用户,指定用户时还必须给出 CODEX_HOME,也就是 Codex 放配置文件的目录。接着它把这些信息整理成一个清楚的身份对象,再调用核心库去做真正的 Windows 沙箱准备工作。准备成功后,它还会把配置写进 Codex 的配置目录,记录沙箱模式是 elevated,意思是以后按提升权限的 Windows 沙箱方式运行。最后它打印一条成功消息。这里有个重要限制:目前只支持 --elevated,不带这个参数会直接报错。
SandboxSetupCommand::setup_level48–54 ↗
fn setup_level(&self) -> anyhow::Result<SandboxSetupLevel>
作用:这个函数检查用户想设置哪种沙箱级别。现在它只接受 --elevated,也就是提升权限的 Windows 沙箱;如果没写,就明确报错。
数据流:进去的是已经解析好的命令参数 → 它查看 elevated_sandbox_level 这个开关是否打开 → 如果打开就返回“Elevated”级别;如果没打开,就返回一个错误,告诉用户当前必须加 --elevated。
调用关系:它被 run 先调用,相当于开工前先看用户选的安装类型是否合法。只有它确认是支持的级别后,run 才会继续把工作交给 run_elevated。
run57–61 ↗
async fn run(cmd: SandboxSetupCommand) -> anyhow::Result<()>
作用:这是执行沙箱设置命令的总入口。它先确认要设置的沙箱类型,再把具体工作交给对应的执行函数。
数据流:进去的是一个 SandboxSetupCommand 命令对象 → 它调用 setup_level 判断当前设置类型 → 如果是提升权限沙箱,就调用 run_elevated 继续执行;最后把成功或失败结果返回给上层。
调用关系:它由更外层的 cli_main 调用,是命令行程序进入这个功能后的第一站。它自己不做安装细节,而是像调度员一样判断路线,然后转给 run_elevated。
调用图:调用 2 个内部函数(setup_level, run_elevated);被 1 处调用(cli_main)。
parse_setup_command63–76 ↗
fn parse_setup_command(
sandbox_command: &[String],
) -> anyhow::Result<Option<SandboxSetupCommand>>
作用:这个函数从 codex sandbox ... 后面的参数里识别是不是 setup 子命令。如果是,它就把文字参数解析成结构化的命令对象;如果不是,就表示这个文件不该处理。
数据流:进去的是一串命令行文字,比如 setup --elevated --user ... → 它先看第一个词是不是 setup → 不是就返回 None;是的话就用 clap(一个命令行参数解析工具)检查和转换参数,成功返回 SandboxSetupCommand,失败返回错误。
调用关系:它由 cli_main 调用,用来分辨当前 sandbox 命令是不是 setup。测试函数也会调用它,确认它既能识别 setup,也不会误接管别的命令。
调用图:被 3 处调用(cli_main, ignores_non_setup_sandbox_command_args, parses_setup_from_sandbox_command_args);外部调用 1 个(try_parse_from)。
run_elevated78–101 ↗
async fn run_elevated(cmd: SandboxSetupCommand) -> anyhow::Result<()>
作用:这个函数执行真正的提升权限沙箱设置流程。它会确定目标用户和配置目录,调用核心库做 Windows 沙箱准备,然后把“使用 elevated 模式”写进配置。
数据流:进去的是已经解析好的命令 → 它先调用 resolve_sandbox_setup_identity 得到真实 Windows 用户名和 Codex 配置目录 → 再调用 run_elevated_provisioning_setup 做系统层面的准备 → 然后用 ConfigEditsBuilder 修改配置文件,把 Windows 沙箱模式设成 elevated → 最后打印成功信息并返回成功;如果配置保存失败,会返回带说明的错误。
调用关系:它只在 run 判断沙箱级别为 Elevated 后被调用。它把身份解析交给 resolve_sandbox_setup_identity,把系统准备交给核心库,把配置写入交给 ConfigEditsBuilder,自己负责把这些步骤串起来。
调用图:调用 3 个内部函数(resolve_sandbox_setup_identity, new, run_elevated_provisioning_setup);被 1 处调用(run);外部调用 1 个(println!)。
resolve_sandbox_setup_identity108–139 ↗
fn resolve_sandbox_setup_identity(
cmd: &SandboxSetupCommand,
) -> anyhow::Result<SandboxSetupIdentity>
作用:这个函数决定这次要给哪个 Windows 用户配置沙箱,以及 Codex 的配置目录在哪里。它把命令行里的用户选择整理成后续步骤能直接使用的信息。
数据流:进去的是命令参数 → 如果用户选择 --current-user,它从环境变量 USERNAME 或 USER 里读取当前用户名,并使用传入的 codex_home,没有传就自动查找默认 Codex 目录 → 如果用户选择 --user,它要求同时提供 --codex-home → 最后返回包含用户名和目录的 SandboxSetupIdentity;缺少必要信息时返回错误。
调用关系:它被 run_elevated 调用,是安装前确认身份的步骤。它会调用 find_codex_home 查默认配置目录,也会读取系统环境变量来识别当前用户。
调用图:调用 1 个内部函数(find_codex_home);被 1 处调用(run_elevated);外部调用 1 个(var)。
tests::parses_managed_user_identity146–164 ↗
fn parses_managed_user_identity()
作用:这个测试确认:当用户指定一个受管理的 Windows 用户和 Codex 配置目录时,命令行参数能被正确读懂。
数据流:进去的是一组模拟命令行参数 → 测试用 try_parse_from 把它们解析成 SandboxSetupCommand → 然后检查 elevated 开关、用户名、current-user 标记和 codex_home 是否都和预期一样。
调用关系:它直接测试 clap 对 SandboxSetupCommand 的解析结果,确保真实用户输入 --user 和 --codex-home 时不会被程序误解。
调用图:外部调用 3 个(assert!, assert_eq!, try_parse_from)。
tests::requires_explicit_user_identity167–172 ↗
fn requires_explicit_user_identity()
作用:这个测试确认:只写 setup --elevated 还不够,用户必须明确说明给谁配置。
数据流:进去的是缺少用户身份的模拟参数 → 解析应该失败 → 测试检查失败原因是不是“缺少必需参数”。
调用关系:它验证 SandboxSetupCommand 上的参数规则是否生效,防止程序在不知道目标用户的情况下继续做沙箱设置。
调用图:外部调用 2 个(assert_eq!, try_parse_from)。
tests::requires_codex_home_for_managed_user175–181 ↗
fn requires_codex_home_for_managed_user()
作用:这个测试确认:如果指定了 --user,就必须同时指定 --codex-home。这样程序才知道要把配置写到哪个用户的 Codex 目录里。
数据流:进去的是有用户名但没有 Codex 配置目录的模拟参数 → 解析应该失败 → 测试检查错误类型是否是缺少必需参数。
调用关系:它保护的是指定用户场景下的安全边界:resolve_sandbox_setup_identity 后面需要明确目录,所以命令解析阶段就先拦住不完整输入。
调用图:外部调用 2 个(assert_eq!, try_parse_from)。
tests::parses_setup_from_sandbox_command_args184–197 ↗
fn parses_setup_from_sandbox_command_args()
作用:这个测试确认 parse_setup_command 能从 sandbox 命令参数里正确识别并解析 setup 子命令。
数据流:进去的是一组以 setup 开头的模拟参数 → 它调用 parse_setup_command → 得到一个命令对象后,检查里面的用户名是否是预期的 DOMAIN\alice。
调用关系:它测试的是外层命令分发前的识别步骤,确保 cli_main 把 sandbox 参数交给这里时,真正的 setup 命令会被接住。
调用图:调用 1 个内部函数(parse_setup_command);外部调用 1 个(assert_eq!)。
tests::ignores_non_setup_sandbox_command_args200–205 ↗
fn ignores_non_setup_sandbox_command_args()
作用:这个测试确认:如果 sandbox 后面的命令不是 setup,这个文件不会误处理它。
数据流:进去的是 echo hello 这样的非 setup 参数 → 它调用 parse_setup_command → 结果应该是 None,表示这里不负责这个命令。
调用关系:它保证命令分发不会乱套。parse_setup_command 只认 setup,其他 sandbox 子命令可以留给别的代码处理。
调用图:调用 1 个内部函数(parse_setup_command);外部调用 1 个(assert!)。
cli/src/doctor/thread_inventory.rs源码 ↗
Codex 会把每个对话线程保存成 rollout 文件,也会在 SQLite(一种本地小数据库)里记一份线程清单。这个文件就像仓库盘点员:先去 CODEX_HOME 下的 sessions 和 archived_sessions 文件夹找所有 rollout-*.jsonl 文件,再从状态数据库读取 threads 表,然后逐项对账。它会统计活跃文件、归档文件、扫描失败、坏文件名、数据库行数等信息;如果发现文件有但数据库没有、数据库指向的文件不存在、归档标记和文件夹位置不一致、同一个线程或路径重复,就生成 DoctorCheck 报告和具体 DoctorIssue。它还故意限制最多扫描 10000 个候选文件,防止体检因为海量文件卡死。测试部分用临时目录和临时数据库造出正常、缺失、过期、归档不匹配等场景,确保报告结果可信。
RolloutScan::candidate_count49–51 ↗
fn candidate_count(&self) -> usize
作用:算出这次扫描已经遇到多少个候选对象。这里不只算正常文件,也算文件名坏掉的文件和扫描错误。
数据流:输入是 RolloutScan 里已经收集到的正常文件、坏文件名列表、错误列表 → 把三类数量加起来 → 返回一个总数,不改动任何数据。
调用关系:它是扫描上限判断的基础。RolloutScan::reached_candidate_cap 会直接用它,scan_rollout_root 在继续处理新文件前也会用它确认有没有超过安全上限。
调用图:被 2 处调用(reached_candidate_cap, scan_rollout_root)。
RolloutScan::reached_candidate_cap53–55 ↗
fn reached_candidate_cap(&self) -> bool
作用:判断扫描是不是已经碰到最多 10000 个候选文件的上限。这个上限是为了避免体检在异常大目录里跑太久。
数据流:输入是当前 RolloutScan → 调用 RolloutScan::candidate_count 取得总数 → 返回 true 或 false,表示是否已达到上限。
调用关系:它被 RolloutScan::record_malformed_name 和 RolloutScan::record_scan_error 使用,用来决定还能不能继续记录问题,还是只标记“扫描到顶了”。
调用图:调用 1 个内部函数(candidate_count);被 2 处调用(record_malformed_name, record_scan_error)。
RolloutScan::record_malformed_name57–64 ↗
fn record_malformed_name(&mut self, path: PathBuf)
作用:记录一个文件名不合规的 rollout 文件。所谓不合规,就是文件看起来像会话记录,但不能从名字和内容里正常认出线程信息。
数据流:输入是一个路径 → 先看是否已经达到扫描上限 → 没到就把路径放进 malformed_names;到了就只把 reached_scan_cap 标成 true → 最后更新是否触顶的状态。
调用关系:scan_rollout_root 在读到 rollout 文件但无法按规则解析线程名时调用它。它内部依赖 RolloutScan::reached_candidate_cap 来保护扫描规模。
调用图:调用 1 个内部函数(reached_candidate_cap);被 1 处调用(scan_rollout_root)。
RolloutScan::record_scan_error66–73 ↗
fn record_scan_error(&mut self, message: String)
作用:记录一次扫描过程中遇到的错误,比如目录打不开、文件类型读不到、rollout 文件读不出来。
数据流:输入是一段错误说明文字 → 先检查扫描上限 → 没到上限就加入 scan_errors;到了上限就只标记 reached_scan_cap → 返回时 RolloutScan 已带上新的错误状态。
调用关系:scan_rollout_root 遇到磁盘读取问题或文件不可用时会调用它。它也通过 RolloutScan::reached_candidate_cap 避免错误列表无限增长。
调用图:调用 1 个内部函数(reached_candidate_cap);被 1 处调用(scan_rollout_root)。
RolloutScan::active_count75–77 ↗
fn active_count(&self) -> usize
作用:统计扫描到的未归档 rollout 文件数量。未归档通常表示还在普通 sessions 文件夹里的当前会话记录。
数据流:输入是 RolloutScan 的 files 列表 → 筛出 archived 为 false 的文件 → 返回数量,不改动列表。
调用关系:thread_inventory_check_for_roots 用它把“活跃 rollout 文件数”写进体检详情,帮助用户快速看盘点规模。
RolloutScan::archived_count79–81 ↗
fn archived_count(&self) -> usize
作用:统计扫描到的已归档 rollout 文件数量。已归档通常表示文件在 archived_sessions 文件夹里。
数据流:输入是 RolloutScan 的 files 列表 → 筛出 archived 为 true 的文件 → 返回数量,不改动列表。
调用关系:thread_inventory_check_for_roots 用它把“归档 rollout 文件数”写进体检详情,和数据库里的归档行数做对照。
thread_inventory_check84–91 ↗
async fn thread_inventory_check(config: &Config) -> DoctorCheck
作用:这是外部体检流程调用的入口函数。它从配置里拿到 Codex 主目录、SQLite 目录和默认模型提供商,然后启动线程清单对账。
数据流:输入是 Config → 读取 codex_home、sqlite_home、model_provider_id → 交给 thread_inventory_check_for_roots → 返回一个 DoctorCheck 体检结果。
调用关系:它是这个文件对 doctor 系统暴露的主函数,本身不做细账,只负责把配置转换成路径和参数,再把工作交给 thread_inventory_check_for_roots。
调用图:调用 1 个内部函数(thread_inventory_check_for_roots)。
thread_inventory_check_for_roots93–153 ↗
async fn thread_inventory_check_for_roots(
codex_home: &Path,
sqlite_home: &Path,
default_provider: &str,
) -> DoctorCheck
作用:按给定目录真正执行一次“rollout 文件和状态数据库是否一致”的检查。测试也直接调用它,因为这样可以传入临时目录。
数据流:输入是 Codex 主目录、SQLite 目录、默认模型提供商 → 调用 scan_rollout_files 扫文件,调用 state_db_path 找数据库,再尝试 read_thread_state_audit_rows 读线程行 → 如果数据库不存在就走 missing_state_db_check;如果能读到数据库就走 parity_check_from_scan_and_rows;如果读库失败就直接生成警告报告。
调用关系:thread_inventory_check 会调用它。两个测试也调用它来验证正常和异常场景。它把前半段“收集材料”的活儿做好,再把不同情况分给 missing_state_db_check 或 parity_check_from_scan_and_rows。
调用图:调用 6 个内部函数(new, new, missing_state_db_check, parity_check_from_scan_and_rows, push_samples, scan_rollout_files);被 3 处调用(thread_inventory_check_ok_when_rollouts_match_db, thread_inventory_check_warns_for_missing_stale_and_mismatched_rows, thread_inventory_check);外部调用 4 个(read_thread_state_audit_rows, state_db_path, format!, vec!)。
missing_state_db_check155–210 ↗
fn missing_state_db_check(scan: RolloutScan, details: Vec<String>) -> DoctorCheck
作用:处理“状态数据库不存在”这个特殊情况。它判断这是正常的空环境,还是已经有 rollout 文件却没有数据库的异常情况。
数据流:输入是扫描结果和已有详情 → 如果没有文件、没有错误、没有坏名字、也没触顶,就返回 Ok,表示没东西可比;否则生成 Warning,并在有 rollout 文件时提示需要启动 Codex 让它回填数据库,在扫描不完整时提示检查权限或异常文件。
调用关系:thread_inventory_check_for_roots 发现 state DB 文件不存在时调用它。它不再读数据库,只根据已经扫到的文件情况生成最终 DoctorCheck。
调用图:调用 2 个内部函数(new, new);被 1 处调用(thread_inventory_check_for_roots);外部调用 1 个(format!)。
parity_check_from_scan_and_rows212–421 ↗
fn parity_check_from_scan_and_rows(
codex_home: &Path,
scan: RolloutScan,
rows: Vec<ThreadStateAuditRow>,
mut details: Vec<String>,
) -> DoctorCheck
作用:这是核心对账函数:把磁盘上的 rollout 文件和数据库里的线程行一一比较,找出所有不一致。
数据流:输入是 Codex 主目录、扫描结果、数据库线程行、已有详情 → 先按路径建立查找表,再找出数据库缺行、数据库指向坏文件、归档状态不一致、线程 ID 重复、数据库路径重复等问题 → 把统计和样例写进 details → 最后返回 Ok 或 Warning 的 DoctorCheck,并附上对应问题说明。
调用关系:thread_inventory_check_for_roots 在文件和数据库都可用时调用它。它会使用 missing_rollout_paths、duplicate_rollout_thread_ids、duplicate_db_paths、path_key、push_path_samples、push_samples 等小工具完成盘点和报告整理。
调用图:调用 8 个内部函数(new, new, duplicate_db_paths, duplicate_rollout_thread_ids, missing_rollout_paths, path_key, push_path_samples, push_samples);被 1 处调用(thread_inventory_check_for_roots);外部调用 3 个(new, new, format!)。
scan_rollout_files423–438 ↗
async fn scan_rollout_files(codex_home: &Path) -> RolloutScan
作用:扫描 Codex 主目录下两类会话文件夹:普通 sessions 和 archived_sessions。它把活跃与归档 rollout 文件收集到同一个扫描结果里。
数据流:输入是 codex_home 路径 → 创建空 RolloutScan → 分别调用 scan_rollout_root 扫 sessions(标记为未归档)和 archived_sessions(标记为已归档)→ 返回装好文件、错误、坏文件名和触顶状态的 RolloutScan。
调用关系:thread_inventory_check_for_roots 先调用它收集磁盘侧证据。它把具体递归扫目录的工作交给 scan_rollout_root。
调用图:调用 1 个内部函数(scan_rollout_root);被 1 处调用(thread_inventory_check_for_roots);外部调用 2 个(join, default)。
scan_rollout_root440–503 ↗
async fn scan_rollout_root(root: &Path, archived: bool, scan: &mut RolloutScan)
作用:递归扫描某一个 rollout 根目录,找出真正的 rollout-*.jsonl 文件,并读出它们的线程 ID。
数据流:输入是根目录、是否归档的标记、可修改的 RolloutScan → 用栈逐层遍历目录;遇到目录就继续深入,遇到非 rollout 文件就跳过;遇到 rollout 文件就调用 thread_id_from_rollout 读线程 ID;成功则加入 files,失败则记录坏名字或扫描错误;如果达到 10000 个候选对象就停止。
调用关系:scan_rollout_files 会分别用它扫描活跃目录和归档目录。它会调用 is_rollout_file 判断文件名,调用 thread_id_from_rollout 解析文件内容,也会调用 RolloutScan 的记录函数保存异常。
调用图:调用 6 个内部函数(candidate_count, record_malformed_name, record_scan_error, is_rollout_file, path_key, thread_id_from_rollout);被 1 处调用(scan_rollout_files);外部调用 3 个(format!, read_dir, vec!)。
thread_id_from_rollout505–516 ↗
async fn thread_id_from_rollout(path: &Path) -> RolloutThreadId
作用:从一个 rollout 文件里读出它代表的线程 ID。线程 ID 可以理解为一场对话的身份证号。
数据流:输入是 rollout 文件路径 → 调用 RolloutRecorder::load_rollout_items 读取文件里的记录行 → 如果文件读不了或没有可解析内容,返回 Unusable;如果能读,就用 builder_from_items 从内容和文件名推导线程信息 → 成功返回 Id,文件名不符合规则则返回 MalformedName。
调用关系:scan_rollout_root 发现候选 rollout 文件后调用它。它把底层 rollout 解析库的结果翻译成扫描流程能理解的三种结果:有 ID、名字坏、文件不可用。
调用图:调用 1 个内部函数(load_rollout_items);被 1 处调用(scan_rollout_root);外部调用 2 个(Unusable, builder_from_items)。
is_rollout_file518–524 ↗
fn is_rollout_file(path: &Path) -> bool
作用:判断一个路径是不是本检查关心的 rollout 文件。规则是扩展名必须是 .jsonl,文件名必须以 rollout- 开头。
数据流:输入是路径 → 检查扩展名和文件名开头 → 返回 true 或 false,不读取文件内容。
调用关系:scan_rollout_root 遍历目录时用它过滤无关文件,避免把普通文件误当成会话记录来解析。
调用图:被 1 处调用(scan_rollout_root);外部调用 3 个(new, extension, file_name)。
count_or_skipped526–532 ↗
fn count_or_skipped(count: usize, complete: bool) -> String
作用:把一个统计数字转换成报告里显示的文字。如果扫描没完整做完,就显示“跳过”,避免给出误导性的数字。
数据流:输入是数量和 complete 标记 → complete 为 true 时返回数字字符串;否则返回 skipped (scan cap reached) → 不改动外部数据。
调用关系:parity_check_from_scan_and_rows 在汇总 stale rows 和 archive mismatches 时使用它。因为扫描触顶后,某些对比结果就不可靠。
path_key534–536 ↗
fn path_key(path: &Path) -> PathBuf
作用:把路径转换成适合比较的标准形式。这样可以减少不同写法的同一路径被误判为不同路径。
数据流:输入是路径 → 调用 normalize_for_path_comparison 做规范化;如果规范化失败,就退回原路径 → 返回用于 HashMap 查找和比较的 PathBuf。
调用关系:scan_rollout_root 存文件时、parity_check_from_scan_and_rows 建索引时、archived_from_rollout_path 判断文件位置时都会用它,保证路径比较尽量一致。
调用图:被 3 处调用(archived_from_rollout_path, parity_check_from_scan_and_rows, scan_rollout_root);外部调用 1 个(normalize_for_path_comparison)。
archived_from_rollout_path538–547 ↗
fn archived_from_rollout_path(codex_home: &Path, path: &Path) -> Option<bool>
作用:根据 rollout 文件所在文件夹判断它应该算归档还是活跃。它看路径是在 archived_sessions 下面,还是 sessions 下面。
数据流:输入是 Codex 主目录和某个 rollout 路径 → 先用 path_key 标准化路径 → 如果在 archived_sessions 下返回 Some(true),如果在 sessions 下返回 Some(false),否则返回 None。
调用关系:parity_check_from_scan_and_rows 在检查数据库归档标记时会间接用到它:当扫描表里没找到但磁盘文件还存在时,用路径位置推断正确归档状态。
missing_rollout_paths549–559 ↗
fn missing_rollout_paths(
files: &'a [RolloutAuditFile],
rows_by_key: &HashMap<PathBuf, Vec<&ThreadStateAuditRow>>,
archived: bool,
) -> Vec<&'a Path>
作用:找出那些磁盘上有 rollout 文件、但数据库里没有匹配线程行的路径。可以分别查活跃文件或归档文件。
数据流:输入是扫描到的文件列表、按路径分组的数据库行、目标归档状态 → 筛出归档状态匹配但 has_matching_thread_row 为 false 的文件 → 返回这些文件的路径列表。
调用关系:parity_check_from_scan_and_rows 用它分别计算 missing_active 和 missing_archived。它依靠 has_matching_thread_row 判断数据库是否真的有同路径同线程 ID 的记录。
调用图:被 1 处调用(parity_check_from_scan_and_rows);外部调用 1 个(iter)。
has_matching_thread_row561–569 ↗
fn has_matching_thread_row(
file: &RolloutAuditFile,
rows_by_key: &HashMap<PathBuf, Vec<&ThreadStateAuditRow>>,
) -> bool
作用:判断某个 rollout 文件在数据库里是否有真正匹配的一行。匹配不只看路径,还要看线程 ID。
数据流:输入是一个 RolloutAuditFile 和按路径分组的数据库行 → 先按文件 key 找数据库行;没有则返回 false;有则检查其中是否存在 id 等于文件 thread_id 的行 → 返回 true 或 false。
调用关系:missing_rollout_paths 用它做逐文件判断。它是防止“路径碰巧一样但线程 ID 不对”这种假匹配的关键小检查。
duplicate_rollout_thread_ids571–582 ↗
fn duplicate_rollout_thread_ids(files: &[RolloutAuditFile]) -> Vec<String>
作用:找出磁盘 rollout 文件中重复出现的线程 ID。一个线程 ID 理论上应该只对应一个线程。
数据流:输入是扫描到的 rollout 文件列表 → 用 seen 记录第一次看到的 ID,用 duplicates 记录再次出现的 ID → 排序后返回重复 ID 列表。
调用关系:parity_check_from_scan_and_rows 用它判断磁盘侧是否有重复线程。若有重复,会在 DoctorCheck 里生成“duplicate thread inventory entries found”问题。
调用图:被 1 处调用(parity_check_from_scan_and_rows);外部调用 2 个(new, iter)。
duplicate_db_paths584–592 ↗
fn duplicate_db_paths(rows_by_key: &HashMap<PathBuf, Vec<&ThreadStateAuditRow>>) -> Vec<PathBuf>
作用:找出数据库里被多行同时指向的 rollout 路径。一个 rollout 文件路径理论上不该对应多条线程记录。
数据流:输入是按路径分组的数据库行 → 找出每组行数大于 1 的路径 → 排序后返回这些重复路径。
调用关系:parity_check_from_scan_and_rows 用它检查数据库侧重复记录。发现后会和重复线程 ID 一起写入体检问题。
调用图:被 1 处调用(parity_check_from_scan_and_rows)。
source_category594–619 ↗
fn source_category(source: &str) -> &'static str
作用:把数据库里记录的会话来源整理成少量好读的类别,比如 cli、vscode、subagent:review。这样报告不会充满复杂 JSON。
数据流:输入是 source 字符串 → 先尝试按 SessionSource 解析,既支持 JSON 形式,也支持普通字符串形式 → 解析失败返回 unparsable;解析成功则按来源枚举映射成固定分类文字。
调用关系:parity_check_from_scan_and_rows 在生成“rollout DB sources”汇总时使用它。测试 tests::source_category_coarsens_structured_sources 验证它能把结构化来源压缩成稳定类别。
count_summary621–657 ↗
fn count_summary(values: I) -> String
作用:把一串类别值统计成简短摘要,比如 cli=10, vscode=2。类别太多时只显示前 8 类,其余合并成 other。
数据流:输入是一批可转成字符串的值 → 逐个计数 → 如果没有值返回 none;否则按数量从多到少、名字从小到大排序 → 取前 8 项拼成字符串,剩下的合并说明 → 返回摘要文字。
调用关系:parity_check_from_scan_and_rows 用它汇总模型提供商和来源类别。测试 tests::count_summary_caps_distinct_values 专门验证类别过多时会被截断并合并。
调用图:被 1 处调用(count_summary_caps_distinct_values);外部调用 2 个(new, format!)。
push_path_samples659–665 ↗
fn push_path_samples(
details: &mut Vec<String>,
label: &str,
paths: impl Iterator<Item = &'a Path>,
)
作用:把一批路径样例追加到体检详情里。它只是给 push_samples 包了一层,让路径能以人能读的文字显示。
数据流:输入是详情列表、标签、一批路径 → 把路径转成显示字符串 → 调用 push_samples 最多追加 5 条样例 → 修改 details。
调用关系:parity_check_from_scan_and_rows 用它添加缺失文件、过期数据库行、归档不匹配、重复路径等样例,方便用户或支持人员定位具体文件。
调用图:调用 1 个内部函数(push_samples);被 1 处调用(parity_check_from_scan_and_rows);外部调用 1 个(map)。
push_samples667–675 ↗
fn push_samples(details: &mut Vec<String>, label: &str, values: I)
作用:把最多 5 个样例值写进体检详情,避免报告太长。它像报告里的“举几个例子”。
数据流:输入是详情列表、标签、一批值 → 只取前 SAMPLE_LIMIT 个 → 每个拼成“标签: 值”的文字追加到 details → 返回时 details 变长。
调用关系:thread_inventory_check_for_roots 用它记录扫描错误和坏文件名样例;parity_check_from_scan_and_rows 用它记录重复线程 ID;push_path_samples 也把路径样例交给它处理。
调用图:被 3 处调用(parity_check_from_scan_and_rows, push_path_samples, thread_inventory_check_for_roots);外部调用 2 个(take, format!)。
tests::thread_inventory_check_ok_when_rollouts_match_db689–729 ↗
async fn thread_inventory_check_ok_when_rollouts_match_db()
作用:测试正常情况:磁盘 rollout 文件和数据库线程行完全匹配时,体检应该通过。
数据流:先创建临时环境 → 写一个活跃 rollout 和一个归档 rollout → 分别插入匹配的数据库行 → 调用 thread_inventory_check_for_roots → 断言状态是 Ok,且各种缺失、过期、归档不匹配数量都是 0。
调用关系:它验证主流程的 happy path,也就是最理想的对账结果。它依赖 tests::Fixture::new、tests::Fixture::write_rollout、tests::Fixture::insert_thread_row 和 tests::assert_detail。
调用图:调用 1 个内部函数(thread_inventory_check_for_roots);外部调用 3 个(assert_eq!, new, assert_detail)。
tests::thread_inventory_check_warns_for_missing_stale_and_mismatched_rows732–788 ↗
async fn thread_inventory_check_warns_for_missing_stale_and_mismatched_rows()
作用:测试异常情况:有文件没入库、有数据库行指向不存在文件、归档标记不对时,体检应该报警。
数据流:先创建临时环境 → 写一个会缺数据库行的 rollout,写一个归档文件但插入成未归档,另造一个数据库指向但磁盘不存在的路径 → 调用 thread_inventory_check_for_roots → 断言状态是 Warning、问题数量为 3,并检查详情里出现对应统计和样例。
调用关系:它覆盖 parity_check_from_scan_and_rows 的主要告警分支,确保报告不会把这些真实风险漏掉,也不会给出不该出现的重启建议。
调用图:调用 1 个内部函数(thread_inventory_check_for_roots);外部调用 4 个(assert!, assert_eq!, new, assert_detail)。
tests::Fixture::new796–809 ↗
tests::Fixture::write_rollout811–838 ↗
fn write_rollout(&self, archived: bool, timestamp: &str, thread_id: &str) -> PathBuf
作用:在测试目录里写一个假的 rollout 文件。它模拟 Codex 真正保存会话记录的样子。
数据流:输入是是否归档、时间戳、线程 ID → 选择 sessions 或 archived_sessions 目录 → 创建目录 → 组装一行 SessionMeta 会话元数据 → 序列化成 JSON 写入 rollout-时间-ID.jsonl → 返回文件路径。
调用关系:测试用它准备磁盘侧数据。thread_inventory_check_for_roots 随后会像处理真实文件一样扫描这些文件。
调用图:调用 1 个内部函数(from_string);外部调用 7 个(default, path, format!, SessionMeta, to_string, create_dir_all, write)。
tests::Fixture::insert_thread_row840–884 ↗
async fn insert_thread_row(&self, id: &str, rollout_path: &Path, archived: bool)
作用:往测试 SQLite 数据库的 threads 表插入一条线程记录。它模拟 Codex 状态数据库里已有的清单数据。
数据流:输入是线程 ID、rollout 路径、是否归档 → 找到测试数据库文件 → 建立 SQLite 连接池 → 执行 INSERT,把路径、来源、模型提供商、归档标记等字段写入 threads 表 → 关闭连接池。
调用关系:测试用它准备数据库侧数据。然后 thread_inventory_check_for_roots 会读取这些行并和 tests::Fixture::write_rollout 写出的文件对账。
调用图:外部调用 6 个(display, new, new, path, state_db_path, query)。
tests::assert_detail887–895 ↗
fn assert_detail(check: &DoctorCheck, label: &str, expected: &str)
作用:测试小工具:检查 DoctorCheck 的详情里某个标签对应的值是不是预期值。
数据流:输入是检查结果、标签、期望值 → 在 details 里找以“标签: ”开头的条目 → 取出后面的实际值 → 用断言比较实际值和期望值。
调用关系:两个主测试都用它验证报告里的关键统计数字。它让测试代码少写重复的查找逻辑。
调用图:外部调用 2 个(assert_eq!, format!)。
tests::source_category_coarsens_structured_sources898–910 ↗
fn source_category_coarsens_structured_sources()
作用:测试 source_category 能把不同形式的来源字段归成稳定、简短的类别。
数据流:输入是几个固定来源字符串,包括普通 cli 和 JSON 形式的 subagent 来源 → 调用 source_category → 断言输出分别是 cli、subagent:memory_consolidation、subagent:thread_spawn。
调用关系:它专门保护报告汇总逻辑,避免以后来源格式变化时把统计类别弄乱。
调用图:外部调用 1 个(assert_eq!)。
tests::count_summary_caps_distinct_values913–920 ↗
fn count_summary_caps_distinct_values()
作用:测试 count_summary 在类别太多时会限制展示数量,并把剩余类别合并成 other。
数据流:输入是 a 到 i 九个不同值 → 调用 count_summary → 断言结果只列出前八项,并把第九项写成 other=1 across 1 categories。
调用关系:它验证报告摘要不会无限变长。count_summary 本身也被 parity_check_from_scan_and_rows 用在真实体检详情里。
调用图:调用 1 个内部函数(count_summary);外部调用 1 个(assert_eq!)。
cli/src/doctor.rs源码 ↗
这个文件像一个“电脑体检单生成器”。用户运行 doctor 后,它先收集当前配置和运行环境,再分头做很多小检查:程序是不是装对了,配置文件能不能读,登录凭据是否完整,网络代理和证书有没有问题,终端能不能显示颜色,MCP 服务器是否缺环境变量等。每个小检查都会产出一个 DoctorCheck,里面有状态、摘要、细节和修复建议;具体问题会放进 DoctorIssue。最后它把这些检查合成一份报告,可以打印成人能读的文本,也可以输出 JSON。这里还特别注意隐私:JSON 报告会把可能包含路径、命令参数或密钥线索的内容做简化或遮盖,避免用户把报告发出去时泄露太多本机信息。 这段代码像一个电脑体检员。它会看终端窗口够不够大,tmux(一种终端复用工具)里有哪些设置;会检查 Codex 的本地目录、日志目录、SQLite 数据库(一种本地小数据库)是否能读、是否损坏;还会试着访问模型服务、WebSocket(浏览器和服务器之间保持长连接的一种网络通道)和 MCP 服务,判断是网络、代理、防火墙、认证还是地址配置出了问题。它不是简单报“失败”,而是把测到的细节、预期状态和建议修复办法放进 DoctorCheck/DoctorIssue 里,最后给用户一份更容易理解的健康报告。文件后半部分还有大量测试,专门确认这些检查在各种边界情况下不会误报或泄露密钥。 doctor 功能的作用,是在用户真正运行工具前,先帮他看看周围环境有没有坑。比如终端是不是太窄、颜色能不能显示、MCP 服务能不能连上、配置里写的命令是否真的存在、证书文件能不能读。这个片段里的函数大多是测试用例:它们故意搭出一些“出问题”的场景,比如假服务器超时、文件没读权限、TERM=dumb、语言环境不是 UTF-8,然后调用真正的检查函数,看返回的状态、摘要和修复建议是否符合预期。这样做的价值是防止以后改代码时,把诊断结果改坏。它像给体检医生准备的一组标准病例,医生必须每次都判断对。
DoctorIssue::new229–238 ↗
fn new(severity: CheckStatus, cause: impl Into<String>) -> Self
作用:新建一条具体问题记录,比如“TERM=dumb”或“凭据缺失”。它先只填最重要的两项:严重程度和问题原因。
数据流:输入一个状态和一段原因文字 → 把原因转成字符串,并把测量值、期望值、修复办法、相关字段都先留空 → 输出一个可以继续补充信息的 DoctorIssue。
调用关系:各种检查发现具体毛病时会先调用它起草问题单,然后再接着用 measured、expected、remedy、field 这些方法把问题说明补完整。
调用图:被 8 处调用(git_check_from_inputs, provider_reachability_check, terminal_check_from_inputs, terminal_size_issues, missing_state_db_check, parity_check_from_scan_and_rows, thread_inventory_check_for_roots, terminal_title_check_from_inputs);外部调用 2 个(into, new)。
DoctorIssue::measured240–243 ↗
fn measured(mut self, measured: impl Into<String>) -> Self
作用:给问题补上“实际看到的情况”。比如实际测到 TERM=dumb。
数据流:输入已有的问题和一段实际值 → 把实际值写进 measured 字段 → 返回更新后的问题,方便继续链式补充。
调用关系:通常跟在 DoctorIssue::new 后面,用来让报告不只说有问题,还说清楚机器上实际是什么样。
调用图:外部调用 1 个(into)。
DoctorIssue::expected245–248 ↗
fn expected(mut self, expected: impl Into<String>) -> Self
作用:给问题补上“本来应该是什么”。这样用户能对比实际值和理想值。
数据流:输入已有的问题和期望说明 → 写入 expected 字段 → 返回补充后的问题。
调用关系:常和 measured、remedy 一起使用,让终端、配置等检查给出完整的“实际 vs 应该”。
调用图:外部调用 1 个(into)。
DoctorIssue::remedy250–253 ↗
fn remedy(mut self, remedy: impl Into<String>) -> Self
作用:给问题补上修复建议。它告诉用户下一步可以怎么做。
数据流:输入已有的问题和修复文字 → 写入 remedy 字段 → 返回更新后的问题。
调用关系:由各类检查在发现可修复问题时使用,最后会随 DoctorCheck 一起显示给用户。
调用图:外部调用 1 个(into)。
DoctorIssue::field255–258 ↗
fn field(mut self, field: impl Into<String>) -> Self
作用:标记这个问题和哪个字段或环境变量有关。比如 TERM、TERMINFO。
数据流:输入已有问题和字段名 → 把字段名追加到 fields 列表 → 返回更新后的问题。
调用关系:通常在构造问题的最后使用,方便 JSON 报告或人类报告指出该看哪一项。
调用图:外部调用 1 个(into)。
DoctorCheck::new262–278 ↗
fn new(
id: impl Into<String>,
category: impl Into<String>,
status: CheckStatus,
summary: impl Into<String>,
) -> Self
作用:新建一个体检项目结果。它是 doctor 报告里的基本条目,比如“配置检查”“认证检查”。
数据流:输入检查编号、分类、状态和一句摘要 → 初始化空细节、空问题、空修复建议和耗时 0 → 输出 DoctorCheck。
调用关系:几乎所有具体检查都会用它生成结果;运行包装函数之后还会给它补上耗时。
调用图:被 25 处调用(auth_check, background_server_check, config_check, fallback_state_check, git_check_from_inputs, installation_check, mcp_check_from_servers, network_check, render_human_report_includes_threads_row_in_environment, provider_reachability_check (+15 more));外部调用 2 个(into, new)。
DoctorCheck::detail280–283 ↗
fn detail(mut self, detail: impl Into<String>) -> Self
作用:给某个检查追加一条细节说明。比如配置文件路径、当前模型名。
数据流:输入已有检查和一条文字 → 把文字加入 details → 返回更新后的检查。
调用关系:用于检查过程中逐步丰富报告内容,让用户能看到检查依据。
调用图:外部调用 1 个(into)。
DoctorCheck::details285–288 ↗
fn details(mut self, details: Vec<String>) -> Self
作用:一次性给检查加入多条细节。适合检查函数先收集好一整组信息再塞进去。
数据流:输入已有检查和一组字符串 → 把这组字符串追加到 details → 返回更新后的检查。
调用关系:很多检查函数在最后创建 DoctorCheck 后调用它,把前面收集的环境信息放进报告。
DoctorCheck::remediation290–293 ↗
fn remediation(mut self, remediation: impl Into<String>) -> Self
作用:给检查结果补上一段总体修复建议。比如“重新运行 codex login”。
数据流:输入已有检查和建议文字 → 写入 remediation → 返回更新后的检查。
调用关系:当检查状态不是正常,或者失败原因明确时,各检查函数会用它告诉用户怎么补救。
调用图:外部调用 1 个(into)。
DoctorCheck::issue295–298 ↗
fn issue(mut self, issue: DoctorIssue) -> Self
作用:把一条具体问题挂到某个检查下面。一个检查可以有多个问题。
数据流:输入已有检查和 DoctorIssue → 把问题追加到 issues 列表 → 返回更新后的检查。
调用关系:terminal_check_from_inputs 等检查先收集多个 DoctorIssue,再逐个放进 DoctorCheck。
run_doctor306–331 ↗
async fn run_doctor(
command: DoctorCommand,
root_config_overrides: CliConfigOverrides,
interactive: &TuiCli,
arg0_paths: &Arg0DispatchPaths,
) -> anyhow::Result<()>
作用:这是 doctor 命令的主要执行入口。它负责生成报告、按用户要求输出文本或 JSON,并在失败时返回非零退出码。
数据流:输入命令参数、配置覆盖项、交互式参数和程序路径信息 → 调用 build_report 生成完整报告 → 根据 json 开关打印 JSON 或人类可读文本;如果总体失败就让进程以 1 退出,否则正常返回。
调用关系:它由 cli_main 调用,是 doctor 子命令进入本文件的门口;真正的检查编排交给 build_report,输出格式化交给报告渲染相关函数。
调用图:调用 1 个内部函数(build_report);被 1 处调用(cli_main);外部调用 3 个(print!, println!, exit)。
build_report333–492 ↗
async fn build_report(
command: &DoctorCommand,
root_config_overrides: CliConfigOverrides,
interactive: &TuiCli,
arg0_paths: &Arg0DispatchPaths,
) -> DoctorReport
作用:组织所有体检项目,最后拼成一份完整 DoctorReport。它决定哪些检查先跑、哪些可以并发跑、配置坏了时还能做哪些兜底检查。
数据流:输入 doctor 命令、配置覆盖、交互参数和路径信息 → 先跑系统、安装、运行时、搜索等基础检查,再尝试加载配置;配置成功就并发跑认证、网络、MCP、沙箱、终端、Git、状态库、服务可达性等检查,配置失败就跑较少的兜底检查 → 计算总状态、写入生成时间和版本号,输出报告。
调用关系:run_doctor 只管调用它拿报告;它像总调度员一样调用 load_config、run_sync_check、run_async_check、overall_status、generated_at 等函数,把各个检查结果串起来。
调用图:调用 8 个内部函数(default_reachability_plan, generated_at, load_config, overall_status, doctor_progress, provider_reachability_plan, run_sync_check, shared_from_config);被 1 处调用(run_doctor);外部调用 3 个(new, env!, join!)。
load_config494–520 ↗
async fn load_config(
root_config_overrides: CliConfigOverrides,
interactive: &TuiCli,
arg0_paths: &Arg0DispatchPaths,
) -> anyhow::Result<Config>
作用:按 doctor 运行时的命令行参数加载 Codex 配置。它会把临时覆盖项和交互式选项一起合进去。
数据流:输入根配置覆盖、交互式参数和可执行文件路径 → 解析命令行覆盖;如果用户开了 web search 就额外写入配置;再构造 ConfigOverrides 并交给 ConfigBuilder → 成功输出 Config,失败输出带上下文的错误。
调用关系:build_report 在进入依赖配置的检查前调用它;它内部使用 config_overrides_from_interactive 把 TUI 参数转成配置覆盖。
调用图:调用 2 个内部函数(config_overrides_from_interactive, parse_overrides);被 1 处调用(build_report);外部调用 2 个(default, String)。
config_overrides_from_interactive522–552 ↗
fn config_overrides_from_interactive(
interactive: &TuiCli,
arg0_paths: &Arg0DispatchPaths,
) -> ConfigOverrides
作用:把交互式启动参数转换成配置覆盖。它让 doctor 检查时看到的配置和真实启动 Codex 时尽量一致。
数据流:输入 TuiCli 参数和程序路径 → 根据危险绕过开关、模型、工作目录、沙箱、OSS 模式、额外可写目录等生成 ConfigOverrides → 输出这份覆盖配置。
调用关系:load_config 调用它来准备 ConfigBuilder;测试也会直接检查它是否保留了全局选项。
调用图:被 2 处调用(load_config, config_overrides_from_interactive_preserves_global_options);外部调用 1 个(default)。
JsonDetailValue::push608–615 ↗
fn push(&mut self, value: String)
作用:给 JSON 细节里的同名字段追加一个值。它能把“单个值”自动升级成“多个值”。
数据流:输入当前 JsonDetailValue 和新字符串 → 如果原来只有一个值,就变成包含旧值和新值的列表;如果已经是列表,就追加进去 → 原对象被就地更新。
调用关系:structured_json_details 在整理重复细节键时调用它,避免同一个键的后续值丢失。
调用图:外部调用 2 个(Many, vec!)。
redacted_json_report618–634 ↗
fn redacted_json_report(report: &DoctorReport) -> JsonDoctorReport
作用:把内部 DoctorReport 转成适合对外分享的 JSON 报告。重点是结构化并去掉敏感细节。
数据流:输入完整报告 → 遍历每个检查,调用 redacted_json_check 生成脱敏版本,并按检查 id 放入映射 → 输出 JsonDoctorReport。
调用关系:run_doctor 在用户要求 JSON 输出时会使用它;测试也会验证它会结构化和清理细节。
调用图:被 1 处调用(redacted_json_report_structures_and_sanitizes_details)。
redacted_json_check636–649 ↗
fn redacted_json_check(check: &DoctorCheck) -> JsonDoctorCheck
作用:把单个检查转成脱敏 JSON 形式。它会把细节拆成键值和普通备注两类。
数据流:输入 DoctorCheck → 调用 structured_json_details 处理 details,脱敏 remediation,并把 issues 逐个交给 redacted_json_issue → 输出 JsonDoctorCheck。
调用关系:redacted_json_report 处理每个检查时调用它;它负责连接检查级脱敏和问题级脱敏。
调用图:调用 1 个内部函数(structured_json_details)。
redacted_json_issue651–664 ↗
fn redacted_json_issue(issue: &DoctorIssue) -> JsonDoctorIssue
作用:把一条具体问题转成适合 JSON 输出的脱敏版本。防止原因、实际值、建议等带出敏感信息。
数据流:输入 DoctorIssue → 对 cause、measured、expected、remedy、fields 逐项调用 redact_detail → 输出 JsonDoctorIssue。
调用关系:redacted_json_check 在处理检查里的问题列表时调用它。
调用图:调用 1 个内部函数(redact_detail)。
structured_json_details671–694 ↗
fn structured_json_details(details: &[String]) -> (BTreeMap<String, JsonDetailValue>, Vec<String>)
作用:把一堆“键: 值”形式的细节整理成 JSON 里的结构化字段。不是这种格式的内容会放进 notes。
数据流:输入细节字符串列表 → 先逐条脱敏;能按冒号分成键和值的放进 BTreeMap,重复键用 JsonDetailValue::push 合并;不能拆的放进 notes → 输出结构化细节和备注列表。
调用关系:redacted_json_check 调用它来让 JSON 报告更好读、更容易被工具处理。
调用图:调用 2 个内部函数(json_detail_value, redact_detail);被 1 处调用(redacted_json_check);外部调用 3 个(new, new, One)。
json_detail_value696–709 ↗
fn json_detail_value(key: &str, value: &str) -> String
作用:决定某个细节值在 JSON 里该原样显示还是只显示“set”。它主要保护编辑器和分页器这类可能带命令参数的环境变量。
数据流:输入键名和值 → 如果键是 VISUAL、EDITOR、PAGER 等且值不是 not set,就返回 set;否则返回原值 → 输出安全后的字符串。
调用关系:structured_json_details 在放入 JSON 结构化细节前调用它。
调用图:被 1 处调用(structured_json_details);外部调用 1 个(matches!)。
run_sync_check711–722 ↗
fn run_sync_check(
label: &'static str,
progress: Arc<dyn DoctorProgress>,
f: impl FnOnce() -> DoctorCheck,
) -> DoctorCheck
作用:运行一个普通同步检查,并记录开始、结束、状态和耗时。同步检查就是不用等待异步网络任务的检查。
数据流:输入检查标签、进度对象和检查函数 → 通知进度开始,记录时间,执行检查函数,写入耗时,再通知进度结束 → 输出带耗时的 DoctorCheck。
调用关系:build_report 用它包装系统、安装、配置、网络、终端等同步检查;测试也会验证进度通知是否正确。
调用图:被 2 处调用(build_report, run_sync_check_notifies_progress);外部调用 1 个(now)。
run_async_check724–751 ↗
async fn run_async_check(
label: &'static str,
progress: Arc<dyn DoctorProgress>,
future: Fut,
) -> DoctorCheck
作用:运行一个异步检查,并在它耗时较久时定期提示还在进行。异步检查常见于网络或文件状态探测。
数据流:输入检查标签、进度对象和未来会完成的任务 Future → 通知开始,计时,一边等任务完成一边按间隔检查是否需要 heartbeat 提醒;任务完成后写入耗时并通知结束 → 输出 DoctorCheck。
调用关系:build_report 用它包装 websocket、Git、状态库、MCP、后台服务和供应商可达性等异步检查;测试会检查慢任务进度提示。
调用图:被 1 处调用(run_async_check_notifies_progress);外部调用 4 个(now, pin!, select!, interval)。
overall_status753–764 ↗
fn overall_status(checks: &[DoctorCheck]) -> CheckStatus
作用:根据所有小检查推导整份报告的总状态。只要有失败,总体就是失败;没有失败但有警告,总体就是警告。
数据流:输入检查列表 → 扫描是否存在 Fail,再扫描是否存在 Warning → 输出 Fail、Warning 或 Ok。
调用关系:build_report 在所有检查结束后调用它,决定 doctor 最终是否算通过;run_doctor 又根据这个结果决定是否退出码为 1。
调用图:被 1 处调用(build_report);外部调用 1 个(iter)。
generated_at766–774 ↗
fn generated_at() -> String
作用:给报告写一个生成时间。这里用的是 Unix 纪元以来的秒数说明。
数据流:读取系统当前时间 → 如果能算出距离 1970-01-01 的秒数,就格式化成字符串;失败则返回 unknown → 输出时间字符串。
调用关系:build_report 创建 DoctorReport 时调用它,让报告知道自己是什么时候生成的。
调用图:被 1 处调用(build_report);外部调用 2 个(format!, now)。
installation_check776–866 ↗
fn installation_check(show_details: bool) -> DoctorCheck
作用:检查当前 Codex 安装是否自洽,尤其是 npm 全局安装时,更新目标是不是当前正在运行的那一份。
数据流:输入是否显示详细信息 → 读取当前可执行文件、安装上下文、包管理器环境变量、PATH 里的 codex 位置;如果是 npm 管理的安装,还检查 npm root -g 是否指向同一个包目录 → 输出安装检查结果,必要时带失败或警告和修复建议。
调用关系:build_report 通过 run_sync_check 调用它;它会借助 doctor_install_context、doctor_managed_by_npm、codex_path_entries、npm_global_root_check 等小工具完成判断。
调用图:调用 8 个内部函数(new, codex_path_entries, doctor_install_context, doctor_managed_by_npm, inherited_managed_env_for_cargo_binary, npm_global_root_check, push_env_path_detail, push_path_detail);外部调用 3 个(new, current_exe, format!)。
doctor_install_context868–877 ↗
fn doctor_install_context(current_exe: Option<&Path>) -> InstallContext
作用:取得 doctor 眼里的安装来源。它会特别排除一种情况:本地 cargo 编译的二进制继承了包管理器环境变量。
数据流:输入当前可执行文件路径 → 如果判断是 cargo 构建产物误带了 npm/bun 环境,就返回 Other;否则返回当前安装上下文 → 输出 InstallContext。
调用关系:installation_check 用它解释“Codex 是怎么安装的”。
调用图:调用 2 个内部函数(inherited_managed_env_for_cargo_binary, current);被 1 处调用(installation_check)。
doctor_managed_by_npm879–882 ↗
fn doctor_managed_by_npm(current_exe: Option<&Path>) -> bool
作用:判断当前运行的 Codex 是否真的是 npm 管理的安装。
数据流:输入当前可执行文件路径 → 查看 CODEX_MANAGED_BY_NPM 是否存在,并排除 cargo 构建产物继承环境变量的情况 → 输出布尔值。
调用关系:installation_check 用它决定是否需要进一步做 npm 全局目录一致性检查。
调用图:调用 1 个内部函数(inherited_managed_env_for_cargo_binary);被 1 处调用(installation_check);外部调用 1 个(var_os)。
inherited_managed_env_for_cargo_binary884–901 ↗
fn inherited_managed_env_for_cargo_binary(current_exe: Option<&Path>) -> bool
作用:识别“本地开发编译出来的 Codex 误继承了 npm/bun 管理标记”的情况。这样 doctor 不会把开发环境误判成包管理器安装。
数据流:输入当前可执行文件路径 → 先看是否存在 npm 或 bun 管理环境变量;再检查路径里是否有 target/debug 或 target/release → 是则返回 true,否则 false。
调用关系:installation_check、doctor_install_context、doctor_managed_by_npm 都用它避免安装来源误判。
调用图:被 3 处调用(doctor_install_context, doctor_managed_by_npm, installation_check);外部调用 1 个(var_os)。
describe_install_context903–946 ↗
fn describe_install_context(context: &InstallContext) -> String
作用:把安装上下文翻译成用户能看的文字。比如 standalone、npm、bun、brew,以及相关目录。
数据流:输入 InstallContext → 根据安装方式和包目录布局拼出描述;可选路径用 display_optional_path 处理 → 输出一段说明文字。
调用关系:installation_check 把它的结果放进细节,帮助用户知道当前 Codex 来自哪里。
调用图:调用 2 个内部函数(describe_method_with_package_layout, display_optional_path);外部调用 1 个(format!)。
describe_method_with_package_layout948–964 ↗
fn describe_method_with_package_layout(
method: &str,
package_layout: Option<&CodexPackageLayout>,
) -> String
作用:为 npm、bun、brew、other 这类安装方式生成统一格式的描述。
数据流:输入安装方式名称和可选包布局 → 有布局就写出 package、bin、resources、path 目录;没有布局就只返回方式名 → 输出描述字符串。
调用关系:describe_install_context 在非 standalone 分支里调用它。
调用图:调用 1 个内部函数(display_optional_path);被 1 处调用(describe_install_context);外部调用 1 个(format!)。
display_optional_path966–969 ↗
fn display_optional_path(path: Option<&Path>) -> String
作用:把可有可无的路径转成好读文字。没有路径时显示 none。
数据流:输入 Option<Path> → 有路径就转成显示字符串,没有就返回 none → 输出字符串。
调用关系:describe_install_context 和 describe_method_with_package_layout 用它避免报告里出现空值或难懂格式。
调用图:被 2 处调用(describe_install_context, describe_method_with_package_layout)。
npm_global_root_check984–999 ↗
fn npm_global_root_check() -> NpmRootCheck
作用:检查 npm 全局安装目录里的 @openai/codex,是否就是当前运行的 Codex 包目录。
数据流:读取 CODEX_MANAGED_PACKAGE_ROOT → 如果没有就返回缺包根;否则运行 npm root -g,取输出中的目录,再交给 compare_npm_package_roots 比较 → 输出匹配、错配或 npm 不可用等结果。
调用关系:installation_check 在确认是 npm 管理安装后调用它,用来发现“更新会更新到另一份 Codex”的危险情况。
调用图:调用 2 个内部函数(compare_npm_package_roots, run_command);被 1 处调用(installation_check);外部调用 3 个(from, NpmUnavailable, var_os)。
compare_npm_package_roots1001–1015 ↗
fn compare_npm_package_roots(running_package_root: &Path, npm_root: &Path) -> NpmRootCheck
作用:比较当前运行包目录和 npm 全局目标包目录是不是同一个地方。
数据流:输入运行中的包根目录和 npm root 目录 → 拼出 npm root 下的 @openai/codex;把两边路径标准化后比较 → 输出 Match 或 Mismatch。
调用关系:npm_global_root_check 拿到 npm root -g 的结果后调用它完成核心判断。
调用图:调用 1 个内部函数(normalize_path_for_compare);被 1 处调用(npm_global_root_check);外部调用 2 个(join, to_path_buf)。
normalize_path_for_compare1017–1025 ↗
fn normalize_path_for_compare(path: &Path) -> String
作用:把路径整理成适合比较的字符串。它会尽量解析真实路径,并统一斜杠;Windows 上还会忽略大小写。
数据流:输入路径 → 尝试 canonicalize 得到规范路径,失败就用原路径;把反斜杠换成斜杠;Windows 转小写 → 输出标准化字符串。
调用关系:compare_npm_package_roots 用它减少符号链接、斜杠和大小写造成的误判。
调用图:被 1 处调用(compare_npm_package_roots);外部调用 2 个(canonicalize, cfg!)。
display_list1027–1037 ↗
codex_path_entries1039–1052 ↗
fn codex_path_entries() -> Vec<String>
作用:找出 PATH 里能找到的所有 codex 命令位置。多个位置可能说明用户机器上装了多份 Codex。
数据流:根据系统选择 where codex 或 which -a codex → 运行命令,读取输出每一行,去掉空行 → 输出路径字符串列表。
调用关系:installation_check 用它判断 PATH 是否有多个 Codex 入口,并在需要时显示这些位置。
调用图:调用 1 个内部函数(run_command);被 1 处调用(installation_check)。
run_command1054–1071 ↗
fn run_command(program: &str, args: I) -> Result<String, String>
作用:运行一个外部命令并取回标准输出。失败时把错误转成字符串,方便放进 doctor 报告。
数据流:输入程序名和参数 → 启动进程并等待完成;成功返回 stdout;命令启动失败或退出码失败时返回 stderr 或状态说明 → 输出 Result<String, String>。
调用关系:codex_path_entries 和 npm_global_root_check 用它调用系统工具,比如 which、where、npm。
调用图:被 2 处调用(codex_path_entries, npm_global_root_check);外部调用 3 个(from_utf8_lossy, new, format!)。
config_check1073–1102 ↗
fn config_check(config: &Config) -> DoctorCheck
作用:检查配置是否已成功加载,并把关键配置位置和设置列出来。它也会报告启动警告。
数据流:输入 Config → 收集 CODEX_HOME、cwd、模型、提供商、日志目录、SQLite 目录、MCP 数量、功能开关、config.toml 状态;如果有 startup_warnings 就加入统计和每条警告 → 输出配置检查结果,可能是 Ok 或 Warning。
调用关系:build_report 在配置加载成功后通过 run_sync_check 调用它;它内部调用 feature_flag_details、config_toml_details、push_startup_warning_counts。
调用图:调用 4 个内部函数(new, config_toml_details, feature_flag_details, push_startup_warning_counts);外部调用 2 个(new, format!)。
push_startup_warning_counts1104–1119 ↗
fn push_startup_warning_counts(details: &mut Vec<String>, warnings: &[String])
作用:把启动警告按数量和常见来源做个统计。这样用户能快速看出是 skill、hook、plugin、MCP 还是弃用配置在报警。
数据流:输入 details 列表和警告列表 → 追加总数,再按关键词统计几类警告数量 → details 被追加多行统计文字。
调用关系:config_check 在发现启动警告时调用它;测试也会验证已知来源的分组统计。
调用图:被 2 处调用(config_check, startup_warning_counts_group_known_sources);外部调用 1 个(format!)。
feature_flag_details1121–1149 ↗
fn feature_flag_details(config: &Config, details: &mut Vec<String>)
作用:把功能开关状态写进报告。功能开关就是控制某些实验或可选能力是否启用的配置。
数据流:输入 Config 和 details → 读取当前 features,统计已启用数量、列出已启用名称、列出和默认值不同的覆盖项,并记录旧式别名用法 → details 被追加相关说明。
调用关系:config_check 调用它,让配置报告不只显示路径,也显示功能行为是否被改过。
调用图:被 1 处调用(config_check);外部调用 1 个(format!)。
config_toml_details1151–1164 ↗
fn config_toml_details(config: &Config, details: &mut Vec<String>)
作用:检查 config.toml 文件是否存在、能否读取、TOML 格式能否解析。TOML 是一种常见配置文件格式。
数据流:输入 Config 和 details → 拼出 config.toml 路径;尝试读取文件并解析 TOML;根据结果追加 ok、missing、read 错误或 parse 错误 → details 被更新。
调用关系:config_check 调用它,把配置文件本身的健康情况放进报告。
调用图:被 1 处调用(config_check);外部调用 2 个(format!, read_to_string)。
auth_check1166–1267 ↗
fn auth_check(config: &Config) -> DoctorCheck
作用:检查 Codex 登录凭据是否可用。它会看环境变量、auth.json、本机密钥环等来源。
数据流:输入 Config → 记录认证存储模式和 auth.json 路径;检查常见认证环境变量;如果当前模型提供商有自己的认证规则,先交给 provider_specific_auth_check;否则读取 auth.json,判断存储模式、API key、ChatGPT token、agent identity 等是否完整 → 输出 Ok、Warning 或 Fail,并在失败时给修复建议。
调用关系:build_report 在配置成功后调用它;它会调用 provider_specific_auth_check、stored_auth_issues、stored_auth_mode 等函数完成细分判断。
调用图:调用 3 个内部函数(new, provider_specific_auth_check, stored_auth_issues);外部调用 4 个(new, auth_keyring_backend_kind, load_auth_dot_json, format!)。
provider_specific_auth_check1269–1322 ↗
fn provider_specific_auth_check(
requires_openai_auth: bool,
provider_env_key: Option<&str>,
provider_env_key_instructions: Option<&str>,
mut details: Vec<String>,
env_var_present:
作用:处理非 OpenAI 模型提供商的认证规则。有些提供商不需要 OpenAI 登录,而是需要自己的环境变量。
数据流:输入是否需要 OpenAI 认证、提供商环境变量名、设置说明、已有细节和查环境变量的函数 → 如果仍需要 OpenAI 认证就返回 None 让 auth_check 继续常规流程;否则检查提供商环境变量是否存在,生成成功或失败的 DoctorCheck → 输出可选检查结果。
调用关系:auth_check 会先调用它;如果它返回 Some,就说明提供商认证已经能独立给出结论,auth_check 直接返回。
调用图:调用 2 个内部函数(new, env_var_present);被 3 处调用(auth_check, provider_specific_auth_allows_non_openai_provider_without_env_key, provider_specific_auth_fails_when_provider_env_key_is_missing);外部调用 1 个(format!)。
stored_auth_mode1324–1333 ↗
fn stored_auth_mode(auth: &codex_login::AuthDotJson) -> &'static str
作用:把存储凭据的认证模式转成报告里好读的固定字符串。
数据流:输入 auth.json 内容 → 调用 stored_auth_mode_value 判断真实模式 → 把枚举值映射成 api_key、chatgpt 等字符串 → 输出静态字符串。
调用关系:auth_check 在成功读取 auth.json 后调用它,显示当前凭据属于哪种登录方式。
调用图:调用 1 个内部函数(stored_auth_mode_value)。
stored_auth_mode_value1335–1348 ↗
fn stored_auth_mode_value(auth: &AuthDotJson) -> codex_app_server_protocol::AuthMode
作用:判断 auth.json 实际使用哪种认证模式。它既看显式 auth_mode,也会根据已有字段推断。
数据流:输入 AuthDotJson → 如果 auth_mode 已写明就直接用;否则按 personal access token、Bedrock key、OpenAI API key、ChatGPT 的顺序推断 → 输出 AuthMode。
调用关系:stored_auth_mode 用它做展示;stored_auth_issues 用它决定应该检查哪些必需字段。
调用图:被 2 处调用(stored_auth_issues, stored_auth_mode)。
stored_auth_issues1350–1424 ↗
fn stored_auth_issues(
auth: &AuthDotJson,
env_var_present: impl Fn(&str) -> bool,
) -> Vec<&'static str>
作用:找出 auth.json 里缺了哪些关键凭据字段。不同认证方式需要的字段不同。
数据流:输入 auth.json 和查环境变量的函数 → 先判断认证模式;再分别检查 API key、ChatGPT access/refresh token、账号 id、agent identity、个人访问令牌、Bedrock API key 等是否缺失或为空 → 输出问题说明列表。
调用关系:auth_check 用它决定认证检查是失败、警告还是通过,并把问题加入报告细节。
调用图:调用 2 个内部函数(env_var_present, stored_auth_mode_value);被 1 处调用(auth_check);外部调用 1 个(new)。
network_check1426–1460 ↗
fn network_check() -> DoctorCheck
作用:检查网络相关环境是否看起来可读,尤其是代理变量和自定义 CA 证书文件。CA 证书文件用于让程序信任某些 HTTPS 证书。
数据流:读取代理环境变量、CODEX_CA_CERTIFICATE 和 SSL_CERT_FILE → 对证书路径检查是否存在、是否文件、能否读取一个字节 → 输出网络环境检查,证书路径异常时给 Warning。
调用关系:build_report 通过 run_sync_check 调用它;它内部调用 push_proxy_env_details 和 read_probe_file。
调用图:调用 3 个内部函数(new, push_proxy_env_details, read_probe_file);外部调用 5 个(from, new, var_os, format!, metadata)。
push_proxy_env_details1462–1476 ↗
fn push_proxy_env_details(details: &mut Vec<String>)
作用:把当前设置了哪些代理环境变量写入报告。代理变量会影响网络请求走哪条路。
数据流:输入 details → 扫描预设的代理变量名;没有就追加 none,有就追加变量名列表 → details 被更新。
调用关系:network_check 和 websocket_reachability_check 都会用它说明网络环境背景。
调用图:被 2 处调用(network_check, websocket_reachability_check);外部调用 1 个(format!)。
read_probe_file1478–1483 ↗
fn read_probe_file(path: &Path) -> std::io::Result<()>
作用:轻量确认一个文件真的能读。它只打开文件并读 1 个字节,不关心内容。
数据流:输入文件路径 → 打开文件,读入一个字节缓冲区 → 成功返回 Ok,打不开或读不了就返回 IO 错误。
调用关系:network_check 用它测试 CA 文件;terminal_path_readiness 用它测试 terminfo 文件;测试也会验证不可读文件会被拒绝。
调用图:被 3 处调用(network_check, terminal_path_readiness, read_probe_file_rejects_unreadable_file);外部调用 1 个(open)。
mcp_check1485–1487 ↗
async fn mcp_check(config: &Config) -> DoctorCheck
作用:从 Config 里取出 MCP 服务器配置并执行 MCP 检查。MCP 是让 Codex 连接外部工具服务的一种协议。
数据流:输入 Config → 取出 mcp_servers → 交给 mcp_check_from_servers 异步检查 → 输出 DoctorCheck。
调用关系:build_report 通过 run_async_check 调用它;真正逐个服务器分析的工作在 mcp_check_from_servers。
调用图:调用 1 个内部函数(mcp_check_from_servers)。
mcp_check_from_servers1489–1629 ↗
async fn mcp_check_from_servers(servers: &HashMap<String, McpServerConfig>) -> DoctorCheck
作用:检查 MCP 服务器配置是否能用。它会看本地命令是否找得到、需要的环境变量是否设置、HTTP 服务器是否能连上。
数据流:输入服务器配置表 → 如果为空直接返回通过;否则逐个服务器统计传输方式、跳过禁用项;对 stdio 服务器检查 cwd、命令、环境变量;对 HTTP 服务器检查 token/header 环境变量并探测 URL;最后根据必需服务器和可选服务器的问题严重度决定 Ok、Warning 或 Fail → 输出 MCP 检查结果和修复建议。
调用关系:mcp_check 调用它;多项 MCP 单元测试也直接调用它来验证缺命令、缺环境变量、禁用服务器和 HTTP 可达性等情况。
调用图:调用 4 个内部函数(new, env_var_present, mcp_http_probe_url, stdio_command_resolves);被 5 处调用(mcp_check, mcp_check_fails_required_missing_stdio_command, mcp_check_fails_required_remote_stdio_env_var, mcp_check_ignores_disabled_servers, mcp_check_warns_for_optional_http_reachability);外部调用 3 个(new, new, format!)。
sandbox_check1631–1664 ↗
fn sandbox_check(config: &Config, arg0_paths: &Arg0DispatchPaths) -> DoctorCheck
作用:检查沙箱和审批相关配置是否能读,并确认沙箱辅助程序路径是否存在。沙箱就是限制命令访问文件或网络的保护层。
数据流:输入 Config 和程序路径 → 记录审批策略、文件系统沙箱、网络沙箱、Linux sandbox helper、execve wrapper 路径;如果 Linux helper 路径存在配置但文件不存在,就给 Warning → 输出沙箱检查结果。
调用关系:build_report 在配置成功后通过 run_sync_check 调用它。
调用图:调用 2 个内部函数(new, push_path_detail);外部调用 2 个(new, format!)。
TerminalCheckInputs::detect1682–1706 ↗
fn detect(no_color_flag: bool) -> Self
作用:现场采集终端检查需要的所有原始信息。比如环境变量、终端大小、是否支持颜色、是不是 tmux。
数据流:输入 no_color 开关 → 收集终端相关环境变量快照,读取终端尺寸和终端识别信息,检查 stdin/stdout/stderr 是否真连着终端,检测颜色支持,必要时收集 tmux 和 Windows 控制台细节 → 输出 TerminalCheckInputs。
调用关系:terminal_check 调用它获取现场数据,然后交给 terminal_check_from_inputs 分析。
调用图:调用 4 个内部函数(collect_env_snapshot, terminal_env_names, tmux_diagnostic_details, windows_console_details);被 1 处调用(terminal_check);外部调用 8 个(new, terminal_info, size, matches!, stderr, stdin, stdout, on)。
TerminalCheckInputs::env_value1708–1710 ↗
fn env_value(&self, name: &str) -> Option<&str>
作用:读取采集到的某个环境变量值。只返回非空值。
数据流:输入变量名 → 在 env 映射里查找 → 找到就返回字符串引用,找不到返回 None。
调用关系:color_output_summary、push_terminal_env_values、push_terminfo_details、terminal_size_issues 等分析函数用它读取快照,而不是反复直接访问系统环境。
调用图:被 4 处调用(color_output_summary, push_terminal_env_values, push_terminfo_details, terminal_size_issues)。
TerminalCheckInputs::env_present1712–1714 ↗
fn env_present(&self, name: &str) -> bool
作用:判断某个环境变量是否出现过,即使它的值为空也算出现。
数据流:输入变量名 → 在 present_env 集合里检查是否存在 → 输出 true 或 false。
调用关系:终端报告里需要区分“没设置”和“设置了但为空”,所以 color_output_summary、push_presence_env_values 等函数会用它。
调用图:被 4 处调用(color_output_summary, push_presence_env_values, push_terminal_env_values, push_terminfo_details);外部调用 1 个(contains)。
terminal_check1717–1719 ↗
fn terminal_check(no_color_flag: bool) -> DoctorCheck
作用:执行终端检查的入口。它先采集真实终端信息,再分析是否有问题。
数据流:输入 no_color 标志 → 调用 TerminalCheckInputs::detect 收集现场数据 → 调用 terminal_check_from_inputs 生成检查结果 → 输出 DoctorCheck。
调用关系:build_report 通过 run_sync_check 调用它;分析逻辑被拆到 terminal_check_from_inputs,方便测试用假输入覆盖各种终端情况。
调用图:调用 2 个内部函数(detect, terminal_check_from_inputs)。
windows_console_details1762–1764 ↗
terminal_check_from_inputs1766–1856 ↗
fn terminal_check_from_inputs(inputs: TerminalCheckInputs) -> DoctorCheck
作用:把采集到的终端信息分析成体检结果。它会指出颜色、编码、TERM、TERMINFO、窗口大小等常见终端问题。
数据流:输入 TerminalCheckInputs → 先整理终端名称、版本、TERM、复用器、终端尺寸、颜色环境、terminfo、locale、远程终端标记等细节;再根据 TERM=dumb、非 UTF-8 locale、TERMINFO 不可读、终端尺寸问题生成 DoctorIssue;最后按最严重问题决定检查状态和摘要 → 输出 DoctorCheck。
调用关系:terminal_check 把真实采集结果交给它;很多测试也直接给它构造输入,验证窄终端、dumb 终端、非 UTF-8、本地或远程标记等行为。
调用图:调用 7 个内部函数(new, new, effective_locale, push_presence_env_values, push_terminal_env_values, push_terminfo_details, terminal_size_issues);被 9 处调用(terminal_check, terminal_check_includes_windows_console_details, terminal_check_keeps_tmux_probe_failures_non_fatal, terminal_check_reports_remote_indicators_as_present_only, terminal_check_warns_for_declared_narrow_terminal, terminal_check_warns_for_dumb_terminal, terminal_check_warns_for_narrow_terminal, terminal_check_warns_for_non_utf8_locale, terminal_check_warns_for_unreadable_terminfo_path);外部调用 4 个(new, format!, matches!, vec!)。
terminal_name1858–1875 ↗
fn terminal_name(info: &TerminalInfo) -> &'static str
作用:把终端识别枚举翻译成用户熟悉的名字。比如 iTerm2、VS Code、Windows Terminal。
数据流:输入 TerminalInfo → 按其中的 name 枚举匹配 → 输出固定的显示字符串。
调用关系:terminal_check_from_inputs 用它写报告第一行的终端名称。
multiplexer_name1877–1888 ↗
fn multiplexer_name(multiplexer: &Multiplexer) -> String
作用:把终端复用器名称和版本格式化成人能读的文字。终端复用器就是 tmux、zellij 这类在一个终端里管理多个会话的工具。
数据流:输入 Multiplexer → 如果有版本就拼上版本号,没有就只返回名称 → 输出字符串。
调用关系:terminal_check_from_inputs 在检测到 tmux 或 zellij 时调用它。
调用图:外部调用 1 个(format!)。
terminal_env_names1890–1898 ↗
collect_env_snapshot1900–1915 ↗
fn collect_env_snapshot(
names: &BTreeSet<&'static str>,
) -> (BTreeMap<String, String>, BTreeSet<String>)
作用:一次性采集一组环境变量的快照。快照能保证后续分析看到的是同一批数据。
数据流:输入变量名集合 → 对每个名字读取系统环境;记录出现过的变量,并把非空值修剪后放入映射 → 输出值映射和出现集合。
调用关系:TerminalCheckInputs::detect 用它采集终端环境,后面的 env_value 和 env_present 都基于这份快照。
push_terminal_env_values1917–1929 ↗
fn push_terminal_env_values(
details: &mut Vec<String>,
inputs: &TerminalCheckInputs,
names: &[&str],
)
作用:把一组终端环境变量和值写进报告细节。空值不会显示内容,只显示 present。
数据流:输入 details、终端输入快照和变量名列表 → 每个变量先查值,有值就写“NAME: value”;没值但出现过就写“NAME: present” → details 被追加。
调用关系:terminal_check_from_inputs 用它显示尺寸变量和颜色相关变量。
调用图:调用 2 个内部函数(env_present, env_value);被 1 处调用(terminal_check_from_inputs);外部调用 1 个(format!)。
push_presence_env_values1931–1941 ↗
fn push_presence_env_values(
details: &mut Vec<String>,
inputs: &TerminalCheckInputs,
names: &[&str],
)
作用:只记录某些环境变量是否存在,不记录具体值。适合可能暴露远程环境细节的变量。
数据流:输入 details、终端输入快照和变量名列表 → 对出现过的变量追加“NAME: present” → details 被更新。
调用关系:terminal_check_from_inputs 用它显示远程终端指示变量,避免把具体值写进报告。
调用图:调用 1 个内部函数(env_present);被 1 处调用(terminal_check_from_inputs);外部调用 1 个(format!)。
color_output_summary1943–1968 ↗
fn color_output_summary(inputs: &TerminalCheckInputs) -> String
作用:判断 doctor 输出颜色是否会启用,并说明禁用原因。颜色可能被 --no-color、NO_COLOR、TERM=dumb 或非终端输出关闭。
数据流:输入终端检查数据 → 调用 should_enable_color 判断是否启用;若不开启,就按优先级找出原因 → 输出 enabled 或 disabled (...) 字符串。
调用关系:terminal_check_from_inputs 调用它,把颜色输出状态加入终端检查细节。
调用图:调用 3 个内部函数(env_present, env_value, should_enable_color);外部调用 1 个(format!)。
push_terminfo_details1970–1991 ↗
fn push_terminfo_details(details: &mut Vec<String>, inputs: &TerminalCheckInputs) -> bool
作用:检查 TERMINFO 和 TERMINFO_DIRS 指向的终端能力数据库是否可读。terminfo 用来告诉程序终端支持哪些控制能力。
数据流:输入 details 和终端快照 → 如果有 TERMINFO,就检查该路径;如果有 TERMINFO_DIRS,就逐个拆分路径检查;把每个路径状态写入 details,并累计是否有警告 → 输出是否发现不可读或缺失问题。
调用关系:terminal_check_from_inputs 调用它;如果返回 true,会生成 TERMINFO 相关的 DoctorIssue。
调用图:调用 3 个内部函数(env_present, env_value, terminal_path_readiness);被 1 处调用(terminal_check_from_inputs);外部调用 3 个(from, split_paths, format!)。
terminal_path_readiness1993–2007 ↗
fn terminal_path_readiness(path: &Path) -> (String, bool)
作用:判断一个 terminfo 路径是否可用。目录要能列出,文件要能读。
数据流:输入路径 → 查看文件元数据;目录就尝试 read_dir,文件就用 read_probe_file 试读;其他类型、缺失或出错都算警告 → 输出状态文字和是否有警告。
调用关系:push_terminfo_details 用它检查 TERMINFO 和 TERMINFO_DIRS 的每个路径。
调用图:调用 1 个内部函数(read_probe_file);被 1 处调用(push_terminfo_details);外部调用 3 个(format!, metadata, read_dir)。
effective_locale2009–2013 ↗
fn effective_locale(inputs: &TerminalCheckInputs) -> Option<String>
作用:找出当前实际生效的语言区域设置。语言区域会影响 Unicode 字符显示。
数据流:输入终端检查数据 → 按 LOCALE_ENV_VARS 的顺序找第一个有值的环境变量 → 输出该 locale 字符串或 None。
调用关系:terminal_check_from_inputs 调用它,并配合 is_non_utf8_locale 判断是否需要警告。
调用图:被 1 处调用(terminal_check_from_inputs)。
is_non_utf8_locale2015–2018 ↗
fn is_non_utf8_locale(locale: &str) -> bool
作用:判断一个 locale 字符串是不是非 UTF-8。非 UTF-8 可能导致图标、中文或特殊符号显示错乱。
数据流:输入 locale 字符串 → 转成小写 → 如果不包含 utf-8 或 utf8 就认为是非 UTF-8 → 输出布尔值。
调用关系:terminal_check_from_inputs 用它判断是否生成 locale 警告问题。
terminal_size_issues2020–2085 ↗
fn terminal_size_issues(inputs: &TerminalCheckInputs) -> Vec<DoctorIssue>
作用:检查终端窗口是不是太窄或太矮。窗口太小不会让程序崩溃,但输出可能换行乱掉、内容滚出屏幕,所以这里给出提醒。
数据流:输入是一组终端检查信息,包括真实窗口大小和环境变量 COLUMNS、LINES。函数把宽高和推荐值比较,发现太小就生成 DoctorIssue,最后返回这些问题列表,不直接改系统。
调用关系:它由 terminal_check_from_inputs 调用,是终端体检的一小步;发现问题时用 DoctorIssue::new 组装成报告项。
调用图:调用 2 个内部函数(new, env_value);被 1 处调用(terminal_check_from_inputs);外部调用 2 个(new, format!)。
tmux_diagnostic_details2087–2096 ↗
fn tmux_diagnostic_details() -> Vec<String>
作用:收集 tmux 环境里的关键诊断信息。这样用户在 tmux 里遇到颜色、终端类型或显示异常时,报告里能看到相关线索。
数据流:它不接收参数,主动调用 tmux 命令读取 client termtype、termname 和若干 tmux 选项。读到的内容被整理成字符串列表返回,读不到的选项会写成 unavailable。
调用关系:它由 detect 调用;内部把具体读取工作交给 push_tmux_display_detail 和 tmux_option_value。
调用图:调用 2 个内部函数(push_tmux_display_detail, tmux_option_value);被 1 处调用(detect);外部调用 2 个(new, format!)。
push_tmux_display_detail2098–2102 ↗
fn push_tmux_display_detail(details: &mut Vec<String>, label: &str, format: &str)
作用:把一个 tmux display-message 查询结果追加到详情列表里。查不到就安静跳过,避免因为 tmux 不配合导致整个体检失败。
数据流:输入是详情列表、显示标签和 tmux 格式字符串。函数运行查询,若有非空结果,就把“标签: 值”加入列表;没有结果则不改动。
调用关系:它服务于 tmux_diagnostic_details,具体查询交给 tmux_display_message。
调用图:调用 1 个内部函数(tmux_display_message);被 1 处调用(tmux_diagnostic_details);外部调用 1 个(format!)。
tmux_option_value2104–2113 ↗
fn tmux_option_value(option: &str) -> Option<String>
作用:读取一个 tmux 全局选项的值。它把外部命令的输出转成程序里可用的文字。
数据流:输入是 tmux 选项名。函数执行 tmux show-options -gqv,命令成功且输出不是空白时返回文本,否则返回 None。
调用关系:tmux_diagnostic_details 用它批量读取 TMUX_OPTION_NAMES;它用 non_empty_trimmed 清理输出。
调用图:调用 1 个内部函数(non_empty_trimmed);被 1 处调用(tmux_diagnostic_details);外部调用 2 个(from_utf8, new)。
tmux_display_message2115–2124 ↗
fn tmux_display_message(format: &str) -> Option<String>
作用:执行 tmux 的 display-message 查询,用来读取 tmux 当前客户端的某些动态信息。
数据流:输入是 tmux 的格式表达式。函数运行 tmux display-message -p,成功后把标准输出转成字符串并去掉空白;失败或空输出返回 None。
调用关系:push_tmux_display_detail 通过它拿到 tmux 的显示信息;它也依赖 non_empty_trimmed 做最后清理。
调用图:调用 1 个内部函数(non_empty_trimmed);被 1 处调用(push_tmux_display_detail);外部调用 2 个(from_utf8, new)。
non_empty_trimmed2126–2129 ↗
fn non_empty_trimmed(value: String) -> Option<String>
作用:把一段文字前后的空白去掉,并过滤掉空字符串。它是处理命令输出的小工具。
数据流:输入是一段字符串。函数 trim 后,如果还剩内容就返回 Some,否则返回 None;不产生其他副作用。
调用关系:tmux_option_value 和 tmux_display_message 都用它避免把空输出当成有效信息。
调用图:被 2 处调用(tmux_display_message, tmux_option_value)。
state_check2131–2161 ↗
async fn state_check(config: &Config) -> DoctorCheck
作用:检查 Codex 的本地状态文件和数据库是否能访问、是否损坏。没有这一步,很多“历史记录丢了”“启动异常”的问题会很难定位。
数据流:输入是 Config。函数查看 CODEX_HOME、日志目录、SQLite 目录和运行期数据库路径,检查文件可见性和 SQLite 完整性,再统计会话 rollout 文件和独立安装缓存;最后返回一个 DoctorCheck,数据库损坏时标记失败并给出修复建议。
调用关系:这是 doctor 状态检查的主流程;它把路径检查交给 path_readiness,把数据库检查交给 sqlite_integrity_detail,把会话统计交给 rollout_stats_details。
调用图:调用 5 个内部函数(new, path_readiness, rollout_stats_details, sqlite_integrity_detail, standalone_release_cache_details);外部调用 2 个(new, runtime_db_paths)。
sqlite_integrity_detail2163–2189 ↗
async fn sqlite_integrity_detail(
details: &mut Vec<String>,
integrity_failures: &mut Vec<String>,
label: &str,
path: &Path,
)
作用:对一个 SQLite 数据库做完整性检查。简单说,就是问数据库自己“你里面的数据结构还正常吗”。
数据流:输入是详情列表、失败列表、数据库标签和路径。文件不存在就记录跳过;存在则异步运行完整性检查,结果全是 ok 就记录正常,否则把错误同时写入详情和失败列表。
调用关系:state_check 在遍历每个运行期数据库时调用它;真正的数据库检查由 codex_state::sqlite_integrity_check 完成。
调用图:被 1 处调用(state_check);外部调用 3 个(is_file, sqlite_integrity_check, format!)。
rollout_stats_details2191–2196 ↗
fn rollout_stats_details(details: &mut Vec<String>, codex_home: &Path)
作用:统计当前和归档的 rollout 会话文件数量与大小。rollout 文件可以理解为会话过程记录,太多或异常大可能影响排查和性能。
数据流:输入是详情列表和 Codex 主目录。函数分别扫描 sessions 与 archived_sessions,得到统计结果后追加成可读详情。
调用关系:state_check 调用它补充状态目录的体检信息;扫描由 collect_rollout_stats 完成,展示由 push_rollout_stats_detail 完成。
调用图:调用 2 个内部函数(collect_rollout_stats, push_rollout_stats_detail);被 1 处调用(state_check);外部调用 1 个(join)。
push_rollout_stats_detail2198–2208 ↗
fn push_rollout_stats_detail(details: &mut Vec<String>, label: &str, stats: RolloutStats)
作用:把 rollout 文件统计结果写成一句报告详情。它负责把数字变成人能看懂的话。
数据流:输入是详情列表、标签和 RolloutStats。如果统计时出错,就写入失败原因;否则写入文件数、总字节数和平均大小。
调用关系:rollout_stats_details 用它输出 active 和 archived 两类统计;平均值由 RolloutStats::average_bytes 计算。
调用图:被 1 处调用(rollout_stats_details);外部调用 1 个(format!)。
RolloutStats::average_bytes2218–2220 ↗
fn average_bytes(&self) -> u64
作用:计算 rollout 文件的平均大小。没有文件时返回 0,避免除以 0 出错。
数据流:输入是 RolloutStats 自身的文件数和总字节数。函数做安全除法,得到平均字节数,不修改对象。
调用关系:push_rollout_stats_detail 用它展示统计摘要。
collect_rollout_stats2223–2227 ↗
fn collect_rollout_stats(root: &Path) -> RolloutStats
作用:从某个目录开始统计 rollout 文件。它是对外使用的入口,隐藏递归扫描的细节。
数据流:输入是根目录路径。函数创建空统计对象,调用 collect_rollout_stats_inner 递归填充,最后返回统计结果。
调用关系:rollout_stats_details 和相关测试会调用它;实际走目录树的工作交给 collect_rollout_stats_inner。
调用图:调用 1 个内部函数(collect_rollout_stats_inner);被 2 处调用(rollout_stats_details, collect_rollout_stats_counts_nested_rollout_files);外部调用 1 个(default)。
collect_rollout_stats_inner2229–2265 ↗
fn collect_rollout_stats_inner(path: &Path, stats: &mut RolloutStats)
作用:递归扫描目录,寻找真正的 rollout 文件并累计数量和大小。像翻文件柜一样,一层层文件夹都看过去。
数据流:输入是当前路径和可修改的统计对象。函数读取目录,遇到子目录就继续深入,遇到符合规则的文件就累加;一旦遇到读取错误就记录错误并停止后续扫描。
调用关系:它只由 collect_rollout_stats 调用;判断文件名是否算 rollout 交给 is_rollout_file。
调用图:调用 1 个内部函数(is_rollout_file);被 1 处调用(collect_rollout_stats);外部调用 1 个(read_dir)。
is_rollout_file2267–2273 ↗
fn is_rollout_file(path: &Path) -> bool
作用:判断一个文件是不是 Codex 的 rollout 记录文件。规则是扩展名为 jsonl,并且文件名以 rollout- 开头。
数据流:输入是文件路径。函数检查扩展名和文件名,符合条件返回 true,否则返回 false。
调用关系:collect_rollout_stats_inner 用它过滤目录里的普通文件,避免把无关 jsonl 文件算进去。
调用图:被 1 处调用(collect_rollout_stats_inner);外部调用 3 个(new, extension, file_name)。
websocket_reachability_check2275–2409 ↗
async fn websocket_reachability_check(
config: &Config,
auth_manager: Option<Arc<AuthManager>>,
) -> DoctorCheck
作用:检查当前模型提供方的 WebSocket 通道能不能建立握手。WebSocket 如果被代理、防火墙或服务端策略拦住,实时响应功能可能受影响。
数据流:输入是 Config 和可选 AuthManager。函数先记录提供方信息和代理环境;如果提供方不支持 WebSocket 就直接通过。否则它创建运行期 provider、解析认证、构造 endpoint、查 DNS、带必要 header 发起握手探测;成功返回 Ok,超时、认证解析失败、连接失败或握手后立即关闭则返回 Warning 并附修复建议。
调用关系:它是网络体检中的 WebSocket 主检查;错误报告交给 websocket_probe_warning 和 websocket_error_detail,DNS 细节交给 dns_address_family_details。
调用图:调用 7 个内部函数(new, dns_address_family_details, push_proxy_env_details, websocket_error_detail, websocket_probe_warning, new, default_headers);外部调用 6 个(new, from_static, create_model_provider, format!, timeout, vec!)。
websocket_probe_warning2411–2425 ↗
fn websocket_probe_warning(
summary: &'static str,
mut details: Vec<String>,
error_detail: String,
) -> DoctorCheck
作用:把 WebSocket 探测中的非致命失败包装成 Warning。意思是 WebSocket 有问题,但普通 HTTPS 回退可能还能用。
数据流:输入是摘要、已有详情和错误说明。函数把错误说明追加到详情,然后生成带统一修复建议的 DoctorCheck。
调用关系:websocket_reachability_check 在 provider 设置、endpoint、认证、握手或超时出问题时调用它。
调用图:调用 1 个内部函数(new);被 1 处调用(websocket_reachability_check)。
websocket_error_detail2427–2443 ↗
fn websocket_error_detail(err: &ApiError) -> String
作用:把 ApiError 转成适合放进体检报告的一句话。这样用户不用看 Rust 错误类型,也能知道大概是哪类失败。
数据流:输入是 ApiError。函数按传输错误、API 错误、流错误和其他业务错误分类,输出一段字符串。
调用关系:websocket_reachability_check 在握手返回错误时调用它,然后交给 websocket_probe_warning 写入报告。
调用图:被 1 处调用(websocket_reachability_check);外部调用 1 个(format!)。
auth_mode_name2445–2454 ↗
fn auth_mode_name(auth: &CodexAuth) -> &'static str
作用:把认证模式转换成简短英文名字,方便写进诊断详情。认证模式就是“用 API key、ChatGPT token 还是其他身份来登录”。
数据流:输入是 CodexAuth。函数读取 auth_mode,并映射成固定字符串;不修改认证对象。
调用关系:websocket_reachability_check 用它记录当前 WebSocket 探测采用的认证方式。
调用图:调用 1 个内部函数(auth_mode)。
dns_address_family_details2456–2481 ↗
async fn dns_address_family_details(host: &str, port: u16) -> Vec<String>
作用:查看一个主机名解析出了多少 IPv4 和 IPv6 地址。IPv4/IPv6 是两代互联网地址格式,某些网络只对其中一种稳定。
数据流:输入是主机名和端口。函数异步做 DNS 查询,成功后统计 IPv4、IPv6 数量和第一个地址类型;失败则返回查询失败原因。
调用关系:websocket_reachability_check 在拿到 WebSocket URL 后调用它,帮助判断连接问题是否和 DNS 地址族有关。
调用图:被 1 处调用(websocket_reachability_check);外部调用 2 个(lookup_host, vec!)。
fallback_state_check2483–2501 ↗
fn fallback_state_check() -> DoctorCheck
作用:在没有完整配置时,尽量检查 CODEX_HOME 能不能解析出来。它是状态检查的备用方案。
数据流:函数不接收参数,调用 find_codex_home。成功就返回 Ok 并记录路径;失败就返回 Warning 和错误文字。
调用关系:当正常 Config 不可用时,这个函数可以让 doctor 仍然提供一点状态目录线索。
调用图:调用 2 个内部函数(new, find_codex_home);外部调用 1 个(format!)。
ProviderAuthReachabilityMode::description2525–2531 ↗
fn description(self) -> &'static str
作用:给 provider 连通性检查的认证模式起一个人能读懂的描述。
数据流:输入是枚举自身。函数把 NotRequired、ApiKey、Chatgpt 分别变成固定说明字符串。
调用关系:provider_reachability_plan_from_parts 用它给 ReachabilityPlan 填写 description。
调用图:被 1 处调用(provider_reachability_plan_from_parts)。
provider_reachability_plan2534–2556 ↗
fn provider_reachability_plan(config: &Config) -> ReachabilityPlan
作用:根据当前配置和已保存的登录信息,决定应该探测哪些网络地址。它避免 doctor 去测错地方,比如有 API key 时就不必测 ChatGPT 后端。
数据流:输入是 Config。函数读取 auth.json、检查环境变量和 provider 要求,算出认证模式,再把 provider id、名称、base_url、查询参数等交给 provider_reachability_plan_from_parts,输出 ReachabilityPlan。
调用关系:build_report 调用它生成网络检查计划;认证模式判断交给 provider_auth_reachability_mode_from_auth。
调用图:调用 2 个内部函数(provider_auth_reachability_mode_from_auth, provider_reachability_plan_from_parts);被 1 处调用(build_report);外部调用 2 个(auth_keyring_backend_kind, load_auth_dot_json)。
default_reachability_plan2558–2568 ↗
fn default_reachability_plan() -> ReachabilityPlan
作用:提供一个默认的网络探测计划,假定使用 ChatGPT 认证和默认 ChatGPT 后端地址。
数据流:函数不接收参数,直接用固定的 openai/ChatGPT 信息构造 ReachabilityPlan。
调用关系:build_report 在缺少完整配置时可用它兜底;具体构造仍交给 provider_reachability_plan_from_parts。
调用图:调用 1 个内部函数(provider_reachability_plan_from_parts);被 1 处调用(build_report)。
provider_auth_reachability_mode_from_auth2570–2597 ↗
fn provider_auth_reachability_mode_from_auth(
requires_openai_auth: bool,
env_var_present: impl Fn(&str) -> bool,
stored_auth: Option<&AuthDotJson>,
) -> ProviderAuthReachabilityMode
作用:判断连通性检查该按哪种认证路线走:不需要 OpenAI 认证、API key,还是 ChatGPT 登录。
数据流:输入包括 provider 是否需要 OpenAI 认证、一个检查环境变量是否存在的函数、以及可选 auth.json 内容。函数先看 provider 要求,再看环境变量,最后看保存的认证模式,输出 ProviderAuthReachabilityMode。
调用关系:provider_reachability_plan 用它决定后续要探测 API endpoint 还是 ChatGPT endpoint;测试覆盖了环境变量和 auth.json 的优先级。
调用图:调用 1 个内部函数(env_var_present);被 1 处调用(provider_reachability_plan)。
provider_reachability_plan_from_parts2599–2646 ↗
fn provider_reachability_plan_from_parts(
mode: ProviderAuthReachabilityMode,
provider_id: &str,
provider_name: &str,
provider_base_url: Option<&str>,
provider_query_params: Option
作用:把零散的 provider 信息拼成具体的网络探测计划。它决定要测哪个 base URL,是否还要额外测 /models 路由。
数据流:输入是认证模式、provider 标识、名称、base_url、查询参数、是否 Amazon Bedrock、ChatGPT base URL。函数按模式生成一个或多个 ReachabilityEndpoint,并在适合时拼出 route_probe_url,最后返回 ReachabilityPlan。
调用关系:provider_reachability_plan 和 default_reachability_plan 都靠它;多个测试直接调用它验证不同 provider 的路线选择。
调用图:调用 1 个内部函数(description);被 6 处调用(default_reachability_plan, provider_reachability_plan, provider_reachability_api_key_does_not_require_chatgpt, provider_reachability_route_401_keeps_reachability_ok, provider_reachability_route_404_fails_bad_base_url_path, provider_reachability_skips_route_probe_for_bedrock);外部调用 1 个(vec!)。
should_probe_models_route2648–2650 ↗
fn should_probe_models_route(provider_name: &str, base_url: &str, is_amazon_bedrock: bool) -> bool
作用:判断是否应该额外探测 provider 的 /models 路由。这个路由能帮助发现 base_url 配错了路径。
数据流:输入是 provider 名称、base URL 和是否 Amazon Bedrock。函数排除 Bedrock 和 Azure Responses 这类不适合测该路由的 provider,其余返回 true。
调用关系:provider_reachability_plan_from_parts 用它决定是否生成 route_probe_url。
调用图:外部调用 1 个(is_azure_responses_provider)。
provider_url_for_path2652–2680 ↗
fn provider_url_for_path(
base_url: &str,
path: &str,
query_params: Option<&HashMap<String, String>>,
) -> String
作用:把 provider 的 base URL、路径和查询参数拼成完整 URL。它负责处理多余或缺失的斜杠。
数据流:输入是 base_url、path 和可选查询参数。函数清理首尾斜杠,拼接路径;如果有查询参数,就用 ? 或 & 追加,最后返回字符串 URL。
调用关系:provider_reachability_plan_from_parts 用它构造 /models 探测地址。
调用图:外部调用 1 个(format!)。
provider_reachability_check2682–2797 ↗
async fn provider_reachability_check(plan: ReachabilityPlan) -> DoctorCheck
作用:按计划实际探测模型提供方的 HTTP 地址是否可达。HTTP 是普通网页/API 请求协议,这里用它判断网络、代理和 base_url 配置是否正常。
数据流:输入是 ReachabilityPlan。函数先 HEAD 探测每个 base URL;成功后如果有 route_probe_url,再 GET 探测具体路由。它统计必需失败、可选失败和警告,生成 DoctorCheck;404 路由会额外生成 DoctorIssue,提示 base_url 路径可能配错。
调用关系:它执行 provider_reachability_plan 生成的计划;基础探测用 http_probe_url,路由探测用 provider_route_probe_url,最终状态由 provider_reachability_outcome 决定。
调用图:调用 5 个内部函数(new, new, http_probe_url, provider_reachability_outcome, provider_route_probe_url);被 2 处调用(provider_reachability_route_401_keeps_reachability_ok, provider_reachability_route_404_fails_bad_base_url_path);外部调用 3 个(new, format!, vec!)。
provider_route_probe_url2806–2815 ↗
async fn provider_route_probe_url(url: &str) -> RouteProbeOutcome
作用:探测 provider 的具体路由是否存在,尤其是 /models。401 或 403 也算路由存在,因为那说明服务器认识这个地址,只是没授权。
数据流:输入是 URL。函数用 GET 在 3 秒内请求;2xx、401、403 返回 Ok,404 返回 Fail,其他 HTTP 状态返回 Warning,连接类错误返回 TransportError。
调用关系:provider_reachability_check 在 base URL 可达后调用它,进一步判断 API 前缀是否写对。
调用图:调用 1 个内部函数(http_get_probe_status_with_timeout);被 1 处调用(provider_reachability_check);外部调用 7 个(from_secs, Fail, Ok, TransportError, Warning, format!, matches!)。
provider_reachability_outcome2817–2835 ↗
fn provider_reachability_outcome(
required_failures: usize,
warnings: usize,
) -> (CheckStatus, &'static str)
作用:根据失败和警告数量,给 provider 网络检查定最终等级。
数据流:输入是必需失败数和警告数。没有问题返回 Ok,有警告无必需失败返回 Warning,有任何必需失败返回 Fail,并附对应摘要。
调用关系:provider_reachability_check 完成所有 endpoint 探测后调用它汇总结论;测试直接验证它的优先级。
调用图:被 1 处调用(provider_reachability_check)。
http_probe_url2837–2839 ↗
async fn http_probe_url(url: &str) -> Result<String, String>
作用:用默认 3 秒超时探测一个 HTTP 地址是否能响应。它主要用于快速判断“地址是不是连得上”。
数据流:输入是 URL。函数调用 http_probe_url_with_timeout,成功返回类似 HTTP 200 的字符串,失败返回简短错误。
调用关系:provider_reachability_check 和相关测试使用它;实际请求逻辑在 http_probe_url_with_timeout。
调用图:调用 1 个内部函数(http_probe_url_with_timeout);被 2 处调用(provider_reachability_check, http_probe_treats_http_status_as_reachable);外部调用 1 个(from_secs)。
mcp_http_probe_url2841–2843 ↗
async fn mcp_http_probe_url(url: &str) -> Result<String, String>
作用:用默认 3 秒超时探测 MCP HTTP 服务。MCP 是模型上下文协议,用来让 Codex 连接外部工具或服务。
数据流:输入是 URL。函数调用 mcp_http_probe_url_with_timeout,返回 HTTP 状态字符串或错误原因。
调用关系:mcp_check_from_servers 用它检查远程 MCP 服务可达性。
调用图:调用 1 个内部函数(mcp_http_probe_url_with_timeout);被 1 处调用(mcp_check_from_servers);外部调用 1 个(from_secs)。
mcp_http_probe_url_with_timeout2845–2853 ↗
async fn mcp_http_probe_url_with_timeout(url: &str, timeout: Duration) -> Result<String, String>
作用:探测 MCP HTTP 地址,并在 HEAD 请求失败时自动改用 GET。这样能兼容不支持 HEAD 的服务器。
数据流:输入是 URL 和超时时长。函数先调用 http_probe_url_with_timeout;失败后再调用 http_get_probe_url_with_timeout;两者都失败才把两个错误合并返回。
调用关系:mcp_http_probe_url 调用它;测试验证 HEAD 超时或失败时会走 GET 兜底。
调用图:调用 2 个内部函数(http_get_probe_url_with_timeout, http_probe_url_with_timeout);被 2 处调用(mcp_http_probe_url, mcp_http_probe_falls_back_to_get_when_head_times_out);外部调用 1 个(format!)。
http_probe_url_with_timeout2855–2873 ↗
async fn http_probe_url_with_timeout(url: &str, timeout: Duration) -> Result<String, String>
作用:用 HEAD 请求探测 URL。HEAD 像“只问有没有,不下载正文”,适合快速检查服务是否活着。
数据流:输入是 URL 和超时。函数创建 HTTP 客户端,发送 HEAD 请求;成功返回 HTTP 状态码字符串,失败时把超时、连接失败、请求构造失败等情况转成简单文字。
调用关系:http_probe_url 和 mcp_http_probe_url_with_timeout 使用它作为基础探测。
调用图:调用 1 个内部函数(build_reqwest_client);被 2 处调用(http_probe_url, mcp_http_probe_url_with_timeout);外部调用 1 个(format!)。
http_get_probe_url_with_timeout2875–2879 ↗
async fn http_get_probe_url_with_timeout(url: &str, timeout: Duration) -> Result<String, String>
作用:用 GET 请求探测 URL,并把状态码包装成字符串。GET 是普通获取网页/API 的请求方式。
数据流:输入是 URL 和超时。函数调用 http_get_probe_status_with_timeout 得到数字状态码,再转成 HTTP xxx 字符串。
调用关系:mcp_http_probe_url_with_timeout 在 HEAD 探测失败后用它做兜底。
调用图:调用 1 个内部函数(http_get_probe_status_with_timeout);被 1 处调用(mcp_http_probe_url_with_timeout)。
http_get_probe_status_with_timeout2881–2899 ↗
async fn http_get_probe_status_with_timeout(url: &str, timeout: Duration) -> Result<u16, String>
作用:用 GET 请求拿到 URL 的 HTTP 状态码。它是更底层的网络探测工具。
数据流:输入是 URL 和超时。函数发送 GET 请求,成功返回状态码数字;失败时返回简短错误文字,如 request timed out 或 connect failed。
调用关系:provider_route_probe_url 用它判断路由是否存在;http_get_probe_url_with_timeout 用它生成字符串结果。
调用图:调用 1 个内部函数(build_reqwest_client);被 2 处调用(http_get_probe_url_with_timeout, provider_route_probe_url)。
stdio_command_resolves2901–2944 ↗
fn stdio_command_resolves(
command: &str,
cwd: Option<&Path>,
server_env: Option<&HashMap<String, String>>,
) -> Result<(), String>
作用:检查 MCP 的 stdio 命令能不能在本机找到。stdio 命令就是通过标准输入输出和 Codex 通信的本地程序。
数据流:输入是命令、可选工作目录和可选服务器环境变量。函数按绝对路径、相对路径、PATH 搜索三种情况查找可执行文件;Windows 下还会尝试 PATHEXT 扩展名;找到返回 Ok,否则返回错误原因。
调用关系:mcp_check_from_servers 用它确认本地 MCP server 配置不是写了一个不存在的命令;文件存在性检查交给 executable_path_exists。
调用图:调用 1 个内部函数(executable_path_exists);被 1 处调用(mcp_check_from_servers);外部调用 4 个(new, split_paths, var, format!)。
executable_path_exists2946–2952 ↗
fn executable_path_exists(path: &Path) -> Result<(), String>
作用:确认某个路径确实是一个文件,并且可作为可执行程序使用。
数据流:输入是路径。函数读取文件元数据;是普通文件就继续检查权限,不是文件或读取失败则返回错误文字。
调用关系:stdio_command_resolves 调用它检查候选命令路径;权限判断交给 executable_file_permission。
调用图:调用 1 个内部函数(executable_file_permission);被 2 处调用(stdio_command_resolves, executable_path_exists_rejects_non_executable_file);外部调用 1 个(metadata)。
executable_file_permission2966–2968 ↗
fn executable_file_permission(_path: &Path, _metadata: &std::fs::Metadata) -> Result<(), String>
作用:检查文件是否具备可执行权限。这个片段里的实现总是通过,通常是因为当前平台不需要额外检查或有条件编译的其他实现。
数据流:输入是路径和文件元数据。函数返回 Ok,不修改任何东西。
调用关系:executable_path_exists 在确认路径是文件后调用它;相关测试会验证不可执行文件在有权限检查的平台上如何处理。
调用图:被 1 处调用(executable_path_exists);外部调用 2 个(permissions, format!)。
path_readiness2970–2987 ↗
fn path_readiness(details: &mut Vec<String>, label: &str, path: &Path)
作用:把某个重要路径的当前状态写进诊断详情。它会说明路径存在、是目录还是文件,或者为什么读不到。
数据流:输入是详情列表、标签和路径。函数读取元数据,成功就记录 dir/file/other;不存在就记录 missing;其他错误就记录错误内容。
调用关系:state_check 用它检查 CODEX_HOME、日志目录、SQLite 目录和数据库路径。
调用图:被 1 处调用(state_check);外部调用 2 个(format!, metadata)。
standalone_release_cache_details2989–3005 ↗
fn standalone_release_cache_details(details: &mut Vec<String>)
作用:在独立安装模式下,统计本机缓存了多少个 release 目录。release 可以理解为程序版本包。
数据流:输入是详情列表。函数读取当前安装上下文;如果不是 Standalone 安装就跳过;如果能找到 releases 目录,就数里面的条目并追加详情。
调用关系:state_check 调用它补充安装缓存信息;它依赖 InstallContext::current 判断安装方式。
调用图:调用 1 个内部函数(current);被 1 处调用(state_check);外部调用 2 个(format!, read_dir)。
push_path_detail3007–3012 ↗
fn push_path_detail(details: &mut Vec<String>, label: &str, path: Option<&Path>)
作用:把一个可选路径写进详情列表。没有路径时明确写 none,避免报告里含糊不清。
数据流:输入是详情列表、标签和 Option<Path>。有路径就显示路径,没有就追加“标签: none”。
调用关系:installation_check 和 sandbox_check 用它展示各种可执行文件或沙箱路径。
调用图:被 2 处调用(installation_check, sandbox_check);外部调用 1 个(format!)。
push_env_path_detail3014–3019 ↗
fn push_env_path_detail(details: &mut Vec<String>, label: &str, name: &str)
作用:读取某个环境变量里的路径,并写入诊断详情。环境变量就是系统给程序的一组外部设置。
数据流:输入是详情列表、显示标签和环境变量名。函数从系统环境读取值;有值就按路径显示,没有就写 not set。
调用关系:installation_check 用它展示 PATH 类或安装相关环境路径。
调用图:被 1 处调用(installation_check);外部调用 2 个(var_os, format!)。
env_var_present3021–3023 ↗
fn env_var_present(name: &str) -> bool
作用:判断某个环境变量是否存在且不是空值。很多认证和配置都会通过环境变量传入。
数据流:输入是环境变量名。函数读取系统环境,存在且内容非空返回 true,否则 false。
调用关系:provider_auth_reachability_mode_from_auth、provider_specific_auth_check、stored_auth_issues 和 mcp_check_from_servers 都用它判断外部配置是否已提供。
调用图:被 4 处调用(mcp_check_from_servers, provider_auth_reachability_mode_from_auth, provider_specific_auth_check, stored_auth_issues);外部调用 1 个(var_os)。
human_output_options3025–3040 ↗
fn human_output_options(command: &DoctorCommand) -> HumanOutputOptions
作用:根据 doctor 命令参数和当前终端能力,决定人类可读输出应该怎么显示。
数据流:输入是 DoctorCommand。函数读取 TERM、NO_COLOR、stdout 是否是终端以及是否支持颜色,调用 should_enable_color 判断颜色开关,然后返回 HumanOutputOptions,包括是否显示详情、是否显示全部、是否用 ASCII、是否启用颜色。
调用关系:doctor 输出报告前会用它决定展示样式;颜色规则集中在 should_enable_color。
调用图:调用 1 个内部函数(should_enable_color);外部调用 4 个(var, var_os, stdout, on)。
should_enable_color3042–3054 ↗
fn should_enable_color(
no_color_flag: bool,
no_color_env: bool,
term: Option<&str>,
stdout_is_tty: bool,
stream_supports_color: bool,
) -> bool
作用:判断是否应该启用彩色输出。它尊重用户禁用颜色的参数和 NO_COLOR 环境变量。
数据流:输入包括命令行禁色标志、NO_COLOR 是否存在、TERM 值、stdout 是否是真终端、流是否支持颜色。只有所有条件都合适时返回 true。
调用关系:human_output_options 和 color_output_summary 都用它,保证颜色判断规则一致。
调用图:被 2 处调用(color_output_summary, human_output_options)。
tests::RecordingProgress::events3075–3077 ↗
fn events(&self) -> Vec<String>
作用:测试用:取出已经记录的进度事件,方便断言顺序是否正确。
数据流:输入是测试记录器自身。函数加锁读取内部事件列表,克隆后返回,不清空原列表。
调用关系:run_sync_check_notifies_progress 和 run_async_check_notifies_progress 用它检查 begin/finish 是否被调用。
tests::RecordingProgress::begin3081–3086 ↗
fn begin(&self, label: &'static str)
作用:测试用:记录某个检查开始了。
数据流:输入是检查标签。函数把 begin 标签 写入带锁的事件列表。
调用关系:run_sync_check 和 run_async_check 在测试里通过 DoctorProgress 接口触发它。
调用图:外部调用 1 个(format!)。
tests::RecordingProgress::heartbeat3088–3093 ↗
fn heartbeat(&self, label: &'static str, elapsed: Duration)
作用:测试用:记录某个检查运行中的心跳事件。心跳表示任务还活着,没有卡死。
数据流:输入是标签和已耗时长。函数把 heartbeat 标签 秒数 写入事件列表。
调用关系:它实现 DoctorProgress 接口,供进度相关测试或未来检查使用。
调用图:外部调用 1 个(format!)。
tests::RecordingProgress::finish3095–3100 ↗
fn finish(&self, label: &'static str, status: CheckStatus)
作用:测试用:记录某个检查结束以及最终状态。
数据流:输入是标签和 CheckStatus。函数把 finish 标签 状态 写入事件列表。
调用关系:run_sync_check_notifies_progress 和 run_async_check_notifies_progress 依靠它确认检查结束时有通知。
调用图:外部调用 1 个(format!)。
tests::RecordingProgress::settle3102–3107 ↗
fn settle(&self)
作用:测试用:记录进度显示进入稳定状态。
数据流:函数不接收额外输入,只把 settle 字符串写入事件列表。
调用关系:它是 DoctorProgress 测试实现的一部分,用于覆盖进度接口。
tests::respond_once3110–3115 ↗
fn respond_once(listener: &TcpListener, response: &[u8])
作用:测试用:启动一个只响应一次的简易 TCP 服务端。TCP 可以理解为网络连接的基础通道。
数据流:输入是监听器和原始 HTTP 响应字节。函数接受一个连接,读掉请求内容,然后写回指定响应。
调用关系:provider_reachability 和 HTTP 探测测试用它模拟服务端返回 401、404 等状态。
调用图:外部调用 1 个(accept)。
tests::overall_status_prefers_fail3118–3124 ↗
fn overall_status_prefers_fail()
作用:测试整体状态汇总时,Fail 应该压过 Warning。
数据流:函数构造一个 Warning 检查和一个 Fail 检查,调用 overall_status,断言结果是 Fail。
调用关系:它保护报告总状态的优先级规则,避免严重错误被警告掩盖。
调用图:外部调用 2 个(assert_eq!, vec!)。
tests::run_sync_check_notifies_progress3127–3140 ↗
fn run_sync_check_notifies_progress()
作用:测试同步检查会正确通知进度开始和结束。
数据流:函数创建 RecordingProgress,运行一个返回 Ok 的同步检查,然后断言状态和事件列表都是预期值。
调用关系:它验证 run_sync_check 与 DoctorProgress 的配合。
调用图:调用 1 个内部函数(run_sync_check);外部调用 3 个(new, assert_eq!, default)。
tests::run_async_check_notifies_progress3143–3157 ↗
async fn run_async_check_notifies_progress()
作用:测试异步检查也会正确通知进度开始和结束。异步就是任务可能等待网络或磁盘时不阻塞整个运行线程。
数据流:函数创建 RecordingProgress,运行一个返回 Warning 的异步检查,等待完成后断言状态和事件列表。
调用关系:它验证 run_async_check 与 DoctorProgress 的配合。
调用图:调用 2 个内部函数(new, run_async_check);外部调用 3 个(new, assert_eq!, default)。
tests::compare_npm_package_roots_detects_match3160–3169 ↗
fn compare_npm_package_roots_detects_match()
作用:测试 npm 安装路径一致时能识别为匹配。npm 是 Node.js 的包管理工具。
数据流:函数给出当前运行包路径和 npm root,调用 compare_npm_package_roots,断言结果包含正确 package_root。
调用关系:它保护安装检查里判断当前 Codex 是否来自同一个 npm 包根目录的逻辑。
调用图:外部调用 2 个(from, assert_eq!)。
tests::compare_npm_package_roots_detects_mismatch3172–3182 ↗
fn compare_npm_package_roots_detects_mismatch()
作用:测试 npm 安装路径不一致时能识别为不匹配。
数据流:函数给出旧运行路径和新 npm root,调用 compare_npm_package_roots,断言结果同时包含运行路径和 npm 推导路径。
调用关系:它帮助 installation_check 发现用户可能正在运行旧版本或错位置的 Codex。
调用图:外部调用 2 个(from, assert_eq!)。
tests::startup_warning_counts_group_known_sources3185–3207 ↗
fn startup_warning_counts_group_known_sources()
作用:测试启动警告能按来源分类统计,比如 skills、hooks、plugins、MCP 和 deprecated。
数据流:函数准备几条典型启动警告,调用 push_startup_warning_counts,然后断言详情列表包含总数和各分类计数。
调用关系:它保证启动阶段的杂乱警告在 doctor 报告里能被整理成可读统计。
调用图:调用 1 个内部函数(push_startup_warning_counts);外部调用 3 个(new, assert_eq!, vec!)。
tests::config_overrides_from_interactive_preserves_global_options3210–3254 ↗
fn config_overrides_from_interactive_preserves_global_options()
作用:测试从交互式命令行参数转成配置覆盖项时,不会丢掉全局选项。
数据流:函数构造一组 CLI 参数和可执行文件路径,调用 config_overrides_from_interactive,然后逐项断言模型、provider、工作目录、沙箱、审批策略、额外目录和可执行路径都保留下来。
调用关系:它保护 doctor 或交互入口复用配置时的参数传递正确性。
调用图:调用 1 个内部函数(config_overrides_from_interactive);外部调用 3 个(from, parse_from, assert_eq!)。
tests::redacted_json_report_structures_and_sanitizes_details3257–3360 ↗
fn redacted_json_report_structures_and_sanitizes_details()
作用:测试 JSON 报告会隐藏敏感信息,同时保持结构清楚。redact 就是打码,避免密钥、用户名密码和查询参数泄露。
数据流:函数构造含密钥、URL 凭据、编辑器命令和重复详情的 DoctorReport,调用 redacted_json_report,再序列化检查敏感字符串不存在、URL 被清理、details/notes/issues 结构符合预期。
调用关系:它保护 doctor 的机器可读报告安全性,特别是用户把报告发给别人排查时不泄密。
调用图:调用 1 个内部函数(redacted_json_report);外部调用 5 个(assert!, assert_eq!, to_string, to_value, vec!)。
tests::mcp_check_ignores_disabled_servers3363–3392 ↗
async fn mcp_check_ignores_disabled_servers()
作用:测试禁用的 MCP server 不会被当成错误检查。
数据流:函数解析一个 enabled=false 的 MCP 配置,调用 mcp_check_from_servers,断言状态 Ok、详情显示 disabled 数量,并且不出现 token 环境变量或 reachability failed。
调用关系:它保护 MCP 配置检查不会骚扰用户已经明确关闭的服务。
调用图:调用 1 个内部函数(mcp_check_from_servers);外部调用 4 个(from, assert!, assert_eq!, from_str)。
tests::mcp_check_warns_for_optional_http_reachability3395–3414 ↗
async fn mcp_check_warns_for_optional_http_reachability()
作用:测试可选 MCP HTTP 服务连不上时只给 Warning,而不是 Fail。
数据流:函数配置一个默认可选的本地不可达 URL,调用 mcp_check_from_servers,断言状态 Warning,并确认详情写入 optional reachability failed。
调用关系:它验证 MCP 检查区分“必须可用”和“可选可用”的规则。
调用图:调用 1 个内部函数(mcp_check_from_servers);外部调用 4 个(from, assert!, assert_eq!, from_str)。
tests::mcp_check_fails_required_remote_stdio_env_var3417–3442 ↗
async fn mcp_check_fails_required_remote_stdio_env_var()
作用:测试必需的 stdio MCP server 如果使用 remote 来源的环境变量,会被判为失败。
数据流:函数用当前测试可执行文件作为 command,配置 env_vars 中 source=remote,调用 mcp_check_from_servers,断言状态 Fail 且详情说明该配置需要 remote MCP stdio。
调用关系:它保护 MCP 配置检查能发现本地 stdio 场景下不适用的远程环境变量来源。
调用图:调用 1 个内部函数(mcp_check_from_servers);外部调用 7 个(from, assert!, assert_eq!, format!, current_exe, String, from_str)。
tests::provider_specific_auth_allows_non_openai_provider_without_env_key3445–3460 ↗
fn provider_specific_auth_allows_non_openai_provider_without_env_key()
作用:测试非 OpenAI provider 在不要求专用环境变量时可以通过认证检查。
数据流:函数调用 provider_specific_auth_check,传入不需要 OpenAI auth、没有 provider env key、环境变量检查恒为 false,断言返回 Ok 和正确摘要。
调用关系:它保护第三方或本地 provider 不会被错误要求 OpenAI 密钥。
调用图:调用 1 个内部函数(provider_specific_auth_check);外部调用 2 个(new, assert_eq!)。
tests::provider_specific_auth_fails_when_provider_env_key_is_missing3463–3482 ↗
fn provider_specific_auth_fails_when_provider_env_key_is_missing()
作用:测试 provider 明确要求某个环境变量时,缺失就应失败。
数据流:函数传入 PROVIDER_API_KEY 和修复说明,但环境变量检查恒为 false;调用 provider_specific_auth_check 后断言 Fail、摘要和 remediation 正确。
调用关系:它保护 provider 专属认证提示能准确告诉用户该设置什么。
调用图:调用 1 个内部函数(provider_specific_auth_check);外部调用 2 个(new, assert_eq!)。
tests::stored_auth_validation_rejects_missing_api_key3485–3501 ↗
fn stored_auth_validation_rejects_missing_api_key()
作用:测试保存的 API key 认证如果没有真正的 key,会被指出问题;但环境变量里有 key 时可视为补足。
数据流:函数构造 auth_mode=ApiKey 但 openai_api_key=None 的 AuthDotJson,调用 stored_auth_issues 两次,分别断言缺 key 和环境变量补救后的结果。
调用关系:它保护本地 auth.json 健康检查不会把空壳认证当成有效。
调用图:外部调用 2 个(assert!, assert_eq!)。
tests::stored_auth_validation_rejects_missing_chatgpt_tokens3504–3522 ↗
fn stored_auth_validation_rejects_missing_chatgpt_tokens()
作用:测试 ChatGPT 认证缺少 token 和刷新信息时会被报告。
数据流:函数构造没有 token、没有 last_refresh 的 AuthDotJson,调用 stored_auth_issues,断言返回两条缺失说明。
调用关系:它保护登录状态检查能发现损坏或不完整的 ChatGPT 登录文件。
调用图:外部调用 1 个(assert_eq!)。
tests::stored_auth_validation_handles_personal_access_token3525–3545 ↗
fn stored_auth_validation_handles_personal_access_token()
作用:测试个人访问令牌这种认证模式能被正确识别和校验。
数据流:函数先构造带 personal_access_token 的 auth,断言模式名正确且无问题;再把 token 清空并指定该模式,断言会报告缺少个人访问令牌。
调用关系:它保护 stored_auth_mode 和 stored_auth_issues 对 personal access token 的支持。
调用图:外部调用 2 个(assert!, assert_eq!)。
tests::provider_reachability_mode_uses_api_key_auth3548–3575 ↗
fn provider_reachability_mode_uses_api_key_auth()
作用:测试 provider 连通性计划在有 API key 认证时选择 API key 路线。
数据流:函数分别构造 auth.json 中有 API key、以及环境变量中有 OPENAI_API_KEY 的情况,调用 provider_auth_reachability_mode_from_auth,断言结果都是 ApiKey。
调用关系:它保护 provider_reachability_plan 的认证路线选择不会误走 ChatGPT 后端。
调用图:外部调用 1 个(assert_eq!)。
tests::provider_reachability_uses_active_provider_endpoint3578–3599 ↗
fn provider_reachability_uses_active_provider_endpoint()
作用:测试不需要 OpenAI 认证的 provider 会探测它自己的 endpoint。
数据流:函数调用 provider_reachability_plan_from_parts,传入 Azure 风格 base URL,断言生成的 endpoint 使用该 URL,且不加 /models 路由探测。
调用关系:它保护第三方 provider 的连通性检查使用当前配置,而不是默认 OpenAI 地址。
调用图:外部调用 1 个(assert_eq!)。
tests::provider_reachability_adds_models_route_probe_for_openai_compatible_base_urls3602–3627 ↗
fn provider_reachability_adds_models_route_probe_for_openai_compatible_base_urls()
作用:测试 OpenAI 兼容 base URL 会额外生成 /models 路由探测地址,并保留查询参数。
数据流:函数准备 api-version 查询参数,调用 provider_reachability_plan_from_parts,断言 route_probe_url 是 base URL 加 models 和查询参数。
调用关系:它保护 provider_url_for_path 和 should_probe_models_route 的组合行为。
调用图:外部调用 2 个(from, assert_eq!)。
tests::provider_reachability_skips_route_probe_for_bedrock3630–3642 ↗
fn provider_reachability_skips_route_probe_for_bedrock()
作用:测试 Amazon Bedrock provider 不会做 /models 路由探测。
数据流:函数用 Bedrock base URL 构造计划,断言第一个 endpoint 的 route_probe_url 是 None。
调用关系:它保护 should_probe_models_route 对 Bedrock 的特殊处理。
调用图:调用 1 个内部函数(provider_reachability_plan_from_parts);外部调用 1 个(assert_eq!)。
tests::provider_reachability_api_key_does_not_require_chatgpt3645–3665 ↗
fn provider_reachability_api_key_does_not_require_chatgpt()
作用:测试使用 API key 时,连通性检查只需要 OpenAI API,不需要 ChatGPT 后端。
数据流:函数以 ApiKey 模式构造计划,断言 endpoint 是 https://api.openai.com/v1,并带 /models 路由探测。
调用关系:它保护 provider_reachability_plan_from_parts 的认证模式分流。
调用图:调用 1 个内部函数(provider_reachability_plan_from_parts);外部调用 1 个(assert_eq!)。
tests::provider_reachability_outcome_reports_required_failures3668–3683 ↗
fn provider_reachability_outcome_reports_required_failures()
作用:测试 provider 连通性汇总结论:警告得到 Warning,必需失败得到 Fail。
数据流:函数直接调用 provider_reachability_outcome,分别传入有警告和有必需失败的数量,断言状态和摘要。
调用关系:它保护 provider_reachability_check 最终状态的判断规则。
调用图:外部调用 1 个(assert_eq!)。
tests::provider_reachability_route_404_fails_bad_base_url_path3686–3724 ↗
async fn provider_reachability_route_404_fails_bad_base_url_path()
作用:测试 /models 路由返回 404 时,doctor 会判定 base_url 路径可能配错并失败。
数据流:函数启动本地测试服务器连续返回 404,构造带错误路径的 provider 计划,运行 provider_reachability_check,断言状态 Fail、详情包含 404、issue 中有修复建议。
调用关系:它验证 provider_route_probe_url 的 404 语义和 provider_reachability_check 的错误提示。
调用图:调用 2 个内部函数(provider_reachability_check, provider_reachability_plan_from_parts);外部调用 5 个(bind, assert!, assert_eq!, format!, spawn)。
tests::provider_reachability_route_401_keeps_reachability_ok3727–3760 ↗
async fn provider_reachability_route_401_keeps_reachability_ok()
作用:测试 /models 路由返回 401 时仍算可达。401 表示没授权,但路由存在。
数据流:函数启动本地测试服务器,base URL 探测返回 404,路由探测返回 401;运行 provider_reachability_check 后断言状态 Ok,详情写着 route exists。
调用关系:它保护 provider_route_probe_url 把 401/403 当作路由存在的规则。
调用图:调用 2 个内部函数(provider_reachability_check, provider_reachability_plan_from_parts);外部调用 5 个(bind, assert!, assert_eq!, format!, spawn)。
tests::collect_rollout_stats_counts_nested_rollout_files3763–3785 ↗
fn collect_rollout_stats_counts_nested_rollout_files()
作用:测试 rollout 文件统计会递归进入子目录,并只统计符合命名规则的文件。
数据流:函数创建临时目录和嵌套 sessions 路径,写入一个 rollout jsonl 和一个普通 jsonl,调用 collect_rollout_stats,断言只统计前者且大小为 5。
调用关系:它验证 collect_rollout_stats_inner 和 is_rollout_file 的配合。
调用图:调用 1 个内部函数(collect_rollout_stats);外部调用 4 个(assert_eq!, create_dir_all, write, tempdir)。
tests::http_probe_treats_http_status_as_reachable3788–3806 ↗
async fn http_probe_treats_http_status_as_reachable()
作用:测试 HTTP 探测只要收到状态码就算地址可响应,即使状态码是 405。
数据流:函数启动本地服务器返回 HTTP 405,调用 http_probe_url,断言结果是 Ok("HTTP 405")。
调用关系:它保护 http_probe_url_with_timeout 的设计:网络通了和业务成功是两回事,doctor 在这里只判断可达性。
调用图:调用 1 个内部函数(http_probe_url);外部调用 4 个(bind, assert_eq!, format!, spawn)。
tests::mcp_http_probe_falls_back_to_get_when_head_times_out3809–3839 ↗
async fn mcp_http_probe_falls_back_to_get_when_head_times_out()
作用:这个测试确认:检查 MCP HTTP 地址时,如果先发的 HEAD 请求超时,程序会改用 GET 再试一次,而不是直接放弃。HEAD 可以理解成“只问门开没开,不拿内容”的轻量请求。
数据流:测试先开一个本地临时 TCP 服务器。第一次连接时,服务器故意读了请求后睡一会儿,让 HEAD 探测超时;第二次连接时,服务器回复 HTTP 405。然后测试把这个地址交给 mcp_http_probe_url_with_timeout,并设置很短的超时时间。最后它检查结果是否是 Ok("HTTP 405"),说明程序确实从超时的 HEAD 退回到了 GET,并把服务端返回的状态带了出来。
调用关系:这是 mcp_http_probe_url_with_timeout 的保护性测试。真正的探测函数负责访问 HTTP MCP 服务;这个测试在旁边伪造一个行为刁钻的小服务器,确认探测流程遇到 HEAD 卡住时,会继续尝试 GET,而不是让整个 MCP 检查误判为完全不可达。
调用图:调用 1 个内部函数(mcp_http_probe_url_with_timeout);外部调用 5 个(from_millis, bind, assert_eq!, format!, spawn)。
tests::mcp_check_fails_required_missing_stdio_command3842–3864 ↗
async fn mcp_check_fails_required_missing_stdio_command()
作用:这个测试确认:如果配置里有一个“必须可用”的 MCP stdio 服务,但它写的启动命令根本找不到,doctor 会把结果判为失败。stdio 这里可以理解成通过标准输入输出和子程序说话。
数据流:测试先用一段 TOML 文本造出一个 MCP 服务配置,里面的 command 是一个几乎不可能存在的名字,并标记 required = true。它把这个配置放进服务器列表,交给 mcp_check_from_servers。检查函数会尝试判断命令是否能被系统找到。最后测试确认返回状态是 Fail,摘要说明必要输入或可达性失败,并且详情里明确写出这个 stdio 命令不可解析。
调用关系:这是 mcp_check_from_servers 的规则测试。它模拟用户配置了一个必需 MCP 服务但命令写错的情况,确保上层 doctor 报告会把它当成真正失败,而不是只给一个轻飘飘的警告。
调用图:调用 1 个内部函数(mcp_check_from_servers);外部调用 4 个(from, assert!, assert_eq!, from_str)。
tests::read_probe_file_rejects_unreadable_file3868–3887 ↗
fn read_probe_file_rejects_unreadable_file()
作用:这个测试确认:用来探测文件内容的函数不会把“存在但读不了”的文件当成正常文件。它主要防止证书、配置等文件权限不对时被误判为可用。
数据流:测试创建一个临时文件,写入 cert,然后把文件权限改成 000,也就是任何人都不能读。接着调用 read_probe_file 读取这个文件。调用后,测试把权限恢复,避免临时文件清理出问题。最后它检查结果是错误,说明函数正确识别了“文件不可读”。
调用关系:这是 read_probe_file 的权限测试。doctor 里的某些检查需要读文件来确认配置是否有效;这个测试保证底层读文件步骤遇到权限问题时,会把错误往上交,而不是假装成功。
调用图:调用 1 个内部函数(read_probe_file);外部调用 5 个(assert!, metadata, set_permissions, write, new)。
tests::executable_path_exists_rejects_non_executable_file3891–3911 ↗
fn executable_path_exists_rejects_non_executable_file()
作用:这个测试确认:检查可执行程序路径时,光有文件还不够,文件还必须真的有“可执行”权限。可执行权限可以理解成系统允许把它当程序启动。
数据流:测试创建一个临时文件,写入像脚本开头一样的内容,但先把权限设成 600,也就是只能读写、不能执行。它调用 executable_path_exists,期望得到错误。然后测试再把权限改成 700,让文件变成可执行,并再次调用同一个函数,期望得到 Ok(())。
调用关系:这是 executable_path_exists 的边界测试。MCP stdio 服务或其他外部命令检查会依赖这个判断;这个测试确保检查不会被一个“看起来像程序但不能运行”的文件骗过。
调用图:调用 1 个内部函数(executable_path_exists);外部调用 6 个(assert!, assert_eq!, metadata, set_permissions, write, new)。
tests::should_enable_color_respects_terminal_inputs3914–3950 ↗
fn should_enable_color_respects_terminal_inputs()
作用:这个测试确认:是否启用彩色输出,会同时尊重命令行开关、环境变量、终端类型以及输出目标。也就是说,颜色不是想开就开,要看用户和终端是否允许。
数据流:测试把几组输入分别传给 should_enable_color。第一组是正常终端、支持颜色、没有禁用要求,应该返回 true。后面几组分别打开 --no-color、设置 NO_COLOR、把 TERM 设成 dumb、让 stdout 不是终端,都应该返回 false。
调用关系:这是 should_enable_color 的决策表测试。终端体检和输出渲染都会关心颜色是否能用;这个测试确保只要有明确禁用原因,函数就不会强行输出彩色内容。
调用图:外部调用 1 个(assert!)。
tests::terminal_inputs3952–3972 ↗
fn terminal_inputs() -> TerminalCheckInputs
作用:这个辅助函数造出一份“正常终端”的默认测试输入,方便后面的测试只改自己关心的那一项。它像一张标准体检表模板。
数据流:函数不接收参数。它创建 TerminalCheckInputs,里面包含终端名未知、TERM=xterm-256color、标准输入输出错误都是终端、支持颜色、窗口大小 120×40、没有 tmux 或 Windows 控制台额外信息等默认值。返回的是这份完整输入结构。
调用关系:后面的多个 terminal_check 测试都会先调用 terminal_inputs 得到干净基线,再改成 dumb 终端、窄窗口、非 UTF-8 语言环境等异常情况。这样每个测试只突出一个问题,避免互相干扰。
tests::set_terminal_env3974–3981 ↗
fn set_terminal_env(inputs: &mut TerminalCheckInputs, name: &str, value: &str)
作用:这个辅助函数用来在测试输入里设置某个环境变量,同时记录这个变量“出现过”。环境变量可以理解成系统给程序的一些外部说明,比如 TERM、LANG。
数据流:函数接收一份可修改的 TerminalCheckInputs、变量名和值。它先把变量名加入 present_env,表示这个变量存在过。如果值是空字符串,就从 env 表里删除这个变量;否则就把变量名和值写入 env 表。结果是输入对象被原地改好,没有单独返回值。
调用关系:很多终端测试需要模拟不同环境变量,例如 TERM=dumb、COLUMNS=60、LANG=C、NO_COLOR。它们通过 set_terminal_env 改测试输入,再交给 terminal_check_from_inputs 或 color_output_summary。
tests::terminal_check_warns_for_dumb_terminal3984–4002 ↗
fn terminal_check_warns_for_dumb_terminal()
作用:这个测试确认:当终端类型是 dumb 时,doctor 会明确报失败,并告诉用户颜色和光标控制都不可用。dumb 终端可以理解成“功能很弱的终端”。
数据流:测试先拿一份默认终端输入,然后把终端名和 TERM 都改成 dumb。接着调用 terminal_check_from_inputs。最后它检查状态是 Fail,摘要是 TERM=dumb 导致颜色和光标控制关闭,并且修复建议是把 TERM 改成类似 xterm-256color 的真实值。
调用关系:这是 terminal_check_from_inputs 的关键失败场景测试。doctor 需要尽早发现 dumb 终端,因为这种环境会影响彩色文字、光标移动和交互显示。
调用图:调用 1 个内部函数(terminal_check_from_inputs);外部调用 3 个(assert_eq!, set_terminal_env, terminal_inputs)。
tests::terminal_check_warns_for_narrow_terminal4005–4021 ↗
fn terminal_check_warns_for_narrow_terminal()
作用:这个测试确认:如果实际终端宽度小于推荐值,doctor 会给出警告,提示输出可能换行变乱。
数据流:测试从默认输入开始,把 terminal_size 改成 79 列、24 行。然后调用 terminal_check_from_inputs。返回结果应是 Warning,摘要说明宽度 79 列可能导致输出折行,问题详情里写着期望至少 80 列,并给出把窗口拉宽到至少 80 列的建议。
调用关系:这是终端尺寸检查的测试。terminal_check_from_inputs 会把窗口大小作为诊断信息的一部分;这个测试保证窗口太窄时不会被忽略,也不会被夸大成严重失败。
调用图:调用 1 个内部函数(terminal_check_from_inputs);外部调用 2 个(assert_eq!, terminal_inputs)。
tests::terminal_check_warns_for_declared_narrow_terminal4024–4037 ↗
fn terminal_check_warns_for_declared_narrow_terminal()
作用:这个测试确认:如果环境变量 COLUMNS 自称终端只有 60 列,doctor 也会提醒用户输出可能换行。
数据流:测试创建默认终端输入,然后用 set_terminal_env 设置 COLUMNS=60。它调用 terminal_check_from_inputs 后,检查状态是 Warning,摘要点名 COLUMNS=60,详情里包含 COLUMNS: 60,并且问题字段指向 COLUMNS。
调用关系:这是对“声明的终端宽度”的测试。terminal_check_from_inputs 不只看实际探测到的窗口大小,也会看环境变量里用户或系统声明的宽度;这个测试确保这条线索能进入报告。
调用图:调用 1 个内部函数(terminal_check_from_inputs);外部调用 4 个(assert!, assert_eq!, set_terminal_env, terminal_inputs)。
tests::terminal_check_warns_for_non_utf8_locale4040–4056 ↗
fn terminal_check_warns_for_non_utf8_locale()
作用:这个测试确认:如果语言环境不是 UTF-8,doctor 会警告 Unicode 字符可能显示不正常。UTF-8 是现在最常见的文字编码方式,用来正确显示各种语言和符号。
数据流:测试从默认输入开始,把 LANG 设置成 C。然后调用 terminal_check_from_inputs。结果应是 Warning,摘要说明 locale 不是 UTF-8,详情里写出 effective locale: C,并给出导出 LANG=en_US.UTF-8 或其他 UTF-8 locale 的建议。
调用关系:这是语言环境检查的测试。终端输出可能包含特殊符号或多语言文字;terminal_check_from_inputs 需要发现编码环境不合适的情况,并给用户一个可操作的修复方向。
调用图:调用 1 个内部函数(terminal_check_from_inputs);外部调用 4 个(assert!, assert_eq!, set_terminal_env, terminal_inputs)。
tests::terminal_check_warns_for_unreadable_terminfo_path4059–4082 ↗
fn terminal_check_warns_for_unreadable_terminfo_path()
作用:这个测试确认:如果 TERMINFO 指向一个不存在或读不了的目录,doctor 会报失败。terminfo 是终端能力数据库,程序靠它知道终端能做什么。
数据流:测试创建一个临时目录,再拼出一个并不存在的 missing-terminfo 路径。它把 TERMINFO 设置到这个缺失路径,然后调用 terminal_check_from_inputs。返回结果应是 Fail,摘要说明 TERMINFO 不可读,详情里标出这个路径缺失,并建议检查 $TERMINFO 是否指向可读目录。
调用关系:这是终端能力数据库路径检查的测试。terminal_check_from_inputs 会读取 TERMINFO 相关线索;这个测试保证如果用户把 TERMINFO 配坏,doctor 会明确指出根因,而不是只说终端未知。
调用图:调用 1 个内部函数(terminal_check_from_inputs);外部调用 5 个(assert!, assert_eq!, set_terminal_env, terminal_inputs, tempdir)。
tests::terminal_check_reports_remote_indicators_as_present_only4085–4102 ↗
fn terminal_check_reports_remote_indicators_as_present_only()
作用:这个测试确认:doctor 发现 SSH_CONNECTION 这类远程登录信息时,只报告“存在”,不会把里面的 IP 地址原样展示出来。这样能减少泄露隐私的风险。
数据流:测试创建默认输入,把 SSH_CONNECTION 设置成包含本地和远端 IP、端口的一串值。调用 terminal_check_from_inputs 后,它检查详情里有 SSH_CONNECTION: present,同时确认没有任何详情包含具体 IP 地址 10.0.0.1。
调用关系:这是终端环境报告的隐私测试。terminal_check_from_inputs 会把一些环境变量放进诊断详情;这个测试确保对远程连接变量只展示是否存在,不暴露具体网络地址。
调用图:调用 1 个内部函数(terminal_check_from_inputs);外部调用 3 个(assert!, set_terminal_env, terminal_inputs)。
tests::terminal_check_includes_windows_console_details4105–4118 ↗
fn terminal_check_includes_windows_console_details()
作用:这个测试确认:如果收集到了 Windows 控制台的额外信息,doctor 会把它放进终端检查详情里,方便排查 Windows 上的显示问题。
数据流:测试拿默认终端输入,往 windows_console_details 里加入一条控制台模式说明,比如 stdout console mode 和 VT processing 是否开启。然后调用 terminal_check_from_inputs,并检查返回详情包含这条原文。
调用关系:这是 Windows 终端信息透传的测试。底层探测代码可能收集 Windows 控制台状态;terminal_check_from_inputs 负责把这些信息带到最终报告中,方便用户或维护者判断颜色、转义序列等功能是否可用。
调用图:调用 1 个内部函数(terminal_check_from_inputs);外部调用 2 个(assert!, terminal_inputs)。
tests::terminal_check_keeps_tmux_probe_failures_non_fatal4121–4129 ↗
fn terminal_check_keeps_tmux_probe_failures_non_fatal()
作用:这个测试确认:如果检测到用户在 tmux 里,但拿不到 tmux 版本,这件事不会被当成错误。tmux 是一种终端复用工具,可以在一个窗口里开多个会话。
数据流:测试从默认输入开始,把 multiplexer 设置成 Tmux,但 version 是 None,表示知道在 tmux 里,却不知道版本号。调用 terminal_check_from_inputs 后,结果应仍是 Ok,摘要为 terminal metadata was detected。
调用关系:这是 tmux 探测容错测试。terminal_check_from_inputs 会整理终端和复用器信息;这个测试保证附加探测失败不会影响主检查结果,避免因为拿不到版本号就吓到用户。
调用图:调用 1 个内部函数(terminal_check_from_inputs);外部调用 2 个(assert_eq!, terminal_inputs)。
tests::color_output_summary_reports_disabled_reasons4132–4152 ↗
fn color_output_summary_reports_disabled_reasons()
作用:这个测试确认:当彩色输出被关闭时,摘要会说清楚具体原因,而不是只说“不能用颜色”。这能让用户知道该改哪个开关或环境变量。
数据流:测试分四轮构造输入。第一轮打开 no_color_flag,期望摘要是 disabled (--no-color)。第二轮设置 NO_COLOR,期望摘要是 disabled (NO_COLOR)。第三轮把 TERM 改成 dumb,期望摘要是 disabled (TERM=dumb)。第四轮让 stdout 不是终端,期望摘要是 disabled (stdout is not a terminal)。
调用关系:这是 color_output_summary 的说明文字测试。终端检查会用这个函数把颜色状态讲给用户听;这个测试保证不同禁用原因能被准确区分,最终报告才有指导意义。
调用图:外部调用 3 个(assert_eq!, set_terminal_env, terminal_inputs)。
tui/src/session_archive_commands.rs源码 ↗
这个文件像一个“前台办事窗口”。用户在命令行里说要归档、删除或恢复某个会话,但用户可能给的是一串 UUID,也可能只是会话标题。这里先启动或连接 app server(应用后台服务),再把这个目标查清楚:如果是 UUID 就直接用;如果是名字,就分页翻会话列表,找名字完全一样的那一条。删除比较危险,所以默认会在交互式终端里二次确认,避免一敲错就永久删掉。真正改数据的动作不在这里做,而是交给 app server 的 RPC(远程过程调用,可以理解成“让后台执行某个命令”)。最后它会返回一句适合显示给用户看的成功提示。
success_message57–71 ↗
fn success_message(
action: SessionArchiveAction,
session_id: ThreadId,
session_name: Option<&str>,
) -> String
作用:把一次归档、删除或恢复操作的结果,整理成一句人能看懂的成功提示。有人调用完后台操作后,用它来生成要打印给用户的文字。
数据流:进去的是动作类型、会话 UUID,以及可能存在的会话名。它先把动作换成英文过去式,比如 Archived 或 Deleted,再决定提示里要不要带会话名。出来的是一段完整字符串,不会改动外部数据。
调用关系:它处在流程最后一步。run_session_archive_action_with_app_server 做完真正的后台操作后,会调用它生成最终返回给命令行用户的消息。
调用图:被 1 处调用(run_session_archive_action_with_app_server);外部调用 1 个(format!)。
run_session_archive_command78–85 ↗
async fn run_session_archive_command(
action: SessionArchiveAction,
target: String,
options: SessionArchiveCommandOptions,
) -> Result<String>
作用:这是这组命令的总入口。它负责先准备后台连接,再把具体的归档、删除或恢复工作交给下一层。
数据流:进去的是要执行的动作、用户输入的目标文字,以及命令行选项。它先调用 start_app_server_for_archive_command 启动或连接 app server,然后把连接、动作和目标交给 run_session_archive_action_with_app_server。出来的是要显示给用户的结果文字,或者错误。
调用关系:它把“准备环境”和“执行动作”串起来。CLI 命令会走到这里;它先找 start_app_server_for_archive_command 做启动准备,再让 run_session_archive_action_with_app_server 处理业务动作。
调用图:调用 2 个内部函数(run_session_archive_action_with_app_server, start_app_server_for_archive_command)。
run_session_archive_action_with_app_server87–117 ↗
async fn run_session_archive_action_with_app_server(
app_server: &mut AppServerSession,
action: SessionArchiveAction,
target: &str,
) -> Result<String>
作用:在已经有后台连接的前提下,真正决定要归档、删除还是恢复哪个会话。它也负责删除前的确认。
数据流:进去的是 app server 连接、动作类型和用户输入的目标。它先用 resolve_session_target 把目标变成明确的会话 UUID 和可能的名字;如果是删除且需要确认,就调用 confirm_session_delete;随后分别调用后台的 thread_archive、thread_delete 或 thread_unarchive。出来的是成功提示;如果用户取消删除,就出来“Delete cancelled.”。
调用关系:它是核心调度点。run_session_archive_command 会调用它;它向下使用 resolve_session_target 查目标,用 confirm_session_delete 防误删,用 app server 的接口执行修改,最后用 success_message 收尾。
调用图:调用 6 个内部函数(thread_archive, thread_delete, thread_unarchive, confirm_session_delete, resolve_session_target, success_message);被 1 处调用(run_session_archive_command);外部调用 1 个(matches!)。
resolve_session_target119–159 ↗
async fn resolve_session_target(
app_server: &mut AppServerSession,
action: SessionArchiveAction,
target: &str,
) -> Result<ResolvedSessionTarget>
作用:把用户输入的“目标”变成一个明确的会话。目标可能是 UUID,也可能是会话名;这个函数负责消除这种不确定性。
数据流:进去的是后台连接、动作类型和目标字符串。它先尝试把字符串解析成 ThreadId(会话 UUID);如果删除需要确认,还会读取会话信息拿到名字。若不是 UUID,它会根据动作决定搜索活跃会话、归档会话,或两者都搜,再调用 lookup_session_by_exact_name 按精确名字查找。出来的是 ResolvedSessionTarget,也就是会话 UUID 加上可能的会话名;找不到就返回错误。
调用关系:它被 run_session_archive_action_with_app_server 调用,是执行动作前的“认人”步骤。它会使用 thread_read 读取单个会话,也会让 lookup_session_by_exact_name 去列表里找名字,找到后再交给 session_target_from_app_server_thread 转成内部可用的目标。
调用图:调用 4 个内部函数(from_string, thread_read, lookup_session_by_exact_name, session_target_from_app_server_thread);被 1 处调用(run_session_archive_action_with_app_server);外部调用 2 个(eyre!, matches!)。
lookup_session_by_exact_name161–203 ↗
async fn lookup_session_by_exact_name(
app_server: &mut AppServerSession,
name: &str,
archived: bool,
) -> Result<Option<AppServerThread>>
作用:按会话名精确查找某个会话。它解决的问题是:用户记得标题,但不知道 UUID。
数据流:进去的是后台连接、要找的名字,以及是否只看已归档会话。它会一页一页请求会话列表,每页最多 100 条,并检查每条的名字是否和输入完全相同。它先尝试带搜索词查,找不到再不带搜索词扫一遍,避免某些存储系统过滤时漏掉改名后的标题。出来的是找到的会话,或者 None。
调用关系:它只在 resolve_session_target 需要按名字查会话时被调用。它把具体翻页、筛选、继续拿下一页这些琐事包起来,让上层只关心“有没有找到”。
调用图:调用 1 个内部函数(thread_list);被 1 处调用(resolve_session_target);外部调用 1 个(resume_source_kinds)。
session_target_from_app_server_thread205–212 ↗
fn session_target_from_app_server_thread(thread: AppServerThread) -> Result<ResolvedSessionTarget>
作用:把后台返回的会话对象,转换成这个文件内部使用的明确目标格式。这样后续操作只需要会话 UUID 和名字,不用关心后台对象长什么样。
数据流:进去的是 app server 返回的 Thread 对象,其中 id 是字符串形式。它把 id 解析成 ThreadId;如果后台给了非法 id,就返回带说明的错误。出来的是 ResolvedSessionTarget,包含可安全使用的会话 UUID 和会话名。
调用关系:它被 resolve_session_target 调用,专门接在按名字查到会话之后。lookup_session_by_exact_name 找到的是后台对象,这个函数把它整理成后续归档、删除、恢复都能直接用的目标。
调用图:调用 1 个内部函数(from_string);被 1 处调用(resolve_session_target)。
confirm_session_delete214–241 ↗
fn confirm_session_delete(target: &ResolvedSessionTarget) -> Result<bool>
作用:删除前向用户确认,防止误删。因为删除是永久的,所以它要求用户在真实的交互式终端里明确输入 yes 或 y。
数据流:进去的是已解析出的会话目标。它先检查标准输入和标准错误是不是终端;如果不是,就拒绝确认,并提示改用强制选项和 UUID。然后它把会话名、UUID 和“不可撤销”的警告写到 stderr,读取用户输入。出来的是 true 或 false,表示是否继续删除。
调用关系:它只在 run_session_archive_action_with_app_server 处理需要提示的删除动作时使用。如果它返回 false,上层会停止删除并返回取消消息;如果返回 true,上层才会调用 thread_delete。
调用图:被 1 处调用(run_session_archive_action_with_app_server);外部调用 6 个(new, eyre!, stderr, stdin, write!, writeln!)。
start_app_server_for_archive_command243–397 ↗
async fn start_app_server_for_archive_command(
options: SessionArchiveCommandOptions,
) -> Result<AppServerSession>
作用:为归档、删除、恢复命令准备一个可用的 app server 会话。简单说,它负责把配置、登录相关信息、运行路径、本地或远程目标这些东西都装配好。
数据流:进去的是命令行选项,包括 CLI 参数、程序路径和可能指定的远程端点。它读取 Codex 主目录,解析 -c 配置覆盖项,判断能不能复用本地后台进程,决定连接本地还是远程;接着加载配置文件和云端配置,准备执行环境、模型选择、工作目录、状态数据库,最后启动 app server 并包装成 AppServerSession。出来的是一个可以发 thread_archive、thread_delete、thread_unarchive 等请求的会话连接。
调用关系:它是 run_session_archive_command 的第一步。它不直接归档或删除会话,而是把“后台服务能不能用、该连哪里、该用什么配置”这些前置工作做好,然后把连接交给 run_session_archive_action_with_app_server 去执行具体动作。
调用图:调用 8 个内部函数(default, load_config_toml_with_layer_stack, resolve_oss_provider, resolve_profile_v2_config_path, from_env, from_optional_paths, new, new);被 1 处调用(run_session_archive_command);外部调用 12 个(default, cloud_config_bundle_loader_for_storage, find_codex_home, default, default, resolve_bootstrap_auth_keyring_backend_kind, app_server_target_for_launch, can_reuse_implicit_local_daemon, config_cwd_for_app_server_target, init_state_db_for_app_server_target (+2 more))。
补丁和 MCP 工具
这些独立命令处理器用于应用远程任务 diff,并管理 MCP 服务器配置和身份验证。
chatgpt/src/apply_command.rs源码 ↗
这个文件像一个“取快递并拆包安装”的流程。用户输入一个任务 ID,程序先读取配置和命令行覆盖项,知道该去哪里找任务;然后调用 get_task 拿到这个 Codex 任务的结果;接着从任务里找到当前最新的代码差异,也就是 diff(描述哪些文件要改、怎么改的文本补丁);最后交给 git apply 去真正改本地文件。如果任务里没有 diff,或者没有可用于 PR 的输出,它会直接报错,避免悄悄什么都不做。真正应用补丁时,它还会检查 git 返回码;失败时会把已应用、跳过、冲突的文件数量,以及标准输出和错误输出一起报出来,方便人定位问题。
run_apply_command22–36 ↗
async fn run_apply_command(
apply_cli: ApplyCommand,
cwd: Option<PathBuf>,
) -> anyhow::Result<()>
作用:这是 apply 命令的总入口。它负责把用户给的任务 ID 和配置准备好,然后去取任务,再把任务里的代码改动交给后续步骤应用到本地。
数据流:进去的是 ApplyCommand,也就是命令行里解析出来的任务 ID 和配置覆盖项,以及可选的工作目录 cwd。它先把配置覆盖项解析出来,并用这些覆盖项加载完整配置;再用配置和任务 ID 调用 get_task 拉取任务内容;最后把拿到的任务响应和工作目录传给 apply_diff_from_task。出来的是成功或失败的结果;它自己不直接改文件,真正改文件发生在后面的补丁应用步骤。
调用关系:cli_main 在用户运行这个 apply 子命令时调用它。它是流程的串联者:先把配置交给外部的 load_with_cli_overrides,接着把任务 ID 交给 get_task,最后把任务结果交给 apply_diff_from_task。
调用图:调用 2 个内部函数(apply_diff_from_task, get_task);被 1 处调用(cli_main);外部调用 1 个(load_with_cli_overrides)。
apply_diff_from_task38–54 ↗
async fn apply_diff_from_task(
task_response: GetTaskResponse,
cwd: Option<PathBuf>,
) -> anyhow::Result<()>
作用:这个函数负责从任务结果里挑出真正的代码补丁。它像在一包材料里找到“施工图”,找到后才会继续让本地代码按图修改。
数据流:进去的是 GetTaskResponse,也就是服务器返回的任务信息,以及可选工作目录 cwd。它先检查任务里有没有 current_diff_task_turn;没有就报“No diff turn found”。有的话,它会在输出项里寻找 PR 输出项,并取出里面的 output_diff。找到 diff 后,把 diff 文本交给 apply_diff;如果找不到 PR 输出项,就报“No PR output item found”。出来的是应用补丁的成功或失败结果;它本身不直接碰磁盘文件。
调用关系:run_apply_command 在拿到任务响应后会调用它。测试用例 test_apply_command_creates_fibonacci_file 和 test_apply_command_with_merge_conflicts 也会直接调用它,分别验证正常创建文件和遇到合并冲突时的行为。它找到补丁后,把真正改本地文件的活交给 apply_diff。
调用图:调用 1 个内部函数(apply_diff);被 3 处调用(run_apply_command, test_apply_command_creates_fibonacci_file, test_apply_command_with_merge_conflicts);外部调用 1 个(bail!)。
apply_diff56–77 ↗
async fn apply_diff(diff: &str, cwd: Option<PathBuf>) -> anyhow::Result<()>
作用:这个函数真正把 diff 应用到本地 Git 工作目录。简单说,它就是把前面找到的“改动清单”交给 Git 执行,并检查有没有成功。
数据流:进去的是 diff 文本和可选工作目录 cwd。它先决定在哪个目录操作:如果传了 cwd 就用它;否则尝试用当前目录,连当前目录都取不到时退到系统临时目录。然后它组装 ApplyGitRequest,里面包含工作目录、diff 内容,并明确不是回滚、也不是只预检。接着调用 apply_git_patch 执行补丁。若 Git 返回的 exit_code 不是 0,它会带着已应用、跳过、冲突路径数量以及 stdout、stderr 报错;若成功,就打印“Successfully applied diff”,并返回成功。
调用关系:apply_diff_from_task 在已经从任务里找到 diff 后调用它。它把具体执行工作交给外部的 apply_git_patch;如果失败,使用 bail! 立刻结束并给出详细错误;如果成功,使用 println! 告诉用户补丁已经应用。
调用图:被 1 处调用(apply_diff_from_task);外部调用 4 个(bail!, apply_git_patch, println!, current_dir)。
cli/src/mcp_cmd.rs源码 ↗
MCP 可以理解成让 Codex 连接外部工具的一套接口规则;MCP 服务器就是提供这些工具的服务。这个文件像一个“工具服务器通讯录”的前台:用户输入 list、get、add、remove、login、logout,它就读取 Codex 配置,找到已登记的服务器,按需要改写全局配置文件,或启动 OAuth 登录流程。OAuth 是一种网页登录授权方式,避免直接把密码交给程序。文件里还区分两类服务器:一种是本地命令启动后用标准输入输出通信,另一种是通过 HTTP 地址访问。它特别注意名字是否合法、输出能不能给人看或给机器读 JSON、以及登录时某些服务器不接受“权限范围”时会自动重试一次。
McpCli::run171–203 ↗
async fn run(self, loader_overrides: LoaderOverrides) -> Result<()>
作用:这是 codex mcp 命令组的总调度员。它看用户选择了哪个子命令,然后把事情交给对应的处理函数。
数据流:进去的是命令行解析出来的参数和配置加载选项 → 它先拆出通用配置覆盖项和具体子命令,如果用户指定了配置 profile,还会先验证迁移后的配置能不能加载 → 最后调用 list、get、add、remove、login 或 logout 中的一个,成功时不返回额外数据,只表示命令完成。
调用关系:它是这个文件里所有具体动作的入口。它会按分支调用 run_list、run_get、run_add、run_remove、run_login、run_logout,必要时先调用 validate_profile_v2_migration 做配置兼容检查。
调用图:调用 7 个内部函数(run_add, run_get, run_list, run_login, run_logout, run_remove, validate_profile_v2_migration)。
perform_oauth_login_retry_without_scopes210–258 ↗
async fn perform_oauth_login_retry_without_scopes(
name: &str,
url: &str,
store_mode: codex_config::types::OAuthCredentialsStoreMode,
keyring_backend_kind: codex_config::types::AuthKey
作用:这个函数负责执行 MCP 服务器的 OAuth 登录,并处理一个兼容性坑:有些老服务器不喜欢带权限范围的登录请求。它会在合适的时候自动去掉权限范围再试一次。
数据流:进去的是服务器名、登录网址、凭据保存方式、请求头、已算好的 OAuth 权限范围、客户端 ID、回调地址等信息 → 它先按这些范围调用真正的登录流程 → 如果成功就结束;如果失败且错误看起来是“权限范围被拒绝”,就打印提示并用空权限范围再登录一次;如果是别的错误,就把错误交还给调用者。
调用关系:run_add 在新增服务器并发现支持 OAuth 时会用它自动登录;run_login 在用户手动登录时也会用它。它自己把真正的网页登录授权交给外部的 perform_oauth_login,并用 should_retry_without_scopes 判断要不要重试。
调用图:被 2 处调用(run_add, run_login);外部调用 3 个(should_retry_without_scopes, perform_oauth_login, println!)。
validate_profile_v2_migration260–274 ↗
async fn validate_profile_v2_migration(
config_overrides: &CliConfigOverrides,
loader_overrides: LoaderOverrides,
) -> Result<()>
作用:这个函数用来确认用户指定的配置 profile 在新版配置加载方式下仍然能正常读取。它的作用是提前发现配置迁移问题,而不是等后面操作时才报错。
数据流:进去的是命令行配置覆盖项和配置加载选项 → 它先把覆盖项解析成内部能用的配置修改 → 再通过配置构建器尝试完整加载配置 → 如果加载成功就什么也不改并返回成功;如果失败就带着“加载配置失败”的上下文返回错误。
调用关系:只有 McpCli::run 在检测到用户用了 profile 时会调用它。它不处理 MCP 服务器本身,只是给后续 run_* 命令扫清配置加载的障碍。
调用图:调用 1 个内部函数(parse_overrides);被 1 处调用(run);外部调用 1 个(default)。
run_add276–409 ↗
async fn run_add(config_overrides: &CliConfigOverrides, add_args: AddArgs) -> Result<()>
作用:这个函数实现 codex mcp add,把一个新的 MCP 服务器登记到全局配置里。它还会在服务器支持 OAuth 时,顺手带用户完成登录。
数据流:进去的是通用配置覆盖项和新增服务器参数 → 它加载配置,检查服务器名字是否合法,读取当前全局 MCP 服务器列表 → 根据用户给的是本地命令还是 HTTP 地址,组装一条新的服务器配置 → 写回配置文件 → 如果 HTTP 服务器看起来支持 OAuth,就解析登录所需权限并启动登录 → 最后在终端打印新增和登录结果。
调用关系:McpCli::run 在用户选择 Add 时调用它。它会用 validate_server_name 防止坏名字写入配置,用配置编辑器保存服务器列表,并在需要登录时把登录细节交给 perform_oauth_login_retry_without_scopes。
调用图:调用 6 个内部函数(perform_oauth_login_retry_without_scopes, validate_server_name, new, find_codex_home, load_global_mcp_servers, parse_overrides);被 1 处调用(run);外部调用 7 个(new, new, load_with_cli_overrides, bail!, oauth_login_support, resolve_oauth_scopes, println!)。
run_remove411–442 ↗
async fn run_remove(config_overrides: &CliConfigOverrides, remove_args: RemoveArgs) -> Result<()>
作用:这个函数实现 codex mcp remove,从全局配置中删掉一个 MCP 服务器登记。它让用户不用手动打开配置文件删除条目。
数据流:进去的是配置覆盖项和要删除的服务器名 → 它先解析覆盖项并检查名字格式 → 找到 Codex 的配置目录,读出现有 MCP 服务器列表 → 尝试按名字删除 → 如果确实删到了,就把新列表写回配置文件;如果没找到,就只打印提示 → 返回成功或写文件错误。
调用关系:McpCli::run 在 Remove 子命令下调用它。它和 run_add 一样依赖 validate_server_name,但不处理登录凭据,只负责配置里的服务器条目。
调用图:调用 5 个内部函数(validate_server_name, new, find_codex_home, load_global_mcp_servers, parse_overrides);被 1 处调用(run);外部调用 1 个(println!)。
run_login444–497 ↗
async fn run_login(config_overrides: &CliConfigOverrides, login_args: LoginArgs) -> Result<()>
作用:这个函数实现 codex mcp login,给一个已经配置好的 HTTP MCP 服务器做 OAuth 登录。它用于服务器需要网页授权才能访问工具的情况。
数据流:进去的是配置覆盖项、服务器名和用户可选填写的权限范围 → 它加载配置并得到实际可用的 MCP 服务器列表 → 找到指定服务器,确认它是 HTTP 类型,因为本地命令型不支持这里的 OAuth 登录 → 决定使用用户指定、配置中写好的、或服务器发现到的权限范围 → 调用登录函数完成授权 → 成功后打印登录成功。
调用关系:McpCli::run 在 Login 子命令下调用它。它会创建 MCP 管理器读取服务器配置,必要时调用外部的权限发现函数,最后把实际登录交给 perform_oauth_login_retry_without_scopes。
调用图:调用 4 个内部函数(perform_oauth_login_retry_without_scopes, new, new, parse_overrides);被 1 处调用(run);外部调用 6 个(new, load_with_cli_overrides, bail!, discover_supported_scopes, resolve_oauth_scopes, println!)。
run_logout499–534 ↗
async fn run_logout(config_overrides: &CliConfigOverrides, logout_args: LogoutArgs) -> Result<()>
作用:这个函数实现 codex mcp logout,删除某个 MCP HTTP 服务器保存下来的 OAuth 登录凭据。它相当于把这个服务器的“记住我”清掉。
数据流:进去的是配置覆盖项和服务器名 → 它加载配置,找到对应的 MCP 服务器 → 确认服务器是 HTTP 类型并取出网址 → 调用删除令牌的函数,把本地保存的 OAuth token 删除 → 根据是否真的删到凭据打印不同提示;如果删除失败就返回错误。
调用关系:McpCli::run 在 Logout 子命令下调用它。它读取服务器信息的方式和 run_login 类似,但后续不是登录,而是把清理凭据的工作交给外部的 delete_oauth_tokens。
调用图:调用 3 个内部函数(new, new, parse_overrides);被 1 处调用(run);外部调用 6 个(new, anyhow!, load_with_cli_overrides, bail!, delete_oauth_tokens, println!)。
run_list536–791 ↗
async fn run_list(config_overrides: &CliConfigOverrides, list_args: ListArgs) -> Result<()>
作用:这个函数实现 codex mcp list,列出当前配置里的所有 MCP 服务器。它既能输出人看的表格,也能输出程序容易读取的 JSON。
数据流:进去的是配置覆盖项和是否输出 JSON 的选项 → 它加载配置,取得已配置服务器和实际生效服务器 → 计算每个服务器的登录状态 → 按名字排序 → 如果要求 JSON,就把服务器名、启用状态、连接方式、超时和认证状态整理成 JSON 打印;否则把本地命令型和 HTTP 型分成两张表打印。
调用关系:McpCli::run 在 List 子命令下调用它。它会调用 format_mcp_status 把启用或禁用状态变成短文字,也会借助外部的认证状态计算函数判断每个服务器是否已登录、是否不支持登录等。
调用图:调用 4 个内部函数(format_mcp_status, new, new, parse_overrides);被 1 处调用(run);外部调用 7 个(new, new, load_with_cli_overrides, compute_auth_statuses, format_env_display, println!, to_string_pretty)。
run_get793–962 ↗
async fn run_get(config_overrides: &CliConfigOverrides, get_args: GetArgs) -> Result<()>
作用:这个函数实现 codex mcp get <name>,查看某一个 MCP 服务器的详细配置。它适合用户确认某个服务器到底怎么连接、启没启用、有哪些工具限制。
数据流:进去的是配置覆盖项、服务器名和是否输出 JSON 的选项 → 它加载配置并查找指定服务器 → 如果找不到就报错 → 如果要求 JSON,就把主要配置字段序列化后打印 → 如果是普通输出,就按人容易读的格式打印启用状态、工具白名单或黑名单、连接方式、环境变量、HTTP 头、超时和删除命令提示。
调用关系:McpCli::run 在 Get 子命令下调用它。它不修改配置,只读取并展示;展示环境变量时会调用外部的 format_env_display,避免把信息排得杂乱。
调用图:调用 3 个内部函数(new, new, parse_overrides);被 1 处调用(run);外部调用 7 个(new, load_with_cli_overrides, bail!, format_env_display, println!, json!, to_string_pretty)。
parse_env_pair964–977 ↗
fn parse_env_pair(raw: &str) -> Result<(String, String), String>
作用:这个小函数把命令行里的 KEY=VALUE 环境变量写法拆成键和值。它用于新增本地命令型 MCP 服务器时给启动命令附加环境变量。
数据流:进去的是一段原始字符串,比如 TOKEN=abc → 它只按第一个等号切开,左边去掉空白后当作变量名,右边原样当作变量值 → 如果没有变量名或没有等号,就返回给命令行解析器看的错误文字;否则返回一对字符串。
调用关系:它被挂在 AddMcpStdioArgs 的命令行参数解析上。也就是说用户输入 --env KEY=VALUE 时,真正进入 run_add 前就会先经过它检查和拆分。
validate_server_name979–990 ↗
fn validate_server_name(name: &str) -> Result<()>
作用:这个函数检查 MCP 服务器名字是不是安全、简单的名字。它只允许字母、数字、横线和下划线,避免把奇怪字符写进配置或命令提示里。
数据流:进去的是服务器名字符串 → 它检查名字不能为空,并且每个字符都必须是 ASCII 字母数字、- 或 _ → 合法就返回成功;不合法就返回错误,告诉用户应该用哪些字符。
调用关系:run_add 在写入新服务器前调用它,run_remove 在删除服务器前也调用它。它是配置改动前的一道小门卫,防止无效名字进入后续读写流程。
调用图:被 2 处调用(run_add, run_remove);外部调用 1 个(bail!)。
format_mcp_status992–1000 ↗
fn format_mcp_status(config: &McpServerConfig) -> String
作用:这个函数把服务器的启用状态变成适合列表里显示的一小段文字。它让 list 输出不用到处重复判断启用、禁用和禁用原因。
数据流:进去的是一个 MCP 服务器配置 → 它查看 enabled 字段;如果启用就输出 enabled,如果禁用且有原因就输出 disabled: 原因,如果禁用但没原因就输出 disabled → 不改动原配置,只返回显示用字符串。
调用关系:run_list 在组装表格行时调用它。它只负责把状态翻译成人话,列表的排序、认证状态和打印排版仍由 run_list 完成。