【免费下载链接】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/_fin/260720_issue180_cli_account_parity/003_management_api_contracts.md的调研结论,系统梳理代理进程暴露的/api/*管理端点:从客户端认证与端口发现、到 Codex(ChatGPT)账户池、通用 OAuth 多账号与 API-Key 池三大家族凭证契约,再到 provider 能力判别矩阵与 CLI 账号命令(list/current/use)的落地要点。读者读完可掌握为 CLI、GUI 或脚本对接 opencodex 管理面所需的全部请求形状、鉴权前提与语义边界。
一、管理面契约的总闸门:认证与跨域防线
所有/api/*管理路由都由代理进程自身提供,并统一穿过两道检查:
- 管理鉴权:
requireApiAuth(req, config, "management")(见 src/server/index.ts 的调用入口)。即使用户环境不要求 API 认证(loopback 绑定),管理面也要求请求携带合法的 Host 与 Origin。 - 来源检查:
handleManagementAPI在进入任何业务 handler 之前,先调用isAllowedManagementOrigin(req, config),跨域来源直接返回 403(src/server/management-api.ts):
if (!isAllowedManagementOrigin(req, config)) { return jsonResponse({ error: "cross-origin request blocked" }, 403, req, config); }同时,所有 POST/PUT/PATCH 请求在 handler 缓冲 body 之前会拒绝超过 2 MiB 的载荷(返回 413request body too large),避免管理面被超大 JSON 拖垮(src/server/management-api.ts)。
- 错误约定:一律返回 JSON
{ "error": string };codex-auth相关端点有时额外附加code/reason字段。 - 浏览器安全头:管理面响应带
X-Frame-Options: DENY与Content-Security-Policy: frame-ancestors 'none'(src/server/auth-cors.ts)。 - CORS 头:管理面额外放行
X-OpenCodex-GUI-Origin、X-OpenCodex-CSRF-Token两个自定义请求头(src/server/auth-cors.ts)。
Origin校验采取“进程推导 origin 精确匹配”策略:无Origin头、与Host推导出的 origin 完全一致、或命中运维配置的corsAllowOrigins列表时才放行——后者专门覆盖 TLS 终结器把进程观测到的http://…变为外部https://…的场景(src/server/auth-cors.ts)。
二、客户端鉴权与端口解析:CLI 连代理的第一公里
2.1 绑定地址决定鉴权强度
isApiAuthRequired(config)的实现极简:绑定地址为 loopback(localhost/127.0.0.1/::1/[::1]之一)时返回false,否则返回true(src/server/auth-cors.ts):
export function isApiAuthRequired(config: Pick<OcxConfig, "hostname">): boolean { return !isLoopbackHostname(config.hostname); }两种情形下管理面的要求不同:
| 绑定方式 | 客户端要求 |
|---|---|
Loopback(默认127.0.0.1) | 无需 token;但Host必须是 loopback(isLoopbackRequestHost按 hostname 判定信任边界,端口可不同——ssh -L转发同样合法),且不得携带外部Origin(src/server/auth-cors.ts) |
| 非 loopback 绑定 | 必须携带x-opencodex-api-key、Authorization: Bearer或x-api-key三者之一,且值需匹配OPENCODEX_API_AUTH_TOKEN环境变量或config.apiKeys中某一项 |
这些头是跨数据面与管理面共享的白名单的一部分:STATIC_ALLOWED_REQUEST_HEADERS中明确包含X-OpenCodex-API-Key与X-Api-Key(src/server/auth-cors.ts)。CLI 侧的既有惯例是runningProxyUpdateHeaders()——读取configuredAdminToken()后注入X-OpenCodex-API-Key头(src/oauth/login-cli.ts)。
2.2 端口阶梯(canonical)
findLiveProxy()按固定优先级定位正在运行的代理(src/server/proxy-liveness.ts):
- PID 文件:
readAlivePid(src/config.ts)——从 pid 文件读取存活进程; runtime-port.json:readRuntimePort(src/config.ts)——读取运行时记录的真实端口;/healthz身份探针:isOpencodexHealthz(src/server/proxy-liveness.ts)——对候选端口发起健康检查,确认是 opencodex 自己的/healthz;- 兜底:
config.port ?? 10100。
配置目录为OPENCODEX_HOME环境变量指定目录,缺省为~/.opencodex(src/config.ts)。
值得注意的是
configuredPort()直接解析_corsOrigin(默认http://localhost:10100),且任何准入检查都不依赖端口号——loopback 谓词只认 hostname(src/server/auth-cors.ts)。
三、Family A —— Codex(ChatGPT)账户池契约
Codex 账户池路由由handleCodexAuthAPI统一处理(src/server/management-api.ts 动态导入 src/codex/auth-api/routes.ts)。其身份规则:
- 主账户(Codex App 登录)id 恒为
__main__(MAIN_CODEX_ACCOUNT_ID,src/codex/main-account.ts); - 池账户 id 必须匹配
^[a-zA-Z0-9._-]{1,64}$; - token 永不离服务端,邮箱一律经
maskEmail掩码后展示(src/lib/privacy.ts)。
完整端点契约如下表:
| Endpoint | 形状 / 语义 | 锚点 |
|---|---|---|
GET /api/codex-auth/accounts(?refresh=1强制刷新配额) | { accounts: CodexAuthAccountDto[] },main-first;DTO 为{ id, email(masked), plan?, logLabel?, isMain, quota: { weeklyPercent?, monthlyPercent?, weeklyResetAt?, monthlyResetAt?, resetCredits?, updatedAt } \| null, needsReauth?, hasCredential };go/free 套餐只暴露月度字段 | routes.ts |
POST /api/codex-auth/accounts | 手动导入 token;受OPENCODEX_ENABLE_UNVERIFIED_CODEX_IMPORT=1环境变量门控,未开启则 403manual_import_disabled | routes.ts |
DELETE /api/codex-auth/accounts?id= | 恒 200{ ok: true }(不返回 404);清除凭证、配额与线程亲和,若删除的是当前 active 账户则同时清空activeCodexAccountId | src/codex/account-lifecycle.ts |
GET /api/codex-auth/active | { activeCodexAccountId: string \| null, autoSwitchThreshold: number(默认 80), upstreamFailoverThreshold: number(默认 3) } | routes.ts |
PUT /api/codex-auth/activebody{ accountId: string \| null } | "__main__"表示 Codex App 登录;池 id 必须存在,否则 400 "Account not found";null清除 pin(回落自动选择最低用量,src/codex/routing.ts)。响应{ ok, activeCodexAccountId }。只写配置,不清除内存中的线程亲和——已 pin 的线程在亲和过期前保持原账户(src/codex/routing.ts),即只作用于新线程/新会话 | routes.ts |
PUT /api/codex-auth/auto-switchbody{ threshold: 0-100 } | 0表示禁用;持久化为autoSwitchThreshold | routes.ts |
PUT /api/codex-auth/failoverbody{ threshold: 0-20 } | 连续上游失败次数阈值,达到后触发故障转移 | routes.ts |
GET /api/codex-auth/quota | { quotas: { [accountId]: StoredAccountQuota } }(不含 token/邮箱) | routes.ts |
GET /api/codex-auth/reset-credits?accountId= | { credits: [{ granted_at, expires_at }], available_count? };400/401/404 状态矩阵 | routes.ts |
POST /api/codex-auth/reset-credits/consumebody{ accountId } | 成功返回{ code: "reset" }并强制刷新配额;可选operationId提供幂等语义(格式非法返回 400),池配额探测忙碌时返回 503server_busy且带Retry-After: 1 | routes.ts |
POST /api/codex-auth/loginbody{ id? } | 浏览器 OAuth 流程(服务端拉起浏览器);返回{ ok, flowId, url, instructions? };流程进行中重复发起返回 409 | routes.ts |
POST /api/codex-auth/login/cancel/GET /api/codex-auth/login-status?flowId= | { ok, cancelled }/{ status: pending\|done\|error\|expired\|idle, accountId?, email?(masked), error? } | routes.ts |
实现细节佐证:配额查询直接遍历
listAccountQuotas()内存表(routes.ts);reset-credits 消费端具备幂等 operationId 与 503 忙碌语义(routes.ts)。
四、Family B —— 通用 OAuth provider 账户契约
管理面在 src/server/management-api.ts 的 accounts/logout 区块(约 L1427-L1471)处理通用 OAuth 账户。有效 provider 集合由listOAuthProviders()给出:xai、anthropic、kimi、kiro、google-antigravity、cursor、github-copilot;chatgpt通过isPublicOAuthProvider排除在公开列表外(src/oauth/index.ts)。未知 provider 一律 400"unknown oauth provider"。
| Endpoint | 形状 / 语义 | 锚点 |
|---|---|---|
GET /api/oauth/providers | { providers: string[] } | management-api.ts |
POST /api/oauth/loginbody{ provider, addAccount? } | addAccount: true强制拉起全新浏览器身份;返回{ url, instructions? };冲突时 409 | management-api.ts |
POST /api/oauth/login/cancel/POST /api/oauth/login/code | cancel 返回{ ok, cancelled };粘贴 code:{ provider, input? }→{ ok: true },被拒时 409(不适用于 chatgpt) | management-api.ts |
GET /api/oauth/status?provider= | { loggedIn, email?(masked), source?, error?, done, activeAccountId?, accounts?: [{ id, email?(masked), active, needsReauth?, expiresAt? }] } | src/oauth/index.ts |
POST /api/oauth/logout?provider= | 通过 query 参数指定;只移除 ACTIVE 账户,并提升第一个剩余账户为 active | src/oauth/store.ts |
GET /api/oauth/accounts?provider= | { activeAccountId: string \| null, accounts: OAuthAccountSummary[] }(掩码展示) | management-api.ts |
PUT /api/oauth/accounts/activebody{ provider, accountId } | { ok, provider, activeAccountId };缺 id 400、不存在 404 "account not found"。立即生效——getCredential每次请求重读 active 行 | src/oauth/store.ts |
DELETE /api/oauth/accounts?provider=&id= | { ok: true };缺 id 400;不存在 404;删除 active 时提升第一个剩余账户 | src/oauth/store.ts |
存储层背景:OAuth token 存在~/.opencodex/auth.json,按 provider 名组织为ProviderAccountSet{ activeAccountId, accounts: [{ id, credential, needsReauth?, addedAt? }] };旧的单凭证形态({ access, refresh, expires, ... })在加载时自动归一化,首次以新形态落盘会先备份auth.json.pre-multiauth,防止降级加载器静默丢弃刷新 token(src/oauth/store.ts)。
五、Family C —— API-Key 池契约
API-Key 池仅对isKeyAuthProvider判定的 provider 开放——即鉴权方式既非 oauth 也非 forward 的已配置 provider(src/providers/api-keys.ts)。池定义在provider.apiKeyPool(src/types.ts),provider.apiKey镜像当前 active 条目。key id 为sha256(key)[:8]前 8 位(src/providers/api-keys.ts);掩码规则maskApiKey:first4****last4,长度 ≤ 8 时整体显示****,${ENV}引用原样展示(src/providers/api-keys.ts)。
| Endpoint | 形状 / 语义 | 锚点 |
|---|---|---|
GET /api/providers/keys?name= | { activeId: string \| null, keys: [{ id, label?, masked, active, addedAt? }] };非 key provider 返回{ activeId: null, keys: [] };未知 name 返回 404 | management-api.ts / api-keys.ts |
POST /api/providers/keysbody{ name, key, label? } | 201{ ok, id };新 key立即成为 ACTIVE;同时清空 model/quota 缓存与 key 冷却状态 | management-api.ts |
PUT /api/providers/keys/activebody{ name, id } | { ok, name, activeId };缺 id 400;未知 provider/key 404。立即生效(镜像provider.apiKey) | management-api.ts |
DELETE /api/providers/keys?name=&id= | { ok: true };删除 active 时提升第一个剩余 key | management-api.ts |
相邻端点(注意区分):
GET /api/key-providers:key 登录选择器的元数据;GET/POST/DELETE /api/keys:代理自身的准入 key(admission keys)——POST 返回完整ocx_…key,且只返回一次,用于外部客户端接入代理。
六、Provider 能力判别矩阵
程序化判别依据两个字段:authKind(src/providers/registry.ts)与codexAccountMode(值为"direct" | "pool",见 src/types.ts),二者均通过GET /api/provider-presets暴露(src/providers/derive.ts)。
| Provider | authKind | 账户能力 |
|---|---|---|
| openai(内置) | forward | Codex 池(默认)或仅主账户(codexAccountMode: "direct") |
| xai | oauth | 多账户 |
| anthropic | oauth | 多账户 |
| kimi | oauth | 多账户——JWTuser_id/sub稳定身份(src/oauth/kimi.ts);并非issue 假设的单槽 |
| kiro | oauth | 替换式单槽——凭证中不含 accountId/email(src/oauth/store.ts) |
| google-antigravity | oauth | 多账户 |
| cursor | oauth | 多账户(JWTsub) |
| github-copilot | oauth | 多账户 |
全部authKind: "key"provider(openrouter、groq、google、cerebras、opencode、qwen-* 等) | key | apiKeyPool多 key + active 镜像 |
| ollama / vllm / lm-studio | local | 无凭证 |
已知缺口:多账户与替换式单槽的差异无法仅凭 HTTP 判别。SINGLE_SLOT_PROVIDERS常量只覆盖chatgpt(src/oauth/store.ts);kiro 的替换行为来自无身份凭证分支(登录时未提取 accountId/email,src/oauth/store.ts)。因此 CLI 必须硬编码已知的替换式集合(kiro)或向用户展示通用提示。
七、服务端缺失契约与 CLI 落地结论
调研同时记录了五条对 CLI 账号命令有直接影响的“服务端缺口”:
- 没有跨 provider 的聚合账户视图——CLI 必须扇出:先
GET /api/oauth/providers,再对每个 provider 调GET /api/oauth/accounts,并对每个 key-provider 调GET /api/providers/keys(Codex 池是唯一自带聚合的); - Codex 池没有手动粘贴 code 的登录路径(仅浏览器流程)——openai 的
account add保持浏览器流,超出最小命令集范围; - 没有可经 HTTP 推导的多/替换标志(见矩阵缺口);
PUT /api/codex-auth/active不强制立即切换——已 pin 线程保留原账户,CLI 输出必须注明"applies to new sessions/threads";- 可容忍的不对称:
DELETE /api/codex-auth/accounts与DELETE /api/keys对未知 id 返回 200;/api/oauth/logout走 query 参数。
最终结论:issue #180 的最小范围(list/current/use)不需要任何新的服务端契约——三大家族已经完整暴露了所需的读取与切换能力。这为 CLI 账号子命令的实现划定了清晰的集成边界:读操作走扇出式聚合,切换操作分别落在PUT /api/codex-auth/active(Codex 池)、PUT /api/oauth/accounts/active(OAuth 多账号)与PUT /api/providers/keys/active(Key 池)之上,并各自遵守上文表格中的鉴权、掩码与幂等语义。
【免费下载链接】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
相关推荐
HAMi异构GPU调度:如何让AI计算资源利用率提升100%?
HAMi异构GPU调度:如何让AI计算资源利用率提升100%? 在当今AI计算浪潮中,GPU资源短缺与浪费并存成为企业面临的严峻挑战。 HAMi异构AI计算虚拟
云原生容器编排人工智能任务调度IronClaw Reborn 产品认证契约解析:OAuth 流程、凭证账户与 HTTP 路由的完整实现指南
IronClaw Reborn 产品认证契约解析:OAuth 流程、凭证账户与 HTTP 路由的完整实现指南 IronClaw 是一个以隐私、安全与可扩展性为核
人工智能AI 应用交互助手AI Agentopencodex 多账号安全加固:账户生命周期事务与凭据代际失效机制解析
opencodex 多账号安全加固:账户生命周期事务与凭据代际失效机制解析 opencodex 作为同时服务 OpenAI Codex 与 Claude Cod
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考