Files
codex-retry-gateway/err.md

174 lines
7.8 KiB
Markdown

# err.md
## 2026-06-26 独立 Codex Retry Gateway
### 设计边界
- 只解决 Codex 已可访问上游时的 `reasoning_tokens = 516` 重试问题
- 不替代 `cc-switch` 的协议路由转换
- 流式场景默认策略是:
- 先缓存上游流
- 一旦检测到命中 `516`
- 统一返回 `502`
### 当前已知限制
- 如果上游只支持 Chat Completions、而 Codex 当前链路需要 Responses 协议转换,这个项目不处理该转换
- 这个项目依赖 Codex / Codex Desktop 自身的自动重试能力
### 本次已确认并修复的问题
1. `gateway.mjs` 非流式透传发头顺序错误
- 现象:`ERR_HTTP_HEADERS_SENT`
- 根因:`writeHead()``copyHeadersToClient()` 之前调用
- 结果:正常 `128` 响应也会被打断
2. PowerShell 脚本在 `powershell.exe` 下的解析兼容性
- 现象:脚本乱码并伴随解析异常
- 根因:新脚本初版包含中文运行时字符串,且 `param(...)` 不在文件最前
- 处理:运行时输出改成 ASCII,并把 `param(...)` 提前到文件顶部
3. `stop-gateway.ps1` 与 PowerShell 内置只读变量 `$PID` 冲突
- 现象:安装脚本在重启 gateway 时失败
- 处理:改用 `$gatewayPid`
4. `start-gateway.ps1` 启动 Node 时路径带空格
- 现象:gateway 进程启动后立刻退出
- 根因:`Start-Process` 参数未显式带引号
- 处理:改为手工拼带引号的 `ArgumentList`
5. PowerShell 单元素数组落盘时被拆成标量
- 现象:`reasoning_equals` 被写成 `516`,不是 `[516]`
- 处理:在公共归一化函数里强制返回数组
6. 旧脏配置迁移后出现嵌套/拼接 endpoints
- 现象:`endpoints` 可能变成嵌套数组,或出现一条用空格拼接的脏字符串
- 处理:安装脚本合并 endpoints 时做递归拍平和空白拆分
7. 真实 Codex 客户端请求路径不是 `/v1/responses`
- 现象:`codex exec` 在 gateway 关闭时真实报错地址是 `http://127.0.0.1:4610/responses`
- 结论:默认配置必须同时覆盖:
- `/responses`
- `/chat/completions`
- `/v1/responses`
- `/v1/chat/completions`
8. UI 恢复动作最初采用“子进程拉起 restore 脚本”方案
- 现象:浏览器拿到 `202`,但临时 `config.toml``state.json``gateway.pid` 都没有变化
- 根因:恢复动作通过 detached 子进程接力时,链路可靠性不足,实际没有把恢复流程真正执行完
- 处理:改为当前 gateway 进程直接复制备份、清理状态并自我退出
9. 新增内嵌 UI 管理页
- 入口:`/__codex_retry_gateway/ui`
- 能力:
- 查看当前接管状态
- 热更新 `reasoning_equals`
- 热更新 `endpoints`
- 热更新 `non_stream_status_code`
- 开关 `log_match`
- 一键恢复 Codex 原设置
10. 用户不接受 `cc-switch` 路由模式,且不希望手工改设置
- 现象:仅有安装脚本和 UI 还不够,首次接管、再次拉起、重新打开 UI 仍需要手工串命令
- 处理:新增 `launch-ui.ps1`
- 结果:
- 首次运行自动安装并打开 UI
- 再次运行自动复用 `state.json + config.json` 并重启 gateway
- 平时规则调整和恢复统一回到 UI 内完成
11. UI 需要动态显示实时日志、`516` 次数和占比
- 现象:原 UI 只能改配置,看不到运行中的命中趋势
- 处理:
-`gateway.mjs` 内增加运行期统计
- 增加日志接口
- UI 轮询显示“被检查响应总数 / 516 命中次数 / 516 占比 / 实时日志”
- 统计口径:
- 按本次 gateway 启动以来累计
- `516` 占比 = `reasoning_tokens = 516` 的响应次数 / 被检查响应总数
12. macOS / Linux 不能直接使用现有 PowerShell 管理脚本
- 现象:`launch-ui.ps1``restore-codex-config.ps1` 等入口绑定了 PowerShell 和 Windows 进程控制
- 处理:
- 新增跨平台 `node` 管理核心
- 新增 `.sh` 包装入口:
- `launch-ui.sh`
- `restore-codex-config.sh`
- `install-for-current-provider.sh`
- `start-gateway.sh`
- `stop-gateway.sh`
- 结果:
- Windows 继续走 `.ps1`
- macOS / Linux 直接走 `.sh`
- UI、状态文件、gateway 主逻辑保持同一套
13. Windows 主机上模拟 Unix shell 入口时存在路径与 Node 版本兼容问题
- 现象:
- Bash 入口最初找不到脚本路径
- Bash 默认 `node` 版本过老,不支持现代语法
- `node.exe` 需要 Windows 路径,而 shell 侧是 POSIX 路径
- 处理:
- 测试改成相对 POSIX 路径执行 `.sh`
- `.sh` 优先选择 `node.exe`
- 在 WSL / Bash 场景下把路径参数转换回 Windows 路径后再交给 `node.exe`
14. 上游流式连接中途终止时被误记为网关错误,首次瞬断也缺少最小重试
- 现象:
- 日志出现:
- `TypeError: terminated`
- `TypeError: fetch failed`
- 其中一部分来自上游 SSE 中途断流,另一部分来自上游首次连接瞬时失败
- 根因:
- `handleStreaming()` 直接把 `reader.read()` 抛出的 `AbortError` / `TypeError: terminated` 冒到统一错误处理
- `proxyRequest()` 对上游 `fetch()` 没有做一次轻量重试,首个瞬断会直接返回 `502`
- 处理:
- 新增预期流终止识别:
- `AbortError`
- `TypeError: terminated`
- 这两类在流式处理中按“连接已结束”收口,不再记 `[error]`
- 新增上游 `fetch failed` 的一次自动重试
- 新增严格 `502` 流式模式:
- 默认不再抢先透传 `200` 头和首个 chunk
- 先缓存流,再根据 `reasoning_tokens` 决定透传或返回 `502`
- 验证:
- `scripts/test-gateway-e2e.mjs`
- 新增 `/responses` 流式覆盖
- 新增“上游半路断流不刷 error 日志”断言
- 新增“首次 fetch failed 后第二次成功恢复”断言
- 新增“流式 `516` 统一返回 `502`,不再先透传半截 chunk”断言
- `scripts/test-install-restore.mjs` 继续通过
### 2026-06-26 实测证据
- 假上游 E2E
- `test-gateway-e2e.ps1` 通过
- 已验证 root 路径和 `/v1` 路径都能区分 `516``128`
- 安装/恢复闭环
- `test-install-restore.ps1` 通过
- 已验证 UI 页面、状态接口、日志接口、516 统计、热更新配置、UI 恢复闭环
- 一键启动入口
- `test-launch-ui.ps1` 通过
- 已验证首次启动自动安装、再次启动自动复用、UI 页面可达、默认 `516 -> 502` 规则仍生效
- Unix shell 入口
- `test-launch-ui-unix.ps1` 通过
- 已验证 `.sh` 入口能完成启动、透传、恢复闭环
- Bash 默认入口实机验证
- `bash ./scripts/launch-ui.sh --no-open` 通过
- 输出 `mode=reuse`
- `GET /__codex_retry_gateway/health` 返回 `200`
- `GET /__codex_retry_gateway/ui` 返回 `200`
- `GET /v1/models` 返回 `200`,并继续透传到真实上游
- Bash 入口后的 `codex exec` 实机验证
- 命令退出码 `0`
- 最后一条消息文件返回 `OK`
- 当前真实 provider
- 当前 Codex 配置里的 `base_url` 已可切到 `http://127.0.0.1:4610`
- 当前 gateway 运行配置里的 `upstream_base_url` 会指向用户自己的真实上游
- `GET /__codex_retry_gateway/health` 返回 `ok=true`
- `GET /v1/models` 已经经本地 gateway 成功透传到真实上游
- `GET /__codex_retry_gateway/ui` 已实机打开,页面显示当前 upstream、provider、config 路径和 516 规则
- 真实 `codex exec`
- gateway 停止时,CLI 真实提示:
- `url: http://127.0.0.1:4610/responses`
- 并自动进入 `Reconnecting...`
- gateway 恢复后,`codex exec` 在临时目录再次成功返回 `OK`