@badgerclaw/connect 1.1.2 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@badgerclaw/connect",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "BadgerClaw channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "dependencies": {
@@ -1,7 +1,8 @@
1
1
  import type { MatrixClient } from "@vector-im/matrix-bot-sdk";
2
- import { appendFileSync, mkdirSync } from "node:fs";
2
+ import { appendFileSync, writeFileSync, mkdirSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
4
  import { join } from "node:path";
5
+ import { generateKeyPairSync } from "node:crypto";
5
6
  import { getMatrixLogService } from "../sdk-runtime.js";
6
7
 
7
8
  export type KeyBackupStatus = {
@@ -10,19 +11,12 @@ export type KeyBackupStatus = {
10
11
  deviceId: string | null;
11
12
  };
12
13
 
13
- export async function getEncryptionKeyBackupStatus(
14
- client: MatrixClient,
15
- ): Promise<KeyBackupStatus> {
14
+ export async function getEncryptionKeyBackupStatus(client: MatrixClient): Promise<KeyBackupStatus> {
16
15
  const LogService = getMatrixLogService();
17
16
  try {
18
17
  const backupInfo = await client.getKeyBackupVersion();
19
18
  const whoami = await client.getWhoAmI();
20
- const deviceId = whoami.device_id ?? null;
21
- return {
22
- enabled: backupInfo !== null,
23
- version: backupInfo?.version ?? null,
24
- deviceId,
25
- };
19
+ return { enabled: backupInfo !== null, version: backupInfo?.version ?? null, deviceId: whoami.device_id ?? null };
26
20
  } catch (err) {
27
21
  LogService.warn("MatrixKeyBackup", "Failed to check key backup status:", err);
28
22
  return { enabled: false, version: null, deviceId: null };
@@ -33,17 +27,12 @@ export async function setupKeyBackup(client: MatrixClient): Promise<void> {
33
27
  const LogService = getMatrixLogService();
34
28
 
35
29
  let deviceId = "(unknown)";
36
- try {
37
- const whoami = await client.getWhoAmI();
38
- deviceId = whoami.device_id ?? deviceId;
39
- } catch {
40
- // Non-fatal
41
- }
30
+ try { deviceId = (await client.getWhoAmI()).device_id ?? deviceId; } catch { /* non-fatal */ }
42
31
 
43
32
  LogService.info("MatrixKeyBackup", `Crypto ready — device ID: ${deviceId}`);
44
33
 
45
34
  if (!client.crypto) {
46
- LogService.info("MatrixKeyBackup", "Crypto client not available, skipping key backup setup");
35
+ LogService.info("MatrixKeyBackup", "Crypto not available, skipping backup setup");
47
36
  return;
48
37
  }
49
38
 
@@ -51,45 +40,52 @@ export async function setupKeyBackup(client: MatrixClient): Promise<void> {
51
40
  let backupInfo = await client.getKeyBackupVersion();
52
41
 
53
42
  if (!backupInfo) {
54
- // No backup existscreate one now
55
- LogService.info("MatrixKeyBackup", "No key backup found — creating server-side key backup");
56
- try {
57
- backupInfo = await (client as any).signAndCreateKeyBackupVersion({
58
- algorithm: "m.megolm_backup.v1.curve25519-aes-sha2",
59
- auth_data: {},
60
- });
61
- LogService.info("MatrixKeyBackup", `Created new key backup version ${backupInfo?.version}`);
62
- } catch (createErr) {
63
- LogService.warn("MatrixKeyBackup", "Failed to create key backup version:", createErr);
64
- return;
65
- }
43
+ LogService.info("MatrixKeyBackup", "No backup foundgenerating Curve25519 key pair and creating backup version");
44
+
45
+ // Generate X25519 (Curve25519) key pair using Node crypto
46
+ const { publicKey: pubKeyObj, privateKey: privKeyObj } = generateKeyPairSync("x25519");
47
+ const pubKeyRaw = pubKeyObj.export({ type: "spki", format: "der" }).slice(-32);
48
+ const privKeyRaw = privKeyObj.export({ type: "pkcs8", format: "der" }).slice(-32);
49
+ const publicKeyBase64 = pubKeyRaw.toString("base64");
50
+ const privateKeyBase64 = privKeyRaw.toString("base64");
51
+
52
+ // Create the backup version on the server
53
+ await client.signAndCreateKeyBackupVersion({
54
+ algorithm: "m.megolm_backup.v1.curve25519-aes-sha2",
55
+ auth_data: { public_key: publicKeyBase64 },
56
+ } as any);
57
+
58
+ // Save private key for cross-device restore
59
+ const backupDir = join(homedir(), ".openclaw", "backup");
60
+ mkdirSync(backupDir, { recursive: true });
61
+ writeFileSync(
62
+ join(backupDir, `private-key-${deviceId}.json`),
63
+ JSON.stringify({ privateKey: privateKeyBase64, deviceId, created: new Date().toISOString() }),
64
+ { mode: 0o600 }
65
+ );
66
+
67
+ backupInfo = await client.getKeyBackupVersion();
68
+ LogService.info("MatrixKeyBackup", `Created backup version ${backupInfo?.version}`);
66
69
  }
67
70
 
68
71
  if (!backupInfo) {
69
- LogService.warn("MatrixKeyBackup", "Key backup info unavailable after creation attempt");
72
+ LogService.warn("MatrixKeyBackup", "No backup info after creation attempt");
70
73
  return;
71
74
  }
72
75
 
73
76
  await client.crypto.enableKeyBackup(backupInfo);
74
- LogService.info(
75
- "MatrixKeyBackup",
76
- `Key backup enabled (version ${backupInfo.version}) on device ${deviceId} — keys will upload automatically`,
77
- );
78
- _persistBackupRecord(backupInfo.version, deviceId);
77
+ LogService.info("MatrixKeyBackup", `Backup enabled v${backupInfo.version} on ${deviceId} — uploading room keys`);
78
+ _persistRecord(backupInfo.version, deviceId);
79
79
  } catch (err) {
80
- LogService.warn("MatrixKeyBackup", "Key backup setup failed:", err);
80
+ LogService.warn("MatrixKeyBackup", "Backup setup failed:", err);
81
81
  }
82
82
  }
83
83
 
84
- function _persistBackupRecord(version: string, deviceId: string): void {
84
+ function _persistRecord(version: string, deviceId: string): void {
85
85
  try {
86
86
  const dir = join(homedir(), ".openclaw", "backup");
87
87
  mkdirSync(dir, { recursive: true });
88
- const path = join(dir, "key-backup-record.log");
89
- const entry =
90
- JSON.stringify({ version, deviceId, timestamp: new Date().toISOString() }) + "\n";
91
- appendFileSync(path, entry, "utf8");
92
- } catch {
93
- // Non-fatal — backup is enabled, record persistence is best-effort
94
- }
88
+ appendFileSync(join(dir, "key-backup-record.log"),
89
+ JSON.stringify({ version, deviceId, timestamp: new Date().toISOString() }) + "\n");
90
+ } catch { /* non-fatal */ }
95
91
  }