chore(claude): bump CLI fingerprint to 2.1.88 and accept claude-code/ UA
- Centralize Claude CLI fingerprint constants (UA, x-stainless-*) in pkg/claude with BuildCLI/CodeUserAgent helpers - Reuse constants in DefaultHeaders, identity_service defaults, and antigravity identity defaults to keep all callers in sync - Extend ClaudeCodeValidator to accept both claude-cli/ and claude-code/ UA prefixes (transport/helper requests use the latter) - Update related tests to cover the new UA prefix and version
This commit is contained in:
parent
6620b56b5a
commit
d6df41feaa
@ -1,8 +1,24 @@
|
|||||||
// Package claude provides constants and helpers for Claude API integration.
|
// Package claude provides constants and helpers for Claude API integration.
|
||||||
package claude
|
package claude
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
// Claude Code 客户端相关常量
|
// Claude Code 客户端相关常量
|
||||||
|
|
||||||
|
const (
|
||||||
|
DefaultCLIProductVersion = "2.1.88"
|
||||||
|
DefaultUserType = "external"
|
||||||
|
DefaultEntrypoint = "cli"
|
||||||
|
DefaultStainlessLang = "js"
|
||||||
|
DefaultStainlessPackageVersion = "0.74.0"
|
||||||
|
DefaultStainlessOS = "MacOS"
|
||||||
|
DefaultStainlessArch = "arm64"
|
||||||
|
DefaultStainlessRuntime = "node"
|
||||||
|
DefaultStainlessRuntimeVersion = "v24.3.0"
|
||||||
|
DefaultCLIUserAgent = "claude-cli/" + DefaultCLIProductVersion + " (" + DefaultUserType + ", " + DefaultEntrypoint + ")"
|
||||||
|
DefaultCodeUserAgent = "claude-code/" + DefaultCLIProductVersion
|
||||||
|
)
|
||||||
|
|
||||||
// Beta header 常量
|
// Beta header 常量
|
||||||
const (
|
const (
|
||||||
BetaOAuth = "oauth-2025-04-20"
|
BetaOAuth = "oauth-2025-04-20"
|
||||||
@ -52,19 +68,45 @@ const APIKeyHaikuBetaHeader = BetaInterleavedThinking
|
|||||||
var DefaultHeaders = map[string]string{
|
var DefaultHeaders = map[string]string{
|
||||||
// Keep these in sync with recent Claude CLI traffic to reduce the chance
|
// 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.
|
// that Claude Code-scoped OAuth credentials are rejected as "non-CLI" usage.
|
||||||
"User-Agent": "claude-cli/2.1.84 (external, cli)",
|
"User-Agent": DefaultCLIUserAgent,
|
||||||
"X-Stainless-Lang": "js",
|
"X-Stainless-Lang": DefaultStainlessLang,
|
||||||
"X-Stainless-Package-Version": "0.74.0",
|
"X-Stainless-Package-Version": DefaultStainlessPackageVersion,
|
||||||
"X-Stainless-OS": "MacOS",
|
"X-Stainless-OS": DefaultStainlessOS,
|
||||||
"X-Stainless-Arch": "arm64",
|
"X-Stainless-Arch": DefaultStainlessArch,
|
||||||
"X-Stainless-Runtime": "node",
|
"X-Stainless-Runtime": DefaultStainlessRuntime,
|
||||||
"X-Stainless-Runtime-Version": "v24.3.0",
|
"X-Stainless-Runtime-Version": DefaultStainlessRuntimeVersion,
|
||||||
"X-Stainless-Retry-Count": "0",
|
"X-Stainless-Retry-Count": "0",
|
||||||
"X-Stainless-Timeout": "600",
|
"X-Stainless-Timeout": "600",
|
||||||
"X-App": "cli",
|
"X-App": "cli",
|
||||||
"Anthropic-Dangerous-Direct-Browser-Access": "true",
|
"Anthropic-Dangerous-Direct-Browser-Access": "true",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BuildCLIUserAgent returns the current Claude Code API client user-agent.
|
||||||
|
func BuildCLIUserAgent(version, userType, entrypoint string) string {
|
||||||
|
version = strings.TrimSpace(version)
|
||||||
|
if version == "" {
|
||||||
|
version = DefaultCLIProductVersion
|
||||||
|
}
|
||||||
|
userType = strings.TrimSpace(userType)
|
||||||
|
if userType == "" {
|
||||||
|
userType = DefaultUserType
|
||||||
|
}
|
||||||
|
entrypoint = strings.TrimSpace(entrypoint)
|
||||||
|
if entrypoint == "" {
|
||||||
|
entrypoint = DefaultEntrypoint
|
||||||
|
}
|
||||||
|
return "claude-cli/" + version + " (" + userType + ", " + entrypoint + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildCodeUserAgent returns the current Claude Code transport/helper user-agent.
|
||||||
|
func BuildCodeUserAgent(version string) string {
|
||||||
|
version = strings.TrimSpace(version)
|
||||||
|
if version == "" {
|
||||||
|
version = DefaultCLIProductVersion
|
||||||
|
}
|
||||||
|
return "claude-code/" + version
|
||||||
|
}
|
||||||
|
|
||||||
// ApplyFingerprintOverrides 用配置覆盖默认指纹值(每个实例可设不同值)
|
// ApplyFingerprintOverrides 用配置覆盖默认指纹值(每个实例可设不同值)
|
||||||
// cliVersion: Claude CLI 版本(如 "2.1.81")
|
// cliVersion: Claude CLI 版本(如 "2.1.81")
|
||||||
// pkgVersion: SDK 版本(如 "0.80.0")
|
// pkgVersion: SDK 版本(如 "0.80.0")
|
||||||
@ -73,7 +115,7 @@ var DefaultHeaders = map[string]string{
|
|||||||
// arch: 架构(如 "arm64")
|
// arch: 架构(如 "arm64")
|
||||||
func ApplyFingerprintOverrides(cliVersion, pkgVersion, runtimeVersion, os_, arch string) {
|
func ApplyFingerprintOverrides(cliVersion, pkgVersion, runtimeVersion, os_, arch string) {
|
||||||
if cliVersion != "" {
|
if cliVersion != "" {
|
||||||
DefaultHeaders["User-Agent"] = "claude-cli/" + cliVersion + " (external, cli)"
|
DefaultHeaders["User-Agent"] = BuildCLIUserAgent(cliVersion, "", "")
|
||||||
}
|
}
|
||||||
if pkgVersion != "" {
|
if pkgVersion != "" {
|
||||||
DefaultHeaders["X-Stainless-Package-Version"] = pkgVersion
|
DefaultHeaders["X-Stainless-Package-Version"] = pkgVersion
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/Wei-Shaw/sub2api/internal/pkg/claude"
|
||||||
infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
|
infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
|
||||||
"github.com/Wei-Shaw/sub2api/internal/pkg/httpclient"
|
"github.com/Wei-Shaw/sub2api/internal/pkg/httpclient"
|
||||||
"github.com/Wei-Shaw/sub2api/internal/service"
|
"github.com/Wei-Shaw/sub2api/internal/service"
|
||||||
@ -15,8 +16,8 @@ import (
|
|||||||
|
|
||||||
const defaultClaudeUsageURL = "https://api.anthropic.com/api/oauth/usage"
|
const defaultClaudeUsageURL = "https://api.anthropic.com/api/oauth/usage"
|
||||||
|
|
||||||
// 默认 User-Agent,与用户抓包的请求一致
|
// 默认 User-Agent,与 Claude Code 2.1.88 的 helper/transport 请求保持一致。
|
||||||
const defaultUsageUserAgent = "claude-code/2.1.7"
|
const defaultUsageUserAgent = claude.DefaultCodeUserAgent
|
||||||
|
|
||||||
type claudeUsageService struct {
|
type claudeUsageService struct {
|
||||||
usageURL string
|
usageURL string
|
||||||
|
|||||||
@ -40,6 +40,7 @@ func TestValidate_ClaudeCLIUserAgent(t *testing.T) {
|
|||||||
want bool
|
want bool
|
||||||
}{
|
}{
|
||||||
{"标准版本号", "claude-cli/1.0.0", true},
|
{"标准版本号", "claude-cli/1.0.0", true},
|
||||||
|
{"官方 transport UA", "claude-code/2.1.88", true},
|
||||||
{"多位版本号", "claude-cli/12.34.56", true},
|
{"多位版本号", "claude-cli/12.34.56", true},
|
||||||
{"大写开头", "Claude-CLI/1.0.0", true},
|
{"大写开头", "Claude-CLI/1.0.0", true},
|
||||||
{"非 claude-cli", "curl/7.64.1", false},
|
{"非 claude-cli", "curl/7.64.1", false},
|
||||||
@ -90,6 +91,19 @@ func TestValidate_MessagesPath_FullValid(t *testing.T) {
|
|||||||
require.True(t, result, "完整有效请求应通过")
|
require.True(t, result, "完整有效请求应通过")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidate_MessagesPath_FullValid_ClaudeCodeUA(t *testing.T) {
|
||||||
|
v := newTestValidator()
|
||||||
|
|
||||||
|
req := httptest.NewRequest("POST", "/v1/messages", nil)
|
||||||
|
req.Header.Set("User-Agent", "claude-code/2.1.88")
|
||||||
|
req.Header.Set("X-App", "claude-code")
|
||||||
|
req.Header.Set("anthropic-beta", "max-tokens-3-5-sonnet-2024-07-15")
|
||||||
|
req.Header.Set("anthropic-version", "2023-06-01")
|
||||||
|
|
||||||
|
result := v.Validate(req, validClaudeCodeBody())
|
||||||
|
require.True(t, result, "官方 transport/helper UA 也应通过")
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidate_MessagesPath_MissingHeaders(t *testing.T) {
|
func TestValidate_MessagesPath_MissingHeaders(t *testing.T) {
|
||||||
v := newTestValidator()
|
v := newTestValidator()
|
||||||
body := validClaudeCodeBody()
|
body := validClaudeCodeBody()
|
||||||
|
|||||||
@ -15,11 +15,13 @@ import (
|
|||||||
type ClaudeCodeValidator struct{}
|
type ClaudeCodeValidator struct{}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// User-Agent 匹配: claude-cli/x.x.x (仅支持官方 CLI,大小写不敏感)
|
// User-Agent 匹配: 官方 Claude Code 目前存在两类产品前缀:
|
||||||
claudeCodeUAPattern = regexp.MustCompile(`(?i)^claude-cli/\d+\.\d+\.\d+`)
|
// 1. 主 Anthropic API 客户端: claude-cli/x.y.z (...)
|
||||||
|
// 2. transport / helper 请求: claude-code/x.y.z
|
||||||
|
claudeCodeUAPattern = regexp.MustCompile(`(?i)^claude-(?:cli|code)/\d+\.\d+\.\d+`)
|
||||||
|
|
||||||
// 带捕获组的版本提取正则
|
// 带捕获组的版本提取正则
|
||||||
claudeCodeUAVersionPattern = regexp.MustCompile(`(?i)^claude-cli/(\d+\.\d+\.\d+)`)
|
claudeCodeUAVersionPattern = regexp.MustCompile(`(?i)^claude-(?:cli|code)/(\d+\.\d+\.\d+)`)
|
||||||
|
|
||||||
// System prompt 相似度阈值(默认 0.5,和 claude-relay-service 一致)
|
// System prompt 相似度阈值(默认 0.5,和 claude-relay-service 一致)
|
||||||
systemPromptThreshold = 0.5
|
systemPromptThreshold = 0.5
|
||||||
@ -55,7 +57,7 @@ func NewClaudeCodeValidator() *ClaudeCodeValidator {
|
|||||||
// Validate 验证请求是否来自 Claude Code CLI
|
// Validate 验证请求是否来自 Claude Code CLI
|
||||||
// 采用与 claude-relay-service 完全一致的验证策略:
|
// 采用与 claude-relay-service 完全一致的验证策略:
|
||||||
//
|
//
|
||||||
// Step 1: User-Agent 检查 (必需) - 必须是 claude-cli/x.x.x
|
// Step 1: User-Agent 检查 (必需) - 必须是官方 claude-cli/ 或 claude-code/ 前缀
|
||||||
// Step 2: 对于非 messages 路径,只要 UA 匹配就通过
|
// Step 2: 对于非 messages 路径,只要 UA 匹配就通过
|
||||||
// Step 3: 检查 max_tokens=1 + haiku 探测请求绕过(UA 已验证)
|
// Step 3: 检查 max_tokens=1 + haiku 探测请求绕过(UA 已验证)
|
||||||
// Step 4: 对于 messages 路径,进行严格验证:
|
// Step 4: 对于 messages 路径,进行严格验证:
|
||||||
|
|||||||
@ -64,6 +64,7 @@ func TestExtractVersion(t *testing.T) {
|
|||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{"claude-cli/2.1.22 (darwin; arm64)", "2.1.22"},
|
{"claude-cli/2.1.22 (darwin; arm64)", "2.1.22"},
|
||||||
|
{"claude-code/2.1.88", "2.1.88"},
|
||||||
{"claude-cli/1.0.0", "1.0.0"},
|
{"claude-cli/1.0.0", "1.0.0"},
|
||||||
{"Claude-CLI/3.10.5 (linux; x86_64)", "3.10.5"}, // 大小写不敏感
|
{"Claude-CLI/3.10.5 (linux; x86_64)", "3.10.5"}, // 大小写不敏感
|
||||||
{"curl/8.0.0", ""}, // 非 Claude CLI
|
{"curl/8.0.0", ""}, // 非 Claude CLI
|
||||||
|
|||||||
@ -689,6 +689,41 @@ func TestGatewayService_AnthropicOAuth_NotAffectedByAPIKeyPassthroughToggle(t *t
|
|||||||
require.Contains(t, getHeaderRaw(req.Header, "anthropic-beta"), claude.BetaOAuth, "OAuth 链路仍应按原逻辑补齐 oauth beta")
|
require.Contains(t, getHeaderRaw(req.Header, "anthropic-beta"), claude.BetaOAuth, "OAuth 链路仍应按原逻辑补齐 oauth beta")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGatewayService_AnthropicOAuth_InjectsClaudeCodeSessionHeaderFromMetadata(t *testing.T) {
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
c, _ := gin.CreateTestContext(rec)
|
||||||
|
c.Request = httptest.NewRequest(http.MethodPost, "/v1/messages", nil)
|
||||||
|
|
||||||
|
sessionID := "12345678-1234-1234-1234-123456789abc"
|
||||||
|
body, err := json.Marshal(map[string]any{
|
||||||
|
"model": "claude-3-7-sonnet-20250219",
|
||||||
|
"metadata": map[string]any{
|
||||||
|
"user_id": FormatMetadataUserID(
|
||||||
|
"d61f76d0730d2b920763648949bad5c79742155c27037fc77ac3f9805cb90169",
|
||||||
|
"",
|
||||||
|
sessionID,
|
||||||
|
claude.DefaultCLIProductVersion,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
svc := &GatewayService{
|
||||||
|
cfg: &config.Config{
|
||||||
|
Gateway: config.GatewayConfig{MaxLineSize: defaultMaxLineSize},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
account := &Account{
|
||||||
|
Platform: PlatformAnthropic,
|
||||||
|
Type: AccountTypeOAuth,
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := svc.buildUpstreamRequest(context.Background(), c, account, body, "oauth-token", "oauth", "claude-3-7-sonnet-20250219", false, false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, sessionID, getHeaderRaw(req.Header, "X-Claude-Code-Session-Id"))
|
||||||
|
}
|
||||||
|
|
||||||
func TestGatewayService_AnthropicOAuth_ForwardPreservesBillingHeaderSystemBlock(t *testing.T) {
|
func TestGatewayService_AnthropicOAuth_ForwardPreservesBillingHeaderSystemBlock(t *testing.T) {
|
||||||
gin.SetMode(gin.TestMode)
|
gin.SetMode(gin.TestMode)
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,12 @@ func TestIsClaudeCodeClient(t *testing.T) {
|
|||||||
metadataUserID: "session_123e4567-e89b-12d3-a456-426614174000",
|
metadataUserID: "session_123e4567-e89b-12d3-a456-426614174000",
|
||||||
want: true,
|
want: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Claude Code helper client",
|
||||||
|
userAgent: "claude-code/2.1.88",
|
||||||
|
metadataUserID: "session_123e4567-e89b-12d3-a456-426614174000",
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Claude Code without version suffix",
|
name: "Claude Code without version suffix",
|
||||||
userAgent: "claude-cli/2.0.0",
|
userAgent: "claude-cli/2.0.0",
|
||||||
|
|||||||
@ -328,8 +328,8 @@ func isClaudeCodeCredentialScopeError(msg string) bool {
|
|||||||
// sseDataRe matches SSE data lines with optional whitespace after colon.
|
// sseDataRe matches SSE data lines with optional whitespace after colon.
|
||||||
// Some upstream APIs return non-standard "data:" without space (should be "data: ").
|
// Some upstream APIs return non-standard "data:" without space (should be "data: ").
|
||||||
var (
|
var (
|
||||||
sseDataRe = regexp.MustCompile(`^data:\s*`)
|
sseDataRe = regexp.MustCompile(`^data:\s*`)
|
||||||
claudeCliUserAgentRe = regexp.MustCompile(`^claude-cli/\d+\.\d+\.\d+`)
|
claudeCodeUserAgentRe = regexp.MustCompile(`^claude-(?:cli|code)/\d+\.\d+\.\d+`)
|
||||||
|
|
||||||
// claudeCodePromptPrefixes 用于检测 Claude Code 系统提示词的前缀列表
|
// claudeCodePromptPrefixes 用于检测 Claude Code 系统提示词的前缀列表
|
||||||
// 支持多种变体:标准版、Agent SDK 版、Explore Agent 版、Compact 版等
|
// 支持多种变体:标准版、Agent SDK 版、Explore Agent 版、Compact 版等
|
||||||
@ -3739,7 +3739,7 @@ func isClaudeCodeClient(userAgent string, metadataUserID string) bool {
|
|||||||
if metadataUserID == "" {
|
if metadataUserID == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return claudeCliUserAgentRe.MatchString(userAgent)
|
return claudeCodeUserAgentRe.MatchString(userAgent)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isClaudeCodeRequest(ctx context.Context, c *gin.Context, parsed *ParsedRequest) bool {
|
func isClaudeCodeRequest(ctx context.Context, c *gin.Context, parsed *ParsedRequest) bool {
|
||||||
@ -5758,12 +5758,11 @@ func (s *GatewayService) buildUpstreamRequest(ctx context.Context, c *gin.Contex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 同步 X-Claude-Code-Session-Id 头:取 body 中已处理的 metadata.user_id 的 session_id 覆盖
|
// Claude Code 主 API 客户端会始终发送 X-Claude-Code-Session-Id。
|
||||||
if sessionHeader := getHeaderRaw(req.Header, "X-Claude-Code-Session-Id"); sessionHeader != "" {
|
// 对于 mimic / 转发场景,只要 body 中 metadata.user_id 可解析,就主动注入并同步该头。
|
||||||
if uid := gjson.GetBytes(body, "metadata.user_id").String(); uid != "" {
|
if uid := gjson.GetBytes(body, "metadata.user_id").String(); uid != "" {
|
||||||
if parsed := ParseMetadataUserID(uid); parsed != nil {
|
if parsed := ParseMetadataUserID(uid); parsed != nil {
|
||||||
setHeaderRaw(req.Header, "X-Claude-Code-Session-Id", parsed.SessionID)
|
setHeaderRaw(req.Header, "X-Claude-Code-Session-Id", parsed.SessionID)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8486,12 +8485,11 @@ func (s *GatewayService) buildCountTokensRequest(ctx context.Context, c *gin.Con
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 同步 X-Claude-Code-Session-Id 头:取 body 中已处理的 metadata.user_id 的 session_id 覆盖
|
// Claude Code 主 API 客户端会始终发送 X-Claude-Code-Session-Id。
|
||||||
if sessionHeader := getHeaderRaw(req.Header, "X-Claude-Code-Session-Id"); sessionHeader != "" {
|
// 对于 mimic / 转发场景,只要 body 中 metadata.user_id 可解析,就主动注入并同步该头。
|
||||||
if uid := gjson.GetBytes(body, "metadata.user_id").String(); uid != "" {
|
if uid := gjson.GetBytes(body, "metadata.user_id").String(); uid != "" {
|
||||||
if parsed := ParseMetadataUserID(uid); parsed != nil {
|
if parsed := ParseMetadataUserID(uid); parsed != nil {
|
||||||
setHeaderRaw(req.Header, "X-Claude-Code-Session-Id", parsed.SessionID)
|
setHeaderRaw(req.Header, "X-Claude-Code-Session-Id", parsed.SessionID)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/Wei-Shaw/sub2api/internal/pkg/claude"
|
||||||
"github.com/Wei-Shaw/sub2api/internal/pkg/logger"
|
"github.com/Wei-Shaw/sub2api/internal/pkg/logger"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
"github.com/tidwall/sjson"
|
"github.com/tidwall/sjson"
|
||||||
@ -26,13 +27,13 @@ var (
|
|||||||
|
|
||||||
// 默认指纹值(当客户端未提供时使用)
|
// 默认指纹值(当客户端未提供时使用)
|
||||||
var defaultFingerprint = Fingerprint{
|
var defaultFingerprint = Fingerprint{
|
||||||
UserAgent: "claude-cli/2.1.84 (external, cli)",
|
UserAgent: claude.DefaultCLIUserAgent,
|
||||||
StainlessLang: "js",
|
StainlessLang: claude.DefaultStainlessLang,
|
||||||
StainlessPackageVersion: "0.74.0",
|
StainlessPackageVersion: claude.DefaultStainlessPackageVersion,
|
||||||
StainlessOS: "MacOS",
|
StainlessOS: claude.DefaultStainlessOS,
|
||||||
StainlessArch: "arm64",
|
StainlessArch: claude.DefaultStainlessArch,
|
||||||
StainlessRuntime: "node",
|
StainlessRuntime: claude.DefaultStainlessRuntime,
|
||||||
StainlessRuntimeVersion: "v24.3.0",
|
StainlessRuntimeVersion: claude.DefaultStainlessRuntimeVersion,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fingerprint represents account fingerprint data
|
// Fingerprint represents account fingerprint data
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
|
import "github.com/Wei-Shaw/sub2api/internal/pkg/claude"
|
||||||
|
|
||||||
// ==============================================================
|
// ==============================================================
|
||||||
// antigravity — identity_service 扩展
|
// antigravity — identity_service 扩展
|
||||||
//
|
//
|
||||||
@ -15,7 +17,7 @@ package service
|
|||||||
// 允许不同部署实例设置不同的 CLI/SDK 版本号,避免所有实例指纹相同
|
// 允许不同部署实例设置不同的 CLI/SDK 版本号,避免所有实例指纹相同
|
||||||
func ApplyDefaultFingerprintOverrides(cliVersion, pkgVersion, runtimeVersion, os_, arch string) {
|
func ApplyDefaultFingerprintOverrides(cliVersion, pkgVersion, runtimeVersion, os_, arch string) {
|
||||||
if cliVersion != "" {
|
if cliVersion != "" {
|
||||||
defaultFingerprint.UserAgent = "claude-cli/" + cliVersion + " (external, cli)"
|
defaultFingerprint.UserAgent = claude.BuildCLIUserAgent(cliVersion, "", "")
|
||||||
}
|
}
|
||||||
if pkgVersion != "" {
|
if pkgVersion != "" {
|
||||||
defaultFingerprint.StainlessPackageVersion = pkgVersion
|
defaultFingerprint.StainlessPackageVersion = pkgVersion
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user