package main import ( "bufio" "encoding/json" "fmt" "os" "path/filepath" "strings" ) // loadConfig reads .env and .config.json from root (if present) and sets any // keys that aren't already in the process environment. Real env vars win. // Recognized keys: OPENAI_API_KEY, OPENAI_BASE_URL, OPENAI_MODEL. func loadConfig(root string) error { if err := loadEnvFile(filepath.Join(root, ".env")); err != nil { return fmt.Errorf(".env: %w", err) } if err := loadJSONConfig(filepath.Join(root, ".config.json")); err != nil { return fmt.Errorf(".config.json: %w", err) } return nil } func loadEnvFile(path string) error { f, err := os.Open(path) if err != nil { if os.IsNotExist(err) { return nil } return err } defer f.Close() scanner := bufio.NewScanner(f) lineNo := 0 for scanner.Scan() { lineNo++ line := strings.TrimSpace(scanner.Text()) if line == "" || strings.HasPrefix(line, "#") { continue } if strings.HasPrefix(line, "export ") { line = strings.TrimPrefix(line, "export ") } eq := strings.IndexByte(line, '=') if eq <= 0 { return fmt.Errorf("line %d: missing '='", lineNo) } key := strings.TrimSpace(line[:eq]) val := strings.TrimSpace(line[eq+1:]) val = unquote(val) setIfEmpty(key, val) } return scanner.Err() } func loadJSONConfig(path string) error { b, err := os.ReadFile(path) if err != nil { if os.IsNotExist(err) { return nil } return err } var m map[string]string if err := json.Unmarshal(b, &m); err != nil { return err } for k, v := range m { setIfEmpty(k, v) } return nil } func setIfEmpty(key, val string) { if key == "" { return } if _, ok := os.LookupEnv(key); ok { return } _ = os.Setenv(key, val) } func unquote(s string) string { if len(s) >= 2 { first, last := s[0], s[len(s)-1] if (first == '"' && last == '"') || (first == '\'' && last == '\'') { return s[1 : len(s)-1] } } return s }