fix(matrix): pass loaded cfg to verify CLI subcommands (#70992) [AI-assisted] (#71102)

Merged via squash.

Prepared head SHA: 9fffdf2ca6
Co-authored-by: luyao618 <17723416+luyao618@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
This commit is contained in:
Yao
2026-04-25 03:44:22 +08:00
committed by GitHub
parent 8226a3f8fe
commit 37d5c34749
3 changed files with 117 additions and 16 deletions

View File

@@ -72,6 +72,7 @@ Docs: https://docs.openclaw.ai
- Gateway/chat: register chat.send runs in the chat run registry so lifecycle error events reach the client instead of being silently dropped, fixing stuck 'waiting' state and /abort reporting no active run. (#69747) Thanks @wangshu94.
- Plugins/QQ Bot: enable the bundled qqbot plugin by default so its runtime dependency `@tencent-connect/qqbot-connector` is installed on first launch, unblocking the QR-code binding flow that dynamically imports the connector before any account is configured. (#71051) Thanks @cxyhhhhh.
- Gateway/agent RPC: register active `agent` runs into the chat abort controller map so `chat.abort` and `sessions.abort` can interrupt them, matching `chat.send` behavior and unblocking external runtimes that drive the Gateway through the public `agent` RPC. Fixes #71128. (#71214) Thanks @bitloi.
- Matrix/CLI: pass resolved runtime config into verify commands, so `openclaw matrix verify status` and sibling verify subcommands no longer crash before acquiring the Matrix client. Fixes #70992. (#71102) Thanks @luyao618.
## 2026.4.23

View File

@@ -211,7 +211,9 @@ describe("matrix CLI verification commands", () => {
});
const program = buildProgram();
await program.parseAsync(["matrix", "verify", "bootstrap", "--json"], { from: "user" });
await program.parseAsync(["matrix", "verify", "bootstrap", "--json"], {
from: "user",
});
expect(process.exitCode).toBe(1);
});
@@ -267,6 +269,76 @@ describe("matrix CLI verification commands", () => {
expect(process.exitCode).toBe(1);
});
it("passes loaded cfg to verify status action", async () => {
const fakeCfg = { channels: { matrix: {} } };
matrixRuntimeLoadConfigMock.mockReturnValue(fakeCfg);
mockMatrixVerificationStatus({ recoveryKeyCreatedAt: null });
const program = buildProgram();
await program.parseAsync(["matrix", "verify", "status"], { from: "user" });
expect(getMatrixVerificationStatusMock).toHaveBeenCalledWith(
expect.objectContaining({ cfg: fakeCfg }),
);
});
it("passes loaded cfg to all verify subcommands", async () => {
const fakeCfg = { channels: { matrix: {} } };
matrixRuntimeLoadConfigMock.mockReturnValue(fakeCfg);
// verify bootstrap
const program1 = buildProgram();
await program1.parseAsync(["matrix", "verify", "bootstrap"], {
from: "user",
});
expect(bootstrapMatrixVerificationMock).toHaveBeenCalledWith(
expect.objectContaining({ cfg: fakeCfg }),
);
// verify device
verifyMatrixRecoveryKeyMock.mockResolvedValue({ success: true });
const program2 = buildProgram();
await program2.parseAsync(["matrix", "verify", "device", "test-key"], {
from: "user",
});
expect(verifyMatrixRecoveryKeyMock).toHaveBeenCalledWith(
"test-key",
expect.objectContaining({ cfg: fakeCfg }),
);
// verify backup status
getMatrixRoomKeyBackupStatusMock.mockResolvedValue({});
const program3 = buildProgram();
await program3.parseAsync(["matrix", "verify", "backup", "status"], {
from: "user",
});
expect(getMatrixRoomKeyBackupStatusMock).toHaveBeenCalledWith(
expect.objectContaining({ cfg: fakeCfg }),
);
// verify backup reset
const program4 = buildProgram();
await program4.parseAsync(["matrix", "verify", "backup", "reset", "--yes"], { from: "user" });
expect(resetMatrixRoomKeyBackupMock).toHaveBeenCalledWith(
expect.objectContaining({ cfg: fakeCfg }),
);
// verify backup restore
restoreMatrixRoomKeyBackupMock.mockResolvedValue({
success: true,
imported: 0,
total: 0,
backup: {},
});
const program5 = buildProgram();
await program5.parseAsync(["matrix", "verify", "backup", "restore"], {
from: "user",
});
expect(restoreMatrixRoomKeyBackupMock).toHaveBeenCalledWith(
expect.objectContaining({ cfg: fakeCfg }),
);
});
it("lists matrix devices", async () => {
listMatrixOwnDevicesMock.mockResolvedValue([
{
@@ -332,7 +404,9 @@ describe("matrix CLI verification commands", () => {
from: "user",
});
expect(pruneMatrixStaleGatewayDevicesMock).toHaveBeenCalledWith({ accountId: "poe" });
expect(pruneMatrixStaleGatewayDevicesMock).toHaveBeenCalledWith({
accountId: "poe",
});
expect(console.log).toHaveBeenCalledWith("Deleted stale OpenClaw devices: BritdXC6iL");
expect(console.log).toHaveBeenCalledWith("Current device: A7hWrQ70ea");
expect(console.log).toHaveBeenCalledWith("Remaining devices: 1");
@@ -452,7 +526,9 @@ describe("matrix CLI verification commands", () => {
{ from: "user" },
);
expect(bootstrapMatrixVerificationMock).toHaveBeenCalledWith({ accountId: "ops" });
expect(bootstrapMatrixVerificationMock).toHaveBeenCalledWith({
accountId: "ops",
});
expect(console.log).toHaveBeenCalledWith("Matrix verification bootstrap: complete");
expect(console.log).toHaveBeenCalledWith(
`Recovery key created at: ${formatExpectedLocalTimestamp("2026-03-09T06:00:00.000Z")}`,
@@ -705,7 +781,9 @@ describe("matrix CLI verification commands", () => {
});
const program = buildProgram();
await program.parseAsync(["matrix", "verify", "bootstrap", "--json"], { from: "user" });
await program.parseAsync(["matrix", "verify", "bootstrap", "--json"], {
from: "user",
});
expect(process.exitCode).toBe(0);
});
@@ -715,7 +793,9 @@ describe("matrix CLI verification commands", () => {
mockMatrixVerificationStatus({ recoveryKeyCreatedAt: recoveryCreatedAt });
const program = buildProgram();
await program.parseAsync(["matrix", "verify", "status", "--verbose"], { from: "user" });
await program.parseAsync(["matrix", "verify", "status", "--verbose"], {
from: "user",
});
expect(console.log).toHaveBeenCalledWith(
`Recovery key created at: ${formatExpectedLocalTimestamp(recoveryCreatedAt)}`,
@@ -920,7 +1000,9 @@ describe("matrix CLI verification commands", () => {
it("requires --yes before resetting the Matrix room-key backup", async () => {
const program = buildProgram();
await program.parseAsync(["matrix", "verify", "backup", "reset"], { from: "user" });
await program.parseAsync(["matrix", "verify", "backup", "reset"], {
from: "user",
});
expect(process.exitCode).toBe(1);
expect(resetMatrixRoomKeyBackupMock).not.toHaveBeenCalled();
@@ -936,7 +1018,10 @@ describe("matrix CLI verification commands", () => {
from: "user",
});
expect(resetMatrixRoomKeyBackupMock).toHaveBeenCalledWith({ accountId: "default" });
expect(resetMatrixRoomKeyBackupMock).toHaveBeenCalledWith({
accountId: "default",
cfg: {},
});
expect(console.log).toHaveBeenCalledWith("Reset success: yes");
expect(console.log).toHaveBeenCalledWith("Previous backup version: 1");
expect(console.log).toHaveBeenCalledWith("Deleted backup version: 1");
@@ -981,6 +1066,7 @@ describe("matrix CLI verification commands", () => {
expect(getMatrixVerificationStatusMock).toHaveBeenCalledWith({
accountId: "assistant",
cfg: {},
includeRecoveryKey: false,
});
expect(console.log).toHaveBeenCalledWith("Account: assistant");

View File

@@ -96,6 +96,17 @@ function resolveMatrixCliAccountId(accountId?: string): string {
return resolveMatrixAuthContext({ cfg, accountId }).accountId;
}
function resolveMatrixCliAccountContext(accountId?: string): {
accountId: string;
cfg: CoreConfig;
} {
const cfg = getMatrixRuntime().config.loadConfig() as CoreConfig;
return {
accountId: resolveMatrixAuthContext({ cfg, accountId }).accountId,
cfg,
};
}
function formatMatrixCliCommand(command: string, accountId?: string): string {
const normalizedAccountId = normalizeAccountId(accountId);
const suffix = normalizedAccountId === "default" ? "" : ` --account ${normalizedAccountId}`;
@@ -925,13 +936,14 @@ export function registerMatrixCli(params: { program: Command }): void {
includeRecoveryKey?: boolean;
json?: boolean;
}) => {
const accountId = resolveMatrixCliAccountId(options.account);
const { accountId, cfg } = resolveMatrixCliAccountContext(options.account);
await runMatrixCliCommand({
verbose: options.verbose === true,
json: options.json === true,
run: async () =>
await getMatrixVerificationStatus({
accountId,
cfg,
includeRecoveryKey: options.includeRecoveryKey === true,
}),
onText: (status, verbose) => {
@@ -952,11 +964,11 @@ export function registerMatrixCli(params: { program: Command }): void {
.option("--verbose", "Show detailed diagnostics")
.option("--json", "Output as JSON")
.action(async (options: { account?: string; verbose?: boolean; json?: boolean }) => {
const accountId = resolveMatrixCliAccountId(options.account);
const { accountId, cfg } = resolveMatrixCliAccountContext(options.account);
await runMatrixCliCommand({
verbose: options.verbose === true,
json: options.json === true,
run: async () => await getMatrixRoomKeyBackupStatus({ accountId }),
run: async () => await getMatrixRoomKeyBackupStatus({ accountId, cfg }),
onText: (status, verbose) => {
printAccountLabel(accountId);
printBackupSummary(status);
@@ -979,7 +991,7 @@ export function registerMatrixCli(params: { program: Command }): void {
.option("--json", "Output as JSON")
.action(
async (options: { account?: string; yes?: boolean; verbose?: boolean; json?: boolean }) => {
const accountId = resolveMatrixCliAccountId(options.account);
const { accountId, cfg } = resolveMatrixCliAccountContext(options.account);
await runMatrixCliCommand({
verbose: options.verbose === true,
json: options.json === true,
@@ -987,7 +999,7 @@ export function registerMatrixCli(params: { program: Command }): void {
if (options.yes !== true) {
throw new Error("Refusing to reset Matrix room-key backup without --yes");
}
return await resetMatrixRoomKeyBackup({ accountId });
return await resetMatrixRoomKeyBackup({ accountId, cfg });
},
onText: (result, verbose) => {
printAccountLabel(accountId);
@@ -1025,13 +1037,14 @@ export function registerMatrixCli(params: { program: Command }): void {
verbose?: boolean;
json?: boolean;
}) => {
const accountId = resolveMatrixCliAccountId(options.account);
const { accountId, cfg } = resolveMatrixCliAccountContext(options.account);
await runMatrixCliCommand({
verbose: options.verbose === true,
json: options.json === true,
run: async () =>
await restoreMatrixRoomKeyBackup({
accountId,
cfg,
recoveryKey: options.recoveryKey,
}),
onText: (result, verbose) => {
@@ -1074,13 +1087,14 @@ export function registerMatrixCli(params: { program: Command }): void {
verbose?: boolean;
json?: boolean;
}) => {
const accountId = resolveMatrixCliAccountId(options.account);
const { accountId, cfg } = resolveMatrixCliAccountContext(options.account);
await runMatrixCliCommand({
verbose: options.verbose === true,
json: options.json === true,
run: async () =>
await bootstrapMatrixVerification({
accountId,
cfg,
recoveryKey: options.recoveryKey,
forceResetCrossSigning: options.forceResetCrossSigning === true,
}),
@@ -1129,11 +1143,11 @@ export function registerMatrixCli(params: { program: Command }): void {
.option("--json", "Output as JSON")
.action(
async (key: string, options: { account?: string; verbose?: boolean; json?: boolean }) => {
const accountId = resolveMatrixCliAccountId(options.account);
const { accountId, cfg } = resolveMatrixCliAccountContext(options.account);
await runMatrixCliCommand({
verbose: options.verbose === true,
json: options.json === true,
run: async () => await verifyMatrixRecoveryKey(key, { accountId }),
run: async () => await verifyMatrixRecoveryKey(key, { accountId, cfg }),
onText: (result, verbose) => {
printAccountLabel(accountId);
if (!result.success) {