@xopcai/xopc 0.0.251 → 0.0.252

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 (329) hide show
  1. package/README.md +8 -5
  2. package/README.zh-CN.md +8 -5
  3. package/dist/browser-ext/dist/background.js +358 -768
  4. package/dist/browser-ext/manifest.json +1 -2
  5. package/dist/extensions/telegram/xopc.extension.json +1 -1
  6. package/dist/gateway/static/root/assets/{activity-store-DfBjTSo9.js → activity-store-w3eq3YpP.js} +1 -1
  7. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-Bo_dmLv8.js → agent-avatar-dicebear-k039rbUz.js} +1 -1
  8. package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-kILWelNb.js → agent-defaults-settings-panel-D4J4oq8p.js} +1 -1
  9. package/dist/gateway/static/root/assets/{agents-yuUn8n-f.js → agents-Bmth_bSQ.js} +1 -1
  10. package/dist/gateway/static/root/assets/{api-zxpcJfgj.js → api-2joIfbrH.js} +1 -1
  11. package/dist/gateway/static/root/assets/{app-management-settings-panel-Cz56nrVF.js → app-management-settings-panel-ByJ4kiD0.js} +1 -1
  12. package/dist/gateway/static/root/assets/{appearance-settings-DxSws5U4.js → appearance-settings-BK2Jga-B.js} +1 -1
  13. package/dist/gateway/static/root/assets/apps-page-B3ky7cWU.js +1 -0
  14. package/dist/gateway/static/root/assets/{archive-plugin-7k15-hqo.js → archive-plugin-DMDKtYpi.js} +1 -1
  15. package/dist/gateway/static/root/assets/{attachment-load-B-SFpjd9.js → attachment-load-B764Ql3Z.js} +1 -1
  16. package/dist/gateway/static/root/assets/{automations-page-Qy36j19q.js → automations-page-D-sO8CZe.js} +1 -1
  17. package/dist/gateway/static/root/assets/automations-workspace-Ds5-_-9z.js +6 -0
  18. package/dist/gateway/static/root/assets/{binary-plugins-CPOZQt8F.js → binary-plugins-fxtzYRpv.js} +1 -1
  19. package/dist/gateway/static/root/assets/{block-editor-DhvVlIp9.js → block-editor-Bcjtvckh.js} +3 -3
  20. package/dist/gateway/static/root/assets/browser-automation-inputs-BSmnusjD.js +1 -0
  21. package/dist/gateway/static/root/assets/browser-automations-page-DC_OQIBk.js +3 -0
  22. package/dist/gateway/static/root/assets/browser-settings-page-C1FojyG6.js +3 -0
  23. package/dist/gateway/static/root/assets/capabilities-settings-panel-Bs59UCut.js +7 -0
  24. package/dist/gateway/static/root/assets/channels-settings-CM9vNmoo.js +1 -0
  25. package/dist/gateway/static/root/assets/{channels-status-swr-CX-yWwLD.js → channels-status-swr-BuWjIG_a.js} +1 -1
  26. package/dist/gateway/static/root/assets/{chat-terminal-dock-1PJoxRuV.js → chat-terminal-dock-5SevEo_I.js} +2 -2
  27. package/dist/gateway/static/root/assets/connectors-page-BPZ44D-V.js +2 -0
  28. package/dist/gateway/static/root/assets/{cron-expression-CBCc6zFA.js → cron-expression-DtsfalBM.js} +1 -1
  29. package/dist/gateway/static/root/assets/{date-picker-u8K5Kt_N.js → date-picker-tDEQV5TP.js} +1 -1
  30. package/dist/gateway/static/root/assets/{dependency-picker-y_i_cnpz.js → dependency-picker-HhNwg0Y2.js} +1 -1
  31. package/dist/gateway/static/root/assets/{desktop-pet-DyL0JxLm.js → desktop-pet-acU4Li-C.js} +1 -1
  32. package/dist/gateway/static/root/assets/{desktop-pet-settings-d2d_IsyP.js → desktop-pet-settings-R84AR0Pe.js} +1 -1
  33. package/dist/gateway/static/root/assets/{extension-debug-page-DqIBKJwl.js → extension-debug-page-CrLlRkSN.js} +1 -1
  34. package/dist/gateway/static/root/assets/{extension-page-DUW1xxj2.js → extension-page-BH5lmZXE.js} +1 -1
  35. package/dist/gateway/static/root/assets/{extension-settings-page-BHY9qaNw.js → extension-settings-page-BscjNAd1.js} +1 -1
  36. package/dist/gateway/static/root/assets/{gateway-config-swr-CMNbl0FZ.js → gateway-config-swr-9HidFZUd.js} +1 -1
  37. package/dist/gateway/static/root/assets/gateway-settings-BxLcW6ij.js +1 -0
  38. package/dist/gateway/static/root/assets/{gateway-startup-retry-CR7Xg8As.js → gateway-startup-retry-DRCPXONr.js} +1 -1
  39. package/dist/gateway/static/root/assets/{heartbeat-settings-7CD9iPR7.js → heartbeat-settings-K5Zhl2rx.js} +1 -1
  40. package/dist/gateway/static/root/assets/home-page-CZAg9RBv.js +1 -0
  41. package/dist/gateway/static/root/assets/{image-generation-api-C5mn73ca.js → image-generation-api-CR6Kle8H.js} +1 -1
  42. package/dist/gateway/static/root/assets/index-D4Ykja-U.js +72 -0
  43. package/dist/gateway/static/root/assets/index-zIJ3AXgR.css +2 -0
  44. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-BtlqMleF.js → keyboard-shortcuts-settings-B1QR49-7.js} +1 -1
  45. package/dist/gateway/static/root/assets/{local-app-workbench-page-BjxfkjxE.js → local-app-workbench-page-wicoV-ti.js} +2 -2
  46. package/dist/gateway/static/root/assets/{local-apps-page-B7xqQe2s.js → local-apps-page-C74It-T8.js} +1 -1
  47. package/dist/gateway/static/root/assets/{locale-store-CF4erhjB.js → locale-store-BfZGZKSs.js} +1 -1
  48. package/dist/gateway/static/root/assets/{logs-page-kACaPZKL.js → logs-page-xX-7S49A.js} +2 -2
  49. package/dist/gateway/static/root/assets/{management-settings-A432siER.js → management-settings-1kNnYL97.js} +1 -1
  50. package/dist/gateway/static/root/assets/{markdown-view-CQvzsE24.js → markdown-view-DjiwT28p.js} +2 -2
  51. package/dist/gateway/static/root/assets/{media-plugins-CpvYiauZ.js → media-plugins-B5c0AoS1.js} +1 -1
  52. package/dist/gateway/static/root/assets/messages-DaIwVWar.js +3 -0
  53. package/dist/gateway/static/root/assets/{mobile-pairing-wizard-DyjVGngO.js → mobile-pairing-wizard-CsIZqK51.js} +1 -1
  54. package/dist/gateway/static/root/assets/note-detail-panel-CfWxopl4.js +5 -0
  55. package/dist/gateway/static/root/assets/notes-page-CYN-LCHJ.js +8 -0
  56. package/dist/gateway/static/root/assets/{page-tabs-B3FKzjR6.js → page-tabs-CqJN6Ljz.js} +1 -1
  57. package/dist/gateway/static/root/assets/{preview-open-alternatives-DnjrwAHD.js → preview-open-alternatives-DqRIGxf4.js} +1 -1
  58. package/dist/gateway/static/root/assets/{product-open-page-COcr_NUJ.js → product-open-page-DE2nPE_K.js} +1 -1
  59. package/dist/gateway/static/root/assets/project-detail-page-B5G3LzDq.js +1 -0
  60. package/dist/gateway/static/root/assets/{projects-page-BzH8pjEz.js → projects-page-CuVK2mKu.js} +1 -1
  61. package/dist/gateway/static/root/assets/{registry-api-BsRBlji7.js → registry-api-Bpy8XX-5.js} +1 -1
  62. package/dist/gateway/static/root/assets/remote-access-hub-Dnd73xWt.js +1 -0
  63. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-LO4wZ_dO.js → runtime-tools-settings-panel-Bb4b74-P.js} +2 -2
  64. package/dist/gateway/static/root/assets/{schema-form-crcj4-Pm.js → schema-form-DoPU5j3J.js} +1 -1
  65. package/dist/gateway/static/root/assets/sessions-page-XL4LyKZe.js +1 -0
  66. package/dist/gateway/static/root/assets/settings-advanced-gate-0ForzDSS.js +1 -0
  67. package/dist/gateway/static/root/assets/{settings-form-section-Bl4MvTmq.js → settings-form-section-Ci-9WajJ.js} +1 -1
  68. package/dist/gateway/static/root/assets/{settings-page-DplrjZ6x.js → settings-page-CL_2F95D.js} +1 -1
  69. package/dist/gateway/static/root/assets/setup-status-panel-DVws6XZc.js +1 -0
  70. package/dist/gateway/static/root/assets/{share-preview-page-CJP1kURj.js → share-preview-page-XUA3oKQX.js} +1 -1
  71. package/dist/gateway/static/root/assets/shares-settings-BwDvcacF.js +1 -0
  72. package/dist/gateway/static/root/assets/skill-reload-api-BJjrATqE.js +1 -0
  73. package/dist/gateway/static/root/assets/{skills-page-DsjcKg9Q.js → skills-page-sXXAZDc9.js} +1 -1
  74. package/dist/gateway/static/root/assets/{skills-page.utils-D6gWBbvy.js → skills-page.utils-DD79Bl1I.js} +1 -1
  75. package/dist/gateway/static/root/assets/{system-settings-panel-_eCXfqCo.js → system-settings-panel-BEH64qBV.js} +1 -1
  76. package/dist/gateway/static/root/assets/task-detail-page-0jxMi75O.js +1 -0
  77. package/dist/gateway/static/root/assets/{theme-store-BOgkc_9D.js → theme-store-CISSxU1c.js} +1 -1
  78. package/dist/gateway/static/root/assets/{tunnel-api-W5mktxl1.js → tunnel-api-DrKDhnJm.js} +1 -1
  79. package/dist/gateway/static/root/assets/{use-autosave-Na2DYzT3.js → use-autosave-CPYePL-Q.js} +1 -1
  80. package/dist/gateway/static/root/assets/user-model-page-B6zLDfDl.js +2 -0
  81. package/dist/gateway/static/root/assets/{voice-api-key-field-OXweUxS_.js → voice-api-key-field-DKqzrCXx.js} +1 -1
  82. package/dist/gateway/static/root/assets/{voice-config-api-C-ryeIxj.js → voice-config-api-e9xWjwBX.js} +1 -1
  83. package/dist/gateway/static/root/assets/{work-discovery-overlay-BqOsO47I.js → work-discovery-overlay-kTQug6bq.js} +1 -1
  84. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-B9S_lfT1.js → workflow-run-setup-panel-m9SKj_GT.js} +2 -2
  85. package/dist/gateway/static/root/assets/workflows-page-Bcd1XqP7.js +5 -0
  86. package/dist/gateway/static/root/index.html +10 -10
  87. package/dist/package.js +1 -1
  88. package/dist/packages/gateway-contract/src/new-session.js +9 -0
  89. package/dist/src/agent/agent-manager.d.ts +2 -1
  90. package/dist/src/agent/agent-manager.js +6 -5
  91. package/dist/src/agent/background-review/run-background-review.js +1 -1
  92. package/dist/src/agent/coding/repository-instructions.js +2 -2
  93. package/dist/src/agent/coding/workspace-revision.js +2 -2
  94. package/dist/src/agent/commands/command-isolation.js +2 -2
  95. package/dist/src/agent/embedded/model-runtime.js +1 -1
  96. package/dist/src/agent/embedded/run-for-session.js +1 -1
  97. package/dist/src/agent/external-tools/composio-provider.d.ts +1 -0
  98. package/dist/src/agent/external-tools/composio-provider.js +64 -34
  99. package/dist/src/agent/external-tools/search-error.js +5 -3
  100. package/dist/src/agent/image/tool-model-config.js +1 -1
  101. package/dist/src/agent/mcp/oauth/mcp-oauth-store.js +1 -1
  102. package/dist/src/agent/service.js +4 -3
  103. package/dist/src/agent/service.types.d.ts +2 -1
  104. package/dist/src/agent/session/session-inspector.js +1 -1
  105. package/dist/src/agent/skills/hub-lock.js +1 -1
  106. package/dist/src/agent/skills/marketplace/adapters/store/store-api-client.js +4 -0
  107. package/dist/src/agent/tasks/task-judge-service.js +3 -3
  108. package/dist/src/agent/tool-manuals/browser.d.ts +1 -1
  109. package/dist/src/agent/tool-manuals/browser.js +15 -226
  110. package/dist/src/agent/tools/browser/tool/browser-use-tool.d.ts +4 -21
  111. package/dist/src/agent/tools/browser/tool/browser-use-tool.js +48 -165
  112. package/dist/src/agent/tools/browser/tool/schemas.d.ts +203 -16
  113. package/dist/src/agent/tools/browser/tool/schemas.js +146 -16
  114. package/dist/src/agent/tools/browser-automation-tool.d.ts +121 -0
  115. package/dist/src/agent/tools/browser-automation-tool.js +197 -0
  116. package/dist/src/agent/tools/factory.d.ts +6 -9
  117. package/dist/src/agent/tools/factory.js +28 -55
  118. package/dist/src/agent/tools/index.d.ts +2 -2
  119. package/dist/src/agent/tools/index.js +3 -3
  120. package/dist/src/agent/tools/review-workspace.js +2 -2
  121. package/dist/src/ai-assist/text-assist.js +1 -1
  122. package/dist/src/auth/credentials.js +2 -2
  123. package/dist/src/auth/oauth/xopc-cloud.js +3 -1
  124. package/dist/src/auth/profiles/store.js +1 -1
  125. package/dist/src/auth/sync-provider-auth.js +2 -2
  126. package/dist/src/automations/domain/defaults.js +1 -1
  127. package/dist/src/automations/domain/types.d.ts +5 -5
  128. package/dist/src/automations/domain/validation.d.ts +12 -12
  129. package/dist/src/automations/domain/validation.js +3 -3
  130. package/dist/src/automations/draft/automation-draft-validator.d.ts +3 -3
  131. package/dist/src/automations/draft/automation-draft-validator.js +1 -1
  132. package/dist/src/automations/service/action-executor.d.ts +1 -1
  133. package/dist/src/automations/service/action-executor.js +7 -7
  134. package/dist/src/browser/{recipes → automations}/index.d.ts +1 -0
  135. package/dist/src/browser/automations/index.js +4 -0
  136. package/dist/src/browser/automations/repository.d.ts +13 -0
  137. package/dist/src/browser/automations/repository.js +60 -0
  138. package/dist/src/browser/automations/runner.d.ts +17 -0
  139. package/dist/src/browser/automations/runner.js +173 -0
  140. package/dist/src/browser/automations/runtime-service.d.ts +6 -0
  141. package/dist/src/browser/automations/runtime-service.js +37 -0
  142. package/dist/src/browser/automations/schema.d.ts +144 -0
  143. package/dist/src/browser/automations/schema.js +135 -0
  144. package/dist/src/browser/automations/service.d.ts +146 -0
  145. package/dist/src/browser/automations/service.js +168 -0
  146. package/dist/src/browser/automations/types.d.ts +90 -0
  147. package/dist/src/browser/cache-dir-policy.d.ts +1 -1
  148. package/dist/src/browser/cache-dir-policy.js +1 -4
  149. package/dist/src/browser/drivers/browser-driver.d.ts +17 -0
  150. package/dist/src/browser/drivers/create-driver.d.ts +3 -0
  151. package/dist/src/browser/drivers/create-driver.js +84 -0
  152. package/dist/src/browser/drivers/extension-driver.d.ts +20 -0
  153. package/dist/src/browser/drivers/extension-driver.js +47 -0
  154. package/dist/src/browser/drivers/playwright-driver.d.ts +38 -0
  155. package/dist/src/browser/drivers/playwright-driver.js +406 -0
  156. package/dist/src/browser/index.d.ts +10 -15
  157. package/dist/src/browser/index.js +10 -15
  158. package/dist/src/browser/install-lock.d.ts +1 -2
  159. package/dist/src/browser/install-lock.js +3 -9
  160. package/dist/src/browser/install-progress.d.ts +1 -1
  161. package/dist/src/browser/observation/format.d.ts +2 -0
  162. package/dist/src/browser/observation/format.js +26 -0
  163. package/dist/src/browser/policy/approval-store.d.ts +17 -0
  164. package/dist/src/browser/policy/approval-store.js +62 -0
  165. package/dist/src/browser/policy/browser-policy.d.ts +3 -0
  166. package/dist/src/browser/policy/browser-policy.js +27 -0
  167. package/dist/src/browser/providers/browser-ext-install.d.ts +1 -1
  168. package/dist/src/browser/providers/browser-ext-install.js +4 -6
  169. package/dist/src/browser/providers/browser-use.js +1 -1
  170. package/dist/src/browser/providers/browserbase.js +1 -1
  171. package/dist/src/browser/providers/extension-ws-acquire.d.ts +1 -4
  172. package/dist/src/browser/providers/extension-ws-acquire.js +1 -13
  173. package/dist/src/browser/providers/extension.d.ts +3 -22
  174. package/dist/src/browser/providers/extension.js +7 -15
  175. package/dist/src/browser/providers/index.d.ts +2 -2
  176. package/dist/src/browser/providers/index.js +3 -3
  177. package/dist/src/browser/providers/types.d.ts +0 -94
  178. package/dist/src/browser/readiness.d.ts +4 -22
  179. package/dist/src/browser/readiness.js +55 -118
  180. package/dist/src/browser/runtime/browser-runtime.d.ts +30 -0
  181. package/dist/src/browser/runtime/browser-runtime.js +362 -0
  182. package/dist/src/browser/url-policy.d.ts +3 -3
  183. package/dist/src/browser/url-policy.js +7 -6
  184. package/dist/src/browser/verification/expectation.d.ts +2 -0
  185. package/dist/src/browser/verification/expectation.js +17 -0
  186. package/dist/src/cli/command-loaders.js +1 -0
  187. package/dist/src/cli/command-manifest.js +5 -1
  188. package/dist/src/cli/commands/browser-cli-helpers.d.ts +0 -6
  189. package/dist/src/cli/commands/browser-cli-helpers.js +11 -144
  190. package/dist/src/cli/commands/browser.d.ts +0 -5
  191. package/dist/src/cli/commands/browser.js +11 -152
  192. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  193. package/dist/src/cli/commands/doctor/checks/node-version.js +25 -9
  194. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  195. package/dist/src/cli/commands/models.js +1 -1
  196. package/dist/src/cli/commands/platform.d.ts +1 -0
  197. package/dist/src/cli/commands/platform.js +111 -0
  198. package/dist/src/config/index.js +4 -4
  199. package/dist/src/config/loader.js +10 -4
  200. package/dist/src/config/models-json.js +1 -1
  201. package/dist/src/config/schema.d.ts +131 -84
  202. package/dist/src/config/schema.js +112 -59
  203. package/dist/src/connectors/composio-sessions.js +1 -3
  204. package/dist/src/extensions/lockfile.js +1 -1
  205. package/dist/src/extensions/marketplace.js +4 -0
  206. package/dist/src/gateway/agents-admin.js +1 -1
  207. package/dist/src/gateway/file-path-classifier.js +1 -1
  208. package/dist/src/gateway/heartbeat/service.js +1 -1
  209. package/dist/src/gateway/hono/lib/config-payload.d.ts +12 -26
  210. package/dist/src/gateway/hono/lib/config-payload.js +11 -40
  211. package/dist/src/gateway/hono/routes/browser-automations.d.ts +3 -0
  212. package/dist/src/gateway/hono/routes/browser-automations.js +77 -0
  213. package/dist/src/gateway/hono/routes/browser-install.js +0 -32
  214. package/dist/src/gateway/hono/routes/browser.d.ts +0 -17
  215. package/dist/src/gateway/hono/routes/browser.js +46 -568
  216. package/dist/src/gateway/hono/routes/config-patch/misc.js +8 -1
  217. package/dist/src/gateway/hono/routes/config.js +0 -11
  218. package/dist/src/gateway/hono/routes/lazy-bundles.js +5 -5
  219. package/dist/src/gateway/hono/routes/models.js +1 -1
  220. package/dist/src/gateway/service/config-coordinator.js +1 -1
  221. package/dist/src/gateway/service.d.ts +4 -6
  222. package/dist/src/gateway/service.js +24 -27
  223. package/dist/src/heartbeat/index.js +1 -1
  224. package/dist/src/platform/client.d.ts +121 -0
  225. package/dist/src/platform/client.js +108 -0
  226. package/dist/src/platform/contracts.d.ts +194 -0
  227. package/dist/src/platform/contracts.js +150 -0
  228. package/dist/src/platform/index.d.ts +4 -0
  229. package/dist/src/platform/index.js +9 -0
  230. package/dist/src/platform/resolution.d.ts +4 -0
  231. package/dist/src/platform/resolution.js +16 -0
  232. package/dist/src/platform/worker.d.ts +6 -0
  233. package/dist/src/platform/worker.js +75 -0
  234. package/dist/src/providers/auth-runtime/resolve-auth.js +1 -1
  235. package/dist/src/providers/index.js +2 -2
  236. package/dist/src/providers/model-registry.js +1 -1
  237. package/dist/src/providers/register-runtime-providers.js +1 -1
  238. package/dist/src/providers/xopc-cloud-config.js +3 -1
  239. package/dist/src/session/session-context-for-llm.js +41 -1
  240. package/dist/src/share/hosted-session-share.js +3 -1
  241. package/dist/src/share/share-thumbnail.js +1 -1
  242. package/dist/src/storage/sqlite/migrations/153_browser_control_v2.sql +45 -0
  243. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  244. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  245. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  246. package/dist/src/storage/sqlite/schema.js +1 -1
  247. package/dist/src/storage/sqlite/schema.sql +16 -20
  248. package/dist/src/tasks/home-query-service.js +1 -1
  249. package/dist/src/tui/tui-startup-resources.js +1 -1
  250. package/dist/src/tui/tui.js +1 -1
  251. package/dist/src/tunnel/env.js +3 -1
  252. package/dist/src/work-discovery/service.js +1 -1
  253. package/dist/src/workflows/service/workflow-run-service.js +1 -1
  254. package/package.json +6 -6
  255. package/dist/gateway/static/root/assets/apps-page-BnizTdgs.js +0 -1
  256. package/dist/gateway/static/root/assets/automations-workspace-CZRKqSqY.js +0 -6
  257. package/dist/gateway/static/root/assets/browser-settings-page-pN5LbChY.js +0 -8
  258. package/dist/gateway/static/root/assets/browser-workflow-inputs-BX1-uM9L.js +0 -1
  259. package/dist/gateway/static/root/assets/browser-workflows-page-C65MI57t.js +0 -3
  260. package/dist/gateway/static/root/assets/capabilities-settings-panel-CJw2e16J.js +0 -7
  261. package/dist/gateway/static/root/assets/channels-settings-CRgjb4Ck.js +0 -1
  262. package/dist/gateway/static/root/assets/connectors-page-Dcpdy06y.js +0 -2
  263. package/dist/gateway/static/root/assets/gateway-config-api-e3BRcgDx.js +0 -1
  264. package/dist/gateway/static/root/assets/gateway-settings-Cheo0Rkc.js +0 -1
  265. package/dist/gateway/static/root/assets/home-page-DVrTRqNc.js +0 -1
  266. package/dist/gateway/static/root/assets/index-B7CX4Qob.css +0 -2
  267. package/dist/gateway/static/root/assets/index-BHrpyJ4t.js +0 -72
  268. package/dist/gateway/static/root/assets/messages-Cf5hpZE6.js +0 -3
  269. package/dist/gateway/static/root/assets/note-detail-panel-CUd8O4K7.js +0 -5
  270. package/dist/gateway/static/root/assets/notes-page-BLQwd39W.js +0 -8
  271. package/dist/gateway/static/root/assets/project-detail-page-NttW338u.js +0 -1
  272. package/dist/gateway/static/root/assets/remote-access-hub-BxpjSDnL.js +0 -1
  273. package/dist/gateway/static/root/assets/sessions-page-BLMaibOb.js +0 -1
  274. package/dist/gateway/static/root/assets/settings-advanced-gate-BRvvKhoH.js +0 -1
  275. package/dist/gateway/static/root/assets/setup-status-panel-DTOsxQC1.js +0 -1
  276. package/dist/gateway/static/root/assets/shares-settings-CemgzEi4.js +0 -1
  277. package/dist/gateway/static/root/assets/skill-reload-api-BWYanBlQ.js +0 -1
  278. package/dist/gateway/static/root/assets/task-detail-page-D0Z_K5JT.js +0 -1
  279. package/dist/gateway/static/root/assets/user-model-page-B_-SNat-.js +0 -2
  280. package/dist/gateway/static/root/assets/workflows-page-CNOT2Coo.js +0 -5
  281. package/dist/src/agent/tools/browser-recipe-tool.d.ts +0 -12
  282. package/dist/src/agent/tools/browser-recipe-tool.js +0 -113
  283. package/dist/src/browser/actions/extension-registry-bridge.d.ts +0 -5
  284. package/dist/src/browser/actions/extension-registry-bridge.js +0 -312
  285. package/dist/src/browser/actions/registry.d.ts +0 -5
  286. package/dist/src/browser/actions/registry.js +0 -741
  287. package/dist/src/browser/actions/types.d.ts +0 -65
  288. package/dist/src/browser/backend-from-config.d.ts +0 -22
  289. package/dist/src/browser/backend-from-config.js +0 -99
  290. package/dist/src/browser/browser-command-timeout.d.ts +0 -2
  291. package/dist/src/browser/browser-command-timeout.js +0 -11
  292. package/dist/src/browser/cdp-local-launcher.d.ts +0 -35
  293. package/dist/src/browser/cdp-local-launcher.js +0 -154
  294. package/dist/src/browser/cdp-supervisor.d.ts +0 -58
  295. package/dist/src/browser/cdp-supervisor.js +0 -133
  296. package/dist/src/browser/humanize.d.ts +0 -80
  297. package/dist/src/browser/humanize.js +0 -363
  298. package/dist/src/browser/manager.d.ts +0 -50
  299. package/dist/src/browser/manager.js +0 -251
  300. package/dist/src/browser/pipeline/output.d.ts +0 -35
  301. package/dist/src/browser/pipeline/output.js +0 -74
  302. package/dist/src/browser/pipeline/runner.d.ts +0 -27
  303. package/dist/src/browser/pipeline/runner.js +0 -453
  304. package/dist/src/browser/pipeline/schema.d.ts +0 -42
  305. package/dist/src/browser/pipeline/schema.js +0 -116
  306. package/dist/src/browser/pipeline/template.d.ts +0 -30
  307. package/dist/src/browser/pipeline/template.js +0 -112
  308. package/dist/src/browser/providers/cloakbrowser.d.ts +0 -83
  309. package/dist/src/browser/providers/cloakbrowser.js +0 -612
  310. package/dist/src/browser/recipes/index.js +0 -3
  311. package/dist/src/browser/recipes/repository.d.ts +0 -13
  312. package/dist/src/browser/recipes/repository.js +0 -81
  313. package/dist/src/browser/recipes/runtime-service.d.ts +0 -6
  314. package/dist/src/browser/recipes/runtime-service.js +0 -30
  315. package/dist/src/browser/recipes/service.d.ts +0 -36
  316. package/dist/src/browser/recipes/service.js +0 -186
  317. package/dist/src/browser/recipes/types.d.ts +0 -36
  318. package/dist/src/browser/schemas.d.ts +0 -53
  319. package/dist/src/browser/schemas.js +0 -68
  320. package/dist/src/browser/session-lifecycle.d.ts +0 -40
  321. package/dist/src/browser/session-lifecycle.js +0 -136
  322. package/dist/src/browser/snapshot-helpers.d.ts +0 -24
  323. package/dist/src/browser/snapshot-helpers.js +0 -70
  324. package/dist/src/browser/stealth.d.ts +0 -46
  325. package/dist/src/browser/stealth.js +0 -163
  326. package/dist/src/gateway/hono/routes/browser-workflows.d.ts +0 -3
  327. package/dist/src/gateway/hono/routes/browser-workflows.js +0 -78
  328. /package/dist/src/browser/{actions → automations}/types.js +0 -0
  329. /package/dist/src/browser/{recipes/types.js → drivers/browser-driver.js} +0 -0
@@ -1,9 +1,3 @@
1
- const XOPC_EXT_PORT = 19820;
2
- const XOPC_EXT_HOST = "127.0.0.1";
3
- const XOPC_EXT_WS_URL = `ws://${XOPC_EXT_HOST}:${XOPC_EXT_PORT}/browser-ext`;
4
- const WS_RECONNECT_BASE_DELAY = 2e3;
5
- const WS_RECONNECT_MAX_DELAY = 5e3;
6
-
7
1
  const LOG_COLORS = {
8
2
  debug: "color: #888",
9
3
  info: "color: #3b82f6",
@@ -28,32 +22,100 @@ function createLogger(prefix) {
28
22
  };
29
23
  }
30
24
 
31
- async function sendContentMessage(tabId, message) {
25
+ const log$2 = createLogger("CDP");
26
+ const attached = /* @__PURE__ */ new Set();
27
+ function isDebuggableUrl(url) {
28
+ if (!url) return true;
29
+ return url.startsWith("http://") || url.startsWith("https://") || url === "about:blank" || url.startsWith("data:");
30
+ }
31
+ async function ensureAttached(tabId) {
32
32
  try {
33
- await chrome.tabs.sendMessage(tabId, message);
34
- } catch {
33
+ const tab = await chrome.tabs.get(tabId);
34
+ if (!isDebuggableUrl(tab.url)) {
35
+ attached.delete(tabId);
36
+ throw new Error(`Cannot debug tab ${tabId}: URL is ${tab.url ?? "unknown"}`);
37
+ }
38
+ } catch (e) {
39
+ if (e instanceof Error && e.message.startsWith("Cannot debug tab")) throw e;
40
+ attached.delete(tabId);
41
+ throw new Error(`Tab ${tabId} no longer exists`);
42
+ }
43
+ if (attached.has(tabId)) {
44
+ try {
45
+ await chrome.debugger.sendCommand({ tabId }, "Runtime.evaluate", {
46
+ expression: "1",
47
+ returnByValue: true
48
+ });
49
+ return;
50
+ } catch {
51
+ attached.delete(tabId);
52
+ }
53
+ }
54
+ const MAX_RETRIES = 3;
55
+ const RETRY_DELAY = 500;
56
+ let lastError = "";
57
+ for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
58
+ try {
59
+ try {
60
+ await chrome.debugger.detach({ tabId });
61
+ } catch {
62
+ }
63
+ await chrome.debugger.attach({ tabId }, "1.3");
64
+ attached.add(tabId);
65
+ await chrome.debugger.sendCommand({ tabId }, "Runtime.enable", {});
66
+ await chrome.debugger.sendCommand({ tabId }, "Page.enable", {});
67
+ log$2.debug("Attached to tab", { tabId, attempt });
68
+ return;
69
+ } catch (e) {
70
+ lastError = e instanceof Error ? e.message : String(e);
71
+ if (attempt < MAX_RETRIES) {
72
+ await new Promise((r) => setTimeout(r, RETRY_DELAY));
73
+ }
74
+ }
35
75
  }
76
+ throw new Error(`Failed to attach debugger to tab ${tabId}: ${lastError}`);
36
77
  }
37
- async function showOverlayOnTab(tabId, status) {
38
- await sendContentMessage(tabId, { type: "content/show-overlay", status });
78
+ async function sendCommand(tabId, method, params) {
79
+ await ensureAttached(tabId);
80
+ return chrome.debugger.sendCommand({ tabId }, method, params ?? {});
39
81
  }
40
- async function hideOverlayOnTab(tabId) {
41
- await sendContentMessage(tabId, { type: "content/hide-overlay" });
82
+ async function evaluate(tabId, expression) {
83
+ const result = await sendCommand(tabId, "Runtime.evaluate", {
84
+ expression,
85
+ returnByValue: true,
86
+ awaitPromise: true,
87
+ userGesture: true
88
+ });
89
+ if (result.exceptionDetails) {
90
+ throw new Error(`JS evaluation failed: ${result.exceptionDetails.text ?? "unknown error"}`);
91
+ }
92
+ return result.result?.value;
42
93
  }
43
- async function showClickRippleOnTab(tabId, x, y) {
44
- await sendContentMessage(tabId, { type: "content/show-click-ripple", x, y });
94
+ async function captureScreenshot(tabId, options) {
95
+ const result = await sendCommand(tabId, "Page.captureScreenshot", {
96
+ format: options?.format,
97
+ quality: options?.quality
98
+ });
99
+ return result.data;
45
100
  }
46
- async function showHoverHighlightOnTab(tabId, selector) {
47
- await sendContentMessage(tabId, { type: "content/show-hover-highlight", selector });
101
+ async function dispatchInput(tabId, type, params) {
102
+ await sendCommand(tabId, `Input.${type}`, params);
48
103
  }
49
- async function showInputFlashOnTab(tabId, selector) {
50
- await sendContentMessage(tabId, { type: "content/show-input-flash", selector });
104
+ chrome.debugger.onDetach.addListener((source) => {
105
+ if (source.tabId) attached.delete(source.tabId);
106
+ });
107
+
108
+ async function sendContentMessage(tabId, message) {
109
+ try {
110
+ await chrome.tabs.sendMessage(tabId, message);
111
+ } catch {
112
+ }
51
113
  }
52
- async function showScrollIndicatorOnTab(tabId, direction) {
53
- await sendContentMessage(tabId, { type: "content/show-scroll-indicator", direction });
114
+ async function hideOverlayOnTab(tabId) {
115
+ await sendContentMessage(tabId, { type: "content/hide-overlay" });
54
116
  }
55
117
 
56
- const log$2 = createLogger("SessionManager");
118
+ const log$1 = createLogger("SessionManager");
57
119
  const XOPC_TAB_GROUP_TITLE = "xopc";
58
120
  const automationSessions = /* @__PURE__ */ new Map();
59
121
  const WINDOW_IDLE_TIMEOUT = 12e4;
@@ -75,7 +137,7 @@ function resetWindowIdleTimer(workspace = DEFAULT_WORKSPACE) {
75
137
  } catch {
76
138
  }
77
139
  automationSessions.delete(workspace);
78
- log$2.info("Automation tab group cleared due to idle timeout", { workspace });
140
+ log$1.info("Automation tab group cleared due to idle timeout", { workspace });
79
141
  }, WINDOW_IDLE_TIMEOUT);
80
142
  }
81
143
  async function waitForTabLoad(tabId, timeoutMs = 3e4) {
@@ -153,7 +215,7 @@ async function getOrCreateAutomationWindow(workspace = DEFAULT_WORKSPACE) {
153
215
  };
154
216
  automationSessions.set(workspace, session);
155
217
  resetWindowIdleTimer(workspace);
156
- log$2.info("Created automation tab group", { workspace, windowId, groupId, tabId: tab.id });
218
+ log$1.info("Created automation tab group", { workspace, windowId, groupId, tabId: tab.id });
157
219
  return session;
158
220
  }
159
221
  async function getActiveTabId(workspace = DEFAULT_WORKSPACE) {
@@ -197,7 +259,7 @@ async function closeSession(workspace = DEFAULT_WORKSPACE) {
197
259
  });
198
260
  }
199
261
  automationSessions.delete(workspace);
200
- log$2.info("Closed automation session", { workspace });
262
+ log$1.info("Closed automation session", { workspace });
201
263
  }
202
264
  async function closeAllSessions() {
203
265
  for (const workspace of [...automationSessions.keys()]) {
@@ -205,719 +267,296 @@ async function closeAllSessions() {
205
267
  }
206
268
  }
207
269
 
208
- async function handleOpen(cmd) {
209
- const url = cmd.args?.url;
210
- if (!url) return { id: cmd.id, ok: false, error: "Missing required arg: url" };
211
- const tabId = cmd.tabId ?? await getActiveTabId();
212
- await showOverlayOnTab(tabId, "operating");
270
+ const observations = /* @__PURE__ */ new Map();
271
+ async function executeBrowserCommand(command) {
272
+ const startedAt = Date.now();
213
273
  try {
214
- await chrome.tabs.update(tabId, { url });
215
- await waitForTabLoad(tabId, cmd.timeout ?? 3e4);
216
- resetWindowIdleTimer();
217
- const tab = await chrome.tabs.get(tabId);
218
- await hideOverlayOnTab(tabId);
219
- if (tab.groupId != null) {
220
- await chrome.tabGroups.update(tab.groupId, { collapsed: false }).catch(() => {
221
- });
222
- }
223
- await chrome.tabs.update(tabId, { active: true }).catch(() => {
224
- });
274
+ const result = await execute(command.input, command.timeoutMs, command.visualFallback, startedAt);
275
+ return { id: command.id, result };
276
+ } catch (error) {
225
277
  return {
226
- id: cmd.id,
227
- ok: true,
228
- data: { url: tab.url, title: tab.title, tabId }
229
- };
230
- } catch (e) {
231
- await hideOverlayOnTab(tabId);
232
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
233
- }
234
- }
235
- async function handleNavigate(cmd) {
236
- return handleOpen(cmd);
237
- }
238
- async function handleReload(cmd) {
239
- const tabId = cmd.tabId ?? await getActiveTabId();
240
- await showOverlayOnTab(tabId, "operating");
241
- try {
242
- await chrome.tabs.reload(tabId);
243
- await waitForTabLoad(tabId, cmd.timeout ?? 3e4);
244
- resetWindowIdleTimer();
245
- const tab = await chrome.tabs.get(tabId);
246
- await hideOverlayOnTab(tabId);
247
- return { id: cmd.id, ok: true, data: { url: tab.url, title: tab.title } };
248
- } catch (e) {
249
- await hideOverlayOnTab(tabId);
250
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
251
- }
252
- }
253
- async function handleBack(cmd) {
254
- const tabId = cmd.tabId ?? await getActiveTabId();
255
- try {
256
- await chrome.tabs.goBack(tabId);
257
- await waitForTabLoad(tabId, cmd.timeout ?? 1e4);
258
- const tab = await chrome.tabs.get(tabId);
259
- return { id: cmd.id, ok: true, data: { url: tab.url, title: tab.title } };
260
- } catch (e) {
261
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
262
- }
263
- }
264
- async function handleForward(cmd) {
265
- const tabId = cmd.tabId ?? await getActiveTabId();
266
- try {
267
- await chrome.tabs.goForward(tabId);
268
- await waitForTabLoad(tabId, cmd.timeout ?? 1e4);
269
- const tab = await chrome.tabs.get(tabId);
270
- return { id: cmd.id, ok: true, data: { url: tab.url, title: tab.title } };
271
- } catch (e) {
272
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
273
- }
274
- }
275
- async function handleGetUrl(cmd) {
276
- const tabId = cmd.tabId ?? await getActiveTabId();
277
- try {
278
- const tab = await chrome.tabs.get(tabId);
279
- return { id: cmd.id, ok: true, data: { url: tab.url } };
280
- } catch (e) {
281
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
282
- }
283
- }
284
- async function handleGetTitle(cmd) {
285
- const tabId = cmd.tabId ?? await getActiveTabId();
286
- try {
287
- const tab = await chrome.tabs.get(tabId);
288
- return { id: cmd.id, ok: true, data: { title: tab.title } };
289
- } catch (e) {
290
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
291
- }
292
- }
293
-
294
- const log$1 = createLogger("CDP");
295
- const attached = /* @__PURE__ */ new Set();
296
- function isDebuggableUrl(url) {
297
- if (!url) return true;
298
- return url.startsWith("http://") || url.startsWith("https://") || url === "about:blank" || url.startsWith("data:");
299
- }
300
- async function ensureAttached(tabId) {
301
- try {
302
- const tab = await chrome.tabs.get(tabId);
303
- if (!isDebuggableUrl(tab.url)) {
304
- attached.delete(tabId);
305
- throw new Error(`Cannot debug tab ${tabId}: URL is ${tab.url ?? "unknown"}`);
306
- }
307
- } catch (e) {
308
- if (e instanceof Error && e.message.startsWith("Cannot debug tab")) throw e;
309
- attached.delete(tabId);
310
- throw new Error(`Tab ${tabId} no longer exists`);
311
- }
312
- if (attached.has(tabId)) {
313
- try {
314
- await chrome.debugger.sendCommand({ tabId }, "Runtime.evaluate", {
315
- expression: "1",
316
- returnByValue: true
317
- });
318
- return;
319
- } catch {
320
- attached.delete(tabId);
321
- }
322
- }
323
- const MAX_RETRIES = 3;
324
- const RETRY_DELAY = 500;
325
- let lastError = "";
326
- for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
327
- try {
328
- try {
329
- await chrome.debugger.detach({ tabId });
330
- } catch {
331
- }
332
- await chrome.debugger.attach({ tabId }, "1.3");
333
- attached.add(tabId);
334
- await chrome.debugger.sendCommand({ tabId }, "Runtime.enable", {});
335
- await chrome.debugger.sendCommand({ tabId }, "Page.enable", {});
336
- log$1.debug("Attached to tab", { tabId, attempt });
337
- return;
338
- } catch (e) {
339
- lastError = e instanceof Error ? e.message : String(e);
340
- if (attempt < MAX_RETRIES) {
341
- await new Promise((r) => setTimeout(r, RETRY_DELAY));
278
+ id: command.id,
279
+ result: {
280
+ ok: false,
281
+ error: {
282
+ code: /timeout/i.test(String(error)) ? "TIMEOUT" : "DRIVER_UNAVAILABLE",
283
+ message: error instanceof Error ? error.message : String(error)
284
+ }
342
285
  }
343
- }
344
- }
345
- throw new Error(`Failed to attach debugger to tab ${tabId}: ${lastError}`);
346
- }
347
- async function sendCommand(tabId, method, params) {
348
- await ensureAttached(tabId);
349
- return chrome.debugger.sendCommand({ tabId }, method, params ?? {});
350
- }
351
- async function evaluate(tabId, expression) {
352
- const result = await sendCommand(tabId, "Runtime.evaluate", {
353
- expression,
354
- returnByValue: true,
355
- awaitPromise: true,
356
- userGesture: true
357
- });
358
- if (result.exceptionDetails) {
359
- throw new Error(`JS evaluation failed: ${result.exceptionDetails.text ?? "unknown error"}`);
286
+ };
360
287
  }
361
- return result.result?.value;
362
288
  }
363
- async function captureScreenshot(tabId, options) {
364
- if (options?.fullPage) {
365
- const metrics = await sendCommand(tabId, "Page.getLayoutMetrics", {});
366
- if (metrics.contentSize) {
367
- await sendCommand(tabId, "Emulation.setDeviceMetricsOverride", {
368
- width: Math.ceil(metrics.contentSize.width),
369
- height: Math.ceil(metrics.contentSize.height),
370
- deviceScaleFactor: 1,
371
- mobile: false
372
- });
289
+ async function execute(input, timeoutMs, visualFallback, startedAt) {
290
+ const sessionId = input.sessionId;
291
+ if (!sessionId) return fail("INVALID_INPUT", "Extension actions require a session id.");
292
+ if (input.action === "close") {
293
+ await closeSession(sessionId);
294
+ observations.delete(sessionId);
295
+ return success("close", "read", startedAt);
296
+ }
297
+ if (input.action === "observe") {
298
+ const mode = input.visual === "always" ? "always" : input.visual === "never" || !visualFallback ? "never" : "auto";
299
+ const observation2 = await observe(sessionId, mode);
300
+ return success("observe", "read", startedAt, observation2);
301
+ }
302
+ if (input.action === "navigate") {
303
+ const tabId2 = await getActiveTabId(sessionId);
304
+ await chrome.tabs.update(tabId2, { url: input.url });
305
+ await waitForTabLoad(tabId2, timeoutMs);
306
+ resetWindowIdleTimer(sessionId);
307
+ const observation2 = await observe(sessionId, visualFallback ? "auto" : "never");
308
+ return verified(input, observation2, startedAt, "read");
309
+ }
310
+ if (input.action === "tabs") return tabs(sessionId, input, startedAt, timeoutMs);
311
+ if (input.action === "sequence") return fail("INVALID_INPUT", "Sequences are expanded by the browser runtime.");
312
+ const current = observations.get(sessionId);
313
+ if (!current || current.revision !== input.revision) {
314
+ return fail("STALE_OBSERVATION", "The page changed. Observe it again.", await observe(sessionId, "never"));
315
+ }
316
+ const tabId = await getActiveTabId(sessionId);
317
+ const ref = "ref" in input ? input.ref : void 0;
318
+ if (ref && !current.nodes.some((node) => node.ref === ref)) {
319
+ return fail("TARGET_NOT_FOUND", `Target ${ref} was not found.`);
320
+ }
321
+ if (input.action === "click") {
322
+ const point = await evaluateRef(tabId, input.ref, `(element) => {
323
+ const rect = element.getBoundingClientRect();
324
+ return { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 };
325
+ }`);
326
+ if (!point) return fail("TARGET_NOT_FOUND", `Target ${input.ref} was not found.`);
327
+ const coordinates = point;
328
+ await dispatchInput(tabId, "dispatchMouseEvent", { type: "mousePressed", ...coordinates, button: "left", clickCount: 1 });
329
+ await dispatchInput(tabId, "dispatchMouseEvent", { type: "mouseReleased", ...coordinates, button: "left", clickCount: 1 });
330
+ } else if (input.action === "fill") {
331
+ const changed = await evaluateRef(tabId, input.ref, `(element, value) => {
332
+ element.focus();
333
+ const prototype = element instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
334
+ const setter = Object.getOwnPropertyDescriptor(prototype, 'value')?.set;
335
+ setter?.call(element, value);
336
+ element.dispatchEvent(new Event('input', { bubbles: true }));
337
+ element.dispatchEvent(new Event('change', { bubbles: true }));
338
+ return true;
339
+ }`, input.value);
340
+ if (!changed) return fail("TARGET_NOT_FOUND", `Target ${input.ref} was not found.`);
341
+ if (input.submit) await pressKey(tabId, "Enter");
342
+ } else if (input.action === "select") {
343
+ const changed = await evaluateRef(tabId, input.ref, `(element, value) => {
344
+ element.value = value;
345
+ element.dispatchEvent(new Event('input', { bubbles: true }));
346
+ element.dispatchEvent(new Event('change', { bubbles: true }));
347
+ return true;
348
+ }`, input.value);
349
+ if (!changed) return fail("TARGET_NOT_FOUND", `Target ${input.ref} was not found.`);
350
+ } else if (input.action === "press") {
351
+ if (input.ref) await evaluateRef(tabId, input.ref, "(element) => { element.focus(); return true; }");
352
+ await pressKey(tabId, input.key);
353
+ } else if (input.action === "scroll") {
354
+ if (input.ref) {
355
+ await evaluateRef(tabId, input.ref, "(element, deltaY) => { element.scrollBy({ top: deltaY }); return true; }", input.deltaY);
356
+ } else {
357
+ await dispatchInput(tabId, "dispatchMouseEvent", { type: "mouseWheel", x: 640, y: 360, deltaX: 0, deltaY: input.deltaY });
373
358
  }
374
- }
375
- const result = await sendCommand(tabId, "Page.captureScreenshot", {
376
- format: options?.format,
377
- quality: options?.quality
378
- });
379
- if (options?.fullPage) {
380
- await sendCommand(tabId, "Emulation.clearDeviceMetricsOverride", {});
381
- }
382
- return result.data;
383
- }
384
- async function dispatchInput(tabId, type, params) {
385
- await sendCommand(tabId, `Input.${type}`, params);
386
- }
387
- chrome.debugger.onDetach.addListener((source) => {
388
- if (source.tabId) attached.delete(source.tabId);
389
- });
390
-
391
- async function handleMouseMove(cmd) {
392
- const tabId = cmd.tabId ?? await getActiveTabId();
393
- const x = cmd.args?.x ?? 0;
394
- const y = cmd.args?.y ?? 0;
395
- try {
396
- await dispatchInput(tabId, "dispatchMouseEvent", {
397
- type: "mouseMoved",
398
- x,
399
- y
400
- });
401
- return { id: cmd.id, ok: true, data: { x, y } };
402
- } catch (e) {
403
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
404
- }
405
- }
406
- async function handleMouseClick(cmd) {
407
- const tabId = cmd.tabId ?? await getActiveTabId();
408
- const x = cmd.args?.x ?? 0;
409
- const y = cmd.args?.y ?? 0;
410
- const button = cmd.args?.button ?? "left";
411
- const clickCount = cmd.args?.clickCount ?? 1;
412
- try {
413
- await dispatchInput(tabId, "dispatchMouseEvent", {
414
- type: "mousePressed",
415
- x,
416
- y,
417
- button,
418
- clickCount
419
- });
420
- await dispatchInput(tabId, "dispatchMouseEvent", {
421
- type: "mouseReleased",
422
- x,
423
- y,
424
- button,
425
- clickCount
426
- });
427
- await showClickRippleOnTab(tabId, x, y);
428
- return { id: cmd.id, ok: true, data: { x, y, button } };
429
- } catch (e) {
430
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
431
- }
432
- }
433
- async function handleMouseWheel(cmd) {
434
- const tabId = cmd.tabId ?? await getActiveTabId();
435
- const x = cmd.args?.x ?? 0;
436
- const y = cmd.args?.y ?? 0;
437
- const deltaX = cmd.args?.deltaX ?? 0;
438
- const deltaY = cmd.args?.deltaY ?? 0;
439
- try {
440
- await dispatchInput(tabId, "dispatchMouseEvent", {
441
- type: "mouseWheel",
442
- x,
443
- y,
444
- deltaX,
445
- deltaY
446
- });
447
- return { id: cmd.id, ok: true, data: { deltaX, deltaY } };
448
- } catch (e) {
449
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
450
- }
451
- }
452
- async function handleKeyboardPress(cmd) {
453
- const tabId = cmd.tabId ?? await getActiveTabId();
454
- const key = cmd.args?.key;
455
- if (!key) return { id: cmd.id, ok: false, error: "Missing required arg: key" };
456
- try {
457
- await dispatchInput(tabId, "dispatchKeyEvent", {
458
- type: "keyDown",
459
- key,
460
- windowsVirtualKeyCode: key.length === 1 ? key.charCodeAt(0) : 0
461
- });
462
- await dispatchInput(tabId, "dispatchKeyEvent", {
463
- type: "keyUp",
464
- key,
465
- windowsVirtualKeyCode: key.length === 1 ? key.charCodeAt(0) : 0
359
+ } else if (input.action === "wait") {
360
+ await wait(tabId, input, timeoutMs);
361
+ } else if (input.action === "upload") {
362
+ const document = await sendCommand(tabId, "DOM.getDocument", { depth: 1 });
363
+ const query = await sendCommand(tabId, "DOM.querySelectorAll", {
364
+ nodeId: document.root.nodeId,
365
+ selector: `[data-xopc-ref="${input.ref}"]`
466
366
  });
467
- return { id: cmd.id, ok: true, data: { key } };
468
- } catch (e) {
469
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
470
- }
471
- }
472
- async function handleKeyboardType(cmd) {
473
- const tabId = cmd.tabId ?? await getActiveTabId();
474
- const text = cmd.args?.text;
475
- if (!text) return { id: cmd.id, ok: false, error: "Missing required arg: text" };
476
- try {
477
- for (const char of text) {
478
- await dispatchInput(tabId, "dispatchKeyEvent", {
479
- type: "char",
480
- text: char
481
- });
482
- }
483
- return { id: cmd.id, ok: true, data: { text } };
484
- } catch (e) {
485
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
486
- }
487
- }
488
- async function handleType(cmd) {
489
- const tabId = cmd.tabId ?? await getActiveTabId();
490
- const selector = cmd.args?.selector;
491
- const text = cmd.args?.text;
492
- if (!text) return { id: cmd.id, ok: false, error: "Missing required arg: text" };
493
- try {
494
- if (selector) {
495
- await evaluate(tabId, `document.querySelector('${selector}')?.focus()`);
496
- await showInputFlashOnTab(tabId, selector);
497
- }
498
- await sendCommand(tabId, "Input.insertText", { text });
499
- if (cmd.args?.pressEnter) {
500
- await dispatchInput(tabId, "dispatchKeyEvent", {
501
- type: "keyDown",
502
- key: "Enter",
503
- code: "Enter",
504
- windowsVirtualKeyCode: 13
505
- });
506
- await dispatchInput(tabId, "dispatchKeyEvent", {
507
- type: "keyUp",
508
- key: "Enter",
509
- code: "Enter",
510
- windowsVirtualKeyCode: 13
511
- });
512
- }
513
- return { id: cmd.id, ok: true, data: { text, selector } };
514
- } catch (e) {
515
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
516
- }
517
- }
518
-
519
- async function handleClick(cmd) {
520
- const tabId = cmd.tabId ?? await getActiveTabId();
521
- const selector = cmd.args?.selector;
522
- const text = cmd.args?.text;
523
- try {
524
- let x, y;
525
- if (selector) {
526
- await showHoverHighlightOnTab(tabId, selector);
527
- const box = await evaluate(tabId, `(() => {
528
- const el = document.querySelector('${selector}');
529
- if (!el) return null;
530
- const r = el.getBoundingClientRect();
531
- return { x: r.x + r.width / 2, y: r.y + r.height / 2 };
532
- })()`);
533
- if (!box || typeof box !== "object") {
534
- return { id: cmd.id, ok: false, error: `Element not found: ${selector}` };
535
- }
536
- const coords = box;
537
- x = coords.x;
538
- y = coords.y;
539
- } else if (text) {
540
- const box = await evaluate(tabId, `(() => {
541
- const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
542
- while (walker.nextNode()) {
543
- if (walker.currentNode.textContent?.trim().includes('${text.replace(/'/g, "\\'")}')) {
544
- const el = walker.currentNode.parentElement;
545
- if (el) { const r = el.getBoundingClientRect(); return { x: r.x + r.width / 2, y: r.y + r.height / 2 }; }
546
- }
547
- }
548
- return null;
549
- })()`);
550
- if (!box || typeof box !== "object") {
551
- return { id: cmd.id, ok: false, error: `Element with text not found: ${text}` };
367
+ if (query.nodeIds.length !== 1) return fail("TARGET_NOT_FOUND", `Target ${input.ref} was not uniquely found.`);
368
+ await sendCommand(tabId, "DOM.setFileInputFiles", { nodeId: query.nodeIds[0], files: input.paths });
369
+ }
370
+ await new Promise((resolve) => setTimeout(resolve, 200));
371
+ const observation = await observe(sessionId, visualFallback ? "auto" : "never");
372
+ return verified(input, observation, startedAt, "draft");
373
+ }
374
+ async function observe(sessionId, visual) {
375
+ const tabId = await getActiveTabId(sessionId);
376
+ const tab = await chrome.tabs.get(tabId);
377
+ const raw = await evaluate(tabId, `(() => {
378
+ const selector = 'a[href],button,input:not([type="hidden"]),textarea,select,summary,[contenteditable="true"],[role="button"],[role="link"],[role="checkbox"],[role="radio"],[role="combobox"],[role="menuitem"],[role="option"],[role="tab"],[tabindex]:not([tabindex="-1"])';
379
+ globalThis.__xopcRefCounter = globalThis.__xopcRefCounter || 0;
380
+ const nodes = [];
381
+ const seenRefs = new Set();
382
+ for (const element of Array.from(document.querySelectorAll(selector)).slice(0, 1500)) {
383
+ const rect = element.getBoundingClientRect();
384
+ if (rect.width <= 0 || rect.height <= 0) continue;
385
+ const style = getComputedStyle(element);
386
+ if (style.visibility === 'hidden' || style.display === 'none') continue;
387
+ let ref = element.getAttribute('data-xopc-ref');
388
+ if (!/^xopc-ed+$/.test(ref || '') || seenRefs.has(ref)) {
389
+ ref = 'xopc-e' + (++globalThis.__xopcRefCounter);
390
+ element.setAttribute('data-xopc-ref', ref);
552
391
  }
553
- const coords = box;
554
- x = coords.x;
555
- y = coords.y;
556
- } else if (cmd.args?.x !== void 0 && cmd.args?.y !== void 0) {
557
- x = cmd.args.x;
558
- y = cmd.args.y;
559
- } else {
560
- return { id: cmd.id, ok: false, error: "Click requires selector, text, or x/y coordinates" };
392
+ seenRefs.add(ref);
393
+ const tag = element.tagName.toLowerCase();
394
+ const role = element.getAttribute('role') || (tag === 'a' ? 'link' : tag === 'button' || tag === 'summary' ? 'button' : tag === 'select' ? 'combobox' : tag === 'input' && element.type === 'checkbox' ? 'checkbox' : tag === 'input' && element.type === 'radio' ? 'radio' : 'textbox');
395
+ const label = element.labels?.[0]?.innerText || '';
396
+ const name = (element.getAttribute('aria-label') || label || element.getAttribute('alt') || element.getAttribute('placeholder') || element.innerText || element.getAttribute('title') || '').trim().replace(/\\s+/g, ' ').slice(0, 240);
397
+ const states = [];
398
+ const autocomplete = (element.getAttribute('autocomplete') || '').toLowerCase();
399
+ const sensitive = element.type === 'password' || /^(cc-|one-time-code$|transaction-)/.test(autocomplete);
400
+ if (element.disabled) states.push('disabled');
401
+ if (element.checked) states.push('checked');
402
+ if (document.activeElement === element) states.push('focused');
403
+ if (element.getAttribute('aria-expanded') === 'true') states.push('expanded');
404
+ if (sensitive) states.push('sensitive');
405
+ if ((tag === 'button' || tag === 'input') && element.type === 'submit') states.push('submit');
406
+ nodes.push({ ref, role, name, value: !sensitive && 'value' in element ? String(element.value || '').slice(0, 500) : undefined, states, bounds: { x: rect.x, y: rect.y, width: rect.width, height: rect.height } });
407
+ if (nodes.length >= 500) break;
561
408
  }
562
- await dispatchInput(tabId, "dispatchMouseEvent", { type: "mousePressed", x, y, button: "left", clickCount: 1 });
563
- await dispatchInput(tabId, "dispatchMouseEvent", { type: "mouseReleased", x, y, button: "left", clickCount: 1 });
564
- await showClickRippleOnTab(tabId, x, y);
565
- return { id: cmd.id, ok: true, data: { x, y, selector, text } };
566
- } catch (e) {
567
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
568
- }
569
- }
570
- async function handleScroll(cmd) {
571
- const tabId = cmd.tabId ?? await getActiveTabId();
572
- const deltaY = cmd.args?.deltaY ?? (cmd.args?.direction === "up" ? -300 : 300);
573
- try {
574
- await dispatchInput(tabId, "dispatchMouseEvent", {
575
- type: "mouseWheel",
576
- x: 640,
577
- y: 360,
578
- deltaX: 0,
579
- deltaY
580
- });
581
- await showScrollIndicatorOnTab(tabId, deltaY > 0 ? "down" : "up");
582
- return { id: cmd.id, ok: true, data: { deltaY } };
583
- } catch (e) {
584
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
585
- }
586
- }
587
- async function handleEvaluate(cmd) {
588
- const tabId = cmd.tabId ?? await getActiveTabId();
589
- const expression = cmd.args?.expression;
590
- if (!expression) return { id: cmd.id, ok: false, error: "Missing required arg: expression" };
591
- try {
592
- const result = await evaluate(tabId, expression);
593
- return { id: cmd.id, ok: true, data: { result } };
594
- } catch (e) {
595
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
596
- }
409
+ return { marker: location.href + '|' + performance.timeOrigin, nodes, hasVisualSurface: !!document.querySelector('canvas,embed[type="application/pdf"],object[type="application/pdf"]') };
410
+ })()`);
411
+ const prior = observations.get(sessionId);
412
+ const state = prior?.documentMarker === raw.marker ? { ...prior, revision: prior.revision + 1, nodes: raw.nodes } : { documentMarker: raw.marker, documentId: crypto.randomUUID(), revision: 1, nodes: raw.nodes };
413
+ const changes = diff(prior?.documentMarker === raw.marker ? prior.nodes : [], raw.nodes);
414
+ observations.set(sessionId, state);
415
+ const capture = visual === "always" || visual === "auto" && (raw.nodes.length === 0 || raw.hasVisualSurface);
416
+ const image = capture ? await captureScreenshot(tabId, { format: "jpeg", quality: 70}) : void 0;
417
+ return {
418
+ sessionId,
419
+ tabId: String(tabId),
420
+ revision: state.revision,
421
+ documentId: state.documentId,
422
+ url: tab.url ?? "",
423
+ title: tab.title ?? "",
424
+ focused: raw.nodes.find((node) => node.states.includes("focused"))?.ref,
425
+ nodes: raw.nodes,
426
+ changes,
427
+ visual: image ? { mimeType: "image/jpeg", data: image } : void 0
428
+ };
597
429
  }
598
- async function handleWait(cmd) {
599
- const tabId = cmd.tabId ?? await getActiveTabId();
600
- const selector = cmd.args?.selector;
601
- const timeout = cmd.timeout ?? 1e4;
602
- if (!selector) {
603
- const ms = cmd.args?.ms ?? 1e3;
604
- await new Promise((r) => setTimeout(r, ms));
605
- return { id: cmd.id, ok: true, data: { waited: ms } };
606
- }
607
- try {
608
- const start = Date.now();
609
- while (Date.now() - start < timeout) {
610
- const found = await evaluate(tabId, `!!document.querySelector('${selector}')`);
611
- if (found) return { id: cmd.id, ok: true, data: { selector, elapsed: Date.now() - start } };
612
- await new Promise((r) => setTimeout(r, 200));
430
+ async function tabs(sessionId, input, startedAt, timeoutMs) {
431
+ const session = await getOrCreateAutomationWindow(sessionId);
432
+ if (input.operation === "create") {
433
+ const created = await addTabToAutomationGroup(input.url ?? "about:blank", sessionId);
434
+ session.activeTabId = created.tabId;
435
+ if (input.url) await waitForTabLoad(created.tabId, timeoutMs);
436
+ } else if (input.operation === "activate") {
437
+ const tabId = Number(input.tabId);
438
+ if (!Number.isInteger(tabId) || !session.tabIds.includes(tabId)) return fail("TARGET_NOT_FOUND", `Tab ${input.tabId ?? ""} was not found.`);
439
+ session.activeTabId = tabId;
440
+ await chrome.tabs.update(tabId, { active: true });
441
+ } else if (input.operation === "close") {
442
+ const tabId = Number(input.tabId);
443
+ if (!Number.isInteger(tabId) || !session.tabIds.includes(tabId)) return fail("TARGET_NOT_FOUND", `Tab ${input.tabId ?? ""} was not found.`);
444
+ await chrome.tabs.remove(tabId);
445
+ session.tabIds = session.tabIds.filter((id) => id !== tabId);
446
+ session.activeTabId = session.tabIds[0] ?? null;
447
+ }
448
+ const currentTabs = await chrome.tabs.query({ groupId: session.groupId });
449
+ return {
450
+ ok: true,
451
+ receipt: {
452
+ action: "tabs",
453
+ risk: "read",
454
+ durationMs: Date.now() - startedAt,
455
+ verified: true,
456
+ tabs: currentTabs.map((tab) => ({ id: String(tab.id), url: tab.url ?? "", title: tab.title ?? "", active: tab.id === session.activeTabId }))
613
457
  }
614
- return { id: cmd.id, ok: false, error: `Timeout waiting for selector: ${selector}` };
615
- } catch (e) {
616
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
617
- }
618
- }
619
- async function handleQuerySelector(cmd) {
620
- const tabId = cmd.tabId ?? await getActiveTabId();
621
- const selector = cmd.args?.selector;
622
- if (!selector) return { id: cmd.id, ok: false, error: "Missing required arg: selector" };
623
- try {
624
- const result = await evaluate(tabId, `(() => {
625
- const el = document.querySelector('${selector}');
626
- if (!el) return null;
627
- const r = el.getBoundingClientRect();
628
- return { tag: el.tagName.toLowerCase(), text: el.textContent?.slice(0, 200), rect: { x: r.x, y: r.y, width: r.width, height: r.height } };
629
- })()`);
630
- return { id: cmd.id, ok: true, data: result };
631
- } catch (e) {
632
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
633
- }
634
- }
635
-
636
- async function handleState(cmd) {
637
- const tabId = cmd.tabId ?? await getActiveTabId();
638
- try {
639
- const tab = await chrome.tabs.get(tabId);
640
- return {
641
- id: cmd.id,
642
- ok: true,
643
- data: { url: tab.url, title: tab.title, tabId, status: tab.status }
644
- };
645
- } catch (e) {
646
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
647
- }
648
- }
649
- async function handleSnapshot(cmd) {
650
- const tabId = cmd.tabId ?? await getActiveTabId();
651
- try {
652
- const accessibility = await sendCommand(tabId, "Accessibility.getFullAXTree", {});
653
- const nodes = accessibility.nodes?.slice(0, 200).map((n) => ({
654
- role: n.role?.value,
655
- name: n.name?.value
656
- })).filter((n) => n.role && n.name) ?? [];
657
- const tab = await chrome.tabs.get(tabId);
658
- return {
659
- id: cmd.id,
660
- ok: true,
661
- data: { url: tab.url, title: tab.title, nodes, nodeCount: nodes.length }
662
- };
663
- } catch (e) {
664
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
665
- }
666
- }
667
- async function handleScreenshot(cmd) {
668
- const tabId = cmd.tabId ?? await getActiveTabId();
669
- const fullPage = cmd.args?.fullPage ?? cmd.args?.full_page ?? false;
670
- try {
671
- const base64 = await captureScreenshot(tabId, {
672
- format: "png",
673
- fullPage
674
- });
675
- return { id: cmd.id, ok: true, data: { base64, format: "png", fullPage } };
676
- } catch (e) {
677
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
678
- }
679
- }
680
- async function handleGetViewportSize(cmd) {
681
- const tabId = cmd.tabId ?? await getActiveTabId();
682
- try {
683
- const size = await evaluate(tabId, `({ width: window.innerWidth, height: window.innerHeight })`);
684
- return { id: cmd.id, ok: true, data: size };
685
- } catch (e) {
686
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
687
- }
688
- }
689
- async function handleSetViewportSize(cmd) {
690
- const tabId = cmd.tabId ?? await getActiveTabId();
691
- const width = cmd.args?.width ?? 1280;
692
- const height = cmd.args?.height ?? 720;
693
- try {
694
- await sendCommand(tabId, "Emulation.setDeviceMetricsOverride", {
695
- width,
696
- height,
697
- deviceScaleFactor: 1,
698
- mobile: false
699
- });
700
- return { id: cmd.id, ok: true, data: { width, height } };
701
- } catch (e) {
702
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
703
- }
704
- }
705
- async function handleClose(cmd) {
706
- try {
707
- await closeSession();
708
- return { id: cmd.id, ok: true, data: { closed: true } };
709
- } catch (e) {
710
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
711
- }
712
- }
713
- async function handleNewTab(cmd) {
714
- try {
715
- const url = cmd.args?.url ?? "about:blank";
716
- const { tabId } = await addTabToAutomationGroup(url);
717
- return { id: cmd.id, ok: true, data: { tabId, url } };
718
- } catch (e) {
719
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
720
- }
458
+ };
721
459
  }
722
- async function handleListTabs(cmd) {
723
- try {
724
- const sess = automationSessions.get(DEFAULT_WORKSPACE);
725
- if (!sess) {
726
- return { id: cmd.id, ok: true, data: { tabs: [] } };
460
+ async function wait(tabId, input, defaultTimeout) {
461
+ const timeout = input.timeoutMs ?? defaultTimeout;
462
+ const deadline = Date.now() + timeout;
463
+ while (Date.now() < deadline) {
464
+ if (input.condition === "page_idle") {
465
+ const tab = await chrome.tabs.get(tabId);
466
+ if (tab.status === "complete") return;
467
+ } else if (input.condition === "text") {
468
+ const found = await evaluate(tabId, `document.body?.innerText.includes(${JSON.stringify(input.value ?? "")})`);
469
+ if (found) return;
470
+ } else if (input.ref) {
471
+ const visible = await evaluateRef(tabId, input.ref, `(element) => {
472
+ const rect = element.getBoundingClientRect();
473
+ const style = getComputedStyle(element);
474
+ return rect.width > 0 && rect.height > 0 && style.visibility !== 'hidden' && style.display !== 'none';
475
+ }`);
476
+ if (input.condition === "visible" && visible || input.condition === "hidden" && !visible) return;
727
477
  }
728
- const tabs = await chrome.tabs.query({ groupId: sess.groupId });
729
- return {
730
- id: cmd.id,
731
- ok: true,
732
- data: {
733
- tabs: tabs.map((t) => ({ id: t.id, url: t.url, title: t.title, active: t.active }))
734
- }
735
- };
736
- } catch (e) {
737
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
738
- }
739
- }
740
- async function handleGetCookies(cmd) {
741
- const url = cmd.args?.url;
742
- try {
743
- const cookies = await chrome.cookies.getAll(url ? { url } : {});
744
- return { id: cmd.id, ok: true, data: { cookies: cookies.slice(0, 100) } };
745
- } catch (e) {
746
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
747
- }
748
- }
749
- async function handleDialog(cmd) {
750
- const tabId = cmd.tabId ?? await getActiveTabId();
751
- const accept = cmd.args?.accept ?? true;
752
- const promptText = cmd.args?.text;
753
- try {
754
- await sendCommand(tabId, "Page.handleJavaScriptDialog", {
755
- accept,
756
- promptText
757
- });
758
- return { id: cmd.id, ok: true, data: { accept, promptText } };
759
- } catch (e) {
760
- return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
761
- }
478
+ await new Promise((resolve) => setTimeout(resolve, 100));
479
+ }
480
+ throw new Error(`Timed out waiting for ${input.condition}`);
481
+ }
482
+ async function evaluateRef(tabId, ref, functionBody, value) {
483
+ const expression = `(() => {
484
+ const matches = document.querySelectorAll('[data-xopc-ref="' + ${JSON.stringify(ref)} + '"]');
485
+ if (matches.length !== 1) return null;
486
+ const element = matches[0];
487
+ return (${functionBody})(element, ${JSON.stringify(value)});
488
+ })()`;
489
+ return evaluate(tabId, expression);
490
+ }
491
+ async function pressKey(tabId, key) {
492
+ await dispatchInput(tabId, "dispatchKeyEvent", { type: "keyDown", key });
493
+ await dispatchInput(tabId, "dispatchKeyEvent", { type: "keyUp", key });
494
+ }
495
+ function verified(input, observation, startedAt, risk) {
496
+ const expectation = "expect" in input ? input.expect : void 0;
497
+ const pageText = observation.nodes.map((node) => `${node.name}
498
+ ${node.value ?? ""}`).join("\n");
499
+ const failure = expectation?.urlIncludes && !observation.url.includes(expectation.urlIncludes) ? `URL does not include ${expectation.urlIncludes}` : expectation?.titleIncludes && !observation.title.includes(expectation.titleIncludes) ? `Title does not include ${expectation.titleIncludes}` : expectation?.textIncludes && !pageText.includes(expectation.textIncludes) ? `Page does not include ${expectation.textIncludes}` : null;
500
+ return failure ? fail("EXPECTATION_FAILED", failure, observation) : success(input.action, risk, startedAt, observation);
501
+ }
502
+ function success(action, risk, startedAt, observation) {
503
+ return { ok: true, receipt: { action, risk, durationMs: Date.now() - startedAt, verified: true, observation } };
504
+ }
505
+ function fail(code, message, observation) {
506
+ return { ok: false, error: { code, message, observation } };
507
+ }
508
+ function diff(previous, next) {
509
+ const before = new Map(previous.map((node) => [node.ref, node]));
510
+ const after = new Map(next.map((node) => [node.ref, node]));
511
+ return {
512
+ added: next.filter((node) => !before.has(node.ref)),
513
+ changed: next.filter((node) => before.has(node.ref) && JSON.stringify(before.get(node.ref)) !== JSON.stringify(node)),
514
+ removed: previous.filter((node) => !after.has(node.ref)).map((node) => node.ref)
515
+ };
762
516
  }
763
517
 
518
+ const XOPC_EXT_PORT = 19820;
519
+ const XOPC_EXT_HOST = "127.0.0.1";
520
+ const XOPC_EXT_WS_URL = `ws://${XOPC_EXT_HOST}:${XOPC_EXT_PORT}/browser-ext`;
521
+ const WS_RECONNECT_BASE_DELAY = 2e3;
522
+ const WS_RECONNECT_MAX_DELAY = 5e3;
523
+
764
524
  const log = createLogger("Background");
765
525
  let ws = null;
766
526
  let reconnectTimer = null;
767
527
  let reconnectDelay = WS_RECONNECT_BASE_DELAY;
768
528
  let intentionalClose = false;
769
- async function dispatchCommand(cmd) {
770
- const start = Date.now();
771
- try {
772
- let result;
773
- switch (cmd.action) {
774
- // Navigation
775
- case "open":
776
- result = await handleOpen(cmd);
777
- break;
778
- case "navigate":
779
- result = await handleNavigate(cmd);
780
- break;
781
- case "reload":
782
- result = await handleReload(cmd);
783
- break;
784
- case "back":
785
- result = await handleBack(cmd);
786
- break;
787
- case "forward":
788
- result = await handleForward(cmd);
789
- break;
790
- case "get_url":
791
- result = await handleGetUrl(cmd);
792
- break;
793
- case "get_title":
794
- result = await handleGetTitle(cmd);
795
- break;
796
- // Page
797
- case "state":
798
- result = await handleState(cmd);
799
- break;
800
- case "snapshot":
801
- result = await handleSnapshot(cmd);
802
- break;
803
- case "screenshot":
804
- result = await handleScreenshot(cmd);
805
- break;
806
- case "get_viewport_size":
807
- result = await handleGetViewportSize(cmd);
808
- break;
809
- case "set_viewport_size":
810
- result = await handleSetViewportSize(cmd);
811
- break;
812
- case "close":
813
- result = await handleClose(cmd);
814
- break;
815
- case "new_tab":
816
- result = await handleNewTab(cmd);
817
- break;
818
- case "list_tabs":
819
- result = await handleListTabs(cmd);
820
- break;
821
- case "get_cookies":
822
- result = await handleGetCookies(cmd);
823
- break;
824
- case "dialog":
825
- result = await handleDialog(cmd);
826
- break;
827
- // Elements
828
- case "click":
829
- result = await handleClick(cmd);
830
- break;
831
- case "scroll":
832
- result = await handleScroll(cmd);
833
- break;
834
- case "evaluate":
835
- result = await handleEvaluate(cmd);
836
- break;
837
- case "wait":
838
- result = await handleWait(cmd);
839
- break;
840
- case "query_selector":
841
- result = await handleQuerySelector(cmd);
842
- break;
843
- // Input
844
- case "mouse_move":
845
- result = await handleMouseMove(cmd);
846
- break;
847
- case "mouse_click":
848
- result = await handleMouseClick(cmd);
849
- break;
850
- case "mouse_wheel":
851
- result = await handleMouseWheel(cmd);
852
- break;
853
- case "keys":
854
- result = await handleKeyboardPress(cmd);
855
- break;
856
- case "keyboard_type":
857
- result = await handleKeyboardType(cmd);
858
- break;
859
- case "type":
860
- result = await handleType(cmd);
861
- break;
862
- // Ping
863
- case "ping":
864
- result = {
865
- id: cmd.id,
866
- ok: true,
867
- data: {
868
- pong: true,
869
- extensionVersion: chrome.runtime.getManifest().version,
870
- timestamp: Date.now()
871
- }
872
- };
873
- break;
874
- default:
875
- result = { id: cmd.id, ok: false, error: `Unknown action: ${cmd.action}` };
876
- }
877
- result.durationMs = Date.now() - start;
878
- return result;
879
- } catch (e) {
880
- return {
881
- id: cmd.id,
882
- ok: false,
883
- error: e instanceof Error ? e.message : String(e),
884
- durationMs: Date.now() - start
885
- };
886
- }
887
- }
888
529
  function connect() {
889
- if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) {
890
- return;
891
- }
530
+ if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) return;
892
531
  intentionalClose = false;
893
- log.info("Connecting to xopc daemon...", { url: XOPC_EXT_WS_URL });
894
532
  ws = new WebSocket(XOPC_EXT_WS_URL);
895
533
  ws.onopen = () => {
896
- log.info("Connected to xopc daemon");
897
534
  reconnectDelay = WS_RECONNECT_BASE_DELAY;
898
535
  sendStatus();
536
+ log.info("Connected to xopc daemon");
899
537
  };
900
538
  ws.onmessage = async (event) => {
901
539
  try {
902
- const cmd = JSON.parse(event.data);
903
- log.debug("Received command", { action: cmd.action, id: cmd.id });
904
- const result = await dispatchCommand(cmd);
540
+ const command = JSON.parse(String(event.data));
541
+ const result = await executeBrowserCommand(command);
905
542
  ws?.send(JSON.stringify(result));
906
- } catch (e) {
907
- log.error("Failed to process message", {
908
- error: e instanceof Error ? e.message : String(e)
909
- });
543
+ } catch (error) {
544
+ log.error("Failed to process browser command", { error: error instanceof Error ? error.message : String(error) });
910
545
  }
911
546
  };
912
547
  ws.onclose = () => {
913
548
  ws = null;
914
- if (!intentionalClose) {
915
- log.warn("Disconnected, reconnecting...", { delay: reconnectDelay });
916
- scheduleReconnect();
917
- }
549
+ if (!intentionalClose) scheduleReconnect();
918
550
  };
919
- ws.onerror = () => {
551
+ }
552
+ function sendStatus() {
553
+ if (ws?.readyState !== WebSocket.OPEN) return;
554
+ const status = {
555
+ type: "status",
556
+ connected: true,
557
+ sessionCount: automationSessions.size
920
558
  };
559
+ ws.send(JSON.stringify(status));
921
560
  }
922
561
  function scheduleReconnect() {
923
562
  if (reconnectTimer) clearTimeout(reconnectTimer);
@@ -929,76 +568,27 @@ function scheduleReconnect() {
929
568
  }
930
569
  function disconnect() {
931
570
  intentionalClose = true;
932
- if (reconnectTimer) {
933
- clearTimeout(reconnectTimer);
934
- reconnectTimer = null;
935
- }
936
- if (ws) {
937
- ws.close();
938
- ws = null;
939
- }
940
- }
941
- async function sendStatus() {
942
- if (!ws || ws.readyState !== WebSocket.OPEN) return;
943
- try {
944
- const sess = automationSessions.get(DEFAULT_WORKSPACE);
945
- let activeTabId;
946
- let activeUrl;
947
- if (sess?.activeTabId != null) {
948
- const tab = await chrome.tabs.get(sess.activeTabId).catch(() => null);
949
- if (tab) {
950
- activeTabId = tab.id;
951
- activeUrl = tab.url;
952
- }
953
- }
954
- const status = {
955
- type: "status",
956
- connected: true,
957
- tabCount: sess ? sess.tabIds.length : 0,
958
- activeTabId,
959
- activeUrl
960
- };
961
- ws.send(JSON.stringify(status));
962
- } catch {
963
- }
571
+ if (reconnectTimer) clearTimeout(reconnectTimer);
572
+ reconnectTimer = null;
573
+ ws?.close();
574
+ ws = null;
964
575
  }
965
- chrome.runtime.onMessage.addListener(
966
- (message, _sender, sendResponse) => {
967
- switch (message.type) {
968
- case "popup/get-status":
969
- sendResponse({
970
- connected: ws?.readyState === WebSocket.OPEN,
971
- url: XOPC_EXT_WS_URL
972
- });
973
- break;
974
- case "popup/connect":
975
- connect();
976
- sendResponse({ ok: true });
977
- break;
978
- case "popup/disconnect":
979
- disconnect();
980
- sendResponse({ ok: true });
981
- break;
982
- case "content/heartbeat":
983
- sendResponse({ ok: true });
984
- break;
985
- default:
986
- sendResponse({ ok: false, error: `Unknown message: ${message.type}` });
987
- }
988
- return true;
989
- }
990
- );
991
- chrome.runtime.onInstalled.addListener(() => {
992
- log.info("Extension installed/updated");
993
- connect();
994
- });
995
- chrome.runtime.onStartup.addListener(() => {
996
- log.info("Browser started");
997
- connect();
576
+ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
577
+ if (message.type === "popup/get-status") sendResponse({ connected: ws?.readyState === WebSocket.OPEN, url: XOPC_EXT_WS_URL });
578
+ else if (message.type === "popup/connect") {
579
+ connect();
580
+ sendResponse({ ok: true });
581
+ } else if (message.type === "popup/disconnect") {
582
+ disconnect();
583
+ sendResponse({ ok: true });
584
+ } else if (message.type === "content/heartbeat") sendResponse({ ok: true });
585
+ else sendResponse({ ok: false, error: `Unknown message: ${message.type}` });
586
+ return true;
998
587
  });
588
+ chrome.runtime.onInstalled.addListener(connect);
589
+ chrome.runtime.onStartup.addListener(connect);
999
590
  connect();
1000
591
  self.addEventListener("beforeunload", () => {
1001
592
  disconnect();
1002
593
  void closeAllSessions();
1003
594
  });
1004
- log.info("xopc Browser Bridge service worker loaded");