909c8b1894
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.
59 lines
1003 B
Go
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)
|
|
}
|