Files

7.8 KiB

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.tomlstate.jsongateway.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 内完成
  1. UI 需要动态显示实时日志、516 次数和占比
  • 现象:原 UI 只能改配置,看不到运行中的命中趋势
  • 处理:
    • gateway.mjs 内增加运行期统计
    • 增加日志接口
    • UI 轮询显示“被检查响应总数 / 516 命中次数 / 516 占比 / 实时日志”
  • 统计口径:
    • 按本次 gateway 启动以来累计
    • 516 占比 = reasoning_tokens = 516 的响应次数 / 被检查响应总数
  1. macOS / Linux 不能直接使用现有 PowerShell 管理脚本
  • 现象:launch-ui.ps1restore-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 主逻辑保持同一套
  1. Windows 主机上模拟 Unix shell 入口时存在路径与 Node 版本兼容问题
  • 现象:
    • Bash 入口最初找不到脚本路径
    • Bash 默认 node 版本过老,不支持现代语法
    • node.exe 需要 Windows 路径,而 shell 侧是 POSIX 路径
  • 处理:
    • 测试改成相对 POSIX 路径执行 .sh
    • .sh 优先选择 node.exe
    • 在 WSL / Bash 场景下把路径参数转换回 Windows 路径后再交给 node.exe
  1. 上游流式连接中途终止时被误记为网关错误,首次瞬断也缺少最小重试
  • 现象:
    • 日志出现:
      • 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 路径都能区分 516128
  • 安装/恢复闭环
    • 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