@xopcai/xopc 0.0.273 → 0.0.274

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 (140) hide show
  1. package/dist/browser-ext/dist/assets/{auth-DkwOUI6_.js → auth-O8RiOF3l.js} +198 -81
  2. package/dist/browser-ext/dist/background.js +1 -1
  3. package/dist/browser-ext/dist/sidepanel.html +1 -1
  4. package/dist/browser-ext/dist/sidepanel.js +46 -16
  5. package/dist/browser-ext/manifest.json +1 -1
  6. package/dist/extensions/telegram/xopc.extension.json +1 -1
  7. package/dist/gateway/static/root/assets/{activity-store-cF7IX2RP.js → activity-store-SJZ5fS17.js} +1 -1
  8. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-BrgxX8nG.js → agent-avatar-dicebear-BckaANxj.js} +1 -1
  9. package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-DvhuRCLA.js → agent-defaults-settings-panel-B8eyC6K1.js} +1 -1
  10. package/dist/gateway/static/root/assets/agents-CSWj_yo1.js +1 -0
  11. package/dist/gateway/static/root/assets/{api-CYkdUpkx.js → api-CnJAVHBu.js} +1 -1
  12. package/dist/gateway/static/root/assets/{app-management-settings-panel-DfoNzWIa.js → app-management-settings-panel-DY-Zbgmt.js} +1 -1
  13. package/dist/gateway/static/root/assets/{appearance-settings-czbLdCqj.js → appearance-settings-CoJR7ZRl.js} +1 -1
  14. package/dist/gateway/static/root/assets/{apps-page-BSJXKWcE.js → apps-page-CSMvkuot.js} +1 -1
  15. package/dist/gateway/static/root/assets/{archive-plugin-D14P13mX.js → archive-plugin-BwAlRT53.js} +1 -1
  16. package/dist/gateway/static/root/assets/attachment-load-R835uxgL.js +1 -0
  17. package/dist/gateway/static/root/assets/{attachment-preview-renderer-BDTD8YUe.js → attachment-preview-renderer-CfEDFYPE.js} +1 -1
  18. package/dist/gateway/static/root/assets/attachment-process-heavy-C5CqLCsX.js +5 -0
  19. package/dist/gateway/static/root/assets/{automations-page-DM7ZEewI.js → automations-page-BEZy0P_u.js} +1 -1
  20. package/dist/gateway/static/root/assets/automations-workspace-CpaI13J7.js +6 -0
  21. package/dist/gateway/static/root/assets/{binary-plugins-CxNg6InF.js → binary-plugins-CESFx3yb.js} +2 -2
  22. package/dist/gateway/static/root/assets/{block-editor-D22VcYnC.js → block-editor-DDJcf2wR.js} +3 -3
  23. package/dist/gateway/static/root/assets/{browser-automation-inputs-6NWTLEpd.js → browser-automation-inputs-DGCqIPXT.js} +1 -1
  24. package/dist/gateway/static/root/assets/{browser-automations-page-BrqySdbW.js → browser-automations-page-BH4lSfr5.js} +2 -2
  25. package/dist/gateway/static/root/assets/browser-settings-page-CbTooPd2.js +8 -0
  26. package/dist/gateway/static/root/assets/capabilities-settings-panel-C4LZWHhv.js +8 -0
  27. package/dist/gateway/static/root/assets/channels-settings-BAljmSa7.js +1 -0
  28. package/dist/gateway/static/root/assets/{channels-status-swr-BUZimNmp.js → channels-status-swr-BTT2sIGg.js} +1 -1
  29. package/dist/gateway/static/root/assets/{chat-terminal-dock-CZPBNpMI.js → chat-terminal-dock-D8U97O95.js} +2 -2
  30. package/dist/gateway/static/root/assets/connectors-page-CVRMjNNr.js +2 -0
  31. package/dist/gateway/static/root/assets/{date-picker-Da_0QyLe.js → date-picker-7sDSMswL.js} +1 -1
  32. package/dist/gateway/static/root/assets/{dependency-picker-D_djqzy7.js → dependency-picker-DMIC86ZW.js} +1 -1
  33. package/dist/gateway/static/root/assets/{desktop-pet-Celmbw-T.js → desktop-pet-BtdiY5jK.js} +1 -1
  34. package/dist/gateway/static/root/assets/{desktop-pet-settings-CqNNgF45.js → desktop-pet-settings-D_B8azDH.js} +1 -1
  35. package/dist/gateway/static/root/assets/device-pairing-wizard-Dmh3Nh4d.js +1 -0
  36. package/dist/gateway/static/root/assets/excel-worksheet-utils-DpdB-DjU.js +1 -0
  37. package/dist/gateway/static/root/assets/{extension-debug-page-ku3kCjvH.js → extension-debug-page-CoFKb01d.js} +1 -1
  38. package/dist/gateway/static/root/assets/{extension-page-C7FpHwL4.js → extension-page-CEkNsCAY.js} +1 -1
  39. package/dist/gateway/static/root/assets/{extension-settings-page-OMj2tU2_.js → extension-settings-page-Bi2R8P5j.js} +1 -1
  40. package/dist/gateway/static/root/assets/{gateway-config-swr-6eb1vKE1.js → gateway-config-swr-cd8owslz.js} +1 -1
  41. package/dist/gateway/static/root/assets/gateway-settings-q9p3sduo.js +1 -0
  42. package/dist/gateway/static/root/assets/{gateway-startup-retry-DUFFCU8H.js → gateway-startup-retry-CVTa5HSS.js} +1 -1
  43. package/dist/gateway/static/root/assets/{heartbeat-settings-CSFzOACM.js → heartbeat-settings-DQtelFiU.js} +1 -1
  44. package/dist/gateway/static/root/assets/home-page-COpGnTFZ.js +1 -0
  45. package/dist/gateway/static/root/assets/{image-generation-api-C1cHRBRp.js → image-generation-api-DtMRG1oF.js} +1 -1
  46. package/dist/gateway/static/root/assets/{index-C6OWYFtQ.js → index-SxqsTD4i.js} +35 -35
  47. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-BuvD3iv8.js → keyboard-shortcuts-settings-BuYmZS1K.js} +1 -1
  48. package/dist/gateway/static/root/assets/local-app-workbench-page-DV_17Zxc.js +2 -0
  49. package/dist/gateway/static/root/assets/{local-apps-page-DdJ8zwBp.js → local-apps-page-CExnZyGZ.js} +1 -1
  50. package/dist/gateway/static/root/assets/{locale-store-BBSCIl4I.js → locale-store-BkJ7g35d.js} +1 -1
  51. package/dist/gateway/static/root/assets/{logs-page-BaR2he0K.js → logs-page-CA5ArMoN.js} +2 -2
  52. package/dist/gateway/static/root/assets/management-settings-1UiJPrkX.js +1 -0
  53. package/dist/gateway/static/root/assets/{markdown-view-c5vtnYe8.js → markdown-view-B2IR4E2r.js} +1 -1
  54. package/dist/gateway/static/root/assets/{media-plugins-D_a6GDY6.js → media-plugins-nNFOk9HL.js} +1 -1
  55. package/dist/gateway/static/root/assets/{messages-DjuBdDCn.js → messages-BGc-OJWg.js} +2 -2
  56. package/dist/gateway/static/root/assets/note-detail-panel-B5OQ0fBY.js +5 -0
  57. package/dist/gateway/static/root/assets/notes-page-CVPPMVDl.js +8 -0
  58. package/dist/gateway/static/root/assets/{page-tabs-C7Le9bSI.js → page-tabs-CRombigq.js} +1 -1
  59. package/dist/gateway/static/root/assets/{preview-open-alternatives-B2pOxJBK.js → preview-open-alternatives-Bcuj43lk.js} +1 -1
  60. package/dist/gateway/static/root/assets/{proactive-card-BlQ1vCtV.js → proactive-card-Cb3Xeoyx.js} +1 -1
  61. package/dist/gateway/static/root/assets/{proactive-page-CghKTfYa.js → proactive-page-TbF9KVpm.js} +1 -1
  62. package/dist/gateway/static/root/assets/{product-open-page-DpjiHgwu.js → product-open-page-DsQcgZkI.js} +1 -1
  63. package/dist/gateway/static/root/assets/project-detail-page-B9OQ2l9x.js +1 -0
  64. package/dist/gateway/static/root/assets/projects-page-DCqSTNkY.js +1 -0
  65. package/dist/gateway/static/root/assets/{registry-api-CyQ4k1Ux.js → registry-api-CTq8EECj.js} +1 -1
  66. package/dist/gateway/static/root/assets/remote-access-hub-d9Fg70gX.js +1 -0
  67. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-Bl4ArLHf.js → runtime-tools-settings-panel-BudlvBu-.js} +2 -2
  68. package/dist/gateway/static/root/assets/{schema-form-KQFmfWd4.js → schema-form-Ch3MEX4h.js} +1 -1
  69. package/dist/gateway/static/root/assets/sessions-page-D8RiQ-ag.js +1 -0
  70. package/dist/gateway/static/root/assets/settings-advanced-gate-DVH_l6Hr.js +1 -0
  71. package/dist/gateway/static/root/assets/{settings-form-section-Dbnls9ty.js → settings-form-section-CIA1AA-A.js} +1 -1
  72. package/dist/gateway/static/root/assets/{settings-page-CDKirYRG.js → settings-page-B0Khoduh.js} +1 -1
  73. package/dist/gateway/static/root/assets/{setup-status-panel-SQmtWoOs.js → setup-status-panel-COJFl4N_.js} +1 -1
  74. package/dist/gateway/static/root/assets/share-preview-page-Cj9XWg-X.js +2 -0
  75. package/dist/gateway/static/root/assets/shares-settings-_OuE6A0G.js +1 -0
  76. package/dist/gateway/static/root/assets/skill-reload-api-BM4Ezjbx.js +1 -0
  77. package/dist/gateway/static/root/assets/{skills-page-Bd0_ED_p.js → skills-page-Dyh1kf9t.js} +1 -1
  78. package/dist/gateway/static/root/assets/{skills-page.utils-rA-XT-YG.js → skills-page.utils-DsQZkMti.js} +1 -1
  79. package/dist/gateway/static/root/assets/{system-settings-panel-CSkMrHLz.js → system-settings-panel-BSho_Tpk.js} +1 -1
  80. package/dist/gateway/static/root/assets/task-detail-page-B5HWM12k.js +1 -0
  81. package/dist/gateway/static/root/assets/{theme-store-DWpIEWen.js → theme-store-mV6Cm2vj.js} +1 -1
  82. package/dist/gateway/static/root/assets/tunnel-api-4DUHxSQG.js +1 -0
  83. package/dist/gateway/static/root/assets/{use-autosave-DkQAIAuB.js → use-autosave-BG8Yx7hj.js} +1 -1
  84. package/dist/gateway/static/root/assets/user-model-page-IjVyv5jj.js +2 -0
  85. package/dist/gateway/static/root/assets/{voice-api-key-field-BrqcH0P-.js → voice-api-key-field-DYXwDhHq.js} +1 -1
  86. package/dist/gateway/static/root/assets/{voice-config-api-BJ95Z3pT.js → voice-config-api-D3DdcH4H.js} +1 -1
  87. package/dist/gateway/static/root/assets/{work-discovery-overlay-vfTWKm13.js → work-discovery-overlay-CQqjzaRo.js} +1 -1
  88. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-CHdr_ng9.js → workflow-run-setup-panel-C5H-Fd-U.js} +2 -2
  89. package/dist/gateway/static/root/assets/workflows-page-D1hMyPsY.js +6 -0
  90. package/dist/gateway/static/root/index.html +9 -9
  91. package/dist/package.js +1 -1
  92. package/dist/packages/gateway-contract/src/device-pairing.js +41 -2
  93. package/dist/src/agent/service.js +20 -16
  94. package/dist/src/browser/native-messaging-host.d.ts +1 -1
  95. package/dist/src/browser/native-messaging-host.js +3 -2
  96. package/dist/src/gateway/hono/app.js +5 -2
  97. package/dist/src/gateway/hono/routes/agent-stream.js +1 -1
  98. package/dist/src/gateway/hono/routes/device-pairing-approval.d.ts +9 -2
  99. package/dist/src/gateway/hono/routes/device-pairing-approval.js +50 -10
  100. package/dist/src/gateway/hono/routes/devices.d.ts +2 -1
  101. package/dist/src/gateway/hono/routes/devices.js +33 -16
  102. package/dist/src/gateway/hono/routes/session-input-handler.d.ts +1 -1
  103. package/dist/src/gateway/hono/routes/session-input-handler.js +1 -1
  104. package/dist/src/gateway/hono/routes/side-chats.js +1 -1
  105. package/dist/src/gateway/hono/routes/tunnel.js +34 -2
  106. package/dist/src/gateway/rate-limit/buckets.js +2 -1
  107. package/dist/src/gateway/service/run-gateway-agent.js +1 -1
  108. package/dist/src/storage/sqlite/device-pairing-approval.js +3 -1
  109. package/dist/src/tui/tui.js +1 -1
  110. package/dist/src/tunnel/xopc-cloud-registration.d.ts +5 -0
  111. package/dist/src/tunnel/xopc-cloud-registration.js +17 -11
  112. package/package.json +3 -3
  113. package/dist/gateway/static/root/assets/agents-C0By2FIo.js +0 -1
  114. package/dist/gateway/static/root/assets/attachment-load-Dok_B1Gv.js +0 -2
  115. package/dist/gateway/static/root/assets/attachment-process-heavy-B2_kr5EA.js +0 -11
  116. package/dist/gateway/static/root/assets/automations-workspace--1TsZf3O.js +0 -6
  117. package/dist/gateway/static/root/assets/browser-settings-page-DOHgQer_.js +0 -8
  118. package/dist/gateway/static/root/assets/capabilities-settings-panel-Bpse_5K-.js +0 -8
  119. package/dist/gateway/static/root/assets/channels-settings-yHsY174W.js +0 -1
  120. package/dist/gateway/static/root/assets/connectors-page-B_wLKGnH.js +0 -2
  121. package/dist/gateway/static/root/assets/device-pairing-wizard-B5_w9qlm.js +0 -1
  122. package/dist/gateway/static/root/assets/excel-worksheet-utils-EyvwNT4X.js +0 -1
  123. package/dist/gateway/static/root/assets/gateway-settings-CH-6vkgN.js +0 -1
  124. package/dist/gateway/static/root/assets/home-page-BJebAKB-.js +0 -1
  125. package/dist/gateway/static/root/assets/local-app-workbench-page-CaC4-THD.js +0 -2
  126. package/dist/gateway/static/root/assets/management-settings-r-iYWZFY.js +0 -1
  127. package/dist/gateway/static/root/assets/note-detail-panel-BV6nbgac.js +0 -5
  128. package/dist/gateway/static/root/assets/notes-page-BmLKoWwi.js +0 -8
  129. package/dist/gateway/static/root/assets/project-detail-page-CV8Lh296.js +0 -1
  130. package/dist/gateway/static/root/assets/projects-page-COweOaJP.js +0 -1
  131. package/dist/gateway/static/root/assets/remote-access-hub-ChPPGVU5.js +0 -1
  132. package/dist/gateway/static/root/assets/sessions-page-DgmHibOK.js +0 -1
  133. package/dist/gateway/static/root/assets/settings-advanced-gate-DV7kMco2.js +0 -1
  134. package/dist/gateway/static/root/assets/share-preview-page-DwgLjS1m.js +0 -2
  135. package/dist/gateway/static/root/assets/shares-settings-Cs5bUrYw.js +0 -1
  136. package/dist/gateway/static/root/assets/skill-reload-api-DyKrjq7F.js +0 -1
  137. package/dist/gateway/static/root/assets/task-detail-page-CEQU-L_G.js +0 -1
  138. package/dist/gateway/static/root/assets/tunnel-api-COJcGnEi.js +0 -1
  139. package/dist/gateway/static/root/assets/user-model-page-BQAU55lf.js +0 -2
  140. package/dist/gateway/static/root/assets/workflows-page-hwRJD7vR.js +0 -6
@@ -5655,7 +5655,34 @@ class RealtimeClient {
5655
5655
  }
5656
5656
  }
5657
5657
 
5658
- _enum(["mobile", "browser"]);
5658
+ const DEVICE_PAIRING_VERSION = 3;
5659
+ const BROWSER_PAIRING_INVITATION_VERSION = 1;
5660
+ const BROWSER_PAIRING_INVITATION_PREFIX = `XOPC-BROWSER-INVITE-V${BROWSER_PAIRING_INVITATION_VERSION}:`;
5661
+ const devicePairingTargetKindSchema = _enum(["mobile", "browser"]);
5662
+ const devicePairingRouteSchema = strictObject({
5663
+ id: string().trim().min(1).max(80),
5664
+ kind: _enum(["xopc-secure-link", "tailscale", "custom-https", "local-browser"]),
5665
+ url: string().url()
5666
+ }).superRefine((route, context) => {
5667
+ const url = new URL(route.url);
5668
+ const isOrigin = !url.username && !url.password && url.pathname === "/" && !url.search && !url.hash;
5669
+ const valid = route.kind === "local-browser" ? url.protocol === "http:" && url.hostname === "127.0.0.1" && isOrigin : url.protocol === "https:" && isOrigin;
5670
+ if (!valid) context.addIssue({ code: "custom", path: ["url"], message: "Pairing route must be a secure origin" });
5671
+ });
5672
+ const devicePairingInvitationPayloadSchema = strictObject({
5673
+ version: literal(DEVICE_PAIRING_VERSION),
5674
+ targetKind: devicePairingTargetKindSchema,
5675
+ pairingToken: string().regex(/^xopc_pair_[0-9a-f-]{36}_[A-Za-z0-9_-]{43}$/i),
5676
+ gatewayId: string().uuid(),
5677
+ gatewayName: string().trim().min(1).max(120),
5678
+ gatewayPublicKey: string().regex(/^[A-Za-z0-9_-]{43}$/),
5679
+ routes: array(devicePairingRouteSchema).min(1).max(8),
5680
+ expiresAt: number().int().positive()
5681
+ }).superRefine((payload, context) => {
5682
+ if (payload.targetKind === "mobile" && payload.routes.some((route) => route.kind === "local-browser")) {
5683
+ context.addIssue({ code: "custom", path: ["routes"], message: "Mobile invitations require remote routes" });
5684
+ }
5685
+ });
5659
5686
  const devicePairingKeySchema = strictObject({
5660
5687
  kty: literal("EC"),
5661
5688
  crv: literal("P-256"),
@@ -5691,6 +5718,18 @@ devicePairingProofSchema.extend({
5691
5718
  idempotencyKey: string().uuid(),
5692
5719
  initialRefreshToken: initialDeviceRefreshTokenSchema
5693
5720
  });
5721
+ function isRetryableDevicePairingHttpStatus(status) {
5722
+ return status >= 500 || [404, 408, 425, 429].includes(status);
5723
+ }
5724
+ function readBrowserPairingInvitation(value) {
5725
+ const invitation = value.trim();
5726
+ if (!invitation.startsWith(BROWSER_PAIRING_INVITATION_PREFIX)) {
5727
+ throw new Error("Browser pairing invitation is invalid");
5728
+ }
5729
+ const encodedPayload = invitation.slice(BROWSER_PAIRING_INVITATION_PREFIX.length);
5730
+ if (!/^[A-Za-z0-9_-]+$/.test(encodedPayload)) throw new Error("Browser pairing invitation is invalid");
5731
+ return encodedPayload;
5732
+ }
5694
5733
  function canonical(value) {
5695
5734
  if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
5696
5735
  if (value !== null && typeof value === "object") {
@@ -5977,6 +6016,7 @@ async function clearBrowserOutboxes() {
5977
6016
  }
5978
6017
 
5979
6018
  const PROFILE_KEY = "xopc.browser.profile";
6019
+ const PAIRING_JOURNAL_KEY = "xopc.browser.pairing";
5980
6020
  const AUTO_CONNECT_KEY = "xopc.browser.auto-connect";
5981
6021
  const KEY_DATABASE = "xopc-browser-identity";
5982
6022
  const KEY_STORE = "identity";
@@ -6014,13 +6054,13 @@ async function discoverLocalGateway(options) {
6014
6054
  nonce
6015
6055
  }
6016
6056
  );
6017
- if (response.ok !== true || typeof response.gatewayUrl !== "string" || typeof response.pairingLink !== "string" || typeof response.expiresAt !== "number") {
6057
+ if (response.ok !== true || typeof response.gatewayUrl !== "string" || typeof response.invitation !== "string" || typeof response.expiresAt !== "number") {
6018
6058
  if (options?.force) throw new Error(response.error || "The local xopc enrollment host returned an invalid response");
6019
6059
  return void 0;
6020
6060
  }
6021
6061
  return {
6022
6062
  gatewayUrl: response.gatewayUrl,
6023
- pairingLink: response.pairingLink,
6063
+ invitation: response.invitation,
6024
6064
  expiresAt: response.expiresAt
6025
6065
  };
6026
6066
  } catch (cause) {
@@ -6049,25 +6089,12 @@ function randomNonce(bytes = 24) {
6049
6089
  function createRefreshToken() {
6050
6090
  return `xopc_rt_${crypto.randomUUID()}_${randomNonce(32)}`;
6051
6091
  }
6052
- function parseBrowserPairingLink(value) {
6053
- const url = new URL(value.trim());
6054
- if (url.protocol !== "https:" || url.hostname !== "link.xopc.ai" || url.pathname !== "/connect") {
6055
- throw new Error("Pairing link is not from xopc");
6056
- }
6057
- const encoded = new URLSearchParams(url.hash.slice(1)).get("p");
6058
- if (!encoded) throw new Error("Pairing link is invalid");
6059
- const payload = decodeJson(encoded);
6060
- if (payload.version !== 3 || payload.targetKind !== "browser" || !payload.gatewayId || !payload.pairingToken || !payload.gatewayPublicKey || !Array.isArray(payload.routes)) {
6061
- throw new Error("Pairing link version is not supported");
6062
- }
6063
- if (payload.expiresAt <= Date.now()) throw new Error("Pairing link has expired");
6064
- if (!payload.routes.length) throw new Error("Pairing link has no Gateway route");
6065
- for (const route of payload.routes) {
6066
- const routeUrl = new URL(route.url);
6067
- if (!route.id || !["http:", "https:"].includes(routeUrl.protocol)) {
6068
- throw new Error("Pairing link has an invalid Gateway route");
6069
- }
6070
- }
6092
+ function parseBrowserPairingInvitation(value, allowExpired = false) {
6093
+ const encoded = readBrowserPairingInvitation(value);
6094
+ const parsed = devicePairingInvitationPayloadSchema.safeParse(decodeJson(encoded));
6095
+ if (!parsed.success || parsed.data.targetKind !== "browser") throw new Error("Pairing invitation version is not supported");
6096
+ const payload = parsed.data;
6097
+ if (!allowExpired && payload.expiresAt <= Date.now()) throw new Error("Pairing invitation has expired");
6071
6098
  return payload;
6072
6099
  }
6073
6100
  function openIdentityDatabase() {
@@ -6154,6 +6181,11 @@ function requestGatewayOriginPermissions(origins) {
6154
6181
  if (!patterns.length) return Promise.resolve(true);
6155
6182
  return chrome.permissions.request({ origins: patterns });
6156
6183
  }
6184
+ async function hasGatewayOriginPermission(origin) {
6185
+ const url = new URL(origin);
6186
+ if (url.protocol === "http:" && ["127.0.0.1", "localhost"].includes(url.hostname)) return true;
6187
+ return chrome.permissions.contains({ origins: [`${url.origin}/*`] });
6188
+ }
6157
6189
  class GatewayHttpError extends Error {
6158
6190
  constructor(message, status) {
6159
6191
  super(message);
@@ -6222,7 +6254,12 @@ async function signedPairingRequest(pair, payload, origin, requestId, action, co
6222
6254
  return decoded;
6223
6255
  }
6224
6256
  function shouldTryNextRoute(cause) {
6225
- return !(cause instanceof GatewayHttpError) || cause.status >= 500 || [404, 408, 425, 429].includes(cause.status);
6257
+ return !(cause instanceof GatewayHttpError) || isRetryableDevicePairingHttpStatus(cause.status);
6258
+ }
6259
+ function isRouteFailure(cause) {
6260
+ if (cause instanceof GatewayHttpError) return isRetryableDevicePairingHttpStatus(cause.status);
6261
+ const message = cause instanceof Error ? cause.message : String(cause);
6262
+ return /network|fetch|respond|identity|route/i.test(message);
6226
6263
  }
6227
6264
  async function signedPairingRequestAcrossRoutes(pair, payload, origins, requestId, action, completion) {
6228
6265
  let lastRouteError;
@@ -6236,9 +6273,11 @@ async function signedPairingRequestAcrossRoutes(pair, payload, origins, requestI
6236
6273
  }
6237
6274
  throw lastRouteError instanceof Error ? lastRouteError : new Error("No Gateway route could be reached");
6238
6275
  }
6239
- async function refreshAccessToken(gatewayUrls, refreshToken, pair, gateway) {
6240
- const nextRefreshToken = createRefreshToken();
6241
- const requestId = crypto.randomUUID();
6276
+ async function refreshAccessToken(gatewayUrls, refreshToken, pair, gateway, operation = {
6277
+ requestId: crypto.randomUUID(),
6278
+ nextRefreshToken: createRefreshToken()
6279
+ }) {
6280
+ const { nextRefreshToken, requestId } = operation;
6242
6281
  const timestamp = Date.now();
6243
6282
  const nonce = randomNonce();
6244
6283
  const credentialId = refreshToken.slice("xopc_rt_".length).split("_")[0];
@@ -6285,71 +6324,149 @@ ${nextRefreshToken}`;
6285
6324
  function wait(milliseconds) {
6286
6325
  return new Promise((resolve) => setTimeout(resolve, milliseconds));
6287
6326
  }
6288
- async function pairGateway(link, onApproval) {
6289
- const payload = parseBrowserPairingLink(link);
6327
+ async function pairGateway(invitation, onApproval, requestedOrigin) {
6328
+ const payload = parseBrowserPairingInvitation(invitation, true);
6290
6329
  const origins = [...new Set(payload.routes.map((route) => new URL(route.url).origin))];
6291
- const permissionTask = requestGatewayOriginPermissions(origins);
6330
+ const selectedOrigin = requestedOrigin && origins.includes(requestedOrigin) ? requestedOrigin : origins[0];
6331
+ const permissionTask = requestGatewayOriginPermissions([selectedOrigin]);
6292
6332
  if (!await permissionTask) throw new Error("Gateway site permission was not granted");
6333
+ const permittedOrigins = (await Promise.all(origins.map(async (origin) => ({
6334
+ origin,
6335
+ permitted: await hasGatewayOriginPermission(origin)
6336
+ })))).filter((candidate) => candidate.permitted).map((candidate) => candidate.origin);
6293
6337
  const pair = await getOrCreateKeyPair();
6294
- const requestId = crypto.randomUUID();
6295
- const initialRefreshToken = createRefreshToken();
6296
- let { origin, result } = await signedPairingRequestAcrossRoutes(
6297
- pair,
6298
- payload,
6299
- origins,
6300
- requestId,
6301
- "request"
6302
- );
6303
- if (result.request.status === "pending") onApproval(result.request.confirmationCode);
6304
- while (result.request.status === "pending") {
6305
- await wait(1500);
6306
- const response = await signedPairingRequestAcrossRoutes(
6307
- pair,
6308
- payload,
6309
- [origin, ...origins.filter((candidate) => candidate !== origin)],
6310
- requestId,
6311
- "status"
6312
- );
6313
- ({ origin, result } = response);
6338
+ const stored = await chrome.storage.local.get(PAIRING_JOURNAL_KEY);
6339
+ let journal = stored[PAIRING_JOURNAL_KEY];
6340
+ if (journal && journal.payload.pairingToken !== payload.pairingToken) {
6341
+ throw new Error("PAIRING_ALREADY_PENDING");
6314
6342
  }
6315
- if (result.request.status !== "approved" && result.request.status !== "completed") {
6316
- throw new Error(`Pairing ${result.request.status}`);
6343
+ if (!journal) {
6344
+ if (payload.expiresAt <= Date.now()) throw new Error("Pairing invitation has expired");
6345
+ journal = {
6346
+ invitation: invitation.trim(),
6347
+ payload,
6348
+ requestId: crypto.randomUUID(),
6349
+ idempotencyKey: crypto.randomUUID(),
6350
+ initialRefreshToken: createRefreshToken()
6351
+ };
6352
+ await chrome.storage.local.set({ [PAIRING_JOURNAL_KEY]: journal });
6317
6353
  }
6318
- if (result.request.status !== "completed") {
6319
- const response = await signedPairingRequestAcrossRoutes(
6354
+ delete journal.nextOrigin;
6355
+ await chrome.storage.local.set({ [PAIRING_JOURNAL_KEY]: journal });
6356
+ const requestId = journal.requestId;
6357
+ const availableOrigins = journal.origin && permittedOrigins.includes(journal.origin) ? [journal.origin, ...permittedOrigins.filter((candidate) => candidate !== journal.origin)] : [selectedOrigin, ...permittedOrigins.filter((candidate) => candidate !== selectedOrigin)];
6358
+ try {
6359
+ let { origin, result } = await signedPairingRequestAcrossRoutes(
6320
6360
  pair,
6321
- payload,
6322
- [origin, ...origins.filter((candidate) => candidate !== origin)],
6361
+ journal.payload,
6362
+ availableOrigins,
6323
6363
  requestId,
6324
- "complete",
6325
- {
6326
- idempotencyKey: crypto.randomUUID(),
6327
- initialRefreshToken
6328
- }
6364
+ "request"
6365
+ );
6366
+ journal.origin = origin;
6367
+ await chrome.storage.local.set({ [PAIRING_JOURNAL_KEY]: journal });
6368
+ if (result.request.status === "pending") onApproval(result.request.confirmationCode);
6369
+ while (result.request.status === "pending") {
6370
+ await wait(1500);
6371
+ const response = await signedPairingRequestAcrossRoutes(
6372
+ pair,
6373
+ journal.payload,
6374
+ [origin, ...permittedOrigins.filter((candidate) => candidate !== origin)],
6375
+ requestId,
6376
+ "status"
6377
+ );
6378
+ ({ origin, result } = response);
6379
+ journal.origin = origin;
6380
+ await chrome.storage.local.set({ [PAIRING_JOURNAL_KEY]: journal });
6381
+ }
6382
+ if (result.request.status !== "approved" && result.request.status !== "completed") {
6383
+ await chrome.storage.local.remove(PAIRING_JOURNAL_KEY);
6384
+ throw new Error(`Pairing ${result.request.status}`);
6385
+ }
6386
+ if (result.request.status !== "completed") {
6387
+ const response = await signedPairingRequestAcrossRoutes(
6388
+ pair,
6389
+ journal.payload,
6390
+ [origin, ...permittedOrigins.filter((candidate) => candidate !== origin)],
6391
+ requestId,
6392
+ "complete",
6393
+ {
6394
+ idempotencyKey: journal.idempotencyKey,
6395
+ initialRefreshToken: journal.initialRefreshToken
6396
+ }
6397
+ );
6398
+ ({ origin, result } = response);
6399
+ }
6400
+ if (!result.request.deviceId) throw new Error("Gateway did not register the browser");
6401
+ journal.origin = origin;
6402
+ journal.completed = { deviceId: result.request.deviceId, gatewayName: result.gateway.name };
6403
+ journal.refresh ??= { requestId: crypto.randomUUID(), nextRefreshToken: createRefreshToken() };
6404
+ await chrome.storage.local.set({ [PAIRING_JOURNAL_KEY]: journal });
6405
+ const orderedOrigins = [origin, ...permittedOrigins.filter((candidate) => candidate !== origin)];
6406
+ const tokens = await refreshAccessToken(
6407
+ orderedOrigins,
6408
+ journal.initialRefreshToken,
6409
+ pair,
6410
+ journal.payload,
6411
+ journal.refresh
6412
+ );
6413
+ const profile = {
6414
+ gatewayId: journal.payload.gatewayId,
6415
+ gatewayName: journal.completed.gatewayName,
6416
+ gatewayUrl: tokens.gatewayUrl,
6417
+ gatewayUrls: [tokens.gatewayUrl, ...orderedOrigins.filter((candidate) => candidate !== tokens.gatewayUrl)],
6418
+ gatewayPublicKey: journal.payload.gatewayPublicKey,
6419
+ deviceId: journal.completed.deviceId,
6420
+ refreshToken: tokens.refreshToken,
6421
+ accessToken: tokens.accessToken,
6422
+ accessTokenExpiresAt: tokens.accessTokenExpiresAt
6423
+ };
6424
+ await chrome.storage.local.set({ [PROFILE_KEY]: profile });
6425
+ await chrome.storage.local.remove(PAIRING_JOURNAL_KEY);
6426
+ return profile;
6427
+ } catch (cause) {
6428
+ const nextOrigin = origins.find((origin) => !permittedOrigins.includes(origin));
6429
+ if (nextOrigin && isRouteFailure(cause)) {
6430
+ journal.nextOrigin = nextOrigin;
6431
+ await chrome.storage.local.set({ [PAIRING_JOURNAL_KEY]: journal });
6432
+ throw new Error(`PAIRING_ROUTE_PERMISSION_REQUIRED:${nextOrigin}`);
6433
+ }
6434
+ throw cause;
6435
+ }
6436
+ }
6437
+ async function readPendingBrowserPairing() {
6438
+ const stored = await chrome.storage.local.get(PAIRING_JOURNAL_KEY);
6439
+ const journal = stored[PAIRING_JOURNAL_KEY];
6440
+ return journal ? { invitation: journal.invitation, ...journal.nextOrigin ? { nextOrigin: journal.nextOrigin } : {} } : void 0;
6441
+ }
6442
+ async function cancelPendingBrowserPairing() {
6443
+ const stored = await chrome.storage.local.get(PAIRING_JOURNAL_KEY);
6444
+ const journal = stored[PAIRING_JOURNAL_KEY];
6445
+ if (!journal) return;
6446
+ const origins = [...new Set(journal.payload.routes.map((route) => new URL(route.url).origin))];
6447
+ const permittedOrigins = (await Promise.all(origins.map(async (origin) => ({
6448
+ origin,
6449
+ permitted: await hasGatewayOriginPermission(origin)
6450
+ })))).filter((candidate) => candidate.permitted).map((candidate) => candidate.origin);
6451
+ try {
6452
+ await signedPairingRequestAcrossRoutes(
6453
+ await getOrCreateKeyPair(),
6454
+ journal.payload,
6455
+ journal.origin && permittedOrigins.includes(journal.origin) ? [journal.origin, ...permittedOrigins.filter((candidate) => candidate !== journal.origin)] : permittedOrigins,
6456
+ journal.requestId,
6457
+ "cancel"
6329
6458
  );
6330
- ({ origin, result } = response);
6459
+ } catch (cause) {
6460
+ if (!(cause instanceof GatewayHttpError) || !["PAIRING_NOT_FOUND", "PAIRING_EXPIRED", "PAIRING_CANCELLED"].includes(cause.message)) throw cause;
6331
6461
  }
6332
- if (!result.request.deviceId) throw new Error("Gateway did not register the browser");
6333
- const orderedOrigins = [origin, ...origins.filter((candidate) => candidate !== origin)];
6334
- const tokens = await refreshAccessToken(orderedOrigins, initialRefreshToken, pair, payload);
6335
- const profile = {
6336
- gatewayId: payload.gatewayId,
6337
- gatewayName: result.gateway.name,
6338
- gatewayUrl: tokens.gatewayUrl,
6339
- gatewayUrls: [tokens.gatewayUrl, ...orderedOrigins.filter((candidate) => candidate !== tokens.gatewayUrl)],
6340
- gatewayPublicKey: payload.gatewayPublicKey,
6341
- deviceId: result.request.deviceId,
6342
- refreshToken: tokens.refreshToken,
6343
- accessToken: tokens.accessToken,
6344
- accessTokenExpiresAt: tokens.accessTokenExpiresAt
6345
- };
6346
- await chrome.storage.local.set({ [PROFILE_KEY]: profile });
6347
- return profile;
6462
+ await chrome.storage.local.remove(PAIRING_JOURNAL_KEY);
6348
6463
  }
6349
6464
  async function readProfile() {
6350
- const stored = await chrome.storage.local.get(PROFILE_KEY);
6465
+ const stored = await chrome.storage.local.get([PROFILE_KEY, PAIRING_JOURNAL_KEY]);
6351
6466
  const profile = stored[PROFILE_KEY];
6352
- return profile?.gatewayId && profile.gatewayUrl && profile.refreshToken ? profile : void 0;
6467
+ if (!profile?.gatewayId || !profile.gatewayUrl || !profile.refreshToken) return void 0;
6468
+ if (stored[PAIRING_JOURNAL_KEY]) await chrome.storage.local.remove(PAIRING_JOURNAL_KEY);
6469
+ return profile;
6353
6470
  }
6354
6471
  async function getAccessProfile() {
6355
6472
  const profile = await readProfile();
@@ -6428,7 +6545,7 @@ async function createBrowserEndpointHello(tools = []) {
6428
6545
  };
6429
6546
  }
6430
6547
  async function forgetProfile() {
6431
- await chrome.storage.local.remove(PROFILE_KEY);
6548
+ await chrome.storage.local.remove([PROFILE_KEY, PAIRING_JOURNAL_KEY]);
6432
6549
  }
6433
6550
  async function revokeAndForgetProfile() {
6434
6551
  try {
@@ -6451,4 +6568,4 @@ async function revokeAndForgetProfile() {
6451
6568
  }
6452
6569
  }
6453
6570
 
6454
- export { ENDPOINT_PROTOCOL_VERSION as E, PENDING_CONTEXT_KEY as P, RealtimeClient as R, TAB_BINDING_PREFIX as T, activeTabId as a, captureTabWithPermission as b, canonicalJson as c, createBrowserEndpointHello as d, currentTabDescriptor as e, deleteBrowserOutbox as f, discoverLocalGateway as g, endpointToolContentSchema as h, gatewayFetch as i, getAccessProfile as j, readProfile as k, registerBrowserEndpoint as l, revokeAndForgetProfile as m, runWithTabSiteAccess as n, pairGateway as p, readBrowserOutbox as r, setAutoConnectEnabled as s, writeBrowserOutbox as w };
6571
+ export { ENDPOINT_PROTOCOL_VERSION as E, PENDING_CONTEXT_KEY as P, RealtimeClient as R, TAB_BINDING_PREFIX as T, activeTabId as a, canonicalJson as b, cancelPendingBrowserPairing as c, captureTabWithPermission as d, createBrowserEndpointHello as e, currentTabDescriptor as f, deleteBrowserOutbox as g, discoverLocalGateway as h, endpointToolContentSchema as i, gatewayFetch as j, getAccessProfile as k, readPendingBrowserPairing as l, readProfile as m, registerBrowserEndpoint as n, revokeAndForgetProfile as o, pairGateway as p, runWithTabSiteAccess as q, readBrowserOutbox as r, setAutoConnectEnabled as s, writeBrowserOutbox as w };
@@ -1,4 +1,4 @@
1
- import { h as endpointToolContentSchema, E as ENDPOINT_PROTOCOL_VERSION, c as canonicalJson, T as TAB_BINDING_PREFIX, b as captureTabWithPermission, P as PENDING_CONTEXT_KEY, k as readProfile, l as registerBrowserEndpoint, j as getAccessProfile, R as RealtimeClient, d as createBrowserEndpointHello, i as gatewayFetch } from './assets/auth-DkwOUI6_.js';
1
+ import { i as endpointToolContentSchema, E as ENDPOINT_PROTOCOL_VERSION, b as canonicalJson, T as TAB_BINDING_PREFIX, d as captureTabWithPermission, P as PENDING_CONTEXT_KEY, m as readProfile, n as registerBrowserEndpoint, k as getAccessProfile, R as RealtimeClient, e as createBrowserEndpointHello, j as gatewayFetch } from './assets/auth-O8RiOF3l.js';
2
2
 
3
3
  const BROWSER_EXTENSION_PROTOCOL_VERSION = 4;
4
4
  const BROWSER_CONTROL_ENDPOINT_TOOL_NAME = "browser.control";
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>xopc</title>
7
7
  <script type="module" crossorigin src="./sidepanel.js"></script>
8
- <link rel="modulepreload" crossorigin href="./assets/auth-DkwOUI6_.js">
8
+ <link rel="modulepreload" crossorigin href="./assets/auth-O8RiOF3l.js">
9
9
  <link rel="stylesheet" crossorigin href="./assets/sidepanel-C86O7q5Y.css">
10
10
  </head>
11
11
  <body>
@@ -1,4 +1,4 @@
1
- import { R as RealtimeClient, i as gatewayFetch, a as activeTabId, r as readBrowserOutbox, w as writeBrowserOutbox, f as deleteBrowserOutbox, e as currentTabDescriptor, T as TAB_BINDING_PREFIX, j as getAccessProfile, n as runWithTabSiteAccess, P as PENDING_CONTEXT_KEY, b as captureTabWithPermission, p as pairGateway, s as setAutoConnectEnabled, k as readProfile, g as discoverLocalGateway, m as revokeAndForgetProfile } from './assets/auth-DkwOUI6_.js';
1
+ import { R as RealtimeClient, j as gatewayFetch, a as activeTabId, r as readBrowserOutbox, w as writeBrowserOutbox, g as deleteBrowserOutbox, f as currentTabDescriptor, T as TAB_BINDING_PREFIX, k as getAccessProfile, q as runWithTabSiteAccess, P as PENDING_CONTEXT_KEY, d as captureTabWithPermission, p as pairGateway, s as setAutoConnectEnabled, l as readPendingBrowserPairing, m as readProfile, h as discoverLocalGateway, c as cancelPendingBrowserPairing, o as revokeAndForgetProfile } from './assets/auth-O8RiOF3l.js';
2
2
 
3
3
  true &&(function polyfill() {
4
4
  const relList = document.createElement("link").relList;
@@ -27549,7 +27549,7 @@ function pairingErrorMessage(cause) {
27549
27549
  if (message === "Gateway site permission was not granted") {
27550
27550
  return "Chrome needs access to the Gateway address. Select Connect again and allow the site permission.";
27551
27551
  }
27552
- if (message === "PAIRING_EXPIRED" || message === "Pairing expired" || message === "Pairing link has expired") {
27552
+ if (message === "PAIRING_EXPIRED" || message === "Pairing expired" || message === "Pairing invitation has expired") {
27553
27553
  return "This invitation expired. Create and copy a new invitation from Device access.";
27554
27554
  }
27555
27555
  if (message === "PAIRING_DEVICE_MISMATCH") {
@@ -27558,32 +27558,37 @@ function pairingErrorMessage(cause) {
27558
27558
  if (message === "No Gateway route could be reached") {
27559
27559
  return "None of the Gateway addresses responded. Check that the Gateway is online and its secure connection is active.";
27560
27560
  }
27561
+ if (message.startsWith("PAIRING_ROUTE_PERMISSION_REQUIRED:")) {
27562
+ return "The current Gateway address did not respond. Select Continue to allow the next address listed in the invitation.";
27563
+ }
27561
27564
  return message;
27562
27565
  }
27563
27566
  function SidePanelApp() {
27564
27567
  const [profile, setProfile] = reactExports.useState();
27565
- const [pairingLink, setPairingLink] = reactExports.useState("");
27568
+ const [invitation, setInvitation] = reactExports.useState("");
27566
27569
  const [confirmationCode, setConfirmationCode] = reactExports.useState("");
27567
27570
  const [state, setState] = reactExports.useState("loading");
27568
27571
  const [error, setError] = reactExports.useState("");
27569
- const [localPairingLink, setLocalPairingLink] = reactExports.useState("");
27572
+ const [localInvitation, setLocalInvitation] = reactExports.useState("");
27573
+ const [nextOrigin, setNextOrigin] = reactExports.useState();
27570
27574
  const initializationStarted = reactExports.useRef(false);
27571
27575
  const pairingStarted = reactExports.useRef(false);
27572
- const connect = reactExports.useCallback(async (link) => {
27576
+ const connect = reactExports.useCallback(async (value, origin) => {
27573
27577
  if (pairingStarted.current) return;
27574
27578
  pairingStarted.current = true;
27575
27579
  setState("pairing");
27576
27580
  setError("");
27577
27581
  setConfirmationCode("");
27578
- setPairingLink(link);
27582
+ setInvitation(value);
27579
27583
  try {
27580
- const paired = await pairGateway(link, setConfirmationCode);
27584
+ const paired = await pairGateway(value, setConfirmationCode, origin);
27581
27585
  await setAutoConnectEnabled(true);
27582
27586
  await reconnectBrowserBridge();
27583
27587
  setProfile(paired);
27584
27588
  setState("online");
27585
27589
  } catch (cause) {
27586
27590
  setError(pairingErrorMessage(cause));
27591
+ setNextOrigin((await readPendingBrowserPairing())?.nextOrigin);
27587
27592
  setState("unpaired");
27588
27593
  } finally {
27589
27594
  pairingStarted.current = false;
@@ -27600,20 +27605,28 @@ function SidePanelApp() {
27600
27605
  setState("online");
27601
27606
  return;
27602
27607
  }
27608
+ const pending = await readPendingBrowserPairing();
27609
+ if (pending) {
27610
+ setInvitation(pending.invitation);
27611
+ setNextOrigin(pending.nextOrigin);
27612
+ if (!pending.nextOrigin) await connect(pending.invitation);
27613
+ else setState("unpaired");
27614
+ return;
27615
+ }
27603
27616
  const local = await discoverLocalGateway();
27604
27617
  if (!local) {
27605
27618
  setState("unpaired");
27606
27619
  return;
27607
27620
  }
27608
- setLocalPairingLink(local.pairingLink);
27609
- await connect(local.pairingLink);
27621
+ setLocalInvitation(local.invitation);
27622
+ await connect(local.invitation);
27610
27623
  })();
27611
27624
  }, [connect]);
27612
27625
  reactExports.useEffect(() => watchSidePanelTheme(profile?.gatewayUrl), [profile?.gatewayUrl]);
27613
27626
  async function disconnect() {
27614
27627
  await revokeAndForgetProfile();
27615
27628
  setProfile(void 0);
27616
- setLocalPairingLink("");
27629
+ setLocalInvitation("");
27617
27630
  setState("unpaired");
27618
27631
  }
27619
27632
  async function connectLocalGateway() {
@@ -27622,8 +27635,22 @@ function SidePanelApp() {
27622
27635
  try {
27623
27636
  const local = await discoverLocalGateway({ force: true });
27624
27637
  if (!local) throw new Error("No local xopc Gateway was found");
27625
- setLocalPairingLink(local.pairingLink);
27626
- await connect(local.pairingLink);
27638
+ setLocalInvitation(local.invitation);
27639
+ await connect(local.invitation);
27640
+ } catch (cause) {
27641
+ setError(cause instanceof Error ? cause.message : String(cause));
27642
+ setState("unpaired");
27643
+ }
27644
+ }
27645
+ async function cancelPairing() {
27646
+ setState("pairing");
27647
+ setError("");
27648
+ try {
27649
+ await cancelPendingBrowserPairing();
27650
+ setInvitation("");
27651
+ setNextOrigin(void 0);
27652
+ setConfirmationCode("");
27653
+ setState("unpaired");
27627
27654
  } catch (cause) {
27628
27655
  setError(cause instanceof Error ? cause.message : String(cause));
27629
27656
  setState("unpaired");
@@ -27644,9 +27671,9 @@ function SidePanelApp() {
27644
27671
  state === "loading" ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "muted", children: "Loading…" }) : null,
27645
27672
  state === "unpaired" || state === "pairing" ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "card", children: [
27646
27673
  /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { children: "Connect a Gateway" }),
27647
- localPairingLink ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
27674
+ localInvitation ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
27648
27675
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "muted", children: "A local xopc Gateway was found. xopc connects automatically on this computer." }),
27649
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "primary local-connect", onClick: () => void connect(localPairingLink), disabled: state === "pairing", children: state === "pairing" ? "Connecting…" : "Retry local connection" }),
27676
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "primary local-connect", onClick: () => void connect(localInvitation), disabled: state === "pairing", children: state === "pairing" ? "Connecting…" : "Retry local connection" }),
27650
27677
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pairing-divider", children: "or paste an invitation" })
27651
27678
  ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
27652
27679
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "muted", children: "Connect locally, or paste the one-time invitation copied from Device access." }),
@@ -27655,14 +27682,17 @@ function SidePanelApp() {
27655
27682
  ] }),
27656
27683
  /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "field", children: [
27657
27684
  "One-time invitation",
27658
- /* @__PURE__ */ jsxRuntimeExports.jsx("textarea", { rows: 4, value: pairingLink, onChange: (event) => setPairingLink(event.target.value), disabled: state === "pairing" })
27685
+ /* @__PURE__ */ jsxRuntimeExports.jsx("textarea", { rows: 4, value: invitation, onChange: (event) => setInvitation(event.target.value), disabled: state === "pairing" })
27659
27686
  ] }),
27660
27687
  confirmationCode ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
27661
27688
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "muted", children: "Confirm this code in the Gateway:" }),
27662
27689
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "approval-code", children: confirmationCode })
27663
27690
  ] }) : null,
27664
27691
  error ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "error-text", children: error }) : null,
27665
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "actions", children: /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "primary", onClick: () => void connect(pairingLink), disabled: !pairingLink.trim() || state === "pairing", children: state === "pairing" ? "Waiting for approval…" : "Connect" }) })
27692
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "actions", children: [
27693
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "primary", onClick: () => void connect(invitation, nextOrigin), disabled: !invitation.trim() || state === "pairing", children: state === "pairing" ? "Waiting for approval…" : nextOrigin ? `Allow ${new URL(nextOrigin).host} and continue` : "Connect" }),
27694
+ invitation ? /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => void cancelPairing(), disabled: state === "pairing", children: "Cancel pairing" }) : null
27695
+ ] })
27666
27696
  ] }) : null
27667
27697
  ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChatPanel, {}),
27668
27698
  state === "online" && profile ? /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "disconnect-link", onClick: disconnect, title: `Disconnect ${profile.gatewayName}`, children: "Disconnect" }) : null
@@ -2,7 +2,7 @@
2
2
  "manifest_version": 3,
3
3
  "minimum_chrome_version": "120",
4
4
  "name": "xopc",
5
- "version": "0.0.273",
5
+ "version": "0.0.274",
6
6
  "description": "Chat with xopc and explicitly share or control the current page.",
7
7
  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqnpr4vpLC/nmvy+wiOWk3SWlp2nEZkFijfk8ivGw7StNZgCK90nOMvwhdEtlkk3GOWDvdVnq8HxCsM3UNQ8FnGUyerpH1FJXqXuradxeHf8ZxFjiQU/mIWfnKbUuONWQORon3/pigyoXoK/VUXgg0Wbm/rbVwsS0bv+25R6m+mZgBspOLeKuoH7ivhL+48WV6POtMBsFPZweY26477EM7ukh+56Ff5ENevB8ymxBBn7qK5ZoeZd2a23U8ioX5TsHfe6fnrjitPWjIxD8G0NoKQxcaa/Mo+xs+T6UiLKxP0wjz5bm7jB5H+CXtkRJqzBWUB0h+RSLJckzZfA8BAJrRwIDAQAB",
8
8
  "permissions": [
@@ -2,7 +2,7 @@
2
2
  "id": "telegram",
3
3
  "name": "Telegram Channel",
4
4
  "description": "Bundled Telegram Bot channel (private workspace sources; ships inside @xopcai/xopc dist/)",
5
- "version": "0.0.273",
5
+ "version": "0.0.274",
6
6
  "kind": "channel",
7
7
  "main": "src/index.js",
8
8
  "channels": [
@@ -1 +1 @@
1
- import{p as e}from"./locale-store-BBSCIl4I.js";var t=`xopc.activity-center.v1`,n=80,r=720*60*60*1e3;function i(e){return e===`error`?`failed`:e===`warning`?`attention`:`done`}function a(){if(!(typeof window>`u`))return window.location.hash.replace(/^#/,``)||`${window.location.pathname}${window.location.search}`||void 0}function o(){try{let e=globalThis.localStorage?.getItem(t);if(!e)return[];let i=JSON.parse(e);if(!Array.isArray(i.items))return[];let a=Date.now()-r,o=i.items.filter(e=>e&&typeof e.id==`string`&&typeof e.title==`string`).filter(e=>Number.isFinite(e.updatedAt)&&e.updatedAt>=a).slice(0,n);return o.length!==i.items.length&&s(o),o}catch{return[]}}function s(e){try{globalThis.localStorage?.setItem(t,JSON.stringify({items:e}))}catch{}}function c(e){return`${e.toString(36)}-${Math.random().toString(36).slice(2,8)}`}var l=e(e=>({items:o(),add:t=>e(e=>{let r=t.title.trim();if(!r)return e;let o=Date.now(),l=t.tone??`info`,u=t.dedupeKey?e.items.findIndex(e=>e.dedupeKey===t.dedupeKey):-1,d=[{id:u>=0?e.items[u].id:c(o),tone:l,status:t.status??i(l),title:r,message:t.message?.trim()||void 0,source:t.source?.trim()||void 0,href:t.href??a(),dedupeKey:t.dedupeKey,createdAt:u>=0?e.items[u].createdAt:o,updatedAt:o,read:t.read??!1,occurrences:u>=0?e.items[u].occurrences+1:1},...u>=0?e.items.filter((e,t)=>t!==u):e.items].slice(0,n);return s(d),{items:d}}),markAllRead:()=>e(e=>{let t=e.items.map(e=>({...e,read:!0}));return s(t),{items:t}}),remove:t=>e(e=>{let n=e.items.filter(e=>e.id!==t);return s(n),{items:n}}),clearFinished:()=>e(e=>{let t=e.items.filter(e=>e.status!==`done`);return s(t),{items:t}}),clear:()=>{s([]),e({items:[]})}}));function u(e){l.getState().add(e)}export{u as t};
1
+ import{p as e}from"./locale-store-BkJ7g35d.js";var t=`xopc.activity-center.v1`,n=80,r=720*60*60*1e3;function i(e){return e===`error`?`failed`:e===`warning`?`attention`:`done`}function a(){if(!(typeof window>`u`))return window.location.hash.replace(/^#/,``)||`${window.location.pathname}${window.location.search}`||void 0}function o(){try{let e=globalThis.localStorage?.getItem(t);if(!e)return[];let i=JSON.parse(e);if(!Array.isArray(i.items))return[];let a=Date.now()-r,o=i.items.filter(e=>e&&typeof e.id==`string`&&typeof e.title==`string`).filter(e=>Number.isFinite(e.updatedAt)&&e.updatedAt>=a).slice(0,n);return o.length!==i.items.length&&s(o),o}catch{return[]}}function s(e){try{globalThis.localStorage?.setItem(t,JSON.stringify({items:e}))}catch{}}function c(e){return`${e.toString(36)}-${Math.random().toString(36).slice(2,8)}`}var l=e(e=>({items:o(),add:t=>e(e=>{let r=t.title.trim();if(!r)return e;let o=Date.now(),l=t.tone??`info`,u=t.dedupeKey?e.items.findIndex(e=>e.dedupeKey===t.dedupeKey):-1,d=[{id:u>=0?e.items[u].id:c(o),tone:l,status:t.status??i(l),title:r,message:t.message?.trim()||void 0,source:t.source?.trim()||void 0,href:t.href??a(),dedupeKey:t.dedupeKey,createdAt:u>=0?e.items[u].createdAt:o,updatedAt:o,read:t.read??!1,occurrences:u>=0?e.items[u].occurrences+1:1},...u>=0?e.items.filter((e,t)=>t!==u):e.items].slice(0,n);return s(d),{items:d}}),markAllRead:()=>e(e=>{let t=e.items.map(e=>({...e,read:!0}));return s(t),{items:t}}),remove:t=>e(e=>{let n=e.items.filter(e=>e.id!==t);return s(n),{items:n}}),clearFinished:()=>e(e=>{let t=e.items.filter(e=>e.status!==`done`);return s(t),{items:t}}),clear:()=>{s([]),e({items:[]})}}));function u(e){l.getState().add(e)}export{u as t};