mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-31 07:46:51 +00:00
Matrix-js: add verbose verify output and docs
This commit is contained in:
@@ -114,7 +114,7 @@ describe("matrix-js CLI verification commands", () => {
|
||||
expect(process.exitCode).toBe(0);
|
||||
});
|
||||
|
||||
it("prints local timezone timestamps for verify status output", async () => {
|
||||
it("prints local timezone timestamps for verify status output in verbose mode", async () => {
|
||||
const recoveryCreatedAt = "2026-02-25T20:10:11.000Z";
|
||||
getMatrixVerificationStatusMock.mockResolvedValue({
|
||||
encryptionEnabled: true,
|
||||
@@ -135,14 +135,14 @@ describe("matrix-js CLI verification commands", () => {
|
||||
});
|
||||
const program = buildProgram();
|
||||
|
||||
await program.parseAsync(["matrix-js", "verify", "status"], { from: "user" });
|
||||
await program.parseAsync(["matrix-js", "verify", "status", "--verbose"], { from: "user" });
|
||||
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
`Recovery key created at: ${formatExpectedLocalTimestamp(recoveryCreatedAt)}`,
|
||||
);
|
||||
});
|
||||
|
||||
it("prints local timezone timestamps for verify bootstrap and device output", async () => {
|
||||
it("prints local timezone timestamps for verify bootstrap and device output in verbose mode", async () => {
|
||||
const recoveryCreatedAt = "2026-02-25T20:10:11.000Z";
|
||||
const verifiedAt = "2026-02-25T20:14:00.000Z";
|
||||
bootstrapMatrixVerificationMock.mockResolvedValue({
|
||||
@@ -200,8 +200,12 @@ describe("matrix-js CLI verification commands", () => {
|
||||
});
|
||||
const program = buildProgram();
|
||||
|
||||
await program.parseAsync(["matrix-js", "verify", "bootstrap"], { from: "user" });
|
||||
await program.parseAsync(["matrix-js", "verify", "device", "valid-key"], { from: "user" });
|
||||
await program.parseAsync(["matrix-js", "verify", "bootstrap", "--verbose"], {
|
||||
from: "user",
|
||||
});
|
||||
await program.parseAsync(["matrix-js", "verify", "device", "valid-key", "--verbose"], {
|
||||
from: "user",
|
||||
});
|
||||
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
`Recovery key created at: ${formatExpectedLocalTimestamp(recoveryCreatedAt)}`,
|
||||
@@ -211,7 +215,37 @@ describe("matrix-js CLI verification commands", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("prints backup health lines for verify backup status", async () => {
|
||||
it("keeps default output concise when verbose is not provided", async () => {
|
||||
const recoveryCreatedAt = "2026-02-25T20:10:11.000Z";
|
||||
getMatrixVerificationStatusMock.mockResolvedValue({
|
||||
encryptionEnabled: true,
|
||||
verified: true,
|
||||
userId: "@bot:example.org",
|
||||
deviceId: "DEVICE123",
|
||||
backupVersion: "1",
|
||||
backup: {
|
||||
serverVersion: "1",
|
||||
activeVersion: "1",
|
||||
trusted: true,
|
||||
matchesDecryptionKey: true,
|
||||
decryptionKeyCached: true,
|
||||
},
|
||||
recoveryKeyStored: true,
|
||||
recoveryKeyCreatedAt: recoveryCreatedAt,
|
||||
pendingVerifications: 0,
|
||||
});
|
||||
const program = buildProgram();
|
||||
|
||||
await program.parseAsync(["matrix-js", "verify", "status"], { from: "user" });
|
||||
|
||||
expect(console.log).not.toHaveBeenCalledWith(
|
||||
`Recovery key created at: ${formatExpectedLocalTimestamp(recoveryCreatedAt)}`,
|
||||
);
|
||||
expect(console.log).not.toHaveBeenCalledWith("Pending verifications: 0");
|
||||
expect(console.log).toHaveBeenCalledWith("Backup: active and trusted on this device");
|
||||
});
|
||||
|
||||
it("prints backup health lines for verify backup status in verbose mode", async () => {
|
||||
getMatrixRoomKeyBackupStatusMock.mockResolvedValue({
|
||||
serverVersion: "2",
|
||||
activeVersion: null,
|
||||
@@ -221,7 +255,7 @@ describe("matrix-js CLI verification commands", () => {
|
||||
});
|
||||
const program = buildProgram();
|
||||
|
||||
await program.parseAsync(["matrix-js", "verify", "backup", "status"], {
|
||||
await program.parseAsync(["matrix-js", "verify", "backup", "status", "--verbose"], {
|
||||
from: "user",
|
||||
});
|
||||
|
||||
|
||||
@@ -94,6 +94,26 @@ function printBackupStatus(backup: MatrixCliBackupStatus): void {
|
||||
console.log(`Backup key cached locally: ${yesNoUnknown(backup.decryptionKeyCached)}`);
|
||||
}
|
||||
|
||||
function summarizeBackupHealth(backup: MatrixCliBackupStatus): string {
|
||||
if (!backup.serverVersion) {
|
||||
return "missing on server";
|
||||
}
|
||||
if (backup.trusted === false || backup.matchesDecryptionKey === false) {
|
||||
return "present but not trusted on this device";
|
||||
}
|
||||
if (!backup.activeVersion) {
|
||||
return "present on server but inactive on this device";
|
||||
}
|
||||
return "active and trusted on this device";
|
||||
}
|
||||
|
||||
function printBackupSummary(backup: MatrixCliBackupStatus): void {
|
||||
console.log(`Backup: ${summarizeBackupHealth(backup)}`);
|
||||
if (backup.serverVersion) {
|
||||
console.log(`Backup version: ${backup.serverVersion}`);
|
||||
}
|
||||
}
|
||||
|
||||
function buildVerificationGuidance(status: MatrixCliVerificationStatus): string[] {
|
||||
const backup = resolveBackupStatus(status);
|
||||
const nextSteps = new Set<string>();
|
||||
@@ -133,7 +153,8 @@ function printGuidance(lines: string[]): void {
|
||||
}
|
||||
}
|
||||
|
||||
function printVerificationStatus(status: MatrixCliVerificationStatus): void {
|
||||
function printVerificationStatus(status: MatrixCliVerificationStatus, verbose = false): void {
|
||||
const backup = resolveBackupStatus(status);
|
||||
if (status.verified) {
|
||||
console.log("Verified: yes");
|
||||
console.log(`User: ${status.userId ?? "unknown"}`);
|
||||
@@ -143,10 +164,15 @@ function printVerificationStatus(status: MatrixCliVerificationStatus): void {
|
||||
console.log(`User: ${status.userId ?? "unknown"}`);
|
||||
console.log(`Device: ${status.deviceId ?? "unknown"}`);
|
||||
}
|
||||
printBackupStatus(resolveBackupStatus(status));
|
||||
printBackupSummary(backup);
|
||||
if (verbose) {
|
||||
printBackupStatus(backup);
|
||||
}
|
||||
console.log(`Recovery key stored: ${status.recoveryKeyStored ? "yes" : "no"}`);
|
||||
printTimestamp("Recovery key created at", status.recoveryKeyCreatedAt);
|
||||
console.log(`Pending verifications: ${status.pendingVerifications}`);
|
||||
if (verbose) {
|
||||
printTimestamp("Recovery key created at", status.recoveryKeyCreatedAt);
|
||||
console.log(`Pending verifications: ${status.pendingVerifications}`);
|
||||
}
|
||||
printGuidance(buildVerificationGuidance(status));
|
||||
}
|
||||
|
||||
@@ -162,31 +188,39 @@ export function registerMatrixJsCli(params: { program: Command }): void {
|
||||
.command("status")
|
||||
.description("Check Matrix-js device verification status")
|
||||
.option("--account <id>", "Account ID (for multi-account setups)")
|
||||
.option("--verbose", "Show detailed diagnostics")
|
||||
.option("--include-recovery-key", "Include stored recovery key in output")
|
||||
.option("--json", "Output as JSON")
|
||||
.action(async (options: { account?: string; includeRecoveryKey?: boolean; json?: boolean }) => {
|
||||
try {
|
||||
const status = await getMatrixVerificationStatus({
|
||||
accountId: options.account,
|
||||
includeRecoveryKey: options.includeRecoveryKey === true,
|
||||
});
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(status, null, 2));
|
||||
return;
|
||||
.action(
|
||||
async (options: {
|
||||
account?: string;
|
||||
verbose?: boolean;
|
||||
includeRecoveryKey?: boolean;
|
||||
json?: boolean;
|
||||
}) => {
|
||||
try {
|
||||
const status = await getMatrixVerificationStatus({
|
||||
accountId: options.account,
|
||||
includeRecoveryKey: options.includeRecoveryKey === true,
|
||||
});
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(status, null, 2));
|
||||
return;
|
||||
}
|
||||
printVerificationStatus(status, options.verbose === true);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify({ error: message }, null, 2));
|
||||
} else {
|
||||
console.error(`Error: ${message}`);
|
||||
}
|
||||
markCliFailure();
|
||||
} finally {
|
||||
scheduleMatrixJsCliExit();
|
||||
}
|
||||
printVerificationStatus(status);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify({ error: message }, null, 2));
|
||||
} else {
|
||||
console.error(`Error: ${message}`);
|
||||
}
|
||||
markCliFailure();
|
||||
} finally {
|
||||
scheduleMatrixJsCliExit();
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
const backup = verify.command("backup").description("Matrix room-key backup health and restore");
|
||||
|
||||
@@ -194,15 +228,19 @@ export function registerMatrixJsCli(params: { program: Command }): void {
|
||||
.command("status")
|
||||
.description("Show Matrix room-key backup status for this device")
|
||||
.option("--account <id>", "Account ID (for multi-account setups)")
|
||||
.option("--verbose", "Show detailed diagnostics")
|
||||
.option("--json", "Output as JSON")
|
||||
.action(async (options: { account?: string; json?: boolean }) => {
|
||||
.action(async (options: { account?: string; verbose?: boolean; json?: boolean }) => {
|
||||
try {
|
||||
const status = await getMatrixRoomKeyBackupStatus({ accountId: options.account });
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(status, null, 2));
|
||||
return;
|
||||
}
|
||||
printBackupStatus(status);
|
||||
printBackupSummary(status);
|
||||
if (options.verbose === true) {
|
||||
printBackupStatus(status);
|
||||
}
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
if (options.json) {
|
||||
@@ -221,46 +259,57 @@ export function registerMatrixJsCli(params: { program: Command }): void {
|
||||
.description("Restore encrypted room keys from server backup")
|
||||
.option("--account <id>", "Account ID (for multi-account setups)")
|
||||
.option("--recovery-key <key>", "Optional recovery key to load before restoring")
|
||||
.option("--verbose", "Show detailed diagnostics")
|
||||
.option("--json", "Output as JSON")
|
||||
.action(async (options: { account?: string; recoveryKey?: string; json?: boolean }) => {
|
||||
try {
|
||||
const result = await restoreMatrixRoomKeyBackup({
|
||||
accountId: options.account,
|
||||
recoveryKey: options.recoveryKey,
|
||||
});
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
.action(
|
||||
async (options: {
|
||||
account?: string;
|
||||
recoveryKey?: string;
|
||||
verbose?: boolean;
|
||||
json?: boolean;
|
||||
}) => {
|
||||
try {
|
||||
const result = await restoreMatrixRoomKeyBackup({
|
||||
accountId: options.account,
|
||||
recoveryKey: options.recoveryKey,
|
||||
});
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
if (!result.success) {
|
||||
markCliFailure();
|
||||
}
|
||||
return;
|
||||
}
|
||||
console.log(`Restore success: ${result.success ? "yes" : "no"}`);
|
||||
if (result.error) {
|
||||
console.log(`Error: ${result.error}`);
|
||||
}
|
||||
console.log(`Backup version: ${result.backupVersion ?? "none"}`);
|
||||
console.log(`Imported keys: ${result.imported}/${result.total}`);
|
||||
printBackupSummary(result.backup);
|
||||
if (options.verbose === true) {
|
||||
console.log(
|
||||
`Loaded key from secret storage: ${result.loadedFromSecretStorage ? "yes" : "no"}`,
|
||||
);
|
||||
printTimestamp("Restored at", result.restoredAt);
|
||||
printBackupStatus(result.backup);
|
||||
}
|
||||
if (!result.success) {
|
||||
markCliFailure();
|
||||
}
|
||||
return;
|
||||
}
|
||||
console.log(`Restore success: ${result.success ? "yes" : "no"}`);
|
||||
if (result.error) {
|
||||
console.log(`Error: ${result.error}`);
|
||||
}
|
||||
console.log(`Backup version: ${result.backupVersion ?? "none"}`);
|
||||
console.log(`Imported keys: ${result.imported}/${result.total}`);
|
||||
console.log(
|
||||
`Loaded key from secret storage: ${result.loadedFromSecretStorage ? "yes" : "no"}`,
|
||||
);
|
||||
printTimestamp("Restored at", result.restoredAt);
|
||||
printBackupStatus(result.backup);
|
||||
if (!result.success) {
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify({ success: false, error: message }, null, 2));
|
||||
} else {
|
||||
console.error(`Backup restore failed: ${message}`);
|
||||
}
|
||||
markCliFailure();
|
||||
} finally {
|
||||
scheduleMatrixJsCliExit();
|
||||
}
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify({ success: false, error: message }, null, 2));
|
||||
} else {
|
||||
console.error(`Backup restore failed: ${message}`);
|
||||
}
|
||||
markCliFailure();
|
||||
} finally {
|
||||
scheduleMatrixJsCliExit();
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
verify
|
||||
.command("bootstrap")
|
||||
@@ -268,12 +317,14 @@ export function registerMatrixJsCli(params: { program: Command }): void {
|
||||
.option("--account <id>", "Account ID (for multi-account setups)")
|
||||
.option("--recovery-key <key>", "Recovery key to apply before bootstrap")
|
||||
.option("--force-reset-cross-signing", "Force reset cross-signing identity before bootstrap")
|
||||
.option("--verbose", "Show detailed diagnostics")
|
||||
.option("--json", "Output as JSON")
|
||||
.action(
|
||||
async (options: {
|
||||
account?: string;
|
||||
recoveryKey?: string;
|
||||
forceResetCrossSigning?: boolean;
|
||||
verbose?: boolean;
|
||||
json?: boolean;
|
||||
}) => {
|
||||
try {
|
||||
@@ -296,12 +347,17 @@ export function registerMatrixJsCli(params: { program: Command }): void {
|
||||
console.log(`Verified: ${result.verification.verified ? "yes" : "no"}`);
|
||||
console.log(`User: ${result.verification.userId ?? "unknown"}`);
|
||||
console.log(`Device: ${result.verification.deviceId ?? "unknown"}`);
|
||||
console.log(
|
||||
`Cross-signing published: ${result.crossSigning.published ? "yes" : "no"} (master=${result.crossSigning.masterKeyPublished ? "yes" : "no"}, self=${result.crossSigning.selfSigningKeyPublished ? "yes" : "no"}, user=${result.crossSigning.userSigningKeyPublished ? "yes" : "no"})`,
|
||||
);
|
||||
printBackupStatus(resolveBackupStatus(result.verification));
|
||||
printTimestamp("Recovery key created at", result.verification.recoveryKeyCreatedAt);
|
||||
console.log(`Pending verifications: ${result.pendingVerifications}`);
|
||||
if (options.verbose === true) {
|
||||
console.log(
|
||||
`Cross-signing published: ${result.crossSigning.published ? "yes" : "no"} (master=${result.crossSigning.masterKeyPublished ? "yes" : "no"}, self=${result.crossSigning.selfSigningKeyPublished ? "yes" : "no"}, user=${result.crossSigning.userSigningKeyPublished ? "yes" : "no"})`,
|
||||
);
|
||||
printBackupStatus(resolveBackupStatus(result.verification));
|
||||
printTimestamp("Recovery key created at", result.verification.recoveryKeyCreatedAt);
|
||||
console.log(`Pending verifications: ${result.pendingVerifications}`);
|
||||
} else {
|
||||
console.log(`Cross-signing published: ${result.crossSigning.published ? "yes" : "no"}`);
|
||||
printBackupSummary(resolveBackupStatus(result.verification));
|
||||
}
|
||||
printGuidance(
|
||||
buildVerificationGuidance({
|
||||
...result.verification,
|
||||
@@ -329,42 +385,48 @@ export function registerMatrixJsCli(params: { program: Command }): void {
|
||||
.command("device <key>")
|
||||
.description("Verify device using a Matrix recovery key")
|
||||
.option("--account <id>", "Account ID (for multi-account setups)")
|
||||
.option("--verbose", "Show detailed diagnostics")
|
||||
.option("--json", "Output as JSON")
|
||||
.action(async (key: string, options: { account?: string; json?: boolean }) => {
|
||||
try {
|
||||
const result = await verifyMatrixRecoveryKey(key, { accountId: options.account });
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
if (!result.success) {
|
||||
.action(
|
||||
async (key: string, options: { account?: string; verbose?: boolean; json?: boolean }) => {
|
||||
try {
|
||||
const result = await verifyMatrixRecoveryKey(key, { accountId: options.account });
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
if (!result.success) {
|
||||
markCliFailure();
|
||||
}
|
||||
} else if (result.success) {
|
||||
console.log("Device verification completed successfully.");
|
||||
console.log(`User: ${result.userId ?? "unknown"}`);
|
||||
console.log(`Device: ${result.deviceId ?? "unknown"}`);
|
||||
printBackupSummary(resolveBackupStatus(result));
|
||||
if (options.verbose === true) {
|
||||
printBackupStatus(resolveBackupStatus(result));
|
||||
printTimestamp("Recovery key created at", result.recoveryKeyCreatedAt);
|
||||
printTimestamp("Verified at", result.verifiedAt);
|
||||
}
|
||||
printGuidance(
|
||||
buildVerificationGuidance({
|
||||
...result,
|
||||
pendingVerifications: 0,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
console.error(`Verification failed: ${result.error ?? "unknown error"}`);
|
||||
markCliFailure();
|
||||
}
|
||||
} else if (result.success) {
|
||||
console.log("Device verification completed successfully.");
|
||||
console.log(`User: ${result.userId ?? "unknown"}`);
|
||||
console.log(`Device: ${result.deviceId ?? "unknown"}`);
|
||||
printBackupStatus(resolveBackupStatus(result));
|
||||
printTimestamp("Recovery key created at", result.recoveryKeyCreatedAt);
|
||||
printTimestamp("Verified at", result.verifiedAt);
|
||||
printGuidance(
|
||||
buildVerificationGuidance({
|
||||
...result,
|
||||
pendingVerifications: 0,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
console.error(`Verification failed: ${result.error ?? "unknown error"}`);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify({ success: false, error: message }, null, 2));
|
||||
} else {
|
||||
console.error(`Verification failed: ${message}`);
|
||||
}
|
||||
markCliFailure();
|
||||
} finally {
|
||||
scheduleMatrixJsCliExit();
|
||||
}
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify({ success: false, error: message }, null, 2));
|
||||
} else {
|
||||
console.error(`Verification failed: ${message}`);
|
||||
}
|
||||
markCliFailure();
|
||||
} finally {
|
||||
scheduleMatrixJsCliExit();
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user