Commit Graph
2 Commits
Author SHA1 Message Date
alfadb ca08825935 feat(claude): 自动同步 Claude Code 客户端版本号
出站伪装用的 Claude Code 版本号此前硬编码在 claude.CLICurrentVersion,
只能靠环境变量 SUB2API_CLAUDE_CLI_VERSION 覆盖且需重启;Anthropic 抬高
版本下限时(如 Opus 5.5 要求 >= 2.1.280)必须改代码发版才能跟上。

改为每小时从官方 GitHub Releases 同步最新稳定版,无需重启也无需发版。

- 新增 ClaudeCodeVersionSyncService:1 小时间隔,数据源 anthropics/claude-code
  的 releases/latest,失败回退最近 30 条列表;过滤 draft/prerelease;
  只向前推进,拉取失败保留上次值不降级;面板可关闭
- 新增三个设置项:claude_code_client_version(管理员手动固定)、
  claude_code_client_version_synced(同步服务独占写)、
  claude_code_version_auto_sync_enabled(开关,默认开)
- 取值优先级:面板手动值 -> 同步值 -> SUB2API_CLAUDE_CLI_VERSION -> 内置基线,
  每层经 IsSupportedCLIVersion 校验,含 60s 缓存与 singleflight
- 版本号运行期化:claude.DefaultHeaders 由包级 var 改为函数,新增
  EffectiveCLIVersion/DefaultUserAgent 与 SetCLIVersionResolver(未注入时
  与 CLIVersion() 等价);identity 的 defaultFingerprint 不再在包 init 固化,
  指纹下限与主版本超前校验改用运行期值
- 守住一致性铁律:buildUpstreamRequest 与 count_tokens 各取一次 UA 字符串,
  同时喂给出站 User-Agent 头与请求体 billing 的 cc_version,避免缓存翻转
  瞬间头体版本不一致被判非正版客户端
- 管理后台设置页新增配置区块,含中英文案与「当前同步到」只读展示
2026-09-23 13:35:44 +08:00
alfadb 3cb2381bdc fix(claude): 让伪装的 Claude Code 版本真正生效,修复 Fable 5.1 仍然 400
claude-fable-5-1 已登记进 DefaultModels,但 OAuth 账号实际调用仍然全部失败:

  400 claude_code_version_too_old
  "Claude Code 2.1.220 does not support this model;
   version 2.1.251 or newer is required."

两个独立的原因,缺一不可:

1. CLICurrentVersion 停留在 2.1.220,低于上游对该模型的客户端版本闸门。
   升到 2.1.258(当前官方 CLI 最新版,npm @anthropic-ai/claude-code)。

2. 只升常量对所有已有账号无效。上游请求实际发送的 User-Agent 来自账号级
   持久指纹(IdentityService,Redis fingerprint:<accountID>),不是
   DefaultHeaders;defaultFingerprint 只在首次创建指纹时使用。
   GetOrCreateFingerprint 的缓存命中路径只有两条改写分支:UA 畸形时自愈,
   或客户端送来更新版本时升级。指纹 7 天 TTL 但活跃账号每 24h 续期,实际
   近乎永不过期,系统内也没有重置入口——客户端版本比缓存旧时(例如缓存
   2.1.220、客户端 2.1.75),存量账号会永远对上游声称 2.1.220。

   生产实测:升级 CLICurrentVersion 并部署后仍报同样的 400,读 Redis 确认
   出问题账号的缓存值为 "claude-cli/2.1.220 (external, cli)"。

修复:新增 floorClaudeCLIUserAgentVersion,把 CLICurrentVersion 作为缓存
指纹里 claude-cli 版本的下限,缓存命中与首次创建两条路径共用。

  - 只对 claude-cli 产品生效,其它客户端一律不动
  - 只升不降:版本等于或高于下限(含客户端上报的更新版本)不做任何改动
  - 只替换版本号段,UA 其余部分原样保留,例如
    "(external, claude-desktop-3p, agent-sdk/0.3.100)" 不受影响
  - 与既有的"客户端更新版本"升级分支相互独立、二者取更新者
  - X-Stainless-* 维持既有 merge 语义,不被下限逻辑覆盖

cc_version 无需单独处理:billing attribution block 的版本已由
syncBillingHeaderVersion 跟随实际发送的 User-Agent 同步。

测试:identity_service_version_floor_test.go 覆盖 helper 的 8 种 UA 形态与
5 个 GetOrCreateFingerprint 场景,其中一个直接复现线上故障(缓存 2.1.220 +
客户端 2.1.75 → 写回 2.1.258,Stainless 字段保持不变)。
constants_cli_version_test.go 锁定常量与 DefaultHeaders 的 UA 同源,并断言
不低于 Fable 5.1 的闸门下限。回退实现后对应用例确认转红。

identity_service_user_agent_validation_test.go 中 3 个把版本钉死在旧字面量
的既有用例改为钉 CLICurrentVersion 或更高版本——它们断言的正是本次有意改变
的行为,各自的原始意图(哨兵拒绝、合法升级生效、健康缓存不重写)保持不变。
2026-09-07 16:34:35 +08:00