返回 DeepSeek-Reasonix
expand.go
根目录 / internal / config / expand.go
1 package config
2
3 import (
4 "os"
5 "regexp"
6 "strings"
7 )
8
9 // varRef matches ${VAR} and ${VAR:-default}: a shell-style reference with an
10 // optional ":-default" fallback used when the variable is unset or empty.
11 var varRef = regexp.MustCompile(`\$\{([A-Za-z_][A-Za-z0-9_]*)(:-([^}]*))?\}`)
12
13 type envLookup func(string) (string, bool)
14
15 // ExpandVars substitutes ${VAR} / ${VAR:-default} references from the process
16 // environment. An unset variable with no default expands to "" (matching the
17 // MCP / Claude Code convention), so a missing secret yields an empty header
18 // rather than a literal "${TOKEN}" leaking onto the wire.
19 func ExpandVars(s string) string {
20 return expandVarsWithLookup(s, func(name string) (string, bool) {
21 v, ok := os.LookupEnv(name)
22 return v, ok && v != ""
23 })
24 }
25
26 func expandVarsWithLookup(s string, lookup envLookup) string {
27 if !strings.Contains(s, "${") {
28 return s
29 }
30 return varRef.ReplaceAllStringFunc(s, func(m string) string {
31 g := varRef.FindStringSubmatch(m)
32 name, hasDefault, def := g[1], g[2] != "", g[3]
33 if v, ok := lookup(name); ok {
34 return v
35 }
36 if hasDefault {
37 return def
38 }
39 return ""
40 })
41 }
42
43 func scopedEnvLookup(scoped map[string]string) envLookup {
44 return func(name string) (string, bool) {
45 if v, ok := os.LookupEnv(name); ok {
46 return v, v != ""
47 }
48 if v, ok := scoped[name]; ok && v != "" {
49 return v, true
50 }
51 return "", false
52 }
53 }
54
55 func (c *Config) expandVars(s string) string {
56 if c == nil {
57 return ExpandVars(s)
58 }
59 return expandVarsWithLookup(s, scopedEnvLookup(c.expansionEnv))
60 }
61
62 // ExpandedPlugin returns a copy of e with ${VAR} references expanded across the
63 // command, args, env values, url, and header values — the fields Claude Code
64 // also expands. The entry itself is left untouched.
65 func (e PluginEntry) ExpandedPlugin() PluginEntry {
66 lookup := scopedEnvLookup(e.expansionEnv)
67 out := e
68 out.Command = expandVarsWithLookup(e.Command, lookup)
69 out.URL = expandVarsWithLookup(e.URL, lookup)
70 if len(e.Args) > 0 {
71 out.Args = make([]string, len(e.Args))
72 for i, a := range e.Args {
73 out.Args[i] = expandVarsWithLookup(a, lookup)
74 }
75 }
76 out.Env = expandMap(e.Env, lookup)
77 out.Headers = expandMap(e.Headers, lookup)
78 return out
79 }
80
81 func expandMap(m map[string]string, lookup envLookup) map[string]string {
82 if len(m) == 0 {
83 return m
84 }
85 out := make(map[string]string, len(m))
86 for k, v := range m {
87 out[k] = expandVarsWithLookup(v, lookup)
88 }
89 return out
90 }
91
91 lines GO