From 8eb2bbcb20b9aae2f48e4ed3062411e0a359fc7b Mon Sep 17 00:00:00 2001 From: win Date: Mon, 30 Mar 2026 19:04:52 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BB=8E=20main=20=E5=88=86=E6=94=AF?= =?UTF-8?q?=E8=BF=81=E7=A7=BB=20Claude=20=E6=8C=87=E7=BA=B9=E5=B8=B8?= =?UTF-8?q?=E9=87=8F=E5=92=8C=E5=AE=9E=E4=BE=8B=E7=BA=A7=E9=9A=94=E7=A6=BB?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 main 分支的 Claude/Anthropic 相关逆向工作迁移到 codex 分支: - claude/constants.go: 添加 4 个新 Beta 常量 + 版本升级至 2.1.84/0.74.0 - config.go: 添加 InstanceSalt 和 FingerprintDefaultsConfig 配置 - identity_service: 版本升级 + instanceSalt 支持 + ApplyDefaultFingerprintOverrides - wire_gen.go: 初始化指纹覆盖 + 使用 NewIdentityServiceWithSalt Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/cmd/server/wire_gen.go | 7 +++- backend/internal/config/config.go | 29 ++++++++++++++ backend/internal/pkg/claude/constants.go | 36 +++++++++++++++-- backend/internal/service/identity_service.go | 21 ++++++---- .../service/identity_service_antigravity.go | 39 +++++++++++++++++++ 5 files changed, 120 insertions(+), 12 deletions(-) create mode 100644 backend/internal/service/identity_service_antigravity.go diff --git a/backend/cmd/server/wire_gen.go b/backend/cmd/server/wire_gen.go index ce898a4a..d0dcacd2 100644 --- a/backend/cmd/server/wire_gen.go +++ b/backend/cmd/server/wire_gen.go @@ -12,6 +12,7 @@ import ( "github.com/Wei-Shaw/sub2api/internal/config" "github.com/Wei-Shaw/sub2api/internal/handler" "github.com/Wei-Shaw/sub2api/internal/handler/admin" + "github.com/Wei-Shaw/sub2api/internal/pkg/claude" "github.com/Wei-Shaw/sub2api/internal/repository" "github.com/Wei-Shaw/sub2api/internal/server" "github.com/Wei-Shaw/sub2api/internal/server/middleware" @@ -35,6 +36,10 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) { if err != nil { return nil, err } + // 应用实例级指纹覆盖(不同 sub2api 实例可设不同的默认版本号) + fpd := configConfig.Gateway.FingerprintDefaults + claude.ApplyFingerprintOverrides(fpd.ClaudeCLIVersion, fpd.StainlessPackageVersion, fpd.StainlessRuntimeVersion, fpd.StainlessOS, fpd.StainlessArch) + service.ApplyDefaultFingerprintOverrides(fpd.ClaudeCLIVersion, fpd.StainlessPackageVersion, fpd.StainlessRuntimeVersion, fpd.StainlessOS, fpd.StainlessArch) client, err := repository.ProvideEnt(configConfig) if err != nil { return nil, err @@ -171,7 +176,7 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) { return nil, err } billingService := service.NewBillingService(configConfig, pricingService) - identityService := service.NewIdentityService(identityCache) + identityService := service.NewIdentityServiceWithSalt(identityCache, configConfig.Gateway.InstanceSalt) deferredService := service.ProvideDeferredService(accountRepository, timingWheelService) claudeTokenProvider := service.ProvideClaudeTokenProvider(accountRepository, geminiTokenCache, oAuthService, oauthRefreshAPI) digestSessionStore := service.NewDigestSessionStore() diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go index 3ee5d6cd..7037cd60 100644 --- a/backend/internal/config/config.go +++ b/backend/internal/config/config.go @@ -456,6 +456,18 @@ type GatewayConfig struct { // TLSFingerprint: TLS指纹伪装配置 TLSFingerprint TLSFingerprintConfig `mapstructure:"tls_fingerprint"` + // InstanceSalt: 实例级隔离盐值 + // 用于 user_id 重写和 session hash 的种子混淆, + // 不同 sub2api 实例设置不同的 salt,确保相同输入产生不同输出。 + // 为空时使用默认行为(无 salt),建议生产环境必须配置。 + // 生成方法: openssl rand -hex 32 + InstanceSalt string `mapstructure:"instance_salt"` + + // FingerprintDefaults: 指纹默认值覆盖 + // 允许每个实例配置不同的 Claude CLI 版本号,与其他 sub2api 实例区分。 + // 为空时使用代码内置默认值。 + FingerprintDefaults FingerprintDefaultsConfig `mapstructure:"fingerprint_defaults"` + // UsageRecord: 使用量记录异步队列配置(有界队列 + 固定 worker) UsageRecord GatewayUsageRecordConfig `mapstructure:"usage_record"` @@ -685,6 +697,23 @@ type TLSProfileConfig struct { Extensions []uint16 `mapstructure:"extensions"` } +// FingerprintDefaultsConfig 指纹默认值配置 +// 允许每个 sub2api 实例设置不同的默认指纹值,与其他实例区分。 +// 所有字段为空时使用代码内置默认值。 +type FingerprintDefaultsConfig struct { + // ClaudeCLIVersion: Claude CLI 版本号(如 "2.1.81"), + // 最终 User-Agent 为 "claude-cli/{version} (external, cli)" + ClaudeCLIVersion string `mapstructure:"claude_cli_version"` + // StainlessPackageVersion: @anthropic-ai/sdk 版本(如 "0.80.0") + StainlessPackageVersion string `mapstructure:"stainless_package_version"` + // StainlessRuntimeVersion: Node.js 版本(如 "v24.13.0") + StainlessRuntimeVersion string `mapstructure:"stainless_runtime_version"` + // StainlessOS: 操作系统(如 "Linux", "Darwin") + StainlessOS string `mapstructure:"stainless_os"` + // StainlessArch: 架构(如 "arm64", "x64") + StainlessArch string `mapstructure:"stainless_arch"` +} + // GatewaySchedulingConfig accounts scheduling configuration. type GatewaySchedulingConfig struct { // 粘性会话排队配置 diff --git a/backend/internal/pkg/claude/constants.go b/backend/internal/pkg/claude/constants.go index dfca252f..c9c015bb 100644 --- a/backend/internal/pkg/claude/constants.go +++ b/backend/internal/pkg/claude/constants.go @@ -12,6 +12,10 @@ const ( BetaTokenCounting = "token-counting-2024-11-01" BetaContext1M = "context-1m-2025-08-07" BetaFastMode = "fast-mode-2026-02-01" + BetaRedactThinking = "redact-thinking-2026-02-12" + BetaContextManagement = "context-management-2025-06-27" + BetaPromptCachingScope = "prompt-caching-scope-2026-01-05" + BetaEffort = "effort-2025-11-24" ) // DroppedBetas 是转发时需要从 anthropic-beta header 中移除的 beta token 列表。 @@ -48,19 +52,43 @@ const APIKeyHaikuBetaHeader = BetaInterleavedThinking var DefaultHeaders = map[string]string{ // Keep these in sync with recent Claude CLI traffic to reduce the chance // that Claude Code-scoped OAuth credentials are rejected as "non-CLI" usage. - "User-Agent": "claude-cli/2.1.22 (external, cli)", + "User-Agent": "claude-cli/2.1.84 (external, cli)", "X-Stainless-Lang": "js", - "X-Stainless-Package-Version": "0.70.0", - "X-Stainless-OS": "Linux", + "X-Stainless-Package-Version": "0.74.0", + "X-Stainless-OS": "MacOS", "X-Stainless-Arch": "arm64", "X-Stainless-Runtime": "node", - "X-Stainless-Runtime-Version": "v24.13.0", + "X-Stainless-Runtime-Version": "v24.3.0", "X-Stainless-Retry-Count": "0", "X-Stainless-Timeout": "600", "X-App": "cli", "Anthropic-Dangerous-Direct-Browser-Access": "true", } +// ApplyFingerprintOverrides 用配置覆盖默认指纹值(每个实例可设不同值) +// cliVersion: Claude CLI 版本(如 "2.1.81") +// pkgVersion: SDK 版本(如 "0.80.0") +// runtimeVersion: Node.js 版本(如 "v24.13.0") +// os_: 操作系统(如 "Linux") +// arch: 架构(如 "arm64") +func ApplyFingerprintOverrides(cliVersion, pkgVersion, runtimeVersion, os_, arch string) { + if cliVersion != "" { + DefaultHeaders["User-Agent"] = "claude-cli/" + cliVersion + " (external, cli)" + } + if pkgVersion != "" { + DefaultHeaders["X-Stainless-Package-Version"] = pkgVersion + } + if runtimeVersion != "" { + DefaultHeaders["X-Stainless-Runtime-Version"] = runtimeVersion + } + if os_ != "" { + DefaultHeaders["X-Stainless-OS"] = os_ + } + if arch != "" { + DefaultHeaders["X-Stainless-Arch"] = arch + } +} + // Model 表示一个 Claude 模型 type Model struct { ID string `json:"id"` diff --git a/backend/internal/service/identity_service.go b/backend/internal/service/identity_service.go index 3d706508..c6a260a8 100644 --- a/backend/internal/service/identity_service.go +++ b/backend/internal/service/identity_service.go @@ -26,13 +26,13 @@ var ( // 默认指纹值(当客户端未提供时使用) var defaultFingerprint = Fingerprint{ - UserAgent: "claude-cli/2.1.22 (external, cli)", + UserAgent: "claude-cli/2.1.84 (external, cli)", StainlessLang: "js", - StainlessPackageVersion: "0.70.0", - StainlessOS: "Linux", + StainlessPackageVersion: "0.74.0", + StainlessOS: "MacOS", StainlessArch: "arm64", StainlessRuntime: "node", - StainlessRuntimeVersion: "v24.13.0", + StainlessRuntimeVersion: "v24.3.0", } // Fingerprint represents account fingerprint data @@ -63,7 +63,8 @@ type IdentityCache interface { // IdentityService 管理OAuth账号的请求身份指纹 type IdentityService struct { - cache IdentityCache + cache IdentityCache + instanceSalt string // 实例级隔离盐值,不同 sub2api 实例产生不同的 hash 输出 } // NewIdentityService 创建新的IdentityService @@ -242,8 +243,14 @@ func (s *IdentityService) RewriteUserID(body []byte, accountID int64, accountUUI sessionTail := parsed.SessionID // 原始session UUID - // 生成新的session hash: SHA256(accountID::sessionTail) -> UUID格式 - seed := fmt.Sprintf("%d::%s", accountID, sessionTail) + // 生成新的session hash: SHA256(salt::accountID::sessionTail) -> UUID格式 + // instanceSalt 使不同 sub2api 实例对相同输入产生不同的 hash + var seed string + if s.instanceSalt != "" { + seed = fmt.Sprintf("%s::%d::%s", s.instanceSalt, accountID, sessionTail) + } else { + seed = fmt.Sprintf("%d::%s", accountID, sessionTail) + } newSessionHash := generateUUIDFromSeed(seed) // 根据客户端版本选择输出格式 diff --git a/backend/internal/service/identity_service_antigravity.go b/backend/internal/service/identity_service_antigravity.go new file mode 100644 index 00000000..e725a7fb --- /dev/null +++ b/backend/internal/service/identity_service_antigravity.go @@ -0,0 +1,39 @@ +package service + +// ============================================================== +// antigravity — identity_service 扩展 +// +// 此文件包含 Antigravity fork 对 IdentityService 的扩展, +// 新增了实例级隔离盐值和指纹默认值覆盖功能。 +// +// 对上游文件 identity_service.go 的最小化改动: +// - defaultFingerprint 版本号更新 +// - IdentityService struct 新增 instanceSalt 字段 +// ============================================================== + +// ApplyDefaultFingerprintOverrides 用配置覆盖 identity_service 的默认指纹 +// 允许不同部署实例设置不同的 CLI/SDK 版本号,避免所有实例指纹相同 +func ApplyDefaultFingerprintOverrides(cliVersion, pkgVersion, runtimeVersion, os_, arch string) { + if cliVersion != "" { + defaultFingerprint.UserAgent = "claude-cli/" + cliVersion + " (external, cli)" + } + if pkgVersion != "" { + defaultFingerprint.StainlessPackageVersion = pkgVersion + } + if runtimeVersion != "" { + defaultFingerprint.StainlessRuntimeVersion = runtimeVersion + } + if os_ != "" { + defaultFingerprint.StainlessOS = os_ + } + if arch != "" { + defaultFingerprint.StainlessArch = arch + } +} + +// NewIdentityServiceWithSalt 创建带实例盐值的 IdentityService +// 实例盐值用于 user_id 重写时的 session hash 混淆, +// 使不同 sub2api 实例对相同输入产生不同的 hash 输出,增加隔离性 +func NewIdentityServiceWithSalt(cache IdentityCache, salt string) *IdentityService { + return &IdentityService{cache: cache, instanceSalt: salt} +}