Skip to content

CLI 调度器 — 代码走读

codex-rs/cli/src/main.rs — CLI 主入口(~3773 行)

程序入口与 argv[0] 多工具分发

Codex CLI 部署为单个二进制文件,但需要暴露多个工具入口(codex-linux-sandboxapply_patchcodex-execve-wrapper)。arg0_dispatch 通过检查 argv[0](即程序被调用时的文件名)来实现多工具分发:

rust
fn main() -> anyhow::Result<()> {
    arg0_dispatch_or_else(|arg0_paths: Arg0DispatchPaths| async move {
        cli_main(arg0_paths).await?;
        Ok(())
    })
}

arg0_dispatch() 内部分发逻辑:

  1. 提取 argv[0]file_name() 部分
  2. Unix 特殊路径:若 exe_name == "codex-execve-wrapper",在单线程 tokio 运行时上运行 shell 提权包装器
  3. exe_name == "codex-linux-sandbox",调用 codex_linux_sandbox::run_main()(不会返回)
  4. exe_name == "apply_patch""applypatch",调用 codex_apply_patch::main()
  5. 检查 argv[1] 是否等于 CODEX_FS_HELPER_ARG1CODEX_CORE_APPLY_PATCH_ARG1,分发到对应的辅助入口

PATH 环境变量准备prepare_path_env_var_with_aliases()~/.codex/tmp/arg0/ 下创建临时目录,其中包含指向当前可执行文件的符号链接(apply_patchcodex-linux-sandboxcodex-execve-wrapper),并将此目录添加到 PATH 前面。临时目录内的 .lock 文件防止清理器误删正在使用的目录。

arg0_dispatch_or_else 包装器会:

  • 在名为 "codex-main" 的线程上启动,栈大小为 16 MB
  • 构建同样使用 16 MB 栈大小的多线程 tokio 运行时
  • 构造 Arg0DispatchPaths 并保持临时目录存活直到异步 main 完成
rust
pub struct Arg0DispatchPaths {
    pub codex_self_exe: Option<PathBuf>,
    pub codex_linux_sandbox_exe: Option<PathBuf>,
    pub main_execve_wrapper_exe: Option<PathBuf>,
}

MultitoolCli — 顶层 CLI 定义

rust
#[derive(Debug, Parser)]
#[clap(
    author, version,
    subcommand_negates_reqs = true,
    bin_name = "codex",
    override_usage = "codex [OPTIONS] [PROMPT]\n       codex [OPTIONS] <COMMAND> [ARGS]"
)]
struct MultitoolCli {
    #[clap(flatten)] pub config_overrides: CliConfigOverrides,
    #[clap(flatten)] pub feature_toggles: FeatureToggles,
    #[clap(flatten)] remote: InteractiveRemoteOptions,
    #[clap(flatten)] interactive: TuiCli,
    #[clap(subcommand)] subcommand: Option<Subcommand>,
}

cli_main() — 子命令路由器

核心分发逻辑:

rust
async fn cli_main(arg0_paths: Arg0DispatchPaths) -> Result<()> {
    let cli = MultitoolCli::parse();
    let config_overrides = cli.fold_config_overrides();
    match cli.subcommand {
        Some(Subcommand::Exec(args)) => exec::run_main(args, paths),
        Some(Subcommand::AppServer(args)) => app_server::run_main(args, paths),
        Some(Subcommand::Login) => login_flow(),
        Some(Subcommand::Logout) => logout_flow(),
        Some(Subcommand::Review(args)) => review_main(args),
        Some(Subcommand::Mcp(args)) => mcp_main(args),
        Some(Subcommand::McpServer(args)) => mcp_server_main(args),
        Some(Subcommand::Resume(args)) => resume_main(args),
        Some(Subcommand::Archive(args)) => archive_main(args),
        Some(Subcommand::Doctor) => doctor_main(),
        Some(Subcommand::Sandbox(args)) => sandbox_main(args),
        Some(Subcommand::Apply(args)) => apply_main(args),
        Some(Subcommand::Execpolicy(args)) => execpolicy_main(args),
        // ... 更多子命令
        None => run_interactive_tui(cli),
    }
}

关键子命令处理模式:

  • 无子命令(交互 TUI):调用 run_interactive_tui() 启动 TUI,支持可选的远程端点
  • Exec:继承根级共享选项,调用 codex_exec::run_main()
  • Review:内部重新解析为 codex exec 并设置 Review 命令变体——reviewexec 的薄包装
  • Resume/Fork:合并根级和 resume/fork 范围的标志,然后运行交互 TUI
  • Sandbox:使用条件编译(#[cfg(target_os = ...)])分发到平台特定沙箱

fold_config_overrides — 配置覆盖合并

FeatureToggles--enable/--disable 标志转换为 features.<name>=true/false 格式,然后合并到 CliConfigOverrides.raw_overrides

rust
#[derive(Debug, Default, Parser, Clone)]
struct FeatureToggles {
    #[arg(long = "enable", action = clap::ArgAction::Append, global = true)]
    enable: Vec<String>,
    #[arg(long = "disable", action = clap::ArgAction::Append, global = true)]
    disable: Vec<String>,
}

impl FeatureToggles {
    fn to_overrides(&self) -> Vec<String> {
        // 验证每个 feature 名称是否已知
        // 生成如 "features.unified_exec=true" 的字符串
    }
}

CliConfigOverrides-c key=value 机制:

rust
pub struct CliConfigOverrides {
    #[arg(short = 'c', long = "config", action = ArgAction::Append, global = true)]
    pub raw_overrides: Vec<String>,
}

TOML 解析技巧:将值包装为 _x_ = {value} 再解析为 TOML 文档,提取 _x_ 键。这允许用户写 -c model=o3 而无需引号(裸字符串不是合法 TOML,解析失败后回退为原始字符串)。

优先级控制prepend_root_overrides 将根级标志插入到子命令标志之前,确保子命令标志具有更高优先级。

Subcommand 枚举 — 全部 CLI 子命令

rust
enum Subcommand {
    Exec(ExecCli),           // 别名: "e"
    Review(ReviewCommand),
    Login(LoginCommand),
    Logout(LogoutCommand),
    Mcp(McpCli),
    Plugin(PluginCli),
    McpServer(McpServerCommand),
    AppServer(AppServerCommand),
    RemoteControl(RemoteControlCommand),
    Completion(CompletionCommand),
    Update,
    Doctor(DoctorCommand),
    Sandbox(HostSandboxArgs),
    Debug(DebugCommand),
    Execpolicy(ExecpolicyCommand),  // 隐藏
    Apply(ApplyCommand),            // 别名: "a"
    Resume(ResumeCommand),
    Archive(SessionArchiveCommand),
    Unarchive(SessionArchiveCommand),
    Fork(ForkCommand),
    Cloud(CloudTasksCli),
    // ... 以及多个隐藏子命令
}

codex-rs/cli/src/lib.rs — 平台特定沙箱命令

lib.rs 为三个平台定义了平行的沙箱命令结构体:

rust
#[cfg(target_os = "macos")]  type HostSandboxArgs = codex_cli::SeatbeltCommand;
#[cfg(target_os = "linux")]   type HostSandboxArgs = codex_cli::LandlockCommand;
#[cfg(target_os = "windows")] type HostSandboxArgs = codex_cli::WindowsCommand;

每个结构体包含 permissions_profileconfig_profilecwdinclude_managed_configcommand: Vec<String>。macOS 变体额外支持 allow_unix_socketslog_denials

Feature 注册表(codex-rs/features/src/lib.rs)

Feature 枚举有 60+ 个变体,每个通过 FeatureSpec 注册:

rust
pub struct FeatureSpec {
    pub id: Feature,
    pub key: &'static str,
    pub stage: Stage,          // UnderDevelopment | Experimental | Stable | Deprecated | Removed
    pub default_enabled: bool,
}

特性标志的生命周期阶段:UnderDevelopment → Experimental → Stable → Deprecated → RemovedRemoved 阶段的标志保留为空操作,确保配置向后兼容。

Features::from_sources() 按层级构建有效特性集:默认值 → 基础配置 → profile 配置 → CLI 覆盖 → 依赖归一化。

关键函数索引

函数/模块文件路径说明
main()cli/src/main.rs程序入口,触发 arg0 分发
arg0_dispatch_or_else()arg0/src/lib.rsargv[0] 多工具分发 + tokio 运行时构建
cli_main()cli/src/main.rsCLI 主逻辑,子命令分发
run_interactive_tui()cli/src/main.rs启动 TUI 模式,含 SQLite 状态修复循环
fold_config_overrides()cli/src/main.rs合并特性开关到配置覆盖
FeatureToggles.to_overrides()cli/src/main.rs将 --enable/--disable 转为配置字符串
CliConfigOverridesutils/cli/src/config_override.rs-c key=value 配置覆盖机制
parse_overrides()utils/cli/src/config_override.rsTOML 值解析 + 回退
prepend_root_overrides()utils/cli/src/config_override.rs根级覆盖优先级控制
MultitoolClicli/src/main.rsclap Parser 顶层定义
Subcommandcli/src/main.rs25+ 子命令枚举
FeatureSpecfeatures/src/lib.rs特性标志规格定义
Features::from_sources()features/src/lib.rs分层特性集构建
Arg0DispatchPathsarg0/src/lib.rs二进制路径信息
SharedCliOptionsutils/cli/src/shared_options.rs共享 CLI 选项(model, sandbox, cwd 等)