@affectively/dash 5.2.1 → 5.3.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.
Files changed (71) hide show
  1. package/dist/index.d.ts +62 -0
  2. package/dist/index.js +31 -0
  3. package/dist/sync/index.d.ts +6 -0
  4. package/dist/sync/index.js +4 -0
  5. package/package.json +45 -66
  6. package/README.md +0 -193
  7. package/dist/src/api/firebase/auth/index.d.ts +0 -137
  8. package/dist/src/api/firebase/auth/index.js +0 -352
  9. package/dist/src/api/firebase/auth/providers.d.ts +0 -254
  10. package/dist/src/api/firebase/auth/providers.js +0 -518
  11. package/dist/src/api/firebase/database/index.d.ts +0 -108
  12. package/dist/src/api/firebase/database/index.js +0 -368
  13. package/dist/src/api/firebase/errors.d.ts +0 -15
  14. package/dist/src/api/firebase/errors.js +0 -215
  15. package/dist/src/api/firebase/firestore/data-types.d.ts +0 -116
  16. package/dist/src/api/firebase/firestore/data-types.js +0 -280
  17. package/dist/src/api/firebase/firestore/index.d.ts +0 -7
  18. package/dist/src/api/firebase/firestore/index.js +0 -13
  19. package/dist/src/api/firebase/firestore/listeners.d.ts +0 -20
  20. package/dist/src/api/firebase/firestore/listeners.js +0 -50
  21. package/dist/src/api/firebase/firestore/operations.d.ts +0 -123
  22. package/dist/src/api/firebase/firestore/operations.js +0 -490
  23. package/dist/src/api/firebase/firestore/query.d.ts +0 -118
  24. package/dist/src/api/firebase/firestore/query.js +0 -418
  25. package/dist/src/api/firebase/index.d.ts +0 -11
  26. package/dist/src/api/firebase/index.js +0 -17
  27. package/dist/src/api/firebase/storage/index.d.ts +0 -100
  28. package/dist/src/api/firebase/storage/index.js +0 -286
  29. package/dist/src/api/firebase/types.d.ts +0 -341
  30. package/dist/src/api/firebase/types.js +0 -4
  31. package/dist/src/auth/manager.d.ts +0 -182
  32. package/dist/src/auth/manager.js +0 -598
  33. package/dist/src/engine/ai.d.ts +0 -10
  34. package/dist/src/engine/ai.js +0 -76
  35. package/dist/src/engine/sqlite.d.ts +0 -298
  36. package/dist/src/engine/sqlite.js +0 -1088
  37. package/dist/src/engine/vec_extension.d.ts +0 -5
  38. package/dist/src/engine/vec_extension.js +0 -10
  39. package/dist/src/index.d.ts +0 -15
  40. package/dist/src/index.js +0 -24
  41. package/dist/src/mcp/server.d.ts +0 -8
  42. package/dist/src/mcp/server.js +0 -87
  43. package/dist/src/reactivity/signal.d.ts +0 -3
  44. package/dist/src/reactivity/signal.js +0 -31
  45. package/dist/src/schema/lens.d.ts +0 -29
  46. package/dist/src/schema/lens.js +0 -122
  47. package/dist/src/sync/aeon/config.d.ts +0 -21
  48. package/dist/src/sync/aeon/config.js +0 -14
  49. package/dist/src/sync/aeon/delta-adapter.d.ts +0 -62
  50. package/dist/src/sync/aeon/delta-adapter.js +0 -98
  51. package/dist/src/sync/aeon/index.d.ts +0 -18
  52. package/dist/src/sync/aeon/index.js +0 -19
  53. package/dist/src/sync/aeon/offline-adapter.d.ts +0 -110
  54. package/dist/src/sync/aeon/offline-adapter.js +0 -227
  55. package/dist/src/sync/aeon/presence-adapter.d.ts +0 -114
  56. package/dist/src/sync/aeon/presence-adapter.js +0 -157
  57. package/dist/src/sync/aeon/schema-adapter.d.ts +0 -95
  58. package/dist/src/sync/aeon/schema-adapter.js +0 -163
  59. package/dist/src/sync/backup.d.ts +0 -12
  60. package/dist/src/sync/backup.js +0 -44
  61. package/dist/src/sync/connection.d.ts +0 -20
  62. package/dist/src/sync/connection.js +0 -50
  63. package/dist/src/sync/d1-provider.d.ts +0 -97
  64. package/dist/src/sync/d1-provider.js +0 -345
  65. package/dist/src/sync/hybrid-provider.d.ts +0 -172
  66. package/dist/src/sync/hybrid-provider.js +0 -477
  67. package/dist/src/sync/provider.d.ts +0 -11
  68. package/dist/src/sync/provider.js +0 -67
  69. package/dist/src/sync/verify.d.ts +0 -1
  70. package/dist/src/sync/verify.js +0 -23
  71. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -1,598 +0,0 @@
1
- import * as ucans from 'ucans';
2
- const DB_NAME = 'dash-auth';
3
- const DB_VERSION = 1;
4
- const STORE_NAME = 'identity';
5
- const IDENTITY_KEY = 'primary';
6
- /**
7
- * Check if IndexedDB is available
8
- */
9
- function isIndexedDBAvailable() {
10
- try {
11
- return typeof indexedDB !== 'undefined' && indexedDB !== null;
12
- }
13
- catch {
14
- return false;
15
- }
16
- }
17
- /**
18
- * Opens the IndexedDB database for auth storage
19
- * Returns null if IndexedDB is not available
20
- */
21
- async function openAuthDB() {
22
- if (!isIndexedDBAvailable()) {
23
- return null;
24
- }
25
- return new Promise((resolve, reject) => {
26
- const request = indexedDB.open(DB_NAME, DB_VERSION);
27
- request.onerror = () => reject(request.error);
28
- request.onsuccess = () => resolve(request.result);
29
- request.onupgradeneeded = (event) => {
30
- const db = event.target.result;
31
- if (!db.objectStoreNames.contains(STORE_NAME)) {
32
- db.createObjectStore(STORE_NAME);
33
- }
34
- };
35
- });
36
- }
37
- /**
38
- * Derives an encryption key from a password using PBKDF2
39
- */
40
- async function deriveKeyFromPassword(password, salt) {
41
- const encoder = new TextEncoder();
42
- const keyMaterial = await crypto.subtle.importKey('raw', encoder.encode(password), 'PBKDF2', false, ['deriveKey']);
43
- // Create a copy of salt to ensure it's a proper ArrayBuffer
44
- const saltBuffer = new Uint8Array(salt).buffer;
45
- return crypto.subtle.deriveKey({
46
- name: 'PBKDF2',
47
- salt: saltBuffer,
48
- iterations: 100000,
49
- hash: 'SHA-256'
50
- }, keyMaterial, { name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt']);
51
- }
52
- /**
53
- * Encrypts string data with AES-GCM
54
- */
55
- async function encryptString(data, key) {
56
- const encoder = new TextEncoder();
57
- const dataBytes = encoder.encode(data);
58
- const iv = crypto.getRandomValues(new Uint8Array(12));
59
- // Create copies to ensure proper ArrayBuffer (not SharedArrayBuffer)
60
- const ivBuffer = new Uint8Array(iv).buffer;
61
- const dataBuffer = new Uint8Array(dataBytes).buffer;
62
- const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv: ivBuffer }, key, dataBuffer);
63
- return { encrypted: new Uint8Array(encrypted), iv };
64
- }
65
- /**
66
- * Decrypts data with AES-GCM and returns as string
67
- */
68
- async function decryptString(encrypted, iv, key) {
69
- // Create copies to ensure proper ArrayBuffer (not SharedArrayBuffer)
70
- const ivBuffer = new Uint8Array(iv).buffer;
71
- const encryptedBuffer = new Uint8Array(encrypted).buffer;
72
- const decrypted = await crypto.subtle.decrypt({ name: 'AES-GCM', iv: ivBuffer }, key, encryptedBuffer);
73
- const decoder = new TextDecoder();
74
- return decoder.decode(decrypted);
75
- }
76
- /**
77
- * Converts Uint8Array to base64 string
78
- */
79
- function uint8ArrayToBase64(arr) {
80
- return btoa(String.fromCharCode(...arr));
81
- }
82
- /**
83
- * Converts base64 string to Uint8Array
84
- */
85
- function base64ToUint8Array(base64) {
86
- const binary = atob(base64);
87
- const bytes = new Uint8Array(binary.length);
88
- for (let i = 0; i < binary.length; i++) {
89
- bytes[i] = binary.charCodeAt(i);
90
- }
91
- return bytes;
92
- }
93
- export class AuthManager {
94
- keypair = null;
95
- did = null;
96
- oauthIdentities = [];
97
- relayUrl;
98
- initialized = false;
99
- constructor(relayUrl = 'https://relay.buley.dev') {
100
- this.relayUrl = relayUrl;
101
- }
102
- /**
103
- * Initialize the auth manager, loading existing keypair or creating new one
104
- */
105
- async init() {
106
- if (this.initialized && this.did) {
107
- return this.did;
108
- }
109
- // Try to load existing keypair from IndexedDB
110
- const loaded = await this.loadKeypair();
111
- if (!loaded) {
112
- // Create new keypair if none exists (exportable so we can save it)
113
- this.keypair = await ucans.EdKeypair.create({ exportable: true });
114
- this.did = this.keypair.did();
115
- }
116
- this.initialized = true;
117
- return this.did;
118
- }
119
- /**
120
- * Get the DID (Decentralized Identifier)
121
- */
122
- getDID() {
123
- if (!this.did)
124
- throw new Error("AuthManager not initialized");
125
- return this.did;
126
- }
127
- /**
128
- * Get linked OAuth identities
129
- */
130
- getLinkedIdentities() {
131
- return [...this.oauthIdentities];
132
- }
133
- /**
134
- * Check if initialized
135
- */
136
- isInitialized() {
137
- return this.initialized && this.keypair !== null;
138
- }
139
- /**
140
- * Save the keypair to IndexedDB (encrypted with password)
141
- * This enables persistent identity across sessions
142
- */
143
- async saveKeypair(password) {
144
- if (!this.keypair || !this.did) {
145
- throw new Error("No keypair to save - call init() first");
146
- }
147
- // Generate salt for PBKDF2
148
- const salt = crypto.getRandomValues(new Uint8Array(16));
149
- // Derive encryption key from password
150
- const encryptionKey = await deriveKeyFromPassword(password, salt);
151
- // Export the keypair's private key (returns base64-encoded string)
152
- const privateKeyString = await this.keypair.export();
153
- // Encrypt the private key string
154
- const { encrypted, iv } = await encryptString(privateKeyString, encryptionKey);
155
- // Combine IV and encrypted data
156
- const combined = new Uint8Array(iv.length + encrypted.length);
157
- combined.set(iv);
158
- combined.set(encrypted, iv.length);
159
- // Prepare stored identity
160
- const storedIdentity = {
161
- did: this.did,
162
- publicKey: this.did, // DID contains the public key
163
- encryptedPrivateKey: uint8ArrayToBase64(combined),
164
- salt: uint8ArrayToBase64(salt),
165
- oauthIdentities: this.oauthIdentities,
166
- createdAt: new Date().toISOString(),
167
- updatedAt: new Date().toISOString()
168
- };
169
- // Store in IndexedDB (skip if not available)
170
- const db = await openAuthDB();
171
- if (!db) {
172
- console.warn('[AuthManager] IndexedDB not available, keypair not persisted');
173
- return;
174
- }
175
- return new Promise((resolve, reject) => {
176
- const tx = db.transaction(STORE_NAME, 'readwrite');
177
- const store = tx.objectStore(STORE_NAME);
178
- const request = store.put(storedIdentity, IDENTITY_KEY);
179
- request.onerror = () => reject(request.error);
180
- request.onsuccess = () => resolve();
181
- tx.oncomplete = () => db.close();
182
- });
183
- }
184
- /**
185
- * Load keypair from IndexedDB (requires password to decrypt)
186
- * Returns true if successfully loaded, false if no stored identity
187
- */
188
- async loadKeypair(password) {
189
- try {
190
- const db = await openAuthDB();
191
- if (!db) {
192
- // IndexedDB not available
193
- return false;
194
- }
195
- const storedIdentity = await new Promise((resolve, reject) => {
196
- const tx = db.transaction(STORE_NAME, 'readonly');
197
- const store = tx.objectStore(STORE_NAME);
198
- const request = store.get(IDENTITY_KEY);
199
- request.onerror = () => reject(request.error);
200
- request.onsuccess = () => resolve(request.result);
201
- tx.oncomplete = () => db.close();
202
- });
203
- if (!storedIdentity) {
204
- return false;
205
- }
206
- // If no password provided, we can only restore the DID (not the full keypair)
207
- if (!password) {
208
- this.did = storedIdentity.did;
209
- this.oauthIdentities = storedIdentity.oauthIdentities || [];
210
- // Keypair remains null - user needs to provide password for signing operations
211
- return true;
212
- }
213
- // Decrypt the private key
214
- const salt = base64ToUint8Array(storedIdentity.salt);
215
- const encryptionKey = await deriveKeyFromPassword(password, salt);
216
- const combined = base64ToUint8Array(storedIdentity.encryptedPrivateKey);
217
- const iv = combined.slice(0, 12);
218
- const encrypted = combined.slice(12);
219
- const privateKeyString = await decryptString(encrypted, iv, encryptionKey);
220
- // Reconstruct the keypair from the private key (base64 string)
221
- this.keypair = ucans.EdKeypair.fromSecretKey(privateKeyString, { exportable: true });
222
- this.did = this.keypair.did();
223
- this.oauthIdentities = storedIdentity.oauthIdentities || [];
224
- return true;
225
- }
226
- catch (error) {
227
- console.error('[AuthManager] Failed to load keypair:', error);
228
- return false;
229
- }
230
- }
231
- /**
232
- * Bind an OAuth identity to this UCAN identity
233
- * Verifies the OAuth token with the Relay and stores the binding
234
- */
235
- async bindOAuthIdentity(provider, oauthToken, password) {
236
- if (!this.keypair || !this.did) {
237
- throw new Error("AuthManager not initialized");
238
- }
239
- // Verify OAuth token with Relay and get identity info
240
- const response = await fetch(`${this.relayUrl}/auth/oauth/${provider}`, {
241
- method: 'POST',
242
- headers: {
243
- 'Content-Type': 'application/json'
244
- },
245
- body: JSON.stringify({
246
- oauthToken,
247
- did: this.did
248
- })
249
- });
250
- if (!response.ok) {
251
- const error = await response.text();
252
- throw new Error(`OAuth verification failed: ${error}`);
253
- }
254
- const identity = await response.json();
255
- // Add to linked identities (prevent duplicates)
256
- const existingIndex = this.oauthIdentities.findIndex(i => i.provider === provider && i.providerId === identity.providerId);
257
- if (existingIndex >= 0) {
258
- this.oauthIdentities[existingIndex] = identity;
259
- }
260
- else {
261
- this.oauthIdentities.push(identity);
262
- }
263
- // Re-save with updated identities
264
- await this.saveKeypair(password);
265
- return identity;
266
- }
267
- /**
268
- * Recover keypair from cloud escrow using password
269
- * Fetches encrypted escrow from Relay and decrypts locally
270
- */
271
- async recoverFromEscrow(provider, oauthToken, password) {
272
- // First, verify OAuth to prove identity
273
- const verifyResponse = await fetch(`${this.relayUrl}/auth/oauth/${provider}/verify`, {
274
- method: 'POST',
275
- headers: {
276
- 'Content-Type': 'application/json'
277
- },
278
- body: JSON.stringify({ oauthToken })
279
- });
280
- if (!verifyResponse.ok) {
281
- throw new Error('OAuth verification failed');
282
- }
283
- const { userId } = await verifyResponse.json();
284
- // Fetch encrypted escrow
285
- const escrowResponse = await fetch(`${this.relayUrl}/auth/escrow/${userId}`, {
286
- method: 'GET',
287
- headers: {
288
- 'Authorization': `Bearer ${oauthToken}`
289
- }
290
- });
291
- if (!escrowResponse.ok) {
292
- if (escrowResponse.status === 404) {
293
- return false; // No escrow found
294
- }
295
- throw new Error('Failed to fetch escrow');
296
- }
297
- const escrow = await escrowResponse.json();
298
- // Decrypt using password
299
- const salt = base64ToUint8Array(escrow.salt);
300
- const encryptionKey = await deriveKeyFromPassword(password, salt);
301
- const combined = base64ToUint8Array(escrow.encryptedKeypair);
302
- const iv = combined.slice(0, 12);
303
- const encrypted = combined.slice(12);
304
- try {
305
- const privateKeyString = await decryptString(encrypted, iv, encryptionKey);
306
- // Reconstruct keypair from base64 string
307
- this.keypair = ucans.EdKeypair.fromSecretKey(privateKeyString, { exportable: true });
308
- this.did = this.keypair.did();
309
- this.oauthIdentities = escrow.linkedIdentities || [];
310
- // Save locally
311
- await this.saveKeypair(password);
312
- this.initialized = true;
313
- return true;
314
- }
315
- catch (error) {
316
- throw new Error('Invalid password - decryption failed');
317
- }
318
- }
319
- /**
320
- * Upload keypair escrow to cloud for recovery
321
- * The escrow is encrypted with the user's password
322
- */
323
- async createEscrow(password) {
324
- if (!this.keypair || !this.did) {
325
- throw new Error("AuthManager not initialized");
326
- }
327
- // Generate salt
328
- const salt = crypto.getRandomValues(new Uint8Array(16));
329
- const encryptionKey = await deriveKeyFromPassword(password, salt);
330
- // Export and encrypt private key (export returns base64 string)
331
- const privateKeyString = await this.keypair.export();
332
- const { encrypted, iv } = await encryptString(privateKeyString, encryptionKey);
333
- const combined = new Uint8Array(iv.length + encrypted.length);
334
- combined.set(iv);
335
- combined.set(encrypted, iv.length);
336
- // Upload to relay
337
- const response = await fetch(`${this.relayUrl}/auth/escrow`, {
338
- method: 'POST',
339
- headers: {
340
- 'Content-Type': 'application/json'
341
- },
342
- body: JSON.stringify({
343
- did: this.did,
344
- encryptedKeypair: uint8ArrayToBase64(combined),
345
- salt: uint8ArrayToBase64(salt),
346
- linkedIdentities: this.oauthIdentities
347
- })
348
- });
349
- if (!response.ok) {
350
- throw new Error('Failed to create escrow');
351
- }
352
- }
353
- /**
354
- * Issue a UCAN token for room access
355
- */
356
- async issueRoomToken(roomAudience, roomName) {
357
- // Auto-initialize if not already done
358
- if (!this.keypair) {
359
- await this.init();
360
- }
361
- if (!this.keypair) {
362
- throw new Error("Keypair not loaded - provide password to unlock");
363
- }
364
- const capability = {
365
- with: { scheme: "room", hierPart: `//${roomName}` },
366
- can: { namespace: "room", segments: ["write"] }
367
- };
368
- const ucan = await ucans.build({
369
- audience: roomAudience,
370
- issuer: this.keypair,
371
- capabilities: [capability],
372
- lifetimeInSeconds: 3600
373
- });
374
- return ucans.encode(ucan);
375
- }
376
- /**
377
- * Issue a UCAN token with custom capabilities
378
- */
379
- async issueToken(audience, capabilities, lifetimeInSeconds = 3600) {
380
- if (!this.keypair) {
381
- throw new Error("Keypair not loaded - provide password to unlock");
382
- }
383
- const ucan = await ucans.build({
384
- audience,
385
- issuer: this.keypair,
386
- capabilities,
387
- lifetimeInSeconds
388
- });
389
- return ucans.encode(ucan);
390
- }
391
- /**
392
- * Clear local identity (logout)
393
- */
394
- /**
395
- * Clear local identity (logout)
396
- * @param injectedDb (for testing only) - pass a mock IndexedDB instance
397
- */
398
- async clearIdentity(injectedDb) {
399
- this.keypair = null;
400
- this.did = null;
401
- this.oauthIdentities = [];
402
- this.initialized = false;
403
- try {
404
- const db = injectedDb || await openAuthDB();
405
- if (!db) {
406
- return; // IndexedDB not available
407
- }
408
- await new Promise((resolve, reject) => {
409
- let tx, store, request;
410
- try {
411
- tx = db.transaction(STORE_NAME, 'readwrite');
412
- store = tx.objectStore(STORE_NAME);
413
- request = store.delete(IDENTITY_KEY);
414
- }
415
- catch (err) {
416
- reject(err);
417
- return;
418
- }
419
- request.onerror = () => reject(request.error);
420
- request.onsuccess = () => resolve();
421
- if (tx && typeof tx.oncomplete === 'function') {
422
- tx.oncomplete = () => db.close();
423
- }
424
- });
425
- }
426
- catch (error) {
427
- console.error('[AuthManager] Failed to clear identity:', error);
428
- }
429
- }
430
- // ============================================
431
- // INTROSPECTION METHODS FOR DASH-STUDIO
432
- // ============================================
433
- /**
434
- * Parse a UCAN token and extract its structure without verification
435
- * This is for inspection/debugging purposes
436
- */
437
- parseUCAN(token) {
438
- try {
439
- const parts = token.split('.');
440
- if (parts.length !== 3) {
441
- return {
442
- valid: false,
443
- error: 'Invalid token format: expected 3 parts (header.payload.signature)'
444
- };
445
- }
446
- // Decode header
447
- let header;
448
- try {
449
- header = JSON.parse(atob(parts[0].replace(/-/g, '+').replace(/_/g, '/')));
450
- }
451
- catch {
452
- return { valid: false, error: 'Failed to decode header' };
453
- }
454
- // Decode payload
455
- let payload;
456
- try {
457
- payload = JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/')));
458
- }
459
- catch {
460
- return { valid: false, error: 'Failed to decode payload' };
461
- }
462
- const now = Math.floor(Date.now() / 1000);
463
- // Check expiration status
464
- const isExpired = payload.exp ? payload.exp < now : false;
465
- const isNotYetValid = payload.nbf ? payload.nbf > now : false;
466
- // Calculate time until expiration
467
- const expiresIn = payload.exp ? payload.exp - now : null;
468
- // Parse capabilities
469
- const capabilities = (payload.att || []).map((att) => ({
470
- can: att.can || '',
471
- with: att.with || '',
472
- delegationDepth: att.delegationDepth,
473
- constraints: att.constraints
474
- }));
475
- // Extract facts
476
- const facts = payload.fct || {};
477
- return {
478
- valid: true,
479
- header: {
480
- alg: header.alg,
481
- typ: header.typ,
482
- ucv: header.ucv
483
- },
484
- payload: {
485
- iss: payload.iss,
486
- aud: payload.aud,
487
- exp: payload.exp,
488
- nbf: payload.nbf,
489
- nnc: payload.nnc,
490
- att: capabilities,
491
- prf: payload.prf || [],
492
- fct: facts
493
- },
494
- signature: parts[2],
495
- meta: {
496
- isExpired,
497
- isNotYetValid,
498
- expiresIn,
499
- proofCount: (payload.prf || []).length,
500
- capabilityCount: capabilities.length,
501
- hasKarma: 'karma' in facts,
502
- karma: typeof facts.karma === 'number' ? facts.karma : undefined
503
- }
504
- };
505
- }
506
- catch (error) {
507
- return {
508
- valid: false,
509
- error: error instanceof Error ? error.message : 'Failed to parse token'
510
- };
511
- }
512
- }
513
- /**
514
- * Check if keypair is loaded and ready for signing
515
- */
516
- isKeypairLoaded() {
517
- return this.keypair !== null;
518
- }
519
- /**
520
- * Get current DID or null if not initialized
521
- */
522
- getCurrentDID() {
523
- return this.did;
524
- }
525
- /**
526
- * Get full auth status for introspection
527
- */
528
- getAuthStatus() {
529
- return {
530
- initialized: this.initialized,
531
- keypairLoaded: this.keypair !== null,
532
- did: this.did,
533
- linkedIdentityCount: this.oauthIdentities.length,
534
- linkedProviders: this.oauthIdentities.map(i => i.provider),
535
- relayUrl: this.relayUrl
536
- };
537
- }
538
- /**
539
- * Get linked devices (from OAuth identities)
540
- */
541
- getLinkedDevices() {
542
- return this.oauthIdentities.map(identity => ({
543
- provider: identity.provider,
544
- providerId: identity.providerId,
545
- email: identity.email,
546
- displayName: identity.displayName,
547
- photoURL: identity.photoURL,
548
- linkedAt: identity.linkedAt
549
- }));
550
- }
551
- /**
552
- * Generate a device linking token for QR code display
553
- * This token allows another device to link to this identity
554
- */
555
- async generateLinkToken(expirationMinutes = 5) {
556
- if (!this.keypair || !this.did) {
557
- return null;
558
- }
559
- const expiresAt = new Date(Date.now() + expirationMinutes * 60 * 1000);
560
- // Create a capability that allows device linking
561
- const linkCapability = {
562
- with: { scheme: 'device', hierPart: `//link/${this.did}` },
563
- can: { namespace: 'device', segments: ['link'] }
564
- };
565
- const ucan = await ucans.build({
566
- audience: 'did:web:relay.buley.dev',
567
- issuer: this.keypair,
568
- capabilities: [linkCapability],
569
- lifetimeInSeconds: expirationMinutes * 60
570
- });
571
- const token = ucans.encode(ucan);
572
- return {
573
- token,
574
- did: this.did,
575
- expiresAt: expiresAt.toISOString(),
576
- qrData: JSON.stringify({
577
- type: 'dash-device-link',
578
- token,
579
- did: this.did,
580
- relay: this.relayUrl
581
- })
582
- };
583
- }
584
- /**
585
- * Revoke a linked device (by provider and providerId)
586
- */
587
- async revokeDevice(provider, providerId, password) {
588
- const index = this.oauthIdentities.findIndex(i => i.provider === provider && i.providerId === providerId);
589
- if (index === -1) {
590
- return false;
591
- }
592
- this.oauthIdentities.splice(index, 1);
593
- // Re-save with updated identities
594
- await this.saveKeypair(password);
595
- return true;
596
- }
597
- }
598
- export const auth = new AuthManager();
@@ -1,10 +0,0 @@
1
- export declare class VectorEngine {
2
- private pipe;
3
- private modelName;
4
- private readyPromise;
5
- constructor();
6
- init(): Promise<void>;
7
- embed(text: string): Promise<number[]>;
8
- cosineSimilarity(vecA: number[], vecB: number[]): number;
9
- }
10
- export declare const vectorEngine: VectorEngine;
@@ -1,76 +0,0 @@
1
- // import { pipeline } from '@huggingface/transformers'; // Moved to dynamic import
2
- export class VectorEngine {
3
- pipe = null;
4
- modelName = 'Xenova/all-MiniLM-L6-v2';
5
- readyPromise = null;
6
- constructor() { }
7
- async init() {
8
- if (this.readyPromise)
9
- return this.readyPromise;
10
- this.readyPromise = (async () => {
11
- try {
12
- console.log('Dash: Loading AI Model...', this.modelName);
13
- // Import from the new official package
14
- const { pipeline, env } = await import('@huggingface/transformers');
15
- // Skip local checks for browser env
16
- env.allowLocalModels = false;
17
- try {
18
- // 1. Try WebNN (NPU)
19
- console.log('Dash: Attempting WebNN backend...');
20
- this.pipe = await pipeline('feature-extraction', this.modelName, {
21
- device: 'webnn',
22
- dtype: 'fp32'
23
- });
24
- console.log('Dash: AI Model Ready (WebNN)');
25
- }
26
- catch (webnnError) {
27
- console.warn('Dash: WebNN failed, falling back to WebGPU', webnnError);
28
- try {
29
- // 2. Try WebGPU (GPU)
30
- console.log('Dash: Attempting WebGPU backend...');
31
- this.pipe = await pipeline('feature-extraction', this.modelName, {
32
- device: 'webgpu',
33
- dtype: 'fp32'
34
- });
35
- console.log('Dash: AI Model Ready (WebGPU)');
36
- }
37
- catch (gpuError) {
38
- // 3. Fallback to WASM (CPU)
39
- console.warn('Dash: WebGPU failed, failing back to WASM/CPU', gpuError);
40
- this.pipe = await pipeline('feature-extraction', this.modelName, {
41
- device: 'wasm'
42
- });
43
- console.log('Dash: AI Model Ready (WASM)');
44
- }
45
- }
46
- }
47
- catch (err) {
48
- console.error('Dash: Failed to load AI model', err);
49
- throw err;
50
- }
51
- })();
52
- return this.readyPromise;
53
- }
54
- async embed(text) {
55
- if (!this.pipe)
56
- await this.init();
57
- // Generate embedding
58
- const output = await this.pipe(text, { pooling: 'mean', normalize: true });
59
- // Convert generic Tensor to standard number array
60
- return Array.from(output.data);
61
- }
62
- cosineSimilarity(vecA, vecB) {
63
- let dotProduct = 0;
64
- let normA = 0;
65
- let normB = 0;
66
- for (let i = 0; i < vecA.length; i++) {
67
- dotProduct += vecA[i] * vecB[i];
68
- normA += vecA[i] * vecA[i];
69
- normB += vecB[i] * vecB[i];
70
- }
71
- if (normA === 0 || normB === 0)
72
- return 0;
73
- return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
74
- }
75
- }
76
- export const vectorEngine = new VectorEngine();