fix: accept device identity dashboard probes

This commit is contained in:
Peter Steinberger
2026-05-16 17:41:50 +01:00
parent 6ed16d9356
commit 38cf54593e
3 changed files with 36 additions and 1 deletions

View File

@@ -44,6 +44,7 @@ Docs: https://docs.openclaw.ai
- Agents/sessions: remove the transient `*.bak-<pid>-<ts>` 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.

View File

@@ -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: {

View File

@@ -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 {