【免费下载链接】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 单元 260718_provider_workspace_accounts_rail 中的 A 门审计文档 011_account_audit.md,完整复盘该仓库将 Provider 工作区(#providers/workspace)改造成"账户切换一等公民"的过程:如何从既有WorkspaceItem、isAccountProvider、authMode、hasApiKey等字段无侵入地推导出认证表面,如何在并发刷新与失败网络下保住状态权威性,以及审计阶段发现并折叠的四个新增阻塞项。读完本文,你将掌握 opencodex 前端账户体系的认证表面判定规则、安全标签(masked email/序数)回退机制、代码级的状态提交策略,以及一套可复用的"审计 → 修订 → 验证"闭环方法。
1. 审计背景与输入:一条可独立复现的基线
1.1 前置探索结论
在正式 A 门之前,一条"roadmap 前账户探索链"独立追踪了完整链路:列表(list)→ 活动 ID(active id)→ PUT 切换 → 持久化 → 运行时凭证选择,最终返回VERDICT: READY,同时给出九条具体的风险/激活发现(详见 003_audit_synthesis.md)。
1.2 新鲜基线命令
本阶段 A 的基线测试命令与结果(来自 011_account_audit.md):
bun test --isolate tests/oauth-accounts-api.test.ts tests/oauth-store-multi.test.ts tests/oauth-public-surface.test.ts tests/codex-auth-api.test.ts tests/provider-workspace-data.test.ts tests/provider-workspace-state.test.ts 118 pass / 0 fail / 398 assertions注意:后续账户切片实现后,聚焦套件提升至
126 pass / 0 fail / 422 assertions(见 010_account_switcher.md C 验证收据);整合收尾后聚焦套件进一步达到133 pass / 0 fail / 462 assertions(见 030_integration_qa.md)。
1.3 评审委派的真实性记录
两个账户专项评审 agent 在各自三次有界等待后均未产出结果而被退役,这是同一数据包第二次派发失败。按 000_plan.md 的升级规则("主 agent 在两个不同 worker 失败后回收数据包"),主 agent 回收审计权并完成了本合成。这种"失败被记录而非隐藏"的做法是仓库审计纪律的一部分:C 阶段仍要求全新的实现评审。
2. 可行性审计:十个判定点
011_account_audit.md 对账户切片逐一做了可行性论证:
ProviderAuthSurface可从既有字段推导——WorkspaceItem、isAccountProvider、isLocalProvider、authMode、hasApiKey、keyOptional均已存在,无需发明服务端字段。- 渲染局部编排——通用按 provider 生成状态是
Providers.tsx的渲染局部编排;功能性合并可修复当前"子集擦除"缺陷,无需全局 store。 - 切换失败可达——非 2xx 与 rejected fetch 都能触发;计划保留旧状态并增加
finally恢复。 - Codex 假成功直接可达——
setActive当前忽略res.ok;计划只在ok后消费响应并做权威刷新。 - 规范 vs 自定义 forward 判定——必须用
isAccountProvider,不能用authMode === forward。 needsReauth已存在于两个 DTO——阻塞变更并暴露恢复路径可达。- 加载/空/一/多状态——从延迟/失败/真实响应可达;Anthropic 有两条真实活跃行、Codex 有四条,提供了真实的多账户证明。
- 键盘漫游焦点——现有 React tab 按钮可实现,无需依赖;浏览器 QA 必须做,因为仓库没有 DOM 测试夹具且新增依赖被禁止。
- 无身份单槽 provider 保持诚实——暴露当前返回行,但不被描述为"池"。
- 范围适配一个工作相位——服务端路由/存储不变,所有源修改都留在既有 GUI 属主链加一个纯分类器内。
3. 主审计追加折叠的四个阻塞项
除十个判定点外,主审计在 011_account_audit.md 中又发现四个可复现问题并折叠进计划:
3.1 跨 provider 的陈旧 tab 状态
ProviderDetails复用未带 key。修订:按 provider 名加 key,使 tab/设置状态无法跨 provider 身份串扰。实现后工作区详情按 provider 名重挂载(对应 010_account_switcher.md B 收据)。
3.2 OAuth/账户状态自相矛盾
独立请求可能返回陈旧的loggedIn=false覆盖真实活跃行。修订:非空权威账户行确立面板的登录摘要,两路请求不得在活跃行上方渲染矛盾的"未登录"。
3.3 通用 raw-id 反馈泄漏
Providers.tsx(文档标注:131,200-203)在可见通知/确认中使用了email ?? id。修订:所有工作区反馈路径统一走 shared masked-email/ordinal 标签。
3.4 Codex raw-id 确认泄漏
CodexAccountPool.tsx(文档标注:78,85-88)可能把 id 放进标签/确认文案。修订:只使用 masked email 或 main-account 文案。
这四个折叠项与仓库实际落地的安全标签工具相互印证:
oauthAccountDisplayLabel优先返回 alias → masked email → 本地化序数,绝不回退到不透明存储 id(见 auth.ts)。
4. 残余项与主审计结论
4.1 记录的残余
- Codex 池密度:完整内嵌 Codex 池仍比通用 OAuth 行列表稠密,但不阻塞功能;密度修复若进行,需留在
CodexAccountPool/工作区样式内,并先作为 B 偏差补充(对应 020_provider_rail.md 的 rail 精修相位)。 - 删除后焦点恢复:本切片不重新设计账户删除焦点恢复;保留现有确认,新增可访问标签,最终键盘 QA 必须确认非破坏性账户切换后焦点不丢失。
4.2 判定
所有可达的认证选择阻塞项现在都有精确属主与激活证据;不需要后端契约或凭证存储变更。正式评审交付失败被如实记录,最终 C/D 仍要求全新实现评审。
VERDICT: GO-WITH-FIXES (blockers=0)5. 落地对照:认证表面的源码级实现
本小节以当前仓库源码佐证审计判定,帮助读者把"审计说了什么"映射为"代码实际是什么"。
5.1 纯分类器providerAuthSurface
gui/src/provider-workspace/auth.ts 定义了判定函数:
export type ProviderAuthSurface = "codex-accounts" | "oauth-accounts" | "api-keys" | null; export function providerAuthSurface(item: WorkspaceItem): ProviderAuthSurface { if (isAccountProvider(item.name, item)) return "codex-accounts"; const mode = (item.authMode ?? "").toLowerCase(); if (mode === "forward" || mode === "local" || isLocalProvider(item)) return null; if (mode === "oauth") return "oauth-accounts"; const hasKeyMaterial = item.hasApiKey === true; const keyAuth = mode === "key" || hasKeyMaterial || mode === ""; if (!keyAuth || (item.keyOptional === true && !hasKeyMaterial)) return null; return "api-keys"; }规则解读:
- 规范 OpenAI forward →
codex-accounts:只有isAccountProvider为真才拥有 Codex 池。isAccountProvider在 catalog.ts 中定义为"名称等于规范 forward provider 且形状为规范 forward 形状"(name === CANONICAL_FORWARD_PROVIDER && isCanonicalForwardShape(p)),这正是审计点 5"必须用isAccountProvider而非authMode === forward"的实现。 - 自定义 forward / local →
null:绝不把全局 Codex 池继承给"看起来像 forward"的自定义代理,也绝不给无认证 provider 一个死 tab。 oauth→oauth-accounts:通用 OAuth 多账户。- key 形态 →
api-keys:mode === "key"、已有 key 材料、或模式为空且非 keyOptional 时映射到 API keys;keyOptional且无 key 材料时返回null。
5.2 安全标签回退oauthAccountDisplayLabel
同一文件中的安全标签工具实现:
export function oauthAccountDisplayLabel<T extends OAuthAccountIdentity>( accounts: readonly T[], account: OAuthAccountIdentity, t: TFn, ): string { const alias = account.alias?.trim(); if (alias) return alias; const email = account.email?.trim(); if (email) return email; const index = accounts.findIndex(candidate => candidate.id === account.id); return t("pws.accountOrdinal", { count: String(index >= 0 ? index + 1 : 1) }); }优先级为alias → masked email → 本地化序数(如Account 2/계정 2),绝不暴露存储 id。这正是审计折叠项 3/4(raw-id 泄漏)的收敛方案。
5.3 测试契约
测试文件 直接验证了上述两个工具:
- 只有规范 OpenAI forward(
openai-responses+chatgpt.com/backend-api/codexbaseUrl)得到codex-accounts;改名为custom-forward或改 baseUrl 即为null。 anthropic(oauth)→oauth-accounts;paid(key)/configured(hasApiKey: true)→api-keys;free(keyOptional: true无 key)与ollama(local)→null。- 标签:masked email 原样使用;alias 优先;无 email 时输出
Account 2且断言不含opaque-second;未知行 fail-closed 到Account 1。
该测试与 010_account_switcher.md 的"RED → GREEN(8 pass / 0 fail)"收据一致,属于"先写契约测试、再生产代码"的路径。
6. 服务端契约:不变化的后端证据
审计判定"不需要后端契约变更"可在当前仓库源码中验证:
- 通用 OAuth 账户列表:
GET /api/oauth/accounts?provider=P返回{ activeAccountId, accounts[] },且注释明确"Emails are masked; tokens never leave the store"(见 oauth-account-routes.ts)。 - 通用切换:
PUT /api/oauth/accounts/active校验 provider 与accountId,调用setActiveAccount(写凭证存储锁内账户集,见 src/oauth/store.ts),随后清空模型缓存与配额缓存(clearModelCache/clearProviderQuotaCache)。 - Codex 侧:
GET /api/codex-auth/accounts与GET/PUT /api/codex-auth/active承载 canonical Codex 池(见 src/codex/auth-api.ts)。
从源码结构看,前端工作区的改造(ProviderAuthSurface纯分类器、Providers.tsx状态提交、ProviderAuthPanel/CodexAccountPool失败诚实化)完全是 GUI 侧行为,与凭证/路由契约解耦——这正是审计点 10 与最终判定"仅 GUI 属主链 + 一个纯分类器"的直接证据。
7. 后续相位承接与总账
A 门审计不是终点,它把结论交棒给两个实现相位与一个集成相位:
- 010_account_switcher.md:账户切换器实现,含激活矩阵(loading / empty / one / many / reauth / HTTP failure / network failure / stale GET / custom forward / provider change / status race / safe fallback / keyboard tabs / live switch)与验证命令。
- 020_provider_rail.md:rail 两行语义行 + 容器查询响应式修复。
- 030_integration_qa.md:整合对抗 QA 与收尾(含
#providers/workspace深链保持、同步重复切换防护、POST-PUT 刷新诚实性、显式 Codex 键盘切换按钮、诚实登出刷新、可见 DELETE 失败处理、单入口 rail 焦点模型等八项残余修复)。
整体设计意图(来自 000_plan.md 与 001_design_read.md)始终一致:账户身份成为一等工作区 tab,rail 每一行读作一个连贯状态对象;活跃状态由服务端权威,失败的切换不改变选中行,任何可见表面不出现 raw id/全邮箱/不透明账户 id。A 门审计以GO-WITH-FIXES (blockers=0)收敛,为后续 B/C 的实现与验收提供了精确、可复现的靶心。
【免费下载链接】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 工作区账户体系与 Provider Rail 设计规范:从多账户切换到语义化响应式布局
OpenCodex 工作区账户体系与 Provider Rail 设计规范:从多账户切换到语义化响应式布局 导读 本文基于 opencodex 仓库中 devl
opencodex Providers Workspace 集成 QA:账户切换、Provider Rail 与对抗性验收收尾实战
opencodex Providers Workspace 集成 QA:账户切换、Provider Rail 与对抗性验收收尾实战 本指南基于 opencode
告别账号切换烦恼:PrismLauncher多账户管理全攻略
告别账号切换烦恼:PrismLauncher多账户管理全攻略 你是否还在为管理多个Minecraft账号而频繁登录登出?是否在切换不同账号时需要重启启动器?Pr
桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考