mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-04-12 11:37:29 +08:00
642 lines
17 KiB
Go
642 lines
17 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/YspCoder/clawgo/pkg/config"
|
|
"github.com/YspCoder/clawgo/pkg/configops"
|
|
"github.com/YspCoder/clawgo/pkg/providers"
|
|
)
|
|
|
|
func configCmd() {
|
|
if len(os.Args) < 3 {
|
|
configHelp()
|
|
return
|
|
}
|
|
|
|
switch os.Args[2] {
|
|
case "set":
|
|
configSetCmd()
|
|
case "get":
|
|
configGetCmd()
|
|
case "check":
|
|
configCheckCmd()
|
|
case "reload":
|
|
configReloadCmd()
|
|
default:
|
|
fmt.Printf("Unknown config command: %s\n", os.Args[2])
|
|
configHelp()
|
|
}
|
|
}
|
|
|
|
func configHelp() {
|
|
fmt.Println("\nConfig commands:")
|
|
fmt.Println(" set <path> <value> Set config value and trigger hot reload")
|
|
fmt.Println(" get <path> Get config value")
|
|
fmt.Println(" check Validate current config")
|
|
fmt.Println(" reload Trigger gateway hot reload")
|
|
fmt.Println()
|
|
fmt.Println("Examples:")
|
|
fmt.Println(" clawgo config set channels.telegram.enabled true")
|
|
fmt.Println(" clawgo config set channels.telegram.enable true")
|
|
fmt.Println(" clawgo config get models.providers.openai.api_base")
|
|
fmt.Println(" clawgo config check")
|
|
fmt.Println(" clawgo config reload")
|
|
}
|
|
|
|
func configSetCmd() {
|
|
if len(os.Args) < 5 {
|
|
fmt.Println("Usage: clawgo config set <path> <value>")
|
|
return
|
|
}
|
|
|
|
configPath := getConfigPath()
|
|
cfgMap, err := loadConfigAsMap(configPath)
|
|
if err != nil {
|
|
fmt.Printf("Error loading config: %v\n", err)
|
|
return
|
|
}
|
|
|
|
path := normalizeConfigPath(os.Args[3])
|
|
args := os.Args[4:]
|
|
valueParts := make([]string, 0, len(args))
|
|
for i := 0; i < len(args); i++ {
|
|
part := args[i]
|
|
if part == "--debug" || part == "-d" {
|
|
continue
|
|
}
|
|
if part == "--config" {
|
|
i++
|
|
continue
|
|
}
|
|
if strings.HasPrefix(part, "--config=") {
|
|
continue
|
|
}
|
|
valueParts = append(valueParts, part)
|
|
}
|
|
if len(valueParts) == 0 {
|
|
fmt.Println("Error: value is required")
|
|
return
|
|
}
|
|
value := parseConfigValue(strings.Join(valueParts, " "))
|
|
if err := setMapValueByPath(cfgMap, path, value); err != nil {
|
|
fmt.Printf("Error setting value: %v\n", err)
|
|
return
|
|
}
|
|
|
|
data, err := json.MarshalIndent(cfgMap, "", " ")
|
|
if err != nil {
|
|
fmt.Printf("Error serializing config: %v\n", err)
|
|
return
|
|
}
|
|
backupPath, err := writeConfigAtomicWithBackup(configPath, data)
|
|
if err != nil {
|
|
fmt.Printf("Error writing config: %v\n", err)
|
|
return
|
|
}
|
|
|
|
fmt.Printf("Updated %s = %v\n", path, value)
|
|
running, err := triggerGatewayReload()
|
|
if err != nil {
|
|
if running {
|
|
if rbErr := rollbackConfigFromBackup(configPath, backupPath); rbErr != nil {
|
|
fmt.Printf("Hot reload failed and rollback failed: %v\n", rbErr)
|
|
} else {
|
|
fmt.Printf("Hot reload failed, config rolled back: %v\n", err)
|
|
}
|
|
return
|
|
}
|
|
fmt.Printf("Updated config file. Hot reload not applied: %v\n", err)
|
|
} else {
|
|
fmt.Println("Gateway hot reload signal sent")
|
|
}
|
|
}
|
|
|
|
func configGetCmd() {
|
|
if len(os.Args) < 4 {
|
|
fmt.Println("Usage: clawgo config get <path>")
|
|
return
|
|
}
|
|
|
|
configPath := getConfigPath()
|
|
cfgMap, err := loadConfigAsMap(configPath)
|
|
if err != nil {
|
|
fmt.Printf("Error loading config: %v\n", err)
|
|
return
|
|
}
|
|
|
|
path := normalizeConfigPath(os.Args[3])
|
|
value, ok := getMapValueByPath(cfgMap, path)
|
|
if !ok {
|
|
fmt.Printf("Path not found: %s\n", path)
|
|
return
|
|
}
|
|
|
|
data, err := json.Marshal(value)
|
|
if err != nil {
|
|
fmt.Printf("%v\n", value)
|
|
return
|
|
}
|
|
fmt.Println(string(data))
|
|
}
|
|
|
|
func configReloadCmd() {
|
|
if _, err := triggerGatewayReload(); err != nil {
|
|
fmt.Printf("Hot reload not applied: %v\n", err)
|
|
return
|
|
}
|
|
fmt.Println("Gateway hot reload signal sent")
|
|
}
|
|
|
|
func configCheckCmd() {
|
|
cfg, err := config.LoadConfig(getConfigPath())
|
|
if err != nil {
|
|
fmt.Printf("Config load failed: %v\n", err)
|
|
return
|
|
}
|
|
validationErrors := config.Validate(cfg)
|
|
if len(validationErrors) == 0 {
|
|
fmt.Println("Config validation passed")
|
|
return
|
|
}
|
|
|
|
fmt.Println("Config validation failed:")
|
|
for _, ve := range validationErrors {
|
|
fmt.Printf(" - %v\n", ve)
|
|
}
|
|
}
|
|
|
|
func providerCmd() {
|
|
if len(os.Args) >= 3 {
|
|
switch strings.TrimSpace(os.Args[2]) {
|
|
case "login":
|
|
providerLoginCmd()
|
|
return
|
|
case "list":
|
|
providerListCmd()
|
|
return
|
|
case "use":
|
|
providerUseCmd()
|
|
return
|
|
case "configure":
|
|
// Continue into the interactive editor below.
|
|
}
|
|
}
|
|
|
|
cfg, err := loadConfig()
|
|
if err != nil {
|
|
fmt.Printf("Error loading config: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
reader := bufio.NewReader(os.Stdin)
|
|
defaultProvider, defaultModel := config.ParseProviderModelRef(cfg.Agents.Defaults.Model.Primary)
|
|
if defaultProvider == "" {
|
|
defaultProvider = config.PrimaryProviderName(cfg)
|
|
}
|
|
available := providerNames(cfg)
|
|
fmt.Printf("Current primary provider: %s\n", defaultProvider)
|
|
if defaultModel != "" {
|
|
fmt.Printf("Current primary model: %s\n", defaultModel)
|
|
}
|
|
fmt.Printf("Available providers: %s\n", strings.Join(available, ", "))
|
|
|
|
argName := ""
|
|
if len(os.Args) >= 3 {
|
|
argName = strings.TrimSpace(os.Args[2])
|
|
}
|
|
if argName == "" || strings.HasPrefix(argName, "-") {
|
|
argName = defaultProvider
|
|
}
|
|
providerName := promptLine(reader, "Provider name to configure", argName)
|
|
if providerName == "" {
|
|
providerName = defaultProvider
|
|
}
|
|
|
|
pc := providerConfigByName(cfg, providerName)
|
|
if pc.TimeoutSec <= 0 {
|
|
pc.TimeoutSec = 90
|
|
}
|
|
if strings.TrimSpace(pc.Auth) == "" {
|
|
pc.Auth = "bearer"
|
|
}
|
|
if len(pc.Models) == 0 {
|
|
pc.Models = []string{"gpt-5.4"}
|
|
}
|
|
|
|
pc.APIBase = promptLine(reader, "api_base", pc.APIBase)
|
|
apiKey := promptLine(reader, "api_key (leave empty to keep current)", "")
|
|
if apiKey != "" {
|
|
pc.APIKey = apiKey
|
|
}
|
|
modelsRaw := promptLine(reader, "models (comma-separated)", strings.Join(pc.Models, ","))
|
|
if models := parseCSV(modelsRaw); len(models) > 0 {
|
|
pc.Models = models
|
|
}
|
|
pc.Auth = promptLine(reader, "auth (bearer/oauth/hybrid/none)", pc.Auth)
|
|
timeoutRaw := promptLine(reader, "timeout_sec", fmt.Sprintf("%d", pc.TimeoutSec))
|
|
pc.TimeoutSec = parseIntOrDefault(timeoutRaw, pc.TimeoutSec)
|
|
pc.SupportsResponsesCompact = promptBool(reader, "supports_responses_compact", pc.SupportsResponsesCompact)
|
|
if strings.EqualFold(strings.TrimSpace(pc.Auth), "oauth") || strings.EqualFold(strings.TrimSpace(pc.Auth), "hybrid") {
|
|
pc.OAuth.Provider = promptLine(reader, "oauth.provider", firstNonEmptyString(pc.OAuth.Provider, "codex"))
|
|
pc.OAuth.NetworkProxy = promptLine(reader, "oauth.network_proxy", pc.OAuth.NetworkProxy)
|
|
pc.OAuth.CredentialFile = promptLine(reader, "oauth.credential_file", pc.OAuth.CredentialFile)
|
|
pc.OAuth.CallbackPort = parseIntOrDefault(promptLine(reader, "oauth.callback_port", fmt.Sprintf("%d", defaultInt(pc.OAuth.CallbackPort, 1455))), defaultInt(pc.OAuth.CallbackPort, 1455))
|
|
pc.OAuth.CooldownSec = parseIntOrDefault(promptLine(reader, "oauth.cooldown_sec", fmt.Sprintf("%d", defaultInt(pc.OAuth.CooldownSec, 900))), defaultInt(pc.OAuth.CooldownSec, 900))
|
|
}
|
|
|
|
setProviderConfigByName(cfg, providerName, pc)
|
|
|
|
currentPrimaryProvider, currentPrimaryModel := config.ParseProviderModelRef(cfg.Agents.Defaults.Model.Primary)
|
|
makePrimary := promptBool(reader, fmt.Sprintf("Set %s as agents.defaults.model.primary provider", providerName), providerName == currentPrimaryProvider)
|
|
if makePrimary {
|
|
targetModel := currentPrimaryModel
|
|
if targetModel == "" && len(pc.Models) > 0 {
|
|
targetModel = pc.Models[0]
|
|
}
|
|
cfg.Agents.Defaults.Model.Primary = providerName + "/" + targetModel
|
|
}
|
|
|
|
if err := config.SaveConfig(getConfigPath(), cfg); err != nil {
|
|
fmt.Printf("Error saving config: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
fmt.Println("Provider configuration saved.")
|
|
running, err := triggerGatewayReload()
|
|
if err != nil {
|
|
if running {
|
|
fmt.Printf("Hot reload not applied: %v\n", err)
|
|
return
|
|
}
|
|
fmt.Printf("Gateway not running, reload skipped: %v\n", err)
|
|
return
|
|
}
|
|
fmt.Println("Gateway hot reload signal sent")
|
|
}
|
|
|
|
func providerListCmd() {
|
|
cfg, err := loadConfig()
|
|
if err != nil {
|
|
fmt.Printf("Error loading config: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
primary := strings.TrimSpace(cfg.Agents.Defaults.Model.Primary)
|
|
names := providerNames(cfg)
|
|
for _, name := range names {
|
|
pc, _ := config.ProviderConfigByName(cfg, name)
|
|
models := strings.Join(pc.Models, ",")
|
|
if models == "" {
|
|
models = "-"
|
|
}
|
|
marker := " "
|
|
if strings.HasPrefix(primary, name+"/") {
|
|
marker = "*"
|
|
}
|
|
fmt.Printf("%s %s auth=%s models=%s api_base=%s\n", marker, name, strings.TrimSpace(pc.Auth), models, strings.TrimSpace(pc.APIBase))
|
|
}
|
|
if primary != "" {
|
|
fmt.Printf("\nPrimary: %s\n", primary)
|
|
}
|
|
}
|
|
|
|
func providerUseCmd() {
|
|
if len(os.Args) < 4 {
|
|
fmt.Println("Usage: clawgo provider use <provider/model>")
|
|
return
|
|
}
|
|
ref := strings.TrimSpace(os.Args[3])
|
|
providerName, modelName := config.ParseProviderModelRef(ref)
|
|
if providerName == "" || modelName == "" {
|
|
fmt.Println("Error: expected provider/model")
|
|
return
|
|
}
|
|
cfg, err := loadConfig()
|
|
if err != nil {
|
|
fmt.Printf("Error loading config: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
pc, ok := config.ProviderConfigByName(cfg, providerName)
|
|
if !ok {
|
|
fmt.Printf("Error: unknown provider %q\n", providerName)
|
|
os.Exit(1)
|
|
}
|
|
foundModel := false
|
|
for _, candidate := range pc.Models {
|
|
if strings.TrimSpace(candidate) == modelName {
|
|
foundModel = true
|
|
break
|
|
}
|
|
}
|
|
if !foundModel {
|
|
fmt.Printf("Error: model %q not found in provider %q\n", modelName, providerName)
|
|
os.Exit(1)
|
|
}
|
|
cfg.Agents.Defaults.Model.Primary = providerName + "/" + modelName
|
|
if err := config.SaveConfig(getConfigPath(), cfg); err != nil {
|
|
fmt.Printf("Error saving config: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Printf("Primary model set to %s\n", cfg.Agents.Defaults.Model.Primary)
|
|
if running, reloadErr := triggerGatewayReload(); reloadErr == nil {
|
|
fmt.Println("Gateway hot reload signal sent")
|
|
} else if running {
|
|
fmt.Printf("Hot reload not applied: %v\n", reloadErr)
|
|
}
|
|
}
|
|
|
|
func providerLoginCmd() {
|
|
cfg, err := loadConfig()
|
|
if err != nil {
|
|
fmt.Printf("Error loading config: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
providerName, _ := config.ParseProviderModelRef(cfg.Agents.Defaults.Model.Primary)
|
|
if providerName == "" {
|
|
providerName = config.PrimaryProviderName(cfg)
|
|
}
|
|
manual := false
|
|
noBrowser := false
|
|
accountLabel := ""
|
|
networkProxy := ""
|
|
for i := 3; i < len(os.Args); i++ {
|
|
arg := strings.TrimSpace(os.Args[i])
|
|
switch arg {
|
|
case "--manual":
|
|
manual = true
|
|
case "--no-browser":
|
|
noBrowser = true
|
|
case "--label":
|
|
if i+1 < len(os.Args) {
|
|
i++
|
|
accountLabel = strings.TrimSpace(os.Args[i])
|
|
}
|
|
case "--proxy":
|
|
if i+1 < len(os.Args) {
|
|
i++
|
|
networkProxy = strings.TrimSpace(os.Args[i])
|
|
}
|
|
case "":
|
|
default:
|
|
if strings.HasPrefix(arg, "--label=") {
|
|
accountLabel = strings.TrimSpace(strings.TrimPrefix(arg, "--label="))
|
|
continue
|
|
}
|
|
if strings.HasPrefix(arg, "--proxy=") {
|
|
networkProxy = strings.TrimSpace(strings.TrimPrefix(arg, "--proxy="))
|
|
continue
|
|
}
|
|
if !strings.HasPrefix(arg, "-") {
|
|
providerName = arg
|
|
}
|
|
}
|
|
}
|
|
|
|
pc := providerConfigByName(cfg, providerName)
|
|
if !strings.EqualFold(strings.TrimSpace(pc.Auth), "oauth") && !strings.EqualFold(strings.TrimSpace(pc.Auth), "hybrid") {
|
|
fmt.Printf("Provider %s is not configured with auth=oauth/hybrid\n", providerName)
|
|
os.Exit(1)
|
|
}
|
|
if manual {
|
|
noBrowser = true
|
|
}
|
|
if manual && strings.TrimSpace(pc.OAuth.RedirectURL) == "" && pc.OAuth.CallbackPort <= 0 {
|
|
pc.OAuth.CallbackPort = 1455
|
|
}
|
|
if strings.TrimSpace(networkProxy) == "" {
|
|
networkProxy = strings.TrimSpace(pc.OAuth.NetworkProxy)
|
|
}
|
|
|
|
timeout := pc.TimeoutSec
|
|
if timeout <= 0 {
|
|
timeout = 90
|
|
}
|
|
oauth, err := providers.NewOAuthLoginManager(pc, time.Duration(timeout)*time.Second)
|
|
if err != nil {
|
|
fmt.Printf("Error preparing oauth login: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
|
defer cancel()
|
|
session, models, err := oauth.Login(ctx, pc.APIBase, providers.OAuthLoginOptions{
|
|
Manual: manual,
|
|
NoBrowser: noBrowser,
|
|
Reader: os.Stdin,
|
|
AccountLabel: accountLabel,
|
|
NetworkProxy: networkProxy,
|
|
})
|
|
if err != nil {
|
|
fmt.Printf("OAuth login failed: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
if len(models) > 0 {
|
|
pc.Models = models
|
|
}
|
|
if session.CredentialFile != "" {
|
|
pc.OAuth.CredentialFile = session.CredentialFile
|
|
pc.OAuth.CredentialFiles = appendUniqueCSV(pc.OAuth.CredentialFiles, session.CredentialFile)
|
|
} else if pc.OAuth.CredentialFile == "" {
|
|
pc.OAuth.CredentialFile = oauth.CredentialFile()
|
|
pc.OAuth.CredentialFiles = appendUniqueCSV(pc.OAuth.CredentialFiles, pc.OAuth.CredentialFile)
|
|
}
|
|
setProviderConfigByName(cfg, providerName, pc)
|
|
|
|
if err := config.SaveConfig(getConfigPath(), cfg); err != nil {
|
|
fmt.Printf("Error saving config: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
fmt.Printf("OAuth login succeeded for provider %s\n", providerName)
|
|
if manual {
|
|
fmt.Println("Mode: manual callback URL paste")
|
|
} else if noBrowser {
|
|
fmt.Println("Mode: local callback listener without auto-opening browser")
|
|
}
|
|
if session.Email != "" {
|
|
fmt.Printf("Account: %s\n", session.Email)
|
|
}
|
|
if session.NetworkProxy != "" {
|
|
fmt.Printf("Network proxy: %s\n", session.NetworkProxy)
|
|
}
|
|
fmt.Printf("Credential file: %s\n", firstNonEmptyString(session.CredentialFile, oauth.CredentialFile()))
|
|
if len(pc.OAuth.CredentialFiles) > 1 {
|
|
fmt.Printf("OAuth accounts: %d\n", len(pc.OAuth.CredentialFiles))
|
|
}
|
|
if len(models) > 0 {
|
|
fmt.Printf("Discovered models: %s\n", strings.Join(models, ", "))
|
|
}
|
|
if running, reloadErr := triggerGatewayReload(); reloadErr == nil {
|
|
fmt.Println("Gateway hot reload signal sent")
|
|
} else if running {
|
|
fmt.Printf("Hot reload not applied: %v\n", reloadErr)
|
|
}
|
|
}
|
|
|
|
func providerNames(cfg *config.Config) []string {
|
|
names := make([]string, 0, len(cfg.Models.Providers))
|
|
for k := range cfg.Models.Providers {
|
|
k = strings.TrimSpace(k)
|
|
if k == "" {
|
|
continue
|
|
}
|
|
names = append(names, k)
|
|
}
|
|
sort.Strings(names)
|
|
return names
|
|
}
|
|
|
|
func providerConfigByName(cfg *config.Config, name string) config.ProviderConfig {
|
|
if pc, ok := config.ProviderConfigByName(cfg, name); ok {
|
|
return pc
|
|
}
|
|
return config.ProviderConfig{
|
|
TimeoutSec: 90,
|
|
Auth: "bearer",
|
|
Models: []string{"gpt-5.4"},
|
|
}
|
|
}
|
|
|
|
func setProviderConfigByName(cfg *config.Config, name string, pc config.ProviderConfig) {
|
|
name = strings.TrimSpace(name)
|
|
if name == "" {
|
|
return
|
|
}
|
|
if cfg.Models.Providers == nil {
|
|
cfg.Models.Providers = map[string]config.ProviderConfig{}
|
|
}
|
|
cfg.Models.Providers[name] = pc
|
|
}
|
|
|
|
func promptLine(reader *bufio.Reader, label, defaultValue string) string {
|
|
label = strings.TrimSpace(label)
|
|
if defaultValue != "" {
|
|
fmt.Printf("%s [%s]: ", label, defaultValue)
|
|
} else {
|
|
fmt.Printf("%s: ", label)
|
|
}
|
|
line, _ := reader.ReadString('\n')
|
|
line = strings.TrimSpace(line)
|
|
if line == "" {
|
|
return defaultValue
|
|
}
|
|
return line
|
|
}
|
|
|
|
func promptBool(reader *bufio.Reader, label string, defaultValue bool) bool {
|
|
def := "N"
|
|
if defaultValue {
|
|
def = "Y"
|
|
}
|
|
raw := promptLine(reader, label+" (y/n)", def)
|
|
switch strings.ToLower(strings.TrimSpace(raw)) {
|
|
case "y", "yes", "true", "1":
|
|
return true
|
|
case "n", "no", "false", "0":
|
|
return false
|
|
default:
|
|
return defaultValue
|
|
}
|
|
}
|
|
|
|
func parseCSV(raw string) []string {
|
|
parts := strings.Split(raw, ",")
|
|
out := make([]string, 0, len(parts))
|
|
seen := map[string]struct{}{}
|
|
for _, p := range parts {
|
|
v := strings.TrimSpace(p)
|
|
if v == "" {
|
|
continue
|
|
}
|
|
if _, ok := seen[v]; ok {
|
|
continue
|
|
}
|
|
seen[v] = struct{}{}
|
|
out = append(out, v)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func defaultInt(value, fallback int) int {
|
|
if value > 0 {
|
|
return value
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
func firstNonEmptyString(values ...string) string {
|
|
for _, value := range values {
|
|
if trimmed := strings.TrimSpace(value); trimmed != "" {
|
|
return trimmed
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func appendUniqueCSV(values []string, value string) []string {
|
|
trimmed := strings.TrimSpace(value)
|
|
if trimmed == "" {
|
|
return values
|
|
}
|
|
for _, item := range values {
|
|
if strings.TrimSpace(item) == trimmed {
|
|
return values
|
|
}
|
|
}
|
|
return append(values, trimmed)
|
|
}
|
|
|
|
func parseIntOrDefault(raw string, def int) int {
|
|
raw = strings.TrimSpace(raw)
|
|
if raw == "" {
|
|
return def
|
|
}
|
|
v, err := strconv.Atoi(raw)
|
|
if err != nil || v <= 0 {
|
|
return def
|
|
}
|
|
return v
|
|
}
|
|
|
|
func loadConfigAsMap(path string) (map[string]interface{}, error) {
|
|
return configops.LoadConfigAsMap(path)
|
|
}
|
|
|
|
func normalizeConfigPath(path string) string {
|
|
return configops.NormalizeConfigPath(path)
|
|
}
|
|
|
|
func parseConfigValue(raw string) interface{} {
|
|
return configops.ParseConfigValue(raw)
|
|
}
|
|
|
|
func setMapValueByPath(root map[string]interface{}, path string, value interface{}) error {
|
|
return configops.SetMapValueByPath(root, path, value)
|
|
}
|
|
|
|
func getMapValueByPath(root map[string]interface{}, path string) (interface{}, bool) {
|
|
return configops.GetMapValueByPath(root, path)
|
|
}
|
|
|
|
func writeConfigAtomicWithBackup(configPath string, data []byte) (string, error) {
|
|
return configops.WriteConfigAtomicWithBackup(configPath, data)
|
|
}
|
|
|
|
func rollbackConfigFromBackup(configPath, backupPath string) error {
|
|
return configops.RollbackConfigFromBackup(configPath, backupPath)
|
|
}
|
|
|
|
func triggerGatewayReload() (bool, error) {
|
|
return configops.TriggerGatewayReload(getConfigPath(), errGatewayNotRunning)
|
|
}
|