Files
Agent-System/internal/sysadmin/apply.go
T
weichen 86e9d3706b feat(sysadmin): admin env settings + maintenance FD handoff
Persist agent/secrets settings with masked secrets and runner hot-reload;
add maintenance windows, maintpage binary, and zero-downtime listener handoff.
2026-09-24 23:20:20 +00:00

91 lines
2.6 KiB
Go

package sysadmin
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
)
// RunnerApplyPayload 对齐 deploy/pi-agent/runner.mjs POST /admin/reload。
type RunnerApplyPayload struct {
Minimax struct {
Endpoint string `json:"endpoint"`
APIKey string `json:"apiKey"`
ModelID string `json:"modelId"`
APIType string `json:"apiType"`
} `json:"minimax"`
TavilyKeys []string `json:"tavilyKeys"`
PIModel string `json:"piModel,omitempty"`
}
// NewRunnerApplyFunc 构造推送到 PI runner 的 ApplyFunc。
func NewRunnerApplyFunc(svc *Service, runnerURL, authToken string, client *http.Client) ApplyFunc {
if client == nil {
client = &http.Client{Timeout: 8 * time.Second}
}
base := strings.TrimRight(strings.TrimSpace(runnerURL), "/")
token := strings.TrimSpace(authToken)
return func(ctx context.Context) (string, error) {
if base == "" {
return "n/a", fmt.Errorf("sysadmin: PI runner URL empty")
}
payload, err := BuildRunnerPayload(ctx, svc)
if err != nil {
return "error", err
}
body, err := json.Marshal(payload)
if err != nil {
return "error", err
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, base+"/admin/reload", bytes.NewReader(body))
if err != nil {
return "error", err
}
req.Header.Set("Content-Type", "application/json")
if token != "" {
req.Header.Set("Authorization", "Bearer "+token)
}
resp, err := client.Do(req)
if err != nil {
return "error", fmt.Errorf("sysadmin: runner reload: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode >= 300 {
return "error", fmt.Errorf("sysadmin: runner reload HTTP %d", resp.StatusCode)
}
return "ok", nil
}
}
// BuildRunnerPayload 从白名单解析值组装 reload 载荷。
func BuildRunnerPayload(ctx context.Context, svc *Service) (RunnerApplyPayload, error) {
var out RunnerApplyPayload
if svc == nil {
return out, fmt.Errorf("sysadmin: nil service")
}
get := func(key string) string {
v, _ := svc.ResolveRawString(ctx, key)
return strings.TrimSpace(v)
}
out.Minimax.Endpoint = get("agent.minimax_endpoint_url")
out.Minimax.ModelID = get("agent.minimax_model_id")
out.Minimax.APIType = get("agent.minimax_api_type")
out.Minimax.APIKey = get("secrets.minimax_api_key")
out.PIModel = get("agent.pi_agent_model")
rawKeys := get("secrets.tavily_api_keys")
if rawKeys != "" {
for _, part := range strings.FieldsFunc(rawKeys, func(r rune) bool {
return r == ',' || r == ';' || r == '\n' || r == ' '
}) {
if part != "" {
out.TavilyKeys = append(out.TavilyKeys, part)
}
}
}
return out, nil
}