wucm667
e93f6b9953
fix: avoid cooling OAuth accounts for HTML count token errors
2026-08-06 20:31:56 +08:00
shaw
a19c9f8d8a
chore: update sponsors
2026-08-06 19:31:17 +08:00
shaw
a1936d42db
chore: update sponsors
2026-08-06 19:30:44 +08:00
li
fe2c265c91
fix(identity): validate user-agent before persisting account fingerprint
...
IdentityService 把客户端 User-Agent 作为账号级持久身份缓存(Redis
fingerprint:<accountID>),覆盖规则是"只升不降"的纯数值版本比较,且对 UA 内容
不做任何合法性校验:
- userAgentVersionRegex 不锚定,能从 claude-cli/999.0.0-local 解析出 999.0.0
- isNewerVersion 只比数值 + 产品名,999.0.0 恒大于任何真实版本
- fingerprintTTL 7 天配合每 24 小时懒续期,活跃账号上的指纹近乎永不过期
- IdentityCache 只有 Get/Set,管理端也没有重置入口
一个本地构建客户端发出 claude-cli/999.0.0-local 即可把账号身份永久钉死。此后
该账号所有上游请求在 HTTP 头与请求体 cc_version 两处都声称这个不存在的版本,
被上游判为非正版客户端并返回不带限流重置头的 429;无重置头又落到 5 秒兜底冷却,
账号池收缩后对外表现为 503 风暴。一条 failover 请求即可连带毒化多个账号——
指纹写入发生在上游调用之前,三次上游尝试全失败也照样毒化。
改动:
- 新增 isAcceptableFingerprintUserAgent,校验 <product>/<major>.<minor>.<patch>
后必须紧跟空白或结束(拒绝 -local/-dev/+build 等本地构建后缀),并对 claude-cli
的主版本号相对 claude.CLICurrentVersion 设上限(挡掉 999 这类哨兵版本)。
非 claude-cli 产品只校验形态,避免误伤其他合法客户端。
- 创建与升级两条路径共用该校验。只在 isNewerVersion 处加校验不够:
createFingerprintFromHeaders 首次创建时同样会原样保存畸形 UA,删键恢复后
账号可被同一客户端立即再次毒化。
- 读取时自愈:缓存中已是畸形 UA 时(本次加固之前写入的),用本次请求的合法 UA
或默认指纹替换并回写。否则存量已中招的账号仍然只能靠手工删 Redis 键恢复。
- 仅在真正阻止了一次写入时记录被拒 UA,便于定位污染源,同时避免被毒化客户端的
高频重试刷屏。
Fixes #5254
2026-08-06 15:01:28 +08:00
shaw
c123caddd4
chore: update sponsors
2026-08-06 14:22:46 +08:00
Wesley Liddick
e08aee49ed
Merge pull request #5266 from shentry/fix/transient-streak-rate-dependence
...
fix(openai): keep transient failure streak from resetting on sparse traffic
2026-08-06 14:11:49 +08:00
Wesley Liddick
c9e60d1f26
Merge pull request #5031 from keaipiao/fix/easypay-error-utf8
...
fix(payment): preserve UTF-8 in EasyPay errors
2026-08-06 14:10:41 +08:00
Wesley Liddick
47c03c75d8
Merge pull request #5232 from fengshao1227/fix/billing-quantize-monetary-scale
...
fix(billing): quantize usage billing amounts to the NUMERIC(20,8) scale
2026-08-06 14:00:04 +08:00
wucm667
5b7a64334f
fix(grok): support task IDs for video owner binding
2026-08-06 04:30:10 +08:00
wucm667
df249ee5d5
fix(scheduler): diagnose legacy OpenAI exclusions
2026-08-06 02:54:16 +08:00
wucm667
82ccc187cd
fix(ops): preserve custom range in error lists
2026-08-06 02:26:42 +08:00
wucm667
ce14983133
fix(antigravity): support Gemini 3.6 Flash models
2026-08-05 22:35:01 +08:00
必通
fc5a1b78d2
fix(ws): allow Codex prewarm continuation
2026-08-05 21:26:37 +08:00
wucm667
8aa425d22f
fix(apicompat): accept chat reasoning alias
2026-08-05 18:25:58 +08:00
Rain
ba92d70422
fix grok usage guard for compatible accounts
2026-08-05 17:59:20 +08:00
wucm667
2f109e74ca
fix: emit response.failed when compact keepalive commits headers but no SSE payload
...
When OpenAI Responses streaming has compact keepalive commit 200 headers
but no semantic SSE event has been written, upstream 4xx errors were
silently swallowed, causing clients to hang until timeout.
The fix distinguishes between 'headers committed' and 'meaningful SSE
output written'. Compact keepalive may commit headers for heartbeat
purposes without writing actual SSE events. In that case, the Responses
stream still needs its protocol-correct terminal response.failed event.
Preserves existing behavior when a complete upstream response or
meaningful SSE payload was already communicated, and preserves image
keepalive semantics.
Fixes #5289
2026-08-05 16:28:16 +08:00
Rain
5c52fa93d5
fix grok missing usage billing bypass
2026-08-05 09:49:25 +08:00
shaw
00b8596176
chore: update sponsors
2026-08-04 21:55:34 +08:00
shaw
c5e046b7d7
chore: update sponsors
2026-08-04 21:54:50 +08:00
github-actions[bot]
aac53afe0e
chore: sync VERSION to 0.1.171 [skip ci]
2026-08-04 13:41:47 +00:00
Wesley Liddick
f0e7a9c7a2
Merge pull request #5223 from feeeei/main
...
feat(aliyun-captcha): 人机验证增加阿里云验证码 2.0
v0.1.171
2026-08-04 21:16:43 +08:00
feeeei
26e0a89323
人机验证增加阿里云验证码 2.0
...
沿用腾讯天御验证码引入的多服务商模型:aliyun_captcha_enabled 作为独立
开关,与 Cloudflare Turnstile、腾讯天御三方互斥(保存校验 + 运行时
CAPTCHA_PROVIDER_CONFLICT)。后台「安全与认证」合并为单张人机验证卡片:
总开关 + 服务商单选(Turnstile / 腾讯天御 / 阿里云),选中即启用该家并
关闭其它,落库仍是三个独立开关键,由前端映射保证互斥。
阿里云侧同时支持 aliyun 中国站与国际站(alibabacloud.com):两站前端脚本、
region 取值与服务端 API 完全一致,仅账号与 AccessKey 相互独立,因此由
「服务地域」决定线路即可——中国内地走 captcha.cn-shanghai.aliyuncs.com,
非中国内地(新加坡)走 captcha.ap-southeast-1.aliyuncs.com,AccessKey
取自持有该实例的账号,无需在配置中区分站点。
- AliyunCaptchaService 对称 TencentCaptchaService:服务端校验走官方 SDK
VerifyIntelligentCaptcha,调用异常按 fail-closed 拦截,与 Turnstile
网络错误行为对称;保存设置时真实探测 AK/SK 有效性
- 保护面对齐腾讯扩展入口:VerifyTencentCaptchaIfEnabled 通用化为
VerifyActionCaptchaIfEnabled,OAuth 登录启动、passkey 登录在阿里云
启用时同样拦截;Turnstile 维持既有覆盖不扩大
- 前端 AliyunCaptchaWidget 为表单内预验证按钮(popup 模式),同时暴露
verify() 供 OAuth 启动、passkey 等动作入口程序化弹窗;未预验证直接
提交时弹窗兜底。SDK 按钮绑定异步完成,弹窗未出现前按 tick 重试触发,
并轮询弹窗可见性识别用户关闭
- captchaVerifyParam 复用 turnstile_token 请求字段提交;公开设置下发
aliyun_captcha_enabled / scene_id / prefix / region
- CSP 放行验证码 CDN:script-src/style-src 加 *.alicdn.com
2026-08-04 20:57:15 +08:00
Wesley Liddick
d431c57f2e
Merge pull request #5268 from Wei-Shaw/fix/pending-oauth-captcha-submit-gate
...
fix(auth-ui): 第三方 OAuth 建号提交前必须持有验证码票据
2026-08-04 20:43:01 +08:00
shaw
635a27189f
fix(auth-ui): gate pending OAuth account creation on a captcha proof
...
#5261 added a captcha check to POST /auth/oauth/pending/create-account, but
the shared create-account form only gates its send-code button on the
Turnstile token — the submit button's disabled condition never included it.
Turnstile tokens are single-use, so handleSendCode resets the widget in its
finally block and clears the token. Submitting inside the window before the
widget re-solves omits turnstile_token from the payload, and the backend
answers ErrTurnstileVerificationFailed (turnstile_service.go:65). With an
interaction-required challenge the widget does not re-solve on its own, so
the failure persists until the user notices and verifies again — while the
button stays enabled and says nothing.
Gate the submit button on the token, mirroring the send-code button and
EmailVerifyView, which already gates its pending-OAuth submit the same way.
handleSubmit repeats the check because implicit form submission (Enter in a
text input) bypasses the button's disabled state.
The Tencent path is unaffected: handleSubmit already acquires a fresh proof
per submit, and turnstile_enabled is false in that configuration.
Verified with the component spec (11 passed) and vue-tsc --noEmit (clean).
2026-08-04 20:29:53 +08:00
wucm667
d3b5703b55
fix(model-plaza): show composite group models
2026-08-04 19:14:28 +08:00
shentry and Claude Opus 5
7d38e67120
fix(openai): keep transient failure streak from resetting on sparse traffic
...
The account+model transient breaker reset its failure streak whenever the
gap since the previous failure exceeded a one-minute window. That made the
breaker's sensitivity a function of request rate rather than upstream
health: a gateway called less often than once a minute never advanced past
streak 1, where the cooldown is zero, so a consistently broken account was
never blocked. Every request re-selected it, paid a full upstream attempt,
and only then failed over to a healthy account.
Observed on a low-traffic deployment: two accounts returning 500 and 503
stayed in rotation indefinitely, logging `failure_streak: 1, cooldown_ms: 0`
on every request and adding ~750ms to each one before a working account was
reached.
The streak is already cleared on success — recordSuccess deletes the entry,
and every OpenAI handler reports the schedule result — so the time-based
reset is not needed to recover a healthy account. Keep a TTL purely to bound
the map for account+model pairs that stopped being used, and raise it well
above the cooldowns so it no longer doubles as a streak reset.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com >
2026-08-04 17:59:16 +08:00
Wesley Liddick
8b3fe664dc
Merge pull request #5261 from lyen1688/feat/tencent-captcha-gate
...
新增腾讯天御验证码认证门禁
2026-08-04 16:39:55 +08:00
Wesley Liddick
a4d263f62f
Merge pull request #5224 from wucm667/fix/issue-5190-lock-subscription-renewal
...
fix(subscription): serialize concurrent renewals
2026-08-04 16:28:52 +08:00
Wesley Liddick
ae81dfd933
Merge pull request #5177 from r266-tech/fix-nonpassthrough-write-context
...
fix(openai-ws): preserve terminal event on lease loss
2026-08-04 16:28:22 +08:00
Wesley Liddick
35cab3c814
Merge pull request #5258 from feeeei/fix/model_plaza
...
fix(model-plaza): Model Plaza image model price display is inconsistent with the actual price
2026-08-04 16:27:53 +08:00
Wesley Liddick
770e35b474
Merge pull request #5040 from coo1white/upstream-pr/grok-cli-0.2.114
...
fix(grok): bump pinned Grok CLI version to 0.2.114
2026-08-04 16:16:20 +08:00
Wesley Liddick
846dd310a3
Merge pull request #5158 from neboyang/feat/claude-oauth-authorize-url
...
fix(oauth): use claude.com/cai authorize endpoint
2026-08-04 16:15:50 +08:00
Wesley Liddick
9b4575e434
Merge pull request #5243 from wucm667/feat/issue-5240-dashboard-username
...
fix(dashboard): show usernames in spending ranking
2026-08-04 16:15:21 +08:00
Wesley Liddick
1f4cfc44c1
Merge pull request #5226 from spongehah/codex/fix-prompt-audit-output-text
...
fix(prompt-audit): parse responses output text
2026-08-04 16:15:04 +08:00
lyen1688
e592c5f9e0
新增腾讯天御验证码认证门禁
2026-08-04 15:09:29 +08:00
Wesley Liddick
9fd7e76231
Merge pull request #5233 from Wei-Shaw/fix/codex-identity-enforcement
...
fix(codex): 强制统一出站身份并让客户端版本号跟随官方发布
2026-08-04 14:50:44 +08:00
feeeei
785b61d424
修复模型广场图片模型价格展示与实收口径不一致
...
图片计费模型的广场展示价按实收口径计算:档位单价取
分组图片价 > 渠道档位价 > 渠道默认按次价;分组开启生图
独立倍率时实付倍率取独立倍率,不取分组/专属倍率。
2026-08-04 13:48:19 +08:00
shaw
2d3e845205
perf(codex): 版本同步主路径改用 /releases/latest 并保留列表回退
...
自动同步原本每次拉 `/releases?per_page=30`,实测响应 10,017,686 字节——该仓库
预发布极密集,30 条里只有 2 条稳定版(0.145.0 已排到第 26 位),页大小是为了
「窗口里至少有一条稳定版」而定的,不能靠调小来省流量。
改为主路径走 `/releases/latest`(实测约 0.3MB):该端点本身排除 draft 与
prerelease,直接给出最新正式发布,不受预发布密度影响。复用端口上已有的
FetchLatestRelease,不新增任何 HTTP 代码,代理配置、User-Agent、可选 token
与跨主机重定向剥离 Authorization 的行为全部沿用。
保留列表扫描作为回退:latest 是跨 tag 家族按 published_at 取的,若同仓库其他
组件(如 rusty-v8-*)某天发布正式 release 而成为 latest,主路径会被 rust-v
前缀过滤挡掉,此时必须扫一页才能继续跟随,否则版本号会静默停更。
两条路径共用 latestCodexStableReleaseVersion 的同一套过滤(前缀 / draft /
prerelease / 版本号形态),语义不会分叉;单条 latest 也要过版本号校验——仓库里
确实存在 rust-vv0.99.0-alpha.8 这类畸形 tag。只向前推进、抓取失败保持既有值
两条不变式未改动。
测试覆盖:主路径命中时不再拉列表页、四种拿不到(异家族 / 预发布 / 抓取失败 /
空对象)都回退、畸形 tag 被主路径拒绝后由回退兜底、两路径皆失败时保值。
2026-08-04 10:51:04 +08:00
wucm667
80af44cad7
[verified] fix(dashboard): show usernames in spending ranking
2026-08-04 00:43:05 +08:00
shaw
c899c8cf37
fix(codex): 管理员配置的 UA 只贡献指纹,版本段一律用生效版本重建
...
面板「OpenAI Codex UA」此前只在客户端 UA 是浏览器型时才生效,本分支把它提升为
所有 OAuth 出站的规范身份来源,作用域扩大了一个数量级。而它的旧 placeholder 原文
就是 codex_cli_rs/0.144.1 (...):照抄填写过的存量部署会被永久钉在 0.144.1 上——
该值不低于上游门槛 0.144.0,version 头也随之变成 0.144.1,绕过版本自动同步,
稳定落在上游优先降载的那一侧,且面板、日志、审计都不提示。账号级自定义 UA
(credentials.user_agent)有完全相同的陷阱。
改为:管理员配置的 UA 只贡献客户端名与 OS / 架构 / 终端指纹(这是该输入框唯一
不可替代的价值),版本段一律用当前生效版本重建。存量的陈旧配置无需迁移即自愈,
UA 与 version 头从此由构造保证同源,原先「覆写 UA 陈旧时二者不一致」的次优解消失。
- 新增 openai.SetCodexUserAgentVersion:重建首段版本声明,OS / 架构 / 终端指纹
原样保留;尾部官方客户端标识组 (name; version) 与首段同源,一并更新,避免拼出
首段声明新版本、尾部仍是旧版本的自相矛盾身份;非官方括号组(如 OS 组)不动。
- resolveCodexOutboundIdentity 收敛为单一funnel:生效版本只从规范身份取,
候选 UA 的版本段不再参与判定。
- GetOpenAICodexCanonicalUserAgent 重建面板 UA 的版本段;原「面板值等于兜底常量
视同未填」的特例随之成为恒等变换,删除。
- 补齐 4 处静默丢弃账号级自定义 UA 的出站路径(WS 握手、账号测试 x2、用量探针):
它们先写 customUA 再调不带 override 的收口,赋值随即被覆盖,既是行为不一致
也是死代码;账号测试尤其要紧——注释写着「与真实转发一致」而实际不一致。
- 补测试:SetCodexUserAgentVersion / CodexUserAgentVersion 包内用例、陈旧面板 UA
与账号 UA 的版本重建回归、WS 握手尊重账号级 UA。
2026-08-03 21:50:49 +08:00
zhiyu
4c4ff36380
perf(codex): 版本同步间隔改为 6 小时并加启动防抖
...
- 同步间隔 3h → 6h:客户端版本是天级变化,6 小时足够跟上,
对 GitHub 的调用降到每天 4 次。
- 新增启动防抖:借同步设置行自身的 UpdatedAt 判断,若同步值仍在一个周期内
则跳过启动同步。频繁重启、滚动发布或崩溃重启原本会把「启动即同步」
放大成对 GitHub 的连续请求;首次部署尚无同步值时不受影响。
- 补测试:启动防抖两个方向,以及版本比较按段取数字的回归
(字典序会把 0.99.0 判为大于 0.146.0,同时让「取最大值」与
「只向前推进」两处判错)。
2026-08-03 20:55:37 +08:00
wucm667
c418fd522f
[verified] fix(security-audit): deduplicate websocket turn audits
2026-08-03 20:35:56 +08:00
zhiyu
1e08c4c56f
test(server): 补齐 settings 契约用例的 Codex 版本号字段
...
新增的三个设置键会出现在 GET /api/v1/admin/settings 响应里,
契约用例的期望 JSON 需同步,否则 -tags=unit 下断言失败。
2026-08-03 20:26:50 +08:00
zhiyu
2eb24814fe
fix(codex): 强制统一出站身份并让客户端版本号跟随官方发布
...
上游 /backend-api/codex 在容量紧张时按客户端身份分优先级降载,被降载的请求
HTTP 200 后立刻推流内 server_is_overloaded。此前网关对配不出官方身份的客户端
整体回退到硬编码的 codex_cli_rs/0.144.1(落后官方 4 个发布),这些请求稳定
落在被优先丢弃的一侧。
- 强制统一出口:所有 OAuth 出站的 User-Agent / originator / version 一律改写
为网关规范身份,客户端自报身份不参与构造;HTTP / 透传 / WS / alpha-search /
探针全覆盖。compat 桥接故意删除 originator 的路径保持 no-op。
- 版本号收敛为单一来源,运行时优先级为面板覆写 → 自动同步值 → 内置常量;
UA 与 version 头同源派生,不再各自硬编码。
- 新增 3 小时自动同步官方客户端最新稳定版,面板可关闭,无需为跟版本而发版。
- 流内 server_is_overloaded / slow_down 改为先在同账号有界重试再切号,并标记为
请求级瞬时故障,不再据此临时封禁账号。
- 移除被取代的降载身份黑名单、浏览器 UA 兜底及其辅助函数。
2026-08-03 20:14:58 +08:00
li
e2652eb853
fix(billing): quantize usage billing amounts to the NUMERIC(20,8) scale
...
同一笔 ActualCost 会被分别写入两条方向相反的 SQL:
balance = balance - $1 -- 存剩余额度,舍入的是"减法结果"
quota_used = quota_used + $1 -- 存累计用量,舍入的是"加法结果"
两列都是 NUMERIC(20,8),PostgreSQL 按 half-away-from-zero 舍入运算结果。
金额在第 9 位落到 half 边界时,两侧朝相反方向舍入:
10 输入 token × 0.00000125 + 5 输出 token × 0.00001000 = 0.0000625
× 1.25(分组倍率) = 0.000078125
balance: 10000 - 0.000078125 = 9999.999921875 → 9999.99992188 delta 0.00007812
quota_used: 0 + 0.000078125 = 0.000078125 → 0.00007813 delta 0.00007813
余额少扣、API Key 配额多记,单次相差 1e-8 且随请求量线性累积(1000 次 ≈ 1e-5 USD),
余额、Key 配额与用量记录无法精确对账,只能靠 epsilon 比较勉强吻合。
修复:在参数进入 SQL 之前,把命令中的全部金额统一量化到 8 位小数
(half-away-from-zero,与 PostgreSQL NUMERIC 一致)。两条语句拿到的是
同一个已落在 8 位刻度上的金额,存储阶段不再发生舍入,delta 精确相等。
- 走 shopspring/decimal 而非 math.Round(v*1e8)/1e8:后者在乘除中引入额外
二进制误差,边界值可能被推到错误的一侧
- 量化排在指纹计算之后:指纹是请求幂等键,保持由原始金额派生,
避免升级前后同一 request_id 的重试算出不同指纹而被判为 fingerprint conflict
Fixes #5229
2026-08-03 20:00:32 +08:00
li
66ad405dd6
fix(upstream): set explicit TCP dial timeout on upstream transports
...
http.Transport 未设置 DialContext 时使用零值 net.Dialer(Timeout=0),
DNS 解析与 TCP 握手没有任何上限,只能等内核 TCP 重传耗尽(Linux 约 130 秒)。
ResponseHeaderTimeout 只约束连接建立之后等待响应头的阶段,覆盖不到建连。
上游域名被解析到 443 不可达的 IP 时(DNS 污染 / 路由异常),单个账号就要卡满
内核超时;而多账号故障转移是串行的,一次请求会阻塞数分钟且不写中间错误,
客户端表现为"长时间无首字后直接结束"。
- buildUpstreamTransport 显式设置 DialContext(10s)与 TLSHandshakeTimeout(10s)
- proxyutil SOCKS5 分支的 forward dialer 由 proxy.Direct(零值 dialer,同样无超时)
换成带 10s 超时的 net.Dialer;该分支会覆盖 Transport.DialContext,
调用方设置的建连超时对它无效,必须在此补上
Fixes #5151
2026-08-03 19:59:58 +08:00
spongehah
1b04e03cc4
fix(prompt-audit): parse responses output text
2026-08-03 17:38:04 +08:00
wucm667
2be047d1cd
[verified] test(subscription): update unit repository stubs
2026-08-03 17:22:18 +08:00
wucm667
db725a775a
[verified] fix(subscription): serialize concurrent renewals
2026-08-03 16:59:05 +08:00
Wesley Liddick
825ca7b1fc
Merge pull request #5183 from rick147/codex/feat-openai-reset-credit-cache
...
feat(openai): refresh reset credit state after quota reset
2026-08-03 16:01:10 +08:00