config.go 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. package main
  2. import (
  3. "os"
  4. "path/filepath"
  5. "strings"
  6. )
  7. const defaultBalanceURL = "https://api.deepseek.com/user/balance"
  8. // Config is the fully-resolved runtime configuration. Values are loaded with
  9. // precedence: process environment > .env file > built-in default.
  10. type Config struct {
  11. APIKey string
  12. BalanceURL string
  13. // Harness daemon settings; consumed by the harness feature in a later step.
  14. Harness harnessConfig
  15. }
  16. // LoadConfig resolves configuration from the .env file in dir (best-effort),
  17. // overlaid with process environment variables, then defaults.
  18. func LoadConfig(dir string) Config {
  19. dotenv := readDotEnv(filepath.Join(dir, ".env"))
  20. cfg := Config{
  21. BalanceURL: defaultBalanceURL,
  22. Harness: harnessConfig{
  23. Enabled: true,
  24. Command: "npx --yes @deepseek-ai/dsh web",
  25. URL: "http://127.0.0.1:3080",
  26. StartDelay: 15,
  27. LogFile: "harness.log",
  28. },
  29. }
  30. cfg.APIKey = firstNonEmpty(os.Getenv("DEEPSEEK_API_KEY"), dotenv["DEEPSEEK_API_KEY"])
  31. if v := firstNonEmpty(os.Getenv("DEEPSEEK_BALANCE_URL"), dotenv["DEEPSEEK_BALANCE_URL"]); v != "" {
  32. cfg.BalanceURL = v
  33. }
  34. return cfg
  35. }
  36. func firstNonEmpty(values ...string) string {
  37. for _, v := range values {
  38. if v != "" {
  39. return v
  40. }
  41. }
  42. return ""
  43. }
  44. // readDotEnv parses a simple KEY=VALUE file. Missing files yield an empty map.
  45. // Lines are trimmed; '#' starts a comment; quotes are stripped from values.
  46. func readDotEnv(path string) map[string]string {
  47. data, err := os.ReadFile(path)
  48. if err != nil {
  49. return nil
  50. }
  51. m := make(map[string]string)
  52. for _, raw := range strings.Split(string(data), "\n") {
  53. line := strings.TrimSpace(raw)
  54. if line == "" || line[0] == '#' {
  55. continue
  56. }
  57. eq := strings.IndexByte(line, '=')
  58. if eq < 1 {
  59. continue
  60. }
  61. k := strings.TrimSpace(line[:eq])
  62. v := strings.TrimSpace(line[eq+1:])
  63. m[k] = strings.Trim(v, `"'`)
  64. }
  65. return m
  66. }