From b858d6c3a91ee94905131a2d41b7ee17608ebd00 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 13 Mar 2026 02:40:25 +0000 Subject: [PATCH] fix: clarify windows onboarding gateway health --- CHANGELOG.md | 1 + docs/cli/onboard.md | 7 +++ docs/platforms/windows.md | 27 ++++++++++++ .../onboard-non-interactive.gateway.test.ts | 43 ++++++++++++++++++- src/commands/onboard-non-interactive/local.ts | 24 ++++++++++- 5 files changed, 100 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f752a13c..1bccd8097 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -118,6 +118,7 @@ Docs: https://docs.openclaw.ai - Windows/install: stop auto-installing `node-llama-cpp` during normal npm CLI installs so `openclaw@latest` no longer fails on Windows while building optional local-embedding dependencies. - Windows/update: mirror the native installer environment during global npm updates, including portable Git fallback and Windows-safe npm shell settings, so `openclaw update` works again on native Windows installs. - Gateway/status: expose `runtimeVersion` in gateway status output so install/update smoke tests can verify the running version before and after updates. +- Windows/onboarding: explain when non-interactive local onboarding is waiting for an already-running gateway, and surface native Scheduled Task admin requirements more clearly instead of failing with an opaque gateway timeout. - Agents/text sanitization: strip leaked model control tokens (`<|...|>` and full-width `<|...|>` variants) from user-facing assistant text, preventing GLM-5 and DeepSeek internal delimiters from reaching end users. (#42173) Thanks @imwyvern. - iOS/gateway foreground recovery: reconnect immediately on foreground return after stale background sockets are torn down, so the app no longer stays disconnected until a later wake path happens. (#41384) Thanks @mbelinky. - Gateway/Control UI: keep dashboard auth tokens in session-scoped browser storage so same-tab refreshes preserve remote token auth without restoring long-lived localStorage token persistence, while scoping tokens to the selected gateway URL and fragment-only bootstrap flow. (#40892) thanks @velvet-shark. diff --git a/docs/cli/onboard.md b/docs/cli/onboard.md index ae62d1036..6eed344ee 100644 --- a/docs/cli/onboard.md +++ b/docs/cli/onboard.md @@ -95,6 +95,13 @@ openclaw onboard --non-interactive \ --accept-risk ``` +Non-interactive local gateway health: + +- Unless you pass `--skip-health`, onboarding waits for a reachable local gateway before it exits successfully. +- `--install-daemon` starts the managed gateway install path first. Without it, you must already have a local gateway running, for example `openclaw gateway run`. +- If you only want config/workspace/bootstrap writes in automation, use `--skip-health`. +- On native Windows, `--install-daemon` currently uses Scheduled Tasks and may require running PowerShell as Administrator. + Interactive onboarding behavior with reference mode: - Choose **Use secret reference** when prompted. diff --git a/docs/platforms/windows.md b/docs/platforms/windows.md index 3ab668ea0..e6c46368f 100644 --- a/docs/platforms/windows.md +++ b/docs/platforms/windows.md @@ -22,6 +22,33 @@ Native Windows companion apps are planned. - [Install & updates](/install/updating) - Official WSL2 guide (Microsoft): [https://learn.microsoft.com/windows/wsl/install](https://learn.microsoft.com/windows/wsl/install) +## Native Windows status + +Native Windows CLI flows are improving, but WSL2 is still the recommended path. + +What works well on native Windows today: + +- website installer via `install.ps1` +- local CLI use such as `openclaw --version`, `openclaw doctor`, and `openclaw plugins list --json` +- embedded local-agent/provider smoke such as: + +```powershell +openclaw agent --local --agent main --thinking low -m "Reply with exactly WINDOWS-HATCH-OK." +``` + +Current caveats: + +- `openclaw onboard --non-interactive` still expects a reachable local gateway unless you pass `--skip-health` +- `openclaw onboard --non-interactive --install-daemon` and `openclaw gateway install` currently use Windows Scheduled Tasks +- on some native Windows setups, Scheduled Task install may require running PowerShell as Administrator + +If you want the native CLI only, without gateway service install, use one of these: + +```powershell +openclaw onboard --non-interactive --skip-health +openclaw gateway run +``` + ## Gateway - [Gateway runbook](/gateway) diff --git a/src/commands/onboard-non-interactive.gateway.test.ts b/src/commands/onboard-non-interactive.gateway.test.ts index c5d29a121..e7ab668ea 100644 --- a/src/commands/onboard-non-interactive.gateway.test.ts +++ b/src/commands/onboard-non-interactive.gateway.test.ts @@ -1,6 +1,6 @@ import fs from "node:fs/promises"; import path from "node:path"; -import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; +import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest"; import { makeTempWorkspace } from "../test-helpers/workspace.js"; import { captureEnv } from "../test-utils/env.js"; import { createThrowingRuntime, readJsonFile } from "./onboard-non-interactive.test-helpers.js"; @@ -13,6 +13,12 @@ const gatewayClientCalls: Array<{ onClose?: (code: number, reason: string) => void; }> = []; const ensureWorkspaceAndSessionsMock = vi.fn(async (..._args: unknown[]) => {}); +let waitForGatewayReachableMock: + | ((params: { url: string; token?: string; password?: string }) => Promise<{ + ok: boolean; + detail?: string; + }>) + | undefined; vi.mock("../gateway/client.js", () => ({ GatewayClient: class { @@ -46,6 +52,10 @@ vi.mock("./onboard-helpers.js", async (importOriginal) => { return { ...actual, ensureWorkspaceAndSessions: ensureWorkspaceAndSessionsMock, + waitForGatewayReachable: (...args: Parameters) => + waitForGatewayReachableMock + ? waitForGatewayReachableMock(args[0]) + : actual.waitForGatewayReachable(...args), }; }); @@ -116,6 +126,10 @@ describe("onboard (non-interactive): gateway and remote auth", () => { envSnapshot.restore(); }); + afterEach(() => { + waitForGatewayReachableMock = undefined; + }); + it("writes gateway token auth into config", async () => { await withStateDir("state-noninteractive-", async (stateDir) => { const token = "tok_test_123"; @@ -302,6 +316,33 @@ describe("onboard (non-interactive): gateway and remote auth", () => { }); }, 60_000); + it("explains local health failure when no daemon was requested", async () => { + await withStateDir("state-local-health-hint-", async (stateDir) => { + waitForGatewayReachableMock = vi.fn(async () => ({ + ok: false, + detail: "socket closed: 1006 abnormal closure", + })); + + await expect( + runNonInteractiveOnboarding( + { + nonInteractive: true, + mode: "local", + workspace: path.join(stateDir, "openclaw"), + authChoice: "skip", + skipSkills: true, + skipHealth: false, + installDaemon: false, + gatewayBind: "loopback", + }, + runtime, + ), + ).rejects.toThrow( + /only waits for an already-running gateway unless you pass --install-daemon[\s\S]*--skip-health/, + ); + }); + }, 60_000); + it("auto-generates token auth when binding LAN and persists the token", async () => { if (process.platform === "win32") { // Windows runner occasionally drops the temp config write in this flow; skip to keep CI green. diff --git a/src/commands/onboard-non-interactive/local.ts b/src/commands/onboard-non-interactive/local.ts index 4e0482ae2..d6292c015 100644 --- a/src/commands/onboard-non-interactive/local.ts +++ b/src/commands/onboard-non-interactive/local.ts @@ -104,11 +104,33 @@ export async function runNonInteractiveOnboardingLocal(params: { customBindHost: nextConfig.gateway?.customBindHost, basePath: undefined, }); - await waitForGatewayReachable({ + const probe = await waitForGatewayReachable({ url: links.wsUrl, token: gatewayResult.gatewayToken, deadlineMs: 15_000, }); + if (!probe.ok) { + const message = [ + `Gateway did not become reachable at ${links.wsUrl}.`, + probe.detail ? `Last probe: ${probe.detail}` : undefined, + !opts.installDaemon + ? [ + "Non-interactive local onboarding only waits for an already-running gateway unless you pass --install-daemon.", + `Fix: start \`${formatCliCommand("openclaw gateway run")}\`, re-run with \`--install-daemon\`, or use \`--skip-health\`.`, + process.platform === "win32" + ? "Native Windows managed gateway install currently uses Scheduled Tasks and may require running PowerShell as Administrator." + : undefined, + ] + .filter(Boolean) + .join("\n") + : undefined, + ] + .filter(Boolean) + .join("\n"); + runtime.error(message); + runtime.exit(1); + return; + } await healthCommand({ json: false, timeoutMs: 10_000 }, runtime); }