@moltdm/client 1.3.2 → 1.4.1

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/dist/index.js CHANGED
@@ -30,33 +30,156 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ BrowserStorage: () => BrowserStorage,
34
+ FileStorage: () => FileStorage,
35
+ MemoryStorage: () => MemoryStorage,
33
36
  MoltDMClient: () => MoltDMClient,
34
37
  default: () => index_default
35
38
  });
36
39
  module.exports = __toCommonJS(index_exports);
37
40
  var ed = __toESM(require("@noble/ed25519"));
38
41
  var import_ed25519 = require("@noble/curves/ed25519");
39
- var fs = __toESM(require("fs"));
40
- var path = __toESM(require("path"));
41
- var os = __toESM(require("os"));
42
- var import_crypto = require("crypto");
42
+ var MemoryStorage = class {
43
+ data = /* @__PURE__ */ new Map();
44
+ async get(key) {
45
+ return this.data.get(key) ?? null;
46
+ }
47
+ async set(key, value) {
48
+ this.data.set(key, value);
49
+ }
50
+ async delete(key) {
51
+ this.data.delete(key);
52
+ }
53
+ };
54
+ var BrowserStorage = class {
55
+ prefix;
56
+ constructor(prefix = "moltdm") {
57
+ this.prefix = prefix;
58
+ }
59
+ async get(key) {
60
+ if (typeof localStorage === "undefined") return null;
61
+ return localStorage.getItem(`${this.prefix}:${key}`);
62
+ }
63
+ async set(key, value) {
64
+ if (typeof localStorage === "undefined") return;
65
+ localStorage.setItem(`${this.prefix}:${key}`, value);
66
+ }
67
+ async delete(key) {
68
+ if (typeof localStorage === "undefined") return;
69
+ localStorage.removeItem(`${this.prefix}:${key}`);
70
+ }
71
+ };
72
+ var FileStorage = class {
73
+ basePath;
74
+ _fs = null;
75
+ _path = null;
76
+ _initialized = false;
77
+ constructor(basePath) {
78
+ this.basePath = basePath || ".moltdm";
79
+ }
80
+ async ensureModules() {
81
+ if (this._initialized) return;
82
+ if (typeof window !== "undefined") {
83
+ console.warn("FileStorage is not supported in browser. Use BrowserStorage instead.");
84
+ return;
85
+ }
86
+ try {
87
+ const fs = await import(
88
+ /* webpackIgnore: true */
89
+ "fs"
90
+ );
91
+ const path = await import(
92
+ /* webpackIgnore: true */
93
+ "path"
94
+ );
95
+ const os = await import(
96
+ /* webpackIgnore: true */
97
+ "os"
98
+ );
99
+ this._fs = fs;
100
+ this._path = path;
101
+ if (this.basePath === ".moltdm") {
102
+ const envPath = process.env.OPENCLAW_STATE_DIR;
103
+ this.basePath = envPath ? path.join(envPath, ".moltdm") : path.join(os.homedir(), ".moltdm");
104
+ }
105
+ if (!fs.existsSync(this.basePath)) {
106
+ fs.mkdirSync(this.basePath, { recursive: true });
107
+ }
108
+ this._initialized = true;
109
+ } catch (e) {
110
+ console.error("Failed to load Node.js modules for FileStorage:", e);
111
+ }
112
+ }
113
+ async get(key) {
114
+ await this.ensureModules();
115
+ if (!this._fs) return null;
116
+ const filePath = this._path.join(this.basePath, `${key}.json`);
117
+ if (!this._fs.existsSync(filePath)) return null;
118
+ return this._fs.readFileSync(filePath, "utf-8");
119
+ }
120
+ async set(key, value) {
121
+ await this.ensureModules();
122
+ if (!this._fs) return;
123
+ const filePath = this._path.join(this.basePath, `${key}.json`);
124
+ this._fs.writeFileSync(filePath, value);
125
+ }
126
+ async delete(key) {
127
+ await this.ensureModules();
128
+ if (!this._fs) return;
129
+ const filePath = this._path.join(this.basePath, `${key}.json`);
130
+ if (this._fs.existsSync(filePath)) {
131
+ this._fs.unlinkSync(filePath);
132
+ }
133
+ }
134
+ };
43
135
  function toBase64(bytes) {
44
- return Buffer.from(bytes).toString("base64");
136
+ if (typeof Buffer !== "undefined") {
137
+ return Buffer.from(bytes).toString("base64");
138
+ }
139
+ let binary = "";
140
+ for (let i = 0; i < bytes.length; i++) {
141
+ binary += String.fromCharCode(bytes[i]);
142
+ }
143
+ return btoa(binary);
45
144
  }
46
145
  function fromBase64(str) {
47
- return new Uint8Array(Buffer.from(str, "base64"));
146
+ if (typeof Buffer !== "undefined") {
147
+ return new Uint8Array(Buffer.from(str, "base64"));
148
+ }
149
+ const binary = atob(str);
150
+ const bytes = new Uint8Array(binary.length);
151
+ for (let i = 0; i < binary.length; i++) {
152
+ bytes[i] = binary.charCodeAt(i);
153
+ }
154
+ return bytes;
155
+ }
156
+ async function hmacSha256(key, data) {
157
+ const keyBuffer = new Uint8Array(key).buffer;
158
+ const dataBuffer = new Uint8Array(data).buffer;
159
+ const cryptoKey = await crypto.subtle.importKey(
160
+ "raw",
161
+ keyBuffer,
162
+ { name: "HMAC", hash: "SHA-256" },
163
+ false,
164
+ ["sign"]
165
+ );
166
+ const signature = await crypto.subtle.sign("HMAC", cryptoKey, dataBuffer);
167
+ return new Uint8Array(signature);
48
168
  }
49
169
  var MoltDMClient = class {
50
- storagePath;
170
+ storage;
51
171
  relayUrl;
52
172
  identity = null;
53
- // Our sender keys (for messages we send)
54
173
  senderKeys = /* @__PURE__ */ new Map();
55
- // Received sender keys (for messages from others) - keyed by `${convId}:${fromId}`
56
174
  receivedSenderKeys = /* @__PURE__ */ new Map();
57
175
  constructor(options = {}) {
58
- const defaultStoragePath = process.env.OPENCLAW_STATE_DIR ? path.join(process.env.OPENCLAW_STATE_DIR, ".moltdm") : path.join(os.homedir(), ".moltdm");
59
- this.storagePath = options.storagePath || defaultStoragePath;
176
+ if (options.storage) {
177
+ this.storage = options.storage;
178
+ } else if (typeof window !== "undefined" && typeof localStorage !== "undefined") {
179
+ this.storage = new BrowserStorage();
180
+ } else {
181
+ this.storage = new FileStorage(options.storagePath);
182
+ }
60
183
  this.relayUrl = options.relayUrl || "https://relay.moltdm.com";
61
184
  if (options.identity) {
62
185
  this.identity = options.identity;
@@ -85,22 +208,30 @@ var MoltDMClient = class {
85
208
  // ============================================
86
209
  async initialize() {
87
210
  if (this.identity) {
211
+ this.validateIdentity(this.identity);
88
212
  await this.loadSenderKeys();
89
213
  return;
90
214
  }
91
- if (!fs.existsSync(this.storagePath)) {
92
- fs.mkdirSync(this.storagePath, { recursive: true });
93
- }
94
- const identityPath = path.join(this.storagePath, "identity.json");
95
- if (fs.existsSync(identityPath)) {
96
- const data = fs.readFileSync(identityPath, "utf-8");
97
- this.identity = JSON.parse(data);
215
+ const identityJson = await this.storage.get("identity");
216
+ if (identityJson) {
217
+ this.identity = JSON.parse(identityJson);
218
+ this.validateIdentity(this.identity);
98
219
  } else {
99
220
  await this.createIdentity();
100
- fs.writeFileSync(identityPath, JSON.stringify(this.identity, null, 2));
221
+ await this.storage.set("identity", JSON.stringify(this.identity));
101
222
  }
102
223
  await this.loadSenderKeys();
103
224
  }
225
+ validateIdentity(identity) {
226
+ if (!identity.signedPreKey?.privateKey) {
227
+ throw new Error(
228
+ "Identity is missing signedPreKey.privateKey. This moltbot was created with an older client version. Please delete your stored identity and re-register, or update your identity file to include the signedPreKey private key."
229
+ );
230
+ }
231
+ if (!identity.moltbotId || !identity.publicKey || !identity.privateKey) {
232
+ throw new Error("Identity is missing required fields (moltbotId, publicKey, or privateKey)");
233
+ }
234
+ }
104
235
  async createIdentity() {
105
236
  const privateKeyBytes = ed.utils.randomPrivateKey();
106
237
  const publicKeyBytes = await ed.getPublicKeyAsync(privateKeyBytes);
@@ -149,10 +280,9 @@ var MoltDMClient = class {
149
280
  };
150
281
  }
151
282
  async loadSenderKeys() {
152
- const keysPath = path.join(this.storagePath, "sender_keys.json");
153
- if (fs.existsSync(keysPath)) {
154
- const data = fs.readFileSync(keysPath, "utf-8");
155
- const keys = JSON.parse(data);
283
+ const keysJson = await this.storage.get("sender_keys");
284
+ if (keysJson) {
285
+ const keys = JSON.parse(keysJson);
156
286
  for (const [convId, keyData] of Object.entries(keys)) {
157
287
  const k = keyData;
158
288
  const chainKey = fromBase64(k.chainKey || k.key || "");
@@ -164,10 +294,9 @@ var MoltDMClient = class {
164
294
  });
165
295
  }
166
296
  }
167
- const receivedPath = path.join(this.storagePath, "received_sender_keys.json");
168
- if (fs.existsSync(receivedPath)) {
169
- const data = fs.readFileSync(receivedPath, "utf-8");
170
- const keys = JSON.parse(data);
297
+ const receivedJson = await this.storage.get("received_sender_keys");
298
+ if (receivedJson) {
299
+ const keys = JSON.parse(receivedJson);
171
300
  for (const [key, keyData] of Object.entries(keys)) {
172
301
  const k = keyData;
173
302
  this.receivedSenderKeys.set(key, {
@@ -179,7 +308,6 @@ var MoltDMClient = class {
179
308
  }
180
309
  }
181
310
  async saveSenderKeys() {
182
- const keysPath = path.join(this.storagePath, "sender_keys.json");
183
311
  const obj = {};
184
312
  for (const [convId, keyData] of this.senderKeys) {
185
313
  obj[convId] = {
@@ -189,8 +317,7 @@ var MoltDMClient = class {
189
317
  messageIndex: keyData.messageIndex
190
318
  };
191
319
  }
192
- fs.writeFileSync(keysPath, JSON.stringify(obj, null, 2));
193
- const receivedPath = path.join(this.storagePath, "received_sender_keys.json");
320
+ await this.storage.set("sender_keys", JSON.stringify(obj));
194
321
  const receivedObj = {};
195
322
  for (const [key, keyData] of this.receivedSenderKeys) {
196
323
  receivedObj[key] = {
@@ -199,7 +326,7 @@ var MoltDMClient = class {
199
326
  messageIndex: keyData.messageIndex
200
327
  };
201
328
  }
202
- fs.writeFileSync(receivedPath, JSON.stringify(receivedObj, null, 2));
329
+ await this.storage.set("received_sender_keys", JSON.stringify(receivedObj));
203
330
  }
204
331
  // ============================================
205
332
  // Sender Keys Protocol (Signal-style)
@@ -208,37 +335,25 @@ var MoltDMClient = class {
208
335
  * Derive message key from chain key using HMAC
209
336
  * message_key = HMAC-SHA256(chain_key, 0x01)
210
337
  */
211
- deriveMessageKey(chainKey) {
212
- const keyBuffer = Buffer.from(chainKey);
213
- const hmac = (0, import_crypto.createHmac)("sha256", keyBuffer);
214
- hmac.update(Buffer.from([1]));
215
- const digest = hmac.digest();
216
- const result = new Uint8Array(32);
217
- result.set(new Uint8Array(digest));
218
- return result;
338
+ async deriveMessageKey(chainKey) {
339
+ return hmacSha256(chainKey, new Uint8Array([1]));
219
340
  }
220
341
  /**
221
342
  * Ratchet chain key forward
222
343
  * new_chain_key = HMAC-SHA256(chain_key, 0x02)
223
344
  */
224
- ratchetChainKey(chainKey) {
225
- const keyBuffer = Buffer.from(chainKey);
226
- const hmac = (0, import_crypto.createHmac)("sha256", keyBuffer);
227
- hmac.update(Buffer.from([2]));
228
- const digest = hmac.digest();
229
- const result = new Uint8Array(32);
230
- result.set(new Uint8Array(digest));
231
- return result;
345
+ async ratchetChainKey(chainKey) {
346
+ return hmacSha256(chainKey, new Uint8Array([2]));
232
347
  }
233
348
  /**
234
349
  * Ratchet a chain key forward N steps (for catching up on missed messages)
235
350
  */
236
- ratchetChainKeyN(chainKey, steps) {
351
+ async ratchetChainKeyN(chainKey, steps) {
237
352
  const messageKeys = [];
238
353
  let current = chainKey;
239
354
  for (let i = 0; i < steps; i++) {
240
- messageKeys.push(this.deriveMessageKey(current));
241
- current = this.ratchetChainKey(current);
355
+ messageKeys.push(await this.deriveMessageKey(current));
356
+ current = await this.ratchetChainKey(current);
242
357
  }
243
358
  return { chainKey: current, messageKeys };
244
359
  }
@@ -332,27 +447,24 @@ var MoltDMClient = class {
332
447
  async send(conversationId, content, options) {
333
448
  this.ensureInitialized();
334
449
  let senderKeyState = this.senderKeys.get(conversationId);
335
- const isNewKey = !senderKeyState;
336
450
  if (!senderKeyState) {
337
451
  const initialKey = crypto.getRandomValues(new Uint8Array(32));
338
452
  senderKeyState = {
339
453
  chainKey: initialKey,
340
454
  initialChainKey: new Uint8Array(initialKey),
341
- // Copy for distribution
342
455
  version: 1,
343
456
  messageIndex: 0
344
457
  };
345
458
  this.senderKeys.set(conversationId, senderKeyState);
346
459
  }
347
- const messageKey = this.deriveMessageKey(senderKeyState.chainKey);
460
+ const messageKey = await this.deriveMessageKey(senderKeyState.chainKey);
348
461
  const currentIndex = senderKeyState.messageIndex;
349
- senderKeyState.chainKey = this.ratchetChainKey(senderKeyState.chainKey);
462
+ senderKeyState.chainKey = await this.ratchetChainKey(senderKeyState.chainKey);
350
463
  senderKeyState.messageIndex++;
351
464
  const ciphertext = await this.encrypt(content, messageKey);
352
465
  const encryptedSenderKeys = await this.encryptChainKeyForRecipients(
353
466
  conversationId,
354
467
  senderKeyState.initialChainKey
355
- // Send the original, unratcheted key
356
468
  );
357
469
  const response = await this.fetch(`/api/conversations/${conversationId}/messages`, {
358
470
  method: "POST",
@@ -404,26 +516,23 @@ var MoltDMClient = class {
404
516
  sharedSecretCopy.set(new Uint8Array(sharedSecret));
405
517
  const chainKeyCopy = new Uint8Array(32);
406
518
  chainKeyCopy.set(new Uint8Array(chainKey));
407
- const keyMaterial = await crypto.subtle.importKey(
408
- "raw",
409
- sharedSecretCopy.buffer,
410
- { name: "HKDF" },
411
- false,
412
- ["deriveKey"]
413
- );
519
+ const keyMaterial = await crypto.subtle.importKey("raw", sharedSecretCopy.buffer, { name: "HKDF" }, false, [
520
+ "deriveKey"
521
+ ]);
414
522
  const aesKey = await crypto.subtle.deriveKey(
415
- { name: "HKDF", hash: "SHA-256", salt: new Uint8Array(32), info: new TextEncoder().encode("moltdm-sender-key") },
523
+ {
524
+ name: "HKDF",
525
+ hash: "SHA-256",
526
+ salt: new Uint8Array(32),
527
+ info: new TextEncoder().encode("moltdm-sender-key")
528
+ },
416
529
  keyMaterial,
417
530
  { name: "AES-GCM", length: 256 },
418
531
  false,
419
532
  ["encrypt"]
420
533
  );
421
534
  const iv = crypto.getRandomValues(new Uint8Array(12));
422
- const encrypted = await crypto.subtle.encrypt(
423
- { name: "AES-GCM", iv },
424
- aesKey,
425
- chainKeyCopy.buffer
426
- );
535
+ const encrypted = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, aesKey, chainKeyCopy.buffer);
427
536
  const combined = new Uint8Array(32 + 12 + encrypted.byteLength);
428
537
  combined.set(ephemeralPublic);
429
538
  combined.set(iv, 32);
@@ -443,7 +552,19 @@ var MoltDMClient = class {
443
552
  */
444
553
  async decryptChainKey(encryptedBlob) {
445
554
  try {
555
+ if (!encryptedBlob) {
556
+ console.error("[decryptChainKey] No encrypted blob provided");
557
+ return null;
558
+ }
559
+ if (!this.identity?.signedPreKey?.privateKey) {
560
+ console.error("[decryptChainKey] Missing signedPreKey.privateKey in identity");
561
+ return null;
562
+ }
446
563
  const combined = fromBase64(encryptedBlob);
564
+ if (!combined || combined.length < 45) {
565
+ console.error("[decryptChainKey] Invalid encrypted blob length:", combined?.length);
566
+ return null;
567
+ }
447
568
  const ephemeralPublic = combined.slice(0, 32);
448
569
  const iv = combined.slice(32, 44);
449
570
  const encrypted = combined.slice(44);
@@ -457,17 +578,18 @@ var MoltDMClient = class {
457
578
  ["deriveKey"]
458
579
  );
459
580
  const aesKey = await crypto.subtle.deriveKey(
460
- { name: "HKDF", hash: "SHA-256", salt: new Uint8Array(32), info: new TextEncoder().encode("moltdm-sender-key") },
581
+ {
582
+ name: "HKDF",
583
+ hash: "SHA-256",
584
+ salt: new Uint8Array(32),
585
+ info: new TextEncoder().encode("moltdm-sender-key")
586
+ },
461
587
  keyMaterial,
462
588
  { name: "AES-GCM", length: 256 },
463
589
  false,
464
590
  ["decrypt"]
465
591
  );
466
- const decrypted = await crypto.subtle.decrypt(
467
- { name: "AES-GCM", iv },
468
- aesKey,
469
- encrypted
470
- );
592
+ const decrypted = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, aesKey, encrypted);
471
593
  return new Uint8Array(decrypted);
472
594
  } catch (e) {
473
595
  console.error("Failed to decrypt chain key:", e);
@@ -483,9 +605,11 @@ var MoltDMClient = class {
483
605
  const keyId = `${conversationId}:${fromId}`;
484
606
  let receivedKey = this.receivedSenderKeys.get(keyId);
485
607
  if (!encryptedSenderKeys) {
486
- console.error(`[decrypt] Message ${message.id} has no encryptedSenderKeys - was sent before Sender Keys implementation`);
608
+ console.error(`[decrypt] Message ${message.id} has no encryptedSenderKeys - sent before Sender Keys`);
487
609
  } else if (!encryptedSenderKeys[this.moltbotId]) {
488
- console.error(`[decrypt] Message ${message.id} has encryptedSenderKeys but not for us (${this.moltbotId}). Available: ${Object.keys(encryptedSenderKeys).join(", ")}`);
610
+ console.error(
611
+ `[decrypt] Message ${message.id} missing key for ${this.moltbotId}. Available: ${Object.keys(encryptedSenderKeys).join(", ")}`
612
+ );
489
613
  }
490
614
  if (encryptedSenderKeys && encryptedSenderKeys[this.moltbotId]) {
491
615
  if (!receivedKey || receivedKey.version !== senderKeyVersion) {
@@ -509,7 +633,7 @@ var MoltDMClient = class {
509
633
  }
510
634
  if (messageIndex > receivedKey.messageIndex) {
511
635
  const steps = messageIndex - receivedKey.messageIndex + 1;
512
- const { chainKey, messageKeys } = this.ratchetChainKeyN(receivedKey.chainKey, steps);
636
+ const { chainKey, messageKeys } = await this.ratchetChainKeyN(receivedKey.chainKey, steps);
513
637
  const messageKey = messageKeys[messageKeys.length - 1];
514
638
  receivedKey.chainKey = chainKey;
515
639
  receivedKey.messageIndex = messageIndex + 1;
@@ -517,14 +641,14 @@ var MoltDMClient = class {
517
641
  await this.saveSenderKeys();
518
642
  return this.decrypt(ciphertext, messageKey);
519
643
  } else if (messageIndex === receivedKey.messageIndex) {
520
- const messageKey = this.deriveMessageKey(receivedKey.chainKey);
521
- receivedKey.chainKey = this.ratchetChainKey(receivedKey.chainKey);
644
+ const messageKey = await this.deriveMessageKey(receivedKey.chainKey);
645
+ receivedKey.chainKey = await this.ratchetChainKey(receivedKey.chainKey);
522
646
  receivedKey.messageIndex++;
523
647
  this.receivedSenderKeys.set(keyId, receivedKey);
524
648
  await this.saveSenderKeys();
525
649
  return this.decrypt(ciphertext, messageKey);
526
650
  } else {
527
- console.error(`Message index ${messageIndex} is in the past (current: ${receivedKey.messageIndex})`);
651
+ console.error(`[decrypt] Message index ${messageIndex} is in the past (current: ${receivedKey.messageIndex})`);
528
652
  return null;
529
653
  }
530
654
  }
@@ -549,28 +673,22 @@ var MoltDMClient = class {
549
673
  // ============================================
550
674
  async react(conversationId, messageId, emoji) {
551
675
  this.ensureInitialized();
552
- const response = await this.fetch(
553
- `/api/conversations/${conversationId}/messages/${messageId}/reactions`,
554
- {
555
- method: "POST",
556
- body: JSON.stringify({ emoji })
557
- }
558
- );
676
+ const response = await this.fetch(`/api/conversations/${conversationId}/messages/${messageId}/reactions`, {
677
+ method: "POST",
678
+ body: JSON.stringify({ emoji })
679
+ });
559
680
  const data = await response.json();
560
681
  return data.reaction;
561
682
  }
562
683
  async unreact(conversationId, messageId, emoji) {
563
684
  this.ensureInitialized();
564
- await this.fetch(
565
- `/api/conversations/${conversationId}/messages/${messageId}/reactions/${encodeURIComponent(emoji)}`,
566
- { method: "DELETE" }
567
- );
685
+ await this.fetch(`/api/conversations/${conversationId}/messages/${messageId}/reactions/${encodeURIComponent(emoji)}`, {
686
+ method: "DELETE"
687
+ });
568
688
  }
569
689
  async getReactions(conversationId, messageId) {
570
690
  this.ensureInitialized();
571
- const response = await this.fetch(
572
- `/api/conversations/${conversationId}/messages/${messageId}/reactions`
573
- );
691
+ const response = await this.fetch(`/api/conversations/${conversationId}/messages/${messageId}/reactions`);
574
692
  const data = await response.json();
575
693
  return data.reactions;
576
694
  }
@@ -694,9 +812,7 @@ var MoltDMClient = class {
694
812
  const encryptionKeys = {
695
813
  identityKey: this.identity.publicKey,
696
814
  privateKey: this.identity.privateKey,
697
- // Ed25519 private key for signing
698
815
  signedPreKeyPrivate: this.identity.signedPreKey.privateKey,
699
- // X25519 private key for decrypting sender keys
700
816
  senderKeys: senderKeysObj
701
817
  };
702
818
  const response = await this.fetch("/api/pair/approve", {
@@ -736,16 +852,14 @@ var MoltDMClient = class {
736
852
  return data.events;
737
853
  }
738
854
  // ============================================
739
- // Encryption (Simplified for demo)
855
+ // Encryption
740
856
  // ============================================
741
857
  async encrypt(plaintext, key) {
742
858
  const iv = crypto.getRandomValues(new Uint8Array(12));
743
859
  const encoder = new TextEncoder();
744
860
  const data = encoder.encode(plaintext);
745
861
  const keyBuffer = new Uint8Array(key).buffer;
746
- const cryptoKey = await crypto.subtle.importKey("raw", keyBuffer, { name: "AES-GCM" }, false, [
747
- "encrypt"
748
- ]);
862
+ const cryptoKey = await crypto.subtle.importKey("raw", keyBuffer, { name: "AES-GCM" }, false, ["encrypt"]);
749
863
  const encrypted = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, cryptoKey, data);
750
864
  const combined = new Uint8Array(iv.length + encrypted.byteLength);
751
865
  combined.set(iv);
@@ -757,9 +871,7 @@ var MoltDMClient = class {
757
871
  const iv = combined.slice(0, 12);
758
872
  const encrypted = combined.slice(12);
759
873
  const keyBuffer = new Uint8Array(key).buffer;
760
- const cryptoKey = await crypto.subtle.importKey("raw", keyBuffer, { name: "AES-GCM" }, false, [
761
- "decrypt"
762
- ]);
874
+ const cryptoKey = await crypto.subtle.importKey("raw", keyBuffer, { name: "AES-GCM" }, false, ["decrypt"]);
763
875
  const decrypted = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, cryptoKey, encrypted);
764
876
  const decoder = new TextDecoder();
765
877
  return decoder.decode(decrypted);
@@ -772,22 +884,12 @@ var MoltDMClient = class {
772
884
  throw new Error("Not initialized. Call initialize() first.");
773
885
  }
774
886
  }
775
- /**
776
- * Sign a message using Ed25519
777
- */
778
887
  async signMessage(message) {
779
888
  const privateKeyBytes = fromBase64(this.identity.privateKey);
780
- const signature = await ed.signAsync(
781
- new TextEncoder().encode(message),
782
- privateKeyBytes
783
- );
889
+ const signature = await ed.signAsync(new TextEncoder().encode(message), privateKeyBytes);
784
890
  return toBase64(signature);
785
891
  }
786
- /**
787
- * Create the message to sign for a request
788
- * Format: timestamp:method:path:bodyHash
789
- */
790
- async createSignedMessage(timestamp, method, path2, body) {
892
+ async createSignedMessage(timestamp, method, path, body) {
791
893
  let bodyHash = "";
792
894
  if (body) {
793
895
  const bodyBytes = new TextEncoder().encode(body);
@@ -795,18 +897,15 @@ var MoltDMClient = class {
795
897
  const hashArray = Array.from(new Uint8Array(hashBuffer));
796
898
  bodyHash = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
797
899
  }
798
- return `${timestamp}:${method}:${path2}:${bodyHash}`;
900
+ return `${timestamp}:${method}:${path}:${bodyHash}`;
799
901
  }
800
- /**
801
- * Make an authenticated fetch request with Ed25519 signature
802
- */
803
- async fetch(path2, options = {}) {
902
+ async fetch(path, options = {}) {
804
903
  const method = options.method || "GET";
805
904
  const body = options.body;
806
905
  const timestamp = Date.now().toString();
807
- const message = await this.createSignedMessage(timestamp, method, path2, body);
906
+ const message = await this.createSignedMessage(timestamp, method, path, body);
808
907
  const signature = await this.signMessage(message);
809
- const response = await fetch(`${this.relayUrl}${path2}`, {
908
+ const response = await fetch(`${this.relayUrl}${path}`, {
810
909
  ...options,
811
910
  headers: {
812
911
  "Content-Type": "application/json",
@@ -826,5 +925,8 @@ var MoltDMClient = class {
826
925
  var index_default = MoltDMClient;
827
926
  // Annotate the CommonJS export names for ESM import in node:
828
927
  0 && (module.exports = {
928
+ BrowserStorage,
929
+ FileStorage,
930
+ MemoryStorage,
829
931
  MoltDMClient
830
932
  });