From 81e854eb1dafefd6af85a3f95a1e9a6920d05f0d Mon Sep 17 00:00:00 2001 From: Emil Date: Mon, 3 Aug 2026 14:26:17 +0300 Subject: [PATCH] Reopen the already-running setup wizard instead of failing on a busy port --- coordinator/cmd/worker-agent/main.go | 33 ++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/coordinator/cmd/worker-agent/main.go b/coordinator/cmd/worker-agent/main.go index 06e8d07..7d002d7 100644 --- a/coordinator/cmd/worker-agent/main.go +++ b/coordinator/cmd/worker-agent/main.go @@ -10,11 +10,13 @@ import ( "errors" "flag" "fmt" + "io" "log/slog" "net/http" "os" "os/exec" "os/signal" + "strings" "syscall" "time" @@ -166,6 +168,16 @@ func runSetup(args []string) int { }) listener, err := server.Listen() if err != nil { + // A wizard may already be running on this port (left open, or a + // second terminal). If it is ours, opening the browser is the + // friendlier outcome than failing the command. + if existing := wizardAlreadyRunning(*port); existing != "" { + logger.Info("the setup wizard is already running", "url", existing) + if !*noOpen { + openBrowser(existing) + } + return 0 + } logger.Error("setup wizard could not bind the loopback port", "err", err) return 1 } @@ -199,3 +211,24 @@ func openBrowser(url string) { return } } + +// wizardAlreadyRunning probes the requested loopback port and returns its URL +// when it serves the setup wizard page, or "" when it does not (another +// process, or nothing at all). +func wizardAlreadyRunning(port int) string { + url := fmt.Sprintf("http://127.0.0.1:%d/", port) + client := http.Client{Timeout: 2 * time.Second} + resp, err := client.Get(url) + if err != nil { + return "" + } + defer func() { _ = resp.Body.Close() }() + if resp.StatusCode != http.StatusOK { + return "" + } + body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<16)) + if err != nil || !strings.Contains(string(body), "SciMesh Worker ยท Setup") { + return "" + } + return url +}