diff --git a/internal/tools/openclaw.go b/internal/tools/openclaw.go index 1c1aec4..6dcf099 100644 --- a/internal/tools/openclaw.go +++ b/internal/tools/openclaw.go @@ -129,7 +129,7 @@ func writeOpenClawViaCLISequential(providerBlock map[string]any, modelsCatalog m providerName + "/" + reg.Coding().Slug, providerName + "/" + reg.Sub().Slug, }) - cmd = exec.Command("openclaw", "config", "set", "agents.defaults.model.fallbacks", string(fallbacksJSON)) + cmd = exec.Command("openclaw", "config", "set", "--merge", "agents.defaults.model.fallbacks", string(fallbacksJSON)) if out, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("openclaw config set agents.defaults.model.fallbacks: %s: %w", string(out), err) } @@ -139,7 +139,7 @@ func writeOpenClawViaCLISequential(providerBlock map[string]any, modelsCatalog m if err != nil { return fmt.Errorf("marshal models catalog: %w", err) } - cmd = exec.Command("openclaw", "config", "set", "agents.defaults.models", string(modelsJSON)) + cmd = exec.Command("openclaw", "config", "set", "--merge", "agents.defaults.models", string(modelsJSON)) if out, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("openclaw config set agents.defaults.models: %s: %w", string(out), err) } @@ -177,15 +177,10 @@ func writeOpenClawViaCLI(apiKey string, reg *models.Registry) error { // Check OpenClaw version to determine which approach to use version := getOpenClawVersion() if isBatchJSONSupported(version) { - // Use --batch-json to set all config in a single CLI call (~3s vs ~12s sequential). + // Use --batch-json for the fields that don't conflict with existing user config. batchOps := []map[string]any{ {"path": "models.providers.kimchi", "value": providerBlock}, {"path": "agents.defaults.model.primary", "value": providerName + "/" + reg.Main().Slug}, - {"path": "agents.defaults.model.fallbacks", "value": []string{ - providerName + "/" + reg.Coding().Slug, - providerName + "/" + reg.Sub().Slug, - }}, - {"path": "agents.defaults.models", "value": modelsCatalog}, } batchJSON, err := json.Marshal(batchOps) @@ -197,6 +192,26 @@ func writeOpenClawViaCLI(apiKey string, reg *models.Registry) error { if out, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("openclaw config set: %s: %w", string(out), err) } + + // Merge fallbacks so existing entries are preserved. + fallbacksJSON, _ := json.Marshal([]string{ + providerName + "/" + reg.Coding().Slug, + providerName + "/" + reg.Sub().Slug, + }) + cmd = exec.Command("openclaw", "config", "set", "--merge", "agents.defaults.model.fallbacks", string(fallbacksJSON)) + if out, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("openclaw config set --merge agents.defaults.model.fallbacks: %s: %w", string(out), err) + } + + // Merge models catalog so existing entries are preserved. + modelsJSON, err := json.Marshal(modelsCatalog) + if err != nil { + return fmt.Errorf("marshal models catalog: %w", err) + } + cmd = exec.Command("openclaw", "config", "set", "--merge", "agents.defaults.models", string(modelsJSON)) + if out, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("openclaw config set --merge agents.defaults.models: %s: %w", string(out), err) + } } else { // Fall back to sequential calls for older versions if err := writeOpenClawViaCLISequential(providerBlock, modelsCatalog, reg); err != nil { @@ -310,13 +325,25 @@ func writeOpenClawDirect(scope config.ConfigScope, apiKey string, reg *models.Re if defaults == nil { defaults = make(map[string]any) } - defaults["model"] = map[string]any{ - "primary": providerName + "/" + reg.Main().Slug, - "fallbacks": []any{providerName + "/" + reg.Coding().Slug, providerName + "/" + reg.Sub().Slug}, + // Merge into model config to preserve existing keys like temperature, max_tokens. + modelMap, _ := defaults["model"].(map[string]any) + if modelMap == nil { + modelMap = make(map[string]any) + } + modelMap["primary"] = providerName + "/" + reg.Main().Slug + // Merge fallbacks so existing entries are preserved. + existingFallbacks, _ := modelMap["fallbacks"].([]any) + modelMap["fallbacks"] = append(existingFallbacks, + providerName+"/"+reg.Coding().Slug, + providerName+"/"+reg.Sub().Slug, + ) + defaults["model"] = modelMap + + // Merge into the allowed models catalog, preserving existing entries. + modelsCatalog, _ := defaults["models"].(map[string]any) + if modelsCatalog == nil { + modelsCatalog = make(map[string]any) } - - // Add models to the allowed models catalog. - modelsCatalog := make(map[string]any) for _, m := range reg.All() { modelsCatalog[providerName+"/"+m.Slug] = map[string]any{"alias": m.DisplayName} } diff --git a/scripts/install.sh b/scripts/install.sh index f17cff2..13f4a56 100644 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -74,4 +74,11 @@ echo -e "${GREEN}✓ Installed kimchi to ${INSTALL_PATH}${NC}" echo "" echo -e "${BLUE}Launching Kimchi...${NC}" echo "" -exec "$INSTALL_PATH" +if [ -t 0 ]; then + exec "$INSTALL_PATH" +elif [ -c /dev/tty ]; then + exec "$INSTALL_PATH" < /dev/tty +else + echo "" + echo -e "${BLUE}Run 'kimchi' to get started.${NC}" +fi