Matrix: make device probe best-effort on account add

This commit is contained in:
Gustavo Madeira Santana
2026-03-12 15:27:38 +00:00
parent f544ffe0ea
commit 1b2f7fc7f7
2 changed files with 91 additions and 10 deletions

View File

@@ -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();

View File

@@ -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}'.`,
);