Persist agent/secrets settings with masked secrets and runner hot-reload; add maintenance windows, maintpage binary, and zero-downtime listener handoff.
91 lines
2.6 KiB
Go
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
|
|
}
|