Files
anthropic-proxy/internal/auth/selector.go
T
Alexander 909c8b1894 Add request sanitizer, background token refresh, and OpenCode support
Sanitizer renames tool names and replaces system prompt patterns
that Anthropic fingerprints to detect non-Claude-Code clients.
Lowercase tool names (bash, read, glob, etc.) combined together
trigger rejection — renaming to PascalCase bypasses this.
Configurable via YAML sanitize rules for tools, system, and body.

Background OAuth token refresh every 30s with 5-minute pre-expiry
lead. Uses Chrome TLS fingerprint for refresh endpoint too.

Adds /messages route (without /v1 prefix) for OpenCode compat.
2026-04-09 22:52:43 +02:00

59 lines
1003 B
Go

package auth
import (
"context"
"fmt"
"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) {
refreshAll(p)
}