@reverbia/sdk 1.0.0-next.20251229084841 → 1.1.0-next.20251230221037

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.
@@ -2372,6 +2372,24 @@ async function getMemoriesByKeyOp(ctx, namespace, key) {
2372
2372
  return results.map(memoryToStored);
2373
2373
  }
2374
2374
  async function saveMemoryOp(ctx, opts) {
2375
+ if (!opts.namespace || typeof opts.namespace !== "string") {
2376
+ throw new Error("Namespace is required and must be a string");
2377
+ }
2378
+ if (!opts.key || typeof opts.key !== "string") {
2379
+ throw new Error("Key is required and must be a string");
2380
+ }
2381
+ if (!opts.value || typeof opts.value !== "string") {
2382
+ throw new Error("Value is required and must be a string");
2383
+ }
2384
+ if (!opts.type || typeof opts.type !== "string") {
2385
+ throw new Error("Type is required and must be a string");
2386
+ }
2387
+ if (typeof opts.confidence !== "number" || opts.confidence < 0 || opts.confidence > 1) {
2388
+ throw new Error("Confidence must be a number between 0 and 1");
2389
+ }
2390
+ if (typeof opts.pii !== "boolean") {
2391
+ throw new Error("PII must be a boolean");
2392
+ }
2375
2393
  const compositeKey = generateCompositeKey(opts.namespace, opts.key);
2376
2394
  const uniqueKey = generateUniqueKey(opts.namespace, opts.key, opts.value);
2377
2395
  const result = await ctx.database.write(async () => {
@@ -647,6 +647,12 @@ interface BaseUseMemoryStorageOptions {
647
647
  onFactsExtracted?: (facts: MemoryExtractionResult) => void;
648
648
  getToken?: () => Promise<string | null>;
649
649
  baseUrl?: string;
650
+ /** Wallet address for encryption (optional - encryption disabled if not provided) */
651
+ walletAddress?: string | null;
652
+ /** Function to request encryption key (optional - encryption disabled if not provided) */
653
+ requestEncryptionKey?: (address: string) => Promise<void>;
654
+ /** Function to sign message for migration (optional - migration disabled if not provided) */
655
+ signMessage?: (message: string) => Promise<string>;
650
656
  }
651
657
  interface BaseUseMemoryStorageResult {
652
658
  memories: StoredMemory[];
@@ -781,6 +787,12 @@ interface BaseUseChatStorageOptions {
781
787
  onData?: (chunk: string) => void;
782
788
  onFinish?: (response: LlmapiResponseResponse) => void;
783
789
  onError?: (error: Error) => void;
790
+ /** Wallet address for encryption (optional - encryption disabled if not provided) */
791
+ walletAddress?: string | null;
792
+ /** Function to request encryption key (optional - encryption disabled if not provided) */
793
+ requestEncryptionKey?: (address: string) => Promise<void>;
794
+ /** Function to sign message for migration (optional - required for migrating old encrypted data) */
795
+ signMessage?: (message: string) => Promise<string>;
784
796
  }
785
797
  interface BaseSendMessageWithStorageArgs {
786
798
  content: string;
@@ -647,6 +647,12 @@ interface BaseUseMemoryStorageOptions {
647
647
  onFactsExtracted?: (facts: MemoryExtractionResult) => void;
648
648
  getToken?: () => Promise<string | null>;
649
649
  baseUrl?: string;
650
+ /** Wallet address for encryption (optional - encryption disabled if not provided) */
651
+ walletAddress?: string | null;
652
+ /** Function to request encryption key (optional - encryption disabled if not provided) */
653
+ requestEncryptionKey?: (address: string) => Promise<void>;
654
+ /** Function to sign message for migration (optional - migration disabled if not provided) */
655
+ signMessage?: (message: string) => Promise<string>;
650
656
  }
651
657
  interface BaseUseMemoryStorageResult {
652
658
  memories: StoredMemory[];
@@ -781,6 +787,12 @@ interface BaseUseChatStorageOptions {
781
787
  onData?: (chunk: string) => void;
782
788
  onFinish?: (response: LlmapiResponseResponse) => void;
783
789
  onError?: (error: Error) => void;
790
+ /** Wallet address for encryption (optional - encryption disabled if not provided) */
791
+ walletAddress?: string | null;
792
+ /** Function to request encryption key (optional - encryption disabled if not provided) */
793
+ requestEncryptionKey?: (address: string) => Promise<void>;
794
+ /** Function to sign message for migration (optional - required for migrating old encrypted data) */
795
+ signMessage?: (message: string) => Promise<string>;
784
796
  }
785
797
  interface BaseSendMessageWithStorageArgs {
786
798
  content: string;
@@ -2336,6 +2336,24 @@ async function getMemoriesByKeyOp(ctx, namespace, key) {
2336
2336
  return results.map(memoryToStored);
2337
2337
  }
2338
2338
  async function saveMemoryOp(ctx, opts) {
2339
+ if (!opts.namespace || typeof opts.namespace !== "string") {
2340
+ throw new Error("Namespace is required and must be a string");
2341
+ }
2342
+ if (!opts.key || typeof opts.key !== "string") {
2343
+ throw new Error("Key is required and must be a string");
2344
+ }
2345
+ if (!opts.value || typeof opts.value !== "string") {
2346
+ throw new Error("Value is required and must be a string");
2347
+ }
2348
+ if (!opts.type || typeof opts.type !== "string") {
2349
+ throw new Error("Type is required and must be a string");
2350
+ }
2351
+ if (typeof opts.confidence !== "number" || opts.confidence < 0 || opts.confidence > 1) {
2352
+ throw new Error("Confidence must be a number between 0 and 1");
2353
+ }
2354
+ if (typeof opts.pii !== "boolean") {
2355
+ throw new Error("PII must be a boolean");
2356
+ }
2339
2357
  const compositeKey = generateCompositeKey(opts.namespace, opts.key);
2340
2358
  const uniqueKey = generateUniqueKey(opts.namespace, opts.key, opts.value);
2341
2359
  const result = await ctx.database.write(async () => {
@@ -0,0 +1,290 @@
1
+ import {
2
+ decryptData,
3
+ encryptData,
4
+ hasEncryptionKey
5
+ } from "./chunk-T56Y62G7.mjs";
6
+
7
+ // src/lib/backup/oauth/storage.ts
8
+ var STORAGE_KEY_PREFIX = "oauth_token_";
9
+ var ENCRYPTION_PREFIX = "enc:";
10
+ var SESSION_KEY_STORAGE_KEY = "oauth_session_encryption_key";
11
+ function getStorageKey(provider) {
12
+ return `${STORAGE_KEY_PREFIX}${provider}`;
13
+ }
14
+ function isEncrypted(value) {
15
+ return value.startsWith(ENCRYPTION_PREFIX);
16
+ }
17
+ async function getOrCreateSessionKey() {
18
+ if (typeof window === "undefined") {
19
+ throw new Error("Session key generation requires browser environment");
20
+ }
21
+ let sessionKey = sessionStorage.getItem(SESSION_KEY_STORAGE_KEY);
22
+ if (sessionKey) {
23
+ return sessionKey;
24
+ }
25
+ const keyBytes = crypto.getRandomValues(new Uint8Array(32));
26
+ sessionKey = Array.from(keyBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
27
+ sessionStorage.setItem(SESSION_KEY_STORAGE_KEY, sessionKey);
28
+ return sessionKey;
29
+ }
30
+ async function encryptWithSessionKey(plaintext) {
31
+ const sessionKeyHex = await getOrCreateSessionKey();
32
+ const keyBytes = hexToBytes(sessionKeyHex);
33
+ const key = await crypto.subtle.importKey(
34
+ "raw",
35
+ keyBytes.buffer,
36
+ { name: "AES-GCM" },
37
+ false,
38
+ ["encrypt", "decrypt"]
39
+ );
40
+ const iv = crypto.getRandomValues(new Uint8Array(12));
41
+ const plaintextBytes = new TextEncoder().encode(plaintext);
42
+ const encryptedData = await crypto.subtle.encrypt(
43
+ { name: "AES-GCM", iv },
44
+ key,
45
+ plaintextBytes.buffer
46
+ );
47
+ const encryptedBytes = new Uint8Array(encryptedData);
48
+ const combined = new Uint8Array(iv.length + encryptedBytes.length);
49
+ combined.set(iv, 0);
50
+ combined.set(encryptedBytes, iv.length);
51
+ return Array.from(combined).map((b) => b.toString(16).padStart(2, "0")).join("");
52
+ }
53
+ async function decryptWithSessionKey(encryptedHex) {
54
+ const sessionKeyHex = sessionStorage.getItem(SESSION_KEY_STORAGE_KEY);
55
+ if (!sessionKeyHex) {
56
+ throw new Error("Session key not found");
57
+ }
58
+ const keyBytes = hexToBytes(sessionKeyHex);
59
+ const key = await crypto.subtle.importKey(
60
+ "raw",
61
+ keyBytes.buffer,
62
+ { name: "AES-GCM" },
63
+ false,
64
+ ["encrypt", "decrypt"]
65
+ );
66
+ const combined = hexToBytes(encryptedHex);
67
+ const iv = combined.slice(0, 12);
68
+ const encryptedData = combined.slice(12);
69
+ const decryptedData = await crypto.subtle.decrypt(
70
+ { name: "AES-GCM", iv },
71
+ key,
72
+ encryptedData
73
+ );
74
+ return new TextDecoder().decode(decryptedData);
75
+ }
76
+ function hexToBytes(hex) {
77
+ const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
78
+ const bytes = new Uint8Array(cleanHex.length / 2);
79
+ for (let i = 0; i < cleanHex.length; i += 2) {
80
+ bytes[i / 2] = parseInt(cleanHex.slice(i, i + 2), 16);
81
+ }
82
+ return bytes;
83
+ }
84
+ async function reEncryptUnencryptedTokens(walletAddress) {
85
+ if (typeof window === "undefined") return;
86
+ if (!hasEncryptionKey(walletAddress)) return;
87
+ const providers = ["google-drive", "dropbox"];
88
+ for (const provider of providers) {
89
+ try {
90
+ const stored = localStorage.getItem(getStorageKey(provider));
91
+ if (!stored) continue;
92
+ if (!isEncrypted(stored)) {
93
+ try {
94
+ const data = JSON.parse(stored);
95
+ if (data.accessToken) {
96
+ await storeTokenData(provider, data, walletAddress);
97
+ }
98
+ } catch {
99
+ continue;
100
+ }
101
+ } else {
102
+ const encryptedPayload = stored.slice(ENCRYPTION_PREFIX.length);
103
+ try {
104
+ const decrypted = await decryptWithSessionKey(encryptedPayload);
105
+ const data = JSON.parse(decrypted);
106
+ if (data.accessToken) {
107
+ await storeTokenData(provider, data, walletAddress);
108
+ }
109
+ } catch {
110
+ continue;
111
+ }
112
+ }
113
+ } catch {
114
+ continue;
115
+ }
116
+ }
117
+ }
118
+ async function getStoredTokenData(provider, walletAddress) {
119
+ if (typeof window === "undefined") return null;
120
+ try {
121
+ const stored = localStorage.getItem(getStorageKey(provider));
122
+ if (!stored) return null;
123
+ if (isEncrypted(stored)) {
124
+ const encryptedPayload = stored.slice(ENCRYPTION_PREFIX.length);
125
+ if (walletAddress && hasEncryptionKey(walletAddress)) {
126
+ try {
127
+ const decrypted = await decryptData(encryptedPayload, walletAddress);
128
+ const data = JSON.parse(decrypted);
129
+ if (!data.accessToken) return null;
130
+ return data;
131
+ } catch {
132
+ }
133
+ }
134
+ try {
135
+ const decrypted = await decryptWithSessionKey(encryptedPayload);
136
+ const data = JSON.parse(decrypted);
137
+ if (!data.accessToken) return null;
138
+ if (walletAddress && hasEncryptionKey(walletAddress)) {
139
+ try {
140
+ await storeTokenData(provider, data, walletAddress);
141
+ } catch {
142
+ console.error(`Failed to re-encrypt OAuth token for ${provider}`);
143
+ }
144
+ }
145
+ return data;
146
+ } catch {
147
+ return null;
148
+ }
149
+ } else {
150
+ try {
151
+ const data = JSON.parse(stored);
152
+ if (!data.accessToken) return null;
153
+ if (walletAddress && hasEncryptionKey(walletAddress)) {
154
+ try {
155
+ await storeTokenData(provider, data, walletAddress);
156
+ } catch {
157
+ console.error(`Failed to re-encrypt OAuth token for ${provider}`);
158
+ }
159
+ } else {
160
+ try {
161
+ await storeTokenData(provider, data);
162
+ } catch {
163
+ console.error(`Failed to encrypt OAuth token for ${provider}`);
164
+ }
165
+ }
166
+ return data;
167
+ } catch {
168
+ return null;
169
+ }
170
+ }
171
+ } catch {
172
+ return null;
173
+ }
174
+ }
175
+ function getStoredTokenDataSync(provider) {
176
+ if (typeof window === "undefined") return null;
177
+ try {
178
+ const stored = localStorage.getItem(getStorageKey(provider));
179
+ if (!stored) return null;
180
+ if (isEncrypted(stored)) {
181
+ return null;
182
+ }
183
+ const data = JSON.parse(stored);
184
+ if (!data.accessToken) return null;
185
+ return data;
186
+ } catch {
187
+ return null;
188
+ }
189
+ }
190
+ async function storeTokenData(provider, data, walletAddress) {
191
+ if (typeof window === "undefined") return;
192
+ const jsonData = JSON.stringify(data);
193
+ if (walletAddress && hasEncryptionKey(walletAddress)) {
194
+ try {
195
+ const encrypted = await encryptData(jsonData, walletAddress);
196
+ localStorage.setItem(getStorageKey(provider), `${ENCRYPTION_PREFIX}${encrypted}`);
197
+ return;
198
+ } catch (error) {
199
+ const message = error instanceof Error ? error.message : "Unknown encryption error";
200
+ throw new Error(`OAuth token encryption failed: ${message}`);
201
+ }
202
+ }
203
+ try {
204
+ const encrypted = await encryptWithSessionKey(jsonData);
205
+ localStorage.setItem(getStorageKey(provider), `${ENCRYPTION_PREFIX}${encrypted}`);
206
+ } catch (error) {
207
+ const sessionStorageAvailable = typeof window !== "undefined" && typeof window.sessionStorage !== "undefined" && typeof window.sessionStorage.getItem === "function" && typeof window.sessionStorage.setItem === "function";
208
+ if (!sessionStorageAvailable) {
209
+ localStorage.setItem(getStorageKey(provider), jsonData);
210
+ } else {
211
+ const message = error instanceof Error ? error.message : "Unknown encryption error";
212
+ throw new Error(`OAuth token encryption failed: ${message}`);
213
+ }
214
+ }
215
+ }
216
+ function clearTokenData(provider) {
217
+ if (typeof window === "undefined") return;
218
+ localStorage.removeItem(getStorageKey(provider));
219
+ }
220
+ function isTokenExpired(data, bufferSeconds = 60) {
221
+ if (!data) return true;
222
+ if (!data.expiresAt) return false;
223
+ const now = Date.now();
224
+ const bufferMs = bufferSeconds * 1e3;
225
+ return data.expiresAt - bufferMs <= now;
226
+ }
227
+ async function getValidAccessToken(provider, walletAddress) {
228
+ const data = await getStoredTokenData(provider, walletAddress);
229
+ if (!data) return null;
230
+ if (data.expiresAt && isTokenExpired(data)) {
231
+ return null;
232
+ }
233
+ return data.accessToken;
234
+ }
235
+ function getValidAccessTokenSync(provider) {
236
+ const data = getStoredTokenDataSync(provider);
237
+ if (!data) return null;
238
+ if (data.expiresAt && isTokenExpired(data)) {
239
+ return null;
240
+ }
241
+ return data.accessToken;
242
+ }
243
+ async function getRefreshToken(provider, walletAddress) {
244
+ const data = await getStoredTokenData(provider, walletAddress);
245
+ return data?.refreshToken ?? null;
246
+ }
247
+ function getRefreshTokenSync(provider) {
248
+ const data = getStoredTokenDataSync(provider);
249
+ return data?.refreshToken ?? null;
250
+ }
251
+ function hasStoredCredentialsSync(provider) {
252
+ if (typeof window === "undefined") return false;
253
+ const stored = localStorage.getItem(getStorageKey(provider));
254
+ if (!stored) return false;
255
+ if (isEncrypted(stored)) {
256
+ return true;
257
+ }
258
+ try {
259
+ const data = JSON.parse(stored);
260
+ return !!(data?.accessToken || data?.refreshToken);
261
+ } catch {
262
+ return false;
263
+ }
264
+ }
265
+ function tokenResponseToStoredData(accessToken, expiresIn, refreshToken, scope) {
266
+ const data = {
267
+ accessToken,
268
+ refreshToken,
269
+ scope
270
+ };
271
+ if (expiresIn) {
272
+ data.expiresAt = Date.now() + expiresIn * 1e3;
273
+ }
274
+ return data;
275
+ }
276
+
277
+ export {
278
+ reEncryptUnencryptedTokens,
279
+ getStoredTokenData,
280
+ getStoredTokenDataSync,
281
+ storeTokenData,
282
+ clearTokenData,
283
+ isTokenExpired,
284
+ getValidAccessToken,
285
+ getValidAccessTokenSync,
286
+ getRefreshToken,
287
+ getRefreshTokenSync,
288
+ hasStoredCredentialsSync,
289
+ tokenResponseToStoredData
290
+ };