Codex 系统手册

安装环境、主目录发现和本地环境探测

stage-312 个文件

这一阶段像开工前先认路、点工具。程序会弄清自己装在哪、家目录 CODEX_HOME 在哪、捆绑的小工具该从哪里拿,也会看当前电脑、终端、Git、云端执行环境是否可用。安装识别和托管安装文件帮更新、重启找准版本;家目录和主机名给配置加载打底;shell 快照尽量保住用户终端习惯;exec-server 和云环境选择决定命令在哪跑;doctor 检查 Git、系统和运行环境,方便出问题时快速定位。

本阶段涉及的状态8
  • reg-installation-environment程序知道自己装在哪里、家目录在哪里、运行在哪台机器和什么终端里的那组环境信息。
  • reg-sandbox-execution-policy命令和工具运行时要遵守的沙箱、读写、联网、身份和执行策略。
  • reg-exec-environment-processesexec-server 和统一执行层里正在运行或已经结束的进程、退出码、输入输出和失败原因。
  • reg-auto-update-state自动更新检查、可用版本、下载进度、安装结果和待重启标记等更新生命周期状态。
  • reg-shell-environment-snapshot启动时捕获的用户 shell、PATH、环境变量和终端习惯快照,用于后续命令执行和上下文说明。
  • reg-runtime-environment-registry本地、云端或远程执行环境的可用清单、默认选择和当前线程选中的运行目标。
  • reg-worktree-diff-state当前工作树快照、Git 状态、文件改动摘要和待展示或持久化的 diff 状态。
  • reg-active-workspace-context当前会话选中的工作目录、项目根和能力根等工作区上下文,用于提示词、工具执行、权限判断和结果展示。

本阶段的文件12

安装布局发现

这些文件确定 Codex 的安装位置、主目录和捆绑资源所在位置,以及如何识别托管安装。

app-server-daemon/src/managed_install.rs源码 ↗
domain_logicstartup / update check / restart handling

这个文件像是托管版 Codex 的“身份证和地址簿”。它先规定 Codex 可执行文件应该放在 codex_home/packages/standalone/current/ 下面,并按系统选择文件名:Windows 叫 codex.exe,其他系统叫 codex。它还会把路径解析成真实路径,避免遇到软链接(一种像快捷方式一样指向别处的文件)时搞错目标。在 Unix 系统上,它能直接运行这个 Codex,传入 --version,读出版本号;如果程序运行失败、输出不是正常文字、格式不对,就会返回清楚的错误。另一个重要功能是给可执行文件算 SHA-256 摘要,也就是用文件内容算出一串“指纹”,用来判断两个程序文件是不是同一个内容。整体上,它不负责下载或安装,而是给更新、环境初始化、重启判断这些流程提供可靠的底层判断依据。

函数细节7
managed_codex_bin19–25 ↗
fn managed_codex_bin(codex_home: &Path) -> PathBuf

作用:根据 Codex 的主目录,拼出托管版 Codex 可执行文件应该所在的位置。别人想知道“系统管理的 codex 程序在哪儿”时,就会用它。

数据流:进去的是一个 codex_home 路径 → 它在后面依次加上 packages、standalone、current,再加上当前系统该用的文件名 → 出来的是完整的 Codex 可执行文件路径;它不读写磁盘,只是拼路径。

调用关系:环境初始化流程 from_environment 会调用它来确定默认的托管 Codex 路径。它自己把“文件名该叫 codex 还是 codex.exe”这件小事交给 managed_codex_file_name。

调用图:调用 1 个内部函数(managed_codex_file_name);被 1 处调用(from_environment);外部调用 1 个(join)。

resolved_managed_codex_bin28–35 ↗
async fn resolved_managed_codex_bin(codex_bin: &Path) -> Result<PathBuf>

作用:把一个 Codex 程序路径解析成真实的、最终落到磁盘上的路径。这样更新流程不会被软链接或相对路径误导。

数据流:进去的是一个可能包含软链接的 codex_bin 路径 → 它请求操作系统把路径标准化、解析到真实位置 → 出来的是解析后的 PathBuf;如果路径不存在或解析失败,就带着文件名返回错误。

调用关系:update_once 在做一次更新时会用它确认当前托管 Codex 的真实位置。它主要依赖系统的 canonicalize 能力,也就是“把路径查清楚”。

调用图:被 1 处调用(update_once);外部调用 1 个(canonicalize)。

managed_codex_version38–64 ↗
async fn managed_codex_version(codex_bin: &Path) -> Result<String>

作用:运行托管版 Codex,并读取它报告的版本号。更新器和重启逻辑需要知道当前程序版本,才能决定是否需要动作。

数据流:进去的是 Codex 可执行文件路径 → 它启动这个程序并传入 --version → 如果程序正常退出,就把输出的字节转成 UTF-8 文字(常见文本编码),再从文字里提取版本号 → 出来的是版本字符串;如果启动失败、退出码失败、文字编码不对或格式不对,就返回错误。

调用关系:managed_codex_version_best_effort 会用它尝试读取版本但可能容忍失败;try_restart_if_running 会用它辅助判断正在跑的 Codex 是否需要重启。它把“从输出文字里抠出版本号”的细活交给 parse_codex_version。

调用图:调用 1 个内部函数(parse_codex_version);被 2 处调用(managed_codex_version_best_effort, try_restart_if_running);外部调用 3 个(from_utf8, anyhow!, new)。

executable_identity73–78 ↗
async fn executable_identity(executable: &Path) -> Result<ExecutableIdentity>

作用:读取一个可执行文件,并给它算出内容指纹。这个指纹可以用来判断文件是不是被更新、替换,或者和另一个文件是否一致。

数据流:进去的是可执行文件路径 → 它从磁盘读取整个文件内容 → 把读到的字节交给 executable_identity_from_bytes 算 SHA-256 摘要 → 出来的是 ExecutableIdentity;如果文件读不到,就返回带路径说明的错误。

调用关系:current_updater_identity 会用它识别当前更新器本身,update_once 会用它在更新时比较文件身份。它负责读文件,真正计算指纹的活交给 executable_identity_from_bytes。

调用图:调用 1 个内部函数(executable_identity_from_bytes);被 2 处调用(current_updater_identity, update_once);外部调用 1 个(read)。

executable_identity_from_bytes81–85 ↗
fn executable_identity_from_bytes(bytes: &[u8]) -> ExecutableIdentity

作用:根据一段文件内容直接算出可执行文件的“身份指纹”。这适合在已经拿到文件字节时使用,不必再读磁盘。

数据流:进去的是一段字节数组 → 它用 SHA-256(一种常用的内容摘要算法,内容变一点结果就会大变)计算 32 字节摘要 → 出来的是包在 ExecutableIdentity 里的指纹;它不改动外部状态。

调用关系:executable_identity 读完磁盘文件后会调用它。它是指纹计算的核心小零件,外层函数负责拿文件,它负责把内容变成可比较的身份。

调用图:被 1 处调用(executable_identity);外部调用 1 个(digest)。

managed_codex_file_name87–89 ↗
fn managed_codex_file_name() -> &'static str

作用:决定托管版 Codex 可执行文件在当前系统上应该叫什么名字。这样同一套代码能兼容 Windows 和类 Unix 系统。

数据流:进去没有参数 → 它检查编译目标是不是 Windows → 如果是 Windows,出来 codex.exe;否则出来 codex;它不访问磁盘,也不产生副作用。

调用关系:managed_codex_bin 会调用它来完成路径最后一段的文件名选择。它把操作系统差异藏起来,让上层只管拼路径。

调用图:被 1 处调用(managed_codex_bin);外部调用 1 个(cfg!)。

parse_codex_version92–99 ↗
fn parse_codex_version(output: &str) -> Result<String>

作用:从 Codex 的 --version 输出文字里提取真正的版本号。它假设输出类似“codex 1.2.3”,取第二段作为版本。

数据流:进去的是一整段命令输出文字 → 它按空白字符切开,取第 2 个片段,并确认不是空字符串 → 出来的是版本号字符串;如果找不到版本号,就返回“版本输出格式不对”的错误。

调用关系:managed_codex_version 在成功运行 Codex 并拿到输出后,会把文字交给它解析。它只负责理解输出格式,不负责启动程序。

调用图:被 1 处调用(managed_codex_version)。

utils/home-dir/src/lib.rs源码 ↗
utilstartup / config load

程序总得知道自己的“家”在哪里,比如配置、登录信息、缓存要放到哪个文件夹。这个文件就是给 Codex 找这个家目录的。它支持两种方式:如果用户设置了环境变量 CODEX_HOME,也就是在系统里提前告诉程序“请用这个目录”,它就严格检查这个路径必须存在,而且必须真的是文件夹,然后把路径整理成规范的绝对路径。如果用户没设置 CODEX_HOME,它就退回到默认位置:当前用户家目录里的 .codex。这里有个重要区别:自定义 CODEX_HOME 会被检查是否存在;默认的 ~/.codex 不会检查是否已经建好。可以把它想成找办公室地址:别人指定的地址必须真的能进门;如果没人指定,就先记下默认地址,之后需要时再建。

函数细节6
find_codex_home13–18 ↗
fn find_codex_home() -> std::io::Result<AbsolutePathBuf>

作用:这是外部最常用的入口,用来获取 Codex 的配置目录。调用者不用关心环境变量怎么读,它会自动处理 CODEX_HOME 和默认目录两种情况。

数据流:进去时不需要参数;它从系统环境里读取 CODEX_HOME,如果这个值不存在或是空字符串,就当作没设置;然后把读到的结果交给 find_codex_home_from_env。出来的是一个绝对路径,或者一个说明哪里不对的输入输出错误。

调用关系:它是公开函数,其他模块想知道 Codex 配置目录时会调用它。它自己只负责读取环境变量,真正判断路径是否有效、是否使用默认目录的工作交给 find_codex_home_from_env。

调用图:调用 1 个内部函数(find_codex_home_from_env);外部调用 1 个(var)。

find_codex_home_from_env20–63 ↗
fn find_codex_home_from_env(codex_home_env: Option<&str>) -> std::io::Result<AbsolutePathBuf>

作用:这个函数做真正的判断:如果给了 CODEX_HOME,就验证它;如果没给,就拼出默认的 ~/.codex 路径。它把“配置目录到底在哪”这件事变成一个可靠的结果。

数据流:进去的是一个可有可无的字符串:有值表示用户指定了 CODEX_HOME,没有值表示走默认。若有值,它把字符串变成路径,读取这个路径的信息,确认它存在且是目录,再整理成规范的绝对路径;如果不存在、不是目录或无法整理,就返回错误。若没有值,它读取当前用户的家目录,在后面加上 .codex,再包装成绝对路径返回。

调用关系:find_codex_home 会把环境变量读取结果交给它。测试函数也直接调用它,这样可以不用真的改系统环境变量,就能检查各种情况:路径不存在、路径是文件、路径是有效目录、没有设置环境变量。

调用图:调用 1 个内部函数(from_absolute_path);被 5 处调用(find_codex_home, find_codex_home_env_file_path_is_fatal, find_codex_home_env_missing_path_is_fatal, find_codex_home_env_valid_directory_canonicalizes, find_codex_home_without_env_uses_default_home_dir);外部调用 5 个(from, new, home_dir, format!, metadata)。

tests::find_codex_home_env_missing_path_is_fatal76–89 ↗
fn find_codex_home_env_missing_path_is_fatal()

作用:这个测试确认:如果 CODEX_HOME 指向一个不存在的地方,程序不能假装没事,而是必须报错。这样可以避免用户以为配置会写到某个目录,结果实际没有。

数据流:它先创建一个临时目录,再拼出一个并不存在的子路径,把这个路径当作 CODEX_HOME 传给 find_codex_home_from_env。结果应该是错误,并且错误类型应该是“找不到”,错误文字里也应该提到 CODEX_HOME。

调用关系:它直接测试 find_codex_home_from_env 的错误分支,证明自定义 CODEX_HOME 会被严格检查,而不是像默认 ~/.codex 那样仅仅拼出路径。

调用图:调用 1 个内部函数(find_codex_home_from_env);外部调用 3 个(new, assert!, assert_eq!)。

tests::find_codex_home_env_file_path_is_fatal92–106 ↗
fn find_codex_home_env_file_path_is_fatal()

作用:这个测试确认:CODEX_HOME 不能指向一个普通文件,必须是文件夹。因为配置目录需要能放多个配置文件,文件本身不够用。

数据流:它创建一个临时文件,把文件路径传给 find_codex_home_from_env。函数应该返回错误,错误类型是“输入无效”,错误内容也应该说明这不是目录。

调用关系:它覆盖 find_codex_home_from_env 里“路径存在但不是目录”的分支,确保以后改代码时不会放松这个检查。

调用图:调用 1 个内部函数(find_codex_home_from_env);外部调用 4 个(new, assert!, assert_eq!, write)。

tests::find_codex_home_env_valid_directory_canonicalizes109–123 ↗
fn find_codex_home_env_valid_directory_canonicalizes()

作用:这个测试确认:当 CODEX_HOME 指向一个真实目录时,函数会返回整理后的标准绝对路径。这样后续代码拿到的地址更稳定,不容易被相对路径或符号链接搞混。

数据流:它创建一个临时目录,把目录路径传给 find_codex_home_from_env。函数返回解析后的路径;测试再用系统的 canonicalize,也就是把路径整理成标准形式,算出预期结果,最后比较两者必须相同。

调用关系:它验证 find_codex_home_from_env 的正常成功分支,也顺带检查它确实调用了路径规范化步骤,而不是原样返回用户输入。

调用图:调用 2 个内部函数(from_absolute_path, find_codex_home_from_env);外部调用 2 个(new, assert_eq!)。

tests::find_codex_home_without_env_uses_default_home_dir126–133 ↗
fn find_codex_home_without_env_uses_default_home_dir()

作用:这个测试确认:没有设置 CODEX_HOME 时,函数会使用用户家目录下的 .codex。也就是说默认位置是固定、可预测的。

数据流:它把 None 传给 find_codex_home_from_env,表示没有环境变量。函数返回默认路径;测试自己读取用户家目录,手动加上 .codex,再包装成绝对路径,最后比较两个结果是否一致。

调用关系:它覆盖 find_codex_home_from_env 的默认路径分支,保证 find_codex_home 在读不到 CODEX_HOME 时会走到正确的位置。

调用图:调用 2 个内部函数(from_absolute_path, find_codex_home_from_env);外部调用 2 个(assert_eq!, home_dir)。

install-context/src/lib.rs源码 ↗
domain_logicstartup and cross-cutting

Codex 在不同用户电脑上的来源可能不一样:有人用 npm 装,有人用 Homebrew 装,也有人用项目自己下载的独立包。这个文件就像“看门牌识别住址”的小工具:它查看当前可执行文件的位置、环境变量、Codex 主目录,再推断当前运行环境。它还认识一种标准包结构:包根目录下面有 bin、codex-resources、codex-path 等目录。这样其他模块要找 rg(ripgrep,一个搜索文件内容的命令行工具)、zsh 或其他随包附带的文件时,可以先用包里自带的版本;找不到时再退回系统命令。文件里还用 OnceLock(一种只初始化一次的保险箱)缓存当前安装信息,避免每次都重复探测路径。底部的测试用临时目录假装各种安装方式,确认判断不会误把目录当文件,也确认 npm、bun 这类明确标记会优先于路径猜测。

函数细节25
InstallContext::from_exe69–83 ↗
fn from_exe(
        is_macos: bool,
        current_exe: Option<&Path>,
        managed_by_npm: bool,
        managed_by_bun: bool,
    ) -> Self

作用:根据当前程序文件的位置和“是否由 npm/bun 管理”的标记,生成一份安装环境说明。外部代码可以用它知道 Codex 当前来自哪里。

数据流:输入是系统类型、可执行文件路径、npm/bun 标记;它先尝试找到 Codex 的家目录,再把这些信息交给更底层的判断函数;输出是一个 InstallContext,里面写着安装方式和可能存在的包目录布局。

调用关系:这是公开的便捷入口。它自己不做太多判断,而是先调用 find_codex_home 找默认家目录,再把完整信息交给 InstallContext::from_exe_with_codex_home。

调用图:外部调用 2 个(from_exe_with_codex_home, find_codex_home)。

InstallContext::from_exe_with_codex_home85–107 ↗
fn from_exe_with_codex_home(
        is_macos: bool,
        current_exe: Option<&Path>,
        managed_by_npm: bool,
        managed_by_bun: bool,
        codex_home: Option<&Path>,
    ) -> Self

作用:这是安装环境判断的核心版本,可以显式传入 Codex 家目录,所以特别适合测试和精确控制。

数据流:输入是系统类型、程序路径、npm/bun 标记、Codex 家目录;它先看程序是否处在标准包布局里,再按优先级判断安装方式:npm 优先,其次 bun,再根据路径判断独立包、Homebrew 或其他;输出完整的 InstallContext。

调用关系:InstallContext::from_exe 会调用它,很多测试也直接调用它来模拟不同安装场景。它把路径推断这部分工作交给 install_method_from_exe。

调用图:调用 1 个内部函数(install_method_from_exe);被 9 处调用(brew_is_detected_on_macos_prefixes, bundled_file_lookups_ignore_directories, detects_package_layout_independently_from_install_method, detects_standalone_install_from_release_layout, npm_and_bun_take_precedence, npm_managed_package_keeps_package_layout, standalone_package_layout_keeps_standalone_install_method, standalone_package_rg_falls_back_when_codex_path_is_missing, standalone_rg_falls_back_when_resources_are_missing)。

InstallContext::current109–121 ↗
fn current() -> &'static Self

作用:取得“当前正在运行的这个 Codex”的安装环境,并且全程序只计算一次。

数据流:它读取当前可执行文件路径和 CODEX_MANAGED_BY_NPM、CODEX_MANAGED_BY_BUN 这两个环境变量;然后调用 InstallContext::from_exe 得到结果;结果被存在全局缓存里,之后再调用会直接复用。

调用关系:这是运行时最常用的入口。命令分发、诊断信息、更新检查、启动器、搜索线程等地方都会问它:现在这个 Codex 到底是怎么来的。

调用图:被 7 处调用(arg0_dispatch, doctor_install_context, standalone_release_cache_details, apply_package_path_prepend, launcher, search_threads, get_update_action)。

InstallContext::rg_command123–145 ↗
fn rg_command(&self) -> PathBuf

作用:找出应该运行哪个 rg 命令。rg 是 ripgrep,一个常用的高速文本搜索工具。

数据流:它读取当前安装环境;先看标准包的 codex-path 目录里有没有自带 rg 文件,再看独立包资源目录里有没有;如果都没有,就返回普通命令名 rg 或 Windows 上的 rg.exe。

调用关系:搜索功能会调用它来决定执行哪个搜索工具。它内部用 default_rg_command 处理不同系统上的默认文件名。

调用图:调用 1 个内部函数(default_rg_command);被 1 处调用(search_provider)。

InstallContext::bundled_resource147–169 ↗
fn bundled_resource(&self, file_name: impl AsRef<Path>) -> Option<AbsolutePathBuf>

作用:按文件名查找 Codex 安装包里自带的某个资源文件,比如辅助程序或数据文件。

数据流:输入是一个资源文件名;它先在标准包的 codex-resources 目录查找,再在独立安装包的资源目录查找;只有目标确实是文件时才返回路径,否则返回 None。

调用关系:其他模块需要找打包资源时会用它,例如安装相关查找和 zsh 查找。它是更具体资源查找函数的基础。

调用图:被 2 处调用(bundled_zsh_path, find_for_install_context);外部调用 1 个(as_ref)。

InstallContext::bundled_zsh_path171–177 ↗
fn bundled_zsh_path(&self) -> Option<AbsolutePathBuf>

作用:查找安装包里自带的 zsh 程序路径。zsh 是一种 Unix/macOS 上常见的命令行 shell。

数据流:它先判断是否在 Windows;Windows 上直接返回 None;非 Windows 时拼出 zsh 的资源相对路径,再调用 bundled_resource 查找实际文件。

调用关系:bundled_zsh_bin_dir 会调用它来进一步得到 zsh 所在目录。它把具体路径格式交给 zsh_resource_path。

调用图:调用 2 个内部函数(bundled_resource, zsh_resource_path);被 1 处调用(bundled_zsh_bin_dir);外部调用 1 个(cfg!)。

InstallContext::bundled_zsh_bin_dir179–181 ↗
fn bundled_zsh_bin_dir(&self) -> Option<AbsolutePathBuf>

作用:返回自带 zsh 所在的 bin 目录,而不是 zsh 文件本身。

数据流:它先调用 bundled_zsh_path 找 zsh 文件;如果找到了,就取这个文件的父目录;如果没找到,就返回 None。

调用关系:它是 bundled_zsh_path 的小包装,适合需要把 zsh 所在目录加入 PATH 或传给其他启动逻辑的地方。

调用图:调用 1 个内部函数(bundled_zsh_path)。

CodexPackageLayout::from_exe185–192 ↗
fn from_exe(exe_path: &Path) -> Option<Self>

作用:判断一个可执行文件是否位于 Codex 的标准包结构里。

数据流:输入是程序路径;它先把路径规范化成真实的绝对路径,再看程序所在目录是不是名为 bin;如果是,就继续尝试从这个 bin 目录还原整个包布局;否则返回 None。

调用关系:InstallContext::from_exe_with_codex_home 会用它先识别包结构。它把“bin 目录是否合法”的检查交给 CodexPackageLayout::from_package_bin_dir。

调用图:调用 1 个内部函数(canonical_absolute_path);外部调用 2 个(new, from_package_bin_dir)。

CodexPackageLayout::from_package_bin_dir194–206 ↗
fn from_package_bin_dir(bin_dir: AbsolutePathBuf) -> Option<Self>

作用:从一个 bin 目录推断整个 Codex 包的根目录和资源目录。

数据流:输入是已经确认的 bin 目录;它取父目录作为包根目录,检查那里有没有 codex-package.json 这个元数据文件;如果有,就记录 bin、资源目录、PATH 目录等存在的部分。

调用关系:CodexPackageLayout::from_exe 在确认程序位于 bin 目录后会调用它。它使用 existing_dir 来保证只记录真实存在的目录。

调用图:调用 2 个内部函数(existing_dir, parent)。

install_method_from_exe209–225 ↗
fn install_method_from_exe(
    exe_path: &Path,
    codex_home: Option<&Path>,
    package_layout: Option<&CodexPackageLayout>,
    is_macos: bool,
) -> InstallMethod

作用:根据程序路径判断安装方式,主要区分独立安装包、macOS Homebrew 和其他来源。

数据流:输入是程序路径、Codex 家目录、已识别的包布局、是否为 macOS;它先尝试按独立包规则识别;如果不符合,再在 macOS 上检查路径是否位于 Homebrew 常见前缀;最后输出对应的 InstallMethod。

调用关系:InstallContext::from_exe_with_codex_home 在没有 npm/bun 明确标记时调用它。它会先把独立包判断交给 standalone_install_method。

调用图:调用 1 个内部函数(standalone_install_method);被 1 处调用(from_exe_with_codex_home);外部调用 1 个(starts_with)。

standalone_install_method227–252 ↗
fn standalone_install_method(
    exe_path: &Path,
    codex_home: Option<&Path>,
    package_layout: Option<&CodexPackageLayout>,
) -> Option<InstallMethod>

作用:判断当前程序是否来自 Codex 自己管理的独立发布包。

数据流:输入是程序路径、Codex 家目录、可选包布局;它把 Codex 家目录和发布目录转成真实绝对路径,然后检查发布目录是否在 packages/standalone/releases 下面;符合就返回 Standalone,并带上资源目录和平台信息。

调用关系:install_method_from_exe 会优先调用它,因为独立安装包需要特殊处理自带资源。它用 canonical_absolute_path 规范路径,用 standalone_platform 标明 Unix 或 Windows。

调用图:调用 2 个内部函数(canonical_absolute_path, standalone_platform);被 1 处调用(install_method_from_exe)。

canonical_absolute_path254–257 ↗
fn canonical_absolute_path(path: &Path) -> Option<AbsolutePathBuf>

作用:把一个路径变成可靠的真实绝对路径。

数据流:输入是任意路径;它调用系统能力解析符号链接、相对路径等,再确认结果确实是绝对路径;成功就返回 AbsolutePathBuf,失败就返回 None。

调用关系:包布局识别和独立安装包识别都会用它。它像是在判断前先把地址写成标准格式,避免因为软链接或相对路径看错位置。

调用图:调用 1 个内部函数(from_absolute_path);被 2 处调用(from_exe, standalone_install_method);外部调用 1 个(canonicalize)。

standalone_platform259–265 ↗
fn standalone_platform() -> StandalonePlatform

作用:告诉独立安装包当前运行平台是 Windows 还是 Unix 类系统。

数据流:它不接收外部输入,只读取编译时的目标系统信息;Windows 返回 StandalonePlatform::Windows,其他系统返回 StandalonePlatform::Unix。

调用关系:standalone_install_method 在生成 Standalone 安装方式时调用它,用来记录这个独立包对应的平台类别。

调用图:被 1 处调用(standalone_install_method);外部调用 1 个(cfg!)。

existing_dir267–269 ↗
fn existing_dir(path: AbsolutePathBuf) -> Option<AbsolutePathBuf>

作用:只有当路径确实是目录时,才把它留下来。

数据流:输入是一个绝对路径;它检查这个路径是否为目录;是目录就原样返回,不是目录就返回 None。

调用关系:CodexPackageLayout::from_package_bin_dir 用它检查 codex-resources 和 codex-path。这样可以避免把不存在的路径或普通文件误当成目录。

调用图:被 1 处调用(from_package_bin_dir);外部调用 1 个(is_dir)。

default_rg_command271–277 ↗
fn default_rg_command() -> PathBuf

作用:给出系统默认的 rg 命令名。

数据流:它根据编译目标判断系统;Windows 返回 rg.exe,其他系统返回 rg;输出是一个可用于执行命令或拼路径的 PathBuf。

调用关系:InstallContext::rg_command 用它做候选文件名和兜底命令。多项测试也用它保证测试里的文件名和真实逻辑一致。

调用图:被 6 处调用(rg_command, bundled_file_lookups_ignore_directories, detects_package_layout_independently_from_install_method, detects_standalone_install_from_release_layout, npm_managed_package_keeps_package_layout, standalone_package_layout_keeps_standalone_install_method);外部调用 2 个(from, cfg!)。

zsh_resource_path279–281 ↗
fn zsh_resource_path() -> PathBuf

作用:拼出包内 zsh 程序的相对路径。

数据流:它不读取外部输入;固定生成 zsh/bin/zsh 这个路径;输出给资源查找函数使用。

调用关系:bundled_zsh_path 用它知道应该找哪个资源文件。相关测试也用它创建同样位置的假 zsh 文件。

调用图:被 2 处调用(bundled_zsh_path, detects_package_layout_independently_from_install_method);外部调用 1 个(from)。

tests::detects_standalone_install_from_release_layout292–331 ↗
fn detects_standalone_install_from_release_layout() -> std::io::Result<()>

作用:测试旧式独立安装包目录能被正确识别,并且能找到资源文件。

数据流:它创建一个临时 Codex 家目录和 releases 下的假发布目录,写入假 codex、rg 和测试资源;然后调用 from_exe_with_codex_home;最后断言安装方式是 Standalone,资源路径也能找到。

调用关系:这是 standalone_install_method 和 bundled_resource 的端到端验证。它还用 default_rg_command 保证 Windows 和 Unix 文件名都正确。

调用图:调用 3 个内部函数(from_exe_with_codex_home, default_rg_command, from_absolute_path);外部调用 5 个(assert_eq!, cfg!, create_dir_all, write, tempdir)。

tests::standalone_rg_falls_back_when_resources_are_missing334–352 ↗
fn standalone_rg_falls_back_when_resources_are_missing() -> std::io::Result<()>

作用:测试独立安装包没有资源目录时,rg 会退回系统默认命令。

数据流:它创建一个没有 codex-resources 的假独立发布目录,只放入假 codex;生成安装上下文后调用 rg_command;最后确认返回的是默认 rg/rg.exe,而不是不存在的包内路径。

调用关系:它验证 InstallContext::rg_command 的兜底行为,防止资源缺失时搜索功能拿到坏路径。

调用图:调用 1 个内部函数(from_exe_with_codex_home);外部调用 5 个(assert_eq!, cfg!, create_dir_all, write, tempdir)。

tests::detects_package_layout_independently_from_install_method355–424 ↗
fn detects_package_layout_independently_from_install_method() -> std::io::Result<()>

作用:测试标准包布局即使不属于独立安装包,也能被识别出来。

数据流:它创建含 bin、codex-resources、codex-path、codex-package.json 的临时包;写入假 codex、rg、资源和非 Windows 上的 zsh;然后生成上下文;最后确认安装方式是 Other,但 package_layout 存在,资源和 rg 能从包里找到。

调用关系:它覆盖 CodexPackageLayout::from_exe、rg_command、bundled_resource、bundled_zsh_path 和 bundled_zsh_bin_dir 的配合方式。

调用图:调用 4 个内部函数(from_exe_with_codex_home, default_rg_command, zsh_resource_path, from_absolute_path);外部调用 5 个(assert_eq!, cfg!, create_dir_all, write, tempdir)。

tests::standalone_package_layout_keeps_standalone_install_method427–484 ↗
fn standalone_package_layout_keeps_standalone_install_method() -> std::io::Result<()>

作用:测试新式标准包布局放在独立发布目录里时,既识别包布局,也保留 Standalone 安装方式。

数据流:它在 Codex 家目录的 standalone/releases 下创建标准包结构;写入元数据、假 codex、资源和 rg;生成上下文后断言 method 是 Standalone,同时 package_layout 也完整存在;再确认 rg 和资源优先来自包目录。

调用关系:它验证 package_layout 不会覆盖安装方式判断,而是和 Standalone 信息一起存在。

调用图:调用 3 个内部函数(from_exe_with_codex_home, default_rg_command, from_absolute_path);外部调用 5 个(assert_eq!, cfg!, create_dir_all, write, tempdir)。

tests::npm_managed_package_keeps_package_layout487–515 ↗
fn npm_managed_package_keeps_package_layout() -> std::io::Result<()>

作用:测试 npm 明确管理时,安装方式应是 Npm,但标准包布局仍然保留。

数据流:它创建一个有 bin、codex-path 和元数据的临时包,并把 managed_by_npm 设为 true;生成上下文后确认 method 是 Npm,package_layout 存在,rg 从 codex-path 找到。

调用关系:它验证 InstallContext::from_exe_with_codex_home 的优先级:npm 标记优先决定安装方式,但不会妨碍包布局识别。

调用图:调用 3 个内部函数(from_exe_with_codex_home, default_rg_command, from_absolute_path);外部调用 6 个(assert!, assert_eq!, cfg!, create_dir_all, write, tempdir)。

tests::standalone_package_rg_falls_back_when_codex_path_is_missing518–535 ↗
fn standalone_package_rg_falls_back_when_codex_path_is_missing() -> std::io::Result<()>

作用:测试标准包里没有 codex-path 目录时,rg 会退回系统默认命令。

数据流:它创建只有 bin 和元数据的临时包,写入假 codex;生成上下文后调用 rg_command;最后确认返回默认 rg/rg.exe。

调用关系:它验证 rg_command 不会因为识别到 package_layout 就盲目使用不存在的 codex-path。

调用图:调用 1 个内部函数(from_exe_with_codex_home);外部调用 5 个(assert_eq!, cfg!, create_dir_all, write, tempdir)。

tests::bundled_file_lookups_ignore_directories538–560 ↗
fn bundled_file_lookups_ignore_directories() -> std::io::Result<()>

作用:测试资源查找只接受文件,不会把同名目录误认为可执行文件或资源。

数据流:它创建一个标准包,但把资源名和 rg 名都创建成目录而不是文件;生成上下文后确认 rg_command 退回默认命令,bundled_resource 返回 None。

调用关系:它保护 bundled_resource 和 rg_command 的安全边界,避免后续代码拿目录当文件运行或读取。

调用图:调用 2 个内部函数(from_exe_with_codex_home, default_rg_command);外部调用 5 个(assert_eq!, cfg!, create_dir_all, write, tempdir)。

tests::npm_and_bun_take_precedence563–593 ↗
fn npm_and_bun_take_precedence()

作用:测试 npm 和 bun 的环境标记会优先于路径猜测。

数据流:它分别用同一个普通路径模拟 npm 管理和 bun 管理;调用 from_exe_with_codex_home;最后确认结果分别是 Npm 和 Bun,并且没有包布局。

调用关系:它验证 InstallContext::from_exe_with_codex_home 的判断顺序,确保明确的管理器标记不会被后面的路径规则覆盖。

调用图:调用 1 个内部函数(from_exe_with_codex_home);外部调用 2 个(new, assert_eq!)。

tests::brew_is_detected_on_macos_prefixes596–611 ↗
fn brew_is_detected_on_macos_prefixes()

作用:测试 macOS 上 Homebrew 常见安装路径能被识别为 Brew。

数据流:它传入一个 /opt/homebrew/bin/codex 路径,并声明当前系统按 macOS 处理;生成上下文后确认安装方式是 Brew。

调用关系:它验证 install_method_from_exe 中的 Homebrew 路径规则,确保 macOS 用户的安装来源能正确显示和处理。

调用图:调用 1 个内部函数(from_exe_with_codex_home);外部调用 2 个(new, assert_eq!)。

windows-sandbox-rs/src/helper_materialization.rs源码 ↗
domain_logicstartup / sandbox launch preparation

可以把这个文件理解成“辅助程序发放员”。Codex 运行沙箱任务时需要一个叫 command-runner 的小执行文件。这个文件会先找到它原本放在哪里:可能就在当前程序旁边,也可能在 codex-resources 资源目录里。找到后,它不会随便直接用,而是复制到 Codex home 下面的 .sandbox-bin 目录,并且文件名里加上版本号或开发版标记。这样新旧版本不会互相踩。复制前它会检查目标文件是不是已经够新:大小一样、修改时间不旧,就直接复用,省时间。真正复制时,它先写到临时文件,再改名成最终文件,像先把包裹放到中转箱确认无误后再上架,减少半成品文件被别人看到的风险。文件还带了一个内存缓存,避免同一次运行里反复查同一个 helper 路径。失败时也不会立刻崩掉,而是退回老办法找程序路径,并写日志说明。

函数细节28
HelperExecutable::file_name28–32 ↗
fn file_name(self) -> &'static str

作用:把某种辅助程序转换成它真实的文件名。现在只有 command-runner,所以会给出 codex-command-runner.exe。

数据流:进去的是一个 HelperExecutable 类型的值 → 它按类型匹配到固定文件名 → 出来的是可用于查找和复制的字符串文件名。

调用关系:legacy_lookup、sibling_source_path 和 materialized_file_name 都靠它知道该找哪个 .exe 文件;它是后面所有路径拼接的起点。

调用图:被 3 处调用(legacy_lookup, materialized_file_name, sibling_source_path)。

HelperExecutable::label34–38 ↗
fn label(self) -> &'static str

作用:给辅助程序取一个适合写日志的人类可读名字。它不是文件名,而是日志里更短、更好懂的标签。

数据流:进去的是辅助程序种类 → 它把种类翻译成类似 command-runner 的标签 → 出来的是给日志消息用的文字。

调用关系:复制和启动路径解析时会用这个标签写日志,让人排查问题时知道是哪一个 helper 出了事。

helper_bin_dir49–51 ↗
fn helper_bin_dir(codex_home: &Path) -> PathBuf

作用:算出辅助程序应该被放到哪个目录。也就是 Codex home 下面专门给沙箱 helper 用的 .sandbox-bin 目录。

数据流:进去的是 codex_home 路径 → 它交给 sandbox_bin_dir 统一计算 → 出来的是 helper 的共享存放目录路径。

调用关系:helper_destination_for_source 和 resolve_current_exe_for_launch 用它决定复制目标;其他沙箱读权限收集逻辑也会用它,把这个目录加入允许读取的范围。

调用图:被 7 处调用(helper_destination_for_source, resolve_current_exe_for_launch, copy_runner_into_shared_bin_dir, gather_helper_read_roots, build_payload_roots_preserves_helper_roots_when_read_override_is_provided, build_payload_roots_replaces_full_read_policy_when_read_override_is_provided, gather_read_roots_includes_helper_bin_dir);外部调用 1 个(sandbox_bin_dir)。

legacy_lookup53–60 ↗
fn legacy_lookup(kind: HelperExecutable) -> PathBuf

作用:这是复制 helper 失败时的老式兜底找法。它尽量在当前程序旁边或资源目录里找 helper,找不到就只返回裸文件名,让系统按旧规则去找。

数据流:进去的是 helper 种类 → 它读取当前正在运行的 exe 路径,并用 helper 文件名在附近查找 → 出来的是一个可尝试启动的路径,可能是完整路径,也可能只是文件名。

调用关系:resolve_helper_for_launch 在新复制流程失败时调用它,保证 helper 复制出问题时仍有机会继续启动,而不是马上终止。

调用图:调用 2 个内部函数(file_name, bundled_executable_path_for_exe);被 1 处调用(resolve_helper_for_launch);外部调用 2 个(from, current_exe)。

resolve_helper_for_launch62–92 ↗
fn resolve_helper_for_launch(
    kind: HelperExecutable,
    codex_home: &Path,
    log_dir: Option<&Path>,
) -> PathBuf

作用:这是外部启动 helper 前最常用的入口。它负责拿到最终应该运行的 helper 路径,并把成功或失败原因写进日志。

数据流:进去的是 helper 种类、Codex home、可选日志目录 → 它先尝试复制并使用 .sandbox-bin 里的版本;如果失败,就走 legacy_lookup 兜底 → 出来的是一个用于启动进程的 PathBuf。

调用关系:find_runner_exe 会在需要 command-runner 路径时调用它;它把主要工作交给 copy_helper_if_needed,失败时再交给 legacy_lookup。

调用图:调用 3 个内部函数(copy_helper_if_needed, legacy_lookup, log_note);被 1 处调用(find_runner_exe);外部调用 1 个(format!)。

resolve_current_exe_for_launch94–117 ↗
fn resolve_current_exe_for_launch(codex_home: &Path, fallback_executable: &str) -> PathBuf

作用:把当前正在运行的程序本身也复制到沙箱工具目录里,供后续启动使用。它适合需要“从安全目录启动当前 exe 的副本”的场景。

数据流:进去的是 codex_home 和一个失败时用的备用可执行文件名 → 它读取当前 exe 路径,算出 .sandbox-bin 下的目标路径,再按需复制 → 成功出来目标路径;失败则写日志并返回原始路径或备用名。

调用关系:它复用 helper_bin_dir 和 copy_from_source_if_needed 这套复制机制;失败日志会写到 sandbox_dir 对应的沙箱日志位置。

调用图:调用 3 个内部函数(copy_from_source_if_needed, helper_bin_dir, log_note);外部调用 4 个(from, sandbox_dir, format!, current_exe)。

copy_helper_if_needed119–165 ↗
fn copy_helper_if_needed(
    kind: HelperExecutable,
    codex_home: &Path,
    log_dir: Option<&Path>,
) -> Result<PathBuf>

作用:这是“确认 helper 已经在正确地方”的核心函数。它会找源文件、算目标文件名、检查是否要复制,并记住结果。

数据流:进去的是 helper 种类、Codex home、日志目录 → 它先查内存缓存;没有缓存就找源 helper,按版本生成目标名,再检查复制或复用 → 出来的是目标 helper 路径,同时可能新建目录、复制文件、更新缓存和写日志。

调用关系:resolve_helper_for_launch 把主要工作交给它;它内部依次用 cached_helper_path、sibling_source_path、helper_destination_for_source、copy_from_source_if_needed 和 store_helper_path 完成整条链路。

调用图:调用 6 个内部函数(cached_helper_path, copy_from_source_if_needed, helper_destination_for_source, sibling_source_path, store_helper_path, log_note);被 1 处调用(resolve_helper_for_launch);外部调用 1 个(format!)。

cached_helper_path167–171 ↗
fn cached_helper_path(cache_key: &str) -> Option<PathBuf>

作用:从内存缓存里查某个 helper 是否已经解析过路径。这样同一次运行里不用反复碰磁盘。

数据流:进去的是缓存 key 字符串 → 它拿到全局缓存并加锁读取;加锁失败或找不到就返回空 → 出来的是已保存路径的副本,或者没有结果。

调用关系:copy_helper_if_needed 一开始会调用它;如果命中缓存,后面的查找和复制步骤都会被跳过。这里的互斥锁是一把锁,防止多个任务同时改同一张缓存表。

调用图:被 1 处调用(copy_helper_if_needed)。

store_helper_path173–178 ↗
fn store_helper_path(cache_key: String, path: PathBuf)

作用:把已经成功解析出的 helper 路径放进内存缓存。下次同样请求可以直接复用。

数据流:进去的是缓存 key 和路径 → 它拿到全局缓存并尝试加锁写入 → 之后缓存表里多了一条 key 到路径的记录;如果锁失败,就静默放弃缓存,不影响主流程。

调用关系:copy_helper_if_needed 在成功复制或确认复用后调用它,为后续启动节省重复检查成本。

调用图:被 1 处调用(copy_helper_if_needed)。

sibling_source_path180–188 ↗
fn sibling_source_path(kind: HelperExecutable) -> Result<PathBuf>

作用:找到 helper 的原始来源文件。它以当前正在运行的 Codex 程序为参照,在旁边或资源目录里寻找对应的 helper exe。

数据流:进去的是 helper 种类 → 它读取当前 exe 路径,拿到 helper 文件名,然后调用 bundled_executable_path_for_exe 查找 → 找到就返回源路径,找不到就返回带说明的错误。

调用关系:copy_helper_if_needed 需要先靠它确定“从哪里复制”;它把具体搜索规则交给 bundled_executable_path_for_exe。

调用图:调用 2 个内部函数(file_name, bundled_executable_path_for_exe);被 1 处调用(copy_helper_if_needed);外部调用 1 个(current_exe)。

bundled_executable_path_for_exe190–208 ↗
fn bundled_executable_path_for_exe(exe: &Path, file_name: &str) -> Option<PathBuf>

作用:按打包布局规则寻找某个随程序一起发布的 exe 文件。它兼容几种常见摆放方式。

数据流:进去的是当前 exe 路径和要找的文件名 → 它先查当前 exe 同目录;如果当前目录叫 bin,再优先查上级目录的 codex-resources;最后查当前目录下的 codex-resources → 出来的是找到的文件路径,或没有结果。

调用关系:legacy_lookup 和 sibling_source_path 都用它找 helper;测试也覆盖了直接同目录、资源目录、bin 包布局以及优先级,保证打包方式变化时不容易找错。

调用图:被 7 处调用(legacy_lookup, sibling_source_path, helper_source_lookup_checks_package_resource_dir_for_bin_exe, helper_source_lookup_checks_resource_dir, helper_source_lookup_prefers_direct_sibling_over_resource_dir, helper_source_lookup_prefers_package_resource_dir_over_bin_resource_dir, find_setup_exe_for_current_exe);外部调用 2 个(new, parent)。

helper_destination_for_source210–218 ↗
fn helper_destination_for_source(
    kind: HelperExecutable,
    codex_home: &Path,
    source: &Path,
) -> Result<PathBuf>

作用:根据源 helper 文件算出它应该复制到的最终目标路径。目标名会带版本后缀,避免不同版本互相覆盖。

数据流:进去的是 helper 种类、Codex home、源文件路径 → 它先算版本后缀,再生成带后缀的文件名,最后拼到 helper_bin_dir 下 → 出来的是完整目标路径。

调用关系:copy_helper_if_needed 找到源文件后会调用它;它把版本判断交给 helper_version_suffix,把文件名拼法交给 materialized_file_name。

调用图:调用 3 个内部函数(helper_bin_dir, helper_version_suffix, materialized_file_name);被 1 处调用(copy_helper_if_needed)。

materialized_file_name220–233 ↗
fn materialized_file_name(kind: HelperExecutable, suffix: &str) -> String

作用:把原始 helper 文件名改造成带后缀的目标文件名。比如 codex-command-runner.exe 会变成 codex-command-runner-某版本.exe。

数据流:进去的是 helper 种类和后缀字符串 → 它拆出文件主名和扩展名,把后缀插在扩展名前 → 出来的是新的文件名字符串。

调用关系:helper_destination_for_source 用它生成最终复制文件名;相关测试确认后缀确实加在 .exe 前面,而不是加到扩展名后面。

调用图:调用 1 个内部函数(file_name);被 3 处调用(helper_destination_for_source, copy_runner_into_shared_bin_dir, materialized_file_name_adds_suffix_before_extension);外部调用 2 个(new, format!)。

helper_version_suffix235–242 ↗
fn helper_version_suffix(source: &Path) -> Result<String>

作用:决定 helper 目标文件名里的版本后缀。正式构建用包版本号,开发构建则用文件大小和修改时间凑出一个变化标记。

数据流:进去的是源 helper 路径 → 它读取编译时的 CARGO_PKG_VERSION;如果不是 0.0.0 就直接用版本号,否则调用 dev_build_suffix → 出来的是一个可放进文件名的后缀。

调用关系:helper_destination_for_source 靠它让目标文件名和版本绑定;测试会检查正式版和开发版两种路径是否按预期工作。

调用图:调用 1 个内部函数(dev_build_suffix);被 3 处调用(helper_destination_for_source, copy_runner_into_shared_bin_dir, helper_version_suffix_uses_cli_version_or_dev_build_metadata);外部调用 1 个(env!)。

dev_build_suffix244–254 ↗
fn dev_build_suffix(source: &Path) -> Result<String>

作用:给开发版 helper 生成一个临时版本标记。开发时版本号固定不变,所以它用文件大小和修改时间来区分变化。

数据流:进去的是源文件路径 → 它读取文件元数据,包括大小和修改时间,再把修改时间转换成 Unix 时间戳,也就是从 1970 年开始算的秒数 → 出来的是类似 大小-时间 的字符串。

调用关系:helper_version_suffix 只在开发构建版本号等于 0.0.0 时调用它,用来避免开发过程中旧 helper 被误复用。

调用图:被 1 处调用(helper_version_suffix);外部调用 2 个(format!, metadata)。

copy_from_source_if_needed256–328 ↗
fn copy_from_source_if_needed(source: &Path, destination: &Path) -> Result<CopyOutcome>

作用:把一个源文件复制到目标位置,但只有目标不够新时才复制。它是这个文件里真正碰磁盘搬文件的核心。

数据流:进去的是源路径和目标路径 → 它先用 destination_is_fresh 判断目标是否已经可用;如果不可用,就创建目标目录,写入同目录临时文件,刷新到磁盘,删除旧目标,再把临时文件改名成目标 → 出来的是 Reused 或 ReCopied,同时磁盘上目标文件会被保留或更新。

调用关系:copy_helper_if_needed 和 resolve_current_exe_for_launch 都靠它完成安全复制;测试会验证目标缺失时会复制、目标已新时会复用。它还处理一种竞争情况:如果改名失败但发现别人已经放好了新文件,就接受复用结果。

调用图:调用 1 个内部函数(destination_is_fresh);被 5 处调用(copy_helper_if_needed, resolve_current_exe_for_launch, copy_from_source_if_needed_copies_missing_destination, copy_from_source_if_needed_reuses_fresh_destination, copy_runner_into_shared_bin_dir);外部调用 9 个(new_in, exists, parent, open, new, create_dir_all, remove_file, rename, copy)。

destination_is_fresh330–355 ↗
fn destination_is_fresh(source: &Path, destination: &Path) -> Result<bool>

作用:判断目标文件是不是已经足够新,可以不用重新复制。它用文件大小和修改时间做快速判断。

数据流:进去的是源路径和目标路径 → 它读取两个文件的元数据;目标不存在就返回 false;大小不同返回 false;大小相同再比较修改时间 → 出来的是 true 或 false,表示能不能复用目标文件。

调用关系:copy_from_source_if_needed 在复制前和改名失败后的兜底检查都会调用它;测试专门验证修改时间更新后才算新。

调用图:被 1 处调用(copy_from_source_if_needed);外部调用 1 个(metadata)。

tests::copy_from_source_if_needed_copies_missing_destination378–392 ↗
fn copy_from_source_if_needed_copies_missing_destination()

作用:测试目标文件不存在时,复制函数真的会把源文件内容复制过去。

数据流:进去的是测试临时目录里造出来的 source.exe 和目标路径 → 它写入源内容,调用 copy_from_source_if_needed → 最后检查结果是 ReCopied,并且目标文件内容和源一致。

调用关系:这是 copy_from_source_if_needed 的基础行为测试,保证第一次准备 helper 时不会漏复制。

调用图:调用 1 个内部函数(copy_from_source_if_needed);外部调用 3 个(new, assert_eq!, write)。

tests::destination_is_fresh_uses_size_and_mtime395–407 ↗
fn destination_is_fresh_uses_size_and_mtime()

作用:测试“是否新鲜”的判断确实看大小和修改时间,而不只是看文件是否存在。

数据流:进去的是两个同样大小的临时文件 → 它先让目标比源旧,确认结果为不新;再重写目标,让它变新 → 最后确认 destination_is_fresh 返回 true。

调用关系:它直接验证 destination_is_fresh 的核心规则,间接保护 copy_from_source_if_needed 的复用判断不出错。

调用图:外部调用 5 个(new, assert!, write, sleep, from_secs)。

tests::copy_from_source_if_needed_reuses_fresh_destination410–425 ↗
fn copy_from_source_if_needed_reuses_fresh_destination()

作用:测试目标文件已经是最新时,不会重复复制。

数据流:进去的是临时源文件和目标路径 → 它先复制一次,再调用 copy_from_source_if_needed 第二次 → 最后检查第二次结果是 Reused,目标内容仍然正确。

调用关系:它保护 copy_from_source_if_needed 的省事路径,避免每次启动都无意义地重写 helper。

调用图:调用 1 个内部函数(copy_from_source_if_needed);外部调用 3 个(new, assert_eq!, write)。

tests::helper_bin_dir_is_under_sandbox_bin428–435 ↗
fn helper_bin_dir_is_under_sandbox_bin()

作用:测试 helper 存放目录确实在 Codex home 下面的 .sandbox-bin。

数据流:进去的是一个示例 Codex home 路径 → 它调用 helper_bin_dir → 出来路径必须等于预期的 .sandbox-bin 路径。

调用关系:它验证 helper_bin_dir 的目录约定,防止未来改动把 helper 放到沙箱权限规则之外。

调用图:外部调用 2 个(new, assert_eq!)。

tests::copy_runner_into_shared_bin_dir438–459 ↗
fn copy_runner_into_shared_bin_dir()

作用:测试 command-runner 可以按版本化文件名复制到共享 helper 目录。

数据流:进去的是临时 Codex home、临时源目录和 runner 文件 → 它算版本后缀,生成目标文件名,再调用 copy_from_source_if_needed → 最后检查复制结果和文件内容。

调用关系:它把 helper_bin_dir、helper_version_suffix、materialized_file_name 和 copy_from_source_if_needed 串起来测,模拟真实 helper 落盘流程的一小段。

调用图:调用 4 个内部函数(copy_from_source_if_needed, helper_bin_dir, helper_version_suffix, materialized_file_name);外部调用 4 个(new, assert_eq!, create_dir_all, write)。

tests::helper_source_lookup_checks_resource_dir462–477 ↗
fn helper_source_lookup_checks_resource_dir()

作用:测试 helper 放在当前程序旁边的 codex-resources 目录时能被找到。

数据流:进去的是临时 release 目录、codex.exe 和资源目录里的 helper → 它调用 bundled_executable_path_for_exe → 出来路径必须指向资源目录里的 helper。

调用关系:它覆盖 bundled_executable_path_for_exe 的普通资源目录查找规则,保证打包时 helper 不和主程序放同目录也能找到。

调用图:调用 1 个内部函数(bundled_executable_path_for_exe);外部调用 4 个(new, assert_eq!, create_dir_all, write)。

tests::helper_source_lookup_checks_package_resource_dir_for_bin_exe480–497 ↗
fn helper_source_lookup_checks_package_resource_dir_for_bin_exe()

作用:测试当主程序在 package/bin 目录里时,helper 可以从 package/codex-resources 找到。

数据流:进去的是临时 package/bin/codex.exe 和 package/codex-resources/helper → 它调用 bundled_executable_path_for_exe → 出来路径必须是包级资源目录里的 helper。

调用关系:它保护 bin 目录打包布局的查找规则,避免安装包结构变化导致 helper 找不到。

调用图:调用 1 个内部函数(bundled_executable_path_for_exe);外部调用 4 个(new, assert_eq!, create_dir_all, write)。

tests::helper_source_lookup_prefers_package_resource_dir_over_bin_resource_dir500–520 ↗
fn helper_source_lookup_prefers_package_resource_dir_over_bin_resource_dir()

作用:测试当包级资源目录和 bin 下面的资源目录都有 helper 时,优先选择包级资源目录。

数据流:进去的是两个位置各有一个 helper 的临时包结构 → 它调用 bundled_executable_path_for_exe → 出来必须是 package/codex-resources 里的那个。

调用关系:它验证 bundled_executable_path_for_exe 的优先级,防止同时存在多个 helper 时选错版本。

调用图:调用 1 个内部函数(bundled_executable_path_for_exe);外部调用 4 个(new, assert_eq!, create_dir_all, write)。

tests::helper_source_lookup_prefers_direct_sibling_over_resource_dir523–540 ↗
fn helper_source_lookup_prefers_direct_sibling_over_resource_dir()

作用:测试如果 helper 直接放在当前程序同目录,就优先用这个同目录文件。

数据流:进去的是同目录 helper 和资源目录 helper 都存在的临时 release 目录 → 它调用 bundled_executable_path_for_exe → 出来必须是同目录 helper。

调用关系:它保护最直接、最传统的查找方式优先级;legacy_lookup 和 sibling_source_path 都受这个规则影响。

调用图:调用 1 个内部函数(bundled_executable_path_for_exe);外部调用 4 个(new, assert_eq!, create_dir_all, write)。

tests::helper_version_suffix_uses_cli_version_or_dev_build_metadata543–554 ↗
fn helper_version_suffix_uses_cli_version_or_dev_build_metadata()

作用:测试版本后缀的选择规则:正式版用包版本,开发版用文件元数据。

数据流:进去的是临时源 helper 文件 → 它调用 helper_version_suffix,并读取编译时版本号 → 最后根据版本号是否为 0.0.0,检查结果等于 dev_build_suffix 或真实包版本。

调用关系:它保护 helper_destination_for_source 依赖的版本命名逻辑,避免开发版和正式版的缓存行为混乱。

调用图:调用 1 个内部函数(helper_version_suffix);外部调用 4 个(new, assert_eq!, env!, write)。

tests::materialized_file_name_adds_suffix_before_extension557–561 ↗
fn materialized_file_name_adds_suffix_before_extension()

作用:测试生成目标文件名时,后缀会插在 .exe 扩展名前。

数据流:进去的是 CommandRunner 和 test-suffix → 它调用 materialized_file_name → 出来必须是 codex-command-runner-test-suffix.exe。

调用关系:它直接保护 materialized_file_name 的文件名格式;这个格式会被 helper_destination_for_source 用来决定实际落盘文件名。

调用图:调用 1 个内部函数(materialized_file_name);外部调用 1 个(assert_eq!)。

执行环境建模

这些文件定义如何为本地执行上下文提供、选择、验证和捕获启动环境。

core/src/shell_snapshot.rs源码 ↗
domain_logicrequest handling / background cleanup

很多人会在自己的终端里设置别名、函数、环境变量。如果程序另外启动一个 shell,却不带上这些设置,命令结果就可能和用户自己敲的不一样。这个文件做的事就像给当前终端拍一张“环境快照”:先运行一段适合 bash、zsh 或 sh 的小脚本,把可恢复的设置打印出来;再去掉前面的杂音;写到 Codex 家目录里的临时文件;然后立刻试着加载一次,确认这个快照脚本真的能用;最后改名成正式文件。快照文件对象销毁时会自动删文件,避免残留。它还会顺手清理太老、找不到对应会话的旧快照。远程环境会跳过,因为本地文件路径和远程 shell 不一定对应。

函数细节21
ShellSnapshot::new49–63 ↗
fn new(
        codex_home: AbsolutePathBuf,
        session_id: ThreadId,
        session_telemetry: SessionTelemetry,
        state_db: Option<StateDbHandle>,
    ) -> Self

作用:创建一个启用状态的 shell 快照器。调用方把 Codex 的家目录、当前会话编号、统计上报工具和可选的状态数据库交给它,之后它就知道快照该放哪、该归到哪个会话名下。

数据流:进去的是存储目录、会话 ID、遥测对象和可选数据库句柄 → 它把这些信息包进一个共享配置里 → 出来一个带配置的 ShellSnapshot,后面可以用来生成快照。

调用关系:它通常在会话或上下文初始化时被调用,是整个快照功能的开关入口之一。它只准备配置,不真正写文件;真正干活要等 ShellSnapshot::build 再往下交给 ShellSnapshot::build_for_cwd 和 ShellSnapshot::try_create。

调用图:被 1 处调用(new);外部调用 1 个(new)。

ShellSnapshot::disabled65–67 ↗
fn disabled() -> Self

作用:创建一个关闭状态的 shell 快照器。测试、特殊配置或不想保存 shell 环境时会用它,让后续构建快照的流程直接跳过。

数据流:进去没有参数 → 它把内部配置设为空 → 出来一个 ShellSnapshot,但它没有创建快照所需的信息,所以 build 时会返回空结果。

调用关系:它被多个环境解析、测试和会话创建流程使用,用来明确表示“不拍快照”。后面即使有人调用 ShellSnapshot::build,也会因为没有配置而立刻结束。

调用图:被 7 处调用(latest_environment_update_wins_while_previous_resolution_is_pending, local_environment_uses_configured_shell, resolve_turn_environments, new, make_session_and_context, make_session_and_context_with_auth_config_home_and_rx, resolved_environments_for_configuration)。

ShellSnapshot::build69–83 ↗
async fn build(
        self,
        environment: TurnEnvironment,
    ) -> Option<Arc<ShellSnapshotFile>>

作用:按当前这一次操作的环境,决定要不要真的生成 shell 快照。它会避开远程环境,也会要求当前环境里确实有 shell 和本地工作目录。

数据流:进去的是 TurnEnvironment,也就是本轮命令的环境信息 → 它读取是否远程、使用哪个 shell、当前目录能否转成本地绝对路径 → 如果条件都满足,就把配置、目录和 shell 交给 build_for_cwd;否则输出 None。

调用关系:它是外部最常用的快照生成入口。它自己只做筛选和取信息,真正创建文件、记录统计的工作交给 ShellSnapshot::build_for_cwd。

调用图:调用 1 个内部函数(cwd);外部调用 2 个(clone, build_for_cwd)。

ShellSnapshot::build_for_cwd85–116 ↗
async fn build_for_cwd(
        config: Arc<ShellSnapshotConfig>,
        cwd: AbsolutePathBuf,
        shell: Shell,
    ) -> Option<Arc<ShellSnapshotFile>>

作用:围绕某个确定的工作目录创建快照,并记录这次快照是否成功、耗时多久。它像一个带计时器和日志的包装层。

数据流:进去的是共享配置、当前目录和 shell → 它开启 tracing 记录和遥测计时器,然后调用 try_create → 根据成功或失败记录统计标签 → 成功时输出共享的 ShellSnapshotFile,失败时输出 None。

调用关系:ShellSnapshot::build 在确认环境可用后会调用它。它把真正写快照文件的事交给 ShellSnapshot::try_create,同时负责把结果告诉遥测系统,方便后续观察快照功能是否稳定。

调用图:调用 1 个内部函数(try_create);外部调用 2 个(info_span!, vec!)。

ShellSnapshot::try_create118–178 ↗
async fn try_create(
        codex_home: &AbsolutePathBuf,
        session_id: ThreadId,
        session_cwd: &AbsolutePathBuf,
        shell: &Shell,
        state_db: Option<StateDbHandle>,
    ) ->

作用:真正创建一个可用的快照文件。它负责选文件名、写临时文件、验证、改成正式文件,并启动旧文件清理。

数据流:进去的是 Codex 家目录、会话 ID、当前目录、shell 和可选状态数据库 → 它按 shell 类型决定扩展名,用当前时间生成唯一文件名,后台清理旧快照;然后调用 write_shell_snapshot 写临时文件,调用 validate_snapshot 验证能否加载,最后把临时文件重命名为正式文件 → 成功输出 ShellSnapshotFile,失败输出简单的失败原因字符串,并尽量删除临时文件。

调用关系:它由 ShellSnapshot::build_for_cwd 调用,也是测试重点覆盖的核心函数。它串起 cleanup_stale_snapshots、write_shell_snapshot、validate_snapshot 和 remove_snapshot_file,是快照创建流水线的主干。

调用图:调用 5 个内部函数(cleanup_stale_snapshots, remove_snapshot_file, validate_snapshot, write_shell_snapshot, join);被 3 处调用(build_for_cwd, try_create_creates_and_deletes_snapshot_file, try_create_uses_distinct_generation_paths);外部调用 8 个(now, format!, rename, spawn, error!, info!, warn!, clone)。

ShellSnapshotFile::path182–184 ↗
fn path(&self) -> AbsolutePathBuf

作用:拿到快照文件的路径。调用方需要把这个路径传给后续 shell,让它加载这份环境快照。

数据流:进去的是 ShellSnapshotFile 自身 → 它复制内部保存的绝对路径 → 出来一个 AbsolutePathBuf,原对象不变。

调用关系:它通常在快照创建成功之后被使用。ShellSnapshot::try_create 产出 ShellSnapshotFile,外部再通过这个方法知道文件在哪里。

调用图:外部调用 1 个(clone)。

ShellSnapshotFile::drop188–195 ↗
fn drop(&mut self)

作用:在快照文件对象不用时,自动删除对应的磁盘文件。这样临时快照不会一直堆在用户目录里。

数据流:进去的是即将被销毁的 ShellSnapshotFile → 它读取保存的路径,尝试用同步文件删除接口删掉文件 → 成功就什么也不返回,失败只写警告日志,不阻止程序继续。

调用关系:这是 Rust 的 Drop 行为,也就是对象生命周期结束时自动触发。它和 ShellSnapshot::try_create 形成配套:创建时写文件,没人再持有时清文件。

调用图:外部调用 2 个(remove_file, warn!)。

write_shell_snapshot198–225 ↗
async fn write_shell_snapshot(
    shell_type: ShellType,
    output_path: &AbsolutePathBuf,
    cwd: &AbsolutePathBuf,
) -> Result<()>

作用:把当前 shell 的环境内容写成一个快照脚本文件。它负责从 shell 里抓内容、清掉开头杂音,并落盘保存。

数据流:进去的是 shell 类型、输出路径和当前目录 → 它先拒绝目前还不支持的 PowerShell 和 Cmd,再找到可执行 shell,调用 capture_snapshot 抓原始输出,调用 strip_snapshot_preamble 截到真正快照开始处,创建父目录并写文件 → 成功返回空结果,失败返回带上下文的错误。

调用关系:ShellSnapshot::try_create 调用它来生成临时快照文件。它把“从 shell 取得文本”的部分交给 capture_snapshot,把“清理输出”的部分交给 strip_snapshot_preamble。

调用图:调用 5 个内部函数(get_shell, capture_snapshot, strip_snapshot_preamble, display, parent);被 1 处调用(try_create);外部调用 3 个(bail!, create_dir_all, write)。

capture_snapshot227–236 ↗
async fn capture_snapshot(shell: &Shell, cwd: &AbsolutePathBuf) -> Result<String>

作用:根据 shell 类型选择合适的小脚本,并运行它来打印环境快照。不同 shell 的命令语法不一样,所以这里负责分流。

数据流:进去的是 Shell 和当前目录 → 它查看 shell_type,分别生成 zsh、bash、sh 或 PowerShell 的快照脚本,再交给 run_shell_script 执行;Cmd 直接报错 → 出来的是 shell 打印出的原始快照文本。

调用关系:它由 write_shell_snapshot 调用,是“写文件”之前的取数步骤。它会调用 zsh_snapshot_script、bash_snapshot_script、sh_snapshot_script 或 powershell_snapshot_script 来准备脚本文本。

调用图:调用 5 个内部函数(bash_snapshot_script, powershell_snapshot_script, run_shell_script, sh_snapshot_script, zsh_snapshot_script);被 1 处调用(write_shell_snapshot);外部调用 1 个(bail!)。

strip_snapshot_preamble238–245 ↗
fn strip_snapshot_preamble(snapshot: &str) -> Result<String>

作用:删掉快照输出前面可能混入的启动提示或配置文件输出,只保留真正的快照脚本。它通过一个固定标记来找开始位置。

数据流:进去的是 shell 打印出的完整文本 → 它查找 '# Snapshot file' 这个标记 → 找到就返回从标记开始的文本,找不到就报错,说明输出不符合预期。

调用关系:write_shell_snapshot 在 capture_snapshot 之后调用它。这样即使用户的 .bashrc 或 .zshrc 打印了欢迎语,也不会污染最终要加载的快照脚本。

调用图:被 1 处调用(write_shell_snapshot);外部调用 1 个(bail!)。

validate_snapshot247–263 ↗
async fn validate_snapshot(
    shell: &Shell,
    snapshot_path: &AbsolutePathBuf,
    cwd: &AbsolutePathBuf,
) -> Result<()>

作用:检查刚写出来的快照脚本能不能被 shell 正常加载。它相当于写完后先试运行一遍,避免把坏文件交给后续流程。

数据流:进去的是 shell、快照路径和当前目录 → 它拼出一段脚本:开启出错即停,然后 source 这个快照文件 → 调用 run_script_with_timeout 执行 → 成功返回空结果,失败返回错误。

调用关系:ShellSnapshot::try_create 在临时文件写好后调用它。验证失败时,try_create 会调用 remove_snapshot_file 删除临时文件,并把失败记为 validation_failed。

调用图:调用 2 个内部函数(run_script_with_timeout, display);被 1 处调用(try_create);外部调用 1 个(format!)。

run_shell_script265–274 ↗
async fn run_shell_script(shell: &Shell, script: &str, cwd: &AbsolutePathBuf) -> Result<String>

作用:用登录 shell 的方式运行一段脚本并拿回输出。登录 shell 可以读取用户平时的启动配置,更接近用户真实终端。

数据流:进去的是 shell、脚本文本和当前目录 → 它把固定超时时间和“使用登录 shell”这个选择一起传给 run_script_with_timeout → 出来的是脚本标准输出文本,或者错误。

调用关系:capture_snapshot 通过它运行各类快照脚本。它本身是薄包装,真正启动进程和处理超时的事由 run_script_with_timeout 做。

调用图:调用 1 个内部函数(run_script_with_timeout);被 1 处调用(capture_snapshot)。

run_script_with_timeout276–312 ↗
async fn run_script_with_timeout(
    shell: &Shell,
    script: &str,
    snapshot_timeout: Duration,
    use_login_shell: bool,
    cwd: &AbsolutePathBuf,
) -> Result<String>

作用:启动一个 shell 进程运行脚本,并保证它不会无限卡住。它处理命令参数、工作目录、超时、退出状态和输出读取。

数据流:进去的是 shell、脚本、超时时长、是否用登录 shell、当前目录 → 它让 shell 生成执行参数,启动子进程,关闭标准输入,设置工作目录;在 Unix 上还会让进程脱离当前终端控制;然后等待输出但受超时限制 → 成功时返回标准输出字符串,超时、启动失败或退出码非零时返回错误。

调用关系:run_shell_script 和 validate_snapshot 都依赖它。前者用它抓快照内容,后者用它测试快照文件能否加载,所以它是所有 shell 子进程执行的底层通道。

调用图:调用 2 个内部函数(derive_exec_args, name);被 2 处调用(run_shell_script, validate_snapshot);外部调用 5 个(null, from_utf8_lossy, bail!, new, timeout)。

excluded_exports_regex314–316 ↗
fn excluded_exports_regex() -> String

作用:生成一段用于过滤环境变量名的正则表达式文本。这里排除 PWD 和 OLDPWD,因为它们记录当前目录和上一个目录,硬塞进快照可能让目录状态变乱。

数据流:进去没有参数 → 它读取固定列表 EXCLUDED_EXPORT_VARS → 用竖线拼成类似 'PWD|OLDPWD' 的文本并返回。

调用关系:zsh_snapshot_script、bash_snapshot_script 和 sh_snapshot_script 都会调用它,把这段过滤规则嵌进各自的 shell 脚本里。

调用图:被 3 处调用(bash_snapshot_script, sh_snapshot_script, zsh_snapshot_script)。

zsh_snapshot_script318–360 ↗
fn zsh_snapshot_script() -> String

作用:生成专门给 zsh 使用的快照脚本。这个脚本会加载 zsh 配置,然后打印函数、选项、别名和环境变量。

数据流:进去没有参数 → 它先调用 excluded_exports_regex 得到要排除的变量名规则,再把规则替换进一大段 zsh 脚本文本 → 出来的是可交给 zsh 执行的字符串。

调用关系:capture_snapshot 在发现当前 shell 是 Zsh 时调用它。生成的脚本随后会被 run_shell_script 执行,输出再进入 write_shell_snapshot 的写文件流程。

调用图:调用 1 个内部函数(excluded_exports_regex);被 1 处调用(capture_snapshot)。

bash_snapshot_script362–402 ↗
fn bash_snapshot_script() -> String

作用:生成专门给 bash 使用的快照脚本。它会尽量加载用户的 .bashrc,然后导出函数、开启的 shell 选项、别名和环境变量。

数据流:进去没有参数 → 它调用 excluded_exports_regex 得到过滤规则,把规则放进 bash 脚本模板 → 出来一段 bash 脚本文本。

调用关系:capture_snapshot 在 shell 类型是 Bash 时调用它。脚本执行结果会被 strip_snapshot_preamble 清理,再由 write_shell_snapshot 写入快照文件。

调用图:调用 1 个内部函数(excluded_exports_regex);被 1 处调用(capture_snapshot)。

sh_snapshot_script404–470 ↗
fn sh_snapshot_script() -> String

作用:生成给普通 sh 使用的快照脚本。因为 sh 的能力在不同系统上差异很大,这段脚本会先探测命令是否存在,再尽量导出能恢复的内容。

数据流:进去没有参数 → 它调用 excluded_exports_regex 得到过滤规则,塞进兼容性更强的 sh 脚本模板 → 出来一段 sh 脚本文本。

调用关系:capture_snapshot 在 shell 类型是 Sh 时调用它。它生成的脚本同样交给 run_shell_script 执行,用于后续写快照文件。

调用图:调用 1 个内部函数(excluded_exports_regex);被 1 处调用(capture_snapshot)。

powershell_snapshot_script472–495 ↗
fn powershell_snapshot_script() -> &'static str

作用:提供一段 PowerShell 版的快照脚本,用来打印函数、别名和环境变量。不过当前写文件流程还没有真正支持 PowerShell 快照。

数据流:进去没有参数 → 它直接返回一段固定的 PowerShell 脚本文本 → 文本内容运行后会输出可重新设置别名、函数和环境变量的命令。

调用关系:capture_snapshot 在 PowerShell 分支里会引用它。但 write_shell_snapshot 目前会先拒绝 PowerShell,所以正常创建快照时暂时到不了这一步。

调用图:被 1 处调用(capture_snapshot)。

cleanup_stale_snapshots500–561 ↗
async fn cleanup_stale_snapshots(
    codex_home: &AbsolutePathBuf,
    active_session_id: ThreadId,
    state_db: Option<StateDbHandle>,
) -> Result<()>

作用:清理过期或失去对应会话记录的 shell 快照文件。它防止快照目录越积越多,同时不会删除当前正在用的会话快照。

数据流:进去的是 Codex 家目录、当前活跃会话 ID 和可选状态数据库 → 它打开 shell_snapshots 目录;逐个检查文件名能否解析出会话 ID;当前会话跳过;其他会话去找对应 rollout 文件,也就是会话记录文件;找不到就删;找到了再看修改时间,超过保留期也删 → 最后返回成功或读目录等错误。

调用关系:ShellSnapshot::try_create 每次创建新快照时都会后台启动它。它靠 snapshot_session_id_from_file_name 识别文件属于哪个会话,靠 find_thread_path_by_id_str 找会话记录,真正删除文件则交给 remove_snapshot_file。

调用图:调用 4 个内部函数(remove_snapshot_file, snapshot_session_id_from_file_name, find_thread_path_by_id_str, join);被 1 处调用(try_create);外部调用 5 个(now, metadata, read_dir, to_string, warn!)。

remove_snapshot_file563–567 ↗
async fn remove_snapshot_file(path: &Path)

作用:安全地删除一个快照文件。删除失败时只写警告,不让整个流程因为清理失败而崩掉。

数据流:进去的是一个文件路径 → 它调用异步删除文件接口 → 成功没有输出,失败记录 warning 日志。

调用关系:ShellSnapshot::try_create 在验证失败或改名失败时用它删临时文件;cleanup_stale_snapshots 用它删旧快照或无效快照。它是统一的异步清理小工具。

调用图:被 2 处调用(try_create, cleanup_stale_snapshots);外部调用 2 个(remove_file, warn!)。

snapshot_session_id_from_file_name569–579 ↗
fn snapshot_session_id_from_file_name(file_name: &str) -> Option<&str>

作用:从快照文件名里猜出它属于哪个会话。清理旧文件时必须先知道文件对应的会话,才能决定能不能删。

数据流:进去的是文件名字符串 → 它按最后一个点拆出扩展名;如果是 .sh 或 .ps1,就取前面的会话 ID 部分;如果是临时文件扩展样式 tmp-,也按规则取;其他名字返回 None → 出来是可选的会话 ID 字符串切片。

调用关系:cleanup_stale_snapshots 遍历快照目录时调用它。解析不出来的文件会被认为不是有效快照名,然后交给 remove_snapshot_file 删除。

调用图:被 1 处调用(cleanup_stale_snapshots)。

exec-server/src/environment.rs源码 ↗
orchestrationstartup 和 cross-cutting

Codex 需要运行 shell 命令、访问文件、发 HTTP 请求,但这些事可能发生在本机,也可能发生在远程执行服务器。这个文件把这些选择包装成 Environment,并用 EnvironmentManager 保存一组可用环境和默认环境。启动时它会从配置文件、环境变量 CODEX_EXEC_SERVER_URL,或测试参数里生成环境列表;如果写成 none,就表示不提供 shell 和文件工具。远程环境不会一创建就联网,而是先放好连接参数,等真正执行命令或读文件时再懒连接。文件里还用 RwLock(一把可读可写的锁,防止多人同时改环境表)保护环境列表,允许运行中新增或替换远程环境。整体上,它就是“去哪儿干活”的调度台。

函数细节58
EnvironmentManager::default_for_tests61–71 ↗
fn default_for_tests() -> Self

作用:创建一个只给测试用的环境管理器,默认使用本机环境。这样测试不用真的读用户配置或连接远程服务器。

数据流:进去没有参数 → 它造出一个名叫 local 的本机 Environment,并把它同时设为默认环境和本机环境 → 出来一个可直接执行本机命令的 EnvironmentManager。

调用关系:很多测试和测试辅助启动器会用它快速得到一个稳定的默认环境;它内部依赖 Environment::default_for_tests 来造真正的环境对象。

调用图:调用 1 个内部函数(default_for_tests);被 73 处调用(runtime_start_args_use_remote_thread_config_loader_when_configured, start_test_client_with_capacity, guardian_command_execution_notifications_wrap_review_lifecycle, interrupted_subagent_activity_removes_missing_thread_watch, turn_started_omits_active_snapshot_items, start_test_client_with_capacity, refresh_test_state, build_test_processor, get_conversation_summary_by_thread_id_reads_pathless_store_thread, mcp_resource_read_returns_error_for_unknown_thread (+15 more));外部调用 3 个(new, from, new)。

EnvironmentManager::without_environments74–81 ↗
fn without_environments() -> Self

作用:创建一个完全没有执行环境的管理器。它用来表示 shell 和文件访问不可用。

数据流:进去没有参数 → 它把默认环境、本机环境和环境表都设为空 → 出来一个查询任何环境都会失败或返回空的管理器。

调用关系:禁用环境、测试错误路径、验证不会偷偷回退到本机时会用它;后续也可以通过 upsert_environment 动态塞入远程环境。

调用图:被 10 处调用(no_local_runtime_fails_local_stdio_but_keeps_local_http_server, local_http_does_not_require_local_stdio_availability, local_stdio_requires_local_stdio_availability, unknown_explicit_environment_is_rejected, unavailable_environment_does_not_fall_back_to_host_filesystem, default_thread_environment_selections_empty_when_default_disabled, disabled_environment_manager_has_no_default_or_local_environment, environment_manager_rejects_empty_remote_environment_url, environment_manager_upserts_named_remote_environment, noise_environment_refreshes_bundle_for_each_connection_attempt);外部调用 2 个(new, new)。

EnvironmentManager::create_for_tests84–89 ↗
async fn create_for_tests(
        exec_server_url: Option<String>,
        local_runtime_paths: Option<ExecServerRuntimePaths>,
    ) -> Self

作用:按一个原始 exec-server 地址创建测试用管理器。测试可以模拟本机、远程或禁用三种情况。

数据流:进去一个可选 URL 和本机运行路径 → 它交给默认提供者解释这个 URL,比如空值代表本机、none 代表禁用 → 出来一个按这个规则建好的 EnvironmentManager。

调用关系:测试里常用它覆盖真实配置;它只是薄薄转交给 EnvironmentManager::from_default_provider_url。

调用图:被 12 处调用(runtime_start_args_forward_environment_manager, explicit_remote_stdio_and_http_accept_named_environment, remote_stdio_requires_absolute_cwd, default_thread_environment_selections_use_manager_default_id, matching_environment_id_and_cwd_reuse_resolved_environment, build_with_home_and_base_url, environment_manager_carries_local_runtime_paths, environment_manager_includes_local_for_default_provider_without_url, environment_manager_normalizes_empty_url, environment_manager_omits_default_provider_local_lookup_when_default_disabled (+2 more));外部调用 1 个(from_default_provider_url)。

EnvironmentManager::from_codex_home97–103 ↗
async fn from_codex_home(
        codex_home: impl AsRef<std::path::Path>,
        local_runtime_paths: Option<ExecServerRuntimePaths>,
    ) -> Result<Self, ExecServerError>

作用:从 CODEX_HOME 目录里的配置创建环境管理器。用户真正运行程序时,通常会走这条路读取 environments.toml。

数据流:进去 CODEX_HOME 路径和本机运行路径 → 它先读取环境配置提供者,再拿到一份环境快照 → 出来一个按配置组装好的 EnvironmentManager,或返回配置错误。

调用关系:主程序启动、构造提示输入、列出连接器等流程会调用它;它把读配置的活交给 environment_provider_from_codex_home,把组装和校验交给 from_snapshot。

调用图:调用 1 个内部函数(environment_provider_from_codex_home);被 8 处调用(run_main_with_transport_options, list_accessible_connectors_from_mcp_tools_with_options_and_status, toml_default_thread_environment_selections_include_local_and_remote, build_prompt_input, run_main, run_main, run_main, run_main);外部调用 2 个(as_ref, from_snapshot)。

EnvironmentManager::from_env107–112 ↗
async fn from_env(
        local_runtime_paths: Option<ExecServerRuntimePaths>,
    ) -> Result<Self, ExecServerError>

作用:只根据老式环境变量 CODEX_EXEC_SERVER_URL 创建环境管理器,不读取用户配置文件。它用于兼容旧用法。

数据流:进去本机运行路径 → 它读取环境变量提供者的快照 → 出来一个本机、远程或禁用的 EnvironmentManager。

调用关系:主程序和归档命令启动时可能用它;它把环境变量解释交给 DefaultEnvironmentProvider,再用 from_snapshot 统一落地。

调用图:调用 1 个内部函数(from_env);被 4 处调用(run_main_with_transport_options, run_main, run_main, start_app_server_for_archive_command);外部调用 1 个(from_snapshot)。

EnvironmentManager::from_default_provider_url114–123 ↗
async fn from_default_provider_url(
        exec_server_url: Option<String>,
        local_runtime_paths: Option<ExecServerRuntimePaths>,
    ) -> Self

作用:测试内部使用:直接把一个 URL 值当成默认环境来源。它假设默认提供者产出的内容一定合法。

数据流:进去可选 URL 和本机运行路径 → 它创建 DefaultEnvironmentProvider,取内部快照,再调用 from_snapshot → 成功就返回管理器,失败就直接 panic,让测试暴露问题。

调用关系:EnvironmentManager::create_for_tests 会调用它;它连接了“测试传入原始 URL”和“正式快照构建逻辑”。

调用图:调用 1 个内部函数(new);外部调用 2 个(from_snapshot, panic!)。

EnvironmentManager::create_for_tests_with_local127–137 ↗
async fn create_for_tests_with_local(
        exec_server_url: Option<String>,
        local_runtime_paths: ExecServerRuntimePaths,
    ) -> Self

作用:创建测试管理器时强制保留本机环境,即使默认环境可能是远程。这样测试可以同时检查默认环境和显式本机环境。

数据流:进去可选 URL 和本机运行路径 → 它生成默认快照,并把 include_local 改成 true → 出来一个包含 local 的测试管理器,失败则 panic。

调用关系:需要同时测试远程默认环境和本机环境选择时会调用它;它最终仍通过 from_snapshot 做统一校验。

调用图:调用 1 个内部函数(new);被 2 处调用(latest_environment_update_wins_while_previous_resolution_is_pending, build_with_home_and_base_url);外部调用 2 个(from_snapshot, panic!)。

EnvironmentManager::from_snapshot139–202 ↗
fn from_snapshot(
        snapshot: EnvironmentProviderSnapshot,
        local_runtime_paths: Option<ExecServerRuntimePaths>,
    ) -> Result<Self, ExecServerError>

作用:把“环境提供者给出的清单”变成真正可用的 EnvironmentManager。它负责关键校验,防止默认环境不存在、名字重复或占用保留名。

数据流:进去一份快照和本机运行路径 → 它按需创建 local 环境,把远程环境逐个放进表里,检查空 ID、重复 ID、保留 ID 和默认环境是否存在 → 出来合法的 EnvironmentManager,或返回协议错误。

调用关系:from_codex_home、from_env 和测试构造都会走它;它是环境配置落地前的总闸门,并会调用 Environment::local 创建本机环境。

调用图:调用 1 个内部函数(local);被 7 处调用(environment_manager_builds_from_snapshot, environment_manager_disables_provider_default, environment_manager_rejects_empty_environment_id, environment_manager_rejects_provider_supplied_local_environment, environment_manager_rejects_unknown_provider_default, environment_manager_snapshot_without_local_environment_disables_local_default, environment_manager_uses_explicit_provider_default);外部调用 7 个(clone, new, with_capacity, new, Protocol, format!, from)。

EnvironmentManager::default_environment205–209 ↗
fn default_environment(&self) -> Option<Arc<Environment>>

作用:取出当前默认环境对象。调用者用它判断 shell 和文件工具是否可以直接面向模型开放。

数据流:进去没有额外参数,只读取管理器里的默认环境 ID 和环境表 → 它按 ID 查找环境 → 出来对应 Environment,或在没有默认环境时返回空。

调用关系:default_or_local_environment 和一些配置流程会用它;它依赖 get_environment 完成实际查表。

调用图:被 2 处调用(default_or_local_environment, config_cwd_for_app_server_target)。

EnvironmentManager::default_environment_id212–214 ↗
fn default_environment_id(&self) -> Option<&str>

作用:返回默认环境的名字,而不是环境对象本身。适合只需要展示或记录选择结果的地方。

数据流:进去没有参数 → 它读取 default_environment 字段 → 出来一个字符串引用,或者没有默认环境时返回空。

调用关系:启动和测试代码用它确认默认环境选择是否正确;它不触碰环境表,也不会创建连接。

EnvironmentManager::default_environment_ids217–234 ↗
fn default_environment_ids(&self) -> Vec<String>

作用:给新线程准备一组环境 ID,默认环境排在最前面。这样新会话可以优先尝试默认环境,再看到其他可选环境。

数据流:进去没有参数 → 如果没有默认环境就返回空列表;否则读取环境表,把默认 ID 放第一,再追加其他 ID → 出来一个字符串列表。

调用关系:default_thread_environment_selections 会调用它来生成线程启动时的环境选择;它只排序和收集 ID,不创建环境。

调用图:被 1 处调用(default_thread_environment_selections);外部调用 2 个(new, with_capacity)。

EnvironmentManager::try_local_environment237–239 ↗
fn try_local_environment(&self) -> Option<Arc<Environment>>

作用:尝试取出本机环境。名字里的 try 表示本机环境可能根本没配置。

数据流:进去没有参数 → 它读取 local_environment 字段并克隆共享指针 → 出来本机 Environment,或返回空。

调用关系:需要显式本机执行能力的流程和测试会用它;它不看默认环境,所以默认禁用时也能准确反映本机是否存在。

EnvironmentManager::default_or_local_environment242–245 ↗
fn default_or_local_environment(&self) -> Option<Arc<Environment>>

作用:先拿默认环境;如果没有默认环境,就退一步拿本机环境。它给一些内部流程提供更宽松的选择。

数据流:进去没有参数 → 它先调用 default_environment,失败后再调用 try_local_environment → 出来一个可用环境,或两个都没有时返回空。

调用关系:它把“默认优先、本机兜底”的策略封装起来,避免调用者重复写这段选择逻辑。

调用图:调用 1 个内部函数(default_environment)。

EnvironmentManager::get_environment248–254 ↗
fn get_environment(&self, environment_id: &str) -> Option<Arc<Environment>>

作用:按名字取某个环境。用户或会话指定 executor-a、remote、local 这类名字时会用到它。

数据流:进去一个环境 ID → 它加读锁查看环境表 → 找到就克隆共享指针返回,找不到就返回空。

调用关系:resolve_selection 会用它把用户选择解析成具体环境;它是 EnvironmentManager 对外查表的基础入口。

调用图:被 1 处调用(resolve_selection)。

EnvironmentManager::upsert_environment258–286 ↗
fn upsert_environment(
        &self,
        environment_id: String,
        exec_server_url: String,
    ) -> Result<(), ExecServerError>

作用:新增或替换一个普通 URL 形式的远程环境。upsert 的意思是“有就更新,没有就插入”。

数据流:进去环境 ID 和 exec-server URL → 它检查 ID 非空,规范化 URL,拒绝 none 或空 URL,然后创建远程 Environment → 环境表里这个 ID 被新增或替换,默认环境不变。

调用关系:动态发现或配置远程执行器时会用它;它把远程环境创建交给 Environment::remote_inner。

调用图:调用 2 个内部函数(remote_inner, normalize_exec_server_url);外部调用 2 个(new, Protocol)。

EnvironmentManager::upsert_noise_environment293–317 ↗
fn upsert_noise_environment(
        &self,
        environment_id: String,
        provider: Arc<dyn NoiseRendezvousConnectProvider>,
    ) -> Result<(), ExecServerError>

作用:新增或替换一个走 Noise 加密会合通道的远程环境。Noise 是一种加密握手协议,这里用来保证远程连接有身份认证和端到端加密。

数据流:进去环境 ID 和连接提供者 → 它检查 ID,生成一份本端身份,把 provider 和身份包装成远程传输参数 → 环境表里插入或替换这个加密远程环境。

调用关系:需要安全会合连接的运行路径会调用它;它不会退回普通 URL 连接,而是交给 Environment::remote_with_transport 创建专用远程环境。

调用图:调用 2 个内部函数(remote_with_transport, generate);外部调用 2 个(new, Protocol)。

LocalEnvironmentInfoProvider::info343–345 ↗
fn info(&self) -> BoxFuture<'_, Result<EnvironmentInfo, ExecServerError>>

作用:返回本机环境的信息,比如用户默认 shell。它不需要联网。

数据流:进去没有参数 → 它直接生成 EnvironmentInfo::local,并包装成已经完成的异步结果 → 出来本机环境信息。

调用关系:本机 Environment 的 info 方法会间接调用它;它把具体信息生成交给 EnvironmentInfo::local。

调用图:外部调用 2 个(local, ready)。

RemoteEnvironmentInfoProvider::new353–355 ↗
fn new(client: LazyRemoteExecServerClient) -> Self

作用:创建一个远程环境信息提供者,里面保存懒连接客户端。以后查询信息时才真正请求远程服务器。

数据流:进去 LazyRemoteExecServerClient → 它存进结构体 → 出来 RemoteEnvironmentInfoProvider。

调用关系:Environment::remote_with_transport 创建远程环境时会调用它,让环境的信息查询走同一个远程客户端。

调用图:被 1 处调用(remote_with_transport)。

RemoteEnvironmentInfoProvider::info359–361 ↗
fn info(&self) -> BoxFuture<'_, Result<EnvironmentInfo, ExecServerError>>

作用:向远程 exec-server 查询环境信息。比如远程机器默认用什么 shell。

数据流:进去没有额外参数,只读取内部客户端 → 它调用 client.environment_info 发起远程请求 → 出来远程返回的 EnvironmentInfo,或连接/协议错误。

调用关系:远程 Environment 的 info 方法会间接调用它;真正的网络连接由 LazyRemoteExecServerClient 在需要时处理。

调用图:调用 1 个内部函数(environment_info)。

Environment::default_for_tests366–376 ↗
fn default_for_tests() -> Self

作用:创建一个测试用本机环境,没有沙箱辅助路径。它适合跑简单本机命令,但不适合测试需要沙箱的文件操作。

数据流:进去没有参数 → 它配置本机进程执行器、无沙箱本机文件系统、普通 HTTP 客户端和本机信息提供者 → 出来一个本机 Environment。

调用关系:EnvironmentManager::default_for_tests 和多项测试会用它;它是测试里最轻量的环境对象。

调用图:调用 2 个内部函数(unsandboxed, default);被 12 处调用(shell_mode_for_environment_uses_direct_mode_for_remote_environments, test_turn_environment, test_turn_environment, completed_pipe_commands_preserve_exit_code, default_for_tests, default_environment_has_ready_local_executor, test_environment_rejects_sandboxed_filesystem_without_runtime_paths, remote_environment_fetches_info_from_exec_server, oauth_startup_child, streamable_http_initialize_retries_remote_no_response_error (+2 more));外部调用 1 个(new)。

Environment::fmt380–384 ↗
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result

作用:定义 Environment 打印调试信息时显示什么。它避免把所有内部对象都展开,只显示关键 URL。

数据流:进去一个格式化器 → 它写入 Environment 结构名和 exec_server_url 字段 → 出来格式化结果。

调用关系:Rust 调试打印 {:?} 时自动调用它;它不参与执行流程,只帮助排查问题。

调用图:外部调用 1 个(debug_struct)。

Environment::create389–394 ↗
fn create(
        exec_server_url: Option<String>,
        local_runtime_paths: ExecServerRuntimePaths,
    ) -> Result<Self, ExecServerError>

作用:根据原始 exec-server URL 创建正式 Environment。它是单个环境版本的构造入口。

数据流:进去可选 URL 和本机运行路径 → 它转交 create_inner 统一解释 URL → 出来本机或远程 Environment,或在禁用模式时报错。

调用关系:测试 create_local_environment_does_not_connect 会调用它;实际判断逻辑都在 create_inner。

调用图:被 1 处调用(create_local_environment_does_not_connect);外部调用 1 个(create_inner)。

Environment::create_for_tests397–399 ↗
fn create_for_tests(exec_server_url: Option<String>) -> Result<Self, ExecServerError>

作用:测试用:根据 URL 创建 Environment,但不要求提供本机运行路径。方便测试远程或简单本机环境。

数据流:进去可选 URL → 它调用 create_inner,并把本机运行路径设为空 → 出来测试环境,可能是远程,也可能是无沙箱本机环境。

调用关系:许多环境相关测试和测试上下文会用它;它复用 create_inner 的 URL 解释规则。

调用图:被 10 处调用(single_local_environment_cwd_requires_exactly_one_local_environment, shell_mode_for_environment_uses_direct_mode_for_remote_environments, local, test_env, wait_for_remote_streamable_http_server, create_process_context, connect_file_system, create_file_system_context, sandboxed_file_system_helper_finds_bwrap_on_preserved_path, remote_environment_fetches_info_from_exec_server);外部调用 1 个(create_inner)。

Environment::create_inner403–421 ↗
fn create_inner(
        exec_server_url: Option<String>,
        local_runtime_paths: Option<ExecServerRuntimePaths>,
    ) -> Result<Self, ExecServerError>

作用:统一解释 exec-server URL,决定创建远程、本机还是报错。它是 Environment 构造的核心分叉点。

数据流:进去可选 URL 和可选本机运行路径 → 它先规范化 URL;如果是 none 就返回错误;如果有 URL 就建远程;没有 URL 且有运行路径就建正式本机;没有运行路径就建测试本机 → 出来对应 Environment。

调用关系:Environment::create 和 Environment::create_for_tests 都调用它;它会根据情况转给 remote_inner、local 或 default_for_tests。

调用图:调用 1 个内部函数(normalize_exec_server_url);外部调用 4 个(default_for_tests, local, remote_inner, Protocol)。

Environment::local423–435 ↗
fn local(local_runtime_paths: ExecServerRuntimePaths) -> Self

作用:创建正式的本机环境,并带上沙箱辅助程序路径。这样本机文件访问可以按权限规则工作。

数据流:进去 ExecServerRuntimePaths → 它创建本机进程执行器、本机文件系统、HTTP 客户端和本机信息提供者,并保存运行路径 → 出来一个本机 Environment。

调用关系:EnvironmentManager::from_snapshot 在 include_local 为真时调用它;它是正式 local 环境的构造点。

调用图:调用 2 个内部函数(with_runtime_paths, default);被 1 处调用(from_snapshot);外部调用 2 个(new, clone)。

Environment::remote_inner437–445 ↗
fn remote_inner(
        exec_server_url: String,
        local_runtime_paths: Option<ExecServerRuntimePaths>,
    ) -> Self

作用:用 WebSocket URL 创建远程环境。WebSocket 可以理解成一条长期打开的网络通道。

数据流:进去远程 exec-server URL 和可选本机运行路径 → 它把 URL 包成传输参数 → 再交给 remote_with_transport → 出来远程 Environment。

调用关系:upsert_environment 和默认提供者快照会用它;它只是普通 URL 远程连接的快捷入口。

调用图:调用 1 个内部函数(websocket_url);被 2 处调用(upsert_environment, snapshot_inner);外部调用 1 个(remote_with_transport)。

Environment::remote_with_transport447–473 ↗
fn remote_with_transport(
        remote_transport: ExecServerTransportParams,
        local_runtime_paths: Option<ExecServerRuntimePaths>,
    ) -> Self

作用:用任意远程传输方式创建远程环境,包括 WebSocket、Noise 会合或标准输入输出命令。它把远程执行、远程文件系统和 HTTP 客户端绑到同一个懒连接客户端上。

数据流:进去远程传输参数和可选本机运行路径 → 它提取可展示的 URL,创建 LazyRemoteExecServerClient,再基于这个客户端创建 RemoteProcess、RemoteFileSystem、远程信息提供者和 HTTP 客户端 → 出来一个远程 Environment,但此时还不一定联网。

调用关系:upsert_noise_environment、remote_inner 和配置快照创建远程环境时会调用它;它是所有远程环境的统一装配厂。

调用图:调用 4 个内部函数(new, new, new, new);被 2 处调用(upsert_noise_environment, snapshot);外部调用 2 个(new, clone)。

Environment::is_remote475–477 ↗
fn is_remote(&self) -> bool

作用:判断这个环境是不是远程环境。调用者可以据此选择不同 shell 模式或会话打开方式。

数据流:进去没有参数 → 它检查 remote_transport 是否存在 → 出来 true 或 false。

调用关系:shell_mode_for_environment 和 open_session_with_exec_env 会用它判断是否需要远程处理。

调用图:被 2 处调用(shell_mode_for_environment, open_session_with_exec_env)。

Environment::exec_server_url480–482 ↗
fn exec_server_url(&self) -> Option<&str>

作用:返回远程 exec-server 的 URL;如果不是 URL 形式的远程或本机环境,就返回空。

数据流:进去没有参数 → 它读取 exec_server_url 字段并转成字符串引用 → 出来 URL 字符串或空。

调用关系:测试和展示逻辑会用它确认连接目标;它不会触发远程连接。

Environment::local_runtime_paths484–486 ↗
fn local_runtime_paths(&self) -> Option<&ExecServerRuntimePaths>

作用:取出本机运行辅助路径。文件系统需要这些路径时,才能执行带沙箱的本机操作。

数据流:进去没有参数 → 它读取 local_runtime_paths 字段 → 出来路径引用,或没有配置时返回空。

调用关系:测试会用它确认路径被保留下来;本机文件系统构造时也依赖这份信息。

Environment::info489–491 ↗
async fn info(&self) -> Result<EnvironmentInfo, ExecServerError>

作用:获取这个环境的基本信息。对本机是直接计算,对远程是向服务器询问。

数据流:进去没有参数 → 它调用内部 info_provider.info → 出来 EnvironmentInfo,或远程请求失败时返回错误。

调用关系:调用者不用关心本机还是远程;本机走 LocalEnvironmentInfoProvider,远程走 RemoteEnvironmentInfoProvider。

Environment::get_exec_backend493–495 ↗
fn get_exec_backend(&self) -> Arc<dyn ExecBackend>

作用:拿到执行命令用的后端。后端可能是在本机起进程,也可能是把命令发给远程服务器。

数据流:进去没有参数 → 它克隆一个共享指针 → 出来 ExecBackend 接口对象,不复制真正后端。

调用关系:open_session_with_exec_env 会用它启动会话里的命令;Environment 在这里隐藏了本机和远程差异。

调用图:被 1 处调用(open_session_with_exec_env);外部调用 1 个(clone)。

Environment::get_http_client497–499 ↗
fn get_http_client(&self) -> Arc<dyn HttpClient>

作用:拿到这个环境对应的 HTTP 客户端。本机环境用普通客户端,远程环境可能通过远程执行服务器转发。

数据流:进去没有参数 → 它克隆内部 HTTP 客户端共享指针 → 出来 HttpClient 接口对象。

调用关系:需要发网络请求的工具会用它;远程环境中它和远程执行、文件系统共用同一个懒连接客户端。

调用图:外部调用 1 个(clone)。

Environment::get_filesystem501–503 ↗
fn get_filesystem(&self) -> Arc<dyn ExecutorFileSystem>

作用:拿到读写文件用的文件系统接口。调用者不用自己判断文件在本机读还是远程读。

数据流:进去没有参数 → 它克隆内部文件系统共享指针 → 出来 ExecutorFileSystem 接口对象。

调用关系:文件工具和测试会用它;本机环境返回 LocalFileSystem,远程环境返回 RemoteFileSystem。

调用图:外部调用 1 个(clone)。

EnvironmentInfo::local507–511 ↗
fn local() -> Self

作用:生成本机环境信息,主要是本机默认 shell 的名字和路径。shell 可以理解成执行命令的命令行程序。

数据流:进去没有参数 → 它检测当前用户默认 shell,并转换成 ShellInfo → 出来本机 EnvironmentInfo。

调用关系:LocalEnvironmentInfoProvider::info 会调用它;ShellInfo 的转换由 From<DetectedShell> 实现完成。

调用图:调用 1 个内部函数(default_user_shell)。

ShellInfo::from515–520 ↗
fn from(shell: DetectedShell) -> Self

作用:把检测到的 shell 对象转换成协议里可传输的 ShellInfo。它只保留名字和路径这类外部需要看的信息。

数据流:进去 DetectedShell → 它读取 shell 名称和 shell 可执行文件路径 → 出来 ShellInfo。

调用关系:EnvironmentInfo::local 生成本机信息时会间接用到它;它是内部检测结果和协议数据之间的小翻译器。

调用图:调用 1 个内部函数(name)。

tests::test_runtime_paths538–544 ↗
fn test_runtime_paths() -> ExecServerRuntimePaths

作用:给测试生成一份本机运行路径。测试需要正式本机环境时,用它提供必要的辅助路径。

数据流:进去没有参数 → 它读取当前测试可执行文件路径,并构造 ExecServerRuntimePaths → 出来测试可用的运行路径。

调用关系:大量环境管理器测试会调用它,避免每个测试重复写路径准备代码。

调用图:调用 1 个内部函数(new);外部调用 1 个(current_exe)。

tests::assert_local_environment_unavailable546–548 ↗
fn assert_local_environment_unavailable(manager: &EnvironmentManager)

作用:测试辅助断言:确认管理器里没有本机环境。这样测试表达更清楚。

数据流:进去一个 EnvironmentManager 引用 → 它调用 try_local_environment 并断言结果为空 → 没有返回业务结果,失败时测试会报错。

调用关系:禁用环境、远程-only 环境等测试会调用它;它把重复断言封装成一句话。

调用图:外部调用 1 个(assert!)。

tests::create_local_environment_does_not_connect551–558 ↗
async fn create_local_environment_does_not_connect()

作用:验证创建本机环境不会触发远程连接,并且能正常返回环境信息。

数据流:进去没有外部输入 → 测试用空 URL 和运行路径创建 Environment,检查 URL 为空、不是远程、info 可用 → 测试通过表示本机环境构造安全。

调用关系:它覆盖 Environment::create 的本机分支,确保本机环境只做本地准备。

调用图:调用 1 个内部函数(create);外部调用 3 个(assert!, assert_eq!, test_runtime_paths)。

tests::environment_manager_normalizes_empty_url561–581 ↗
async fn environment_manager_normalizes_empty_url()

作用:验证空字符串 URL 会被当成本机默认环境,而不是一个坏的远程地址。

数据流:进去没有外部输入 → 测试传入空字符串创建管理器,检查默认 ID 是 local、本机环境存在、remote 不存在 → 测试通过说明 URL 规范化规则正确。

调用关系:它覆盖 EnvironmentManager::create_for_tests 和默认提供者的兼容行为。

调用图:调用 1 个内部函数(create_for_tests);外部调用 4 个(new, assert!, assert_eq!, test_runtime_paths)。

tests::disabled_environment_manager_has_no_default_or_local_environment584–592 ↗
async fn disabled_environment_manager_has_no_default_or_local_environment()

作用:验证完全禁用的管理器确实没有默认环境和本机环境。

数据流:进去没有外部输入 → 测试创建 without_environments 管理器,并检查各种查询都为空 → 测试通过说明禁用状态不会意外开放工具。

调用关系:它覆盖 EnvironmentManager::without_environments 的基本语义。

调用图:调用 1 个内部函数(without_environments);外部调用 3 个(assert!, assert_eq!, assert_local_environment_unavailable)。

tests::environment_manager_reports_remote_url595–617 ↗
async fn environment_manager_reports_remote_url()

作用:验证传入远程 WebSocket 地址时,默认环境会变成 remote,并能报告这个 URL。

数据流:进去没有外部输入 → 测试用 ws:// 地址创建管理器,检查默认 ID、is_remote、exec_server_url 和 local 缺失情况 → 测试通过说明远程配置被正确识别。

调用关系:它覆盖 create_for_tests 的远程分支和 Environment::exec_server_url。

调用图:调用 1 个内部函数(create_for_tests);外部调用 4 个(assert!, assert_eq!, assert_local_environment_unavailable, test_runtime_paths)。

tests::environment_manager_default_environment_caches_environment620–631 ↗
async fn environment_manager_default_environment_caches_environment()

作用:验证多次获取默认环境拿到的是同一个共享对象,而不是每次新建。

数据流:进去没有外部输入 → 测试创建默认测试管理器,两次取 default_environment,并比较指针和文件系统指针 → 测试通过说明环境被缓存和复用。

调用关系:它保护 EnvironmentManager 作为共享注册表的设计,避免资源重复创建。

调用图:调用 1 个内部函数(default_for_tests);外部调用 1 个(assert!)。

tests::environment_manager_builds_from_snapshot634–659 ↗
async fn environment_manager_builds_from_snapshot()

作用:验证从快照可以构建只有远程环境的管理器。

数据流:进去没有外部输入 → 测试手写一个包含 remote 的 EnvironmentProviderSnapshot,调用 from_snapshot,检查默认 ID、远程状态和 local 缺失 → 测试通过说明快照构建正常。

调用关系:它直接覆盖 EnvironmentManager::from_snapshot 的普通成功路径。

调用图:调用 1 个内部函数(from_snapshot);外部调用 6 个(assert!, assert_eq!, assert_local_environment_unavailable, test_runtime_paths, EnvironmentId, vec!)。

tests::environment_manager_rejects_empty_environment_id662–675 ↗
async fn environment_manager_rejects_empty_environment_id()

作用:验证环境 ID 不能为空。空名字会让后续查找和展示都变得含糊。

数据流:进去没有外部输入 → 测试构造一个 ID 为空的快照,调用 from_snapshot,检查错误消息 → 测试通过说明校验生效。

调用关系:它覆盖 from_snapshot 对非法环境名的防线。

调用图:调用 1 个内部函数(from_snapshot);外部调用 3 个(assert_eq!, test_runtime_paths, vec!)。

tests::environment_manager_rejects_provider_supplied_local_environment678–694 ↗
async fn environment_manager_rejects_provider_supplied_local_environment()

作用:验证配置提供者不能自己声明名叫 local 的环境,因为 local 是管理器保留名。

数据流:进去没有外部输入 → 测试构造一个 ID 为 local 的快照,调用 from_snapshot,检查返回保留名错误 → 测试通过说明不会覆盖内置本机环境。

调用关系:它保护 EnvironmentManager::from_snapshot 中 local 保留 ID 的规则。

调用图:调用 1 个内部函数(from_snapshot);外部调用 3 个(assert_eq!, test_runtime_paths, vec!)。

tests::environment_manager_uses_explicit_provider_default697–716 ↗
async fn environment_manager_uses_explicit_provider_default()

作用:验证配置里明确指定的默认环境会被采用,即使同时包含本机环境。

数据流:进去没有外部输入 → 测试快照里放 devbox 远程环境、include_local 为真、默认设 devbox → 检查默认 ID、默认列表顺序和远程状态。

调用关系:它覆盖 from_snapshot 对显式默认环境和 default_environment_ids 顺序的处理。

调用图:调用 1 个内部函数(from_snapshot);外部调用 5 个(assert!, assert_eq!, test_runtime_paths, EnvironmentId, vec!)。

tests::environment_manager_disables_provider_default719–740 ↗
async fn environment_manager_disables_provider_default()

作用:验证配置可以禁用默认环境,同时仍然保留可显式选择的本机环境。

数据流:进去没有外部输入 → 测试快照包含 devbox 和 local,但 default 设为 Disabled → 检查默认为空、本机环境仍在表中 → 测试通过说明“无默认”和“无本机”被区分开。

调用关系:它覆盖 from_snapshot 对 EnvironmentDefault::Disabled 的处理。

调用图:调用 1 个内部函数(from_snapshot);外部调用 4 个(assert!, assert_eq!, test_runtime_paths, vec!)。

tests::environment_manager_rejects_unknown_provider_default743–760 ↗
async fn environment_manager_rejects_unknown_provider_default()

作用:验证默认环境不能指向不存在的 ID。否则后面启动会话时会找不到要用的环境。

数据流:进去没有外部输入 → 测试构造默认 ID 为 missing 的快照,但实际只配置 devbox → 调用 from_snapshot 并检查错误消息。

调用关系:它覆盖 from_snapshot 的默认环境存在性校验。

调用图:调用 1 个内部函数(from_snapshot);外部调用 4 个(assert_eq!, test_runtime_paths, EnvironmentId, vec!)。

tests::environment_manager_includes_local_for_default_provider_without_url763–783 ↗
async fn environment_manager_includes_local_for_default_provider_without_url()

作用:验证没有提供 URL 时,默认提供者会包含并选择本机环境。

数据流:进去没有外部输入 → 测试用 None 创建管理器,检查默认是 local、本机环境和环境表里的 local 是同一个对象 → 测试通过说明旧行为兼容。

调用关系:它覆盖 EnvironmentManager::create_for_tests 在无 URL 情况下的本机默认逻辑。

调用图:调用 1 个内部函数(create_for_tests);外部调用 3 个(assert!, assert_eq!, test_runtime_paths)。

tests::environment_manager_carries_local_runtime_paths786–809 ↗
async fn environment_manager_carries_local_runtime_paths()

作用:验证本机运行路径会被环境保存下来,并能在重建管理器时继续传递。

数据流:进去没有外部输入 → 测试创建管理器,取出 local 环境检查路径,再用取出的路径重建并再次检查 → 测试通过说明路径没有丢。

调用关系:它覆盖 Environment::local、local_runtime_paths 和 EnvironmentManager::create_for_tests 的配合。

调用图:调用 1 个内部函数(create_for_tests);外部调用 2 个(assert_eq!, test_runtime_paths)。

tests::environment_manager_omits_default_provider_local_lookup_when_default_disabled812–824 ↗
async fn environment_manager_omits_default_provider_local_lookup_when_default_disabled()

作用:验证 URL 为 none 时,不仅没有默认环境,也不会偷偷保留 local 环境。

数据流:进去没有外部输入 → 测试用 none 创建管理器,检查默认、本机、local ID、remote ID 都为空 → 测试通过说明禁用模式彻底生效。

调用关系:它覆盖默认提供者禁用模式和 EnvironmentManager 的环境表结果。

调用图:调用 1 个内部函数(create_for_tests);外部调用 4 个(assert!, assert_eq!, assert_local_environment_unavailable, test_runtime_paths)。

tests::environment_manager_snapshot_without_local_environment_disables_local_default827–843 ↗
async fn environment_manager_snapshot_without_local_environment_disables_local_default()

作用:验证快照如果不包含本机环境,就不能把 local 当默认环境。

数据流:进去没有外部输入 → 测试先构造看似默认 local 的快照,再改成不包含 local 且禁用默认,调用 from_snapshot,检查所有 local 查询为空 → 测试通过说明快照状态一致。

调用关系:它覆盖 from_snapshot 在 include_local 为 false 时的行为。

调用图:调用 1 个内部函数(from_snapshot);外部调用 5 个(new, assert!, assert_eq!, assert_local_environment_unavailable, EnvironmentId)。

tests::get_environment_returns_none_for_unknown_id846–850 ↗
async fn get_environment_returns_none_for_unknown_id()

作用:验证查不存在的环境 ID 会返回空,而不是报错或随便回退。

数据流:进去没有外部输入 → 测试创建默认管理器,查询 does-not-exist,并断言为空 → 测试通过说明命名查找严格。

调用关系:它覆盖 EnvironmentManager::get_environment 的未命中路径。

调用图:调用 1 个内部函数(default_for_tests);外部调用 1 个(assert!)。

tests::environment_manager_upserts_named_remote_environment853–875 ↗
async fn environment_manager_upserts_named_remote_environment()

作用:验证可以动态新增并替换一个命名远程环境,同时不改变默认环境。

数据流:进去没有外部输入 → 测试从空管理器开始,插入 executor-a 的 URL,检查远程状态和 URL;再用新 URL 插入同名环境,检查对象已替换 → 测试通过说明 upsert 语义正确。

调用关系:它直接覆盖 EnvironmentManager::upsert_environment 的新增和替换路径。

调用图:调用 1 个内部函数(without_environments);外部调用 2 个(assert!, assert_eq!)。

tests::environment_manager_rejects_empty_remote_environment_url878–889 ↗
async fn environment_manager_rejects_empty_remote_environment_url()

作用:验证动态新增远程环境时,空 URL 会被拒绝。远程环境必须知道要连接哪里。

数据流:进去没有外部输入 → 测试创建空管理器,调用 upsert_environment 传空字符串 URL,检查错误消息 → 测试通过说明输入校验有效。

调用关系:它覆盖 EnvironmentManager::upsert_environment 对 URL 的错误处理。

调用图:调用 1 个内部函数(without_environments);外部调用 2 个(new, assert_eq!)。

tests::default_environment_has_ready_local_executor892–912 ↗
async fn default_environment_has_ready_local_executor()

作用:验证测试默认环境里的本机执行器真的能启动一个简单命令。

数据流:进去没有外部输入 → 测试创建默认 Environment,拿执行后端启动 true 命令,并检查返回的进程 ID → 测试通过说明本机执行后端可用。

调用关系:它覆盖 Environment::default_for_tests 和 get_exec_backend 与 LocalProcess 的配合。

调用图:调用 3 个内部函数(default_for_tests, from, from_path);外部调用 4 个(default, assert_eq!, current_dir, vec!)。

tests::test_environment_rejects_sandboxed_filesystem_without_runtime_paths915–939 ↗
async fn test_environment_rejects_sandboxed_filesystem_without_runtime_paths()

作用:验证没有运行路径的测试环境不能执行带沙箱的文件读取。否则权限控制会失去必要工具。

数据流:进去没有外部输入 → 测试创建默认测试环境,构造一个受限沙箱上下文,尝试读取文件,并检查报错提示需要运行路径 → 测试通过说明安全限制没有被绕过。

调用关系:它覆盖 Environment::default_for_tests、get_filesystem 和 LocalFileSystem 在缺少沙箱辅助路径时的失败行为。

调用图:调用 6 个内部函数(default_for_tests, from_permission_profile, from_runtime_permissions, restricted, from_absolute_path, from_abs_path);外部调用 3 个(new, assert_eq!, current_exe)。

exec-server/src/environment_provider.rs源码 ↗
configstartup / config load

这个文件像一个“开机选座表”。Codex 启动时需要知道:有没有远程执行服务器地址;如果有,就把远程环境放进清单并默认使用;如果没有,就让外层的 EnvironmentManager 补上本地环境;如果用户把地址写成 none,就表示明确不要任何默认环境。这里的 EnvironmentProvider 是一个接口,意思是“谁能提供环境清单”;DefaultEnvironmentProvider 是默认实现,它读取 CODEX_EXEC_SERVER_URL 这个环境变量(系统里给程序看的配置值)。它还会把地址前后的空格去掉,把空字符串当作没配置,把 none 当作禁用。这样后续代码拿到的是一份统一、干净、不会误解用户意图的启动快照。

函数细节10
DefaultEnvironmentProvider::new46–48 ↗
fn new(exec_server_url: Option<String>) -> Self

作用:用已经读好的远程执行服务器地址,创建一个默认环境提供器。有人在测试或配置代码里已经拿到了这个地址时,就用它来包装成统一对象。

数据流:进去的是一个可能存在、也可能不存在的字符串地址 → 函数只是把它存进 DefaultEnvironmentProvider 里,不做解析 → 出来的是一个 provider,后面可以用它生成环境快照。

调用关系:它是最基础的构造入口。DefaultEnvironmentProvider::from_env 读完环境变量后会交给它;多组测试也直接用它塞入不同地址,检查后续快照是不是符合预期。

调用图:被 7 处调用(create_for_tests_with_local, from_default_provider_url, default_provider_adds_remote_environment_for_websocket_url, default_provider_normalizes_exec_server_url, default_provider_omits_local_environment_for_none_value, default_provider_requests_local_environment_when_url_is_empty, default_provider_requests_local_environment_when_url_is_missing)。

DefaultEnvironmentProvider::from_env51–53 ↗
fn from_env() -> Self

作用:从系统环境变量 CODEX_EXEC_SERVER_URL 里读取远程执行服务器地址,并创建默认环境提供器。它适合真实启动时使用,因为那时配置通常来自环境变量。

数据流:进去时不需要参数 → 它向操作系统读取 CODEX_EXEC_SERVER_URL;读不到就当作没有地址 → 然后调用 DefaultEnvironmentProvider::new,输出一个保存了该配置值的 provider。

调用关系:它是运行时配置进入这个文件的常用入口。上层创建环境提供器时会调用它;它自己不判断地址含义,而是把原始值交给后面的 snapshot_inner 再统一处理。

调用图:被 2 处调用(from_env, environment_provider_from_codex_home);外部调用 2 个(new, var)。

DefaultEnvironmentProvider::snapshot_inner55–83 ↗
fn snapshot_inner(&self) -> EnvironmentProviderSnapshot

作用:把保存的远程地址真正翻译成“启动时有哪些环境、默认用哪个、要不要补本地环境”这份清单。它是这个文件的核心判断逻辑。

数据流:进去的是 provider 里保存的原始地址 → 先调用 normalize_exec_server_url 清理地址并判断是否禁用;如果得到真实地址,就创建一个远程 Environment;然后根据是否禁用、是否有远程环境,决定 include_local 和默认环境 → 出来的是 EnvironmentProviderSnapshot,也就是环境启动快照。

调用关系:DefaultEnvironmentProvider::snapshot 会调用它,把同步判断包成异步结果。它还调用 Environment::remote_inner 创建远程环境对象,并依赖 normalize_exec_server_url 保证地址已经被修剪、none 已被识别。

调用图:调用 2 个内部函数(remote_inner, normalize_exec_server_url);被 1 处调用(snapshot);外部调用 2 个(new, EnvironmentId)。

DefaultEnvironmentProvider::snapshot87–89 ↗
fn snapshot(&self) -> EnvironmentProviderFuture<'_>

作用:按照 EnvironmentProvider 接口的要求,返回环境启动快照。虽然默认实现现在几乎马上能算出来,但它用异步形式返回,方便别的 provider 将来可能需要联网或读文件。

数据流:进去的是当前 provider → 它把 snapshot_inner 的结果放进一个 Future(可以理解成“稍后会给结果的任务”)里 → 出来的是成功的 EnvironmentProviderSnapshot,或者接口允许的错误类型。

调用关系:这是外部通过 EnvironmentProvider 接口会调用的方法。它把具体工作交给 snapshot_inner,只负责把结果包装成统一的异步接口。

调用图:调用 1 个内部函数(snapshot_inner);外部调用 1 个(pin)。

normalize_exec_server_url92–98 ↗
fn normalize_exec_server_url(exec_server_url: Option<String>) -> (Option<String>, bool)

作用:清理并解释 CODEX_EXEC_SERVER_URL 的原始文字。它把“没填”“空白”“none”“真实地址”这几种情况分清楚,避免后面把用户意图搞错。

数据流:进去的是一个可选字符串 → 它先去掉前后空格;没有值或空字符串会变成“没有远程地址且未禁用”;值是 none(不区分大小写)会变成“没有地址但明确禁用”;其他值会变成清理后的地址 → 出来的是二元结果:可用地址,以及是否禁用。

调用关系:snapshot_inner 用它决定启动环境;其他地方如创建或更新环境时也会复用它,保证全项目对这个地址的解释是一致的。

调用图:被 3 处调用(create_inner, upsert_environment, snapshot_inner)。

tests::default_provider_requests_local_environment_when_url_is_missing109–126 ↗
async fn default_provider_requests_local_environment_when_url_is_missing()

作用:这个测试确认:完全没配置远程地址时,系统应该请求使用本地环境,而不是误以为远程环境存在。

数据流:进去的是一个没有地址的 DefaultEnvironmentProvider → 测试生成快照并拆开看 → 验证清单里没有本地和远程对象,但 include_local 为真,默认环境 ID 指向本地。

调用关系:它直接调用 DefaultEnvironmentProvider::new 和 provider 的 snapshot,检查默认配置下最常见的启动路径是否安全。

调用图:调用 1 个内部函数(new);外部调用 2 个(assert!, assert_eq!)。

tests::default_provider_requests_local_environment_when_url_is_empty129–146 ↗
async fn default_provider_requests_local_environment_when_url_is_empty()

作用:这个测试确认:远程地址配置成空字符串时,效果等同于没配置,应该走本地环境。

数据流:进去的是一个保存空字符串的 provider → 生成快照后检查环境清单和默认值 → 出来没有实际业务结果,但测试会确认 include_local 为真、默认环境是本地。

调用关系:它覆盖 normalize_exec_server_url 对空字符串的解释,并通过 snapshot 的最终表现来验证这条规则。

调用图:调用 1 个内部函数(new);外部调用 3 个(new, assert!, assert_eq!)。

tests::default_provider_omits_local_environment_for_none_value149–163 ↗
async fn default_provider_omits_local_environment_for_none_value()

作用:这个测试确认:用户写 none 时,是明确禁用环境,而不是简单地缺少远程地址。这样系统不会偷偷补上本地环境。

数据流:进去的是一个地址值为 none 的 provider → 生成快照 → 验证没有本地、没有远程、include_local 为假,并且默认状态是 Disabled。

调用关系:它保护一个容易误解的配置语义:none 是主动关闭。它间接验证 normalize_exec_server_url 和 snapshot_inner 对禁用状态的配合。

调用图:调用 1 个内部函数(new);外部调用 2 个(assert!, assert_eq!)。

tests::default_provider_adds_remote_environment_for_websocket_url166–188 ↗
async fn default_provider_adds_remote_environment_for_websocket_url()

作用:这个测试确认:配置了 WebSocket 地址(例如 ws://...,一种网络连接地址)时,系统会创建远程环境并默认使用它。

数据流:进去的是一个带远程服务器地址的 provider → 生成快照并转成便于查找的表 → 验证没有要求补本地环境,远程环境存在、确实是远程类型、保存的地址正确,默认环境指向远程。

调用关系:它检查 snapshot_inner 的主要远程路径,也确认 Environment::remote_inner 创建出的对象能被后续识别为远程环境。

调用图:调用 1 个内部函数(new);外部调用 2 个(assert!, assert_eq!)。

tests::default_provider_normalizes_exec_server_url191–200 ↗
async fn default_provider_normalizes_exec_server_url()

作用:这个测试确认:远程地址前后多写空格时,会被自动去掉,避免用户因为配置里多了空格而连不上服务器。

数据流:进去的是一个前后带空格的地址 → 生成快照 → 验证远程环境里保存的是去掉空格后的干净地址。

调用关系:它专门保护 normalize_exec_server_url 的修剪行为,但通过 DefaultEnvironmentProvider::new 和 snapshot 的完整流程来验证最终效果。

调用图:调用 1 个内部函数(new);外部调用 1 个(assert_eq!)。

主机和平台探测

这些文件检测本机身份以及周围的 OS 或云环境,这些因素会影响配置和启动行为。

cli/src/doctor/git.rs源码 ↗
domain_logicdoctor diagnostics

这个文件像一次“Git 体检”。Codex 很多时候需要读 Git 仓库信息,比如当前分支、仓库根目录等;如果 Git 没装、PATH(系统找命令的路径列表)指错了,或者 Windows 上的 Git 太老,就可能让诊断、界面显示或仓库识别出问题。它先找系统里会被执行的 git,再列出 PATH 里所有 git 候选项;如果找到了 Git,就用很短的超时时间去问它版本、执行目录、构建信息、当前分支和 fsmonitor 配置。然后它把这些原始信息整理成 DoctorCheck,也就是一份给用户看的检查结果:正常就给摘要和细节;异常就给警告、期望状态和修复建议。它还特别识别 Windows 上老版本 Git,因为这些版本可能破坏终端界面的显示。

函数细节16
git_check30–60 ↗
async fn git_check(cwd: &Path) -> DoctorCheck

作用:这是 Git 诊断的入口函数。给它一个当前目录,它会实际探测本机 Git 和当前仓库情况,最后产出一份 doctor 检查报告。

数据流:输入是当前工作目录 cwd。它先用 which 查找系统会运行的 git,再列出所有 git 候选路径,并用 get_git_repo_root 判断 cwd 是否在 Git 仓库里;如果找到了 git,就同时运行几条 git 命令读取版本、执行路径、构建选项、当前分支和 core.fsmonitor。最后把这些收集到的材料装进 GitCheckInputs,交给 git_check_from_inputs 生成 DoctorCheck。

调用关系:它是这个文件对外的主流程。doctor 命令需要检查 Git 时会调用它;它自己负责“采集现场信息”,然后把“怎么判断好坏、怎么写报告”的工作交给 git_check_from_inputs。

调用图:调用 2 个内部函数(git_candidates, git_check_from_inputs);外部调用 3 个(get_git_repo_root, join!, which)。

git_check_from_inputs62–156 ↗
fn git_check_from_inputs(inputs: GitCheckInputs) -> DoctorCheck

作用:这个函数把已经收集好的 Git 信息翻译成用户能看懂的诊断结果。它决定是显示正常,还是给出警告和修复建议。

数据流:输入是一包 GitCheckInputs,里面可能有 Git 路径、版本、仓库根目录、分支等信息。它先把这些信息整理成 details 明细,再用 git_summary 做一句摘要;之后按几种常见坏情况判断:找到 git 但跑不起来、在仓库里却找不到 git、Windows Git 太旧。输出是 DoctorCheck;如果发现问题,还会把状态改成 Warning,并附上 DoctorIssue 说明问题、期望结果和补救办法。

调用关系:git_check 在真实诊断时调用它;测试函数也直接调用它,绕开真实电脑环境,用假输入验证判断规则。它会用 push_optional_detail 添加可选明细,用 normalized_branch 美化分支名,用 old_windows_git_warning 判断 Windows 旧版 Git 风险。

调用图:调用 6 个内部函数(new, new, git_summary, normalized_branch, old_windows_git_warning, push_optional_detail);被 4 处调用(git_check, reports_git_candidates_and_repo_metadata, warns_when_git_repo_has_no_git_executable, warns_when_selected_git_cannot_report_version);外部调用 3 个(new, cfg!, format!)。

git_summary158–166 ↗
fn git_summary(inputs: &GitCheckInputs) -> String

作用:这个函数给 Git 检查结果做一句短摘要。它让报告列表里不用展开细节,也能看出 Git 大概是什么状态。

数据流:输入是 GitCheckInputs 的引用。它看 git_version 和 selected_git:有版本就直接返回版本字符串;有 git 但没有版本,就返回“找到了但版本不可用”;连 git 都没找到,就返回“没找到 git”。输出是一段摘要文字,不改动任何数据。

调用关系:git_check_from_inputs 创建 DoctorCheck 时调用它,用它填报告的 summary。它只负责写一句话,不参与实际探测或错误判断。

调用图:被 1 处调用(git_check_from_inputs)。

push_optional_detail168–172 ↗
fn push_optional_detail(details: &mut Vec<String>, label: &str, value: Option<&str>)

作用:这个小工具只在某个信息确实存在时,把它加进诊断明细。这样报告不会充满空字段。

数据流:输入是可修改的 details 列表、一个标签 label,以及一个可能为空的 value。value 存在时,它追加一行“标签: 值”;value 不存在时什么也不做。结果是 details 可能多了一条文字。

调用关系:git_check_from_inputs 多次调用它来添加 Git 版本、执行路径、构建选项、分支、fsmonitor 等可选信息。它像填表时的助手:有内容才写,没有就跳过。

调用图:被 1 处调用(git_check_from_inputs);外部调用 1 个(format!)。

normalized_branch174–180 ↗
fn normalized_branch(branch: Option<&str>) -> Option<&str>

作用:这个函数把 Git 分支名整理成更适合人看的文字。特别是 Git 返回 HEAD 时,它会说明这是“detached HEAD”,也就是没有停在普通分支上。

数据流:输入是一个可能为空的分支字符串。它遇到 HEAD 就输出 detached HEAD;遇到非空普通分支名就原样输出;空值或空字符串就输出 None。它不改动外部数据,只返回整理后的结果。

调用关系:git_check_from_inputs 在写“git branch”明细前调用它。这样报告里不会直接出现让外行困惑的 HEAD,而是给出更明确的描述。

调用图:被 1 处调用(git_check_from_inputs)。

git_candidates182–190 ↗
fn git_candidates() -> Vec<PathBuf>

作用:这个函数列出系统 PATH 里所有叫 git 的可执行文件。它能帮助用户发现是不是有多个 Git,或者 Codex 选到的不是自己以为的那个。

数据流:它没有业务输入,直接读取系统 PATH。它用 which_all 找到所有 git 候选项,用 BTreeSet 去重,最后返回路径列表;如果查找失败,就返回空列表。

调用关系:git_check 在开始诊断时调用它。git_check_from_inputs 会把这些候选项写入报告明细,方便排查 PATH 顺序和多版本 Git 问题。

调用图:被 1 处调用(git_check);外部调用 3 个(new, new, which_all)。

git_output192–204 ↗
async fn git_output(git_path: &Path, cwd: &Path, args: &[&str]) -> Option<String>

作用:这个函数安全地运行一条 git 命令,并拿回它的文本输出。它给每条命令设置 2 秒超时,避免诊断卡住。

数据流:输入是 git 程序路径、当前目录 cwd,以及要传给 git 的参数列表。它创建命令,设置 GIT_OPTIONAL_LOCKS=0 以减少 Git 锁文件带来的干扰,在 cwd 下运行,并设置超时;命令成功完成后,把输出交给 command_output_text 清理。输出是清理后的字符串;如果超时、运行失败或命令返回错误,就输出 None。

调用关系:git_check 找到 git 后会用它询问版本、执行路径、构建选项、当前分支和配置。它把“怎么跑外部命令”的脏活封装起来,让上层只关心拿到没拿到结果。

调用图:调用 1 个内部函数(command_output_text);外部调用 2 个(new, timeout)。

command_output_text206–222 ↗
fn command_output_text(output: Output) -> Option<String>

作用:这个函数把外部命令的原始输出变成干净的一行文字。它过滤失败命令和空输出,让报告内容更整洁。

数据流:输入是一次进程运行的 Output,里面有退出状态和标准输出。它先确认命令是否成功;成功后把 stdout 从字节转成文字,逐行去掉首尾空白,丢掉空行,再用分号连接成一行。输出是 Some(文字);如果命令失败或没有有效内容,就返回 None。

调用关系:git_output 在拿到 git 命令结果后调用它。它是外部命令输出进入诊断报告前的“清洗器”。

调用图:被 1 处调用(git_output);外部调用 1 个(from_utf8_lossy)。

git_entry_summary224–241 ↗
fn git_entry_summary(repo_root: &Path) -> String

作用:这个函数查看仓库根目录下的 .git 到底是什么。它能区分普通 .git 文件夹、指向别处的 .git 文件,以及缺失或无法读取的情况。

数据流:输入是仓库根目录路径。它拼出 repo_root/.git,然后读取文件系统元数据:如果是目录,返回 directory;如果是文件,会尝试读出 gitdir: 后面的真实路径并返回 file -> 路径;如果是其他类型、缺失或读不了,就返回对应说明。它只读取磁盘,不修改文件。

调用关系:git_check 在发现仓库根目录后会用它生成 .git entry 明细。这个信息对理解 worktree(一个仓库的额外工作目录)或子模块一类布局很有帮助。

调用图:外部调用 4 个(join, format!, metadata, read_to_string)。

old_windows_git_warning243–256 ↗
fn old_windows_git_warning(version: Option<&str>, is_windows: bool) -> Option<String>

作用:这个函数判断 Windows 上的 Git 是否老到可能引发终端界面显示问题。它只在 Windows 环境下给警告。

数据流:输入是可能存在的 Git 版本字符串,以及一个 is_windows 布尔值。不是 Windows 就直接返回 None;没有版本也返回 None;版本里含 msysgit 会直接警告;否则用 parse_git_version 拆出主版本和次版本,若低于或等于 2.34 的 Git for Windows,就返回警告文字。输出是可能存在的一条警告说明。

调用关系:git_check_from_inputs 用它作为最后一类风险判断。它把“旧版 Windows Git 的特殊坑”集中在一个地方,避免主报告逻辑里塞太多版本细节。

调用图:调用 1 个内部函数(parse_git_version);被 1 处调用(git_check_from_inputs)。

parse_git_version265–279 ↗
fn parse_git_version(version: &str) -> Option<ParsedGitVersion>

作用:这个函数从 Git 的版本文字里拆出数字版本号。比如把“git version 2.34.1.windows.1”变成 major=2、minor=34、patch=1。

数据流:输入是一整段版本字符串。它要求字符串以 git version 开头,然后取后面的数字部分;如果带 .windows. 后缀,就只看前面的主版本、次版本、补丁版本。解析成功时输出 ParsedGitVersion;格式不对或数字解析失败时输出 None。

调用关系:old_windows_git_warning 调用它来判断 Git 是否太旧。测试函数 parses_git_for_windows_version 专门验证它能正确理解 Git for Windows 的版本格式。

调用图:被 1 处调用(old_windows_git_warning)。

tests::parses_git_for_windows_version288–305 ↗
fn parses_git_for_windows_version()

作用:这个测试确认 parse_git_version 能正确解析 Git for Windows 的版本号。它防止以后改代码时把 Windows 版本格式弄坏。

数据流:输入是测试里写死的两个版本字符串。测试调用 parse_git_version,然后把返回的 ParsedGitVersion 和预期数字比较;如果不一致,测试失败。它不改动真实系统环境。

调用关系:它只在测试时运行。它保护 parse_git_version,而 parse_git_version 又服务于 old_windows_git_warning 的旧版本判断。

调用图:外部调用 1 个(assert_eq!)。

tests::classifies_old_windows_git308–331 ↗
fn classifies_old_windows_git()

作用:这个测试确认旧版 Windows Git 会被警告,新版不会,非 Windows 也不会误报。它保证警告规则不会太松或太严。

数据流:输入是几组写死的版本字符串和 is_windows 标志。测试调用 old_windows_git_warning,并把返回结果同预期比较:老 Windows Git 应有警告,新 Windows Git 应没有,非 Windows 应没有。输出是测试通过或失败。

调用关系:它只在测试阶段运行。它直接覆盖 old_windows_git_warning 的关键分支,间接也检查 parse_git_version 是否能支撑这个判断。

调用图:外部调用 1 个(assert_eq!)。

tests::warns_when_git_repo_has_no_git_executable334–345 ↗
fn warns_when_git_repo_has_no_git_executable()

作用:这个测试确认:如果当前目录是 Git 仓库,但系统找不到 git 命令,诊断会给出警告。这样用户不会误以为环境完全正常。

数据流:输入是一份手工构造的 GitCheckInputs,其中 repo_root 有值,但 selected_git 没有值。测试调用 git_check_from_inputs,检查输出 DoctorCheck 的状态是 Warning,摘要是“检测到仓库但没找到 Git 可执行文件”。

调用关系:它在测试中直接调用 git_check_from_inputs,不走真实 which 和文件系统探测。它验证 git_check_from_inputs 里“有仓库但无 git”的判断规则。

调用图:调用 1 个内部函数(git_check_from_inputs);外部调用 3 个(from, assert_eq!, default)。

tests::warns_when_selected_git_cannot_report_version348–357 ↗
fn warns_when_selected_git_cannot_report_version()

作用:这个测试确认:如果找到了 git 程序,但 git --version 跑不出结果,诊断会给出警告。这个情况通常表示 PATH 指到坏掉的 Git。

数据流:输入是一份假的 GitCheckInputs,其中 selected_git 指向 /usr/bin/git,但 git_version 为空。测试调用 git_check_from_inputs,检查输出状态是 Warning,摘要是“Git 可执行文件找到了但不能运行”。

调用关系:它只在测试时运行,用来保护 git_check_from_inputs 的故障判断。它模拟 git_check 真实探测中“找到程序但命令失败”的情况。

调用图:调用 1 个内部函数(git_check_from_inputs);外部调用 3 个(from, assert_eq!, default)。

tests::reports_git_candidates_and_repo_metadata360–377 ↗
fn reports_git_candidates_and_repo_metadata()

作用:这个测试确认正常情况下,报告会把多个 Git 候选项、仓库信息、分支和 fsmonitor 配置写进明细。它保证诊断结果不只是说“正常”,还给足排查线索。

数据流:输入是一份完整的假 GitCheckInputs,包括 Git 路径列表、版本、执行路径、仓库根目录、.git 类型、分支名和 core.fsmonitor。测试调用 git_check_from_inputs,确认状态是 Ok,并检查 details 里包含候选数量、分支和 fsmonitor 等关键文字。

调用关系:它在测试时直接覆盖 git_check_from_inputs 的正常报告路径。它也间接验证 push_optional_detail 和 normalized_branch 这类小工具确实把信息放进了最终报告。

调用图:调用 1 个内部函数(git_check_from_inputs);外部调用 5 个(from, assert!, assert_eq!, default, vec!)。

cli/src/doctor/system.rs源码 ↗
domain_logicdoctor 诊断命令运行时

很多命令行问题不是程序本身坏了,而是电脑环境不一样:系统版本不同、语言区域不同、默认编辑器没设、分页器设置特殊。这个文件就像医生问诊时先量体温、看病历。它先收集当前机器的操作系统信息和一组环境变量。环境变量可以理解成系统给程序看的“小纸条”,比如告诉程序用哪个编辑器打开文本。然后它把这些信息整理成一个 DoctorCheck,也就是一条诊断结果:状态是正常,但附带详细资料,方便用户或维护者看。这里还特别把“没设置编辑器”写成 not set,避免报告里看不出是空缺还是漏掉了。测试部分用假数据检查报告顺序和内容,保证以后改代码时不会把诊断输出弄乱。

函数细节5
SystemCheckInputs::detect22–57 ↗
fn detect() -> Self

作用:这个函数实际去读取当前电脑的系统环境。它把操作系统、系统语言、语言相关环境变量、编辑器设置、分页器设置收集到一个统一的小包里。

数据流:进去时不需要外部参数;它直接读取电脑当前状态,包括操作系统信息、系统语言和环境变量。它把读到的内容按类别放进 SystemCheckInputs:语言变量只记录存在的,编辑器变量即使没设置也写成“not set”,分页器变量只记录存在的。出来的是一份完整的系统检查原始材料。

调用关系:它是系统检查的“采样”步骤。system_check 会先调用它拿到真实机器的数据,然后把这些数据交给 system_check_from_inputs 去整理成诊断报告。它内部依赖外部库读取操作系统信息和系统语言。

调用图:被 1 处调用(system_check);外部调用 2 个(get, get_locale)。

system_check60–62 ↗
fn system_check() -> DoctorCheck

作用:这是外部真正会调用的系统环境检查入口。别人不需要知道怎么采集信息,只要调用它就能得到一条整理好的诊断结果。

数据流:进去时不需要参数;它先让 SystemCheckInputs::detect 读取当前机器环境,再把这份原始数据交给 system_check_from_inputs。出来的是一个 DoctorCheck,里面有检查名称、分类、状态、摘要和详细环境信息。

调用关系:它像一个前台接待员:先叫 SystemCheckInputs::detect 去量体温和记录信息,再把记录交给 system_check_from_inputs 写成正式报告。doctor 命令的整体流程会用它来加入“system.environment”这项检查。

调用图:调用 2 个内部函数(detect, system_check_from_inputs)。

system_check_from_inputs64–103 ↗
fn system_check_from_inputs(inputs: SystemCheckInputs) -> DoctorCheck

作用:这个函数把已经收集好的系统信息排版成一条诊断报告。它的价值是把“零散数据”变成用户能读懂、程序也能统一处理的 DoctorCheck。

数据流:进去的是一个 SystemCheckInputs,里面有系统名称、系统类型、版本、语言和几类环境变量。它按固定顺序生成 details 明细,比如 os、os type、os version、os language,再补上 LANG、EDITOR、PAGER 等变量;如果系统语言拿不到,就写成 unavailable。出来的是一个状态为 Ok 的 DoctorCheck,并且带有摘要和明细列表。

调用关系:system_check 在真实运行时会调用它;测试函数也会直接调用它,用假输入确认输出是否稳定。它负责最后“写报告”,并调用 DoctorCheck::new 创建报告对象,再追加 details。

调用图:调用 1 个内部函数(new);被 3 处调用(system_check, system_check_handles_missing_os_language, system_check_reports_os_language_locale_editor_and_pager_env);外部调用 2 个(format!, vec!)。

tests::system_check_reports_os_language_locale_editor_and_pager_env112–152 ↗
fn system_check_reports_os_language_locale_editor_and_pager_env()

作用:这个测试确认:当系统语言、语言环境、编辑器和分页器都存在时,报告会把它们完整、按预期顺序写出来。

数据流:进去的是测试里手工造出的假环境数据,包括 macOS、en-US、LANG、EDITOR、VISUAL 和多种 PAGER 设置。测试把这些数据交给 system_check_from_inputs,得到一条诊断报告。然后它检查摘要和明细是否完全等于预期内容;如果顺序或文字变了,测试就会失败。

调用关系:它不参与用户实际运行,只在测试时保护 system_check_from_inputs 的行为。它用固定输入模拟一台配置完整的电脑,确保正式报告不会漏字段或乱排序。

调用图:调用 1 个内部函数(system_check_from_inputs);外部调用 3 个(from, new, assert_eq!)。

tests::system_check_handles_missing_os_language155–181 ↗
fn system_check_handles_missing_os_language()

作用:这个测试确认:当系统语言读不到时,报告不会崩溃,也不会含糊其辞,而是明确写出 unavailable。

数据流:进去的是测试构造的假 Linux 环境,其中 os_language 是 None,表示系统语言不可用,编辑器变量也被设置成 not set。测试调用 system_check_from_inputs 得到报告,再检查摘要和明细是否写成“OS language unavailable”和“os language: unavailable”。

调用关系:它是 system_check_from_inputs 的边界情况测试。也就是说,它专门验证不完美环境下的表现,避免真实用户电脑缺少语言信息时 doctor 命令给出错误或难懂的结果。

调用图:调用 1 个内部函数(system_check_from_inputs);外部调用 3 个(from, new, assert_eq!)。

cloud-tasks/src/env_detect.rs源码 ↗
orchestrationstartup / environment selection

这个文件解决的是“环境选择”问题。云端可能有很多代码运行环境,如果选错了,任务可能跑到不相关的项目里。它先读取本地 git 远程地址,也就是代码仓库连接到 GitHub 的地址,从里面拆出 owner/repo。然后它去服务器问:“这个仓库有没有专属环境?”如果找到了,就按用户指定标签、唯一环境、置顶环境、任务数量这些规则挑一个。如果仓库查不到,就再拉取全局环境列表继续挑。文件里还提供了给界面用的环境列表,会把仓库环境和全局环境合并、去重、排序。可以把它理解成酒店前台:先看你是不是某个团队预订的房间;没有的话,再从所有空房里按规则给你安排一间。

函数细节7
autodetect_environment_id25–108 ↗
async fn autodetect_environment_id(
    base_url: &str,
    headers: &HeaderMap,
    desired_label: Option<String>,
) -> anyhow::Result<AutodetectSelection>

作用:自动决定当前任务应该使用哪个云端环境。调用者只要给它服务器地址、请求头和可选的环境标签,它就尽量选出一个合适的环境。

数据流:进去的是服务器 base_url、HTTP 请求头 headers,以及用户可能指定的 desired_label。它先读取本地 git 远程地址,解析出 GitHub 仓库,再向服务器查询这些仓库对应的环境;如果能挑出一个,就返回环境 id 和标签。否则它再请求全局环境列表,记录返回内容方便排错,解析 JSON,继续挑选;最后要么返回选中的 AutodetectSelection,要么报错说没有可用环境。

调用关系:这是自动选环境的主流程,由 run_main 在启动任务时调用。它把本地仓库发现交给 get_git_origins,把 GitHub 地址拆解交给 parse_owner_repo,把具体挑选规则交给 pick_environment_row;需要访问服务器时,它会创建 HTTP 客户端并请求接口。

调用图:调用 3 个内部函数(get_git_origins, parse_owner_repo, pick_environment_row);被 1 处调用(run_main);外部调用 9 个(clone, new, bail!, builder, build_reqwest_client_with_custom_ca, append_error_log, format!, from_str, to_string_pretty)。

pick_environment_row110–145 ↗
fn pick_environment_row(
    envs: &[CodeEnvironment],
    desired_label: Option<&str>,
) -> Option<CodeEnvironment>

作用:从一堆候选环境里挑出最合适的一个。它把“怎么选”的规则集中在一起,避免每个地方都自己随便挑。

数据流:进去的是环境列表 envs,以及可选的 desired_label。它先处理空列表;如果用户指定了标签,就忽略大小写找同名环境;如果只有一个环境,就直接选它;如果有置顶环境,就选置顶的;最后用 task_count,也就是任务数量,作为粗略判断,选任务最多的或第一个。出来的是一个复制出的 CodeEnvironment,或者没有匹配时返回 None,同时会写一些排错日志。

调用关系:它被 autodetect_environment_id 调用,是自动选择流程里的“裁判”。前面的网络和 git 查询负责收集候选项,它负责按固定顺序做最终决定。

调用图:被 1 处调用(autodetect_environment_id);外部调用 5 个(is_empty, iter, len, append_error_log, format!)。

get_json147–169 ↗
async fn get_json(
    url: &str,
    headers: &HeaderMap,
) -> anyhow::Result<T>

作用:向某个网址发 GET 请求,并把返回内容解析成调用者想要的数据类型。它相当于一个通用的“取 JSON 小工具”。

数据流:进去的是 url 和 HTTP 请求头 headers。它创建支持自定义证书的 reqwest HTTP 客户端,带上请求头访问这个地址,读取状态码、内容类型和正文。如果服务器返回失败状态,就把状态和正文放进错误里;如果成功,就把正文从 JSON 文本解析成目标类型 T 并返回。

调用关系:它是这个文件里访问环境接口时复用的网络工具。它自己不决定选哪个环境,只负责把服务器上的 JSON 安全地拿回来;出错时提供足够信息,方便上层流程判断是继续尝试还是失败。

调用图:外部调用 6 个(clone, bail!, builder, build_reqwest_client_with_custom_ca, append_error_log, format!)。

get_git_origins171–210 ↗
fn get_git_origins() -> Vec<String>

作用:找出当前本地 git 仓库配置过的远程地址。这样程序才能知道“我现在这个目录对应 GitHub 上哪个仓库”。

数据流:它不接收参数,而是运行 git 命令读取远程地址。它先尝试 git config --get-regexp remote\..*\.url;如果没有拿到,再退回到 git remote -v。拿到多条地址后,它会交给 uniq 排序去重;如果两个命令都失败或没有结果,就返回空列表。

调用关系:它被 autodetect_environment_id 和 list_environments 调用,是仓库相关环境查询的第一步。后续 parse_owner_repo 会继续把这些地址拆成 GitHub 的 owner/repo。

调用图:调用 1 个内部函数(uniq);被 2 处调用(autodetect_environment_id, list_environments);外部调用 3 个(from_utf8_lossy, new, new)。

uniq212–216 ↗
fn uniq(mut v: Vec<String>) -> Vec<String>

作用:把一组字符串排序并去掉重复项。这里主要用来清理 git 远程地址,避免同一个地址被查很多遍。

数据流:进去的是一个字符串列表。它先原地排序,让相同内容挨在一起;再删除重复项。出来的是排序后、没有重复字符串的列表。

调用关系:它由 get_git_origins 调用,是一个很小的辅助步骤。get_git_origins 负责收集地址,uniq 负责把结果整理干净。

调用图:被 1 处调用(get_git_origins)。

parse_owner_repo218–252 ↗
fn parse_owner_repo(url: &str) -> Option<(String, String)>

作用:从 GitHub 远程地址里拆出仓库的 owner 和 repo。比如把 git@github.com:openai/codex.git 变成 openai 和 codex。

数据流:进去的是一个远程仓库地址字符串。它会先做简单清理,兼容 ssh://、git@github.com:、https://github.com/、git://github.com/ 等常见写法,并去掉末尾的 .git。能识别时返回 owner/repo 这一对字符串;识别不了,比如不是 GitHub 地址,就返回 None。成功解析时还会写日志。

调用关系:它被 autodetect_environment_id 和 list_environments 调用,位置在读取 git 地址之后、请求服务器之前。只有它成功拆出 owner/repo,后面的代码才知道该向服务器查询哪个仓库的环境。

调用图:被 2 处调用(autodetect_environment_id, list_environments);外部调用 2 个(append_error_log, format!)。

list_environments256–362 ↗
async fn list_environments(
    base_url: &str,
    headers: &HeaderMap,
) -> anyhow::Result<Vec<crate::app::EnvironmentRow>>

作用:列出当前用户可以选择的环境,主要给终端界面里的选择弹窗使用。它不只自动选一个,而是整理出一张干净、好排序的环境清单。

数据流:进去的是服务器 base_url 和请求头 headers。它先读本地 git 远程地址并解析 GitHub 仓库,然后逐个查询仓库专属环境,把结果按环境 id 放进 map 里去重,并合并标签、置顶状态和仓库提示。接着它再查询全局环境列表,继续合并进去;如果全局查询失败但仓库结果已经有了,就保留已有结果。最后它把 map 变成列表,按置顶优先、标签字母顺序、id 顺序排序,返回 EnvironmentRow 列表。

调用关系:它被 resolve_environment_id 和 run_main 调用,用在需要展示或解析环境选择的时候。它和 autodetect_environment_id 使用同一套前置能力:get_git_origins 找仓库地址,parse_owner_repo 拆仓库名,网络请求拿环境数据;不同的是,它的目标是给人看一份列表,而不是直接替人拍板选一个。

调用图:调用 2 个内部函数(get_git_origins, parse_owner_repo);被 2 处调用(resolve_environment_id, run_main);外部调用 4 个(new, format!, info!, warn!)。

config/src/host_name.rs源码 ↗
config首次需要识别本机主机名时运行,之后作为缓存信息被反复读取

有些功能需要判断“这台机器是谁”,比如远程沙箱规则可能只想匹配某些远程主机。这个文件做的事就是:先向操作系统要机器名,再把它清理成统一格式,比如去掉空格、去掉末尾的点、转成小写;然后尽量通过本机的 DNS 解析器找完整域名,也就是 FQDN(Fully Qualified Domain Name,带域名后缀的完整名字,比如 runner-01.ci.example.com)。如果找不到完整域名,它不会直接放弃,而是退回使用清理过的系统主机名。这里还有一个重要点:结果存在 LazyLock 里,意思是“第一次有人问时才计算,之后一直复用”。像门口登记册一样,第一次查清楚后写下来,后面的人直接看登记册,不再反复打电话确认。

函数细节8
host_name15–17 ↗
fn host_name() -> Option<String>

作用:这是对外提供主机名的入口。别人想知道当前进程认为“本机叫什么”时,就调用它。

数据流:调用时不需要传入东西 → 它读取全局缓存 HOST_NAME;如果这是第一次读取,缓存会触发真正的计算 → 返回一个可能存在的字符串:有主机名就返回 Some,没有可用名字就返回 None,并且之后会复用同一个结果。

调用关系:它本身不直接问操作系统,而是依赖 HOST_NAME 这个懒加载缓存;缓存第一次初始化时会交给 compute_host_name 去做辛苦活。其他模块只需要调用 host_name,不用关心底层是 Unix、Windows,还是有没有 DNS。

compute_host_name19–34 ↗
fn compute_host_name() -> Option<String>

作用:这是实际算出本机标准主机名的地方。它先拿系统主机名,再尽量升级成 DNS 里的完整主机名,最后保证能退回到一个可用的短名字。

数据流:进去没有参数 → 它先调用 gethostname 从操作系统拿原始机器名,再交给 normalize_host_name 清理;如果清理后为空,就返回 None → 接着调用 local_fqdn_for_hostname 尝试找完整域名;找到了就返回完整域名,找不到就返回清理后的系统主机名。

调用关系:它由全局缓存 HOST_NAME 在第一次需要主机名时调用。它把“清理名字”的小事交给 normalize_host_name,把“按平台查完整域名”的事交给 local_fqdn_for_hostname,自己负责决定优先级:完整 DNS 名优先,失败时用系统名兜底。

调用图:调用 2 个内部函数(local_fqdn_for_hostname, normalize_host_name);外部调用 1 个(gethostname)。

normalize_host_name36–39 ↗
fn normalize_host_name(hostname: &str) -> Option<String>

作用:这个函数把主机名整理成统一、好比较的样子。它避免同一台机器因为大小写、空格或末尾点不同而被当成不同名字。

数据流:输入一个主机名文本 → 它去掉前后空白,再去掉末尾的点,然后检查是不是空字符串 → 如果还有内容,就转成小写后返回;如果清理完没内容,就返回 None。

调用关系:compute_host_name 用它清理操作系统给出的原始主机名;normalize_fqdn_candidate 也用它清理 DNS 候选名。它是这个文件里所有主机名格式统一的基础小工具。

调用图:被 2 处调用(compute_host_name, normalize_fqdn_candidate)。

local_fqdn_for_hostname66–68 ↗
fn local_fqdn_for_hostname(_hostname: &str) -> Option<String>

作用:这个函数尝试把一个本机短名字换成完整 DNS 名。不同操作系统查法不同,所以这里按平台走不同路线。

数据流:输入一个已经清理过的主机名 → 在 Unix 上,它用 getaddrinfo 向本机解析器查询,并要求返回规范名;在 Windows 上,它向系统询问物理机器的完整 DNS 名;其他平台则直接表示查不到 → 拿到候选名字后,再交给 normalize_fqdn_candidate 确认它真的是带点号的完整域名,最后返回 Some 或 None。

调用关系:compute_host_name 在拿到系统主机名后会调用它,希望得到更正式的 FQDN。它内部会借助系统或库函数,比如 Unix 的 getaddrinfo、Windows 的 get_computer_name;如果这些外部来源不给力,它就返回 None,让上层退回短主机名。

调用图:被 1 处调用(compute_host_name);外部调用 3 个(default, getaddrinfo, get_computer_name)。

normalize_fqdn_candidate70–72 ↗
fn normalize_fqdn_candidate(hostname: &str) -> Option<String>

作用:这个函数检查一个候选名字能不能算作完整 DNS 名。它不接受只有机器短名的结果,比如 runner-01。

数据流:输入一个候选主机名 → 先用 normalize_host_name 做统一清理和小写化 → 再检查里面是否包含点号;有点号才认为像完整域名并返回,没有点号就返回 None。

调用关系:local_fqdn_for_hostname 用它过滤系统或 DNS 返回的候选名,防止把短名字误认为完整域名。测试函数也专门围绕它验证:完整名字能过,短名字不能过,大小写和末尾点会被正常清理。

调用图:调用 1 个内部函数(normalize_host_name)。

tests::normalize_fqdn_candidate_accepts_dns_qualified_name80–85 ↗
fn normalize_fqdn_candidate_accepts_dns_qualified_name()

作用:这个测试确认:正常的完整 DNS 名会被接受。它保护的是最基本的成功场景。

数据流:输入固定样例 runner-01.ci.example.com → 调用 normalize_fqdn_candidate → 用 assert_eq! 检查结果是不是同一个小写完整域名。

调用关系:它是 normalize_fqdn_candidate 的单元测试之一。运行测试时由 Rust 测试框架调用,用来确保过滤规则不会误伤合法的完整主机名。

调用图:外部调用 1 个(assert_eq!)。

tests::normalize_fqdn_candidate_rejects_short_name88–90 ↗
fn normalize_fqdn_candidate_rejects_short_name()

作用:这个测试确认:只有短名字、没有域名后缀的主机名不会被当成完整 DNS 名。

数据流:输入固定样例 runner-01 → 调用 normalize_fqdn_candidate → 用 assert_eq! 检查结果是不是 None,也就是明确拒绝。

调用关系:它补上了失败场景的测试。因为 local_fqdn_for_hostname 依赖 normalize_fqdn_candidate 来分辨完整名和短名,所以这个测试能防止以后有人不小心放宽规则。

调用图:外部调用 1 个(assert_eq!)。

tests::normalize_fqdn_candidate_trims_trailing_dot_and_normalizes_case93–98 ↗
fn normalize_fqdn_candidate_trims_trailing_dot_and_normalizes_case()

作用:这个测试确认:候选完整域名即使有大写字母和末尾点,也会被整理成统一格式。

数据流:输入 RUNNER-01.CI.EXAMPLE.COM. → 调用 normalize_fqdn_candidate → 它应去掉末尾点、转成小写,并返回 runner-01.ci.example.com;测试用 assert_eq! 验证这个结果。

调用关系:它测试 normalize_fqdn_candidate 间接使用 normalize_host_name 的清理效果。这样可以保证从系统或 DNS 拿到的名字格式不规整时,后续匹配仍然稳定。

调用图:外部调用 1 个(assert_eq!)。

运行时来源检查

此文件报告正在运行哪个 Codex 二进制程序,以及关键捆绑辅助工具解析是否正常,尤其是 ripgrep。

cli/src/doctor/runtime.rs源码 ↗
domain_logicdoctor diagnostics

这个文件像是给 Codex 做一张“出生证明”和“工具箱检查表”。很多问题不是代码本身坏了,而是用户装的版本、安装方式、系统平台,或者依赖的搜索命令不对。这里先找出当前正在运行的可执行文件,再判断它像是 npm、brew、独立包,还是本地编译出来的。然后它记录版本号、系统平台、构建提交号这些线索。另一个检查会看 Codex 用来搜文件的 rg 命令,也就是 ripgrep(一个很快的文件搜索工具)是否真的可用:如果是随包带的,就检查那个文件在不在;如果是从系统路径里找的,就尝试运行 rg --version。这些检查一般不会直接让程序失败,而是给出“正常”或“警告”,并在有问题时提示用户安装 ripgrep 或修复 Codex 包。

函数细节5
runtime_check24–49 ↗
fn runtime_check() -> DoctorCheck

作用:这个函数生成一条关于“当前 Codex 是怎么跑起来的”的诊断结果。用户报告问题时,它能告诉维护者:现在运行的是哪个版本、什么平台、什么安装方式、可执行文件在哪里。

数据流:进去时不需要外部参数;它会读取当前进程的可执行文件路径、系统名、CPU 架构、包版本和构建提交号。然后它根据可执行文件推断安装环境,把这些信息整理成若干明细。最后出来的是一个 DoctorCheck 诊断项,状态固定是正常,因为它只是提供信息,不负责判定安装是否坏掉。

调用关系:它是运行时诊断流程里的信息采集点。它会把当前可执行文件交给 doctor_install_context 判断安装背景,用 install_method_name 把安装方式变成短名称,再用 push_path_detail 把路径写进明细,最后通过 DoctorCheck::new 组装成手册式的检查结果。

调用图:调用 2 个内部函数(new, install_method_name);外部调用 5 个(current_exe, format!, doctor_install_context, push_path_detail, vec!)。

search_check57–117 ↗
fn search_check() -> DoctorCheck

作用:这个函数检查 Codex 依赖的文件搜索命令能不能用。没有它,Codex 本身可能能启动,但涉及查找文件的功能会悄悄变差,用户只会觉得“搜索怎么坏了”。

数据流:它先读取当前可执行文件路径,并据此推断安装环境;再从安装环境里拿到应该使用的 rg 搜索命令。若这个命令看起来是一个具体文件路径,它就检查这个文件是否存在且真的是文件;若只是命令名,它就尝试运行 rg --version。最后它输出一个 DoctorCheck:可验证就标为正常,不可验证就标为警告,并附上修复建议。

调用关系:它在 doctor 的运行环境检查中专门负责“搜索工具”这一项。它会调用 search_provider 判断搜索工具来自随包附带还是系统环境,然后用文件元数据检查或启动子进程试运行命令;结果再交给 DoctorCheck::new 包装,必要时追加 remediation,也就是给用户看的修复提示。

调用图:调用 2 个内部函数(new, search_provider);外部调用 8 个(from_utf8_lossy, new, current_exe, format!, metadata, doctor_install_context, unreachable!, vec!)。

install_method_name119–127 ↗
fn install_method_name(context: &InstallContext) -> &'static str

作用:这个小函数把安装方式转换成简短、适合显示的名字,比如 npmbrewstandalone。这样诊断摘要不用暴露内部复杂结构,读起来更像人话。

数据流:进去的是一个安装环境对象,里面记录 Codex 是通过哪种方式安装的。它查看其中的安装方式字段,并把不同情况映射成固定字符串。出来的是一个简短英文标签,不改动任何外部状态。

调用关系:它只被 runtime_check 使用。runtime_check 需要一句简短摘要,例如“running npm on macos-arm64”,于是把安装环境交给它,让它负责把内部枚举值翻译成显示用名称。

调用图:被 1 处调用(runtime_check)。

search_provider129–149 ↗
fn search_provider(context: &InstallContext) -> &'static str

作用:这个函数判断当前使用的搜索工具是 Codex 包里自带的,还是从用户系统里找到的。这个区别很重要:自带工具坏了通常要修包,系统工具缺失则通常要用户安装 ripgrep。

数据流:进去的是安装环境对象。它先取出实际会运行的 rg 命令路径,再看这个路径是否位于 Codex 包的目录里,或者旧版独立安装的资源目录里。符合这些情况就输出 bundled,意思是随包附带;否则输出 system,意思是依赖系统里已有的命令。

调用关系:它由 search_check 调用,用来给搜索检查补上一句“来源说明”。search_check 随后会根据同一个命令做可用性验证,并把这个来源写进诊断明细和摘要里,帮助用户判断该修 Codex 包还是修系统环境。

调用图:调用 1 个内部函数(rg_command);被 1 处调用(search_check);外部调用 1 个(matches!)。

build_commit151–155 ↗
fn build_commit() -> &'static str

作用:这个函数取出 Codex 构建时对应的代码提交号。提交号就像代码版本的精确指纹,能帮助维护者确认用户运行的到底是哪一份代码。

数据流:它不接收参数,而是在编译时写入的环境变量里查找 CODEX_BUILD_COMMIT,找不到再查 GIT_COMMIT。如果两个都没有,就返回 unknown。它只读取编译期信息,不访问网络,也不修改状态。

调用关系:它被 runtime_check 用来填充运行环境诊断的明细。这样诊断结果里除了普通版本号,还能带上更精确的源码位置,方便排查某个问题是否已经在后续提交里修复。

调用图:外部调用 1 个(option_env!)。