diff --git a/CHANGELOG.md b/CHANGELOG.md index b5e711dc848..99341049b62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ Docs: https://docs.openclaw.ai - Agents/sessions: remove the transient `*.bak--` backup written by `repairSessionFileIfNeeded` once the atomic replace succeeds, so a stuck session with a persistently malformed JSONL line no longer accumulates one snapshot per repair invocation. Fixes #80960. (#80969) Thanks @100yenadmin. Co-authored by @tynamite. - CLI/status: show plain empty-state messages instead of empty Channels and Sessions tables when no channels or sessions exist. - CLI/dashboard: probe Gateway readiness before handing out the dashboard URL, prompting to start or install the managed service when the Gateway is stopped and printing recovery commands instead of opening a dead browser tab. +- CLI/dashboard: treat Gateway `device identity required` probes as proof that the dashboard listener is reachable, so `openclaw dashboard` can still open the Control UI. - CLI: hide decorative startup and status emoji on terminals that are unlikely to render them correctly, keeping semantic message and identity emoji intact. - CLI/gateway: recover the Linux user systemd bus environment when `openclaw dashboard` starts the Gateway from stripped desktop shells such as VNC terminals. - Gateway/WebSocket: log expected startup `1013 gateway starting` retry closes at debug instead of warn while preserving WARN for unexpected pre-connect failures. Fixes #76361. (#82457) Thanks @IWhatsskill. diff --git a/src/commands/gateway-readiness.test.ts b/src/commands/gateway-readiness.test.ts index 561d7b898cb..0157ea45a71 100644 --- a/src/commands/gateway-readiness.test.ts +++ b/src/commands/gateway-readiness.test.ts @@ -219,6 +219,38 @@ describe("ensureGatewayReadyForOperation", () => { expect(runtime.log).not.toHaveBeenCalled(); }); + it("can accept a reachable dashboard listener when the RPC needs device identity", async () => { + const status = createStatus({ + service: { + label: "systemd user", + loaded: true, + loadedText: "enabled", + notLoadedText: "disabled", + command: { programArguments: ["openclaw", "gateway", "run", "--port", "18789"] }, + runtime: { status: "running" }, + }, + port: { port: 18789, status: "busy", listeners: [], hints: [] }, + rpc: { + ok: false, + error: "device identity required", + url: "ws://127.0.0.1:18789", + }, + }); + const confirm = vi.fn(); + + const result = await ensureGatewayReadyForOperation({ + runtime, + operation: "open the dashboard", + readyWhenReachable: true, + interactive: true, + deps: { gatherStatus: vi.fn().mockResolvedValue(status), confirm }, + }); + + expect(result).toMatchObject({ ready: true, recovered: false }); + expect(confirm).not.toHaveBeenCalled(); + expect(runtime.log).not.toHaveBeenCalled(); + }); + it("still treats a timeout on the target port as not ready", async () => { const status = createStatus({ service: { diff --git a/src/commands/gateway-readiness.ts b/src/commands/gateway-readiness.ts index 525a046be60..7f0d1ba6204 100644 --- a/src/commands/gateway-readiness.ts +++ b/src/commands/gateway-readiness.ts @@ -96,7 +96,9 @@ function gatewayProbeSawGateway(status: DaemonStatus): boolean { if (rpc.server?.version || rpc.server?.connId) { return true; } - return /\bgateway closed \(\d+\):|\bpairing required\b/i.test(rpc.error ?? ""); + return /\bgateway closed \(\d+\):|\bpairing required\b|\bdevice identity required\b/i.test( + rpc.error ?? "", + ); } function gatewayLooksReachable(status: DaemonStatus): boolean {