CPA 代理 Codex 卡在"正在思考"的排查与 WebSocket 解决方案
使用 CPA 代理 Codex / Codex CLI / Codex Desktop 时,可能会遇到这些现象:
- 卡在"正在思考",长时间没有输出。
- 请求反复失败,偶发超时。
- CPA 代理下 HTTPS / SSE 转发不稳定。
codex doctor显示 WebSocket 失败,随后 fallback 到 HTTPS。
这类问题不一定和出口 IP 区域有关,更常见的关键点是 Codex 到 CPA、CPA 到 OpenAI 这两段是否真的走了 WebSocket。本文整理完整配置方式,以及如何确认链路是否已经变成全程 WebSocket。
目标链路
理想的全程 WebSocket 链路是:
Codex ---ws---> CPA ---wss---> OpenAI / ChatGPT Codex backend
也就是:
Codex -> ws://localhost:8317/v1/responses
CPA -> wss://chatgpt.com/backend-api/codex/responses
注意,这里有两段链路:
Codex -> CPA:由 Codex provider 配置决定。CPA -> OpenAI:由 CPA 当前选中的 Codex OAuth auth 文件决定。
只配置其中一段,可能只能得到半程 WebSocket。
解决方案:开启全程 WebSocket
目标是让 Codex 到 CPA 使用 WebSocket,同时 CPA 到 OpenAI 也使用 WSS。
1. 修改 Codex 配置
编辑 Codex 配置文件:
~/.codex/config.toml
找到 CPA 对应的 provider,例如:
[model_providers.cliproxyapi]
base_url = "http://localhost:8317/v1"
在这个 provider 下添加:
supports_websockets = true
完整示例:
[model_providers.cliproxyapi]
name = "cliproxyapi"
base_url = "http://localhost:8317/v1"
wire_api = "responses"
supports_websockets = true
这个配置必须放在 [model_providers.cliproxyapi] 下面,不要放到 [features] 下面。
2. 不要乱配 feature flag
不要盲目在 [features] 下添加:
responses_websockets_v2 = true
部分 Codex 版本中相关 feature flag 已经被移除或默认内置。可以用下面命令确认:
macOS:
/Applications/Codex.app/Contents/Resources/codex debug features
Windows 示例:
codex debug features
如果输出里显示类似:
responses_websockets: removed
responses_websockets_v2: removed
说明这个开关已经不再需要,也不应该再配。关键配置是 provider 里的 supports_websockets = true。
3. 修改 CPA 的 Codex auth 文件
只改 Codex 配置,通常只能保证:
Codex ---ws---> CPA
要实现:
CPA ---wss---> OpenAI
还需要让 CPA 当前使用的 Codex OAuth auth 开启 WebSocket。
CPA 的 Codex OAuth auth 文件一般在:
macOS 常见位置:
~/.cli-proxy-api/
Windows 运行包常见位置示例:
<CPA_RUNTIME_DIR>\auth\
文件名通常类似:
codex-your-email@example.com-plus.json
codex-your-email@example.com-free.json
编辑 CPA 实际选中的 Codex auth 文件,在 JSON 顶层添加:
"websockets": true
示例结构:
{
"websockets": true,
"access_token": "...",
"account_id": "...",
"email": "...",
"refresh_token": "...",
"type": "codex"
}
注意:
websockets是 JSON 顶层字段。- 不要放进
metadata、attributes或其他嵌套对象里。 - 如果有多个 Codex auth 文件,要改 CPA 实际选中的那个。
- CPA 日志中的
Use OAuth provider=codex auth_file=...会显示当前使用的是哪个 auth 文件。
验证步骤
1. 用 Codex doctor 验证 Codex 到 CPA
macOS:
/Applications/Codex.app/Contents/Resources/codex doctor
Windows / PATH 已配置时:
codex doctor
成功时应看到类似:
websocket connected (HTTP 101 Switching Protocols)
supports websockets true
endpoint ws://localhost:8317/v1/...
这说明:
Codex ---ws---> CPA
已经成立。
2. 用 CPA Manager 监控辅助判断
CPA Manager 的监控列表也可以作为一个直观信号。开启 WebSocket 后,监控里对应模型的请求方法通常会显示为:
GET /v1/responses
这和普通 HTTP/SSE 请求不一样。之前走 HTTP/SSE fallback 时,请求通常是:
POST /v1/responses
Accept: text/event-stream
原因是 WebSocket 握手本身是 HTTP GET + Upgrade: websocket,成功后才升级成 WebSocket 连接。所以在 CPA Manager 里看到连续成功的 GET /v1/responses,基本可以说明 Codex -> CPA 这一段已经在走 WebSocket 路由。
不过它只能证明下游请求进入了 CPA 的 WebSocket handler。要确认 CPA -> OpenAI 也变成 WSS,还需要继续看 main.log 里是否有 codex websockets: upstream connected ... url=wss://...。
3. 打开 CPA 日志验证 CPA 到 OpenAI
这一步不是长期必需,只建议临时打开用于验证。编辑 CPA 配置文件。
macOS Homebrew 常见位置:
/opt/homebrew/etc/cliproxyapi.conf
Windows 运行包示例:
<CPA_RUNTIME_DIR>\config.yaml
临时打开:
debug: true
request-log: true
logging-to-file: true
然后重启 CPA。
日志文件常见位置:
macOS:
~/.cli-proxy-api/logs/main.log
Windows 运行包示例:
<CPA_RUNTIME_DIR>\auth\logs\main.log
用 rg 检索关键日志:
macOS:
rg -n "codex websockets|upstream connected|wss://|responses websocket|Use OAuth" ~/.cli-proxy-api/logs/main.log
Windows:
rg -n "codex websockets|upstream connected|wss://|responses websocket|Use OAuth" <CPA_RUNTIME_DIR>\auth\logs\main.log
成功时会看到类似:
responses websocket: client connected ... remote=127.0.0.1
Use OAuth provider=codex auth_file=codex-example@example.com-plus.json ...
codex websockets: upstream connected ... url=wss://chatgpt.com/backend-api/codex/responses
这就说明链路已经是:
Codex ---ws---> CPA ---wss---> OpenAI
验证完成后,建议把 CPA 日志开关关回去:
debug: false
request-log: false
logging-to-file: false
然后再次重启 CPA。
原因是 request-log: true 可能记录完整请求 payload,包括对话内容、工具调用参数等,长期打开会增加磁盘占用和敏感信息落盘风险。
常见误区
误区 1:把 supports_websockets 放错位置
错误写法:
[features]
supports_websockets = true
正确写法:
[model_providers.cliproxyapi]
supports_websockets = true
supports_websockets 是 provider 能力配置,不是 feature flag。
误区 2:只开了 Codex 到 CPA
如果 CPA 日志里只有:
responses websocket: client connected
但没有:
codex websockets: upstream connected ... url=wss://...
说明大概率只是:
Codex ---ws---> CPA
还没有实现:
CPA ---wss---> OpenAI
这时继续检查 CPA 当前选中的 Codex auth 文件是否有:
"websockets": true
误区 3:被本机代理环境变量干扰
有时环境里设置了:
HTTP_PROXY
HTTPS_PROXY
http_proxy
https_proxy
但 NO_PROXY 没包含本地地址,导致 ws://127.0.0.1:8317 也被拿去走外部代理。
典型报错可能是:
HTTP CONNECT failed with status 504
Proxy URL scheme not supported
临时验证时可以设置:
macOS / Linux:
NO_PROXY=127.0.0.1,localhost no_proxy=127.0.0.1,localhost codex doctor
Windows PowerShell:
$env:NO_PROXY = "127.0.0.1,localhost"
$env:no_proxy = "127.0.0.1,localhost"
codex doctor
如果这样就恢复 HTTP 101 Switching Protocols,说明本地代理绕过规则需要补齐。
误区 4:cc-switch 端口能监听但不支持 WebSocket 路由
有些情况下 Codex provider 指向了 cc-switch.exe 暴露的端口,例如:
ws://127.0.0.1:15721/v1/...
codex doctor 可能显示:
supports websockets true
Responses WebSocket failed
HTTP 405 Method Not Allowed
这说明:
- Codex 端已经尝试 WebSocket。
- 配置也认为 provider 支持 WebSocket。
- 但本地
cc-switch/ COA 的/v1/...WebSocket 路由没有正确接住。 - 实际请求会 fallback 到 HTTPS/SSE,仍可能卡在"正在思考"。
实践建议:如果你的目标只是让 Codex 通过 CPA 访问上游,并且 CPA 已经能提供 http://127.0.0.1:8317/v1 这类 OpenAI-compatible provider,那么可以先关掉 cc-switch,让 Codex 直接指向 CPA。这样链路更短,排查也更清楚。除非你明确依赖 cc-switch 做模型切换或其他额外能力,否则它在这条 WebSocket 修复链路里通常不是必需层。
排查方向:
- 确认 Codex provider 是否应该指向 CPA,而不是 cc-switch 临时端口。
- 确认
base_url是否为 CPA 端口,例如http://127.0.0.1:8317/v1。 - 用
Get-NetTCPConnection/lsof查监听进程,确认端口背后到底是谁。 - 如果必须经过 cc-switch,确认该版本是否支持 Codex Responses WebSocket 路由。
Windows 查看监听进程示例:
Get-NetTCPConnection -LocalPort 8317 | Select-Object LocalAddress,LocalPort,State,OwningProcess
Get-CimInstance Win32_Process | Where-Object { $_.ProcessId -eq <PID> } | Select-Object ProcessId,Name,CommandLine
macOS 查看监听进程示例:
lsof -nP -iTCP:8317 -sTCP:LISTEN
Windows 实测参考
以下是一组 Windows 上验证成功的关键点,路径仅作示例:
Codex 配置:
[model_providers.cliproxyapi]
name = "cliproxyapi"
base_url = "http://127.0.0.1:8317/v1"
supports_websockets = true
wire_api = "responses"
CPA 进程:
<CPA_RUNTIME_DIR>\cli-proxy-api.exe
CPA 配置:
<CPA_RUNTIME_DIR>\config.yaml
CPA auth 目录:
<CPA_RUNTIME_DIR>\auth\
CPA 日志:
<CPA_RUNTIME_DIR>\auth\logs\main.log
成功日志关键行:
responses websocket: client connected ... remote=127.0.0.1
Use OAuth provider=codex auth_file=codex-...-plus.json for model gpt-5.5
codex websockets: upstream connected ... url=wss://chatgpt.com/backend-api/codex/responses
成功后 codex doctor 关键行:
websocket connected (HTTP 101 Switching Protocols)
supports websockets true
endpoint ws://127.0.0.1:8317/v1/<redacted>
快速判断表
现象 含义 下一步websocket connected (HTTP 101)
Codex 到 CPA 已经是 WS
继续看 CPA 日志确认上游 WSS
CPA Manager 显示 GET /v1/responses
下游大概率已走 WS upgrade 路由
继续看 main.log 确认上游 WSS
responses websocket: client connected
CPA 收到了下游 WS
继续找 upstream connected
codex websockets: upstream connected ... wss://...
CPA 到 OpenAI 已经是 WSS
全程 WebSocket 成功
HTTP 405 Method Not Allowed
本地服务没接住 WS 路由
检查端口背后是不是 cc-switch/旧 COA
HTTP CONNECT failed 504
本地 WS 被错误送去代理
设置 NO_PROXY=127.0.0.1,localhost
POST /v1/responses + Accept: text/event-stream
可能仍在 HTTP/SSE fallback
检查 Codex provider 与 CPA auth
最终检查清单
- Codex provider 下有
supports_websockets = true。 - Codex provider 的
base_url指向 CPA,而不是不支持 WS 的中间服务。 - CPA 当前使用的 Codex auth JSON 顶层有
"websockets": true。 codex doctor显示HTTP 101 Switching Protocols。- CPA
main.log显示responses websocket: client connected。 - CPA
main.log显示codex websockets: upstream connected ... url=wss://chatgpt.com/backend-api/codex/responses。 - 验证完成后关闭
debug、request-log、logging-to-file。
4 个帖子 - 4 位参与者