@xopcai/xopc 0.0.264 → 0.0.266

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 (209) hide show
  1. package/dist/browser-ext/dist/assets/auth-CknJ00ae.js +5844 -0
  2. package/dist/browser-ext/dist/assets/sidepanel-C69P3v9u.css +113 -0
  3. package/dist/browser-ext/dist/background.js +200 -68
  4. package/dist/browser-ext/dist/sidepanel.html +14 -0
  5. package/dist/browser-ext/dist/sidepanel.js +18832 -0
  6. package/dist/browser-ext/manifest.json +20 -20
  7. package/dist/extensions/telegram/xopc.extension.json +1 -1
  8. package/dist/gateway/static/root/assets/{activity-store-BYBCpt7_.js → activity-store-Bhyy6ENz.js} +1 -1
  9. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-D20aBHPJ.js → agent-avatar-dicebear-Qrp5ALCF.js} +1 -1
  10. package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-BZvMqNrU.js → agent-defaults-settings-panel-DwinST48.js} +1 -1
  11. package/dist/gateway/static/root/assets/{agents-6foBELwt.js → agents-Dlr0YyNt.js} +1 -1
  12. package/dist/gateway/static/root/assets/{api-YbtjDbuG.js → api-B2y7ZjZk.js} +1 -1
  13. package/dist/gateway/static/root/assets/{app-management-settings-panel-4ps1RrDU.js → app-management-settings-panel-bLNvz93c.js} +1 -1
  14. package/dist/gateway/static/root/assets/{appearance-settings-C7nBHnkG.js → appearance-settings-D5qgIepF.js} +1 -1
  15. package/dist/gateway/static/root/assets/{apps-page-B_g-rL5b.js → apps-page-BQVlXhIb.js} +1 -1
  16. package/dist/gateway/static/root/assets/{archive-plugin-DfKK1iJO.js → archive-plugin-BEA7VAA4.js} +1 -1
  17. package/dist/gateway/static/root/assets/{attachment-load-BOU1pdnQ.js → attachment-load-B1RyF3uu.js} +1 -1
  18. package/dist/gateway/static/root/assets/{automations-page-BePzexAH.js → automations-page-BfH0FdKa.js} +1 -1
  19. package/dist/gateway/static/root/assets/{automations-workspace-7-m5pBEZ.js → automations-workspace-C37AlD3E.js} +1 -1
  20. package/dist/gateway/static/root/assets/{binary-plugins-Cly8XDyt.js → binary-plugins-EU884ZXi.js} +1 -1
  21. package/dist/gateway/static/root/assets/{block-editor-DzSu62js.js → block-editor-Bjr3naHb.js} +1 -1
  22. package/dist/gateway/static/root/assets/{browser-automation-inputs-8bHj7Fv2.js → browser-automation-inputs-s5I78Iko.js} +1 -1
  23. package/dist/gateway/static/root/assets/{browser-automations-page-CzL7Aj2k.js → browser-automations-page-CDrYrNGy.js} +1 -1
  24. package/dist/gateway/static/root/assets/{browser-settings-page-Dum3mTiz.js → browser-settings-page-DOmUkj2k.js} +1 -1
  25. package/dist/gateway/static/root/assets/{capabilities-settings-panel-Ci6qVS-O.js → capabilities-settings-panel-slt1o04n.js} +2 -2
  26. package/dist/gateway/static/root/assets/{channels-settings-BOGEZnSv.js → channels-settings-oHTTX6Jy.js} +1 -1
  27. package/dist/gateway/static/root/assets/{channels-status-swr-BR8oQ_eP.js → channels-status-swr-_OsWr_vS.js} +1 -1
  28. package/dist/gateway/static/root/assets/{chat-terminal-dock-BF9ZXa5Z.js → chat-terminal-dock-DEYQdv6n.js} +1 -1
  29. package/dist/gateway/static/root/assets/{connectors-page-DBvEvupa.js → connectors-page-olrbxHIT.js} +1 -1
  30. package/dist/gateway/static/root/assets/{cron-expression-BszT_gjK.js → cron-expression-DFrbDWpP.js} +1 -1
  31. package/dist/gateway/static/root/assets/{date-picker-D2MKBJ0G.js → date-picker-CYriYKpH.js} +1 -1
  32. package/dist/gateway/static/root/assets/{dependency-picker-DqigBx-G.js → dependency-picker-XY3qUErs.js} +1 -1
  33. package/dist/gateway/static/root/assets/{desktop-pet-CapspBGG.js → desktop-pet-Dsc__ynA.js} +1 -1
  34. package/dist/gateway/static/root/assets/{desktop-pet-settings-DMPk4CaK.js → desktop-pet-settings-EpuX3C08.js} +1 -1
  35. package/dist/gateway/static/root/assets/{extension-debug-page-CXLsDtrW.js → extension-debug-page-B_NeLV4K.js} +1 -1
  36. package/dist/gateway/static/root/assets/{extension-page-B2j87ROw.js → extension-page-D3JC6mLB.js} +1 -1
  37. package/dist/gateway/static/root/assets/{extension-settings-page-t1BApw-u.js → extension-settings-page-CXTQt1lp.js} +1 -1
  38. package/dist/gateway/static/root/assets/{files-BArWTfr7.js → files-Be5viEFY.js} +1 -1
  39. package/dist/gateway/static/root/assets/{gateway-config-swr-BFSesvAC.js → gateway-config-swr-OObCK8jM.js} +1 -1
  40. package/dist/gateway/static/root/assets/{gateway-settings-BFAecoGy.js → gateway-settings-yeYm_UPE.js} +1 -1
  41. package/dist/gateway/static/root/assets/{gateway-startup-retry-D1giYgI-.js → gateway-startup-retry-D_TxKN_A.js} +1 -1
  42. package/dist/gateway/static/root/assets/{heartbeat-settings-CyD9zDcU.js → heartbeat-settings-coh1LFsg.js} +1 -1
  43. package/dist/gateway/static/root/assets/{home-page-uItZMiBE.js → home-page-CAgoBZqp.js} +1 -1
  44. package/dist/gateway/static/root/assets/{image-generation-api-BeB-OPqO.js → image-generation-api-fPU9yGsw.js} +1 -1
  45. package/dist/gateway/static/root/assets/index-Cn__9Un2.js +74 -0
  46. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-Q_CM-ZCs.js → keyboard-shortcuts-settings-Cu1bI3Bg.js} +1 -1
  47. package/dist/gateway/static/root/assets/{local-app-workbench-page-DaiBWfOt.js → local-app-workbench-page-C9s2lFaI.js} +1 -1
  48. package/dist/gateway/static/root/assets/{local-apps-page-BALMNCSp.js → local-apps-page-CJ03Umjr.js} +1 -1
  49. package/dist/gateway/static/root/assets/{locale-store-RJHYFoq9.js → locale-store-DFQX9kTl.js} +1 -1
  50. package/dist/gateway/static/root/assets/{logs-page-C-TS7oFg.js → logs-page-CY_DQKJ2.js} +1 -1
  51. package/dist/gateway/static/root/assets/{management-settings-BajkJeYl.js → management-settings-YPEemeMo.js} +1 -1
  52. package/dist/gateway/static/root/assets/{markdown-view-BxoQJqHx.js → markdown-view-D-5jHRWg.js} +1 -1
  53. package/dist/gateway/static/root/assets/{media-plugins-C_8Hl6KG.js → media-plugins-BjT6jlrA.js} +1 -1
  54. package/dist/gateway/static/root/assets/{messages-Cf1h5WXB.js → messages-Dj8QuS6e.js} +2 -2
  55. package/dist/gateway/static/root/assets/{mobile-pairing-wizard-CsWIZrZW.js → mobile-pairing-wizard-A8eLtV6v.js} +1 -1
  56. package/dist/gateway/static/root/assets/{note-detail-panel-CCbPg_GP.js → note-detail-panel-DHT2QksJ.js} +3 -3
  57. package/dist/gateway/static/root/assets/{notes-page-BdxxcUKa.js → notes-page-BRFQiivM.js} +1 -1
  58. package/dist/gateway/static/root/assets/{page-tabs-BMYQef5D.js → page-tabs-DNUwulnl.js} +1 -1
  59. package/dist/gateway/static/root/assets/{preview-open-alternatives-CKZ-H5hJ.js → preview-open-alternatives-B33KsKkJ.js} +1 -1
  60. package/dist/gateway/static/root/assets/{product-delivery-CV4pDOtG.js → product-delivery-CCC4FJ3Y.js} +1 -1
  61. package/dist/gateway/static/root/assets/{product-open-page-zXJIptXs.js → product-open-page-CIGLPFm7.js} +1 -1
  62. package/dist/gateway/static/root/assets/{project-detail-page-DRhhxhZB.js → project-detail-page-CItEFVt9.js} +1 -1
  63. package/dist/gateway/static/root/assets/{projects-page-Wt7gF3Sx.js → projects-page-Bmy-c_lL.js} +1 -1
  64. package/dist/gateway/static/root/assets/{registry-api-BSfFaxVN.js → registry-api-CsZLatKo.js} +1 -1
  65. package/dist/gateway/static/root/assets/{remote-access-hub-B6de5Zrb.js → remote-access-hub-H6B1cA2V.js} +1 -1
  66. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-DcUB5h1f.js → runtime-tools-settings-panel-BsCovC39.js} +1 -1
  67. package/dist/gateway/static/root/assets/{schema-form-CquWhe7r.js → schema-form-NsPb_Vam.js} +1 -1
  68. package/dist/gateway/static/root/assets/{schemas-BHS7bo06.js → schemas-BaskVM5g.js} +2 -2
  69. package/dist/gateway/static/root/assets/{sessions-page-BNWO2EVF.js → sessions-page-CyYLSXk_.js} +1 -1
  70. package/dist/gateway/static/root/assets/settings-advanced-gate-3T4rPk4A.js +1 -0
  71. package/dist/gateway/static/root/assets/{settings-form-section-DwswAefc.js → settings-form-section-BzPIPe1-.js} +1 -1
  72. package/dist/gateway/static/root/assets/{settings-page-CoAVmjjs.js → settings-page-4idpMhsw.js} +1 -1
  73. package/dist/gateway/static/root/assets/{setup-status-panel-C9EIcWtH.js → setup-status-panel-D2ds_TTD.js} +1 -1
  74. package/dist/gateway/static/root/assets/{share-preview-page-DWwy_4zh.js → share-preview-page-Djo3VTaC.js} +1 -1
  75. package/dist/gateway/static/root/assets/{shares-settings-CIUvq27-.js → shares-settings-fhRzNfRp.js} +1 -1
  76. package/dist/gateway/static/root/assets/skill-reload-api-Ct8tUju_.js +1 -0
  77. package/dist/gateway/static/root/assets/{skills-page-CqsgVtVh.js → skills-page-riM7FytZ.js} +1 -1
  78. package/dist/gateway/static/root/assets/{skills-page.utils-B-N73248.js → skills-page.utils-B3G8LBb-.js} +1 -1
  79. package/dist/gateway/static/root/assets/{system-settings-panel-BndjXuBZ.js → system-settings-panel-CW91jjnB.js} +1 -1
  80. package/dist/gateway/static/root/assets/{task-detail-page-CG6Ha9t7.js → task-detail-page-BBXKvl0k.js} +1 -1
  81. package/dist/gateway/static/root/assets/{theme-store-C5ssn64t.js → theme-store-zRT6vaZc.js} +1 -1
  82. package/dist/gateway/static/root/assets/{tunnel-api-BxZdcp3h.js → tunnel-api-DILER0Yb.js} +1 -1
  83. package/dist/gateway/static/root/assets/{use-autosave-mj9CUuz1.js → use-autosave-GxnP8vLO.js} +1 -1
  84. package/dist/gateway/static/root/assets/{user-model-page-DKeHMXI4.js → user-model-page-I1H4XRfA.js} +1 -1
  85. package/dist/gateway/static/root/assets/{voice-api-key-field-CzxVMYlr.js → voice-api-key-field-CI3sXyl4.js} +1 -1
  86. package/dist/gateway/static/root/assets/{voice-config-api-COA0V1cK.js → voice-config-api-DpJEu2RQ.js} +1 -1
  87. package/dist/gateway/static/root/assets/{work-discovery-overlay-DMiPK8dp.js → work-discovery-overlay-DZC9Exn3.js} +1 -1
  88. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-C82OXCJK.js → workflow-run-setup-panel-CaBaQER2.js} +1 -1
  89. package/dist/gateway/static/root/assets/{workflows-page-HLCUV3rQ.js → workflows-page-C-_Dxihw.js} +1 -1
  90. package/dist/gateway/static/root/index.html +12 -12
  91. package/dist/package.js +1 -1
  92. package/dist/packages/browser-control-contract/src/index.js +14 -0
  93. package/dist/packages/endpoint-tools-protocol/src/index.js +2 -1
  94. package/dist/packages/gateway-contract/src/browser-page-context.js +43 -0
  95. package/dist/packages/gateway-contract/src/browser-tab-binding.js +29 -0
  96. package/dist/packages/gateway-contract/src/device-pairing.js +18 -2
  97. package/dist/packages/realtime-protocol/src/index.js +1 -0
  98. package/dist/packages/realtime-protocol/src/voice-audio.js +70 -9
  99. package/dist/packages/realtime-protocol/src/voice.js +65 -17
  100. package/dist/src/agent/agent-manager.js +4 -4
  101. package/dist/src/agent/background-review/run-background-review.js +1 -1
  102. package/dist/src/agent/embedded/model-runtime.js +1 -1
  103. package/dist/src/agent/embedded/run-for-session.js +1 -1
  104. package/dist/src/agent/image/tool-model-config.js +1 -1
  105. package/dist/src/agent/mcp/oauth/mcp-oauth-store.js +1 -1
  106. package/dist/src/agent/service.js +2 -2
  107. package/dist/src/agent/session/session-inspector.js +1 -1
  108. package/dist/src/agent/skills/hub-lock.js +1 -1
  109. package/dist/src/agent/source-context/browser-page.d.ts +3 -0
  110. package/dist/src/agent/source-context/browser-page.js +39 -0
  111. package/dist/src/agent/source-context/injector.js +9 -2
  112. package/dist/src/agent/source-context/types.d.ts +7 -1
  113. package/dist/src/agent/source-context/types.js +4 -1
  114. package/dist/src/agent/tasks/task-judge-service.js +3 -3
  115. package/dist/src/agent/tools/factory.js +9 -1
  116. package/dist/src/ai-assist/text-assist.js +1 -1
  117. package/dist/src/auth/credentials.js +2 -2
  118. package/dist/src/auth/oauth/xopc-cloud.js +1 -1
  119. package/dist/src/auth/profiles/store.js +1 -1
  120. package/dist/src/auth/sync-provider-auth.js +2 -2
  121. package/dist/src/browser/automations/schema.d.ts +1 -1
  122. package/dist/src/browser/automations/service.d.ts +1 -1
  123. package/dist/src/browser/native-messaging-host.d.ts +15 -0
  124. package/dist/src/browser/native-messaging-host.js +105 -0
  125. package/dist/src/browser/providers/browser-ext-install.d.ts +17 -1
  126. package/dist/src/browser/providers/browser-ext-install.js +82 -16
  127. package/dist/src/browser/providers/extension.d.ts +4 -0
  128. package/dist/src/browser/providers/extension.js +104 -38
  129. package/dist/src/browser/runtime/browser-runtime.d.ts +2 -1
  130. package/dist/src/browser/runtime/browser-runtime.js +6 -2
  131. package/dist/src/cli/commands/browser.js +13 -3
  132. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  133. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  134. package/dist/src/cli/commands/models.js +1 -1
  135. package/dist/src/config/index.js +4 -4
  136. package/dist/src/config/loader.js +2 -2
  137. package/dist/src/config/models-json.js +1 -1
  138. package/dist/src/endpoint-tools/runtime.js +2 -0
  139. package/dist/src/extensions/lockfile.js +1 -1
  140. package/dist/src/gateway/agents-admin.js +1 -1
  141. package/dist/src/gateway/chat-stream/mapper.js +3 -2
  142. package/dist/src/gateway/file-path-classifier.js +1 -1
  143. package/dist/src/gateway/hono/app.js +19 -3
  144. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  145. package/dist/src/gateway/hono/middleware/auth.d.ts +4 -1
  146. package/dist/src/gateway/hono/middleware/auth.js +12 -1
  147. package/dist/src/gateway/hono/routes/browser-install.js +1 -1
  148. package/dist/src/gateway/hono/routes/browser.js +106 -7
  149. package/dist/src/gateway/hono/routes/devices.js +36 -3
  150. package/dist/src/gateway/hono/routes/endpoint-tools.js +15 -0
  151. package/dist/src/gateway/hono/routes/models.js +1 -1
  152. package/dist/src/gateway/hono/routes/session-input-handler.d.ts +20 -2
  153. package/dist/src/gateway/hono/routes/session-input-handler.js +18 -0
  154. package/dist/src/gateway/hono/routes/sessions.js +12 -6
  155. package/dist/src/gateway/hono/routes/voice.js +5 -4
  156. package/dist/src/gateway/security/gateway-scopes.d.ts +1 -0
  157. package/dist/src/gateway/security/gateway-scopes.js +10 -1
  158. package/dist/src/gateway/security/origin-check.d.ts +1 -0
  159. package/dist/src/gateway/security/origin-check.js +5 -1
  160. package/dist/src/gateway/service/agent-runner.js +2 -1
  161. package/dist/src/gateway/service/config-coordinator.js +1 -1
  162. package/dist/src/gateway/service/session-input-coordinator.d.ts +1 -0
  163. package/dist/src/gateway/service/session-input-coordinator.js +5 -4
  164. package/dist/src/gateway/service.js +22 -21
  165. package/dist/src/providers/auth-runtime/resolve-auth.js +1 -1
  166. package/dist/src/providers/index.js +2 -2
  167. package/dist/src/providers/model-registry.js +1 -1
  168. package/dist/src/providers/register-runtime-providers.js +1 -1
  169. package/dist/src/realtime/runtime.js +2 -1
  170. package/dist/src/session/client-history.d.ts +4 -1
  171. package/dist/src/session/client-history.js +6 -3
  172. package/dist/src/storage/sqlite/browser-tab-binding-repository.d.ts +7 -0
  173. package/dist/src/storage/sqlite/browser-tab-binding-repository.js +60 -0
  174. package/dist/src/storage/sqlite/device-access-repository.d.ts +3 -1
  175. package/dist/src/storage/sqlite/device-access-repository.js +4 -3
  176. package/dist/src/storage/sqlite/device-pairing-approval.js +6 -4
  177. package/dist/src/storage/sqlite/device-pairing-repository.d.ts +1 -1
  178. package/dist/src/storage/sqlite/index.d.ts +1 -0
  179. package/dist/src/storage/sqlite/index.js +2 -1
  180. package/dist/src/storage/sqlite/migrations/155_browser_extension.sql +120 -0
  181. package/dist/src/storage/sqlite/migrations/156_browser_tab_bindings.sql +14 -0
  182. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  183. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  184. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  185. package/dist/src/storage/sqlite/schema.js +1 -1
  186. package/dist/src/tui/tui-startup-resources.js +1 -1
  187. package/dist/src/tui/tui.js +1 -1
  188. package/dist/src/voice/realtime/agentBroker.d.ts +54 -0
  189. package/dist/src/voice/realtime/agentBroker.js +116 -0
  190. package/dist/src/voice/realtime/agentEngine.js +62 -15
  191. package/dist/src/voice/realtime/engine.d.ts +2 -1
  192. package/dist/src/voice/realtime/omniEngine.js +36 -7
  193. package/dist/src/voice/realtime/pcmFrameBuffer.d.ts +6 -0
  194. package/dist/src/voice/realtime/pcmFrameBuffer.js +29 -0
  195. package/dist/src/voice/realtime/runtime.js +97 -21
  196. package/dist/src/voice/realtime/runtime.types.d.ts +5 -21
  197. package/dist/src/voice/realtime/turnPolicy.d.ts +36 -0
  198. package/dist/src/voice/realtime/turnPolicy.js +134 -0
  199. package/dist/src/work-discovery/service.js +1 -1
  200. package/dist/src/workflows/service/workflow-run-service.js +1 -1
  201. package/package.json +2 -2
  202. package/dist/browser-ext/dist/content.js +0 -191
  203. package/dist/browser-ext/dist/popup.js +0 -32
  204. package/dist/browser-ext/popup.html +0 -37
  205. package/dist/gateway/static/root/assets/index-DQtjs2x3.js +0 -74
  206. package/dist/gateway/static/root/assets/settings-advanced-gate-DK1yTYE4.js +0 -1
  207. package/dist/gateway/static/root/assets/skill-reload-api-DfE3zFVw.js +0 -1
  208. package/dist/src/voice/realtime/conversationTurn.d.ts +0 -20
  209. package/dist/src/voice/realtime/conversationTurn.js +0 -83
@@ -1,10 +1,10 @@
1
1
  import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
2
2
  import { createLogger } from "../utils/logger/index.js";
3
3
  import { init_logger } from "../utils/logger.js";
4
- import { init_paths, resolveAgentAuthProfilesPath, resolveAuthProfilesPath, resolveCredentialsDir, resolveOAuthPath } from "../config/paths.js";
5
4
  import { init_write_file_atomic, writeTextAtomic } from "../infra/write-file-atomic.js";
6
- import { getOAuthProviderDefinition, init_registry } from "./oauth/registry.js";
5
+ import { init_paths, resolveAgentAuthProfilesPath, resolveAuthProfilesPath, resolveCredentialsDir, resolveOAuthPath } from "../config/paths.js";
7
6
  import { getApiKeyFromEnv, init_env_keys } from "../providers/env-keys.js";
7
+ import { getOAuthProviderDefinition, init_registry } from "./oauth/registry.js";
8
8
  import { init_oauth_provider_lock, withOAuthProviderLock } from "./oauth-provider-lock.js";
9
9
  import { dirname, join } from "path";
10
10
  import { mkdir, readFile, readdir, rm } from "fs/promises";
@@ -1,6 +1,6 @@
1
1
  import { __esmMin } from "../../../_virtual/_rolldown/runtime.js";
2
- import { init_resolution, resolveConnectedPlatformEndpoint } from "../../platform/resolution.js";
3
2
  import { generatePKCE, init_pkce } from "./pkce.js";
3
+ import { init_resolution, resolveConnectedPlatformEndpoint } from "../../platform/resolution.js";
4
4
  import { hostname } from "node:os";
5
5
  import { randomBytes } from "node:crypto";
6
6
  import http from "node:http";
@@ -1,5 +1,5 @@
1
- import { init_paths, resolveCredentialsDir } from "../../config/paths.js";
2
1
  import { init_write_file_atomic, writeTextAtomicSync } from "../../infra/write-file-atomic.js";
2
+ import { init_paths, resolveCredentialsDir } from "../../config/paths.js";
3
3
  import path from "node:path";
4
4
  import { chmodSync, existsSync, mkdirSync, readFileSync } from "node:fs";
5
5
  //#region src/auth/profiles/store.ts
@@ -1,8 +1,8 @@
1
1
  import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
2
- import { getDefaultAgentId, init_resolve_route } from "../routing/resolve-route.js";
3
2
  import { init_paths, resolveAgentAuthProfilesPath, resolveAuthProfilesPath, resolveOAuthPath } from "../config/paths.js";
4
- import { init_loader, loadConfig } from "../config/loader.js";
5
3
  import { init_registry, isOAuthOnlyProvider } from "./oauth/registry.js";
4
+ import { init_loader, loadConfig } from "../config/loader.js";
5
+ import { getDefaultAgentId, init_resolve_route } from "../routing/resolve-route.js";
6
6
  import { existsSync, readFileSync } from "node:fs";
7
7
  //#region src/auth/sync-provider-auth.ts
8
8
  /**
@@ -128,8 +128,8 @@ export declare const BrowserAutomationDefinitionSchema: z.ZodObject<{
128
128
  }, z.core.$strict>, z.ZodObject<{
129
129
  action: z.ZodLiteral<"wait">;
130
130
  condition: z.ZodEnum<{
131
- hidden: "hidden";
132
131
  text: "text";
132
+ hidden: "hidden";
133
133
  visible: "visible";
134
134
  page_idle: "page_idle";
135
135
  }>;
@@ -120,7 +120,7 @@ export declare class BrowserAutomationService {
120
120
  };
121
121
  } | {
122
122
  action: "wait";
123
- condition: "hidden" | "text" | "visible" | "page_idle";
123
+ condition: "text" | "hidden" | "visible" | "page_idle";
124
124
  value?: string;
125
125
  target?: {
126
126
  role: string;
@@ -0,0 +1,15 @@
1
+ export declare const BROWSER_NATIVE_HOST_NAME = "ai.xopc.browser";
2
+ export declare const BROWSER_EXTENSION_ID = "gopbfhaojnnhiheiikblejnpgmfmkmgd";
3
+ export type BrowserNativeBootstrap = {
4
+ type: 'bootstrap';
5
+ };
6
+ export type BrowserNativeBootstrapResult = {
7
+ ok: true;
8
+ gatewayUrl: string;
9
+ pairingLink: string;
10
+ expiresAt: number;
11
+ };
12
+ export declare function encodeNativeMessage(value: unknown): Buffer;
13
+ export declare function decodeNativeMessage(buffer: Buffer): unknown;
14
+ export declare function createBrowserNativeBootstrap(configPath: string): BrowserNativeBootstrapResult;
15
+ export declare function runBrowserNativeMessagingHost(configPath: string): Promise<void>;
@@ -0,0 +1,105 @@
1
+ import { closeXopcDatabase, openXopcDatabase } from "../storage/sqlite/connection.js";
2
+ import { getGatewayIdentityPublicKeyRaw, getOrCreateGatewayIdentity } from "../storage/sqlite/gateway-identity-repository.js";
3
+ import { createDevicePairingSetup } from "../storage/sqlite/device-pairing-repository.js";
4
+ import { init_loader, loadConfig } from "../config/loader.js";
5
+ import os from "node:os";
6
+ //#region src/browser/native-messaging-host.ts
7
+ init_loader();
8
+ const BROWSER_NATIVE_HOST_NAME = "ai.xopc.browser";
9
+ const BROWSER_EXTENSION_ID = "gopbfhaojnnhiheiikblejnpgmfmkmgd";
10
+ function gatewayLoopbackUrl(port) {
11
+ return `http://127.0.0.1:${port}`;
12
+ }
13
+ function encodeNativeMessage(value) {
14
+ const payload = Buffer.from(JSON.stringify(value), "utf8");
15
+ const header = Buffer.allocUnsafe(4);
16
+ header.writeUInt32LE(payload.length, 0);
17
+ return Buffer.concat([header, payload]);
18
+ }
19
+ function decodeNativeMessage(buffer) {
20
+ if (buffer.length < 4) throw new Error("Native message header is incomplete");
21
+ const length = buffer.readUInt32LE(0);
22
+ if (length === 0 || length > 1024 * 1024 || buffer.length !== length + 4) throw new Error("Native message length is invalid");
23
+ return JSON.parse(buffer.subarray(4).toString("utf8"));
24
+ }
25
+ function createBrowserNativeBootstrap(configPath) {
26
+ const gatewayUrl = gatewayLoopbackUrl(loadConfig(configPath).gateway.port ?? 18790);
27
+ openXopcDatabase();
28
+ try {
29
+ const route = {
30
+ id: "local-browser",
31
+ kind: "local-browser",
32
+ url: gatewayUrl
33
+ };
34
+ const setup = createDevicePairingSetup([route], Date.now(), 3);
35
+ const identity = getOrCreateGatewayIdentity();
36
+ return {
37
+ ok: true,
38
+ gatewayUrl,
39
+ pairingLink: `https://link.xopc.ai/connect#p=${Buffer.from(JSON.stringify({
40
+ version: 3,
41
+ pairingToken: setup.token,
42
+ gatewayId: identity.id,
43
+ gatewayName: os.hostname(),
44
+ gatewayPublicKey: getGatewayIdentityPublicKeyRaw(identity),
45
+ routes: [route],
46
+ expiresAt: setup.expiresAt
47
+ })).toString("base64url")}`,
48
+ expiresAt: setup.expiresAt
49
+ };
50
+ } finally {
51
+ closeXopcDatabase();
52
+ }
53
+ }
54
+ async function readOneNativeMessage(input) {
55
+ return new Promise((resolve, reject) => {
56
+ let buffered = Buffer.alloc(0);
57
+ let expectedLength;
58
+ const cleanup = () => {
59
+ input.off("data", onData);
60
+ input.off("end", onEnd);
61
+ input.off("error", onError);
62
+ };
63
+ const onError = (error) => {
64
+ cleanup();
65
+ reject(error);
66
+ };
67
+ const onEnd = () => {
68
+ cleanup();
69
+ reject(/* @__PURE__ */ new Error("Native messaging input ended before one complete request"));
70
+ };
71
+ const onData = (chunk) => {
72
+ buffered = Buffer.concat([buffered, Buffer.from(chunk)]);
73
+ if (expectedLength === void 0 && buffered.length >= 4) {
74
+ expectedLength = buffered.readUInt32LE(0);
75
+ if (expectedLength === 0 || expectedLength > 1024 * 1024) {
76
+ cleanup();
77
+ reject(/* @__PURE__ */ new Error("Native message length is invalid"));
78
+ return;
79
+ }
80
+ }
81
+ if (expectedLength !== void 0 && buffered.length >= expectedLength + 4) {
82
+ cleanup();
83
+ input.pause();
84
+ resolve(buffered.subarray(0, expectedLength + 4));
85
+ }
86
+ };
87
+ input.on("data", onData);
88
+ input.once("end", onEnd);
89
+ input.once("error", onError);
90
+ });
91
+ }
92
+ async function runBrowserNativeMessagingHost(configPath) {
93
+ try {
94
+ const request = decodeNativeMessage(await readOneNativeMessage(process.stdin));
95
+ if (!request || typeof request !== "object" || request.type !== "bootstrap") throw new Error("Unsupported native messaging request");
96
+ process.stdout.write(encodeNativeMessage(createBrowserNativeBootstrap(configPath)));
97
+ } catch (error) {
98
+ process.stdout.write(encodeNativeMessage({
99
+ ok: false,
100
+ error: error instanceof Error ? error.message : String(error)
101
+ }));
102
+ }
103
+ }
104
+ //#endregion
105
+ export { BROWSER_EXTENSION_ID, BROWSER_NATIVE_HOST_NAME, createBrowserNativeBootstrap, decodeNativeMessage, encodeNativeMessage, runBrowserNativeMessagingHost };
@@ -3,7 +3,7 @@
3
3
  * Fixed path — gateway upgrades overwrite in place so Chrome sideload paths stay stable.
4
4
  */
5
5
  import type { Config } from '../../config/schema.js';
6
- export declare const BROWSER_EXT_REQUIRED_FILES: readonly ["manifest.json", "popup.html", "dist/background.js", "dist/content.js", "dist/popup.js"];
6
+ export declare const BROWSER_EXT_REQUIRED_FILES: readonly ["manifest.json", "dist/background.js", "dist/sidepanel.html"];
7
7
  export type BrowserExtBundledFrom = 'npm-dist' | 'git-dev' | 'electron-asar' | 'env-override';
8
8
  export interface BrowserExtInstallMeta {
9
9
  xopcVersion: string;
@@ -32,6 +32,11 @@ export interface EnsureBrowserExtResult {
32
32
  xopcVersion: string;
33
33
  copied: boolean;
34
34
  }
35
+ export interface BrowserNativeHostInstallResult {
36
+ installed: boolean;
37
+ manifestPaths: string[];
38
+ reason?: string;
39
+ }
35
40
  /** Validate a directory contains a loadable extension tree. */
36
41
  export declare function validateBrowserExtLayout(dir: string): boolean;
37
42
  export declare function browserExtContentHash(dir: string): string;
@@ -59,6 +64,17 @@ export declare function ensureBrowserExtensionArtifacts(opts?: {
59
64
  force?: boolean;
60
65
  cacheDir?: string;
61
66
  }): Promise<EnsureBrowserExtResult>;
67
+ export declare function browserNativeManifestDirectories(platform?: NodeJS.Platform, home?: string): string[];
68
+ /** Install the local-only bootstrap host used to discover and pair with a Gateway. */
69
+ export declare function installBrowserNativeMessagingHost(opts?: {
70
+ cacheDir?: string;
71
+ cliPath?: string;
72
+ nodePath?: string;
73
+ platform?: NodeJS.Platform;
74
+ home?: string;
75
+ configPath?: string;
76
+ stateDir?: string;
77
+ }): Promise<BrowserNativeHostInstallResult>;
62
78
  /** Gateway startup hook: ensure artifacts only while the extension driver is active. */
63
79
  export declare function ensureBrowserExtensionOnStartup(config: Config | undefined): Promise<void>;
64
80
  export declare function readInstalledExtensionDir(cacheDir?: string): Promise<string | null>;
@@ -1,12 +1,14 @@
1
1
  import { PACKAGE_VERSION, init_package_version } from "../../package-version.js";
2
+ import { resolveStateDir } from "../../config/paths-state.js";
2
3
  import { createLogger } from "../../utils/logger/index.js";
3
4
  import { init_logger } from "../../utils/logger.js";
4
- import { init_paths, resolveBinDir } from "../../config/paths.js";
5
5
  import { init_write_file_atomic, writeTextAtomic } from "../../infra/write-file-atomic.js";
6
- import { resolvePackageRoot } from "../../infra/update-check.js";
6
+ import { init_paths, resolveBinDir, resolveConfigPath } from "../../config/paths.js";
7
7
  import { assertCacheDir } from "../cache-dir-policy.js";
8
+ import { BROWSER_EXTENSION_ID, BROWSER_NATIVE_HOST_NAME } from "../native-messaging-host.js";
9
+ import { resolvePackageRoot } from "../../infra/update-check.js";
8
10
  import { dirname, join } from "node:path";
9
- import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
11
+ import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
10
12
  import { fileURLToPath } from "node:url";
11
13
  import { spawn } from "node:child_process";
12
14
  import { createHash } from "node:crypto";
@@ -25,16 +27,13 @@ const META_FILENAME = ".meta.json";
25
27
  const STAGING_MAX_AGE_MS = 3600 * 1e3;
26
28
  const INSTALLED_ARTIFACT_NAMES = [
27
29
  "manifest.json",
28
- "popup.html",
29
30
  "dist",
30
31
  "icons"
31
32
  ];
32
33
  const BROWSER_EXT_REQUIRED_FILES = [
33
34
  "manifest.json",
34
- "popup.html",
35
35
  "dist/background.js",
36
- "dist/content.js",
37
- "dist/popup.js"
36
+ "dist/sidepanel.html"
38
37
  ];
39
38
  function moduleDir() {
40
39
  return dirname(fileURLToPath(import.meta.url));
@@ -45,7 +44,16 @@ function validateBrowserExtLayout(dir) {
45
44
  }
46
45
  function browserExtContentHash(dir) {
47
46
  const hash = createHash("sha256");
48
- for (const relativePath of BROWSER_EXT_REQUIRED_FILES) {
47
+ const collectFiles = (relativeDir) => readdirSync(join(dir, relativeDir), { withFileTypes: true }).flatMap((entry) => {
48
+ const relativePath = join(relativeDir, entry.name);
49
+ return entry.isDirectory() ? collectFiles(relativePath) : [relativePath];
50
+ });
51
+ const relativePaths = [
52
+ "manifest.json",
53
+ ...collectFiles("dist"),
54
+ ...collectFiles("icons")
55
+ ].sort();
56
+ for (const relativePath of relativePaths) {
49
57
  hash.update(relativePath);
50
58
  hash.update(readFileSync(join(dir, relativePath)));
51
59
  }
@@ -177,21 +185,25 @@ function copyBundledFile(src, dest) {
177
185
  mkdirSync(dirname(dest), { recursive: true });
178
186
  writeFileSync(dest, readFileSync(src));
179
187
  }
180
- const BROWSER_EXT_DIST_FILES = [
181
- "background.js",
182
- "content.js",
183
- "popup.js"
184
- ];
185
188
  const BROWSER_EXT_ICON_FILES = [
186
189
  "icon-16.png",
187
190
  "icon-32.png",
188
191
  "icon-48.png",
189
192
  "icon-128.png"
190
193
  ];
194
+ function copyBundledDirectory(src, dest) {
195
+ mkdirSync(dest, { recursive: true });
196
+ for (const entry of readdirSync(src, { withFileTypes: true })) {
197
+ const sourcePath = join(src, entry.name);
198
+ const destinationPath = join(dest, entry.name);
199
+ if (entry.isDirectory()) copyBundledDirectory(sourcePath, destinationPath);
200
+ else copyBundledFile(sourcePath, destinationPath);
201
+ }
202
+ }
191
203
  function copyBundledTree(src, dest) {
192
204
  mkdirSync(dest, { recursive: true });
193
- for (const name of ["manifest.json", "popup.html"]) copyBundledFile(join(src, name), join(dest, name));
194
- for (const file of BROWSER_EXT_DIST_FILES) copyBundledFile(join(src, "dist", file), join(dest, "dist", file));
205
+ copyBundledFile(join(src, "manifest.json"), join(dest, "manifest.json"));
206
+ copyBundledDirectory(join(src, "dist"), join(dest, "dist"));
195
207
  for (const icon of BROWSER_EXT_ICON_FILES) {
196
208
  const iconSrc = join(src, "icons", icon);
197
209
  if (existsSync(iconSrc)) copyBundledFile(iconSrc, join(dest, "icons", icon));
@@ -283,6 +295,60 @@ async function ensureBrowserExtensionArtifacts(opts) {
283
295
  copied: true
284
296
  };
285
297
  }
298
+ function shellQuote(value) {
299
+ return `'${value.replaceAll("'", "'\\''")}'`;
300
+ }
301
+ function browserNativeManifestDirectories(platform = process.platform, home = process.env.HOME || process.env.USERPROFILE || "") {
302
+ if (!home) return [];
303
+ if (platform === "darwin") return [
304
+ join(home, "Library/Application Support/Google/Chrome/NativeMessagingHosts"),
305
+ join(home, "Library/Application Support/Chromium/NativeMessagingHosts"),
306
+ join(home, "Library/Application Support/Microsoft Edge/NativeMessagingHosts"),
307
+ join(home, "Library/Application Support/BraveSoftware/Brave-Browser/NativeMessagingHosts")
308
+ ];
309
+ if (platform === "linux") return [
310
+ join(home, ".config/google-chrome/NativeMessagingHosts"),
311
+ join(home, ".config/chromium/NativeMessagingHosts"),
312
+ join(home, ".config/microsoft-edge/NativeMessagingHosts"),
313
+ join(home, ".config/BraveSoftware/Brave-Browser/NativeMessagingHosts")
314
+ ];
315
+ return [];
316
+ }
317
+ /** Install the local-only bootstrap host used to discover and pair with a Gateway. */
318
+ async function installBrowserNativeMessagingHost(opts) {
319
+ const directories = browserNativeManifestDirectories(opts?.platform ?? process.platform, opts?.home);
320
+ if (directories.length === 0) return {
321
+ installed: false,
322
+ manifestPaths: [],
323
+ reason: "Native bootstrap installation is supported on macOS and Linux"
324
+ };
325
+ const cliPath = opts?.cliPath ?? process.argv[1];
326
+ if (!cliPath) throw new Error("Unable to resolve the xopc CLI entry point");
327
+ const hostPath = join((opts?.cacheDir?.trim() ? assertCacheDir(opts.cacheDir) : resolveBinDir()) || resolveBinDir(), "browser-native-host");
328
+ mkdirSync(dirname(hostPath), { recursive: true });
329
+ const stateDir = opts?.stateDir ?? resolveStateDir();
330
+ const configPath = opts?.configPath ?? resolveConfigPath();
331
+ await writeTextAtomic(hostPath, `#!/bin/sh\nexec env XOPC_STATE_DIR=${shellQuote(stateDir)} XOPC_CONFIG_PATH=${shellQuote(configPath)} XOPC_LOG_CONSOLE=false ${shellQuote(opts?.nodePath ?? process.execPath)} ${shellQuote(cliPath)} browser extension native-host\n`);
332
+ chmodSync(hostPath, 493);
333
+ const manifest = JSON.stringify({
334
+ name: BROWSER_NATIVE_HOST_NAME,
335
+ description: "xopc local browser pairing bootstrap",
336
+ path: hostPath,
337
+ type: "stdio",
338
+ allowed_origins: [`chrome-extension://${BROWSER_EXTENSION_ID}/`]
339
+ }, null, 2);
340
+ const manifestPaths = [];
341
+ for (const directory of directories) {
342
+ mkdirSync(directory, { recursive: true });
343
+ const manifestPath = join(directory, `${BROWSER_NATIVE_HOST_NAME}.json`);
344
+ await writeTextAtomic(manifestPath, manifest);
345
+ manifestPaths.push(manifestPath);
346
+ }
347
+ return {
348
+ installed: true,
349
+ manifestPaths
350
+ };
351
+ }
286
352
  /** Gateway startup hook: ensure artifacts only while the extension driver is active. */
287
353
  async function ensureBrowserExtensionOnStartup(config) {
288
354
  if (config?.browser.enabled === false || config?.browser.driver.kind !== "extension") return;
@@ -366,4 +432,4 @@ async function openBrowserExtensionInstallUi(opts) {
366
432
  };
367
433
  }
368
434
  //#endregion
369
- export { BROWSER_EXT_REQUIRED_FILES, browserExtContentHash, browserExtDoctor, computeNeedsRefresh, ensureBrowserExtensionArtifacts, ensureBrowserExtensionOnStartup, openBrowserExtensionInstallUi, readInstalledExtensionDir, resolveBundledBrowserExtDir, resolveInstalledExtensionPath, resolveWindowsExtensionManager, validateBrowserExtLayout };
435
+ export { BROWSER_EXT_REQUIRED_FILES, browserExtContentHash, browserExtDoctor, browserNativeManifestDirectories, computeNeedsRefresh, ensureBrowserExtensionArtifacts, ensureBrowserExtensionOnStartup, installBrowserNativeMessagingHost, openBrowserExtensionInstallUi, readInstalledExtensionDir, resolveBundledBrowserExtDir, resolveInstalledExtensionPath, resolveWindowsExtensionManager, validateBrowserExtLayout };
@@ -31,6 +31,8 @@ export declare class ExtensionBrowserProvider {
31
31
  private connected;
32
32
  private extensionProtocolVersion;
33
33
  private extensionVersion;
34
+ private connectionId;
35
+ private principalId;
34
36
  private readonly config;
35
37
  private connectionWaiters;
36
38
  constructor(config?: ExtensionProviderConfig);
@@ -48,10 +50,12 @@ export declare class ExtensionBrowserProvider {
48
50
  protocolVersion: number | null;
49
51
  expectedProtocolVersion: number;
50
52
  extensionVersion: string | null;
53
+ principalId: string | null;
51
54
  };
52
55
  private protocolMismatchMessage;
53
56
  /** Shutdown the WebSocket server. */
54
57
  shutdown(): Promise<void>;
55
58
  private _handleMessage;
59
+ private _authenticateCandidate;
56
60
  }
57
61
  export declare function isBrowserWireResult(value: unknown): value is BrowserWireResult;
@@ -1,5 +1,10 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
+ import { deleteBrowserTabBindingsByPrincipal, getBrowserTabBindingById } from "../../storage/sqlite/browser-tab-binding-repository.js";
4
+ import { getDevice } from "../../storage/sqlite/device-access-repository.js";
5
+ import { isChromeExtensionOrigin } from "../../gateway/security/origin-check.js";
6
+ import { browserWireAuthenticationPayload } from "../../../packages/browser-control-contract/src/index.js";
7
+ import crypto from "node:crypto";
3
8
  //#region src/browser/providers/extension.ts
4
9
  /**
5
10
  * Extension browser provider — connects to the xopc Chrome Extension via WebSocket.
@@ -29,6 +34,8 @@ var ExtensionBrowserProvider = class {
29
34
  connected = false;
30
35
  extensionProtocolVersion = null;
31
36
  extensionVersion = null;
37
+ connectionId = null;
38
+ principalId = null;
32
39
  config;
33
40
  connectionWaiters = [];
34
41
  constructor(config) {
@@ -62,20 +69,56 @@ var ExtensionBrowserProvider = class {
62
69
  server: this.server,
63
70
  path: "/browser-ext"
64
71
  });
65
- this.wss.on("connection", (ws) => {
66
- log.info("Chrome Extension connected");
67
- const previousClient = this.clientWs;
68
- this.clientWs = ws;
69
- this.socketConnected = true;
70
- this.handshakeReceived = false;
71
- this.connected = false;
72
- this.extensionProtocolVersion = null;
73
- this.extensionVersion = null;
74
- if (previousClient && previousClient !== ws) try {
75
- previousClient.close();
76
- } catch {}
72
+ this.wss.on("connection", (ws, request) => {
73
+ const origin = request.headers.origin?.toLowerCase();
74
+ if (!isChromeExtensionOrigin(origin)) {
75
+ ws.close(4403, "Chrome extension origin required");
76
+ return;
77
+ }
78
+ const challenge = {
79
+ type: "auth_challenge",
80
+ protocolVersion: 4,
81
+ connectionId: crypto.randomUUID(),
82
+ challenge: crypto.randomBytes(32).toString("base64url"),
83
+ issuedAt: Date.now()
84
+ };
85
+ let authenticated = false;
86
+ const authenticationTimer = setTimeout(() => {
87
+ if (!authenticated) ws.close(4401, "Browser authentication timed out");
88
+ }, 5e3);
89
+ ws.send(JSON.stringify(challenge));
77
90
  ws.on("message", (data) => {
78
- this._handleMessage(data.toString(), ws);
91
+ if (authenticated) {
92
+ this._handleMessage(data.toString(), ws);
93
+ return;
94
+ }
95
+ if (!this._authenticateCandidate(data.toString(), origin, challenge)) {
96
+ clearTimeout(authenticationTimer);
97
+ ws.close(4401, "Browser authentication failed");
98
+ return;
99
+ }
100
+ authenticated = true;
101
+ clearTimeout(authenticationTimer);
102
+ const message = JSON.parse(data.toString());
103
+ const previousClient = this.clientWs;
104
+ this.clientWs = ws;
105
+ this.socketConnected = true;
106
+ this.handshakeReceived = true;
107
+ this.connected = true;
108
+ this.extensionProtocolVersion = message.protocolVersion;
109
+ this.extensionVersion = message.extensionVersion;
110
+ this.connectionId = message.connectionId;
111
+ this.principalId = message.principalId;
112
+ if (previousClient && previousClient !== ws) try {
113
+ previousClient.close(4001, "Connection replaced");
114
+ } catch {}
115
+ for (const waiter of this.connectionWaiters) waiter.resolve();
116
+ this.connectionWaiters = [];
117
+ log.info({
118
+ principalId: message.principalId,
119
+ extensionId: message.extensionId,
120
+ connectionId: message.connectionId
121
+ }, "Chrome extension authenticated");
79
122
  });
80
123
  ws.on("close", (code, reason) => {
81
124
  if (this.clientWs !== ws) return;
@@ -84,12 +127,16 @@ var ExtensionBrowserProvider = class {
84
127
  reason: reason.toString(),
85
128
  pendingCount: this.pending.size
86
129
  }, "Chrome Extension disconnected");
130
+ const disconnectedPrincipalId = this.principalId;
87
131
  this.clientWs = null;
88
132
  this.socketConnected = false;
89
133
  this.handshakeReceived = false;
90
134
  this.connected = false;
91
135
  this.extensionProtocolVersion = null;
92
136
  this.extensionVersion = null;
137
+ this.connectionId = null;
138
+ this.principalId = null;
139
+ if (disconnectedPrincipalId) deleteBrowserTabBindingsByPrincipal(disconnectedPrincipalId);
93
140
  for (const [id, req] of this.pending) {
94
141
  clearTimeout(req.timer);
95
142
  req.reject(/* @__PURE__ */ new Error("Extension disconnected"));
@@ -145,7 +192,7 @@ var ExtensionBrowserProvider = class {
145
192
  /** Wait for the Chrome Extension to connect. */
146
193
  async waitForConnection(timeoutMs) {
147
194
  if (this.connected) return;
148
- if (this.socketConnected && this.handshakeReceived && this.extensionProtocolVersion !== 3) throw new Error(this.protocolMismatchMessage());
195
+ if (this.socketConnected && this.handshakeReceived && this.extensionProtocolVersion !== 4) throw new Error(this.protocolMismatchMessage());
149
196
  const timeout = timeoutMs ?? this.config.connectionTimeout;
150
197
  return new Promise((resolve, reject) => {
151
198
  const timer = setTimeout(() => {
@@ -171,10 +218,15 @@ var ExtensionBrowserProvider = class {
171
218
  const detail = this.socketConnected ? this.protocolMismatchMessage() : "Extension not connected. Ensure the Chrome Extension is installed and connected.";
172
219
  throw new Error(detail);
173
220
  }
221
+ if (input.target?.kind === "attached_tab") {
222
+ const binding = getBrowserTabBindingById(input.target.bindingId);
223
+ if (!binding || binding.principalId !== this.principalId) throw new Error("Attached tab binding does not belong to the connected browser");
224
+ }
174
225
  const id = `cmd_${++this.commandCounter}_${Date.now()}`;
175
226
  const cmd = {
176
227
  id,
177
- protocolVersion: 3,
228
+ protocolVersion: 4,
229
+ connectionId: this.connectionId,
178
230
  input,
179
231
  timeoutMs: timeoutMs ?? this.config.commandTimeout,
180
232
  visualFallback
@@ -207,12 +259,13 @@ var ExtensionBrowserProvider = class {
207
259
  socketConnected: this.socketConnected,
208
260
  connected: this.connected,
209
261
  protocolVersion: this.extensionProtocolVersion,
210
- expectedProtocolVersion: 3,
211
- extensionVersion: this.extensionVersion
262
+ expectedProtocolVersion: 4,
263
+ extensionVersion: this.extensionVersion,
264
+ principalId: this.principalId
212
265
  };
213
266
  }
214
267
  protocolMismatchMessage() {
215
- return `Browser extension protocol mismatch (expected 3, received ${this.extensionProtocolVersion ?? "unknown"}). Reload the extension in Chrome.`;
268
+ return `Browser extension protocol mismatch (expected 4, received ${this.extensionProtocolVersion ?? "unknown"}). Reload the extension in Chrome.`;
216
269
  }
217
270
  /** Shutdown the WebSocket server. */
218
271
  async shutdown() {
@@ -246,32 +299,19 @@ var ExtensionBrowserProvider = class {
246
299
  this.handshakeReceived = false;
247
300
  this.extensionProtocolVersion = null;
248
301
  this.extensionVersion = null;
302
+ this.connectionId = null;
303
+ this.principalId = null;
249
304
  log.info("Extension provider shut down");
250
305
  }
251
306
  _handleMessage(raw, sourceWs) {
252
307
  if (sourceWs !== this.clientWs) return;
253
308
  try {
254
309
  const msg = JSON.parse(raw);
310
+ if (!this.connectionId || msg.connectionId !== this.connectionId) return;
255
311
  if (msg.type === "status") {
256
312
  const status = msg;
257
- this.handshakeReceived = true;
258
- this.extensionProtocolVersion = typeof status.protocolVersion === "number" ? status.protocolVersion : null;
259
- this.extensionVersion = typeof status.extensionVersion === "string" ? status.extensionVersion : null;
260
- this.connected = status.protocolVersion === 3;
261
- if (this.connected) {
262
- for (const waiter of this.connectionWaiters) waiter.resolve();
263
- this.connectionWaiters = [];
264
- log.debug(msg, "Compatible browser extension connected");
265
- } else {
266
- const error = new Error(this.protocolMismatchMessage());
267
- for (const waiter of this.connectionWaiters) waiter.reject(error);
268
- this.connectionWaiters = [];
269
- log.warn({
270
- expectedProtocolVersion: 3,
271
- extensionProtocolVersion: this.extensionProtocolVersion,
272
- extensionVersion: this.extensionVersion
273
- }, "Browser extension protocol mismatch; reload the extension");
274
- }
313
+ if (status.principalId !== this.principalId) return;
314
+ log.debug({ sessionCount: status.sessionCount }, "Browser extension status updated");
275
315
  return;
276
316
  }
277
317
  if (isBrowserWireKeepAlive(msg)) return;
@@ -297,14 +337,40 @@ var ExtensionBrowserProvider = class {
297
337
  log.error({ err: e }, "Failed to parse extension message");
298
338
  }
299
339
  }
340
+ _authenticateCandidate(raw, origin, challenge) {
341
+ try {
342
+ const message = JSON.parse(raw);
343
+ if (message.type !== "authenticate" || message.protocolVersion !== 4 || message.connectionId !== challenge.connectionId || `chrome-extension://${message.extensionId}` !== origin || Math.abs(Date.now() - challenge.issuedAt) > 5e3) return false;
344
+ const device = getDevice(message.principalId);
345
+ if (!device || device.revokedAt !== void 0 || device.platform !== "chrome" || device.extensionId !== message.extensionId) return false;
346
+ const payload = browserWireAuthenticationPayload({
347
+ protocolVersion: message.protocolVersion,
348
+ connectionId: message.connectionId,
349
+ challenge: challenge.challenge,
350
+ issuedAt: challenge.issuedAt,
351
+ principalId: message.principalId,
352
+ extensionId: message.extensionId,
353
+ extensionVersion: message.extensionVersion
354
+ });
355
+ return crypto.verify("sha256", Buffer.from(payload), {
356
+ key: crypto.createPublicKey({
357
+ key: device.publicKeyJwk,
358
+ format: "jwk"
359
+ }),
360
+ dsaEncoding: "ieee-p1363"
361
+ }, Buffer.from(message.signature, "base64url"));
362
+ } catch {
363
+ return false;
364
+ }
365
+ }
300
366
  };
301
367
  function isBrowserWireResult(value) {
302
368
  if (!value || typeof value !== "object") return false;
303
369
  const result = value.result;
304
- return typeof value.id === "string" && Boolean(result) && typeof result === "object" && typeof result.ok === "boolean";
370
+ return typeof value.id === "string" && typeof value.connectionId === "string" && Boolean(result) && typeof result === "object" && typeof result.ok === "boolean";
305
371
  }
306
372
  function isBrowserWireKeepAlive(value) {
307
- return Boolean(value) && typeof value === "object" && value.type === "keepalive" && typeof value.timestamp === "number";
373
+ return Boolean(value) && typeof value === "object" && value.type === "keepalive" && typeof value.connectionId === "string" && typeof value.timestamp === "number";
308
374
  }
309
375
  //#endregion
310
376
  export { ExtensionBrowserProvider, isBrowserWireResult };
@@ -1,4 +1,4 @@
1
- import type { BrowserActionInput, BrowserControlResult } from '@xopcai/browser-control-contract';
1
+ import type { BrowserActionInput, BrowserControlResult, BrowserTarget } from '@xopcai/browser-control-contract';
2
2
  import type { Config } from '../../config/schema.js';
3
3
  import type { BrowserDriver } from '../drivers/browser-driver.js';
4
4
  export interface BrowserRuntimeOptions {
@@ -6,6 +6,7 @@ export interface BrowserRuntimeOptions {
6
6
  createDriver: () => Promise<BrowserDriver>;
7
7
  allowedUploadRoots?: string[];
8
8
  emit?: (type: string, payload: unknown) => void;
9
+ resolveTarget?: (taskKey: string) => BrowserTarget | undefined;
9
10
  }
10
11
  export declare class BrowserRuntime {
11
12
  private readonly options;