fix: separate gateway setup mode to prevent crash when config.json missing
When config.json doesn't exist, gateway now starts in minimal setup mode (HTTP + WebSocket only) instead of crashing on CreateProvider with empty LLM.Model. Step1 (init) no longer triggers restart — only Step5 (complete) does. handleSetupComplete now reads config from disk instead of in-memory to preserve gateway settings written by handleSetupInit. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4a899b92c2
commit
dd2390f8d5
2 changed files with 87 additions and 19 deletions
|
|
@ -451,6 +451,12 @@ func gatewayCmd() {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
// If config.json does not exist, start in setup mode (minimal gateway)
|
||||
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
||||
gatewaySetupMode(cfg, configPath)
|
||||
return
|
||||
}
|
||||
|
||||
provider, err := providers.CreateProvider(cfg)
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating provider: %v\n", err)
|
||||
|
|
@ -585,6 +591,11 @@ func gatewayCmd() {
|
|||
fmt.Println("✓ Gateway stopped")
|
||||
|
||||
if restart {
|
||||
execRestart()
|
||||
}
|
||||
}
|
||||
|
||||
func execRestart() {
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
fmt.Printf("Error finding executable: %v\n", err)
|
||||
|
|
@ -594,6 +605,70 @@ func gatewayCmd() {
|
|||
fmt.Printf("Error restarting: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// gatewaySetupMode starts a minimal gateway with only HTTP + WebSocket.
|
||||
// Provider, AgentLoop, CronService, and HeartbeatService are not started.
|
||||
func gatewaySetupMode(cfg *config.Config, configPath string) {
|
||||
fmt.Println("\n⚙ Starting in setup mode (config.json not found)")
|
||||
|
||||
restartCh := make(chan struct{}, 1)
|
||||
|
||||
gwServer := gateway.NewServer(cfg, configPath, func() {
|
||||
select {
|
||||
case restartCh <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
})
|
||||
if err := gwServer.Start(); err != nil {
|
||||
fmt.Printf("Error starting gateway HTTP server: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("✓ Config API started on 127.0.0.1:%d\n", cfg.Gateway.Port)
|
||||
|
||||
msgBus := bus.NewMessageBus()
|
||||
channelManager, err := channels.NewManager(cfg, msgBus, configPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating channel manager: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
if err := channelManager.StartAll(ctx); err != nil {
|
||||
fmt.Printf("Error starting channels: %v\n", err)
|
||||
}
|
||||
|
||||
enabledChannels := channelManager.GetEnabledChannels()
|
||||
if len(enabledChannels) > 0 {
|
||||
fmt.Printf("✓ Channels enabled: %s\n", enabledChannels)
|
||||
}
|
||||
|
||||
fmt.Println("✓ Setup mode ready — waiting for setup wizard")
|
||||
fmt.Println("Press Ctrl+C to stop")
|
||||
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, os.Interrupt)
|
||||
|
||||
restart := false
|
||||
select {
|
||||
case <-sigChan:
|
||||
case <-restartCh:
|
||||
restart = true
|
||||
fmt.Println("\nRestarting after setup complete...")
|
||||
}
|
||||
|
||||
fmt.Println("\nShutting down...")
|
||||
cancel()
|
||||
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer shutdownCancel()
|
||||
gwServer.Stop(shutdownCtx)
|
||||
channelManager.StopAll(shutdownCtx)
|
||||
fmt.Println("✓ Gateway stopped")
|
||||
|
||||
if restart {
|
||||
execRestart()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,23 +51,16 @@ func (s *Server) handleSetupInit(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
logger.InfoC("gateway", "Initial config created via setup wizard")
|
||||
writeJSON(w, http.StatusOK, map[string]string{"status": "ok"})
|
||||
|
||||
if s.onRestart != nil {
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
s.onRestart()
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// handleSetupComplete merges additional settings into an existing config.json.
|
||||
// PUT /api/setup/complete — authentication required.
|
||||
func (s *Server) handleSetupComplete(w http.ResponseWriter, r *http.Request) {
|
||||
s.cfg.RLock()
|
||||
currentData, err := json.Marshal(s.cfg)
|
||||
s.cfg.RUnlock()
|
||||
// Read current config from disk (includes init step's gateway settings)
|
||||
// rather than s.cfg which may still be DefaultConfig in setup mode.
|
||||
currentData, err := os.ReadFile(s.configPath)
|
||||
if err != nil {
|
||||
writeJSONError(w, http.StatusInternalServerError, "failed to read current config")
|
||||
writeJSONError(w, http.StatusInternalServerError, "failed to read config file")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue