Add the startup configuration pipe with file and env layers plus explain
Why: startup had only defaults and CLI flags; dev/test/prod operation needs the standard baseline (explicit file, environment, explicit-only CLI) and a read-only diagnostic. Evidence: internal/config unit tests cover precedence, unset-CLI no-override, presence-based file semantics, unknown-key/missing-file errors, env bool parsing, explain source labelling and DSN redaction; full suite 12 packages pass; live run of config explain with envs/prod.json shows correct layer attribution and redaction. Impact: serve gains --config and AGENT_SYSTEM_* env overrides (behavior unchanged when neither is used); new config explain subcommand; envs/ examples and PROJECT.md configuration section. Refs: documents docs/proposals/2026-09-30-configuration-surface.md; standards/configuration.md
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// agent-system:单二进制服务(HTTP/SSE + 内嵌前端 + 双档存储)。
|
||||
// 部署约束(设计 4.1 原则 8):非容器化;拷贝即运行。
|
||||
// 嵌入式档默认(SQLite + 本地对象目录,零外部服务);--dsn 切换 PostgreSQL 档
|
||||
//(多用户/多实例,认证默认开启,apikey 子命令管理密钥)。
|
||||
// (多用户/多实例,认证默认开启,apikey 子命令管理密钥)。
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"os"
|
||||
|
||||
"agent-system-backend/internal/app"
|
||||
"agent-system-backend/internal/config"
|
||||
)
|
||||
|
||||
var version = "0.1.0-dev"
|
||||
@@ -25,9 +26,12 @@ func main() {
|
||||
os.Args = append([]string{os.Args[0]}, os.Args[2:]...)
|
||||
case "apikey":
|
||||
os.Exit(runAPIKey(os.Args[2:]))
|
||||
case "config":
|
||||
os.Exit(runConfig(os.Args[2:]))
|
||||
}
|
||||
}
|
||||
fs := flag.NewFlagSet("serve", flag.ExitOnError)
|
||||
cfgFile := fs.String("config", "", "显式配置文件(JSON,键见 config explain;无自动发现)")
|
||||
addr := fs.String("addr", "127.0.0.1:8787", "监听地址")
|
||||
data := fs.String("data", "./data", "数据目录(SQLite 与工件对象目录)")
|
||||
pluginsDir := fs.String("plugins", "./plugins", "插件目录(声明资产热加载)")
|
||||
@@ -36,16 +40,56 @@ func main() {
|
||||
artifacts := fs.String("artifacts", "", "工件对象目录(默认 <data>/artifacts;PG 档多实例时指向共享卷)")
|
||||
auth := fs.String("auth", "auto", "API key 认证:auto(PG 档开、嵌入式关)| on | off")
|
||||
fs.Parse(os.Args[1:])
|
||||
cliSet := map[string]bool{}
|
||||
fs.Visit(func(f *flag.Flag) { cliSet[f.Name] = true })
|
||||
|
||||
if err := app.Run(app.Options{
|
||||
Addr: *addr, DataDir: *data, PluginsDir: *pluginsDir, ForceMock: *mock,
|
||||
resolved, err := config.Load(*cfgFile, os.LookupEnv, config.Config{
|
||||
Addr: *addr, DataDir: *data, PluginsDir: *pluginsDir, Mock: *mock,
|
||||
StorageDSN: *dsn, ArtifactsDir: *artifacts, AuthMode: *auth,
|
||||
}, cliSet)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "agent-system:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := app.Run(app.Options{
|
||||
Addr: resolved.Addr, DataDir: resolved.DataDir, PluginsDir: resolved.PluginsDir, ForceMock: resolved.Mock,
|
||||
StorageDSN: resolved.StorageDSN, ArtifactsDir: resolved.ArtifactsDir, AuthMode: resolved.AuthMode,
|
||||
}); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "agent-system:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// runConfig:config explain——只读解析诊断(不建库、不写业务目录;DSN 脱敏)。
|
||||
func runConfig(args []string) int {
|
||||
if len(args) == 0 || args[0] != "explain" {
|
||||
fmt.Fprintln(os.Stderr, "用法:agent-system config explain [--config f.json] [--addr …](其余 serve 同名 flag 用于演示覆盖)")
|
||||
return 2
|
||||
}
|
||||
fs := flag.NewFlagSet("config explain", flag.ExitOnError)
|
||||
cfgFile := fs.String("config", "", "显式配置文件(JSON)")
|
||||
addr := fs.String("addr", "127.0.0.1:8787", "")
|
||||
data := fs.String("data", "./data", "")
|
||||
pluginsDir := fs.String("plugins", "./plugins", "")
|
||||
mock := fs.Bool("mock", false, "")
|
||||
dsn := fs.String("dsn", "", "")
|
||||
artifacts := fs.String("artifacts", "", "")
|
||||
auth := fs.String("auth", "auto", "")
|
||||
fs.Parse(args[1:])
|
||||
cliSet := map[string]bool{}
|
||||
fs.Visit(func(f *flag.Flag) { cliSet[f.Name] = true })
|
||||
resolved, err := config.Load(*cfgFile, os.LookupEnv, config.Config{
|
||||
Addr: *addr, DataDir: *data, PluginsDir: *pluginsDir, Mock: *mock,
|
||||
StorageDSN: *dsn, ArtifactsDir: *artifacts, AuthMode: *auth,
|
||||
}, cliSet)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "agent-system:", err)
|
||||
return 1
|
||||
}
|
||||
resolved.Explain(os.Stdout, *cfgFile)
|
||||
return 0
|
||||
}
|
||||
|
||||
// runAPIKey:密钥管理子命令(create/list/revoke)。
|
||||
func runAPIKey(args []string) int {
|
||||
// 子命令在前、flag 在后:先剥出子命令再解析(Go flag 遇位置参数即停)。
|
||||
|
||||
Reference in New Issue
Block a user