c4c1d4daa4
Sniffs a real Claude Code request on startup to capture exact HTTP headers, then replays them for all proxied requests. Injects the billing header with per-request SHA256 fingerprint into the system prompt. Uses utls with Chrome TLS fingerprint to pass Cloudflare's bot detection on api.anthropic.com. Supports both streaming (SSE) and non-streaming modes, round-robin credential selection with automatic failover, and loading OAuth tokens from both cli-proxy-api auth files and native ~/.claude/.credentials.json.
80 lines
1.6 KiB
Go
80 lines
1.6 KiB
Go
package auth
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type Pool struct {
|
|
creds []*Credential
|
|
cursor int
|
|
mu sync.Mutex
|
|
}
|
|
|
|
func NewPool(creds []*Credential) *Pool {
|
|
return &Pool{creds: creds}
|
|
}
|
|
|
|
func (p *Pool) Pick() (*Credential, error) {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
|
|
n := len(p.creds)
|
|
if n == 0 {
|
|
return nil, fmt.Errorf("no credentials available")
|
|
}
|
|
|
|
for i := 0; i < n; i++ {
|
|
idx := (p.cursor + i) % n
|
|
cred := p.creds[idx]
|
|
if !cred.IsOnCooldown() {
|
|
p.cursor = (idx + 1) % n
|
|
return cred, nil
|
|
}
|
|
}
|
|
|
|
return nil, fmt.Errorf("all %d credentials are on cooldown", n)
|
|
}
|
|
|
|
func (p *Pool) MarkFailure(cred *Credential, statusCode int) {
|
|
switch {
|
|
case statusCode == 429:
|
|
cred.SetCooldown(30 * time.Second)
|
|
case statusCode >= 500:
|
|
cred.SetCooldown(5 * time.Second)
|
|
}
|
|
}
|
|
|
|
func (p *Pool) MarkSuccess(cred *Credential) {
|
|
cred.mu.Lock()
|
|
defer cred.mu.Unlock()
|
|
cred.CooldownUntil = time.Time{}
|
|
}
|
|
|
|
func (p *Pool) RefreshExpiring(ctx context.Context) {
|
|
p.mu.Lock()
|
|
creds := make([]*Credential, len(p.creds))
|
|
copy(creds, p.creds)
|
|
p.mu.Unlock()
|
|
|
|
threshold := time.Now().Add(5 * time.Minute)
|
|
for _, cred := range creds {
|
|
cred.mu.Lock()
|
|
needsRefresh := cred.ExpiresAt.Before(threshold)
|
|
email := cred.Email
|
|
cred.mu.Unlock()
|
|
|
|
if needsRefresh {
|
|
log.Printf("refreshing token for %s (expires %s)", email, cred.ExpiresAt.Format(time.RFC3339))
|
|
if err := RefreshToken(ctx, cred); err != nil {
|
|
log.Printf("failed to refresh token for %s: %v", email, err)
|
|
} else {
|
|
log.Printf("refreshed token for %s, new expiry %s", email, cred.ExpiresAt.Format(time.RFC3339))
|
|
}
|
|
}
|
|
}
|
|
}
|