@embarkai/ui-kit 0.1.1 → 0.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.
@@ -373,6 +373,77 @@ var init_crypto_utils = __esm({
373
373
  }
374
374
  });
375
375
 
376
+ // src/iframe/lib/backup/local-backup.ts
377
+ var local_backup_exports = {};
378
+ __export(local_backup_exports, {
379
+ backupToLocalFile: () => backupToLocalFile,
380
+ restoreFromLocalFile: () => restoreFromLocalFile
381
+ });
382
+ async function backupToLocalFile(data, password) {
383
+ let encryptionPassword = password;
384
+ let credentialId;
385
+ if (!encryptionPassword) {
386
+ const result = await deriveBackupPasswordFromPasskey(data.userId);
387
+ encryptionPassword = result.password;
388
+ credentialId = result.credentialId;
389
+ }
390
+ const encrypted = await encryptKeyshare(
391
+ data,
392
+ encryptionPassword,
393
+ password ? "password" : "passkey",
394
+ credentialId
395
+ );
396
+ const backupData = {
397
+ ...encrypted,
398
+ userId: data.userId,
399
+ createdAt: data.createdAt
400
+ };
401
+ const blob = new Blob([JSON.stringify(backupData, null, 2)], {
402
+ type: "application/json"
403
+ });
404
+ const url = URL.createObjectURL(blob);
405
+ const a = document.createElement("a");
406
+ a.href = url;
407
+ a.download = `lumia-passport-backup-${data.userId}-${Date.now()}.json`;
408
+ document.body.appendChild(a);
409
+ a.click();
410
+ document.body.removeChild(a);
411
+ URL.revokeObjectURL(url);
412
+ }
413
+ async function restoreFromLocalFile(fileContent, userId, password) {
414
+ console.log("[iframe][restoreFromLocalFile] Starting restore from local file");
415
+ const encryptedBackup = JSON.parse(fileContent);
416
+ let decryptionPassword;
417
+ let credentialId;
418
+ if (password) {
419
+ decryptionPassword = password;
420
+ } else {
421
+ const credentialIdFromBackup = encryptedBackup.encryptionMethod === "passkey" ? encryptedBackup.credentialId : void 0;
422
+ const result = await deriveBackupPasswordFromPasskey(
423
+ userId,
424
+ credentialIdFromBackup
425
+ ).catch(() => {
426
+ throw new Error(
427
+ "Restore requires either password or passkey authentication"
428
+ );
429
+ });
430
+ decryptionPassword = result.password;
431
+ credentialId = result.credentialId;
432
+ }
433
+ const { decryptKeyshare: decryptKeyshare2 } = await Promise.resolve().then(() => (init_crypto_utils(), crypto_utils_exports));
434
+ const backupData = await decryptKeyshare2(encryptedBackup, decryptionPassword);
435
+ if (backupData.userId !== userId) {
436
+ throw new Error("Backup file does not match current user");
437
+ }
438
+ console.log("[iframe][restoreFromLocalFile] Restore successful");
439
+ return backupData;
440
+ }
441
+ var init_local_backup = __esm({
442
+ "src/iframe/lib/backup/local-backup.ts"() {
443
+ init_crypto_utils();
444
+ }
445
+ });
446
+
376
447
  // src/iframe/lib/backup/server-backup.ts
377
448
  var server_backup_exports = {};
378
449
  __export(server_backup_exports, {
@@ -477,77 +548,6 @@ var init_server_backup = __esm({
477
548
  }
478
549
  });
479
550
 
480
- // src/iframe/lib/backup/local-backup.ts
481
- var local_backup_exports = {};
482
- __export(local_backup_exports, {
483
- backupToLocalFile: () => backupToLocalFile,
484
- restoreFromLocalFile: () => restoreFromLocalFile
485
- });
486
- async function backupToLocalFile(data, password) {
487
- let encryptionPassword = password;
488
- let credentialId;
489
- if (!encryptionPassword) {
490
- const result = await deriveBackupPasswordFromPasskey(data.userId);
491
- encryptionPassword = result.password;
492
- credentialId = result.credentialId;
493
- }
494
- const encrypted = await encryptKeyshare(
495
- data,
496
- encryptionPassword,
497
- password ? "password" : "passkey",
498
- credentialId
499
- );
500
- const backupData = {
501
- ...encrypted,
502
- userId: data.userId,
503
- createdAt: data.createdAt
504
- };
505
- const blob = new Blob([JSON.stringify(backupData, null, 2)], {
506
- type: "application/json"
507
- });
508
- const url = URL.createObjectURL(blob);
509
- const a = document.createElement("a");
510
- a.href = url;
511
- a.download = `lumia-passport-backup-${data.userId}-${Date.now()}.json`;
512
- document.body.appendChild(a);
513
- a.click();
514
- document.body.removeChild(a);
515
- URL.revokeObjectURL(url);
516
- }
517
- async function restoreFromLocalFile(fileContent, userId, password) {
518
- console.log("[iframe][restoreFromLocalFile] Starting restore from local file");
519
- const encryptedBackup = JSON.parse(fileContent);
520
- let decryptionPassword;
521
- let credentialId;
522
- if (password) {
523
- decryptionPassword = password;
524
- } else {
525
- const credentialIdFromBackup = encryptedBackup.encryptionMethod === "passkey" ? encryptedBackup.credentialId : void 0;
526
- const result = await deriveBackupPasswordFromPasskey(
527
- userId,
528
- credentialIdFromBackup
529
- ).catch(() => {
530
- throw new Error(
531
- "Restore requires either password or passkey authentication"
532
- );
533
- });
534
- decryptionPassword = result.password;
535
- credentialId = result.credentialId;
536
- }
537
- const { decryptKeyshare: decryptKeyshare2 } = await Promise.resolve().then(() => (init_crypto_utils(), crypto_utils_exports));
538
- const backupData = await decryptKeyshare2(encryptedBackup, decryptionPassword);
539
- if (backupData.userId !== userId) {
540
- throw new Error("Backup file does not match current user");
541
- }
542
- console.log("[iframe][restoreFromLocalFile] Restore successful");
543
- return backupData;
544
- }
545
- var init_local_backup = __esm({
546
- "src/iframe/lib/backup/local-backup.ts"() {
547
- init_crypto_utils();
548
- }
549
- });
550
-
551
551
  // ../../node_modules/.pnpm/viem@2.38.0_bufferutil@4.0.9_typescript@5.9.3_utf-8-validate@5.0.10_zod@3.25.76/node_modules/viem/_esm/utils/data/isHex.js
552
552
  function isHex(value, { strict = true } = {}) {
553
553
  if (!value)
@@ -1267,13 +1267,13 @@ var TemplateEngine = class {
1267
1267
  };
1268
1268
 
1269
1269
  // src/iframe/templates/html/authorization.html
1270
- var authorization_default = '<div\n style="\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n background: #000000;\n "\n>\n <div\n style="\n display: flex;\n flex-direction: column;\n gap: var(--iframe-gap);\n align-items: center;\n background: var(--iframe-modal-bg);\n border-radius: 20px;\n padding: 20px;\n max-width: 320px;\n width: calc(100% - 40px);\n max-height: 90vh;\n overflow-y: auto;\n "\n >\n <div style="display: flex; align-items: center; gap: 0; justify-content: center">\n <!-- 10px overlap -->\n <div style="width: 38px; height: 48px; z-index: 10; overflow: visible">\n {{#if userAvatar}}\n <img\n src="{{userAvatar}}"\n alt="{{userName}}"\n style="width: 48px; height: 48px; border-radius: 24px; object-fit: cover"\n />\n {{else}}\n <span\n style="\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border-radius: 24px;\n background-color: var(--iframe-button-bg);\n color: var(--iframe-button-text);\n font-size: 14px;\n font-weight: 600;\n "\n >U</span\n >\n {{/if}}\n </div>\n\n <!-- ------------------------------------------------------------ -->\n\n <div style="width: 38px; height: 48px; z-index: 5; overflow: visible">\n {{#if displayLogo}}\n <img\n src="{{displayLogo}}"\n alt="{{displayName}}"\n style="width: 48px; height: 48px; border-radius: 24px; object-fit: cover"\n />\n {{else}}\n <span\n style="\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border-radius: 24px;\n background-color: var(--iframe-button-bg);\n color: var(--iframe-button-text);\n font-size: 14px;\n font-weight: 600;\n "\n >A</span\n >\n {{/if}}\n </div>\n </div>\n\n <div style="display: flex; align-items: center; gap: var(--iframe-gap); justify-content: center">\n <span\n style="\n width: fit-content;\n font-size: var(--iframe-h2-fz);\n font-weight: var(--iframe-heading-fw);\n line-height: 150%;\n text-align: center;\n "\n >{{displayName}}</span\n >\n </div>\n\n <div class="domain-info {{domainStatusClass}}">\n <span style="display: block; width: 100%; font-size: 12px; text-align: center; font-family: monospace"\n >{{domainStatusIcon}} {{origin}}</span\n >\n </div>\n\n <!-- Permissions box -->\n <div\n id="data-auth-permissions-block"\n aria-expanded="false"\n style="display: flex; flex-direction: column; width: 100%"\n >\n <div\n id="data-auth-permissions-toggler"\n style="\n cursor: pointer;\n text-align: center;\n list-style: none;\n padding: 0 var(--iframe-pd);\n user-select: none;\n font-size: 12px;\n line-height: 20px;\n "\n >\n Show Permissions \u25BC\n </div>\n\n <div\n id="data-auth-permissions-content"\n style="width: 100%; height: var(--iframe-permissions-h, 0); overflow: hidden; transition: height 300ms ease"\n >\n <div\n style="\n width: 100%;\n padding: var(--iframe-gap);\n border-radius: var(--iframe-el-bdrs);\n background: var(--iframe-info);\n font-size: 10px;\n "\n >\n <div style="margin-bottom: var(--iframe-gap)">\n <strong>{{displayName}}</strong> by <span class="project-owner">{{displayName}}</span> wants to access your\n <strong>Lumia Passport</strong> account\n </div>\n\n <div style="margin-bottom: var(--iframe-gap)">\n <strong style="margin-bottom: var(--iframe-gap)">Personal user data</strong>\n <ul style="list-style: none">\n <li style="padding-left: var(--iframe-pd)">Wallet address (read-only)</li>\n <li style="padding-left: var(--iframe-pd)">Public transaction history (read-only)</li>\n </ul>\n </div>\n\n <div style="width: 100%">\n <strong style="margin-bottom: var(--iframe-gap)">Permissions</strong>\n <ul style="list-style: none">\n <li style="padding-left: var(--iframe-pd)">Request transaction signatures</li>\n <li style="padding-left: var(--iframe-pd)">Initiate blockchain operations</li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n\n {{#if showSecurityWarning}}\n <div class="security-warning">\n <strong>\u26A0\uFE0F Warning:</strong> This domain is not verified for {{displayName}}\n <div>Expected: {{expectedDomains}}</div>\n <div>Actual: {{origin}}</div>\n </div>\n {{else}}\n <span style="display: block; text-align: center; font-size: 10px; color: var(--iframe-text-secondary)"\n >By continuing you allow the app to use these permissions, you can revoke access at any time.</span\n >\n {{/if}}\n\n <!-- Action buttons -->\n <div style="display: flex; gap: var(--iframe-gap); width: 100%; align-items: center">\n <button class="cancel-btn">Cancel</button>\n <button class="authorize-btn" {{authorizeButtonState}}>Continue</button>\n </div>\n </div>\n</div>\n';
1270
+ var authorization_default = '<div\n style="\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n background: #000000;\n "\n>\n <div\n style="\n display: flex;\n flex-direction: column;\n gap: var(--iframe-gap);\n align-items: center;\n background: var(--iframe-modal-bg);\n border-radius: 20px;\n padding: 20px;\n max-width: 320px;\n width: calc(100% - 40px);\n max-height: 90vh;\n overflow-y: auto;\n "\n >\n <div style="display: flex; align-items: center; gap: 0; justify-content: center">\n <!-- 10px overlap -->\n <div style="width: 38px; height: 48px; z-index: 10; overflow: visible">\n {{#if userAvatar}}\n <img\n src="{{userAvatar}}"\n alt="{{userName}}"\n style="width: 48px; height: 48px; border-radius: 24px; object-fit: cover"\n />\n {{else}}\n <span\n style="\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border-radius: 24px;\n background-color: var(--iframe-button-bg);\n color: var(--iframe-button-text);\n font-size: 14px;\n font-weight: 600;\n "\n >U</span\n >\n {{/if}}\n </div>\n\n <!-- ------------------------------------------------------------ -->\n\n <div style="width: 38px; height: 48px; z-index: 5; overflow: visible">\n {{#if displayLogo}}\n <img\n src="{{displayLogo}}"\n alt="{{displayName}}"\n style="width: 48px; height: 48px; border-radius: 24px; object-fit: cover"\n />\n {{else}}\n <span\n style="\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border-radius: 24px;\n background-color: var(--iframe-button-bg);\n color: var(--iframe-button-text);\n font-size: 14px;\n font-weight: 600;\n "\n >A</span\n >\n {{/if}}\n </div>\n </div>\n\n <div style="display: flex; align-items: center; gap: var(--iframe-gap); justify-content: center">\n <span\n style="\n width: fit-content;\n font-size: var(--iframe-h2-fz);\n font-weight: var(--iframe-heading-fw);\n line-height: 150%;\n text-align: center;\n "\n >{{displayName}}</span\n >\n </div>\n\n <div class="domain-info {{domainStatusClass}}">\n <span style="display: block; width: 100%; font-size: 12px; text-align: center; font-family: monospace"\n >{{domainStatusIcon}} {{origin}}</span\n >\n </div>\n\n <!-- Permissions box -->\n <div\n id="data-auth-permissions-block"\n aria-expanded="false"\n style="display: flex; flex-direction: column; width: 100%"\n >\n <div\n id="data-auth-permissions-toggler"\n style="\n cursor: pointer;\n text-align: center;\n list-style: none;\n padding: 0 var(--iframe-pd);\n user-select: none;\n font-size: 12px;\n line-height: 20px;\n "\n >\n Show Permissions \u25BC\n </div>\n\n <div\n id="data-auth-permissions-content"\n style="width: 100%; height: var(--iframe-permissions-h, 0); overflow: hidden; transition: height 300ms ease"\n >\n <div\n style="\n width: 100%;\n padding: var(--iframe-gap);\n border-radius: var(--iframe-el-bdrs);\n background: var(--iframe-info);\n font-size: 10px;\n "\n >\n <div style="margin-bottom: var(--iframe-gap)">\n <strong>{{displayName}}</strong> by <span class="project-owner">{{displayName}}</span> wants to access your\n <strong>EmbarkAI</strong> account\n </div>\n\n <div style="margin-bottom: var(--iframe-gap)">\n <strong style="margin-bottom: var(--iframe-gap)">Personal user data</strong>\n <ul style="list-style: none">\n <li style="padding-left: var(--iframe-pd)">Wallet address (read-only)</li>\n <li style="padding-left: var(--iframe-pd)">Public transaction history (read-only)</li>\n </ul>\n </div>\n\n <div style="width: 100%">\n <strong style="margin-bottom: var(--iframe-gap)">Permissions</strong>\n <ul style="list-style: none">\n <li style="padding-left: var(--iframe-pd)">Request transaction signatures</li>\n <li style="padding-left: var(--iframe-pd)">Initiate blockchain operations</li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n\n {{#if showSecurityWarning}}\n <div class="security-warning">\n <strong>\u26A0\uFE0F Warning:</strong> This domain is not verified for {{displayName}}\n <div>Expected: {{expectedDomains}}</div>\n <div>Actual: {{origin}}</div>\n </div>\n {{else}}\n <span style="display: block; text-align: center; font-size: 10px; color: var(--iframe-text-secondary)"\n >By continuing you allow the app to use these permissions, you can revoke access at any time.</span\n >\n {{/if}}\n\n <!-- Action buttons -->\n <div style="display: flex; gap: var(--iframe-gap); width: 100%; align-items: center">\n <button class="cancel-btn">Cancel</button>\n <button class="authorize-btn" {{authorizeButtonState}}>Continue</button>\n </div>\n </div>\n</div>\n';
1271
1271
 
1272
1272
  // src/iframe/templates/html/confirm-tx.html
1273
- var confirm_tx_default = '<div\n style="\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n background: #000000;\n "\n>\n <div\n style="\n display: flex;\n flex-direction: column;\n gap: var(--iframe-gap);\n align-items: center;\n background: var(--iframe-modal-bg);\n border-radius: 20px;\n padding: 20px;\n max-width: 320px;\n width: calc(100% - 40px);\n max-height: 90vh;\n overflow-y: auto;\n "\n >\n <!-- Header with logos -->\n <div style="display: flex; align-items: center; gap: 0; justify-content: center">\n <div style="width: 38px; height: 48px; z-index: 10; overflow: visible">\n {{#if displayLogo}}\n <img\n src="{{displayLogo}}"\n alt="{{displayName}}"\n style="width: 48px; height: 48px; border-radius: 24px; object-fit: cover"\n />\n {{else}}\n <span\n style="\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border-radius: 24px;\n background-color: var(--iframe-button-bg);\n color: var(--iframe-button-text);\n font-size: 14px;\n font-weight: 600;\n "\n >A</span\n >\n {{/if}}\n </div>\n\n <!-- ------------------------------------------------------------ -->\n\n <div style="width: 38px; height: 48px; z-index: 5; overflow: visible">\n {{#if userAvatar}}\n <img\n src="{{userAvatar}}"\n alt="{{userName}}"\n style="width: 48px; height: 48px; border-radius: 24px; object-fit: cover"\n />\n {{else}}\n <span\n style="\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border-radius: 24px;\n background-color: var(--iframe-button-bg);\n color: var(--iframe-button-text);\n font-size: 14px;\n font-weight: 600;\n "\n >U</span\n >\n {{/if}}\n </div>\n </div>\n\n <!-- Transaction title -->\n\n <div style="display: flex; align-items: center; gap: var(--iframe-gap); justify-content: center">\n <span\n style="\n width: fit-content;\n font-size: var(--iframe-h1-fz);\n font-weight: var(--iframe-heading-fw);\n line-height: 150%;\n text-align: center;\n "\n >Confirm Transaction</span\n >\n </div>\n\n <!-- Application info -->\n <div class="domain-info {{domainStatusClass}}">\n <span style="display: block; width: 100%; font-size: 12px; text-align: center; font-family: monospace"\n >{{domainStatusText}} {{origin}}</span\n >\n </div>\n\n <!-- Transaction details box -->\n <div\n id="confirm-transaction-details-block"\n aria-expanded="false"\n style="display: flex; flex-direction: column; width: 100%"\n >\n <div\n id="confirm-transaction-details-toggler"\n style="\n cursor: pointer;\n text-align: center;\n list-style: none;\n padding: 0 var(--iframe-pd);\n user-select: none;\n font-size: 12px;\n line-height: 20px;\n "\n >\n Show Transaction Details \u25BC\n </div>\n\n <div\n id="confirm-transaction-details-content"\n style="\n display: flex;\n flex-direction: column;\n gap: var(--iframe-gap);\n width: 100%;\n max-height: var(--confirm-transaction-details-h, 0px);\n overflow: hidden;\n transition: max-height 300ms ease;\n "\n >\n <div\n style="\n display: flex;\n flex-direction: column;\n gap: var(--iframe-gap);\n width: 100%;\n padding: var(--iframe-gap);\n border-radius: var(--iframe-el-bdrs);\n background: var(--iframe-info);\n font-size: 10px;\n "\n >\n {{#if fromAddress}}\n <div style="display: flex; justify-content: space-between">\n <strong style="color: var(--iframe-text-secondary); display: block">From:</strong>\n <a\n href="{{fromAddressExplorerUrl}}"\n target="_blank"\n rel="noopener noreferrer"\n style="word-break: break-all; display: block; color: #3b82f6; text-decoration: none"\n title="{{fromAddressFull}}"\n >\n <code>{{fromAddress}}</code>\n </a>\n </div>\n {{/if}}\n\n <!-- ------------------------------------------------------------ -->\n\n {{#if toAddress}}\n <div style="display: flex; justify-content: space-between">\n <strong style="color: var(--iframe-text-secondary); display: block">To:</strong>\n <a\n href="{{toAddressExplorerUrl}}"\n target="_blank"\n rel="noopener noreferrer"\n style="word-break: break-all; display: block; color: #3b82f6; text-decoration: none"\n title="{{toAddressFull}}"\n >\n <code>{{toAddress}}</code>\n </a>\n </div>\n {{/if}}\n\n <!-- ------------------------------------------------------------ -->\n\n {{#if hasValue}}\n <div style="display: flex; justify-content: space-between">\n <strong style="color: var(--iframe-text-secondary); display: block">Amount:</strong>\n <code style="word-break: break-all; display: block">{{formattedValue}} LUMIA</code>\n </div>\n {{/if}}\n\n <!-- ------------------------------------------------------------ -->\n\n {{#if showContractInteraction}}\n <div style="display: flex; justify-content: space-between">\n <strong style="color: var(--iframe-text-secondary); display: block">Action:</strong>\n <code style="word-break: break-all; display: block">{{actionDescription}}</code>\n </div>\n {{/if}}\n\n <!-- ------------------------------------------------------------ -->\n\n <div style="display: flex; justify-content: space-between">\n <strong style="color: var(--iframe-text-secondary); display: block">Estimated Cost:</strong>\n <code style="word-break: break-all; display: block; color: #10b981">{{estimatedCost}}</code>\n </div>\n\n <!-- ------------------------------------------------------------ -->\n\n {{#if isSponsored}}\n <div\n style="\n display: flex;\n justify-content: space-between;\n background: var(--iframe-success);\n border-radius: var(--iframe-el-bdrs);\n padding: var(--iframe-gap);\n "\n >\n <span>Gas fees will be paid by the application (no cost to you)</span>\n </div>\n {{/if}}\n </div>\n </div>\n </div>\n\n <!-- ------------------------------------------------------------ -->\n\n {{#if showHighRisk}}\n <div class="security-warning">\n <strong>\u26A0\uFE0F {{riskLevel}} RISK</strong>\n {{riskReasons}}\n </div>\n {{else}}\n <div class="security-warning">Please verify this transaction carefully before confirming.</div>\n {{/if}}\n\n <!-- ------------------------------------------------------------ -->\n\n <div class="trust-app-section">\n <label class="trust-app-label">\n <input type="checkbox" class="trust-app-checkbox" />\n <span>Add this application to trusted list and skip confirmation for future transactions</span>\n </label>\n </div>\n\n <!-- Action buttons -->\n <div style="display: flex; gap: var(--iframe-gap); width: 100%; align-items: center">\n <button class="cancel-btn">Reject</button>\n <button class="confirm-btn">Confirm</button>\n </div>\n\n <!-- Footer notice -->\n <span\n style="\n display: block;\n padding: var(--iframe-gap);\n text-align: center;\n font-size: 10px;\n color: var(--iframe-text-secondary);\n "\n >\n You can manage trusted applications in your Lumia Passport settings.\n </span>\n </div>\n</div>\n';
1273
+ var confirm_tx_default = '<div\n style="\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n background: #000000;\n "\n>\n <div\n style="\n display: flex;\n flex-direction: column;\n gap: var(--iframe-gap);\n align-items: center;\n background: var(--iframe-modal-bg);\n border-radius: 20px;\n padding: 20px;\n max-width: 320px;\n width: calc(100% - 40px);\n max-height: 90vh;\n overflow-y: auto;\n "\n >\n <!-- Header with logos -->\n <div style="display: flex; align-items: center; gap: 0; justify-content: center">\n <div style="width: 38px; height: 48px; z-index: 10; overflow: visible">\n {{#if displayLogo}}\n <img\n src="{{displayLogo}}"\n alt="{{displayName}}"\n style="width: 48px; height: 48px; border-radius: 24px; object-fit: cover"\n />\n {{else}}\n <span\n style="\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border-radius: 24px;\n background-color: var(--iframe-button-bg);\n color: var(--iframe-button-text);\n font-size: 14px;\n font-weight: 600;\n "\n >A</span\n >\n {{/if}}\n </div>\n\n <!-- ------------------------------------------------------------ -->\n\n <div style="width: 38px; height: 48px; z-index: 5; overflow: visible">\n {{#if userAvatar}}\n <img\n src="{{userAvatar}}"\n alt="{{userName}}"\n style="width: 48px; height: 48px; border-radius: 24px; object-fit: cover"\n />\n {{else}}\n <span\n style="\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border-radius: 24px;\n background-color: var(--iframe-button-bg);\n color: var(--iframe-button-text);\n font-size: 14px;\n font-weight: 600;\n "\n >U</span\n >\n {{/if}}\n </div>\n </div>\n\n <!-- Transaction title -->\n\n <div style="display: flex; align-items: center; gap: var(--iframe-gap); justify-content: center">\n <span\n style="\n width: fit-content;\n font-size: var(--iframe-h1-fz);\n font-weight: var(--iframe-heading-fw);\n line-height: 150%;\n text-align: center;\n "\n >Confirm Transaction</span\n >\n </div>\n\n <!-- Application info -->\n <div class="domain-info {{domainStatusClass}}">\n <span style="display: block; width: 100%; font-size: 12px; text-align: center; font-family: monospace"\n >{{domainStatusText}} {{origin}}</span\n >\n </div>\n\n <!-- Transaction details box -->\n <div\n id="confirm-transaction-details-block"\n aria-expanded="false"\n style="display: flex; flex-direction: column; width: 100%"\n >\n <div\n id="confirm-transaction-details-toggler"\n style="\n cursor: pointer;\n text-align: center;\n list-style: none;\n padding: 0 var(--iframe-pd);\n user-select: none;\n font-size: 12px;\n line-height: 20px;\n "\n >\n Show Transaction Details \u25BC\n </div>\n\n <div\n id="confirm-transaction-details-content"\n style="\n display: flex;\n flex-direction: column;\n gap: var(--iframe-gap);\n width: 100%;\n max-height: var(--confirm-transaction-details-h, 0px);\n overflow: hidden;\n transition: max-height 300ms ease;\n "\n >\n <div\n style="\n display: flex;\n flex-direction: column;\n gap: var(--iframe-gap);\n width: 100%;\n padding: var(--iframe-gap);\n border-radius: var(--iframe-el-bdrs);\n background: var(--iframe-info);\n font-size: 10px;\n "\n >\n {{#if fromAddress}}\n <div style="display: flex; justify-content: space-between">\n <strong style="color: var(--iframe-text-secondary); display: block">From:</strong>\n <a\n href="{{fromAddressExplorerUrl}}"\n target="_blank"\n rel="noopener noreferrer"\n style="word-break: break-all; display: block; color: #3b82f6; text-decoration: none"\n title="{{fromAddressFull}}"\n >\n <code>{{fromAddress}}</code>\n </a>\n </div>\n {{/if}}\n\n <!-- ------------------------------------------------------------ -->\n\n {{#if toAddress}}\n <div style="display: flex; justify-content: space-between">\n <strong style="color: var(--iframe-text-secondary); display: block">To:</strong>\n <a\n href="{{toAddressExplorerUrl}}"\n target="_blank"\n rel="noopener noreferrer"\n style="word-break: break-all; display: block; color: #3b82f6; text-decoration: none"\n title="{{toAddressFull}}"\n >\n <code>{{toAddress}}</code>\n </a>\n </div>\n {{/if}}\n\n <!-- ------------------------------------------------------------ -->\n\n {{#if hasValue}}\n <div style="display: flex; justify-content: space-between">\n <strong style="color: var(--iframe-text-secondary); display: block">Amount:</strong>\n <code style="word-break: break-all; display: block">{{formattedValue}} LUMIA</code>\n </div>\n {{/if}}\n\n <!-- ------------------------------------------------------------ -->\n\n {{#if showContractInteraction}}\n <div style="display: flex; justify-content: space-between">\n <strong style="color: var(--iframe-text-secondary); display: block">Action:</strong>\n <code style="word-break: break-all; display: block">{{actionDescription}}</code>\n </div>\n {{/if}}\n\n <!-- ------------------------------------------------------------ -->\n\n <div style="display: flex; justify-content: space-between">\n <strong style="color: var(--iframe-text-secondary); display: block">Estimated Cost:</strong>\n <code style="word-break: break-all; display: block; color: #10b981">{{estimatedCost}}</code>\n </div>\n\n <!-- ------------------------------------------------------------ -->\n\n {{#if isSponsored}}\n <div\n style="\n display: flex;\n justify-content: space-between;\n background: var(--iframe-success);\n border-radius: var(--iframe-el-bdrs);\n padding: var(--iframe-gap);\n "\n >\n <span>Gas fees will be paid by the application (no cost to you)</span>\n </div>\n {{/if}}\n </div>\n </div>\n </div>\n\n <!-- ------------------------------------------------------------ -->\n\n {{#if showHighRisk}}\n <div class="security-warning">\n <strong>\u26A0\uFE0F {{riskLevel}} RISK</strong>\n {{riskReasons}}\n </div>\n {{else}}\n <div class="security-warning">Please verify this transaction carefully before confirming.</div>\n {{/if}}\n\n <!-- ------------------------------------------------------------ -->\n\n <div class="trust-app-section">\n <label class="trust-app-label">\n <input type="checkbox" class="trust-app-checkbox" />\n <span>Add this application to trusted list and skip confirmation for future transactions</span>\n </label>\n </div>\n\n <!-- Action buttons -->\n <div style="display: flex; gap: var(--iframe-gap); width: 100%; align-items: center">\n <button class="cancel-btn">Reject</button>\n <button class="confirm-btn">Confirm</button>\n </div>\n\n <!-- Footer notice -->\n <span\n style="\n display: block;\n padding: var(--iframe-gap);\n text-align: center;\n font-size: 10px;\n color: var(--iframe-text-secondary);\n "\n >\n You can manage trusted applications in your EmbarkAI settings.\n </span>\n </div>\n</div>\n';
1274
1274
 
1275
1275
  // src/iframe/templates/html/ready-indicator.html
1276
- var ready_indicator_default = '<div class="ready-indicator">\n <div class="status-icon">\u2713</div>\n <h2>Secure Wallet Ready</h2>\n <p>Lumia Passport is ready for secure operations</p>\n <div class="info">\n <div class="info-row">\n <strong>Origin:</strong>\n <span>{{origin}}</span>\n </div>\n <div class="info-row">\n <strong>Status:</strong>\n <span class="status-active">Active</span>\n </div>\n <div class="info-row">\n <strong>Version:</strong>\n <span>{{iframeVersion}}</span>\n </div>\n <div class="info-row">\n <strong>Documentation:</strong>\n <a\n href="https://docs.lumiapassport.com/"\n target="_blank"\n rel="noopener noreferrer"\n style="color: #667eea; text-decoration: none"\n >docs.lumiapassport.com</a\n >\n </div>\n </div>\n</div>\n';
1276
+ var ready_indicator_default = '<div class="ready-indicator">\n <div class="status-icon">\u2713</div>\n <h2>Secure Wallet Ready</h2>\n <p>EmbarkAI is ready for secure operations</p>\n <div class="info">\n <div class="info-row">\n <strong>Origin:</strong>\n <span>{{origin}}</span>\n </div>\n <div class="info-row">\n <strong>Status:</strong>\n <span class="status-active">Active</span>\n </div>\n <div class="info-row">\n <strong>Version:</strong>\n <span>{{iframeVersion}}</span>\n </div>\n <div class="info-row">\n <strong>Documentation:</strong>\n <a\n href="https://docs.lumiapassport.com/"\n target="_blank"\n rel="noopener noreferrer"\n style="color: #667eea; text-decoration: none"\n >docs.lumiapassport.com</a\n >\n </div>\n </div>\n</div>\n';
1277
1277
 
1278
1278
  // src/iframe/templates/html/sign-eip712-tx.html
1279
1279
  var sign_eip712_tx_default = '<div\n style="\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n background: #000000;\n "\n>\n <div\n style="\n display: flex;\n flex-direction: column;\n gap: var(--iframe-gap);\n align-items: center;\n background: var(--iframe-modal-bg);\n border-radius: 20px;\n padding: 20px;\n max-width: 320px;\n width: calc(100% - 40px);\n max-height: 90vh;\n overflow-y: auto;\n "\n >\n <!-- Header -->\n <div style="display: flex; align-items: center; gap: var(--iframe-gap); justify-content: center">\n {{#if metadataLogo}}\n <img\n src="{{metadataLogo}}"\n alt="{{metadataName}}"\n style="width: 48px; height: 48px; border-radius: 24px; object-fit: cover"\n />\n {{else}}\n <span\n style="\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border-radius: 24px;\n background-color: var(--iframe-button-bg);\n color: var(--iframe-button-text);\n font-size: 14px;\n font-weight: 600;\n "\n >\u{1F510}</span\n >\n {{/if}}\n\n <!-- ------------------------------------------------------------ -->\n <svg\n xmlns="http://www.w3.org/2000/svg"\n width="24"\n height="24"\n viewBox="0 0 24 24"\n fill="none"\n stroke="var(--iframe-text-secondary)"\n stroke-width="2"\n stroke-linecap="round"\n stroke-linejoin="round"\n >\n <path d="M5 12h14" />\n <path d="m12 5 7 7-7 7" />\n </svg>\n <!-- ------------------------------------------------------------ -->\n\n {{#if userAvatar}}\n <img\n src="{{userAvatar}}"\n alt="{{userName}}"\n style="width: 48px; height: 48px; border-radius: 24px; object-fit: cover"\n />\n {{else}}\n <span\n style="\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border-radius: 24px;\n background-color: var(--iframe-button-bg);\n color: var(--iframe-button-text);\n font-size: 14px;\n font-weight: 600;\n "\n >U</span\n >\n {{/if}}\n </div>\n\n <div style="display: flex; align-items: center; gap: var(--iframe-gap); justify-content: center">\n <span\n style="\n width: fit-content;\n font-size: var(--iframe-h2-fz);\n font-weight: var(--iframe-heading-fw);\n line-height: 150%;\n text-align: center;\n "\n >Signature Request</span\n >\n </div>\n\n <div class="domain-info verified">\n <span style="display: block; width: 100%; font-size: 12px; text-align: center; font-family: monospace">\n {{#if isVerifiedOrigin}}\n <span>\u2713</span>\n {{/if}} {{origin}}</span\n >\n </div>\n\n <!-- EIP712 Message Content -->\n <div class="eip712-content">\n <details class="eip712-details">\n <summary>Advanced details</summary>\n\n <div class="section-title">\u{1F4DD} Message</div>\n <div class="eip712-section">\n <div class="section-subtitle">{{primaryType}}</div>\n {{messageFields}}\n </div>\n\n <div class="section-title" style="margin-top: 16px">\u{1F50D} Domain</div>\n <div class="eip712-section">{{domainFields}}</div>\n\n <div class="section-title" style="margin-top: 16px">\u{1F4CB} Full Message</div>\n <pre class="eip712-raw"><code>{{fullMessageJson}}</code></pre>\n </details>\n </div>\n\n <!-- Trust App Option -->\n <div class="trust-app-section">\n <label class="trust-app-label">\n <input type="checkbox" class="trust-app-checkbox" />\n <span>Trust this application and skip confirmation for future signatures</span>\n </label>\n </div>\n\n <!-- Action Buttons -->\n <div class="actions">\n <button class="cancel-btn">Reject</button>\n <button class="confirm-btn">Sign</button>\n </div>\n\n <!-- Footer Notice -->\n <div class="footer-notice">\n <p class="footer-note">Only sign messages from applications you trust.</p>\n </div>\n </div>\n</div>\n';
@@ -1773,178 +1773,50 @@ var AuthorizationManager = class {
1773
1773
  }
1774
1774
  };
1775
1775
 
1776
- // src/iframe/lib/token-refresh-client.ts
1777
- var TokenRefreshApiClient = class {
1776
+ // src/internal/constants.ts
1777
+ import { Key, Mail, Wallet2 } from "lucide-react";
1778
+ var LOOCAL_BACKUP_STATUS_KEY = "passport-backup-status";
1779
+ var BLOCKSCOUT_QUERY_STALE_TIME = 30 * 1e3;
1780
+ var BLOCKSCOUT_QUERY_GC_TIME = 5 * 60 * 1e3;
1781
+
1782
+ // src/iframe/lib/backup/cloud-backup.ts
1783
+ init_crypto_utils();
1784
+
1785
+ // src/iframe/lib/backup/cloudStorage.ts
1786
+ var GoogleDriveProvider = class {
1778
1787
  constructor() {
1779
- this.pendingTokenRefresh = null;
1780
- this.tssUrl = "https://api.lumiapassport.com/tss";
1788
+ this.name = "Google Drive";
1789
+ this.id = "google-drive";
1790
+ this.icon = "\u{1F4C1}";
1791
+ // Can be replaced with proper icon
1792
+ this.accessToken = null;
1793
+ this.CLIENT_ID = typeof window.__GOOGLE_DRIVE_CLIENT_ID__ !== "undefined" && window.__GOOGLE_DRIVE_CLIENT_ID__ || "";
1794
+ this.DISCOVERY_DOC = "https://www.googleapis.com/discovery/v1/apis/drive/v3/rest";
1795
+ this.SCOPES = "https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/drive.appdata";
1796
+ this.gapiInitialized = false;
1797
+ this.gisInitialized = false;
1798
+ this.initializeAPIs();
1781
1799
  }
1782
- /**
1783
- * Make API call with automatic token refresh on 401
1784
- */
1785
- async apiCall(method, path, body, projectId, accessToken) {
1786
- let url = `${this.tssUrl}${path}`;
1787
- if (projectId) {
1788
- const separator = url.includes("?") ? "&" : "?";
1789
- url = `${url}${separator}projectId=${encodeURIComponent(projectId)}`;
1800
+ async initializeAPIs() {
1801
+ if (!window.gapi) {
1802
+ await this.loadScript("https://apis.google.com/js/api.js");
1790
1803
  }
1791
- const headers = {
1792
- "Content-Type": "application/json"
1793
- };
1794
- if (accessToken) {
1795
- headers["Authorization"] = `Bearer ${accessToken}`;
1804
+ if (!window.google?.accounts) {
1805
+ await this.loadScript("https://accounts.google.com/gsi/client");
1796
1806
  }
1797
- try {
1798
- const response = await fetch(url, {
1799
- method,
1800
- headers,
1801
- body: body ? JSON.stringify(body) : void 0,
1802
- credentials: "include"
1803
- // Send cookies
1807
+ if (!this.gapiInitialized) {
1808
+ await new Promise((resolve) => {
1809
+ window.gapi.load("client", resolve);
1804
1810
  });
1805
- if (response.status === 401 && accessToken) {
1806
- console.log("[iframe][TokenRefresh] Got 401, checking if TOKEN_EXPIRED...");
1807
- try {
1808
- const errorData = await response.json();
1809
- if (errorData.error_code === "TOKEN_EXPIRED" || errorData.action === "REFRESH_TOKEN") {
1810
- console.log("[iframe][TokenRefresh] TOKEN_EXPIRED detected, requesting new token from parent...");
1811
- const newToken = await this.requestNewTokenFromParent();
1812
- if (newToken) {
1813
- console.log("[iframe][TokenRefresh] \u2705 Got new token, retrying request...");
1814
- headers["Authorization"] = `Bearer ${newToken}`;
1815
- const retryResponse = await fetch(url, {
1816
- method,
1817
- headers,
1818
- body: body ? JSON.stringify(body) : void 0,
1819
- credentials: "include"
1820
- });
1821
- if (!retryResponse.ok) {
1822
- const retryErrorText = await retryResponse.text();
1823
- throw new Error(`API call failed after token refresh (${retryResponse.status}): ${retryErrorText}`);
1824
- }
1825
- return await retryResponse.json();
1826
- } else {
1827
- throw new Error("Failed to refresh access token");
1828
- }
1829
- }
1830
- } catch (parseError) {
1831
- console.error("[iframe][TokenRefresh] Failed to parse 401 response:", parseError);
1832
- }
1833
- throw new Error(`API call failed (401): Authentication required`);
1834
- }
1835
- if (!response.ok) {
1836
- const errorText = await response.text();
1837
- throw new Error(`API call failed (${response.status}): ${errorText}`);
1838
- }
1839
- return await response.json();
1840
- } catch (error) {
1841
- console.error(`[iframe][TokenRefresh] API call failed: ${method} ${path}`, error);
1842
- throw error instanceof Error ? error : new Error("Unknown API error");
1811
+ await window.gapi.client.init({
1812
+ discoveryDocs: [this.DISCOVERY_DOC]
1813
+ });
1814
+ this.gapiInitialized = true;
1815
+ console.log("[GoogleDrive] Google API client initialized");
1843
1816
  }
1844
- }
1845
- /**
1846
- * Request new access token from parent window
1847
- */
1848
- async requestNewTokenFromParent() {
1849
- if (this.pendingTokenRefresh) {
1850
- console.log("[iframe][TokenRefresh] Token refresh already in progress, waiting...");
1851
- return this.pendingTokenRefresh;
1852
- }
1853
- this.pendingTokenRefresh = new Promise((resolve, reject) => {
1854
- const messageId = `token_refresh_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
1855
- const timeout = setTimeout(() => {
1856
- cleanup();
1857
- reject(new Error("Token refresh timeout"));
1858
- }, 3e4);
1859
- const messageHandler = (event) => {
1860
- const message = event.data;
1861
- if (message && message.type === "LUMIA_PASSPORT_TOKEN_REFRESHED" && message.messageId === messageId) {
1862
- cleanup();
1863
- if (message.accessToken) {
1864
- console.log("[iframe][TokenRefresh] \u2705 Received new token from parent");
1865
- resolve(message.accessToken);
1866
- } else if (message.error) {
1867
- console.error("[iframe][TokenRefresh] \u274C Token refresh failed:", message.error);
1868
- reject(new Error(message.error));
1869
- } else {
1870
- reject(new Error("Invalid token refresh response"));
1871
- }
1872
- }
1873
- };
1874
- const cleanup = () => {
1875
- clearTimeout(timeout);
1876
- window.removeEventListener("message", messageHandler);
1877
- this.pendingTokenRefresh = null;
1878
- };
1879
- window.addEventListener("message", messageHandler);
1880
- if (window.parent && window.parent !== window) {
1881
- window.parent.postMessage(
1882
- {
1883
- type: "LUMIA_PASSPORT_REQUEST_NEW_TOKEN",
1884
- messageId,
1885
- timestamp: Date.now()
1886
- },
1887
- "*"
1888
- // Parent will validate origin
1889
- );
1890
- console.log("[iframe][TokenRefresh] \u{1F4E4} Sent LUMIA_PASSPORT_REQUEST_NEW_TOKEN to parent");
1891
- } else {
1892
- cleanup();
1893
- reject(new Error("No parent window available"));
1894
- }
1895
- });
1896
- try {
1897
- return await this.pendingTokenRefresh;
1898
- } catch (error) {
1899
- this.pendingTokenRefresh = null;
1900
- console.error("[iframe][TokenRefresh] Token refresh failed:", error);
1901
- return null;
1902
- }
1903
- }
1904
- };
1905
-
1906
- // src/iframe/lib/backup-manager.ts
1907
- init_server_backup();
1908
- init_local_backup();
1909
-
1910
- // src/iframe/lib/backup/cloud-backup.ts
1911
- init_crypto_utils();
1912
-
1913
- // src/iframe/lib/backup/cloudStorage.ts
1914
- var GoogleDriveProvider = class {
1915
- constructor() {
1916
- this.name = "Google Drive";
1917
- this.id = "google-drive";
1918
- this.icon = "\u{1F4C1}";
1919
- // Can be replaced with proper icon
1920
- this.accessToken = null;
1921
- this.CLIENT_ID = typeof window.__GOOGLE_DRIVE_CLIENT_ID__ !== "undefined" && window.__GOOGLE_DRIVE_CLIENT_ID__ || "";
1922
- this.DISCOVERY_DOC = "https://www.googleapis.com/discovery/v1/apis/drive/v3/rest";
1923
- this.SCOPES = "https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/drive.appdata";
1924
- this.gapiInitialized = false;
1925
- this.gisInitialized = false;
1926
- this.initializeAPIs();
1927
- }
1928
- async initializeAPIs() {
1929
- if (!window.gapi) {
1930
- await this.loadScript("https://apis.google.com/js/api.js");
1931
- }
1932
- if (!window.google?.accounts) {
1933
- await this.loadScript("https://accounts.google.com/gsi/client");
1934
- }
1935
- if (!this.gapiInitialized) {
1936
- await new Promise((resolve) => {
1937
- window.gapi.load("client", resolve);
1938
- });
1939
- await window.gapi.client.init({
1940
- discoveryDocs: [this.DISCOVERY_DOC]
1941
- });
1942
- this.gapiInitialized = true;
1943
- console.log("[GoogleDrive] Google API client initialized");
1944
- }
1945
- if (!this.gisInitialized) {
1946
- this.gisInitialized = true;
1947
- console.log("[GoogleDrive] Google Identity Services initialized");
1817
+ if (!this.gisInitialized) {
1818
+ this.gisInitialized = true;
1819
+ console.log("[GoogleDrive] Google Identity Services initialized");
1948
1820
  }
1949
1821
  }
1950
1822
  loadScript(src) {
@@ -2171,6 +2043,140 @@ function getCloudProviders() {
2171
2043
  }));
2172
2044
  }
2173
2045
 
2046
+ // src/iframe/lib/backup-manager.ts
2047
+ init_local_backup();
2048
+ init_server_backup();
2049
+
2050
+ // src/iframe/lib/token-refresh-client.ts
2051
+ var TokenRefreshApiClient = class {
2052
+ constructor() {
2053
+ this.pendingTokenRefresh = null;
2054
+ this.tssUrl = "https://api.lumiapassport.com/tss";
2055
+ }
2056
+ /**
2057
+ * Make API call with automatic token refresh on 401
2058
+ */
2059
+ async apiCall(method, path, body, projectId, accessToken) {
2060
+ let url = `${this.tssUrl}${path}`;
2061
+ if (projectId) {
2062
+ const separator = url.includes("?") ? "&" : "?";
2063
+ url = `${url}${separator}projectId=${encodeURIComponent(projectId)}`;
2064
+ }
2065
+ const headers = {
2066
+ "Content-Type": "application/json"
2067
+ };
2068
+ if (accessToken) {
2069
+ headers["Authorization"] = `Bearer ${accessToken}`;
2070
+ }
2071
+ try {
2072
+ const response = await fetch(url, {
2073
+ method,
2074
+ headers,
2075
+ body: body ? JSON.stringify(body) : void 0,
2076
+ credentials: "include"
2077
+ // Send cookies
2078
+ });
2079
+ if (response.status === 401 && accessToken) {
2080
+ console.log("[iframe][TokenRefresh] Got 401, checking if TOKEN_EXPIRED...");
2081
+ try {
2082
+ const errorData = await response.json();
2083
+ if (errorData.error_code === "TOKEN_EXPIRED" || errorData.action === "REFRESH_TOKEN") {
2084
+ console.log("[iframe][TokenRefresh] TOKEN_EXPIRED detected, requesting new token from parent...");
2085
+ const newToken = await this.requestNewTokenFromParent();
2086
+ if (newToken) {
2087
+ console.log("[iframe][TokenRefresh] \u2705 Got new token, retrying request...");
2088
+ headers["Authorization"] = `Bearer ${newToken}`;
2089
+ const retryResponse = await fetch(url, {
2090
+ method,
2091
+ headers,
2092
+ body: body ? JSON.stringify(body) : void 0,
2093
+ credentials: "include"
2094
+ });
2095
+ if (!retryResponse.ok) {
2096
+ const retryErrorText = await retryResponse.text();
2097
+ throw new Error(`API call failed after token refresh (${retryResponse.status}): ${retryErrorText}`);
2098
+ }
2099
+ return await retryResponse.json();
2100
+ } else {
2101
+ throw new Error("Failed to refresh access token");
2102
+ }
2103
+ }
2104
+ } catch (parseError) {
2105
+ console.error("[iframe][TokenRefresh] Failed to parse 401 response:", parseError);
2106
+ }
2107
+ throw new Error(`API call failed (401): Authentication required`);
2108
+ }
2109
+ if (!response.ok) {
2110
+ const errorText = await response.text();
2111
+ throw new Error(`API call failed (${response.status}): ${errorText}`);
2112
+ }
2113
+ return await response.json();
2114
+ } catch (error) {
2115
+ console.error(`[iframe][TokenRefresh] API call failed: ${method} ${path}`, error);
2116
+ throw error instanceof Error ? error : new Error("Unknown API error");
2117
+ }
2118
+ }
2119
+ /**
2120
+ * Request new access token from parent window
2121
+ */
2122
+ async requestNewTokenFromParent() {
2123
+ if (this.pendingTokenRefresh) {
2124
+ console.log("[iframe][TokenRefresh] Token refresh already in progress, waiting...");
2125
+ return this.pendingTokenRefresh;
2126
+ }
2127
+ this.pendingTokenRefresh = new Promise((resolve, reject) => {
2128
+ const messageId = `token_refresh_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
2129
+ const timeout = setTimeout(() => {
2130
+ cleanup();
2131
+ reject(new Error("Token refresh timeout"));
2132
+ }, 3e4);
2133
+ const messageHandler = (event) => {
2134
+ const message = event.data;
2135
+ if (message && message.type === "LUMIA_PASSPORT_TOKEN_REFRESHED" && message.messageId === messageId) {
2136
+ cleanup();
2137
+ if (message.accessToken) {
2138
+ console.log("[iframe][TokenRefresh] \u2705 Received new token from parent");
2139
+ resolve(message.accessToken);
2140
+ } else if (message.error) {
2141
+ console.error("[iframe][TokenRefresh] \u274C Token refresh failed:", message.error);
2142
+ reject(new Error(message.error));
2143
+ } else {
2144
+ reject(new Error("Invalid token refresh response"));
2145
+ }
2146
+ }
2147
+ };
2148
+ const cleanup = () => {
2149
+ clearTimeout(timeout);
2150
+ window.removeEventListener("message", messageHandler);
2151
+ this.pendingTokenRefresh = null;
2152
+ };
2153
+ window.addEventListener("message", messageHandler);
2154
+ if (window.parent && window.parent !== window) {
2155
+ window.parent.postMessage(
2156
+ {
2157
+ type: "LUMIA_PASSPORT_REQUEST_NEW_TOKEN",
2158
+ messageId,
2159
+ timestamp: Date.now()
2160
+ },
2161
+ "*"
2162
+ // Parent will validate origin
2163
+ );
2164
+ console.log("[iframe][TokenRefresh] \u{1F4E4} Sent LUMIA_PASSPORT_REQUEST_NEW_TOKEN to parent");
2165
+ } else {
2166
+ cleanup();
2167
+ reject(new Error("No parent window available"));
2168
+ }
2169
+ });
2170
+ try {
2171
+ return await this.pendingTokenRefresh;
2172
+ } catch (error) {
2173
+ this.pendingTokenRefresh = null;
2174
+ console.error("[iframe][TokenRefresh] Token refresh failed:", error);
2175
+ return null;
2176
+ }
2177
+ }
2178
+ };
2179
+
2174
2180
  // src/iframe/lib/backup-manager.ts
2175
2181
  var BACKUP_VERSION = "1.0";
2176
2182
  var BackupManager = class {
@@ -2215,7 +2221,7 @@ var BackupManager = class {
2215
2221
  * Get backup status for all methods
2216
2222
  */
2217
2223
  getBackupStatus(userId) {
2218
- const statusData = localStorage.getItem(`lumia-passport.backup.status.${userId}`);
2224
+ const statusData = localStorage.getItem(`${LOOCAL_BACKUP_STATUS_KEY}.${userId}`);
2219
2225
  if (statusData) {
2220
2226
  try {
2221
2227
  return JSON.parse(statusData);
@@ -2235,10 +2241,7 @@ var BackupManager = class {
2235
2241
  updateBackupStatus(userId, method, status) {
2236
2242
  const currentStatus = this.getBackupStatus(userId);
2237
2243
  currentStatus[method] = { ...currentStatus[method], ...status };
2238
- localStorage.setItem(
2239
- `lumia-passport.backup.status.${userId}`,
2240
- JSON.stringify(currentStatus)
2241
- );
2244
+ localStorage.setItem(`${LOOCAL_BACKUP_STATUS_KEY}.${userId}`, JSON.stringify(currentStatus));
2242
2245
  console.log(`[iframe][BackupManager] Updated ${method} backup status for ${userId}`);
2243
2246
  }
2244
2247
  /**
@@ -2334,12 +2337,7 @@ var BackupManager = class {
2334
2337
  encryptionPassword = result.password;
2335
2338
  credentialId = result.credentialId;
2336
2339
  }
2337
- const encrypted = await encryptKeyshare2(
2338
- data,
2339
- encryptionPassword,
2340
- password ? "password" : "passkey",
2341
- credentialId
2342
- );
2340
+ const encrypted = await encryptKeyshare2(data, encryptionPassword, password ? "password" : "passkey", credentialId);
2343
2341
  console.log("[iframe][BackupManager] \u2705 Backup data encrypted");
2344
2342
  return encrypted;
2345
2343
  }
@@ -3230,18 +3228,221 @@ var RampnowOnrampAPI = class {
3230
3228
  }
3231
3229
  };
3232
3230
 
3233
- // src/lib/errors.ts
3234
- var LumiaPassportError = class _LumiaPassportError extends Error {
3235
- constructor(message, code) {
3236
- super(message);
3237
- this.code = code;
3238
- this.name = "LumiaPassportError";
3239
- if (Error.captureStackTrace) {
3240
- Error.captureStackTrace(this, _LumiaPassportError);
3231
+ // src/iframe/lib/opted-out-apps-manager.ts
3232
+ var STORAGE_KEY = "lumia_passport_opted_out_apps";
3233
+ var OptedOutAppsManager = class {
3234
+ /**
3235
+ * Check if user has opted out of auto-trust for this app
3236
+ */
3237
+ isOptedOut(userId, projectId, origin) {
3238
+ const optedOutApps = this.getOptedOutApps();
3239
+ const result = optedOutApps.some(
3240
+ (app) => app.userId === userId && app.projectId === projectId && app.origin === origin
3241
+ );
3242
+ return result;
3243
+ }
3244
+ /**
3245
+ * Add app to opted-out list (when user removes ecosystem app from trusted list)
3246
+ */
3247
+ addOptedOut(userId, projectId, origin) {
3248
+ const optedOutApps = this.getOptedOutApps();
3249
+ const exists = optedOutApps.some(
3250
+ (app) => app.userId === userId && app.projectId === projectId && app.origin === origin
3251
+ );
3252
+ if (exists) {
3253
+ return;
3241
3254
  }
3255
+ optedOutApps.push({
3256
+ userId,
3257
+ projectId,
3258
+ origin,
3259
+ optedOutAt: Date.now()
3260
+ });
3261
+ this.saveOptedOutApps(optedOutApps);
3262
+ }
3263
+ /**
3264
+ * Remove app from opted-out list (when user manually re-trusts via checkbox)
3265
+ */
3266
+ removeOptedOut(userId, projectId, origin) {
3267
+ const optedOutApps = this.getOptedOutApps();
3268
+ const filtered = optedOutApps.filter(
3269
+ (app) => !(app.userId === userId && app.projectId === projectId && app.origin === origin)
3270
+ );
3271
+ this.saveOptedOutApps(filtered);
3272
+ }
3273
+ /**
3274
+ * Clear all opted-out apps for a user (when clearing all trusted apps)
3275
+ */
3276
+ clearOptedOutForUser(userId) {
3277
+ const optedOutApps = this.getOptedOutApps();
3278
+ const filtered = optedOutApps.filter((app) => app.userId !== userId);
3279
+ this.saveOptedOutApps(filtered);
3280
+ }
3281
+ /**
3282
+ * Get all opted-out apps from storage
3283
+ */
3284
+ getOptedOutApps() {
3285
+ try {
3286
+ const data = localStorage.getItem(STORAGE_KEY);
3287
+ if (!data) {
3288
+ return [];
3289
+ }
3290
+ const apps = JSON.parse(data);
3291
+ return Array.isArray(apps) ? apps : [];
3292
+ } catch (error) {
3293
+ console.error("[OptedOutApps] Error reading opted-out apps:", error);
3294
+ return [];
3295
+ }
3296
+ }
3297
+ /**
3298
+ * Save opted-out apps to storage
3299
+ */
3300
+ saveOptedOutApps(apps) {
3301
+ try {
3302
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(apps));
3303
+ } catch (error) {
3304
+ console.error("[OptedOutApps] Error saving opted-out apps:", error);
3305
+ }
3306
+ }
3307
+ };
3308
+
3309
+ // src/iframe/lib/sdk-channel-manager.ts
3310
+ var SdkChannelManager = class {
3311
+ // 30 minutes
3312
+ constructor() {
3313
+ this.channels = /* @__PURE__ */ new Map();
3314
+ this.CHANNEL_TIMEOUT = 30 * 60 * 1e3;
3315
+ setInterval(() => this.cleanupExpiredChannels(), 5 * 60 * 1e3);
3316
+ }
3317
+ /**
3318
+ * Create a new SDK channel
3319
+ */
3320
+ createChannel(projectId, origin) {
3321
+ const channelToken = this.generateSecureToken();
3322
+ const channel = {
3323
+ token: channelToken,
3324
+ projectId,
3325
+ origin,
3326
+ createdAt: Date.now(),
3327
+ lastActivity: Date.now()
3328
+ };
3329
+ this.channels.set(channelToken, channel);
3330
+ console.log(`[iframe][SdkChannel] \u2713 Created: ${channelToken.slice(0, 16)}... origin=${origin}, total=${this.channels.size}`);
3331
+ return channelToken;
3332
+ }
3333
+ /**
3334
+ * Validate channel token and origin
3335
+ */
3336
+ validateChannel(channelToken, origin) {
3337
+ const channel = this.channels.get(channelToken);
3338
+ if (!channel) {
3339
+ console.warn(`[iframe][SdkChannel] \u2717 NOT FOUND: ${channelToken.slice(0, 16)}... (active channels: ${this.channels.size})`);
3340
+ return false;
3341
+ }
3342
+ if (channel.origin !== origin) {
3343
+ console.error(`[iframe][SdkChannel] \u2717 ORIGIN MISMATCH: expected ${channel.origin}, got ${origin}`);
3344
+ return false;
3345
+ }
3346
+ const age = Date.now() - channel.lastActivity;
3347
+ if (age > this.CHANNEL_TIMEOUT) {
3348
+ console.warn(`[iframe][SdkChannel] \u2717 EXPIRED: age=${Math.round(age / 1e3)}s > timeout=${this.CHANNEL_TIMEOUT / 1e3}s`);
3349
+ this.channels.delete(channelToken);
3350
+ return false;
3351
+ }
3352
+ channel.lastActivity = Date.now();
3353
+ return true;
3354
+ }
3355
+ /**
3356
+ * Get channel by token
3357
+ */
3358
+ getChannel(channelToken) {
3359
+ return this.channels.get(channelToken) || null;
3360
+ }
3361
+ /**
3362
+ * Invalidate channel
3363
+ */
3364
+ invalidateChannel(channelToken) {
3365
+ const channel = this.channels.get(channelToken);
3366
+ if (channel) {
3367
+ console.log(`[iframe][SdkChannel] Invalidated channel for origin: ${channel.origin}`);
3368
+ this.channels.delete(channelToken);
3369
+ }
3370
+ }
3371
+ /**
3372
+ * Get all channels for a project
3373
+ */
3374
+ getProjectChannels(projectId) {
3375
+ return Array.from(this.channels.values()).filter(
3376
+ (channel) => channel.projectId === projectId
3377
+ );
3378
+ }
3379
+ /**
3380
+ * Generate cryptographically secure channel token
3381
+ */
3382
+ generateSecureToken() {
3383
+ const buffer = new Uint8Array(32);
3384
+ crypto.getRandomValues(buffer);
3385
+ return Array.from(buffer, (byte) => byte.toString(16).padStart(2, "0")).join("");
3386
+ }
3387
+ /**
3388
+ * Cleanup expired channels
3389
+ */
3390
+ cleanupExpiredChannels() {
3391
+ const now = Date.now();
3392
+ let cleanedCount = 0;
3393
+ for (const [token, channel] of this.channels.entries()) {
3394
+ const age = now - channel.lastActivity;
3395
+ if (age > this.CHANNEL_TIMEOUT) {
3396
+ this.channels.delete(token);
3397
+ cleanedCount++;
3398
+ }
3399
+ }
3400
+ if (cleanedCount > 0) {
3401
+ console.log(`[iframe][SdkChannel] Cleaned up ${cleanedCount} expired channels`);
3402
+ }
3403
+ }
3404
+ /**
3405
+ * Check if there is an active channel for a given origin
3406
+ */
3407
+ hasChannel(origin) {
3408
+ const now = Date.now();
3409
+ for (const channel of this.channels.values()) {
3410
+ if (channel.origin === origin) {
3411
+ const age = now - channel.lastActivity;
3412
+ if (age <= this.CHANNEL_TIMEOUT) {
3413
+ return true;
3414
+ }
3415
+ }
3416
+ }
3417
+ return false;
3418
+ }
3419
+ /**
3420
+ * Get channel statistics
3421
+ */
3422
+ getStats() {
3423
+ const stats = {
3424
+ total: this.channels.size,
3425
+ byProject: {}
3426
+ };
3427
+ for (const channel of this.channels.values()) {
3428
+ stats.byProject[channel.projectId] = (stats.byProject[channel.projectId] || 0) + 1;
3429
+ }
3430
+ return stats;
3242
3431
  }
3243
3432
  };
3244
- var _UserRejectedError = class _UserRejectedError extends LumiaPassportError {
3433
+
3434
+ // src/lib/errors.ts
3435
+ var AccountAbstractionError = class _AccountAbstractionError extends Error {
3436
+ constructor(message, code) {
3437
+ super(message);
3438
+ this.code = code;
3439
+ this.name = "LumiaPassportError";
3440
+ if (Error.captureStackTrace) {
3441
+ Error.captureStackTrace(this, _AccountAbstractionError);
3442
+ }
3443
+ }
3444
+ };
3445
+ var _UserRejectedError = class _UserRejectedError extends AccountAbstractionError {
3245
3446
  constructor(message = "User rejected transaction") {
3246
3447
  super(message, _UserRejectedError.CODE);
3247
3448
  this.name = "UserRejectedError";
@@ -3307,7 +3508,7 @@ var SecureMessenger = class {
3307
3508
  return;
3308
3509
  }
3309
3510
  if (!this.validateMessageStructure(message)) {
3310
- console.error("[iframe][Messenger] Invalid Lumia Passport message structure:", {
3511
+ console.error("[iframe][Messenger] Invalid EmbarkAI message structure:", {
3311
3512
  type: message.type,
3312
3513
  hasMessageId: !!message.messageId,
3313
3514
  hasTimestamp: !!message.timestamp,
@@ -3377,7 +3578,7 @@ var SecureMessenger = class {
3377
3578
  return;
3378
3579
  }
3379
3580
  const errorMessage = error instanceof Error ? error.message : error;
3380
- const errorCode = error instanceof LumiaPassportError ? error.code : void 0;
3581
+ const errorCode = error instanceof AccountAbstractionError ? error.code : void 0;
3381
3582
  const response = {
3382
3583
  type: "LUMIA_PASSPORT_ERROR",
3383
3584
  messageId,
@@ -3419,7 +3620,7 @@ var SecureMessenger = class {
3419
3620
  console.log("[iframe][Messenger] SDK channel token set");
3420
3621
  }
3421
3622
  /**
3422
- * Quick check if message looks like Lumia Passport protocol
3623
+ * Quick check if message looks like EmbarkAI protocol
3423
3624
  * This filters out browser extension messages, dev tools, etc.
3424
3625
  */
3425
3626
  isLumiaMessage(message) {
@@ -3527,131 +3728,6 @@ var SecureMessenger = class {
3527
3728
  }
3528
3729
  };
3529
3730
 
3530
- // src/iframe/lib/sdk-channel-manager.ts
3531
- var SdkChannelManager = class {
3532
- // 30 minutes
3533
- constructor() {
3534
- this.channels = /* @__PURE__ */ new Map();
3535
- this.CHANNEL_TIMEOUT = 30 * 60 * 1e3;
3536
- setInterval(() => this.cleanupExpiredChannels(), 5 * 60 * 1e3);
3537
- }
3538
- /**
3539
- * Create a new SDK channel
3540
- */
3541
- createChannel(projectId, origin) {
3542
- const channelToken = this.generateSecureToken();
3543
- const channel = {
3544
- token: channelToken,
3545
- projectId,
3546
- origin,
3547
- createdAt: Date.now(),
3548
- lastActivity: Date.now()
3549
- };
3550
- this.channels.set(channelToken, channel);
3551
- console.log(`[iframe][SdkChannel] \u2713 Created: ${channelToken.slice(0, 16)}... origin=${origin}, total=${this.channels.size}`);
3552
- return channelToken;
3553
- }
3554
- /**
3555
- * Validate channel token and origin
3556
- */
3557
- validateChannel(channelToken, origin) {
3558
- const channel = this.channels.get(channelToken);
3559
- if (!channel) {
3560
- console.warn(`[iframe][SdkChannel] \u2717 NOT FOUND: ${channelToken.slice(0, 16)}... (active channels: ${this.channels.size})`);
3561
- return false;
3562
- }
3563
- if (channel.origin !== origin) {
3564
- console.error(`[iframe][SdkChannel] \u2717 ORIGIN MISMATCH: expected ${channel.origin}, got ${origin}`);
3565
- return false;
3566
- }
3567
- const age = Date.now() - channel.lastActivity;
3568
- if (age > this.CHANNEL_TIMEOUT) {
3569
- console.warn(`[iframe][SdkChannel] \u2717 EXPIRED: age=${Math.round(age / 1e3)}s > timeout=${this.CHANNEL_TIMEOUT / 1e3}s`);
3570
- this.channels.delete(channelToken);
3571
- return false;
3572
- }
3573
- channel.lastActivity = Date.now();
3574
- return true;
3575
- }
3576
- /**
3577
- * Get channel by token
3578
- */
3579
- getChannel(channelToken) {
3580
- return this.channels.get(channelToken) || null;
3581
- }
3582
- /**
3583
- * Invalidate channel
3584
- */
3585
- invalidateChannel(channelToken) {
3586
- const channel = this.channels.get(channelToken);
3587
- if (channel) {
3588
- console.log(`[iframe][SdkChannel] Invalidated channel for origin: ${channel.origin}`);
3589
- this.channels.delete(channelToken);
3590
- }
3591
- }
3592
- /**
3593
- * Get all channels for a project
3594
- */
3595
- getProjectChannels(projectId) {
3596
- return Array.from(this.channels.values()).filter(
3597
- (channel) => channel.projectId === projectId
3598
- );
3599
- }
3600
- /**
3601
- * Generate cryptographically secure channel token
3602
- */
3603
- generateSecureToken() {
3604
- const buffer = new Uint8Array(32);
3605
- crypto.getRandomValues(buffer);
3606
- return Array.from(buffer, (byte) => byte.toString(16).padStart(2, "0")).join("");
3607
- }
3608
- /**
3609
- * Cleanup expired channels
3610
- */
3611
- cleanupExpiredChannels() {
3612
- const now = Date.now();
3613
- let cleanedCount = 0;
3614
- for (const [token, channel] of this.channels.entries()) {
3615
- const age = now - channel.lastActivity;
3616
- if (age > this.CHANNEL_TIMEOUT) {
3617
- this.channels.delete(token);
3618
- cleanedCount++;
3619
- }
3620
- }
3621
- if (cleanedCount > 0) {
3622
- console.log(`[iframe][SdkChannel] Cleaned up ${cleanedCount} expired channels`);
3623
- }
3624
- }
3625
- /**
3626
- * Check if there is an active channel for a given origin
3627
- */
3628
- hasChannel(origin) {
3629
- const now = Date.now();
3630
- for (const channel of this.channels.values()) {
3631
- if (channel.origin === origin) {
3632
- const age = now - channel.lastActivity;
3633
- if (age <= this.CHANNEL_TIMEOUT) {
3634
- return true;
3635
- }
3636
- }
3637
- }
3638
- return false;
3639
- }
3640
- /**
3641
- * Get channel statistics
3642
- */
3643
- getStats() {
3644
- const stats = {
3645
- total: this.channels.size,
3646
- byProject: {}
3647
- };
3648
- for (const channel of this.channels.values()) {
3649
- stats.byProject[channel.projectId] = (stats.byProject[channel.projectId] || 0) + 1;
3650
- }
3651
- return stats;
3652
- }
3653
- };
3654
-
3655
3731
  // src/iframe/lib/storage-manager.ts
3656
3732
  var StorageManager = class {
3657
3733
  constructor() {
@@ -3782,7 +3858,7 @@ var StorageManager = class {
3782
3858
  };
3783
3859
 
3784
3860
  // src/iframe/lib/trusted-apps-manager.ts
3785
- var STORAGE_KEY = "lumia_passport_trusted_apps";
3861
+ var STORAGE_KEY2 = "passport-trusted-apps";
3786
3862
  var TrustedAppsManager = class {
3787
3863
  /**
3788
3864
  * Check if app is trusted by user
@@ -3857,7 +3933,7 @@ var TrustedAppsManager = class {
3857
3933
  */
3858
3934
  getTrustedApps() {
3859
3935
  try {
3860
- const data = localStorage.getItem(STORAGE_KEY);
3936
+ const data = localStorage.getItem(STORAGE_KEY2);
3861
3937
  console.log("[TrustedApps] Raw storage data:", data);
3862
3938
  if (!data) {
3863
3939
  console.log("[TrustedApps] No data in storage");
@@ -3876,7 +3952,7 @@ var TrustedAppsManager = class {
3876
3952
  */
3877
3953
  saveTrustedApps(apps) {
3878
3954
  try {
3879
- localStorage.setItem(STORAGE_KEY, JSON.stringify(apps));
3955
+ localStorage.setItem(STORAGE_KEY2, JSON.stringify(apps));
3880
3956
  } catch (error) {
3881
3957
  console.error("[TrustedApps] Error saving trusted apps:", error);
3882
3958
  throw new Error("Failed to save trusted apps");
@@ -3884,84 +3960,6 @@ var TrustedAppsManager = class {
3884
3960
  }
3885
3961
  };
3886
3962
 
3887
- // src/iframe/lib/opted-out-apps-manager.ts
3888
- var STORAGE_KEY2 = "lumia_passport_opted_out_apps";
3889
- var OptedOutAppsManager = class {
3890
- /**
3891
- * Check if user has opted out of auto-trust for this app
3892
- */
3893
- isOptedOut(userId, projectId, origin) {
3894
- const optedOutApps = this.getOptedOutApps();
3895
- const result = optedOutApps.some(
3896
- (app) => app.userId === userId && app.projectId === projectId && app.origin === origin
3897
- );
3898
- return result;
3899
- }
3900
- /**
3901
- * Add app to opted-out list (when user removes ecosystem app from trusted list)
3902
- */
3903
- addOptedOut(userId, projectId, origin) {
3904
- const optedOutApps = this.getOptedOutApps();
3905
- const exists = optedOutApps.some(
3906
- (app) => app.userId === userId && app.projectId === projectId && app.origin === origin
3907
- );
3908
- if (exists) {
3909
- return;
3910
- }
3911
- optedOutApps.push({
3912
- userId,
3913
- projectId,
3914
- origin,
3915
- optedOutAt: Date.now()
3916
- });
3917
- this.saveOptedOutApps(optedOutApps);
3918
- }
3919
- /**
3920
- * Remove app from opted-out list (when user manually re-trusts via checkbox)
3921
- */
3922
- removeOptedOut(userId, projectId, origin) {
3923
- const optedOutApps = this.getOptedOutApps();
3924
- const filtered = optedOutApps.filter(
3925
- (app) => !(app.userId === userId && app.projectId === projectId && app.origin === origin)
3926
- );
3927
- this.saveOptedOutApps(filtered);
3928
- }
3929
- /**
3930
- * Clear all opted-out apps for a user (when clearing all trusted apps)
3931
- */
3932
- clearOptedOutForUser(userId) {
3933
- const optedOutApps = this.getOptedOutApps();
3934
- const filtered = optedOutApps.filter((app) => app.userId !== userId);
3935
- this.saveOptedOutApps(filtered);
3936
- }
3937
- /**
3938
- * Get all opted-out apps from storage
3939
- */
3940
- getOptedOutApps() {
3941
- try {
3942
- const data = localStorage.getItem(STORAGE_KEY2);
3943
- if (!data) {
3944
- return [];
3945
- }
3946
- const apps = JSON.parse(data);
3947
- return Array.isArray(apps) ? apps : [];
3948
- } catch (error) {
3949
- console.error("[OptedOutApps] Error reading opted-out apps:", error);
3950
- return [];
3951
- }
3952
- }
3953
- /**
3954
- * Save opted-out apps to storage
3955
- */
3956
- saveOptedOutApps(apps) {
3957
- try {
3958
- localStorage.setItem(STORAGE_KEY2, JSON.stringify(apps));
3959
- } catch (error) {
3960
- console.error("[OptedOutApps] Error saving opted-out apps:", error);
3961
- }
3962
- }
3963
- };
3964
-
3965
3963
  // src/iframe/lib/signing-manager.ts
3966
3964
  var SigningManager = class extends TokenRefreshApiClient {
3967
3965
  constructor() {
@@ -4406,11 +4404,11 @@ var SigningManager = class extends TokenRefreshApiClient {
4406
4404
  };
4407
4405
 
4408
4406
  // src/iframe/main.ts
4409
- var IFRAME_VERSION = "0.1.1";
4407
+ var IFRAME_VERSION = "0.1.3";
4410
4408
  var IframeWallet = class {
4411
4409
  constructor() {
4412
4410
  console.log("=".repeat(60));
4413
- console.log(` Lumia Passport Secure Wallet - iframe version ${IFRAME_VERSION}`);
4411
+ console.log(` EmbarkAI Secure Wallet - iframe version ${IFRAME_VERSION}`);
4414
4412
  console.log("=".repeat(60));
4415
4413
  this.messenger = new SecureMessenger();
4416
4414
  this.sdkChannelManager = new SdkChannelManager();
@@ -4521,11 +4519,15 @@ var IframeWallet = class {
4521
4519
  const { messageId } = message;
4522
4520
  const isValid = this.sdkChannelManager.validateChannel(sessionToken, origin);
4523
4521
  const stats = this.sdkChannelManager.getStats();
4524
- this.messenger.sendResponse(messageId, {
4525
- type: "LUMIA_PASSPORT_HEARTBEAT_RESPONSE",
4526
- valid: isValid,
4527
- channelsCount: stats.total
4528
- }, origin);
4522
+ this.messenger.sendResponse(
4523
+ messageId,
4524
+ {
4525
+ type: "LUMIA_PASSPORT_HEARTBEAT_RESPONSE",
4526
+ valid: isValid,
4527
+ channelsCount: stats.total
4528
+ },
4529
+ origin
4530
+ );
4529
4531
  console.log(`[iframe] HEARTBEAT: valid=${isValid}, channels=${stats.total}`);
4530
4532
  }
4531
4533
  async handleSDKAuth(message, origin) {
@@ -4570,13 +4572,7 @@ var IframeWallet = class {
4570
4572
  if (!isOptedOut) {
4571
4573
  const metadata = await this.authManager.getProjectMetadata(projectId);
4572
4574
  console.log(`[iframe] Got metadata for auto-trust:`, metadata?.name, metadata?.logo);
4573
- this.trustedApps.addTrustedApp(
4574
- userId,
4575
- projectId,
4576
- origin,
4577
- metadata?.name,
4578
- metadata?.logo
4579
- );
4575
+ this.trustedApps.addTrustedApp(userId, projectId, origin, metadata?.name, metadata?.logo);
4580
4576
  console.log(`[iframe] \u2705 Auto-added to trusted apps list for seamless transactions`);
4581
4577
  } else {
4582
4578
  console.log(`[iframe] \u2139\uFE0F User opted-out of auto-trust for this app, skipping`);