@@ -57,17 +57,6 @@ var codexOfficialClientOriginators = map[string]bool{
5757 "codex_sdk_ts" : true , // TypeScript SDK
5858}
5959
60- // IsBrowserUserAgent 判断 User-Agent 是否来自浏览器(Chrome/Firefox/Safari/Edge/Opera 等)。
61- // 所有现代浏览器的 UA 均以 "Mozilla/" 作为前缀,CLI 工具(codex/claude/curl/postman/python-requests 等)不会。
62- // 该判定用于避免 Cloudflare 对浏览器型 UA 在 OpenAI 上游接口上触发 JS 质询。
63- func IsBrowserUserAgent (userAgent string ) bool {
64- ua := strings .TrimSpace (userAgent )
65- if ua == "" {
66- return false
67- }
68- return strings .HasPrefix (strings .ToLower (ua ), "mozilla/" )
69- }
70-
7160// IsCodexCLIRequest checks if the User-Agent indicates a Codex CLI request
7261func IsCodexCLIRequest (userAgent string ) bool {
7362 ua := normalizeCodexClientHeader (userAgent )
@@ -259,46 +248,21 @@ func canonicalizeCodexOriginator(name string) string {
259248// 也是身份归一化的目标身份。
260249const CodexCLIOriginator = "codex_cli_rs"
261250
262- // codexLoadShedOriginators:上游 /backend-api/codex 按 originator 分桶调度容量,命中降载桶的
263- // 请求即使 HTTP 200 也会立刻推 SSE `event: error`(code=server_is_overloaded)并以
264- // response.failed 收尾。2026-07-29 起 codex-tui 被观测到落入降载桶:同账号、同请求体、同 UA,
265- // 仅把 originator 换成 codex_cli_rs 即恢复正常(换言之 UA 不是判定因子,originator 才是)。
266- // 网关会把该错误判定为瞬时上游故障并让账号进入冷却,对外表现为「账号过载不可用」,
267- // 因此出站前需要把命中的身份改写为 CLI 身份。
268- //
269- // 该集合是上游容量策略的快照而非协议常量,上游调整分桶后需同步修订。
270- var codexLoadShedOriginators = map [string ]bool {
271- "codex-tui" : true ,
272- }
273-
274- // IsCodexLoadShedOriginator 判断 originator 是否落在上游降载桶。
275- func IsCodexLoadShedOriginator (originator string ) bool {
276- return codexLoadShedOriginators [normalizeCodexClientHeader (originator )]
277- }
278-
279- // NormalizeCodexClientIdentityToCLI 把落在降载桶的官方身份改写为 Codex CLI 身份:
280- // UA 首段替换为 codex_cli_rs,并裁掉尾部 `(name; version)` 客户端标识组(真实 CLI UA 无该组),
281- // 版本 / OS / 架构 / 终端指纹原样保留。返回配套的 originator 与 UA,未命中降载桶时 changed=false。
282- //
283- // 入参应为 PairCodexClientIdentity 输出的已配对身份;改写后 UA 首段与 originator 仍然配套,
284- // 不破坏上游的配对校验。
285- func NormalizeCodexClientIdentityToCLI (originator , userAgent string ) (string , string , bool ) {
286- if ! IsCodexLoadShedOriginator (originator ) {
287- return originator , userAgent , false
288- }
251+ // CodexUserAgentVersion 提取 Codex UA 的完整版本段,即 `{client}/{version} (...` 中的 version。
252+ // 与 ParseCodexEngineVersion 的区别:后者只取三段数字用于引擎版本比较(会丢掉 -alpha.4
253+ // 之类的预发布后缀),本函数保留原样,因为出站 version 头必须与 UA 版本段逐字一致。
254+ // 取不到(非 Codex 形态 UA)时返回空串。
255+ func CodexUserAgentVersion (userAgent string ) string {
289256 ua := strings .TrimSpace (userAgent )
290257 slash := strings .IndexByte (ua , '/' )
291258 if slash <= 0 {
292- return CodexCLIOriginator , ua , true
259+ return ""
293260 }
294- rest := ua [slash :]
295- // 仅当尾部括号组确为官方客户端标识时才裁剪,避免误截合法 UA 尾巴(如 `(Ubuntu 22.4.0; x86_64)`)。
296- if trailer := codexUATrailerName (ua ); trailer != "" && IsCodexOfficialClientOriginator (trailer ) {
297- if open := strings .LastIndex (rest , "(" ); open > 0 {
298- rest = strings .TrimRight (rest [:open ], " " )
299- }
261+ rest := ua [slash + 1 :]
262+ if space := strings .IndexByte (rest , ' ' ); space >= 0 {
263+ rest = rest [:space ]
300264 }
301- return CodexCLIOriginator , CodexCLIOriginator + rest , true
265+ return strings . TrimSpace ( rest )
302266}
303267
304268// codexEngineVersionPattern 提取版本段开头的三段数字 X.Y.Z(忽略 -alpha 等后缀)。
0 commit comments