This commit is contained in:
@@ -1,39 +1,53 @@
|
||||
# Codex 集成与用量同步
|
||||
# CLIProxyAPI 与 Codex 用量同步
|
||||
|
||||
## 账号隔离与进程生命周期
|
||||
## 外部依赖与凭据边界
|
||||
|
||||
每个 `accounts` 记录对应一个 `accountRuntime` 和独立 `codex app-server` 子进程。账号 1 为升级兼容固定使用 `/data/codex`;其他账号使用 `/data/accounts/<id>/codex`。该目录通过子进程 `CODEX_HOME` 注入,保存并刷新 ChatGPT 登录凭据。
|
||||
Codex Helper 不再启动 `codex app-server`,也不保存 Codex OAuth token。所有 Codex 登录、凭据持久化和刷新均由外部 CLIProxyAPI(CPA)负责;本项目只通过环境变量读取:
|
||||
|
||||
`ensureReady` 用 lifecycle mutex 串行冷启动:创建目录、启动子进程、在 20 秒内调用 `initialize`,成功后才标记 ready。仅子进程存活不代表协议已初始化;失败进程必须关闭后重试。`syncing` mutex 串行同账号同步并保护 Dashboard,`stateMu` 保护 ready/stopped。删除账号先从应用 map 移除并停止进程,再删除数据库和精确凭据目录。
|
||||
- `CLIPROXY_API_BASE_URL`:CPA Management API 根地址。
|
||||
- `CLIPROXY_API_MANAGEMENT_KEY`:CPA management key,只进入服务端请求头,不写入 SQLite、日志或前端响应。
|
||||
|
||||
## JSONL 协议与登录
|
||||
每条 `accounts` 记录保存一个唯一的非空 `auth_index`。添加或修改绑定时,后端先调用 CPA `GET /v0/management/auth-files?auth_index=...`,确认索引唯一匹配、provider/type 为 `codex`,且凭据未 disabled。CPA 的临时 `unavailable`/error 状态可能正是额度耗尽导致,不能据此拒绝读取,否则仪表盘无法显示 100% 用量和重置时间。账号邮箱、套餐和 ChatGPT account ID 来自该认证条目的安全元数据与 ID token claims;原始 token 不会进入 Codex Helper。
|
||||
|
||||
客户端以 stdio 启动 `codex app-server`,每行一个 JSON 消息。递增 ID 将响应关联到 buffered channel;请求受调用 context 或 20 秒 timeout 控制。旧进程晚退出时不能把替代进程标记为断开。
|
||||
旧数据库迁移会为已有账号增加空 `auth_index`。这些账号会保持未连接,直到管理员在设置页绑定 CPA authIndex。新数据库不再自动创建无凭据的默认账号。
|
||||
|
||||
设备码登录调用:
|
||||
## Management API 代请求
|
||||
|
||||
```text
|
||||
account/login/start {type:"chatgptDeviceCode"}
|
||||
```
|
||||
后端通过 CPA `POST /v0/management/api-call` 发起上游请求,请求体使用 `auth_index` 选择凭据,并在 Authorization 中传递 `Bearer $TOKEN$` 占位符,由 CPA 注入和刷新真实 access token。关键上游请求是:
|
||||
|
||||
前端展示 `verificationUrl` 与 `userCode`。收到 `account/login/completed`、`account/updated` 或 `account/rateLimits/updated` 后异步重拉数据;登录完成最多以 0、1、3 秒退避等待套餐分类可用。官方流程和字段见 [Codex App Server 文档](https://learn.chatgpt.com/docs/app-server)。Dockerfile 固定的 CLI 版本及项目测试仍是实际兼容基线。
|
||||
- `GET https://chatgpt.com/backend-api/wham/usage`:套餐、普通限额、代码审查限额、`additional_rate_limits` 和可用重置卡摘要。
|
||||
- `GET https://chatgpt.com/backend-api/wham/profiles/me`:Token 活动摘要与每日 bucket。
|
||||
- `GET https://chatgpt.com/backend-api/wham/rate-limit-reset-credits`:可选的重置卡详情及到期时间。
|
||||
|
||||
## 同步数据流
|
||||
CPA HTTP 层和包装的上游状态分别校验。错误只包含安全的阶段、HTTP 状态和上游错误类型,不包含 management key、OAuth token、原始响应正文或 CPA 凭据内容。请求均有超时和响应体大小限制。
|
||||
|
||||
一次同步依次读取 `account/read`、`account/rateLimits/read` 和 `account/usage/read`:
|
||||
这些 `/wham/*` 路径属于 ChatGPT/Codex 内部接口,并非稳定公开 API;CLIProxyAPI 版本、当前解析代码和测试是本项目的实际兼容基线。
|
||||
|
||||
- `account/read` 决定连接、邮箱、认证模式和套餐;读取失败会使整次同步失败。
|
||||
- 限额读取兼容 `rateLimitsByLimitId` 多 bucket 和旧 `rateLimits` 单 bucket,再把 primary/secondary 展平;同时读取 `rateLimitResetCredits`,只在有可用卡时保留数量和到期时间。失败时保留空限额而不令整次同步失败。
|
||||
- 套餐未知时,可从所有可分类且一致的限额 bucket 回填;冲突或未知时必须保持 unknown。
|
||||
- 用量读取保存 summary 和每日 Token bucket;接口失败时使用空摘要和空历史,不令整次同步失败。同步成功后,以当前仍有效的最长限额窗口作为当前重置周期,按每日 bucket 汇总周期起点至当前日期的 Token,写入 Dashboard 的 `currentCycle`;周期外和未来日期不会计入。
|
||||
- 每次限额同步写入快照、检测用量百分比显著回落,并更新账号元数据及内存 Dashboard。
|
||||
## 限额归一化
|
||||
|
||||
`account/usage/read` 的 optional 指标和 daily buckets 可能暂未提供。仅 API Key 或 Bedrock 登录不能保证读取 ChatGPT 用量;不得在缺失数据时合成调用次数、输入/输出 Token、价格或账单日期。
|
||||
一次成功同步会把 `/wham/usage` 转换为现有 `Dashboard.Limits`:
|
||||
|
||||
重置卡来自 `account/rateLimits/read` 的 `rateLimitResetCredits`。该信息只保存在内存 Dashboard 中,下一次同步会重新读取;没有可用卡时不返回到 Dashboard。当前项目只展示数量和到期时间,不执行消耗操作。
|
||||
- 顶层 `rate_limit` 使用稳定 `limitId=codex`。
|
||||
- `code_review_rate_limit` 使用稳定 `limitId=code_review`。
|
||||
- `additional_rate_limits[]` 优先使用 `metered_feature` 作为 limit ID,`limit_name` 作为显示名称;例如 Spark 的 `codex_bengalfox`。
|
||||
- `primary_window` 和 `secondary_window` 分别保留为 `windowType=primary|secondary`,前端依据窗口时长识别小时和周额度,不假定字段顺序。
|
||||
- `limit_window_seconds` 向上取整转换为分钟。
|
||||
- `used_percent` 限制在 0–100,但保留小数。
|
||||
- 重置时间优先使用 Unix 秒 `reset_at`;缺失时以本次响应时间加 `reset_after_seconds` 推算。
|
||||
- `plan_type` 未知时按原值保留,并由现有套餐分类逻辑安全显示为 unknown。
|
||||
|
||||
`currentCycle` 的周期长度和重置时间来自 `account/rateLimits/read`,不硬编码为七天;通常会选择 secondary 周窗口。由于 daily bucket 只有日期没有每次请求时间,周期边界所在日期按整日汇总,因此该值是当前周期的日级统计,不是 Credits 或美元估值。
|
||||
可用重置卡数量优先以详情接口的 `available_count` 为准,详情接口失败时可退回 `/wham/usage` 的 `rate_limit_reset_credits.available_count`。只有状态为 available 且能解析 RFC3339 `expires_at` 的记录才进入到期时间列表;详情失败不影响主同步。
|
||||
|
||||
## 套餐类型
|
||||
## Token 活动与当前周期
|
||||
|
||||
用户期望类型仅为连接后的校验提示:`any`、`personal`、`team`。当前 personal 包括 free/go/plus/pro/prolite;team 包括 team/business 及两种 self-serve business 标识。未知新套餐必须显示 unknown,不能静默当作某一类型。相同邮箱和相同已知实际类型的多个账号标记 `possibleDuplicate`,但不会自动合并或删除。
|
||||
`/wham/profiles/me` 的 `stats` 提供 lifetime、单日峰值、最长任务、连续天数以及 `daily_usage_buckets`。每日 bucket 继续写入按 `account_id` 隔离的 `daily_usage`;optional、`null`、未来新增字段和暂缺 bucket 必须安全降级,不能合成调用次数、输入/输出 Token、价格或账单日期。
|
||||
|
||||
同步成功后,以当前仍有效的最长限额窗口作为当前重置周期,按每日 bucket 汇总周期起点至当前日期的 Token,写入 `Dashboard.currentCycle`。周期边界只有日期粒度,因此边界日按整日汇总。额度和 Profile Token 活动来自不同上游聚合链路,数据新鲜度可能不同。
|
||||
|
||||
## 调度、并发与失败
|
||||
|
||||
应用启动后异步同步全部已绑定账号,之后固定每五分钟同步;手动同步和 Telegram 立即刷新使用同一链路。每个账号的 `syncing` mutex 串行同步并保护内存 Dashboard,不同账号仍保持数据库、历史和提醒隔离。
|
||||
|
||||
`auth_index` 缺失、CPA 未配置、认证条目不可用或核心 `/wham/usage` 失败会使本次同步失败:已有 Dashboard 保留并标记 `stale=true`,内部错误仅对已登录管理员可见。`/wham/profiles/me` 的 Token 活动和重置卡详情都是可选数据;Profile 失败时同一进程内保留上次成功的摘要和每日历史,没有旧值时降级为空,重置卡详情失败时退回用量接口的可用数量。两者都不会隐藏已成功读取的额度窗口。成功同步会清除 stale/error、写入限额快照、检测百分比回落并更新账号元数据。
|
||||
|
||||
删除账号只删除 Codex Helper 中的绑定、数据库历史和内存 Dashboard,不调用 CPA 删除认证文件。CPA OAuth 凭据的删除和恢复必须在 CLIProxyAPI 中单独执行。
|
||||
|
||||
Reference in New Issue
Block a user