版本管理
CC 双渠道(stable/latest)+ 后台自更新 + 服务端版本管控 vs Codex 单流发布 + 退出时按安装方式升级;harness 侧以 which + --version 探测为底座
版本管理
结论
Claude Code 与 Codex CLI 在版本管理上是两种哲学。CC 是「受管的持续交付」:双发布渠道(latest/stable)、原生安装器把每个版本落盘到 ~/.local/share/claude/versions/<version> 再用符号链接激活、进程内每 30 分钟后台检查更新、外加三层服务端管控(minVersion 强制下线、maxVersion 事故熔断、minimumVersion 用户侧防降级)。Codex 则是「检测 + 建议」:单一 GitHub Release 流、启动时异步刷新 $CODEX_HOME/version.json 缓存(20 小时一次)、不做后台自更新,只在 TUI 退出时或用户跑 codex update 时按 InstallContext 推断出的安装方式(npm/bun/brew/standalone)执行对应升级命令。对 harness(Yoda)而言,两者都能用 which + --version 统一探测,但「锁版本」只有 CC 原生安装真正支持多版本共存目录;Codex 的 standalone 布局(~/.codex/packages/standalone/releases/<version>)虽是多版本目录结构,却没有暴露 pin 接口。
研究问题
- 两家 CLI 的版本号查询方式与输出格式差异?
- 升级渠道(自更新 / 包管理器 / 安装脚本)各有哪些?发布渠道如何划分?
- 版本如何锁定 / 防降级 / 服务端强控?
- harness 如何检测已安装版本,能否管理多版本?
各 Agent 设计与实现
Claude Code
研究底座:v2.1.88 重构源码(src_2026-03-31/,泄露重构件,只做架构描述 + 短引,滞后线上约 2 个月)+ 官方 docs(2026-06)。
版本查询:claude --version 是入口处的零依赖快速路径,输出 <version> (Claude Code),版本号由 MACRO.VERSION 构建期内联(entrypoints/cli.tsx:36-42)[一手源码]。版本号采用 SemVer + 构建元数据(X.X.X+SHA):兼容性比较忽略 +SHA,而 claude update 用精确字符串比较,以便仅 SHA 变化也触发更新(utils/autoUpdater.ts:54-69 注释)[一手源码]。
发布渠道:type ReleaseChannel = 'stable' | 'latest'(utils/config.ts:74)[一手源码]。渠道由 settings 的 autoUpdatesChannel(zod enum ['latest','stable'],utils/settings/types.ts:804-807)控制,默认 latest;stable 通常滞后约一周并跳过有重大回归的版本(docs setup.md "Configure release channel")[一手文档]。版本源有两套:
- npm dist-tags:
npm view <pkg> dist-tags --json --prefer-online,从homedir()运行以防项目级.npmrc被恶意改写指向攻击者 registry(utils/autoUpdater.ts:324-326, 355-361)[一手源码]; - GCS bucket:
https://storage.googleapis.com/claude-code-dist-.../claude-code-releases/<channel>返回版本号纯文本,供无 npm 的原生/包管理器安装使用(utils/autoUpdater.ts:30-31, 384-397)[一手源码]。
自更新:原生安装在进程内每 30 分钟检查一次(components/NativeAutoUpdater.tsx:162:useInterval(checkForUpdates, 30 * 60 * 1000))[一手源码];Homebrew/WinGet 走 PackageManagerAutoUpdater.tsx:72(同样 1 800 000 ms),且需 CLAUDE_CODE_PACKAGE_MANAGER_AUTO_UPDATE=1 才会代跑升级命令 [一手源码+一手文档]。更新在后台下载安装、下次启动生效(docs setup.md "Auto-updates")[一手文档]。npm 全局安装的更新走 installGlobalPackage():抢 ~/.claude/.update.lock 文件锁(wx 原子创建 + 5 分钟 stale 超时 + 复查关闭 TOCTOU 竞态,utils/autoUpdater.ts:161-249),检查全局前缀可写性,再 npm/bun install -g(utils/autoUpdater.ts:456-533)[一手源码]。
禁用与锁定(多层):
DISABLE_AUTOUPDATER只停后台检查,claude update/claude install仍可用;DISABLE_UPDATES封死全部更新路径(getAutoUpdaterDisabledReason()返回 development / env / config 三类原因,utils/config.ts:1735-1755;docssetup.md"Disable auto-updates")[一手源码+一手文档];minimumVersionsetting 是版本地板:切到 stable 渠道时防降级,shouldSkipVersion()对低于地板的目标版本直接跳过(utils/autoUpdater.ts:145-159)[一手源码];- 服务端管控:
assertMinVersion()拉取动态配置tengu_version_config,当前版本低于minVersion时打印升级提示并退出进程(utils/autoUpdater.ts:70-99);getMaxVersion()读tengu_max_version_config,作为事故期间暂停推新的服务端 kill switch(utils/autoUpdater.ts:101-114注释)[一手源码]; - 企业管控:managed settings 的
requiredMinimumVersion/requiredMaximumVersion让 CC 在版本区间外拒绝启动(docssetup.md"Pin a minimum version")[一手文档]。
多版本共存:原生安装器把每个版本放在 ~/.local/share/claude/versions/<version>(XDG data),~/.local/bin/claude 是指向当前版本二进制的符号链接,保留最近 VERSION_RETENTION_COUNT = 2 个版本(utils/nativeInstaller/installer.ts:73, 119-131, 468)[一手源码]。
Codex CLI
研究底座:codex-rs/ @ b89ce9a(2026-06-06),一手开源。
版本查询:clap 标准 --version(cli/src/main.rs:90-95 的 #[clap(author, version, ...)]);运行期常量 CODEX_CLI_VERSION(tui/src/updates.rs:20)[一手源码]。
发布渠道:没有 stable/latest 双渠道,单一上游 = GitHub Release(https://api.github.com/repos/openai/codex/releases/latest,tag 形如 rust-vX.Y.Z,tui/src/updates.rs:66, 132-143)。brew 安装例外地查 Homebrew cask API(formulae.brew.sh/api/cask/codex.json),因为 brew 收录滞后于 GitHub(tui/src/updates.rs:64-65 注释);npm 安装同时查 GitHub + npm registry,并用 ensure_version_ready 确认 npm 侧已可安装该版本(tui/src/updates.rs:99-110)[一手源码]。
更新检查:get_upgrade_version() 受 config 项 check_for_update_on_startup 控制(config/src/config_toml.rs:459),源码构建(is_source_build_version)跳过;缓存写入 $CODEX_HOME/version.json(字段 latest_version/last_checked_at/dismissed_version),距上次检查超过 20 小时才在 tokio 后台任务刷新——本次启动读旧缓存、下次启动才显示更新横幅,确保 TUI 启动不被网络阻塞(tui/src/updates.rs:22-52)[一手源码]。用户可对某个版本点「忽略」,dismissed_version 持久化后该版本不再弹窗(tui/src/updates.rs:145-178)[一手源码]。
升级执行:codex update 子命令(cli/src/main.rs:156-157, 749-764;debug 构建直接报错不可用)。升级命令由 UpdateAction 按安装方式映射(tui/src/update_action.rs:39-61)[一手源码]:
| InstallMethod | 升级命令 |
|---|---|
| Npm | npm install -g @openai/codex |
| Bun | bun install -g @openai/codex |
| Brew | brew upgrade --cask codex |
| Standalone (Unix) | curl -fsSL https://chatgpt.com/codex/install.sh | CODEX_NON_INTERACTIVE=1 sh |
| Standalone (Windows) | irm https://chatgpt.com/codex/install.ps1 | iex(PowerShell -ExecutionPolicy Bypass) |
TUI 退出时若用户在更新弹窗确认,handle_app_exit 在主进程退出前同步执行该命令并要求重启(cli/src/main.rs:685-746)[一手源码]。
安装方式检测(harness 可直接借鉴的设计):独立 crate codex-rs/install-context。优先看 npm shim 注入的环境变量 CODEX_MANAGED_BY_NPM / CODEX_MANAGED_BY_BUN(install-context/src/lib.rs:109-121;shim 在 codex-cli/bin/codex.js:140-148 设置,并附 CODEX_MANAGED_PACKAGE_ROOT),否则按可执行文件 canonical 路径推断:位于 $CODEX_HOME/packages/standalone/releases/ 下为 Standalone,macOS 上 /opt/homebrew 或 /usr/local 前缀为 Brew,否则 Other(install-context/src/lib.rs:209-252)[一手源码]。检测不出时 codex update 放弃并指引手动更新(cli/src/main.rs:759-763)[一手源码]。
差异矩阵
| 维度 | Claude Code | Codex CLI |
|---|---|---|
| 版本输出 | 2.1.x (Claude Code),零依赖快速路径 | clap 标准 codex-cli x.y.z |
| 发布渠道 | latest / stable 双渠道(npm dist-tag + GCS 指针 + 双 brew cask) | 单 GitHub Release 流 |
| 自更新 | 进程内每 30 min 检查,后台静默安装,下次启动生效 | 不自更新;启动异步刷新缓存(20h TTL),退出时/手动执行升级 |
| 更新命令 | claude update、claude install [version|stable|latest] | codex update(debug 构建禁用) |
| 安装方式检测 | getCurrentInstallationType() 路径启发式(6 类) | InstallContext env 变量 + 路径(5 类),独立 crate |
| 版本锁定 | minimumVersion 地板 + managed requiredMin/MaxVersion 区间 | 无 pin 机制,仅 check_for_update_on_startup=false 关检查 |
| 服务端管控 | tengu_version_config 强制最低版 + tengu_max_version_config 熔断 | 无 |
| 禁用开关 | DISABLE_AUTOUPDATER(仅后台)/ DISABLE_UPDATES(全部) | check_for_update_on_startup(config.toml) |
| 多版本落盘 | ~/.local/share/claude/versions/<v> + symlink,保留 2 个 | ~/.codex/packages/standalone/releases/<v-triple>(仅 standalone) |
| 版本缓存 | 无本地缓存文件(npm view / GCS 实时拉取,5s 超时) | $CODEX_HOME/version.json(20h TTL + dismissed_version) |
| 升级并发安全 | ~/.claude/.update.lock 文件锁(O_EXCL + stale 复查) | 无锁(退出后单进程执行) |
最小复现
# harness 通用版本探测底座
$ which claude && claude --version
/Users/mark/.local/bin/claude
2.1.118 (Claude Code)
$ which codex && codex --version
/opt/homebrew/bin/codex
codex-cli 0.52.0
# Codex 版本缓存(20h TTL)
$ cat ~/.codex/version.json
{"latest_version":"0.52.0","last_checked_at":"2026-06-10T03:12:45Z"}
# CC 渠道指针(GCS,返回纯文本版本号;URL 见 utils/autoUpdater.ts:30-31)
$ curl -s https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/stable(前三段为本机示意输出;版本号随时间变化。)
Harness 接入建议(Yoda 实践)
Yoda 已上线的版本探测是两阶段异步流水线(yoda/src/main/core/dependencies/)[一手源码]:
- 路径解析(快,5s 超时):
which(Windows 用where)取首行,立即向 renderer 发事件(probe.ts:8, 15-26;dependency-manager.ts:132-145); - 版本探测(慢,10s 超时):执行
<bin> <versionArgs>,从 stdout/stderr 首行用正则/(\d+\.\d+[\d.]*)/抽版本号(dependency-manager.ts:26, 42-48)。
关键经验:agent CLI 的 --version 行为千奇百怪——有的输出到 stderr、有的探测超时、有的非零退出。Yoda 的 agentResolveStatus 因此把「路径已解析 / 超时但有 stdout / 非零退出但有输出」都判为 available(registry.ts:90-100,注释明确说明了这一容错策略)。每个 runtime 的 versionArgs 可定制:Cline 用 help、Jules 用 version 子命令、Mistral Vibe 用 -h(src/shared/runtime-registry.ts:516, 563, 579)[一手源码]。
改进方向:
- 多版本管理缺位:Yoda 目前只认 PATH 上的那一个二进制。CC 原生布局天然多版本,harness 可用绝对路径
~/.local/share/claude/versions/<v>直接启动特定版本绕过 symlink,实现「锁定已验证版本」[推断,需实测 CC 二进制脱离 symlink 启动时自更新与 doctor 的行为]; - 版本兼容矩阵:Yoda 的 resume / hook 注入依赖各 CLI 的 flag 语义,应在 probe 后把版本号与「已验证版本区间」比对并按需降级功能,而非仅做展示;
- 借鉴 Codex 的
dismissed_version:Yoda 的依赖升级提示应支持按版本忽略,避免重复打扰。
失效条件
- CC 改动
autoUpdatesChannel取值或新增渠道(本章源为 v2.1.88 重构源 + 2026-06 docs,重构源滞后线上约 2 个月,结论以 docs 交叉验证为准) - CC 改动
--version输出格式(Yoda 抽取正则与本章示例都依赖它) - CC 原生安装的
VERSION_RETENTION_COUNT(当前 2)或 versions 目录布局变更 - Codex 引入 stable 渠道或后台自更新(
tui/src/updates.rs大改即触发回归) - Codex
version.json字段或 20h TTL 调整 - Codex
UpdateAction命令映射变化(如 brew cask 改名、standalone 安装 URL 迁移)
参考资料
- CC docs:
claude-code-docs/docs/setup.md(Update Claude Code / Configure release channel / Pin a minimum version / Disable auto-updates) - CC 重构源:
src_2026-03-31/utils/autoUpdater.ts、utils/config.ts、utils/settings/types.ts、utils/nativeInstaller/installer.ts、components/NativeAutoUpdater.tsx、components/PackageManagerAutoUpdater.tsx - Codex 源(b89ce9a):
codex-rs/tui/src/updates.rs、tui/src/update_action.rs、install-context/src/lib.rs、cli/src/main.rs、config/src/config_toml.rs、codex-cli/bin/codex.js - Yoda 源:
src/main/core/dependencies/{probe,dependency-manager,registry}.ts、src/shared/runtime-registry.ts