Files
Agent-System/internal/config/explain.go
T
agent-bot 679028e44e
CI / go-vet (pull_request) Successful in 13s
CI / short-tests-report (pull_request) Failing after 35s
CI / backup-restore-drill (pull_request) Successful in 25s
CI / web (pull_request) Successful in 31s
feat(cli): 'config explain' with per-key layer attribution (U10b slice 1)
- config.Explain(envFiles, cliOverrides): per-key source layer following the
  application precedence cli > process-env > env-file > default; registry is
  reporting-only (values still applied solely by Load); secrets never shown
- ParseEnvFileKeys shares applyEnvFile's parsing rules
- cmd/server 'config explain' resolves the same env-file stack as Load and
  renders the attribution table; smoke: HTTP_PORT/AGENT_BACKEND -> cli
- tests: registry liveness sample + layer attribution (file/process/cli/
  default) + secret masking
- full per-field snapshot with applied/desired revisions remains U10b core
2026-09-29 07:39:28 +00:00

179 lines
5.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package config
import (
"bufio"
"fmt"
"os"
"path/filepath"
"sort"
"strings"
)
// knownEnvKeys 是配置环境键注册表(U10b 第一片:reporting-only)。
// 仅用于 config explain 的来源解释,不参与取值——取值仍由 Load 的
// 应用逻辑唯一决定;新增环境键时须同步登记,漂移由抽样测试约束。
var knownEnvKeys = map[string]bool{
"ADMIN_PASSWORD": true,
"ADMIN_USERNAME": true,
"AGENTX_API_BASE": true,
"AGENTX_API_KEY": true,
"AGENTX_MODEL": true,
"AGENTX_TIMEOUT": true,
"AGENT_BACKEND": true,
"AGENT_TIMEOUT": true,
"ALLOW_IN_MEMORY": true,
"APP_DB_DSN": true,
"APP_PUBLISH_HOST": true,
"AUTH_REGISTRATION_ENABLED": true,
"AUTH_REQUIRE_APPROVAL": true,
"CONTEXT_DB_HOST": true,
"CONTEXT_DB_NAME": true,
"CONTEXT_DB_PASSWORD": true,
"CONTEXT_DB_PORT": true,
"CONTEXT_DB_USER": true,
"CORS_ALLOWED_ORIGINS": true,
"DATA_DIR": true,
"EMBEDDING_API_KEY": true,
"EMBEDDING_ENDPOINT_URL": true,
"EMBEDDING_MODEL": true,
"EMBEDDING_PROVIDER": true,
"ENV": true,
"FAV_DEGRADED_READ": true,
"FAV_PUBLISHER_ENABLED": true,
"HTTP_ADDR": true,
"HTTP_HOST": true,
"HTTP_PORT": true,
"JEV_API_KEY": true,
"JEV_BASE_URL": true,
"JEV_DECIDE_MODE": true,
"KB_DB_HOST": true,
"KB_DB_NAME": true,
"KB_DB_PASSWORD": true,
"KB_DB_PORT": true,
"KB_DB_USER": true,
"MATERIAL_FULLTEXT_MAX_BYTES": true,
"MATERIAL_RAG_TOP_K": true,
"MAX_CONCURRENT_AGENTS": true,
"MINERU_API_KEY": true,
"MINERU_ENDPOINT_URL": true,
"MINERU_LANGUAGE": true,
"MINERU_MODEL_VERSION": true,
"MINERU_TIMEOUT": true,
"MINIMAX_API_KEY": true,
"MINIMAX_ENDPOINT_URL": true,
"PI_AGENT_BIN": true,
"PI_AGENT_BRIDGE_BIN": true,
"PI_AGENT_MODEL": true,
"PI_AGENT_PROVIDER": true,
"PI_AGENT_RUNNER_AUTH_TOKEN": true,
"PI_AGENT_RUNNER_URL": true,
"POSTGRES_SHARED_KB_DSN": true,
"RERANKER_API_KEY": true,
"RERANKER_ENDPOINT_URL": true,
"RERANKER_MODEL": true,
"RERANKER_PROVIDER": true,
"RUNNER_AUTH_TOKEN": true,
"VAULT_MASTER_KEY": true,
"VAULT_STATE_ROOT": true,
"VAULT_STORAGE_IDENTITY": true,
"WORKSPACE_QUOTA_BYTES": true,
"WORKSPACE_TTL": true,
}
// ExplainLayer 是配置键的一个来源层。
type ExplainLayer = string
const (
LayerDefault ExplainLayer = "default"
LayerEnvFile ExplainLayer = "env-file"
LayerProcessEnv ExplainLayer = "process-env"
LayerCLI ExplainLayer = "cli"
)
// ExplainEntry 是单个配置键的解释行。
type ExplainEntry struct {
Key string
Layer ExplainLayer
Value string // 秘密键只显示 <set>/<unset>;env-file 层显示文件路径
}
// ParseEnvFileKeys 用与 applyEnvFile 相同的解析规则列出文件中出现的键。
func ParseEnvFileKeys(path string) ([]string, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
var keys []string
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" || strings.HasPrefix(line, "#") {
continue
}
parts := strings.SplitN(line, "=", 2)
if len(parts) != 2 {
continue
}
keys = append(keys, strings.TrimSpace(parts[0]))
}
return keys, scanner.Err()
}
// Explain 生成各配置键的来源层解释(KTD17 config explain 第一片)。
// envFiles 按加载顺序(后者覆盖前者);cliOverrides 为 CLI 显式覆盖键。
// 层序与生效优先级一致:cli > process-env > env-file > default。
// 非秘密且来源为 env-file/process-env 的键展示脱敏值;秘密键只显示 set 状态。
func Explain(envFiles []string, cliOverrides map[string]string) []ExplainEntry {
fileKeys := map[string]string{} // key -> 首个出现的文件
for _, p := range envFiles {
keys, err := ParseEnvFileKeys(p)
if err != nil {
continue
}
for _, k := range keys {
fileKeys[k] = filepath.ToSlash(p)
}
}
var out []ExplainEntry
for k := range knownEnvKeys {
e := ExplainEntry{Key: k, Layer: LayerDefault, Value: "<unset>"}
if src, ok := fileKeys[k]; ok {
e.Layer = LayerEnvFile
e.Value = src
if v := os.Getenv(k); v != "" {
e.Layer = LayerProcessEnv
e.Value = maskedValue(v)
}
} else if v := os.Getenv(k); v != "" {
e.Layer = LayerProcessEnv
e.Value = maskedValue(v)
}
if v, ok := cliOverrides[k]; ok {
e.Layer = LayerCLI
e.Value = maskedValue(v)
}
out = append(out, e)
}
sort.Slice(out, func(i, j int) bool { return out[i].Key < out[j].Key })
return out
}
func maskedValue(v string) string {
if strings.TrimSpace(v) == "" {
return "<unset>"
}
return "<set>"
}
// FormatExplanation 渲染 explain 表(供 CLI 输出与测试断言)。
func FormatExplanation(entries []ExplainEntry) string {
var b strings.Builder
b.WriteString("key layer detail\n")
for _, e := range entries {
fmt.Fprintf(&b, "%-37s %-12s %s\n", e.Key, e.Layer, e.Value)
}
return b.String()
}