[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-93247":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":8,"htmlUrl":8,"language":9,"languages":8,"totalLinesOfCode":8,"stars":10,"forks":11,"watchers":12,"openIssues":13,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":14,"stars7d":15,"stars30d":16,"stars90d":14,"forks30d":14,"starsTrendScore":14,"compositeScore":17,"rankGlobal":8,"rankLanguage":8,"license":18,"archived":19,"fork":19,"defaultBranch":20,"hasWiki":21,"hasPages":19,"topics":22,"createdAt":8,"pushedAt":8,"updatedAt":23,"readmeContent":24,"aiSummary":25,"trendingCount":14,"starSnapshotCount":14,"syncStatus":26,"lastSyncTime":27,"discoverSource":28},93247,"grokcli-2api","HM2899\u002Fgrokcli-2api","HM2899",null,"Python",200,54,110,1,0,24,79,62.12,"Apache License 2.0",false,"main",true,[],"2026-07-22 04:02:08","# grokcli-2api\n\n把 **Grok OIDC 登录态** 转成 **OpenAI \u002F Anthropic 兼容 API**，并附带 Web 管理台：多 API Key、多账号轮询、设备码 \u002F SSO \u002F JSON 导入导出、协议注册。\n\n**当前版本：v1.9.73** · early SSE · TTFT 明细 · Codex 加速 · 任务终态帧\n\n[![GHCR](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fghcr.io-hm2899%2Fgrokcli--2api-blue)](https:\u002F\u002Fgithub.com\u002Fusers\u002FHM2899\u002Fpackages\u002Fcontainer\u002Fpackage\u002Fgrokcli-2api)\n[![Release](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fv\u002Frelease\u002FHM2899\u002Fgrokcli-2api?display_name=tag)](https:\u002F\u002Fgithub.com\u002FHM2899\u002Fgrokcli-2api\u002Freleases)\n\n| 镜像（全小写） | 说明 |\n|----------------|------|\n| `ghcr.io\u002Fhm2899\u002Fgrokcli-2api:1.9.73` | 当前版本 |\n| `ghcr.io\u002Fhm2899\u002Fgrokcli-2api:latest` | 最近 `v*` tag |\n| `ghcr.io\u002Fhm2899\u002Fgrokcli-2api:edge` | `main` 最新 |\n\n- **独立运行**：不依赖本地 Grok CLI \u002F 浏览器 OAuth\n- **Hybrid 存储（默认强制）**：PostgreSQL 持久 + Redis 热状态 + 多 Worker\n- **协议注册**：内置 `grok-build-auth`（纯 HTTP，无需 Chromium）\n- **中继友好**：兼容 new-api \u002F sub2api \u002F Claude Code \u002F Codex 工具流\n- **大账号池**：Token 自动续期、模型健康探测、冷却状态落库\n- **会话粘性**：`prompt_cache_key` \u002F `previous_response_id` 固定同一账号，利于多轮缓存\n- **秒开流 + 可观测**：early SSE 信封；用量明细含 `ttft_ms` \u002F `latency_ms`；任务日志 + 终态帧\n\n---\n\n## 架构\n\n```\n客户端 (OpenAI \u002F Anthropic SDK · new-api · Claude Code \u002F sub2api)\n        │  \u002Fv1\u002Fchat\u002Fcompletions  ·  \u002Fv1\u002Fresponses  ·  \u002Fv1\u002Fmessages\n        ▼\n  grokcli-2api  (FastAPI · multi-worker · TZ=Asia\u002FShanghai)\n        │  管理台 \u002Fadmin\n        │  账号轮询 · 失败切换 · Prompt Cache 会话粘性\n        │  任务日志（注册 \u002F SSO \u002F JSON \u002F 测活 \u002F 续期）\n        │  PostgreSQL（账号 \u002F Key \u002F 设置 \u002F 冷却 \u002F 任务日志）—— 容器内网\n        │  Redis（粘性 \u002F 计数 \u002F 锁 \u002F 会话 \u002F 任务进度）—— 容器内网\n        ▼\n  cli-chat-proxy.grok.com\n```\n\n> `data\u002F*.json` **仅作旧版迁移源与管理台导入导出**，运行时权威数据在 PostgreSQL \u002F Redis，不再写本地 JSON 镜像。\n\n---\n\n## 功能一览\n\n| 功能 | 说明 |\n|------|------|\n| OpenAI 兼容 | `\u002Fv1\u002Fmodels` · `\u002Fv1\u002Fchat\u002Fcompletions` · `\u002Fv1\u002Fresponses` · SSE |\n| Anthropic 兼容 | `\u002Fv1\u002Fmessages` · tools \u002F tool_use · `count_tokens` |\n| 管理台 | 账号、Key、协议注册、测活、续期、**任务日志**、用量、设置 |\n| 多账号轮询 | `round_robin` \u002F `least_used` \u002F `random`；可选**出站代理池**（聊天\u002F测活\u002F续期） |\n| 会话粘性 | `prompt_cache_key`（body\u002Fheader）与 Responses `previous_response_id` 粘同一账号；未传时自动 mint 并回传 |\n| 冷却状态 | free-usage 等写入 DB；**请求失败冷却仅测活成功 \u002F 手动解除才回池** |\n| Token 续期 | 后台 leader 维护；永久失败 RT 默认硬删除 |\n| 模型探测 | 单账号 \u002F 多选批量 \u002F 全量；大池优先扫冷却\u002F未知号 |\n| 协议注册 | MoeMail \u002F YYDS \u002F GPTMail \u002F CF Temp Email + 内联过盾 \u002F YesCaptcha；代理池；入池后延迟测活 |\n| SSO \u002F JSON | 后台任务 + 实时进度；JSON 支持多文件导入 \u002F 选中导出 |\n| 任务日志 | 注册、SSO、JSON、测活、续期等结果落 PG |\n| 用量统计 | 代理侧 token \u002F 请求：今日·近 N 天·累计；按 Key \u002F 账号 \u002F 模型；**首字 TTFT \u002F 完成耗时** |\n| 流式可靠性 | early SSE 信封；client_gone \u002F 错误路径仍发终态帧（`message_stop` \u002F `response.completed|failed` + `[DONE]`） |\n| 容器时区 | 默认 `TZ=Asia\u002FShanghai`（日志与本地时间） |\n\n---\n\n## 快速开始\n\n### 方式 A：Docker Compose（推荐）\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002FHM2899\u002Fgrokcli-2api.git\ncd grokcli-2api\ncp .env.example .env\n# 编辑 .env：至少改 GROK2API_ADMIN_PASSWORD；生产请改 Postgres 密码\n\ndocker compose up -d --build\ncurl -fsS http:\u002F\u002F127.0.0.1:3000\u002Fhealth\n```\n\n浏览器打开：`http:\u002F\u002F127.0.0.1:3000\u002Fadmin`\n\n#### 启动时指定打码线程数\n\n主容器内联过盾线程数由 `TURNSTILE_THREAD` 控制（默认与注册并发一致，当前默认 **3**）：\n\n```bash\n# compose 启动时直接传参\nTURNSTILE_THREAD=3 GROK2API_REG_CONCURRENCY=3 docker compose up -d --build\n\n# 或写入 .env\n# GROK2API_CAPTCHA_PROVIDER=local\n# GROK2API_INLINE_SOLVER=1\n# GROK2API_REG_CONCURRENCY=3\n# TURNSTILE_THREAD=3\n```\n\n| 变量 | 默认 | 说明 |\n|------|------|------|\n| `GROK2API_CAPTCHA_PROVIDER` | `local` | `local`（容器内联）\u002F `yescaptcha` |\n| `GROK2API_INLINE_SOLVER` | `1` | `1` 时入口脚本在主容器内启动过盾 |\n| `GROK2API_REG_CONCURRENCY` | `3` | 协议注册默认并发 |\n| `TURNSTILE_THREAD` | `= REG_CONCURRENCY` | 本地过盾浏览器线程数 |\n| `TURNSTILE_BROWSER_TYPE` | `camoufox` | 过盾浏览器类型 |\n| `TURNSTILE_PORT` | `5072` | 内联过盾监听端口（容器内 loopback） |\n\n> 2 核小机器建议 `TURNSTILE_THREAD=1~2`；`3` 已较重，`5` 容易把 CPU\u002F内存打满。\n\n**默认只映射应用端口 `3000`（内联部署）。**  \n栈内 **PostgreSQL \u002F Redis \u002F 本地过盾** 都不绑定宿主机端口：\n\n| 服务 | 容器内地址 | 是否映射到宿主机 |\n|------|------------|------------------|\n| app | `0.0.0.0:3000` | 是 → `127.0.0.1:3000` |\n| postgres | `postgres:5432` | **否**（compose 内网） |\n| redis | `redis:6379` | **否**（compose 内网） |\n| 本地过盾 | `127.0.0.1:5072` | **否**（主容器 loopback 内联） |\n\n因此 compose 里应用环境变量应使用服务名，而不是 `127.0.0.1`：\n\n```env\nREDIS_URL=redis:\u002F\u002Fredis:6379\u002F0\nDATABASE_URL=postgresql:\u002F\u002Fgrok2api:grok2api@postgres:5432\u002Fgrok2api\n```\n\n> `.env.example` 中的 `127.0.0.1` 仅适用于「本机直接跑 Python、自己起 DB」的场景。  \n> `docker compose` 启动时会用 `docker-compose.yml` 中的服务名覆盖，无需改成宿主机端口。\n\n若你**确实**需要从宿主机连库调试，可在本地 `docker-compose.override.yml` 临时加 `ports`（该文件已 gitignore，勿提交）。\n\n### 方式 B：GHCR 镜像（注意小写）\n\nDocker \u002F GHCR **镜像名必须全小写**。仓库 owner 可能是 `HM2899`，但拉取时要用：\n\n```text\nghcr.io\u002Fhm2899\u002Fgrokcli-2api\n```\n\n**错误示例（会拉失败）：** `ghcr.io\u002FHM2899\u002Fgrokcli-2api`  \n**正确示例：**\n\n```bash\ndocker pull ghcr.io\u002Fhm2899\u002Fgrokcli-2api:1.9.73\n# 或\ndocker pull ghcr.io\u002Fhm2899\u002Fgrokcli-2api:latest\n```\n\n最小 compose 示例（内联 redis + postgres，**不要**给 DB 映射宿主机端口）：\n\n```yaml\nservices:\n  redis:\n    image: redis:7-alpine\n    # 不要 ports —— 仅容器网络内访问\n    environment:\n      TZ: Asia\u002FShanghai\n    command: [\"redis-server\", \"--save\", \"\", \"--appendonly\", \"no\"]\n    healthcheck:\n      test: [\"CMD\", \"redis-cli\", \"ping\"]\n      interval: 5s\n      timeout: 3s\n      retries: 10\n\n  postgres:\n    image: postgres:16-alpine\n    environment:\n      TZ: Asia\u002FShanghai\n      PGTZ: Asia\u002FShanghai\n      POSTGRES_USER: grok2api\n      POSTGRES_PASSWORD: change-me\n      POSTGRES_DB: grok2api\n    volumes:\n      - grok2api_pg:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata\n    # 不要 ports —— 仅容器网络内访问\n    healthcheck:\n      test: [\"CMD-SHELL\", \"pg_isready -U grok2api -d grok2api\"]\n      interval: 5s\n      timeout: 5s\n      retries: 10\n\n  grokcli-2api:\n    image: ghcr.io\u002Fhm2899\u002Fgrokcli-2api:1.9.73\n    ports:\n      # 只映射应用；不要给 postgres\u002Fredis 加 ports\n      - \"3000:3000\"\n    environment:\n      TZ: Asia\u002FShanghai\n      GROK2API_HOST: \"0.0.0.0\"\n      GROK2API_PORT: \"3000\"\n      GROK2API_ADMIN_PASSWORD: \"change-me\"\n      GROK2API_STORE_BACKEND: \"hybrid\"\n      GROK2API_REQUIRE_SHARED_STORES: \"1\"\n      GROK2API_WORKERS: \"4\"\n      # 内联本地过盾（主容器 loopback，无需对外端口）\n      GROK2API_CAPTCHA_PROVIDER: \"local\"\n      GROK2API_INLINE_SOLVER: \"1\"\n      REDIS_URL: \"redis:\u002F\u002Fredis:6379\u002F0\"\n      DATABASE_URL: \"postgresql:\u002F\u002Fgrok2api:change-me@postgres:5432\u002Fgrok2api\"\n    volumes:\n      - .\u002Fdata:\u002Fapp\u002Fdata\n    depends_on:\n      redis:\n        condition: service_healthy\n      postgres:\n        condition: service_healthy\n\nvolumes:\n  grok2api_pg:\n```\n\n若包为 private，需先登录：\n\n```bash\necho \"$GITHUB_TOKEN\" | docker login ghcr.io -u YOUR_GITHUB_USERNAME --password-stdin\n```\n\n### 必要环境变量\n\n| 变量 | 说明 |\n|------|------|\n| `GROK2API_ADMIN_PASSWORD` | 管理台密码**首次种子**（无库内哈希时导入；之后以数据库为准） |\n| `GROK2API_STORE_BACKEND=hybrid` | 生产模式 |\n| `GROK2API_REQUIRE_SHARED_STORES=1` | Redis\u002FPG 不可用则拒绝启动 |\n| `REDIS_URL` | Compose 内：`redis:\u002F\u002Fredis:6379\u002F0` |\n| `DATABASE_URL` | Compose 内：`postgresql:\u002F\u002F…@postgres:5432\u002F…` |\n| `GROK2API_WORKERS` | 建议 ≥2（按 CPU） |\n| `TZ` | 容器时区，默认 `Asia\u002FShanghai` |\n| `GROK2API_RELOAD` | 开发热更新：`1` 开启（强制单 worker）；生产保持 `0` |\n\n完整模板见 [`.env.example`](.\u002F.env.example)。**生产请修改默认数据库密码。**\n\n### 会话粘性（Prompt Cache）\n\n多轮请求尽量固定同一 Grok 账号，避免池轮转打断缓存局部性。管理台「会话粘性」默认开启。\n\n上游（Grok \u002F cli-chat-proxy）的 prompt cache 是 **自动 prefix cache**：同一账号 + 相同 messages\u002Ftools 前缀 → usage 里出现 `prompt_tokens_details.cached_tokens`。本项目对齐 [superagent-ai\u002Fgrok-cli](https:\u002F\u002Fgithub.com\u002Fsuperagent-ai\u002Fgrok-cli) 的做法，**主动创造命中条件**：\n\n1. **粘账号**（affinity：`prompt_cache_key` \u002F conversation \u002F response 链）\n2. **出站前缀稳定**（tools schema 规范化 + name 排序；messages 字段\u002F参数 JSON 规范化；system 文本形态统一）\n3. **历史压缩前缀稳定**（`HISTORY_PREFIX_STABLE`：旧 tool 结果确定性 placeholder，不反复改写）\n4. **可观测**（响应字段 \u002F header 回传 cache 命中量）\n\n| 客户端提示 | 行为 |\n|------------|------|\n| `prompt_cache_key`（body 或 `x-prompt-cache-key` header） | 作为稳定指纹；**不**再拼接 conversation root |\n| Anthropic `cache_control` \u002F metadata 缓存键 | 映射为粘性 key |\n| Responses `previous_response_id` | 用上轮发出的 `response_id` 找回账号（不再误当 conversation_id） |\n| 显式 `conversation_id` \u002F 相关 header | 最高优先 |\n\n成功响应可观察：\n\n| 字段 \u002F Header | 含义 |\n|---------------|------|\n| `X-Grok2API-Affinity: 1` \u002F `x_grok2api_affinity` | 本轮命中会话粘性 |\n| `X-Grok2API-Affinity-Source` \u002F `x_grok2api_affinity_source` | 粘性来源：`previous_response_id` \u002F `prompt_cache_key` \u002F `conversation_id` \u002F `root` 等 |\n| `x_grok2api_account` | 实际使用的账号（跨轮应一致） |\n| `x_grok2api_cache_read_tokens` \u002F `X-Grok2API-Cache-Read-Tokens` | 上游返回的 cache 读 token |\n| `x_grok2api_cache_hit_ratio` \u002F `X-Grok2API-Cache-Hit-Ratio` | `cached \u002F prompt`（0–1） |\n| `usage.prompt_tokens_details.cached_tokens` | 标准 usage 字段（OpenAI 兼容） |\n| `X-Grok2API-Prompt-Stable: 1` | 本轮已做 tools\u002Fmessages 出站稳定化 |\n\n管理台 **用量** 页会汇总：\n\n- **token 命中率** = `Σ cache_read_tokens \u002F Σ prompt_tokens`\n- **请求命中率** = 成功且 `cache_read_tokens > 0` 的请求占比  \n数据来自 `usage_events`（不是日汇总表），今日 \u002F 近 N 天 \u002F 累计三档。\n\n历史压缩（`GROK2API_HISTORY_COMPACT=1`）开启时，默认 `GROK2API_HISTORY_PREFIX_STABLE=1`：旧 tool 结果用 **确定性 placeholder**（含内容 hash），后续轮次不再反复改写已压缩前缀，避免打断 prefix cache。\n\n**客户端配合（提高命中率）：**\n\n- 始终传稳定的 `prompt_cache_key`（或 Anthropic metadata \u002F `x-prompt-cache-key`）\n- 不要每轮改 system \u002F tools schema\n- 多轮用同一 API Key；观察 `X-Grok2API-Affinity: 1` 且账号字段跨轮不变\n- 第二轮起看 `cached_tokens > 0`；若 affinity=1 仍为 0，则是上游未回 cache，不是粘性失败\n\n### 本地开发热更新\n\n生产默认 `reload=False` + 多 worker。改代码后要自动重启：\n\n```bash\n# 仅起 Redis\u002FPostgres（若尚未运行）\ndocker compose up -d postgres redis\n\n# 宿主机 Python 热更新（监听 .py \u002F static\u002Fjs \u002F static\u002Fadmin）\n.\u002Fdev.sh\n# 或\nGROK2API_RELOAD=1 GROK2API_WORKERS=1 python app.py\n```\n\n说明：\n- `GROK2API_RELOAD=1` 时强制 **1 worker**（uvicorn 限制）\n- 默认忽略 `data\u002F`、`static\u002Fdist\u002F`、`__pycache__\u002F`，避免写库\u002F打包触发无意义重启\n- 管理台 `static\u002Fjs` 源文件变更会触发进程重启；带 hash 的 `static\u002Fdist` 仍建议跑 `python scripts\u002Fbuild_admin_assets.py`\n- Docker 镜像内一般不挂源码，热更新请用宿主机 `.\u002Fdev.sh`，或 bind-mount 代码后再设 `GROK2API_RELOAD=1`\n\n---\n\n## 从旧版（JSON 文件）升级\n\n详见 **[docs\u002FUPGRADE.md](.\u002Fdocs\u002FUPGRADE.md)**。\n\n```bash\n# 备份 data\u002F 后\nchmod +x scripts\u002Fupgrade_from_file_backend.sh\n.\u002Fscripts\u002Fupgrade_from_file_backend.sh --data-dir .\u002Fdata\n\n# 或\ndocker compose up -d redis postgres\ndocker compose run --rm \\\n  -e DATABASE_URL=postgresql:\u002F\u002Fgrok2api:grok2api@postgres:5432\u002Fgrok2api \\\n  grokcli-2api \\\n  python migrate_json_to_pg.py --data-dir \u002Fapp\u002Fdata --merge-pool\n```\n\n迁移内容：`auth.json` \u002F `keys.json` \u002F `settings.json`（含账号池状态）→ PostgreSQL。  \n不迁移：Redis 热状态、管理台登录会话。\n\n已是 hybrid 时，拉新镜像即可；表结构由 `store\u002Fpg.py` 启动时幂等升级。\n\n---\n\n## 客户端接入\n\n### OpenAI 兼容\n\n```bash\nexport OPENAI_BASE_URL=http:\u002F\u002F127.0.0.1:3000\u002Fv1\nexport OPENAI_API_KEY=你的管理台API_Key\n\ncurl \"$OPENAI_BASE_URL\u002Fchat\u002Fcompletions\" \\\n  -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"model\":\"grok-4.5\",\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}]}'\n```\n\n### Anthropic 兼容\n\n```bash\ncurl http:\u002F\u002F127.0.0.1:3000\u002Fv1\u002Fmessages \\\n  -H \"x-api-key: 你的管理台API_Key\" \\\n  -H \"anthropic-version: 2023-06-01\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"model\":\"grok-4.5\",\"max_tokens\":256,\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}]}'\n```\n\nClaude Code \u002F Cursor \u002F Cherry Studio：Base URL 填服务地址（通常带 `\u002Fv1`），Key 用管理台创建的 API Key。\n\n---\n\n## 管理台\n\n| 页面 | 用途 |\n|------|------|\n| 概览 | 池规模、续期\u002F探测状态、今日用量 |\n| 账号 \u002F 轮询 | 设备码、**SSO 导入（进度）**、**JSON 导入\u002F导出（进度）**、协议注册、测活、续期 |\n| API Keys | 客户端密钥 |\n| 用量 | Token \u002F 请求：今日·近 N 天·累计；Key \u002F 账号 \u002F 模型；请求明细 |\n| 任务日志 | 协议注册、SSO、JSON 导入导出、测活、Token 续期等后台任务结果 |\n| 设置 | 轮询与冷却策略、协议注册默认项等 |\n\n### 账号导入 \u002F 导出\n\n| 方式 | 说明 |\n|------|------|\n| SSO Cookie | 粘贴或上传；后台 Device Flow 换 token，页面显示进度条与明细 |\n| JSON 文件 | 支持多文件合并导入；解析 → 入库全程进度 |\n| 导出全部 \u002F 选中 | 后台打包，完成后自动下载；大池不阻塞页面 |\n\n导入导出、测活、续期等完成后，可在 **任务日志** 按类型 \u002F 状态 \u002F 关键词查询历史结果。\n\n### 协议注册\n\n依赖 **临时邮箱** + **过盾**（环境变量或管理台配置，存 PG）：  \n- 邮箱：`MoeMail` \u002F **YYDS Mail**（[vip.215.im](https:\u002F\u002Fvip.215.im\u002Fdocs)）\u002F **GPTMail**（[mail.chatgpt.org.uk](https:\u002F\u002Fmail.chatgpt.org.uk\u002Fzh\u002Fapi\u002F)）  \n- 过盾：本地内联 Turnstile Solver 或 YesCaptcha  \n\n本地过盾默认与主容器同进程（`127.0.0.1:5072`），**无需填写 URL**；选 YesCaptcha 时仅用云端 Key。  \n邮箱有效期：MoeMail 支持 1 小时 \u002F 1 天 \u002F 3 天 \u002F 永久；YYDS \u002F GPTMail 临时邮箱约 24 小时。  \n新注册账号入池后默认 **延迟 30s** 再自动测活；可在管理台「测活等待秒」调整，或用环境变量 `GROK2API_REG_PROBE_DELAY_SEC`（`0`=立即测活）。\n\n---\n\n## 运维\n\n```bash\ncurl -fsS http:\u002F\u002F127.0.0.1:3000\u002Fhealth\ncurl -fsS http:\u002F\u002F127.0.0.1:3000\u002Fmetrics | head\ndocker compose logs -f grokcli-2api\n# 时区\ndocker exec grokcli-2api sh -c 'echo TZ=$TZ; date'\n```\n\n- 仅 **leader** worker 跑 Token 续期与模型健康任务（Redis 选主）\n- 备份重点：**PostgreSQL 卷**（`grok2api_pg`）；Redis 可丢\n- 本地低停机重建：`.\u002Fdocker-rebuild.sh`\n- Postgres \u002F Redis **默认不暴露宿主机端口**\n- 任务日志表 `task_logs` 在 hybrid 启动时幂等创建\n- 默认时区 **Asia\u002FShanghai**（`TZ` \u002F Dockerfile `tzdata`）\n\n### 发布镜像（GHCR）\n\n```bash\n# 1) app.py 中 APP_VERSION 必须与 git tag 一致（镜像路径全小写）\n# 2) 推 main → edge + 版本号；推 v* tag → 额外 latest + GitHub Release\ngit add -A && git commit -m \"release: v1.9.73\"\ngit push origin main\ngit tag -a v1.9.73 -m \"v1.9.73\"\ngit push origin v1.9.73\ngh release create v1.9.73 --title \"v1.9.73 early SSE · TTFT · Codex\" --notes-file - \u003C\u003C'EOF'\n## Highlights\n- early SSE 信封 + TTFT \u002F latency 用量明细\n- Codex \u002F Responses 多工具加速与 sticky prompt_cache_key\n- 任务\u002F工具流终态帧，避免 sub2api \u002F Claude Code 卡 running\nEOF\n# 监视构建\ngh run list --workflow=docker-publish.yml --limit 3\n```\n\n成功后拉取（**必须小写**）：\n\n```bash\ndocker pull ghcr.io\u002Fhm2899\u002Fgrokcli-2api:1.9.73\ndocker pull ghcr.io\u002Fhm2899\u002Fgrokcli-2api:latest\n```\n\nCI 会把 `github.repository` 强制转成小写后再推送，避免 `HM2899` 大小写导致 `docker pull` 失败。  \n`docker-publish.yml` 在 tag 推送时还会校验 `v*` 与 `APP_VERSION` 一致。\n\n---\n\n## 目录提示\n\n```\napp.py \u002F admin_routes.py              # API 与管理路由\nanthropic_compat.py \u002F openai_responses.py  # Claude \u002F Responses 流与终态帧\nconversation_affinity.py              # 会话粘性 \u002F prompt_cache_key \u002F response 链\nhistory_compact.py                    # 大上下文压缩 \u002F 出站工具 gap\nproxy_pool.py                         # 出站 \u002F 注册代理池\ntask_log.py \u002F store\u002Ftask_logs_pg.py   # 任务日志\nstore\u002F                                # Redis + PostgreSQL\nmigrate_json_to_pg.py                 # JSON → PG\nscripts\u002Fbuild_admin_assets.py         # 管理台静态资源打包\nscripts\u002F_test_task_status_terminal.py # TaskUpdate \u002F 终态帧回归\ndocs\u002FUPGRADE.md                       # 升级说明\nstatic\u002F                               # 管理台前端\ngrok-build-auth\u002F                      # 协议注册引擎（vendored）\nturnstile-solver\u002F                     # 本地过盾（内联；懒加载 + 空闲回收）\ndocker-compose.yml                    # redis + postgres（内网）+ app\n.github\u002Fworkflows\u002Fdocker-publish.yml  # GHCR 多架构（小写镜像名）\n```\n\n---\n\n## 安全与免责\n\n- 勿将 `.env`、`data\u002F`、真实 Token 提交到 Git\n- 生产务必修改 Postgres 密码与管理员密码\n- 默认不映射 DB\u002FRedis 端口；调试用本地 override，勿对公网暴露\n- 导出 JSON 含完整 token，请妥善保管\n- 协议注册与账号自动化请遵守 xAI 服务条款与当地法律；本项目仅供自用\u002F研究集成\n\n---\n\n## 版本\n\n- **v1.9.73**（当前）\n  - **用量明细首字\u002F完成时间**：`ttft_ms` + `latency_ms` 落库与管理台展示\n  - **Codex 加速**：原生 Responses 多工具\u002F零 gap、大上下文自动压缩、`previous_response_id` sticky 恢复同一 prompt_cache_key\n  - **断联加固**：修复 warmup 污染 AsyncClient 导致的 `Event loop is closed`；本地 infra 错误不冷却账号\n  - **Update\u002FEdit 修复**：允许 `new_string=\"\"` 删除；不完整 tool 不再空 `{}` 上线\n  - **任务状态收尾**：`client_gone` 仍发 content_block_stop + message_delta\u002Fstop；terminal_error 补 stop_reason\n  - 继承 v1.9.72：early SSE 信封、TTFT 诊断\n- **v1.9.72**\n  - **early SSE 信封**：上游 HTTP 200 后立即发 `message_start` \u002F `response.created` \u002F role chunk，恢复前几版“秒开流”体感；empty-200 改为干净终态错误（不再静默切号）\n  - **TTFT 诊断增强**：日志增加 `early` \u002F `tools` \u002F `held` \u002F `env`，区分信封打开 vs 首 token vs 工具前言 hold\n  - 继承 v1.9.71：测活才解冷却、自动 prompt_cache_key、sub2api 终态帧\n- **v1.9.71**\n  - **严格冷却恢复**：请求失败进入冷却后不再按时间自动恢复，仅测活成功或管理台手动解除才回池\n  - 继承 v1.9.70：自动 prompt_cache_key、sub2api 终态帧\n- **v1.9.70**\n  - **自动 prompt_cache_key**：客户端未传时按 conversation \u002F previous_response_id \u002F session 生成稳定 key，并在响应 body\u002Fheader 回传（`prompt_cache_key` \u002F `X-Grok2API-Prompt-Cache-Key`）\n  - 响应链绑定保存 minted key，仅带 `previous_response_id` 的下一轮也能恢复同一 sticky key\n  - 继承 v1.9.69：sub2api 终态帧修复、空 200 冷却降敏\n- **v1.9.69**\n  - **sub2api 终态帧修复**：`ResponsesLiveStreamer.complete()` 空结果不再 `_closed`，保证后续 `response.failed` + `[DONE]` 可发出，消除 sub2api `missing terminal event` \u002F `upstream stream ended without terminal event`\n  - **空 200 冷却降敏**：empty model output 只短冷却 8–20s，避免号池被打空后 sub2api 报 `no available accounts`\n  - 推荐 sub2api 上游用 Docker 内网 `http:\u002F\u002Fgrokcli-2api:40081\u002Fv1`（避免重启瞬间公网 IP connection refused）\n  - 继承 v1.9.68：断联 usage 明细补全\n- **v1.9.68**\n  - **断联明细补全**：`\u002Fv1\u002Fresponses` \u002F chat \u002F Anthropic 失败路径写入真实 `error` + `detail.message`（上游 status\u002Fbody、空 200、代理异常、全号失败），不再落成裸 `request_failed` + `{}`\n  - 失败 usage 行补 `latency_ms`；`_record_usage_safe` 对 `!ok` 强制补 status\u002Fmessage，方便管理台「断联」排查\n  - 继承 v1.9.67：模型列表入库、续期永久失败硬删除、断联防抖\n- **v1.9.67**\n  - **模型列表入库**：`GET \u002Fv1\u002Fmodels` 只读 PostgreSQL `models` 表；管理台「同步上游模型」从 cli-chat-proxy 拉取并写入数据库（不再读写 `models_cache.json`）\n  - 启动时若表为空，自动灌入默认模型 + 本地 extras；`migrate_json_to_pg.py` 仍可一次性导入旧 `models_cache.json`\n  - **运行时不再写本地 JSON 镜像**：hybrid 下账号 \u002F Key \u002F 设置只落 PostgreSQL；会话粘性只走 Redis；`data\u002F*.json` 仅迁移与管理台导入导出\n- **v1.9.66**\n  - **续期永久失败硬删除**：`invalid_grant` \u002F refresh token revoked 默认直接踢出号池并删除账号（`GROK2API_DELETE_INVALID_REFRESH=1`）\n  - 启动与维护周期 purge 清掉已标记的 dead RT；设 `=0` 才回退 soft-disable\n- **v1.9.65**\n  - **断联防抖**：`is_disconnected` 需连续命中（默认 2，`GROK2API_DISCONNECT_HITS`）才判定 client_gone，避免背压单次 blip 硬切\n  - **stream_started 后置**：仅在真正 yield 出站帧后锁定账号\u002F禁止静默切号，假断联不再阻断 failover\n  - 继承 v1.9.64：软断开终态帧、工具参数别名\u002FUpdate 合并、xhigh thinking\n- **v1.9.64**\n  - **偶发流中断修复**：OpenAI \u002F Anthropic \u002F Responses 软断开不再硬切 SSE；`is_disconnected` 探活异常不再误判 `client_gone`\n  - 已开流时始终发出终态帧（finish\u002F`[DONE]`、`message_delta`\u002F`message_stop`、`response.completed`\u002F`failed`），避免 sub2api\u002FClaude Code 停调度\n  - **工具参数加固**：Update 双 JSON 合并取更完整对象；`path`\u002F`oldString` 等别名归一为 Claude Code schema；schema 不完整工具不刷出\n  - OpenAI chat 默认不限多工具（`GROK2API_OUTBOUND_MAX_TOOLS_OPENAI=0`）；Claude\u002Fsub2api 路径仍默认单工具\n  - xhigh thinking \u002F `budget_tokens` 映射到 `reasoning_effort=xhigh`\n- **v1.9.63**\n  - **注册进度 404 停轮询**：浏览器缓存的过期 `batch_*` \u002F `gba_*` 在后端不存在时清理 track 并停止轮询，避免控制台刷 404\n  - 停止按钮对已消失 batch\u002Fsession 做 not-found 降级\n- **v1.9.62**\n  - **tool_choice 空 200 修复**：sub2api\u002FClaude Code 强制工具时的 `{\"type\":\"function\",\"name\":…}` \u002F nested function 形态映射为 `\"required\"`，避免 cli-chat-proxy 空 body 导致前端 empty envelope\n  - 覆盖 Anthropic `tool`\u002F`any` tool_choice 变体\n- **v1.9.61**\n  - **Responses 失败流修复**：`response.failed` 前必发 `response.created`\u002F`in_progress`；中途失败沿用单调 `sequence_number`（不再回绕到 0）\n  - 修复 Claude Code 将 bare\u002F回绕的 failed SSE 误判为 `empty or malformed response (HTTP 200)`\n- **v1.9.60**\n  - **本地过盾就绪门闩**：注册在本地过盾模式下等待 solver HTTP 就绪后再开跑\n  - **Responses 协议修复**：`sequence_number` 单调且 `response.created` 永远先发；`response.completed` 复用流中 item id（不再重新生成 msg_\u002Ffc_）\n  - 修复 Claude Code \u002F sub2api 将乱序 SSE \u002F id 不一致误判为 `empty or malformed response (HTTP 200)`\n- **v1.9.58**\n  - **工具参数必填键 hold**：Responses 路径在 `anthropic_compat` 导入失败时仍按 `Read.file_path` \u002F `Bash.command` 等本地规则 hold，避免半成品 tool 提前开 `response.created` 导致 Claude Code 报 empty\u002Fmalformed HTTP 200\n  - 回归测试覆盖 local fallback + 网关拦截 body 分类\n- **v1.9.57**\n  - **空 200 流式切号**：Anthropic `message_start` \u002F Responses `response.created` 延后到真实模型输出后才开流，空 body \u002F 网关拦截页可静默切号\n  - OpenAI chat 流仅在真正发出 content\u002Ftool 帧后才锁定账号（忽略不完整 tool 预览）\n- **v1.9.56**\n  - 本地部署默认时区与 prompt-cache 粘性加固后的版本号 bump\n- **v1.9.55**\n  - **Prompt Cache 会话粘性**：`prompt_cache_key` 单独指纹（不 fold root）；Responses `response_id` 链绑定 `previous_response_id`\n  - chat \u002F messages \u002F responses 统一 `api_key_id` 命名空间；流式\u002F非流式均 bind\n  - **默认容器时区** `Asia\u002FShanghai`（Dockerfile + compose + `.env.example`）\n  - 合并待发布：额度冷却自动恢复、出站\u002F注册代理池、大池测活优先扫、空 200 故障切换等（1.9.50–1.9.54）\n- **v1.9.54**：free-usage 冷却时间窗（15m→1h）到期回池；billing 恢复自动解禁\n- **v1.9.53**：空 200 \u002F 网关拦截页可重试切号\n- **v1.9.52**：账号池出站代理池（聊天\u002F测活\u002F续期粘性选代理）\n- **v1.9.51**：协议注册代理池多行 + 策略 + 抽测\n- **v1.9.50**：大池测活优先扫冷却\u002F未知；限流可复检\n- **v1.9.49**：注册任务日志 + 进度硬刷新恢复\n- **v1.9.48**：注册进度恢复；Turnstile 空闲回收加固\n- **v1.9.47**：Turnstile 懒加载 + 空闲回收；默认 workers=2\n- **v1.9.46**：Cloudflare Temp Email；续期软禁用；流式加固\n- **v1.9.45–1.9.38**：YYDS 域名、任务日志、JSON\u002FSSO 进度、内联 hybrid 等\n- 更早变更见 [GitHub Releases](https:\u002F\u002Fgithub.com\u002FHM2899\u002Fgrokcli-2api\u002Freleases)\n\n> 镜像 tag 与 `app.py` 中 `APP_VERSION` 一致（当前 **1.9.73**）。  \n> 拉取路径固定 **`ghcr.io\u002Fhm2899\u002Fgrokcli-2api`**（全小写）。\n\n## License\n\n见 [LICENSE](.\u002FLICENSE)。\n","grokcli-2api 是一个将 Grok OIDC 登录态转换为 OpenAI\u002FAnthropic 兼容 API 的反向代理服务。它支持多账号轮询、SSO\u002F设备码登录、JSON 导入导出与协议注册（如 MoeMail、CF 临时邮箱），内置 PostgreSQL+Redis 混合存储、会话粘性、Token 自动续期及模型健康探测，并提供 Web 管理台与细粒度用量统计（含 TTFT、延迟等指标）。适用于需对接 Grok 后端但希望复用标准 LLM SDK 或中继架构的私有部署场景，如企业内部 AI 网关、多租户大模型调度平台或合规敏感环境下的认证中继服务。",2,"2026-07-14 02:30:05","CREATED_QUERY"]