fix(launcher): detect and display externally-managed gateway as running
TryAutoStartGateway only checked gateway.cmd, which tracks processes the
launcher itself spawned. A gateway managed externally (e.g. via systemd)
was invisible to this check, causing two problems:
1. The launcher started a duplicate gateway instance on every launch.
2. The WebUI showed "Gateway Not Running" even when it was healthy.
Fix: probe the gateway health endpoint in two places:
- TryAutoStartGateway: skip auto-start if the health endpoint responds.
- gatewayStatusData: report "running" when the launcher has no owned
process but the health endpoint is responding. Launcher-owned
transition states (restarting/error) take precedence over the probe.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
249a63be09
commit
afa7fe3f69
1 changed files with 36 additions and 1 deletions
|
|
@ -72,6 +72,13 @@ func (h *Handler) TryAutoStartGateway() {
|
|||
gateway.cmd = nil
|
||||
}
|
||||
|
||||
// Check whether a gateway is already running externally (e.g. via systemd).
|
||||
// If the health endpoint responds we skip auto-start to avoid a duplicate.
|
||||
if h.isGatewayHealthy() {
|
||||
log.Printf("Skip auto-starting gateway: already running externally")
|
||||
return
|
||||
}
|
||||
|
||||
ready, reason, err := h.gatewayStartReady()
|
||||
if err != nil {
|
||||
log.Printf("Skip auto-starting gateway: %v", err)
|
||||
|
|
@ -117,6 +124,27 @@ func (h *Handler) gatewayStartReady() (bool, string, error) {
|
|||
return true, "", nil
|
||||
}
|
||||
|
||||
// isGatewayHealthy probes the gateway health endpoint and returns true if it
|
||||
// responds with HTTP 200. Used to detect an externally-managed gateway process.
|
||||
func (h *Handler) isGatewayHealthy() bool {
|
||||
cfg, err := config.LoadConfig(h.configPath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
host := gatewayProbeHost(h.effectiveGatewayBindHost(cfg))
|
||||
port := cfg.Gateway.Port
|
||||
if port == 0 {
|
||||
port = 18790
|
||||
}
|
||||
url := fmt.Sprintf("http://%s/health", net.JoinHostPort(host, strconv.Itoa(port)))
|
||||
resp, err := gatewayHealthGet(url, 1*time.Second)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
resp.Body.Close()
|
||||
return resp.StatusCode == http.StatusOK
|
||||
}
|
||||
|
||||
func lookupModelConfig(cfg *config.Config, modelName string) *config.ModelConfig {
|
||||
modelCfg, err := cfg.GetModelConfig(modelName)
|
||||
if err != nil {
|
||||
|
|
@ -596,8 +624,15 @@ func (h *Handler) gatewayStatusData() map[string]any {
|
|||
|
||||
if !processAlive {
|
||||
gateway.mu.Lock()
|
||||
data["gateway_status"] = currentGatewayStatusLocked(false)
|
||||
launcherStatus := currentGatewayStatusLocked(false)
|
||||
gateway.mu.Unlock()
|
||||
// Only probe for an external gateway when the launcher has no in-progress
|
||||
// state (restarting/error). Those states take precedence.
|
||||
if launcherStatus == "stopped" && h.isGatewayHealthy() {
|
||||
data["gateway_status"] = "running"
|
||||
} else {
|
||||
data["gateway_status"] = launcherStatus
|
||||
}
|
||||
} else {
|
||||
// Process is alive — probe its health endpoint
|
||||
host := "127.0.0.1"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue