Skip to content

Commit aa0d1db

Browse files
committed
feat: smart re-launch + Ctrl+C child process cleanup
- Skip config/model selection if HPP already configured (no --model flag) - Show "HPP already configured (model: ...)" on re-launch - Reconfigure only when --model flag is provided - Kill gateway child process on Ctrl+C (Windows zombie fix) - Gateway: skip start if already running, restart only if Telegram changed
1 parent 2e22c1d commit aa0d1db

1 file changed

Lines changed: 135 additions & 40 deletions

File tree

internal/openclaw/launch.go

Lines changed: 135 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"os"
88
"os/exec"
9+
"os/signal"
910
"path/filepath"
1011
"runtime"
1112
"strings"
@@ -67,42 +68,49 @@ func Launch(modelFlag string, configOnly bool, hubURL string) error {
6768
fmt.Printf(" ✓ API key: ...%s\n", suffix)
6869
}
6970

70-
// Step 3: Model selection
71-
selectedModel := modelFlag
72-
if selectedModel == "" {
73-
models, err := api.ListModels(cfg.BaseURL, cfg.APIKey)
74-
if err != nil {
75-
return fmt.Errorf("failed to list models: %w", err)
76-
}
77-
if len(models) == 0 {
78-
return fmt.Errorf("no models available")
79-
}
71+
// Step 3: Check if already configured
72+
alreadyConfigured := isHPPConfigured()
8073

81-
// Filter text models (exclude image models)
82-
var textModels []api.Model
83-
for _, m := range models {
84-
if !strings.Contains(m.ID, "dall-e") && !strings.Contains(m.ID, "image") {
85-
textModels = append(textModels, m)
74+
if alreadyConfigured && modelFlag == "" {
75+
// Already set up, no model change requested
76+
currentModel := getCurrentModel()
77+
fmt.Printf(" ✓ HPP already configured (model: %s)\n", currentModel)
78+
} else {
79+
// Need to configure or reconfigure
80+
selectedModel := modelFlag
81+
if selectedModel == "" {
82+
models, err := api.ListModels(cfg.BaseURL, cfg.APIKey)
83+
if err != nil {
84+
return fmt.Errorf("failed to list models: %w", err)
85+
}
86+
if len(models) == 0 {
87+
return fmt.Errorf("no models available")
8688
}
87-
}
8889

89-
selectedModel = selectModel(textModels)
90-
if selectedModel == "" {
91-
return fmt.Errorf("no model selected")
90+
// Filter text models (exclude image models)
91+
var textModels []api.Model
92+
for _, m := range models {
93+
if !strings.Contains(m.ID, "dall-e") && !strings.Contains(m.ID, "image") {
94+
textModels = append(textModels, m)
95+
}
96+
}
97+
98+
selectedModel = selectModel(textModels)
99+
if selectedModel == "" {
100+
return fmt.Errorf("no model selected")
101+
}
92102
}
93-
}
94-
fmt.Printf(" ✓ Model: %s\n", selectedModel)
103+
fmt.Printf(" ✓ Model: %s\n", selectedModel)
95104

96-
// Step 4: Configure OpenClaw
97-
fmt.Println(" Configuring OpenClaw...")
98-
if err := configureOpenClaw(cfg, selectedModel); err != nil {
99-
return fmt.Errorf("failed to configure: %w", err)
100-
}
101-
fmt.Println(" ✓ HPP provider configured in OpenClaw")
105+
fmt.Println(" Configuring OpenClaw...")
106+
if err := configureOpenClaw(cfg, selectedModel); err != nil {
107+
return fmt.Errorf("failed to configure: %w", err)
108+
}
109+
fmt.Println(" ✓ HPP provider configured in OpenClaw")
102110

103-
// Validate config
104-
if err := validateOpenClawConfig(); err != nil {
105-
fmt.Printf(" ⚠ Config validation: %s\n", err)
111+
if err := validateOpenClawConfig(); err != nil {
112+
fmt.Printf(" ⚠ Config validation: %s\n", err)
113+
}
106114
}
107115

108116
if configOnly {
@@ -112,24 +120,40 @@ func Launch(modelFlag string, configOnly bool, hubURL string) error {
112120
}
113121

114122
// Step 5: Ask about Telegram setup (before starting gateway)
115-
if !isTelegramConfigured() {
123+
telegramWasConfigured := isTelegramConfigured()
124+
if !telegramWasConfigured {
116125
fmt.Println()
117126
if promptYesNo(" Set up Telegram bot?") {
118127
SetupTelegram()
119128
}
120129
}
130+
telegramChanged := !telegramWasConfigured && isTelegramConfigured()
121131

122-
// Step 6: Start OpenClaw
123-
fmt.Println(" Starting OpenClaw gateway...")
124-
if err := startOpenClaw(); err != nil {
125-
fmt.Printf(" ⚠ Failed to start gateway: %s\n", err)
126-
if runtime.GOOS == "windows" {
127-
fmt.Println(" Start manually in a new terminal: openclaw gateway")
132+
// Step 6: Start or restart Gateway
133+
if isGatewayRunning() {
134+
if telegramChanged {
135+
fmt.Println(" Restarting gateway to apply Telegram settings...")
136+
if runtime.GOOS == "windows" {
137+
fmt.Println(" ⚠ Please restart the gateway manually (Ctrl+C in gateway terminal, then run 'openclaw gateway' again)")
138+
} else {
139+
_ = RunCommand("gateway", "restart")
140+
fmt.Println(" ✓ Gateway restarted")
141+
}
128142
} else {
129-
fmt.Println(" Start manually: openclaw gateway start")
143+
fmt.Println(" ✓ Gateway already running")
130144
}
131145
} else {
132-
fmt.Println(" ✓ OpenClaw gateway running")
146+
fmt.Println(" Starting OpenClaw gateway...")
147+
if err := startOpenClaw(); err != nil {
148+
fmt.Printf(" ⚠ Failed to start gateway: %s\n", err)
149+
if runtime.GOOS == "windows" {
150+
fmt.Println(" Start manually in a new terminal: openclaw gateway")
151+
} else {
152+
fmt.Println(" Start manually: openclaw gateway start")
153+
}
154+
} else {
155+
fmt.Println(" ✓ OpenClaw gateway running")
156+
}
133157
}
134158

135159
fmt.Println()
@@ -368,12 +392,37 @@ func validateOpenClawConfig() error {
368392
func startOpenClaw() error {
369393
if runtime.GOOS == "windows" {
370394
// Windows: run gateway in foreground (daemon not supported)
395+
// Ensure child process is killed on Ctrl+C
371396
fmt.Println(" Starting gateway in foreground (press Ctrl+C to stop)...")
372397
cmd := exec.Command("openclaw", "gateway")
373398
cmd.Stdin = os.Stdin
374399
cmd.Stdout = os.Stdout
375400
cmd.Stderr = os.Stderr
376-
return cmd.Run()
401+
402+
if err := cmd.Start(); err != nil {
403+
return err
404+
}
405+
406+
// Handle Ctrl+C — kill child process
407+
sigChan := make(chan os.Signal, 1)
408+
signal.Notify(sigChan, os.Interrupt)
409+
410+
done := make(chan error, 1)
411+
go func() {
412+
done <- cmd.Wait()
413+
}()
414+
415+
select {
416+
case <-sigChan:
417+
// Ctrl+C received — kill child process
418+
if cmd.Process != nil {
419+
cmd.Process.Kill()
420+
}
421+
fmt.Println("\n Gateway stopped.")
422+
return nil
423+
case err := <-done:
424+
return err
425+
}
377426
}
378427
// macOS/Linux: start as daemon
379428
cmd := exec.Command("openclaw", "gateway", "start")
@@ -395,6 +444,52 @@ func isWSL() bool {
395444
return strings.Contains(lower, "microsoft") || strings.Contains(lower, "wsl")
396445
}
397446

447+
// isHPPConfigured checks if HPP provider is already set up in OpenClaw config
448+
func isHPPConfigured() bool {
449+
home, _ := os.UserHomeDir()
450+
configPath := filepath.Join(home, ".openclaw", "openclaw.json")
451+
data, err := os.ReadFile(configPath)
452+
if err != nil {
453+
return false
454+
}
455+
var cfg map[string]interface{}
456+
if json.Unmarshal(data, &cfg) != nil {
457+
return false
458+
}
459+
models, _ := cfg["models"].(map[string]interface{})
460+
providers, _ := models["providers"].(map[string]interface{})
461+
hpp, _ := providers["hpp"].(map[string]interface{})
462+
return hpp["apiKey"] != nil
463+
}
464+
465+
// getCurrentModel returns the current default model from OpenClaw config
466+
func getCurrentModel() string {
467+
home, _ := os.UserHomeDir()
468+
configPath := filepath.Join(home, ".openclaw", "openclaw.json")
469+
data, err := os.ReadFile(configPath)
470+
if err != nil {
471+
return "unknown"
472+
}
473+
var cfg map[string]interface{}
474+
if json.Unmarshal(data, &cfg) != nil {
475+
return "unknown"
476+
}
477+
agents, _ := cfg["agents"].(map[string]interface{})
478+
defaults, _ := agents["defaults"].(map[string]interface{})
479+
model, _ := defaults["model"].(map[string]interface{})
480+
primary, _ := model["primary"].(string)
481+
if primary == "" {
482+
return "unknown"
483+
}
484+
return primary
485+
}
486+
487+
// isGatewayRunning checks if OpenClaw gateway is already running
488+
func isGatewayRunning() bool {
489+
cmd := exec.Command("openclaw", "health")
490+
return cmd.Run() == nil
491+
}
492+
398493
// isTelegramConfigured checks if Telegram is already set up in OpenClaw config
399494
func isTelegramConfigured() bool {
400495
home, _ := os.UserHomeDir()

0 commit comments

Comments
 (0)