package plugin import ( "fmt" "net/url" "path/filepath" "strings" "billing/internal/modelcatalog" "gopkg.in/yaml.v3" ) type Config struct { Enabled bool `yaml:"enabled"` CodexOnly bool `yaml:"codex_only"` DatabasePath string `yaml:"database_path"` BootstrapName string `yaml:"bootstrap_name"` BootstrapKey string `yaml:"bootstrap_key"` ModelsDevURL string `yaml:"models_dev_url"` CatalogPath string `yaml:"models_dev_cache_path"` } func defaultConfig() Config { return Config{ Enabled: true, CodexOnly: true, DatabasePath: "data/billing.db", BootstrapName: "default", BootstrapKey: "000000", ModelsDevURL: modelcatalog.DefaultSourceURL, } } func decodeConfig(raw []byte) (Config, error) { cfg := defaultConfig() if len(raw) == 0 { return cfg, nil } if err := yaml.Unmarshal(raw, &cfg); err != nil { return Config{}, fmt.Errorf("解析插件配置: %w", err) } if strings.TrimSpace(cfg.DatabasePath) == "" { return Config{}, fmt.Errorf("database_path 不能为空") } if strings.TrimSpace(cfg.BootstrapName) == "" { return Config{}, fmt.Errorf("bootstrap_name 不能为空") } if !validManagedSecret(cfg.BootstrapKey) { return Config{}, fmt.Errorf("bootstrap_key 必须为 6-256 个不含空白或控制字符的字符") } catalogURL, err := url.Parse(strings.TrimSpace(cfg.ModelsDevURL)) if err != nil || catalogURL.Host == "" || (catalogURL.Scheme != "http" && catalogURL.Scheme != "https") { return Config{}, fmt.Errorf("models_dev_url 必须是有效的 HTTP(S) URL") } cfg.ModelsDevURL = catalogURL.String() if strings.TrimSpace(cfg.CatalogPath) == "" && cfg.DatabasePath != ":memory:" { cfg.CatalogPath = filepath.Join(filepath.Dir(cfg.DatabasePath), "models-dev-catalog.json") } return cfg, nil } func (c Config) accepts(record UsageRecord) bool { if !c.Enabled { return false } if !c.CodexOnly { return true } provider := strings.ToLower(strings.TrimSpace(record.Provider)) model := strings.ToLower(strings.TrimSpace(record.Model)) return provider == "codex" || strings.Contains(model, "codex") || strings.HasPrefix(model, "gpt-") }