| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364 |
- package service
- import (
- "fmt"
- "regexp"
- "strings"
- "github.com/Wei-Shaw/sub2api/internal/pkg/claude"
- "github.com/tidwall/gjson"
- "github.com/tidwall/sjson"
- )
- // ccVersionInBillingRe matches the semver part of cc_version (X.Y.Z).
- var ccVersionInBillingRe = regexp.MustCompile(`cc_version=\d+\.\d+\.\d+`)
- var ccVersionWithFingerprintInBillingRe = regexp.MustCompile(`cc_version=\d+\.\d+\.\d+\.[0-9a-fA-F]{3}\b`)
- // OAuth mimicry forces the built-in User-Agent after applying account fingerprints.
- func effectiveBillingUserAgent(tokenType string, mimicClaudeCode bool, fingerprint *Fingerprint) string {
- if tokenType == "oauth" && mimicClaudeCode {
- return claude.DefaultHeaders["User-Agent"]
- }
- if fingerprint == nil {
- return ""
- }
- return fingerprint.UserAgent
- }
- // syncBillingHeaderVersion rewrites cc_version in x-anthropic-billing-header
- // system text blocks to match the version extracted from userAgent.
- // Recompute any recognized fingerprint suffix because its input includes the version.
- // Only touches system array blocks whose text starts with "x-anthropic-billing-header".
- func syncBillingHeaderVersion(body []byte, userAgent string) []byte {
- version := ExtractCLIVersion(userAgent)
- if version == "" {
- return body
- }
- systemResult := gjson.GetBytes(body, "system")
- if !systemResult.Exists() || !systemResult.IsArray() {
- return body
- }
- replacement := "cc_version=" + version
- idx := 0
- systemResult.ForEach(func(_, item gjson.Result) bool {
- text := item.Get("text")
- if text.Exists() && text.Type == gjson.String &&
- strings.HasPrefix(text.String(), "x-anthropic-billing-header") {
- fingerprintedReplacement := replacement + "." + computeClaudeCodeFingerprint(body, version)
- newText := ccVersionWithFingerprintInBillingRe.ReplaceAllString(text.String(), fingerprintedReplacement)
- newText = ccVersionInBillingRe.ReplaceAllString(newText, replacement)
- if newText != text.String() {
- if updated, err := sjson.SetBytes(body, fmt.Sprintf("system.%d.text", idx), newText); err == nil {
- body = updated
- }
- }
- }
- idx++
- return true
- })
- return body
- }
|