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)
|
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)
|
provider, err := providers.CreateProvider(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error creating provider: %v\n", err)
|
fmt.Printf("Error creating provider: %v\n", err)
|
||||||
|
|
@ -585,6 +591,11 @@ func gatewayCmd() {
|
||||||
fmt.Println("✓ Gateway stopped")
|
fmt.Println("✓ Gateway stopped")
|
||||||
|
|
||||||
if restart {
|
if restart {
|
||||||
|
execRestart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func execRestart() {
|
||||||
exe, err := os.Executable()
|
exe, err := os.Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error finding executable: %v\n", err)
|
fmt.Printf("Error finding executable: %v\n", err)
|
||||||
|
|
@ -594,6 +605,70 @@ func gatewayCmd() {
|
||||||
fmt.Printf("Error restarting: %v\n", err)
|
fmt.Printf("Error restarting: %v\n", err)
|
||||||
os.Exit(1)
|
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")
|
logger.InfoC("gateway", "Initial config created via setup wizard")
|
||||||
writeJSON(w, http.StatusOK, map[string]string{"status": "ok"})
|
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.
|
// handleSetupComplete merges additional settings into an existing config.json.
|
||||||
// PUT /api/setup/complete — authentication required.
|
// PUT /api/setup/complete — authentication required.
|
||||||
func (s *Server) handleSetupComplete(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) handleSetupComplete(w http.ResponseWriter, r *http.Request) {
|
||||||
s.cfg.RLock()
|
// Read current config from disk (includes init step's gateway settings)
|
||||||
currentData, err := json.Marshal(s.cfg)
|
// rather than s.cfg which may still be DefaultConfig in setup mode.
|
||||||
s.cfg.RUnlock()
|
currentData, err := os.ReadFile(s.configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeJSONError(w, http.StatusInternalServerError, "failed to read current config")
|
writeJSONError(w, http.StatusInternalServerError, "failed to read config file")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue