Commit Graph
5540 Commits
Author SHA1 Message Date
Wesley Liddick dce0f6b4e3 Merge pull request #5337 from wucm667/fix/issue-5331-oauth-count-tokens-fallback
fix: avoid cooling OAuth accounts for HTML count token errors
2026-08-07 16:14:48 +08:00
Wesley Liddick 22ef761c27 Merge pull request #5309 from wucm667/fix/issue-5308-antigravity-gemini36
fix(antigravity): support Gemini 3.6 Flash models
2026-08-07 16:14:37 +08:00
Wesley Liddick 865002fd2c Merge pull request #5317 from wucm667/fix/issue-5314-grok-video-task-id
fix(grok): bind video owners for task_id receipts
2026-08-07 16:14:25 +08:00
Wesley Liddick 93367b6db4 Merge pull request #5351 from Wei-Shaw/fix/codex-tui-default-identity
fix(openai): default OAuth identity to codex-tui
2026-08-07 10:17:00 +08:00
shaw b6e53c9320 fix(frontend): align Codex UA setting copy 2026-08-07 10:06:28 +08:00
shaw dbb42881c0 fix(openai): default OAuth identity to codex-tui 2026-08-07 09:56:03 +08:00
Wesley Liddick e4b0e1b66b Merge pull request #5338 from Wei-Shaw/fix/tencent-captcha-region-csp
修复腾讯验证码区域适配、重置与 CSP 加载
2026-08-07 08:37:50 +08:00
shaw 287a9f386b fix: 修复腾讯验证码票据过期与区域切换 2026-08-06 21:27:06 +08:00
shaw 8e102b3a0f fix: 完善腾讯验证码区域适配与 CSP 白名单
修复国内站和国际站 SDK 构造、验证容器、票据重置及动态资源加载问题,并补充认证流程回归测试。
2026-08-06 20:34:37 +08:00
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
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 ce14983133 fix(antigravity): support Gemini 3.6 Flash models 2026-08-05 22:35:01 +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
shentryandClaude 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
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
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
shaw 54a2bcfd15 fix(openai): harden reset-credit refresh and account recovery
Review follow-ups on the reset-credit caching flow:

- Recover account state BEFORE (and independently of) the reset-credit
  display cache. A failed cache refresh could previously abort the run and
  leave the account rate-limited — the very reason the credit was spent
  (#3672 / #3740). The recovered account row is now returned even when the
  cache refresh fails.
- Run the post-reset bookkeeping on a detached, time-boxed context and give
  the panel reset call a larger timeout. A client abort no longer strands a
  consumed (non-refundable) credit with an unrecovered account, and the
  chained upstream calls can no longer exceed the client timeout and invite a
  retry that spends a second credit.
- Persist the reset-credit snapshot through POST /accounts/:id/quota/refresh
  instead of a side-effecting GET flag, so the write is covered by the audit
  middleware. A rejected snapshot write now degrades to cache_persisted=false
  instead of turning a successful upstream read into a 502 that left the card
  without a credit count and the reset button permanently disabled.
- Reject snapshots whose positive count carries no expiration entries, and
  drop expired credits (clamping the count) when rehydrating, so a stale
  cache can no longer light up the reset button.
- Keep nil quota / rate-limit services nil in the handler's interface fields;
  storing a nil *Service made the "not enabled" guards non-nil.
- Time-box the usage-refresh suppression and reuse handleAccountUpdated so the
  patched row also enters the auto-refresh silent window.
2026-08-03 14:40:55 +08:00
Wesley Liddick 27e8f69a9e Merge pull request #5171 from heathermhuang/codex/composite-reasoning-policy
feat(composite): enforce reasoning effort policy
2026-08-03 11:28:38 +08:00