Files
mcp-captcharesolver/internal/config/config.go
T
lukaszraczylo 8bfd64f7ed fix: address code review (v3/v2 detect, gemini caps, fetch timeout, audio mime, json extract, dead temp config)
- detect: recaptcha_v3 now suppresses recaptcha_v2 (hidden g-recaptcha div on v3 pages no longer mis-detected as LLM-solvable); funcaptcha drops over-broad bare "arkose" substring
- llm/gemini: stub methods return ErrUnsupported{Provider:"gemini"} so audio handler errors.As guard catches it; drop unused fmt import
- mcp/tools: remove gemini from audio capability (stub); pick audio filename (.wav/.mp3) from sniffed media type; schema notes for browser/actions no longer advertise unimplemented options
- imageutil: fetchURL uses a package client with 30s Timeout instead of http.DefaultClient
- captcha/json: extractJSON brace-counts to return the first balanced object (handles prose, markdown fences, example-before-real)
- config: remove dead CAPTCHA_LLM_TEMPERATURE / Config.Temperature; solving is fixed at temperature 0
- stealth: Action.Type doc comment drops nonexistent scroll case
2026-06-07 14:15:43 +01:00

75 lines
2.1 KiB
Go

// Package config loads and validates the MCP server configuration from env vars.
package config
import (
"fmt"
"os"
"strconv"
"time"
)
// Config is the validated runtime configuration. Fields are ordered
// largest-alignment-first to satisfy the fieldalignment linter.
//
// Temperature is intentionally not configurable: captcha solving is fixed at
// temperature 0 for deterministic results (see the LLM provider request bodies).
type Config struct {
Provider string
BaseURL string
APIKey string
Model string
AudioModel string
LogLevel string
Timeout time.Duration
MaxTokens int
}
var validProviders = map[string]bool{
"anthropic": true, "openai": true, "openai-compatible": true, "gemini": true,
}
// Load reads CAPTCHA_LLM_* env vars and returns a validated Config.
func Load() (Config, error) {
c := Config{
Provider: os.Getenv("CAPTCHA_LLM_PROVIDER"),
BaseURL: os.Getenv("CAPTCHA_LLM_BASE_URL"),
APIKey: os.Getenv("CAPTCHA_LLM_API_KEY"),
Model: os.Getenv("CAPTCHA_LLM_MODEL"),
AudioModel: os.Getenv("CAPTCHA_LLM_AUDIO_MODEL"),
LogLevel: envOr("CAPTCHA_LOG_LEVEL", "info"),
MaxTokens: 1024,
Timeout: 60 * time.Second,
}
if !validProviders[c.Provider] {
return Config{}, fmt.Errorf("CAPTCHA_LLM_PROVIDER must be one of anthropic|openai|openai-compatible|gemini, got %q", c.Provider)
}
if c.APIKey == "" && c.BaseURL == "" {
return Config{}, fmt.Errorf("CAPTCHA_LLM_API_KEY is required (or set CAPTCHA_LLM_BASE_URL for a keyless local endpoint)")
}
if c.Model == "" {
return Config{}, fmt.Errorf("CAPTCHA_LLM_MODEL is required")
}
if v := os.Getenv("CAPTCHA_LLM_MAX_TOKENS"); v != "" {
n, err := strconv.Atoi(v)
if err != nil {
return Config{}, fmt.Errorf("CAPTCHA_LLM_MAX_TOKENS: %w", err)
}
c.MaxTokens = n
}
if v := os.Getenv("CAPTCHA_LLM_TIMEOUT"); v != "" {
d, err := time.ParseDuration(v)
if err != nil {
return Config{}, fmt.Errorf("CAPTCHA_LLM_TIMEOUT: %w", err)
}
c.Timeout = d
}
return c, nil
}
func envOr(key, def string) string {
if v := os.Getenv(key); v != "" {
return v
}
return def
}