From 1b2f7fc7f7f017efeefd68edf128db726b7471eb Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Thu, 12 Mar 2026 15:27:38 +0000 Subject: [PATCH] Matrix: make device probe best-effort on account add --- extensions/matrix/src/cli.test.ts | 67 +++++++++++++++++++++++++++++++ extensions/matrix/src/cli.ts | 34 +++++++++++----- 2 files changed, 91 insertions(+), 10 deletions(-) diff --git a/extensions/matrix/src/cli.test.ts b/extensions/matrix/src/cli.test.ts index 529635b79c5..9d15c022665 100644 --- a/extensions/matrix/src/cli.test.ts +++ b/extensions/matrix/src/cli.test.ts @@ -463,6 +463,73 @@ describe("matrix CLI verification commands", () => { expect(bootstrapMatrixVerificationMock).not.toHaveBeenCalled(); }); + it("warns instead of failing when device-health probing fails after saving the account", async () => { + listMatrixOwnDevicesMock.mockRejectedValue(new Error("homeserver unavailable")); + const program = buildProgram(); + + await program.parseAsync( + [ + "matrix", + "account", + "add", + "--account", + "ops", + "--homeserver", + "https://matrix.example.org", + "--user-id", + "@ops:example.org", + "--password", + "secret", + ], + { from: "user" }, + ); + + expect(matrixRuntimeWriteConfigFileMock).toHaveBeenCalled(); + expect(process.exitCode).toBeUndefined(); + expect(console.log).toHaveBeenCalledWith("Saved matrix account: ops"); + expect(console.error).toHaveBeenCalledWith( + "Matrix device health warning: homeserver unavailable", + ); + }); + + it("returns device-health warnings in JSON mode without failing the account add command", async () => { + listMatrixOwnDevicesMock.mockRejectedValue(new Error("homeserver unavailable")); + const program = buildProgram(); + + await program.parseAsync( + [ + "matrix", + "account", + "add", + "--account", + "ops", + "--homeserver", + "https://matrix.example.org", + "--user-id", + "@ops:example.org", + "--password", + "secret", + "--json", + ], + { from: "user" }, + ); + + expect(matrixRuntimeWriteConfigFileMock).toHaveBeenCalled(); + expect(process.exitCode).toBeUndefined(); + const jsonOutput = console.log.mock.calls.at(-1)?.[0]; + expect(typeof jsonOutput).toBe("string"); + expect(JSON.parse(String(jsonOutput))).toEqual( + expect.objectContaining({ + accountId: "ops", + deviceHealth: expect.objectContaining({ + currentDeviceId: null, + staleOpenClawDeviceIds: [], + error: "homeserver unavailable", + }), + }), + ); + }); + it("uses --name as fallback account id and prints account-scoped config path", async () => { matrixRuntimeLoadConfigMock.mockReturnValue({ channels: {} }); const program = buildProgram(); diff --git a/extensions/matrix/src/cli.ts b/extensions/matrix/src/cli.ts index 504f4725976..137e4bd72d7 100644 --- a/extensions/matrix/src/cli.ts +++ b/extensions/matrix/src/cli.ts @@ -131,6 +131,7 @@ type MatrixCliAccountAddResult = { deviceHealth: { currentDeviceId: string | null; staleOpenClawDeviceIds: string[]; + error?: string; }; verificationBootstrap: { attempted: boolean; @@ -277,20 +278,31 @@ async function addMatrixAccount(params: { } } - const addedDevices = await listMatrixOwnDevices({ accountId }); - const currentDeviceId = addedDevices.find((device) => device.current)?.deviceId ?? null; - const staleOpenClawDeviceIds = addedDevices - .filter((device) => !device.current && isOpenClawManagedMatrixDevice(device.displayName)) - .map((device) => device.deviceId); + let deviceHealth: MatrixCliAccountAddResult["deviceHealth"] = { + currentDeviceId: null, + staleOpenClawDeviceIds: [], + }; + try { + const addedDevices = await listMatrixOwnDevices({ accountId }); + deviceHealth = { + currentDeviceId: addedDevices.find((device) => device.current)?.deviceId ?? null, + staleOpenClawDeviceIds: addedDevices + .filter((device) => !device.current && isOpenClawManagedMatrixDevice(device.displayName)) + .map((device) => device.deviceId), + }; + } catch (err) { + deviceHealth = { + currentDeviceId: null, + staleOpenClawDeviceIds: [], + error: toErrorMessage(err), + }; + } return { accountId, configPath: resolveMatrixConfigPath(updated, accountId), useEnv: input.useEnv === true, - deviceHealth: { - currentDeviceId, - staleOpenClawDeviceIds, - }, + deviceHealth, verificationBootstrap, profile, }; @@ -709,7 +721,9 @@ export function registerMatrixCli(params: { program: Command }): void { ); } } - if (result.deviceHealth.staleOpenClawDeviceIds.length > 0) { + if (result.deviceHealth.error) { + console.error(`Matrix device health warning: ${result.deviceHealth.error}`); + } else if (result.deviceHealth.staleOpenClawDeviceIds.length > 0) { console.log( `Matrix device hygiene warning: stale OpenClaw devices detected (${result.deviceHealth.staleOpenClawDeviceIds.join(", ")}). Run 'openclaw matrix devices prune-stale --account ${result.accountId}'.`, );