diff --git a/src/commands/onboard-helpers.test.ts b/src/commands/onboard-helpers.test.ts index 8901436465d..5ce319e8a03 100644 --- a/src/commands/onboard-helpers.test.ts +++ b/src/commands/onboard-helpers.test.ts @@ -16,6 +16,7 @@ import { resolveControlUiLinks, summarizeExistingConfig, validateGatewayPasswordInput, + waitForGatewayReachable, } from "./onboard-helpers.js"; const mocks = vi.hoisted(() => ({ @@ -307,6 +308,31 @@ describe("probeGatewayReachable", () => { }); }); +describe("waitForGatewayReachable", () => { + it("keeps oversized poll intervals within the overall deadline", async () => { + mocks.probeGateway.mockResolvedValue({ + ok: false, + url: "ws://127.0.0.1:18789", + connectLatencyMs: null, + error: "connect failed: timeout", + close: null, + health: null, + status: null, + presence: null, + configSnapshot: null, + }); + + const result = await waitForGatewayReachable({ + url: "ws://127.0.0.1:18789", + deadlineMs: 5, + pollMs: Number.MAX_SAFE_INTEGER, + probeTimeoutMs: 1, + }); + + expect(result).toEqual({ ok: false, detail: "connect failed: timeout" }); + }); +}); + describe("summarizeExistingConfig", () => { it("collapses gateway fields into a friendly remote summary", () => { expect( diff --git a/src/commands/onboard-helpers.ts b/src/commands/onboard-helpers.ts index 9592344bd5c..df87185e585 100644 --- a/src/commands/onboard-helpers.ts +++ b/src/commands/onboard-helpers.ts @@ -2,6 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { inspect } from "node:util"; import { cancel, isCancel } from "@clack/prompts"; +import { resolveTimerTimeoutMs } from "@openclaw/normalization-core/number-coercion"; import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; import { uniqueStrings } from "@openclaw/normalization-core/string-normalization"; import { visibleWidth } from "../../packages/terminal-core/src/ansi.js"; @@ -339,7 +340,7 @@ export async function waitForGatewayReachable(params: { pollMs?: number; }): Promise<{ ok: boolean; detail?: string }> { const deadlineMs = params.deadlineMs ?? 15_000; - const pollMs = params.pollMs ?? 400; + const pollMs = resolveTimerTimeoutMs(params.pollMs ?? 400, 400, 0); const probeTimeoutMs = params.probeTimeoutMs ?? 1500; const startedAt = Date.now(); let lastDetail: string | undefined; @@ -355,7 +356,11 @@ export async function waitForGatewayReachable(params: { return probe; } lastDetail = probe.detail; - await sleep(pollMs); + const remainingMs = deadlineMs - (Date.now() - startedAt); + if (remainingMs <= 0) { + break; + } + await sleep(Math.min(pollMs, remainingMs)); } return { ok: false, detail: lastDetail };