# 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`