【免费下载链接】opencodex
Universal provider proxy for OpenAI Codex & Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code
本文基于 opencodex 仓库 devlog 中的发布加固记录(
devlog/_fin/260701_deployability-hardening/00_plan.md),完整还原一次真实发布周期的处理流程:当 dev 分支携带未完成的重构、版本号回退和未覆盖的新头部时,如何通过版本修复、测试补齐与干净工作区验证,安全地把2.6.13推进到2.6.14。读完本文,你将掌握一套可复用的"发布前可部署性检查"方法论,以及 opencodex 中客户端指纹(client fingerprint)头部的实际实现与测试方式。
背景:发布表面(Release Surface)与脏工作区问题
opencodex 的发布面向两个直接可见的表面(Surface):
- release 表面:
package.json中的版本号,即 npm 上对外发布的产品版本; - 请求指纹表面:
src/adapters/anthropic.ts中构造的请求头(headers),它决定了代理发出的请求在 Anthropic 服务端看起来是否是"第一方客户端"。
2026-07-01 的这次加固(C4 类发布表面问题)核心目标是:
让
dev分支相对已发布的origin/main(npmlatest = 2.6.13)具备可部署性,同时不把工作区中未完成、脏状态的指纹重构一并卷入发布。
问题在于:开发分支上正在进行一项跨多文件的client-fingerprint重构,工作区处于 dirty 状态。如果直接发布,既可能带上半成品代码,也可能因为版本号问题导致发布失败或降级发布。
发现的两个发布阻塞项(Blockers)
对git diff origin/main..HEAD的审查发现两个必须处理的阻塞项:
1. 版本回归(RELEASE BLOCKER)
dev分支的package.json版本是2.6.4,而origin/main是2.6.13,npm 上的latest标签同样是2.6.13。原因是在更早的会话中 dev 工作区的package.json被重置过且从未重新提升版本。
后果很直接:从这个工作树发布要么失败,要么会发布一个更低的版本号。在 npm 语义下,从 2.6.13 回退到 2.6.4 发布是危险的降级发布,必须修复。
2. 未测试的头部变更(Untested swept headers)
提交1302a18给src/adapters/anthropic.ts引入了两个新请求头,但没有配套测试:
Accept:按流式条件取值——流式请求为text/event-stream,否则为application/json;User-Agent:固定为@anthropic-ai/sdk/0.74.0。
这两个头部是"第一方指纹加固"的一部分:它们与client-fingerprint.ts/CLAUDE_CODE_HEADERS中固定的 SDK 版本0.74.0一致,并且在isOAuth分支之前设置,因此同时作用于 OAuth 和 API-key 两条路径。意图明确,但缺少测试覆盖——这在发布门禁中属于"未覆盖的变更",不能直接放行。
已在 dev 上验证的意图变更(不重新触碰)
这次发布并非只修问题,计划同时保留 dev 上已经验证、无需回退的变更:
- WP1:anthropic reasoning-"none" 门(提交
5ac3573)+ 配套测试; - WP2:openai-chat 流被截断时的 EOF fail-closed(提交
3ac5dc2)+ 配套测试; - WP4:
server.ts部分拆分——gui-static+adapter-resolve,行为保持不变; - WP5:codex-catalog golden oracle 测试(为未来拆分提供的安全网)。
这些变更通过"已确认、不重刷"的方式进入发布候选,避免无谓的重做引入新风险。
明确排除在外的范围(Out of scope)
正在进行的指纹重构故意不纳入本次发布,包括client-fingerprint.ts(X-Stainless-Arch/OS/Package-Version/Runtime-Version 追加)、google.ts、kiro-wire.ts、kiro.ts、oauth/anthropic.ts、oauth/google-antigravity.ts、oauth/kimi.ts、tests/google-antigravity-wire.test.ts。这些属于用户正在进行的工作,并非本次发布候选的必需内容。
这一"范围切割"决策是发布加固的核心思想:只发布经过验证的增量,把未完成的工作留在工作区。
修复落地(WP1,候选提交 717d2ff)
版本修复:2.6.4 → 2.6.14
package.json版本从2.6.4提升到2.6.14(下一个未使用的版本号)。当时的 npm dist-tags 状态为latest=2.6.13、preview=2.6.11-preview.20260630,因此2.6.14高于两者,可以安全发布。顺带一提,当前仓库package.json的版本已演进到2.60.0,本文记录的是 2026-07-01 这一发布周期。
测试补齐:client fingerprint 断言
在tests/client-fingerprint.test.ts中新增两组断言:
- 双路径覆盖:OAuth 与 API-key 两种认证模式下,请求头都必须包含
Accept=application/json与User-Agent=@anthropic-ai/sdk/0.74.0; - 流式协商:流式请求的
Accept必须是text/event-stream。
红绿验证已确认:删除Accept头部会导致 2 个测试失败——这正是"未测试变更"被补上测试后的直接效果。
源码印证:头部到底是怎么组装的
在 src/adapters/anthropic.ts 的buildRequest中,头部组装逻辑与文档描述完全一致:
const headers: Record<string, string> = { "Content-Type": "application/json", "anthropic-version": "2023-06-01", "Accept": parsed.stream ? "text/event-stream" : "application/json", "User-Agent": "@anthropic-ai/sdk/0.74.0", }; if (isOAuth) { headers["Authorization"] = `Bearer ${provider.apiKey}`; headers["anthropic-beta"] = ANTHROPIC_OAUTH_BETA; Object.assign(headers, CLAUDE_CODE_HEADERS); headers["X-Claude-Code-Session-Id"] = claudeCodeSessionId(provider.apiKey); headers["x-client-request-id"] = crypto.randomUUID(); } else { if (anthropicKeyUsesBearer(provider)) headers["Authorization"] = `Bearer ${provider.apiKey}`; else headers["x-api-key"] = provider.apiKey; }注意Accept和User-Agent位于isOAuth判断之前——这就是文档所说"作用于两条路径"的代码依据。而CLAUDE_CODE_HEADERS定义在 src/adapters/client-fingerprint.ts,包含X-App: cli、X-Stainless-Retry-Count: 0、X-Stainless-Runtime: node、X-Stainless-Lang: js、X-Stainless-Timeout: 600,以及后续 WP2 追加的X-Stainless-Arch(取process.arch)、X-Stainless-OS(取process.platform)、X-Stainless-Package-Version: 0.74.0、X-Stainless-Runtime-Version(取process.version)。
该文件的注释明确解释了为什么需要这些指纹:携带有效 OAuth token 但头部签名为空(或暴露身份的 UA,如字面量 "antigravity")的请求,会被上游视为"非第一方签名",而代理必须让请求指纹与凭证来源匹配。其中claudeCodeSessionId()用 token 的 SHA-256 哈希派生出稳定的 v4 形状 UUID,保证同一会话内跨轮次稳定、且绝不回显原始 token。
测试侧,tests/clients/client-fingerprint.test.ts 正是 WP1 补的两组断言:Accept=application/json+User-Agent=@anthropic-ai/sdk/0.74.0双路径断言,以及流式请求Accept=text/event-stream断言;同文件还验证了 API-key 模式不携带X-App、X-Claude-Code-Session-Id等 Claude Code CLI 头部(见 L113-L118)。
验证流程:强制使用干净的临时工作树
文档强调了一个关键实践:脏的主工作区会掩盖回归问题,因此验证必须在候选 SHA 上以分离 HEAD 的干净工作树进行:
git worktree add --detach /tmp/ocx-verify-717d2ff 717d2ff bun install --frozen-lockfile bun run privacy:scan # passed bun x tsc --noEmit # exit 0 bun test ./tests/ # 977 pass / 0 fail / 5123 expect, 93 files(2 次稳定运行)基线是 975 个通过,新增指纹断言后 +2 达到 977,与补测的 2 个断言一一对应。验证完成后移除临时工作树。
这套命令与仓库 package.json 中定义的脚本一一对应:privacy:scan(scripts/privacy-scan.ts 扫描 token、邮箱、home 路径等隐私泄露)、typecheck(bun x tsc --noEmit)、test(scripts/test.ts)。发布前的完整门禁还包括prepublishOnly(audit + typecheck + GUI 构建)与npm pack --dry-run。
WP2:评审后加固与用户变更折叠
WP1 的 gpt-5.5 评审结论为 SHIP-WITH-NITS(带瑕疵发布)。随后用户确认之前推迟的工作区变更是他们自己的作品,必须进入本次发布,于是进入 WP2(候选提交f34f742),并清掉 WP1 评审中的一个 MEDIUM 问题:
- openai-chat EOF MEDIUM 修复:一个不带尾部换行的最终
finish_reason/usage 帧滞留在buffer中,读取器在 EOF 时永远不会解析它,导致一个完整流被误判为失败。修复方式是在终端信号检查之前增加尾部缓冲冲洗(trailing-buffer flush);而真正被截断的"内容中间帧"仍然 fail-closed。+3 个配套测试(红绿验证)。 - openai-responses:
stripUnsupportedHostedTools在 OAuth 透传前移除原生 slug 拒绝的 hosted tools(如 codex-spark 场景下的image_generation)。+2 个配套测试。 - client-fingerprint:追加
X-Stainless的 Arch/OS/Package-Version/Runtime-Version。 - google:antigravity UA 组装位置调整;运行时
x-goog-api-client移除(改为仅 onboarding 使用)+ 测试更新;修复一处多余 7 空格缩进。 - oauth:anthropic 工具前缀
proxy_→custom_;antigravity onboarding UA +x-goog-api-client;kimi CLI0.14.0+kimi_code_cli平台。 - kiro:指纹盐(fingerprint salt)+
KIRO_IDE_VERSION对齐。
WP2 验证(干净工作树 @ f34f742)
bun test ./tests/ # 982 pass / 0 fail / 5134 expect, 93 files(2 次稳定运行) bun x tsc --noEmit # exit 0 bun run privacy:scan # passed bun run prepublishOnly # GUI build + package prep, exit 0 npm pack --dry-run # bitkyc08-opencodex-2.6.14.tgz, 131 files, validnpm pack --dry-run产出bitkyc08-opencodex-2.6.14.tgz(131 个文件),包内容与package.json的files字段(bin、src、gui/dist、assets/banner.png等)吻合。
openai-chat EOF 修复的源码依据
WP2 的 MEDIUM 修复在 src/adapters/openai-chat.ts 中可以看到现行实现:流读取循环结束后,先检查buffer.length > 0并调用handleDataLine(buffer)冲洗尾部缓冲,再进入终端信号判断(sawFinish)。若没有finish_reason且存在未完成的 tool call,则默认保持 fail-closed 策略(upstream stream ended mid tool call without a terminal signal — possible truncation);仅当 provider 显式开启openaiChatEofTolerance且工具调用参数是完整 JSON 对象时才放行。这印证了文档中"真正截断的中间帧仍 fail-closed"的表述。
可复用的发布加固方法论
从这次 2.6.13 → 2.6.14 的发布周期中,可以提炼出四条可直接复用的实践:
- 发布前强制 diff 审查:以
git diff origin/main..HEAD为审查入口,区分"必须修复的阻塞项"与"已验证的意图变更",避免在发布候选里混入未知增量。 - 版本号先行:发布前确认
package.json版本高于origin/main与 npmlatestdist-tag,杜绝降级发布。 - 未测试变更不放行:任何进入发布候选的代码变更必须有配套测试,红绿验证(删除断言 → 测试失败)是证明测试有效性的标准手段。
- 干净工作树验证:脏工作区会掩盖回归,验证必须在
git worktree add --detach的干净候选 SHA 上执行,跑完privacy:scan+tsc --noEmit+ 全量bun test+prepublishOnly+npm pack --dry-run后才算通过。
总结
opencodex 的 2.6.14 发布周期展示了"可部署性"如何被当作一等公民对待:版本回归被拦截、未覆盖的指纹头部被补上双路径测试、未完成的重构被明确排除、验证在干净的临时工作树中完成,并在评审后把用户工作安全折叠进发布。这套"范围切割 + 测试补齐 + 干净验证 + 分阶段评审"的流程,对任何基于 Git 分支模型发布 npm 包的团队都具有直接的参考价值。
【免费下载链接】opencodex
Universal provider proxy for OpenAI Codex & Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code
相关推荐
opencodex 发布工程实战:从 v2.1.8 发布计划解读版本门禁、CI 验证与 npm 可信发布流水线
opencodex 发布工程实战:从 v2.1.8 发布计划解读版本门禁、CI 验证与 npm 可信发布流水线 本文以仓库中的 v2.1.8 发布计划 http
opencodex 发布与部署门禁加固实战:Service 门径漂移修复、输入注入防护与 dry-run 发布证明
opencodex 发布与部署门禁加固实战:Service 门径漂移修复、输入注入防护与 dry run 发布证明 本文基于仓库 devlog 记录 020_w
opencodex 预览通道(Preview)受控发布实战:从 `preview` 分支到 npm dist-tag 的端到端流程
opencodex 预览通道(Preview)受控发布实战:从 preview 分支到 npm dist tag 的端到端流程 导读 本文以 opencodex
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考