opendevbrowser 0.0.12 → 0.0.16

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 (585) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +425 -42
  3. package/dist/annotate/direct-annotator.d.ts +22 -0
  4. package/dist/annotate/direct-annotator.d.ts.map +1 -0
  5. package/dist/annotate/output.d.ts +10 -0
  6. package/dist/annotate/output.d.ts.map +1 -0
  7. package/dist/browser/annotation-manager.d.ts +30 -0
  8. package/dist/browser/annotation-manager.d.ts.map +1 -0
  9. package/dist/browser/browser-manager.d.ts +397 -0
  10. package/dist/browser/browser-manager.d.ts.map +1 -0
  11. package/dist/browser/fingerprint/adapters.d.ts +26 -0
  12. package/dist/browser/fingerprint/adapters.d.ts.map +1 -0
  13. package/dist/browser/fingerprint/canary.d.ts +25 -0
  14. package/dist/browser/fingerprint/canary.d.ts.map +1 -0
  15. package/dist/browser/fingerprint/profiles.d.ts +16 -0
  16. package/dist/browser/fingerprint/profiles.d.ts.map +1 -0
  17. package/dist/browser/fingerprint/tier1-coherence.d.ts +36 -0
  18. package/dist/browser/fingerprint/tier1-coherence.d.ts.map +1 -0
  19. package/dist/browser/fingerprint/tier2-runtime.d.ts +40 -0
  20. package/dist/browser/fingerprint/tier2-runtime.d.ts.map +1 -0
  21. package/dist/browser/fingerprint/tier3-adaptive.d.ts +30 -0
  22. package/dist/browser/fingerprint/tier3-adaptive.d.ts.map +1 -0
  23. package/dist/browser/manager-types.d.ts +3 -0
  24. package/dist/browser/manager-types.d.ts.map +1 -0
  25. package/dist/browser/ops-browser-manager.d.ts +131 -0
  26. package/dist/browser/ops-browser-manager.d.ts.map +1 -0
  27. package/dist/browser/ops-client.d.ts +56 -0
  28. package/dist/browser/ops-client.d.ts.map +1 -0
  29. package/dist/browser/parallelism-governor.d.ts +31 -0
  30. package/dist/browser/parallelism-governor.d.ts.map +1 -0
  31. package/dist/browser/script-runner.d.ts +23 -0
  32. package/dist/browser/script-runner.d.ts.map +1 -0
  33. package/dist/browser/session-store.d.ts +63 -0
  34. package/dist/browser/session-store.d.ts.map +1 -0
  35. package/dist/browser/target-manager.d.ts +36 -0
  36. package/dist/browser/target-manager.d.ts.map +1 -0
  37. package/dist/cache/chrome-locator.d.ts +2 -0
  38. package/dist/cache/chrome-locator.d.ts.map +1 -0
  39. package/dist/cache/downloader.d.ts +6 -0
  40. package/dist/cache/downloader.d.ts.map +1 -0
  41. package/dist/cache/paths.d.ts +9 -0
  42. package/dist/cache/paths.d.ts.map +1 -0
  43. package/dist/chunk-7W3SPXIB.js +166 -0
  44. package/dist/chunk-7W3SPXIB.js.map +1 -0
  45. package/dist/chunk-ST7CO5FA.js +18668 -0
  46. package/dist/chunk-ST7CO5FA.js.map +1 -0
  47. package/dist/cli/args.d.ts +25 -0
  48. package/dist/cli/args.d.ts.map +1 -0
  49. package/dist/cli/client.d.ts +2 -0
  50. package/dist/cli/client.d.ts.map +1 -0
  51. package/dist/cli/commands/annotate.d.ts +27 -0
  52. package/dist/cli/commands/annotate.d.ts.map +1 -0
  53. package/dist/cli/commands/artifacts.d.ts +24 -0
  54. package/dist/cli/commands/artifacts.d.ts.map +1 -0
  55. package/dist/cli/commands/daemon.d.ts +35 -0
  56. package/dist/cli/commands/daemon.d.ts.map +1 -0
  57. package/dist/cli/commands/devtools/console-poll.d.ts +7 -0
  58. package/dist/cli/commands/devtools/console-poll.d.ts.map +1 -0
  59. package/dist/cli/commands/devtools/debug-trace-snapshot.d.ts +20 -0
  60. package/dist/cli/commands/devtools/debug-trace-snapshot.d.ts.map +1 -0
  61. package/dist/cli/commands/devtools/network-poll.d.ts +7 -0
  62. package/dist/cli/commands/devtools/network-poll.d.ts.map +1 -0
  63. package/dist/cli/commands/devtools/perf.d.ts +7 -0
  64. package/dist/cli/commands/devtools/perf.d.ts.map +1 -0
  65. package/dist/cli/commands/devtools/screenshot.d.ts +17 -0
  66. package/dist/cli/commands/devtools/screenshot.d.ts.map +1 -0
  67. package/dist/cli/commands/dom/attr.d.ts +7 -0
  68. package/dist/cli/commands/dom/attr.d.ts.map +1 -0
  69. package/dist/cli/commands/dom/checked.d.ts +7 -0
  70. package/dist/cli/commands/dom/checked.d.ts.map +1 -0
  71. package/dist/cli/commands/dom/enabled.d.ts +7 -0
  72. package/dist/cli/commands/dom/enabled.d.ts.map +1 -0
  73. package/dist/cli/commands/dom/html.d.ts +7 -0
  74. package/dist/cli/commands/dom/html.d.ts.map +1 -0
  75. package/dist/cli/commands/dom/text.d.ts +7 -0
  76. package/dist/cli/commands/dom/text.d.ts.map +1 -0
  77. package/dist/cli/commands/dom/value.d.ts +7 -0
  78. package/dist/cli/commands/dom/value.d.ts.map +1 -0
  79. package/dist/cli/commands/dom/visible.d.ts +7 -0
  80. package/dist/cli/commands/dom/visible.d.ts.map +1 -0
  81. package/dist/cli/commands/export/clone-component.d.ts +7 -0
  82. package/dist/cli/commands/export/clone-component.d.ts.map +1 -0
  83. package/dist/cli/commands/export/clone-page.d.ts +7 -0
  84. package/dist/cli/commands/export/clone-page.d.ts.map +1 -0
  85. package/dist/cli/commands/interact/check.d.ts +7 -0
  86. package/dist/cli/commands/interact/check.d.ts.map +1 -0
  87. package/dist/cli/commands/interact/click.d.ts +7 -0
  88. package/dist/cli/commands/interact/click.d.ts.map +1 -0
  89. package/dist/cli/commands/interact/hover.d.ts +7 -0
  90. package/dist/cli/commands/interact/hover.d.ts.map +1 -0
  91. package/dist/cli/commands/interact/press.d.ts +7 -0
  92. package/dist/cli/commands/interact/press.d.ts.map +1 -0
  93. package/dist/cli/commands/interact/scroll-into-view.d.ts +7 -0
  94. package/dist/cli/commands/interact/scroll-into-view.d.ts.map +1 -0
  95. package/dist/cli/commands/interact/scroll.d.ts +7 -0
  96. package/dist/cli/commands/interact/scroll.d.ts.map +1 -0
  97. package/dist/cli/commands/interact/select.d.ts +7 -0
  98. package/dist/cli/commands/interact/select.d.ts.map +1 -0
  99. package/dist/cli/commands/interact/type.d.ts +7 -0
  100. package/dist/cli/commands/interact/type.d.ts.map +1 -0
  101. package/dist/cli/commands/interact/uncheck.d.ts +7 -0
  102. package/dist/cli/commands/interact/uncheck.d.ts.map +1 -0
  103. package/dist/cli/commands/macro-resolve.d.ts +18 -0
  104. package/dist/cli/commands/macro-resolve.d.ts.map +1 -0
  105. package/dist/cli/commands/native.d.ts +82 -0
  106. package/dist/cli/commands/native.d.ts.map +1 -0
  107. package/dist/cli/commands/nav/goto.d.ts +7 -0
  108. package/dist/cli/commands/nav/goto.d.ts.map +1 -0
  109. package/dist/cli/commands/nav/snapshot.d.ts +7 -0
  110. package/dist/cli/commands/nav/snapshot.d.ts.map +1 -0
  111. package/dist/cli/commands/nav/wait.d.ts +7 -0
  112. package/dist/cli/commands/nav/wait.d.ts.map +1 -0
  113. package/dist/cli/commands/pages/close.d.ts +6 -0
  114. package/dist/cli/commands/pages/close.d.ts.map +1 -0
  115. package/dist/cli/commands/pages/list.d.ts +7 -0
  116. package/dist/cli/commands/pages/list.d.ts.map +1 -0
  117. package/dist/cli/commands/pages/open.d.ts +7 -0
  118. package/dist/cli/commands/pages/open.d.ts.map +1 -0
  119. package/dist/cli/commands/product-video.d.ts +25 -0
  120. package/dist/cli/commands/product-video.d.ts.map +1 -0
  121. package/dist/cli/commands/registry.d.ts +5 -0
  122. package/dist/cli/commands/registry.d.ts.map +1 -0
  123. package/dist/cli/commands/research.d.ts +27 -0
  124. package/dist/cli/commands/research.d.ts.map +1 -0
  125. package/dist/cli/commands/rpc.d.ts +28 -0
  126. package/dist/cli/commands/rpc.d.ts.map +1 -0
  127. package/dist/cli/commands/run.d.ts +17 -0
  128. package/dist/cli/commands/run.d.ts.map +1 -0
  129. package/dist/cli/commands/serve.d.ts +59 -0
  130. package/dist/cli/commands/serve.d.ts.map +1 -0
  131. package/dist/cli/commands/session/connect.d.ts +9 -0
  132. package/dist/cli/commands/session/connect.d.ts.map +1 -0
  133. package/dist/cli/commands/session/cookie-import.d.ts +31 -0
  134. package/dist/cli/commands/session/cookie-import.d.ts.map +1 -0
  135. package/dist/cli/commands/session/cookie-list.d.ts +17 -0
  136. package/dist/cli/commands/session/cookie-list.d.ts.map +1 -0
  137. package/dist/cli/commands/session/disconnect.d.ts +6 -0
  138. package/dist/cli/commands/session/disconnect.d.ts.map +1 -0
  139. package/dist/cli/commands/session/launch.d.ts +29 -0
  140. package/dist/cli/commands/session/launch.d.ts.map +1 -0
  141. package/dist/cli/commands/session/status.d.ts +7 -0
  142. package/dist/cli/commands/session/status.d.ts.map +1 -0
  143. package/dist/cli/commands/shopping.d.ts +25 -0
  144. package/dist/cli/commands/shopping.d.ts.map +1 -0
  145. package/dist/cli/commands/status.d.ts +19 -0
  146. package/dist/cli/commands/status.d.ts.map +1 -0
  147. package/dist/cli/commands/targets/close.d.ts +6 -0
  148. package/dist/cli/commands/targets/close.d.ts.map +1 -0
  149. package/dist/cli/commands/targets/list.d.ts +7 -0
  150. package/dist/cli/commands/targets/list.d.ts.map +1 -0
  151. package/dist/cli/commands/targets/new.d.ts +7 -0
  152. package/dist/cli/commands/targets/new.d.ts.map +1 -0
  153. package/dist/cli/commands/targets/use.d.ts +7 -0
  154. package/dist/cli/commands/targets/use.d.ts.map +1 -0
  155. package/dist/cli/commands/types.d.ts +13 -0
  156. package/dist/cli/commands/types.d.ts.map +1 -0
  157. package/dist/cli/commands/uninstall.d.ts +14 -0
  158. package/dist/cli/commands/uninstall.d.ts.map +1 -0
  159. package/dist/cli/commands/update.d.ts +7 -0
  160. package/dist/cli/commands/update.d.ts.map +1 -0
  161. package/dist/cli/daemon-autostart.d.ts +46 -0
  162. package/dist/cli/daemon-autostart.d.ts.map +1 -0
  163. package/dist/cli/daemon-client.d.ts +33 -0
  164. package/dist/cli/daemon-client.d.ts.map +1 -0
  165. package/dist/cli/daemon-commands.d.ts +7 -0
  166. package/dist/cli/daemon-commands.d.ts.map +1 -0
  167. package/dist/cli/daemon-state.d.ts +56 -0
  168. package/dist/cli/daemon-state.d.ts.map +1 -0
  169. package/dist/cli/daemon-status.d.ts +19 -0
  170. package/dist/cli/daemon-status.d.ts.map +1 -0
  171. package/dist/cli/daemon.d.ts +29 -0
  172. package/dist/cli/daemon.d.ts.map +1 -0
  173. package/dist/cli/errors.d.ts +20 -0
  174. package/dist/cli/errors.d.ts.map +1 -0
  175. package/dist/cli/help.d.ts +28 -0
  176. package/dist/cli/help.d.ts.map +1 -0
  177. package/dist/cli/index.d.ts +2 -0
  178. package/dist/cli/index.d.ts.map +1 -0
  179. package/dist/cli/index.js +4604 -769
  180. package/dist/cli/index.js.map +1 -1
  181. package/dist/cli/installers/global.d.ts +9 -0
  182. package/dist/cli/installers/global.d.ts.map +1 -0
  183. package/dist/cli/installers/local.d.ts +9 -0
  184. package/dist/cli/installers/local.d.ts.map +1 -0
  185. package/dist/cli/installers/skills.d.ts +19 -0
  186. package/dist/cli/installers/skills.d.ts.map +1 -0
  187. package/dist/cli/output.d.ts +7 -0
  188. package/dist/cli/output.d.ts.map +1 -0
  189. package/dist/cli/remote-manager.d.ts +96 -0
  190. package/dist/cli/remote-manager.d.ts.map +1 -0
  191. package/dist/cli/remote-relay.d.ts +17 -0
  192. package/dist/cli/remote-relay.d.ts.map +1 -0
  193. package/dist/cli/templates/config.d.ts +7 -0
  194. package/dist/cli/templates/config.d.ts.map +1 -0
  195. package/dist/cli/utils/config.d.ts +20 -0
  196. package/dist/cli/utils/config.d.ts.map +1 -0
  197. package/dist/cli/utils/http.d.ts +5 -0
  198. package/dist/cli/utils/http.d.ts.map +1 -0
  199. package/dist/cli/utils/parse.d.ts +8 -0
  200. package/dist/cli/utils/parse.d.ts.map +1 -0
  201. package/dist/cli/utils/skills.d.ts +12 -0
  202. package/dist/cli/utils/skills.d.ts.map +1 -0
  203. package/dist/config.d.ts +208 -0
  204. package/dist/config.d.ts.map +1 -0
  205. package/dist/core/bootstrap.d.ts +3 -0
  206. package/dist/core/bootstrap.d.ts.map +1 -0
  207. package/dist/core/index.d.ts +3 -0
  208. package/dist/core/index.d.ts.map +1 -0
  209. package/dist/core/logging.d.ts +34 -0
  210. package/dist/core/logging.d.ts.map +1 -0
  211. package/dist/core/types.d.ts +34 -0
  212. package/dist/core/types.d.ts.map +1 -0
  213. package/dist/devtools/console-tracker.d.ts +44 -0
  214. package/dist/devtools/console-tracker.d.ts.map +1 -0
  215. package/dist/devtools/exception-tracker.d.ts +42 -0
  216. package/dist/devtools/exception-tracker.d.ts.map +1 -0
  217. package/dist/devtools/network-tracker.d.ts +34 -0
  218. package/dist/devtools/network-tracker.d.ts.map +1 -0
  219. package/dist/export/css-extract.d.ts +5 -0
  220. package/dist/export/css-extract.d.ts.map +1 -0
  221. package/dist/export/dom-capture.d.ts +15 -0
  222. package/dist/export/dom-capture.d.ts.map +1 -0
  223. package/dist/export/react-emitter.d.ts +11 -0
  224. package/dist/export/react-emitter.d.ts.map +1 -0
  225. package/dist/extension-extractor.d.ts +3 -0
  226. package/dist/extension-extractor.d.ts.map +1 -0
  227. package/dist/index.d.ts +3 -4
  228. package/dist/index.d.ts.map +1 -0
  229. package/dist/index.js +1905 -262
  230. package/dist/index.js.map +1 -1
  231. package/dist/macros/execute.d.ts +44 -0
  232. package/dist/macros/execute.d.ts.map +1 -0
  233. package/dist/macros/index.d.ts +9 -0
  234. package/dist/macros/index.d.ts.map +1 -0
  235. package/dist/macros/packs/core.d.ts +3 -0
  236. package/dist/macros/packs/core.d.ts.map +1 -0
  237. package/dist/macros/registry.d.ts +48 -0
  238. package/dist/macros/registry.d.ts.map +1 -0
  239. package/dist/macros-NUBRM44Y.js +399 -0
  240. package/dist/macros-NUBRM44Y.js.map +1 -0
  241. package/dist/opendevbrowser.d.ts +3 -4
  242. package/dist/opendevbrowser.d.ts.map +1 -0
  243. package/dist/opendevbrowser.js +1905 -262
  244. package/dist/opendevbrowser.js.map +1 -1
  245. package/dist/providers/adaptive-concurrency.d.ts +42 -0
  246. package/dist/providers/adaptive-concurrency.d.ts.map +1 -0
  247. package/dist/providers/artifacts.d.ts +34 -0
  248. package/dist/providers/artifacts.d.ts.map +1 -0
  249. package/dist/providers/blocker.d.ts +47 -0
  250. package/dist/providers/blocker.d.ts.map +1 -0
  251. package/dist/providers/community/index.d.ts +44 -0
  252. package/dist/providers/community/index.d.ts.map +1 -0
  253. package/dist/providers/enrichment.d.ts +33 -0
  254. package/dist/providers/enrichment.d.ts.map +1 -0
  255. package/dist/providers/errors.d.ts +41 -0
  256. package/dist/providers/errors.d.ts.map +1 -0
  257. package/dist/providers/index.d.ts +121 -0
  258. package/dist/providers/index.d.ts.map +1 -0
  259. package/dist/providers/normalize.d.ts +39 -0
  260. package/dist/providers/normalize.d.ts.map +1 -0
  261. package/dist/providers/policy.d.ts +5 -0
  262. package/dist/providers/policy.d.ts.map +1 -0
  263. package/dist/providers/registry.d.ts +22 -0
  264. package/dist/providers/registry.d.ts.map +1 -0
  265. package/dist/providers/renderer.d.ts +49 -0
  266. package/dist/providers/renderer.d.ts.map +1 -0
  267. package/dist/providers/runtime-factory.d.ts +20 -0
  268. package/dist/providers/runtime-factory.d.ts.map +1 -0
  269. package/dist/providers/safety/prompt-guard.d.ts +34 -0
  270. package/dist/providers/safety/prompt-guard.d.ts.map +1 -0
  271. package/dist/providers/shared/anti-bot-policy.d.ts +51 -0
  272. package/dist/providers/shared/anti-bot-policy.d.ts.map +1 -0
  273. package/dist/providers/shared/post-policy.d.ts +31 -0
  274. package/dist/providers/shared/post-policy.d.ts.map +1 -0
  275. package/dist/providers/shared/request-headers.d.ts +5 -0
  276. package/dist/providers/shared/request-headers.d.ts.map +1 -0
  277. package/dist/providers/shared/traversal-url.d.ts +2 -0
  278. package/dist/providers/shared/traversal-url.d.ts.map +1 -0
  279. package/dist/providers/shopping/index.d.ts +63 -0
  280. package/dist/providers/shopping/index.d.ts.map +1 -0
  281. package/dist/providers/social/bluesky.d.ts +3 -0
  282. package/dist/providers/social/bluesky.d.ts.map +1 -0
  283. package/dist/providers/social/facebook.d.ts +3 -0
  284. package/dist/providers/social/facebook.d.ts.map +1 -0
  285. package/dist/providers/social/index.d.ts +31 -0
  286. package/dist/providers/social/index.d.ts.map +1 -0
  287. package/dist/providers/social/instagram.d.ts +3 -0
  288. package/dist/providers/social/instagram.d.ts.map +1 -0
  289. package/dist/providers/social/linkedin.d.ts +3 -0
  290. package/dist/providers/social/linkedin.d.ts.map +1 -0
  291. package/dist/providers/social/platform.d.ts +40 -0
  292. package/dist/providers/social/platform.d.ts.map +1 -0
  293. package/dist/providers/social/reddit.d.ts +3 -0
  294. package/dist/providers/social/reddit.d.ts.map +1 -0
  295. package/dist/providers/social/threads.d.ts +3 -0
  296. package/dist/providers/social/threads.d.ts.map +1 -0
  297. package/dist/providers/social/tiktok.d.ts +3 -0
  298. package/dist/providers/social/tiktok.d.ts.map +1 -0
  299. package/dist/providers/social/x.d.ts +3 -0
  300. package/dist/providers/social/x.d.ts.map +1 -0
  301. package/dist/providers/social/youtube-resolver.d.ts +78 -0
  302. package/dist/providers/social/youtube-resolver.d.ts.map +1 -0
  303. package/dist/providers/social/youtube.d.ts +34 -0
  304. package/dist/providers/social/youtube.d.ts.map +1 -0
  305. package/dist/providers/tier-router.d.ts +30 -0
  306. package/dist/providers/tier-router.d.ts.map +1 -0
  307. package/dist/providers/timebox.d.ts +20 -0
  308. package/dist/providers/timebox.d.ts.map +1 -0
  309. package/dist/providers/types.d.ts +344 -0
  310. package/dist/providers/types.d.ts.map +1 -0
  311. package/dist/providers/web/crawl-worker.d.ts +36 -0
  312. package/dist/providers/web/crawl-worker.d.ts.map +1 -0
  313. package/dist/providers/web/crawler.d.ts +101 -0
  314. package/dist/providers/web/crawler.d.ts.map +1 -0
  315. package/dist/providers/web/extract.d.ts +11 -0
  316. package/dist/providers/web/extract.d.ts.map +1 -0
  317. package/dist/providers/web/index.d.ts +24 -0
  318. package/dist/providers/web/index.d.ts.map +1 -0
  319. package/dist/providers/web/policy.d.ts +14 -0
  320. package/dist/providers/web/policy.d.ts.map +1 -0
  321. package/dist/providers/workflows.d.ts +67 -0
  322. package/dist/providers/workflows.d.ts.map +1 -0
  323. package/dist/relay/protocol.d.ts +317 -0
  324. package/dist/relay/protocol.d.ts.map +1 -0
  325. package/dist/relay/relay-endpoints.d.ts +16 -0
  326. package/dist/relay/relay-endpoints.d.ts.map +1 -0
  327. package/dist/relay/relay-server.d.ts +111 -0
  328. package/dist/relay/relay-server.d.ts.map +1 -0
  329. package/dist/relay/relay-types.d.ts +9 -0
  330. package/dist/relay/relay-types.d.ts.map +1 -0
  331. package/dist/skills/continuity-nudge.d.ts +12 -0
  332. package/dist/skills/continuity-nudge.d.ts.map +1 -0
  333. package/dist/skills/skill-loader.d.ts +20 -0
  334. package/dist/skills/skill-loader.d.ts.map +1 -0
  335. package/dist/skills/skill-nudge.d.ts +18 -0
  336. package/dist/skills/skill-nudge.d.ts.map +1 -0
  337. package/dist/skills/types.d.ts +12 -0
  338. package/dist/skills/types.d.ts.map +1 -0
  339. package/dist/snapshot/ops-snapshot.d.ts +16 -0
  340. package/dist/snapshot/ops-snapshot.d.ts.map +1 -0
  341. package/dist/snapshot/refs.d.ts +23 -0
  342. package/dist/snapshot/refs.d.ts.map +1 -0
  343. package/dist/snapshot/snapshotter.d.ts +27 -0
  344. package/dist/snapshot/snapshotter.d.ts.map +1 -0
  345. package/dist/tools/annotate.d.ts +4 -0
  346. package/dist/tools/annotate.d.ts.map +1 -0
  347. package/dist/tools/check.d.ts +4 -0
  348. package/dist/tools/check.d.ts.map +1 -0
  349. package/dist/tools/click.d.ts +4 -0
  350. package/dist/tools/click.d.ts.map +1 -0
  351. package/dist/tools/clone_component.d.ts +4 -0
  352. package/dist/tools/clone_component.d.ts.map +1 -0
  353. package/dist/tools/clone_page.d.ts +4 -0
  354. package/dist/tools/clone_page.d.ts.map +1 -0
  355. package/dist/tools/close.d.ts +4 -0
  356. package/dist/tools/close.d.ts.map +1 -0
  357. package/dist/tools/connect.d.ts +4 -0
  358. package/dist/tools/connect.d.ts.map +1 -0
  359. package/dist/tools/console_poll.d.ts +4 -0
  360. package/dist/tools/console_poll.d.ts.map +1 -0
  361. package/dist/tools/cookie_import.d.ts +25 -0
  362. package/dist/tools/cookie_import.d.ts.map +1 -0
  363. package/dist/tools/cookie_list.d.ts +9 -0
  364. package/dist/tools/cookie_list.d.ts.map +1 -0
  365. package/dist/tools/debug_trace_snapshot.d.ts +4 -0
  366. package/dist/tools/debug_trace_snapshot.d.ts.map +1 -0
  367. package/dist/tools/deps.d.ts +26 -0
  368. package/dist/tools/deps.d.ts.map +1 -0
  369. package/dist/tools/disconnect.d.ts +4 -0
  370. package/dist/tools/disconnect.d.ts.map +1 -0
  371. package/dist/tools/dom_get_html.d.ts +4 -0
  372. package/dist/tools/dom_get_html.d.ts.map +1 -0
  373. package/dist/tools/dom_get_text.d.ts +4 -0
  374. package/dist/tools/dom_get_text.d.ts.map +1 -0
  375. package/dist/tools/get_attr.d.ts +4 -0
  376. package/dist/tools/get_attr.d.ts.map +1 -0
  377. package/dist/tools/get_value.d.ts +4 -0
  378. package/dist/tools/get_value.d.ts.map +1 -0
  379. package/dist/tools/goto.d.ts +4 -0
  380. package/dist/tools/goto.d.ts.map +1 -0
  381. package/dist/tools/hover.d.ts +4 -0
  382. package/dist/tools/hover.d.ts.map +1 -0
  383. package/dist/tools/index.d.ts +4 -0
  384. package/dist/tools/index.d.ts.map +1 -0
  385. package/dist/tools/is_checked.d.ts +4 -0
  386. package/dist/tools/is_checked.d.ts.map +1 -0
  387. package/dist/tools/is_enabled.d.ts +4 -0
  388. package/dist/tools/is_enabled.d.ts.map +1 -0
  389. package/dist/tools/is_visible.d.ts +4 -0
  390. package/dist/tools/is_visible.d.ts.map +1 -0
  391. package/dist/tools/launch.d.ts +4 -0
  392. package/dist/tools/launch.d.ts.map +1 -0
  393. package/dist/tools/list.d.ts +4 -0
  394. package/dist/tools/list.d.ts.map +1 -0
  395. package/dist/tools/macro_resolve.d.ts +25 -0
  396. package/dist/tools/macro_resolve.d.ts.map +1 -0
  397. package/dist/tools/network_poll.d.ts +4 -0
  398. package/dist/tools/network_poll.d.ts.map +1 -0
  399. package/dist/tools/page.d.ts +4 -0
  400. package/dist/tools/page.d.ts.map +1 -0
  401. package/dist/tools/perf.d.ts +4 -0
  402. package/dist/tools/perf.d.ts.map +1 -0
  403. package/dist/tools/press.d.ts +4 -0
  404. package/dist/tools/press.d.ts.map +1 -0
  405. package/dist/tools/product_video_run.d.ts +4 -0
  406. package/dist/tools/product_video_run.d.ts.map +1 -0
  407. package/dist/tools/prompting_guide.d.ts +4 -0
  408. package/dist/tools/prompting_guide.d.ts.map +1 -0
  409. package/dist/tools/research_run.d.ts +4 -0
  410. package/dist/tools/research_run.d.ts.map +1 -0
  411. package/dist/tools/response.d.ts +16 -0
  412. package/dist/tools/response.d.ts.map +1 -0
  413. package/dist/tools/run.d.ts +4 -0
  414. package/dist/tools/run.d.ts.map +1 -0
  415. package/dist/tools/screenshot.d.ts +4 -0
  416. package/dist/tools/screenshot.d.ts.map +1 -0
  417. package/dist/tools/scroll.d.ts +4 -0
  418. package/dist/tools/scroll.d.ts.map +1 -0
  419. package/dist/tools/scroll_into_view.d.ts +4 -0
  420. package/dist/tools/scroll_into_view.d.ts.map +1 -0
  421. package/dist/tools/select.d.ts +4 -0
  422. package/dist/tools/select.d.ts.map +1 -0
  423. package/dist/tools/shopping_run.d.ts +4 -0
  424. package/dist/tools/shopping_run.d.ts.map +1 -0
  425. package/dist/tools/skill_list.d.ts +4 -0
  426. package/dist/tools/skill_list.d.ts.map +1 -0
  427. package/dist/tools/skill_load.d.ts +4 -0
  428. package/dist/tools/skill_load.d.ts.map +1 -0
  429. package/dist/tools/snapshot.d.ts +4 -0
  430. package/dist/tools/snapshot.d.ts.map +1 -0
  431. package/dist/tools/status.d.ts +4 -0
  432. package/dist/tools/status.d.ts.map +1 -0
  433. package/dist/tools/target_close.d.ts +4 -0
  434. package/dist/tools/target_close.d.ts.map +1 -0
  435. package/dist/tools/target_new.d.ts +4 -0
  436. package/dist/tools/target_new.d.ts.map +1 -0
  437. package/dist/tools/target_use.d.ts +4 -0
  438. package/dist/tools/target_use.d.ts.map +1 -0
  439. package/dist/tools/targets_list.d.ts +4 -0
  440. package/dist/tools/targets_list.d.ts.map +1 -0
  441. package/dist/tools/type.d.ts +4 -0
  442. package/dist/tools/type.d.ts.map +1 -0
  443. package/dist/tools/uncheck.d.ts +4 -0
  444. package/dist/tools/uncheck.d.ts.map +1 -0
  445. package/dist/tools/wait.d.ts +4 -0
  446. package/dist/tools/wait.d.ts.map +1 -0
  447. package/dist/tools/workflow-runtime.d.ts +4 -0
  448. package/dist/tools/workflow-runtime.d.ts.map +1 -0
  449. package/dist/utils/crypto.d.ts +2 -0
  450. package/dist/utils/crypto.d.ts.map +1 -0
  451. package/dist/utils/endpoint-validation.d.ts +2 -0
  452. package/dist/utils/endpoint-validation.d.ts.map +1 -0
  453. package/dist/utils/fs.d.ts +5 -0
  454. package/dist/utils/fs.d.ts.map +1 -0
  455. package/dist/utils/hub-enabled.d.ts +3 -0
  456. package/dist/utils/hub-enabled.d.ts.map +1 -0
  457. package/extension/dist/annotate-content.css +237 -0
  458. package/extension/dist/annotate-content.js +934 -0
  459. package/extension/dist/background.js +1203 -35
  460. package/extension/dist/logging.js +50 -0
  461. package/extension/dist/ops/dom-bridge.js +355 -0
  462. package/extension/dist/ops/ops-runtime.js +1751 -0
  463. package/extension/dist/ops/ops-session-store.js +203 -0
  464. package/extension/dist/ops/parallelism-governor.js +117 -0
  465. package/extension/dist/ops/redaction.js +52 -0
  466. package/extension/dist/ops/snapshot-builder.js +4 -0
  467. package/extension/dist/ops/snapshot-shared.js +236 -0
  468. package/extension/dist/popup.js +370 -25
  469. package/extension/dist/relay-settings.js +1 -0
  470. package/extension/dist/services/CDPRouter.js +567 -104
  471. package/extension/dist/services/ConnectionManager.js +469 -60
  472. package/extension/dist/services/NativePortManager.js +182 -0
  473. package/extension/dist/services/RelayClient.js +227 -26
  474. package/extension/dist/services/TabManager.js +81 -0
  475. package/extension/dist/services/TargetSessionMap.js +146 -0
  476. package/extension/dist/services/cdp-router-commands.js +203 -0
  477. package/extension/dist/services/url-restrictions.js +41 -0
  478. package/extension/dist/types.js +3 -1
  479. package/extension/icons/icon128.png +0 -0
  480. package/extension/icons/icon16.png +0 -0
  481. package/extension/icons/icon32.png +0 -0
  482. package/extension/icons/icon48.png +0 -0
  483. package/extension/manifest.json +17 -3
  484. package/extension/popup.html +144 -0
  485. package/package.json +26 -17
  486. package/scripts/native/host.cjs +230 -0
  487. package/scripts/native/install.ps1 +73 -0
  488. package/scripts/native/install.sh +66 -0
  489. package/scripts/native/uninstall.ps1 +25 -0
  490. package/scripts/native/uninstall.sh +26 -0
  491. package/skills/AGENTS.md +47 -66
  492. package/skills/opendevbrowser-best-practices/SKILL.md +196 -49
  493. package/skills/opendevbrowser-best-practices/artifacts/browser-agent-known-issues-matrix.md +44 -0
  494. package/skills/opendevbrowser-best-practices/artifacts/command-channel-reference.md +95 -0
  495. package/skills/opendevbrowser-best-practices/artifacts/debug-trace-playbook.md +36 -0
  496. package/skills/opendevbrowser-best-practices/artifacts/fingerprint-tiers.md +36 -0
  497. package/skills/opendevbrowser-best-practices/artifacts/macro-workflows.md +43 -0
  498. package/skills/opendevbrowser-best-practices/artifacts/parity-gates.md +36 -0
  499. package/skills/opendevbrowser-best-practices/artifacts/provider-workflows.md +89 -0
  500. package/skills/opendevbrowser-best-practices/assets/templates/cdp-forward-envelope.json +11 -0
  501. package/skills/opendevbrowser-best-practices/assets/templates/mode-flag-matrix.json +56 -0
  502. package/skills/opendevbrowser-best-practices/assets/templates/ops-request-envelope.json +9 -0
  503. package/skills/opendevbrowser-best-practices/assets/templates/robustness-checklist.json +79 -0
  504. package/skills/opendevbrowser-best-practices/assets/templates/surface-audit-checklist.json +24 -0
  505. package/skills/opendevbrowser-best-practices/scripts/odb-workflow.sh +144 -0
  506. package/skills/opendevbrowser-best-practices/scripts/run-robustness-audit.sh +83 -0
  507. package/skills/opendevbrowser-best-practices/scripts/validate-skill-assets.sh +93 -0
  508. package/skills/opendevbrowser-continuity-ledger/SKILL.md +67 -23
  509. package/skills/opendevbrowser-data-extraction/SKILL.md +126 -0
  510. package/skills/opendevbrowser-data-extraction/artifacts/extraction-workflows.md +31 -0
  511. package/skills/opendevbrowser-data-extraction/assets/templates/compliance-checklist.md +7 -0
  512. package/skills/opendevbrowser-data-extraction/assets/templates/extraction-schema.json +17 -0
  513. package/skills/opendevbrowser-data-extraction/assets/templates/pagination-state.json +11 -0
  514. package/skills/opendevbrowser-data-extraction/assets/templates/quality-gates.json +10 -0
  515. package/skills/opendevbrowser-data-extraction/examples/sample-schema.json +19 -0
  516. package/skills/opendevbrowser-data-extraction/scripts/run-extraction-workflow.sh +83 -0
  517. package/skills/opendevbrowser-data-extraction/scripts/validate-skill-assets.sh +49 -0
  518. package/skills/opendevbrowser-form-testing/SKILL.md +143 -0
  519. package/skills/opendevbrowser-form-testing/artifacts/form-workflows.md +37 -0
  520. package/skills/opendevbrowser-form-testing/assets/templates/a11y-assertions.md +7 -0
  521. package/skills/opendevbrowser-form-testing/assets/templates/challenge-decision-tree.json +16 -0
  522. package/skills/opendevbrowser-form-testing/assets/templates/multi-step-state.json +11 -0
  523. package/skills/opendevbrowser-form-testing/assets/templates/validation-matrix.json +24 -0
  524. package/skills/opendevbrowser-form-testing/examples/sample-validation-matrix.json +29 -0
  525. package/skills/opendevbrowser-form-testing/scripts/run-form-workflow.sh +82 -0
  526. package/skills/opendevbrowser-form-testing/scripts/validate-skill-assets.sh +49 -0
  527. package/skills/opendevbrowser-login-automation/SKILL.md +159 -0
  528. package/skills/opendevbrowser-login-automation/artifacts/login-workflows.md +39 -0
  529. package/skills/opendevbrowser-login-automation/assets/templates/auth-signals.json +21 -0
  530. package/skills/opendevbrowser-login-automation/assets/templates/challenge-checkpoint.md +10 -0
  531. package/skills/opendevbrowser-login-automation/assets/templates/login-scenario-matrix.json +26 -0
  532. package/skills/opendevbrowser-login-automation/examples/sample-auth-signals.json +14 -0
  533. package/skills/opendevbrowser-login-automation/scripts/record-auth-signals.sh +18 -0
  534. package/skills/opendevbrowser-login-automation/scripts/run-login-workflow.sh +99 -0
  535. package/skills/opendevbrowser-login-automation/scripts/validate-skill-assets.sh +50 -0
  536. package/skills/opendevbrowser-product-presentation-asset/SKILL.md +98 -0
  537. package/skills/opendevbrowser-product-presentation-asset/artifacts/asset-pack-assembly.md +23 -0
  538. package/skills/opendevbrowser-product-presentation-asset/artifacts/ugc-creative-guide.md +21 -0
  539. package/skills/opendevbrowser-product-presentation-asset/assets/templates/claims-evidence-map.md +5 -0
  540. package/skills/opendevbrowser-product-presentation-asset/assets/templates/copy.md +5 -0
  541. package/skills/opendevbrowser-product-presentation-asset/assets/templates/features.md +4 -0
  542. package/skills/opendevbrowser-product-presentation-asset/assets/templates/manifest.schema.json +14 -0
  543. package/skills/opendevbrowser-product-presentation-asset/assets/templates/shot-list.md +7 -0
  544. package/skills/opendevbrowser-product-presentation-asset/assets/templates/ugc-concepts.md +17 -0
  545. package/skills/opendevbrowser-product-presentation-asset/assets/templates/user-actions.md +7 -0
  546. package/skills/opendevbrowser-product-presentation-asset/assets/templates/video-assembly.md +18 -0
  547. package/skills/opendevbrowser-product-presentation-asset/examples/sample-input.json +6 -0
  548. package/skills/opendevbrowser-product-presentation-asset/examples/sample-manifest.json +18 -0
  549. package/skills/opendevbrowser-product-presentation-asset/scripts/capture-screenshots.sh +9 -0
  550. package/skills/opendevbrowser-product-presentation-asset/scripts/collect-product.sh +14 -0
  551. package/skills/opendevbrowser-product-presentation-asset/scripts/download-images.sh +9 -0
  552. package/skills/opendevbrowser-product-presentation-asset/scripts/render-video-brief.sh +96 -0
  553. package/skills/opendevbrowser-product-presentation-asset/scripts/validate-skill-assets.sh +56 -0
  554. package/skills/opendevbrowser-product-presentation-asset/scripts/write-manifest.sh +43 -0
  555. package/skills/opendevbrowser-research/SKILL.md +73 -0
  556. package/skills/opendevbrowser-research/artifacts/research-workflows.md +29 -0
  557. package/skills/opendevbrowser-research/assets/templates/compact.md +7 -0
  558. package/skills/opendevbrowser-research/assets/templates/context.json +18 -0
  559. package/skills/opendevbrowser-research/assets/templates/report.md +9 -0
  560. package/skills/opendevbrowser-research/examples/sample-input.json +6 -0
  561. package/skills/opendevbrowser-research/examples/sample-output.md +4 -0
  562. package/skills/opendevbrowser-research/scripts/render-output.sh +12 -0
  563. package/skills/opendevbrowser-research/scripts/run-research.sh +23 -0
  564. package/skills/opendevbrowser-research/scripts/validate-skill-assets.sh +48 -0
  565. package/skills/opendevbrowser-research/scripts/write-artifacts.sh +29 -0
  566. package/skills/opendevbrowser-shopping/SKILL.md +118 -0
  567. package/skills/opendevbrowser-shopping/artifacts/deal-hunting-workflows.md +37 -0
  568. package/skills/opendevbrowser-shopping/assets/templates/deal-thresholds.json +8 -0
  569. package/skills/opendevbrowser-shopping/assets/templates/deals-context.json +9 -0
  570. package/skills/opendevbrowser-shopping/assets/templates/deals-table.md +4 -0
  571. package/skills/opendevbrowser-shopping/assets/templates/market-analysis.json +30 -0
  572. package/skills/opendevbrowser-shopping/examples/sample-deals.md +4 -0
  573. package/skills/opendevbrowser-shopping/examples/sample-query.json +5 -0
  574. package/skills/opendevbrowser-shopping/scripts/analyze-market.sh +307 -0
  575. package/skills/opendevbrowser-shopping/scripts/normalize-offers.sh +28 -0
  576. package/skills/opendevbrowser-shopping/scripts/render-deals.sh +13 -0
  577. package/skills/opendevbrowser-shopping/scripts/run-deal-hunt.sh +32 -0
  578. package/skills/opendevbrowser-shopping/scripts/run-shopping.sh +19 -0
  579. package/skills/opendevbrowser-shopping/scripts/validate-skill-assets.sh +53 -0
  580. package/dist/chunk-WTFSMBVH.js +0 -2815
  581. package/dist/chunk-WTFSMBVH.js.map +0 -1
  582. package/extension/dist/popup.jsx +0 -150
  583. package/skills/data-extraction/SKILL.md +0 -136
  584. package/skills/form-testing/SKILL.md +0 -113
  585. package/skills/login-automation/SKILL.md +0 -98
@@ -0,0 +1,1751 @@
1
+ import { MAX_OPS_PAYLOAD_BYTES, MAX_SNAPSHOT_BYTES, OPS_PROTOCOL_VERSION } from "../types.js";
2
+ import { TabManager } from "../services/TabManager.js";
3
+ import { getRestrictionMessage, isRestrictedUrl } from "../services/url-restrictions.js";
4
+ import { logError } from "../logging.js";
5
+ import { DomBridge } from "./dom-bridge.js";
6
+ import { buildSnapshot } from "./snapshot-builder.js";
7
+ import { OpsSessionStore } from "./ops-session-store.js";
8
+ import { DEFAULT_OPS_PARALLELISM_POLICY, evaluateOpsGovernor } from "./parallelism-governor.js";
9
+ import { redactConsoleText, redactUrl } from "./redaction.js";
10
+ const MAX_CONSOLE_EVENTS = 200;
11
+ const MAX_NETWORK_EVENTS = 300;
12
+ const SESSION_TTL_MS = 20_000;
13
+ const SCREENSHOT_TIMEOUT_MS = 8000;
14
+ const TAB_CLOSE_TIMEOUT_MS = 5000;
15
+ const TARGET_SCOPED_COMMANDS = new Set([
16
+ "storage.setCookies",
17
+ "storage.getCookies",
18
+ "nav.goto",
19
+ "nav.wait",
20
+ "nav.snapshot",
21
+ "interact.click",
22
+ "interact.hover",
23
+ "interact.press",
24
+ "interact.check",
25
+ "interact.uncheck",
26
+ "interact.type",
27
+ "interact.select",
28
+ "interact.scroll",
29
+ "interact.scrollIntoView",
30
+ "dom.getHtml",
31
+ "dom.getText",
32
+ "dom.getAttr",
33
+ "dom.getValue",
34
+ "dom.isVisible",
35
+ "dom.isEnabled",
36
+ "dom.isChecked",
37
+ "export.clonePage",
38
+ "export.cloneComponent",
39
+ "devtools.perf",
40
+ "page.screenshot"
41
+ ]);
42
+ export class OpsRuntime {
43
+ sendEnvelope;
44
+ cdp;
45
+ tabs = new TabManager();
46
+ dom = new DomBridge();
47
+ sessions = new OpsSessionStore();
48
+ encoder = new TextEncoder();
49
+ closingTimers = new Map();
50
+ parallelWaiters = new Map();
51
+ constructor(options) {
52
+ this.sendEnvelope = options.send;
53
+ this.cdp = options.cdp;
54
+ chrome.tabs.onRemoved.addListener(this.handleTabRemoved);
55
+ chrome.tabs.onUpdated.addListener(this.handleTabUpdated);
56
+ chrome.debugger.onEvent.addListener(this.handleDebuggerEvent);
57
+ chrome.debugger.onDetach.addListener(this.handleDebuggerDetach);
58
+ }
59
+ handleMessage(message) {
60
+ if (message.type === "ops_hello") {
61
+ this.handleHello(message);
62
+ return;
63
+ }
64
+ if (message.type === "ops_ping") {
65
+ this.handlePing(message);
66
+ return;
67
+ }
68
+ if (message.type === "ops_event" && message.event === "ops_client_disconnected") {
69
+ this.handleClientDisconnected(message);
70
+ return;
71
+ }
72
+ if (message.type === "ops_request") {
73
+ void this.handleRequest(message).catch((error) => {
74
+ logError("ops.handle_request", error, { code: "ops_request_failed" });
75
+ this.sendError(message, {
76
+ code: "execution_failed",
77
+ message: error instanceof Error ? error.message : "Ops request failed",
78
+ retryable: false
79
+ });
80
+ });
81
+ }
82
+ }
83
+ handleHello(message) {
84
+ if (message.version !== OPS_PROTOCOL_VERSION) {
85
+ const error = {
86
+ type: "ops_error",
87
+ requestId: "ops_hello",
88
+ clientId: message.clientId,
89
+ error: {
90
+ code: "not_supported",
91
+ message: "Unsupported ops protocol version.",
92
+ retryable: false,
93
+ details: { supported: [OPS_PROTOCOL_VERSION], received: message.version }
94
+ }
95
+ };
96
+ this.sendEnvelope(error);
97
+ return;
98
+ }
99
+ const ack = {
100
+ type: "ops_hello_ack",
101
+ version: OPS_PROTOCOL_VERSION,
102
+ clientId: message.clientId,
103
+ maxPayloadBytes: MAX_OPS_PAYLOAD_BYTES,
104
+ capabilities: []
105
+ };
106
+ this.sendEnvelope(ack);
107
+ }
108
+ handlePing(message) {
109
+ const pong = {
110
+ type: "ops_pong",
111
+ id: message.id,
112
+ clientId: message.clientId
113
+ };
114
+ this.sendEnvelope(pong);
115
+ }
116
+ handleClientDisconnected(message) {
117
+ const clientId = message.clientId;
118
+ if (!clientId)
119
+ return;
120
+ const sessions = this.sessions.listOwnedBy(clientId);
121
+ for (const session of sessions) {
122
+ this.markSessionClosing(session, "ops_session_expired");
123
+ }
124
+ }
125
+ handleTabRemoved = (tabId) => {
126
+ this.handleClosedTarget(tabId, "ops_tab_closed");
127
+ };
128
+ handleTabUpdated = (tabId, changeInfo, tab) => {
129
+ const session = this.sessions.getByTabId(tabId);
130
+ if (!session)
131
+ return;
132
+ if (changeInfo.discarded === true || tab.discarded === true) {
133
+ session.discardedSignals += 1;
134
+ }
135
+ const frozenChange = changeInfo.frozen === true;
136
+ const frozenTab = tab.frozen === true;
137
+ if (frozenChange || frozenTab) {
138
+ session.frozenSignals += 1;
139
+ }
140
+ };
141
+ handleDebuggerDetach = (source) => {
142
+ if (typeof source.tabId !== "number")
143
+ return;
144
+ void this.handleDebuggerDetachForTab(source.tabId);
145
+ };
146
+ handleDebuggerEvent = (source, method, params) => {
147
+ if (typeof source.tabId !== "number")
148
+ return;
149
+ const session = this.sessions.getByTabId(source.tabId);
150
+ if (!session)
151
+ return;
152
+ if (method === "Runtime.consoleAPICalled") {
153
+ const payload = params;
154
+ const parts = Array.isArray(payload?.args)
155
+ ? payload.args.map((arg) => {
156
+ if (typeof arg.value === "string")
157
+ return arg.value;
158
+ if (typeof arg.value === "number" || typeof arg.value === "boolean")
159
+ return String(arg.value);
160
+ if (typeof arg.description === "string")
161
+ return arg.description;
162
+ return "";
163
+ })
164
+ : [];
165
+ const text = redactConsoleText(parts.filter(Boolean).join(" "));
166
+ const event = {
167
+ seq: ++session.consoleSeq,
168
+ level: payload?.type ?? "log",
169
+ text,
170
+ ts: Date.now()
171
+ };
172
+ session.consoleEvents.push(event);
173
+ if (session.consoleEvents.length > MAX_CONSOLE_EVENTS) {
174
+ session.consoleEvents.shift();
175
+ }
176
+ return;
177
+ }
178
+ if (method === "Network.requestWillBeSent") {
179
+ const payload = params;
180
+ const requestId = payload.requestId;
181
+ if (requestId && payload.request) {
182
+ const methodValue = payload.request.method ?? "GET";
183
+ const urlValue = payload.request.url ?? "";
184
+ session.networkRequests.set(requestId, {
185
+ method: methodValue,
186
+ url: urlValue,
187
+ resourceType: payload.type
188
+ });
189
+ const event = {
190
+ seq: ++session.networkSeq,
191
+ method: methodValue,
192
+ url: redactUrl(urlValue),
193
+ resourceType: payload.type,
194
+ ts: Date.now()
195
+ };
196
+ session.networkEvents.push(event);
197
+ if (session.networkEvents.length > MAX_NETWORK_EVENTS) {
198
+ session.networkEvents.shift();
199
+ }
200
+ }
201
+ return;
202
+ }
203
+ if (method === "Network.responseReceived") {
204
+ const payload = params;
205
+ const requestId = payload.requestId;
206
+ if (requestId) {
207
+ const pending = session.networkRequests.get(requestId);
208
+ const urlValue = payload.response?.url ?? pending?.url ?? "";
209
+ const methodValue = pending?.method ?? "GET";
210
+ const event = {
211
+ seq: ++session.networkSeq,
212
+ method: methodValue,
213
+ url: redactUrl(urlValue),
214
+ status: payload.response?.status,
215
+ resourceType: pending?.resourceType,
216
+ ts: Date.now()
217
+ };
218
+ session.networkEvents.push(event);
219
+ if (session.networkEvents.length > MAX_NETWORK_EVENTS) {
220
+ session.networkEvents.shift();
221
+ }
222
+ session.networkRequests.delete(requestId);
223
+ }
224
+ }
225
+ };
226
+ async handleRequest(message) {
227
+ const clientId = message.clientId;
228
+ if (!clientId) {
229
+ this.sendError(message, buildError("invalid_request", "Missing clientId", false));
230
+ return;
231
+ }
232
+ switch (message.command) {
233
+ case "session.launch":
234
+ case "session.connect":
235
+ await this.handleSessionLaunch(message, clientId);
236
+ return;
237
+ case "session.disconnect":
238
+ await this.handleSessionDisconnect(message, clientId);
239
+ return;
240
+ case "session.status":
241
+ await this.handleSessionStatus(message, clientId);
242
+ return;
243
+ case "storage.setCookies":
244
+ await this.withSession(message, clientId, (session) => this.handleStorageSetCookies(message, session));
245
+ return;
246
+ case "storage.getCookies":
247
+ await this.withSession(message, clientId, (session) => this.handleStorageGetCookies(message, session));
248
+ return;
249
+ case "targets.list":
250
+ await this.withSession(message, clientId, (session) => this.handleTargetsList(message, session));
251
+ return;
252
+ case "targets.use":
253
+ await this.withSession(message, clientId, (session) => this.handleTargetsUse(message, session));
254
+ return;
255
+ case "targets.new":
256
+ await this.withSession(message, clientId, (session) => this.handleTargetsNew(message, session));
257
+ return;
258
+ case "targets.close":
259
+ await this.withSession(message, clientId, (session) => this.handleTargetsClose(message, session));
260
+ return;
261
+ case "page.open":
262
+ await this.withSession(message, clientId, (session) => this.handlePageOpen(message, session));
263
+ return;
264
+ case "page.list":
265
+ await this.withSession(message, clientId, (session) => this.handlePageList(message, session));
266
+ return;
267
+ case "page.close":
268
+ await this.withSession(message, clientId, (session) => this.handlePageClose(message, session));
269
+ return;
270
+ case "nav.goto":
271
+ await this.withSession(message, clientId, (session) => this.handleGoto(message, session));
272
+ return;
273
+ case "nav.wait":
274
+ await this.withSession(message, clientId, (session) => this.handleWait(message, session));
275
+ return;
276
+ case "nav.snapshot":
277
+ await this.withSession(message, clientId, (session) => this.handleSnapshot(message, session));
278
+ return;
279
+ case "interact.click":
280
+ await this.withSession(message, clientId, (session) => this.handleClick(message, session));
281
+ return;
282
+ case "interact.hover":
283
+ await this.withSession(message, clientId, (session) => this.handleHover(message, session));
284
+ return;
285
+ case "interact.press":
286
+ await this.withSession(message, clientId, (session) => this.handlePress(message, session));
287
+ return;
288
+ case "interact.check":
289
+ await this.withSession(message, clientId, (session) => this.handleCheck(message, session, true));
290
+ return;
291
+ case "interact.uncheck":
292
+ await this.withSession(message, clientId, (session) => this.handleCheck(message, session, false));
293
+ return;
294
+ case "interact.type":
295
+ await this.withSession(message, clientId, (session) => this.handleType(message, session));
296
+ return;
297
+ case "interact.select":
298
+ await this.withSession(message, clientId, (session) => this.handleSelect(message, session));
299
+ return;
300
+ case "interact.scroll":
301
+ await this.withSession(message, clientId, (session) => this.handleScroll(message, session));
302
+ return;
303
+ case "interact.scrollIntoView":
304
+ await this.withSession(message, clientId, (session) => this.handleScrollIntoView(message, session));
305
+ return;
306
+ case "dom.getHtml":
307
+ await this.withSession(message, clientId, (session) => this.handleDomGetHtml(message, session));
308
+ return;
309
+ case "dom.getText":
310
+ await this.withSession(message, clientId, (session) => this.handleDomGetText(message, session));
311
+ return;
312
+ case "dom.getAttr":
313
+ await this.withSession(message, clientId, (session) => this.handleDomGetAttr(message, session));
314
+ return;
315
+ case "dom.getValue":
316
+ await this.withSession(message, clientId, (session) => this.handleDomGetValue(message, session));
317
+ return;
318
+ case "dom.isVisible":
319
+ await this.withSession(message, clientId, (session) => this.handleDomIsVisible(message, session));
320
+ return;
321
+ case "dom.isEnabled":
322
+ await this.withSession(message, clientId, (session) => this.handleDomIsEnabled(message, session));
323
+ return;
324
+ case "dom.isChecked":
325
+ await this.withSession(message, clientId, (session) => this.handleDomIsChecked(message, session));
326
+ return;
327
+ case "export.clonePage":
328
+ await this.withSession(message, clientId, (session) => this.handleClonePage(message, session));
329
+ return;
330
+ case "export.cloneComponent":
331
+ await this.withSession(message, clientId, (session) => this.handleCloneComponent(message, session));
332
+ return;
333
+ case "devtools.perf":
334
+ await this.withSession(message, clientId, (session) => this.handlePerf(message, session));
335
+ return;
336
+ case "page.screenshot":
337
+ await this.withSession(message, clientId, (session) => this.handleScreenshot(message, session));
338
+ return;
339
+ case "devtools.consolePoll":
340
+ await this.withSession(message, clientId, (session) => this.handleConsolePoll(message, session));
341
+ return;
342
+ case "devtools.networkPoll":
343
+ await this.withSession(message, clientId, (session) => this.handleNetworkPoll(message, session));
344
+ return;
345
+ default:
346
+ this.sendError(message, buildError("invalid_request", `Unknown ops command: ${message.command}`, false));
347
+ }
348
+ }
349
+ async handleSessionLaunch(message, clientId) {
350
+ const payload = isRecord(message.payload) ? message.payload : {};
351
+ const parallelismPolicy = parseParallelismPolicy(payload.parallelismPolicy);
352
+ const startUrl = typeof payload.startUrl === "string" ? payload.startUrl : undefined;
353
+ if (startUrl) {
354
+ try {
355
+ const restriction = getRestrictionMessage(new URL(startUrl));
356
+ if (restriction) {
357
+ this.sendError(message, buildError("restricted_url", restriction, false));
358
+ return;
359
+ }
360
+ }
361
+ catch {
362
+ this.sendError(message, buildError("invalid_request", "Invalid startUrl", false));
363
+ return;
364
+ }
365
+ }
366
+ const activeTab = startUrl
367
+ ? await this.tabs.createTab(startUrl, true)
368
+ : await this.tabs.getActiveTab();
369
+ if (!activeTab || typeof activeTab.id !== "number") {
370
+ this.sendError(message, buildError("ops_unavailable", "No active tab to attach.", true));
371
+ return;
372
+ }
373
+ if (activeTab.url) {
374
+ const restriction = isRestrictedUrl(activeTab.url);
375
+ if (restriction.restricted) {
376
+ this.sendError(message, buildError("restricted_url", restriction.message ?? "Restricted tab.", false));
377
+ return;
378
+ }
379
+ }
380
+ try {
381
+ await this.cdp.attach(activeTab.id);
382
+ }
383
+ catch (error) {
384
+ const detail = error instanceof Error ? error.message : "Debugger attach failed";
385
+ this.sendError(message, buildError("cdp_attach_failed", detail, false));
386
+ return;
387
+ }
388
+ await this.tabs.waitForTabComplete(activeTab.id).catch(() => undefined);
389
+ const leaseId = typeof message.leaseId === "string" && message.leaseId.trim().length > 0
390
+ ? message.leaseId.trim()
391
+ : createId();
392
+ const session = this.sessions.createSession(clientId, activeTab.id, leaseId, {
393
+ url: activeTab.url ?? undefined,
394
+ title: activeTab.title ?? undefined
395
+ }, {
396
+ parallelismPolicy
397
+ });
398
+ await this.enableSessionDomains(session);
399
+ this.sendEvent({
400
+ type: "ops_event",
401
+ clientId,
402
+ opsSessionId: session.id,
403
+ event: "ops_session_created",
404
+ payload: { tabId: session.tabId, targetId: session.targetId }
405
+ });
406
+ this.sendResponse(message, {
407
+ opsSessionId: session.id,
408
+ activeTargetId: session.activeTargetId,
409
+ url: activeTab.url ?? undefined,
410
+ title: activeTab.title ?? undefined,
411
+ leaseId: session.leaseId
412
+ });
413
+ }
414
+ async handleSessionDisconnect(message, clientId) {
415
+ const session = this.getSessionForMessage(message, clientId);
416
+ if (!session)
417
+ return;
418
+ this.sendResponse(message, { ok: true });
419
+ this.scheduleSessionCleanup(session.id, "ops_session_closed");
420
+ }
421
+ async handleSessionStatus(message, clientId) {
422
+ const session = this.getSessionForMessage(message, clientId);
423
+ if (!session)
424
+ return;
425
+ const tab = await this.tabs.getTab(session.tabId);
426
+ this.sendResponse(message, {
427
+ mode: "extension",
428
+ activeTargetId: session.activeTargetId || null,
429
+ url: tab?.url ?? undefined,
430
+ title: tab?.title ?? undefined,
431
+ leaseId: session.leaseId,
432
+ state: session.state
433
+ });
434
+ }
435
+ async handleTargetsList(message, session) {
436
+ const payload = isRecord(message.payload) ? message.payload : {};
437
+ const includeUrls = payload.includeUrls === true;
438
+ const targets = await Promise.all(Array.from(session.targets.values()).map(async (target) => {
439
+ const tab = await this.tabs.getTab(target.tabId);
440
+ return {
441
+ targetId: target.targetId,
442
+ type: "page",
443
+ title: tab?.title ?? target.title,
444
+ url: includeUrls ? tab?.url ?? target.url : undefined
445
+ };
446
+ }));
447
+ this.sendResponse(message, { activeTargetId: session.activeTargetId || null, targets });
448
+ }
449
+ async handleTargetsUse(message, session) {
450
+ const payload = isRecord(message.payload) ? message.payload : {};
451
+ const targetId = typeof payload.targetId === "string" ? payload.targetId : null;
452
+ if (!targetId || !session.targets.has(targetId)) {
453
+ this.sendError(message, buildError("invalid_request", "Unknown targetId", false));
454
+ return;
455
+ }
456
+ session.activeTargetId = targetId;
457
+ const target = session.targets.get(targetId) ?? null;
458
+ if (target) {
459
+ await this.tabs.activateTab(target.tabId).catch(() => undefined);
460
+ }
461
+ const tab = target ? await this.tabs.getTab(target.tabId) : null;
462
+ this.sendResponse(message, {
463
+ activeTargetId: targetId,
464
+ url: tab?.url ?? target?.url,
465
+ title: tab?.title ?? target?.title
466
+ });
467
+ }
468
+ async handleTargetsNew(message, session) {
469
+ const payload = isRecord(message.payload) ? message.payload : {};
470
+ const url = typeof payload.url === "string" ? payload.url : undefined;
471
+ const tab = await this.tabs.createTab(url, true);
472
+ if (!tab?.id) {
473
+ this.sendError(message, buildError("execution_failed", "Target creation failed", false));
474
+ return;
475
+ }
476
+ await this.tabs.waitForTabComplete(tab.id).catch(() => undefined);
477
+ try {
478
+ await this.cdp.attach(tab.id);
479
+ }
480
+ catch (error) {
481
+ const detail = error instanceof Error ? error.message : "Debugger attach failed";
482
+ this.sendError(message, buildError("cdp_attach_failed", detail, false));
483
+ return;
484
+ }
485
+ const target = this.sessions.addTarget(session.id, tab.id, { url: tab.url ?? undefined, title: tab.title ?? undefined });
486
+ session.activeTargetId = target.targetId;
487
+ this.sendResponse(message, { targetId: target.targetId });
488
+ }
489
+ async handleTargetsClose(message, session) {
490
+ const payload = isRecord(message.payload) ? message.payload : {};
491
+ const targetId = typeof payload.targetId === "string" ? payload.targetId : null;
492
+ if (!targetId) {
493
+ this.sendError(message, buildError("invalid_request", "Missing targetId", false));
494
+ return;
495
+ }
496
+ const target = session.targets.get(targetId);
497
+ if (!target) {
498
+ this.sendError(message, buildError("invalid_request", "Unknown targetId", false));
499
+ return;
500
+ }
501
+ this.sessions.removeTarget(session.id, targetId);
502
+ await this.closeTabBestEffort(target.tabId);
503
+ if (target.targetId === session.targetId || session.targets.size === 0) {
504
+ this.sendResponse(message, { ok: true });
505
+ this.scheduleSessionCleanup(session.id, "ops_session_closed");
506
+ return;
507
+ }
508
+ this.sendResponse(message, { ok: true });
509
+ }
510
+ async handlePageOpen(message, session) {
511
+ const payload = isRecord(message.payload) ? message.payload : {};
512
+ const name = typeof payload.name === "string" ? payload.name : null;
513
+ if (!name) {
514
+ this.sendError(message, buildError("invalid_request", "Missing name", false));
515
+ return;
516
+ }
517
+ const existingTargetId = this.sessions.getTargetIdByName(session.id, name);
518
+ if (existingTargetId) {
519
+ const target = session.targets.get(existingTargetId) ?? null;
520
+ this.sendResponse(message, { targetId: existingTargetId, created: false, url: target?.url, title: target?.title });
521
+ return;
522
+ }
523
+ const url = typeof payload.url === "string" ? payload.url : undefined;
524
+ const tab = await this.tabs.createTab(url, true);
525
+ if (!tab?.id) {
526
+ this.sendError(message, buildError("execution_failed", "Target creation failed", false));
527
+ return;
528
+ }
529
+ await this.tabs.waitForTabComplete(tab.id).catch(() => undefined);
530
+ try {
531
+ await this.cdp.attach(tab.id);
532
+ }
533
+ catch (error) {
534
+ const detail = error instanceof Error ? error.message : "Debugger attach failed";
535
+ this.sendError(message, buildError("cdp_attach_failed", detail, false));
536
+ return;
537
+ }
538
+ const target = this.sessions.addTarget(session.id, tab.id, { url: tab.url ?? undefined, title: tab.title ?? undefined });
539
+ this.sessions.setName(session.id, target.targetId, name);
540
+ session.activeTargetId = target.targetId;
541
+ this.sendResponse(message, { targetId: target.targetId, created: true, url: target.url, title: target.title });
542
+ }
543
+ async handlePageList(message, session) {
544
+ const pages = await Promise.all(this.sessions.listNamedTargets(session.id).map(async ({ name, targetId }) => {
545
+ const target = session.targets.get(targetId);
546
+ const tab = target ? await this.tabs.getTab(target.tabId) : null;
547
+ return {
548
+ name,
549
+ targetId,
550
+ url: tab?.url ?? target?.url,
551
+ title: tab?.title ?? target?.title
552
+ };
553
+ }));
554
+ this.sendResponse(message, { pages });
555
+ }
556
+ async handlePageClose(message, session) {
557
+ const payload = isRecord(message.payload) ? message.payload : {};
558
+ const name = typeof payload.name === "string" ? payload.name : null;
559
+ if (!name) {
560
+ this.sendError(message, buildError("invalid_request", "Missing name", false));
561
+ return;
562
+ }
563
+ const targetId = this.sessions.getTargetIdByName(session.id, name);
564
+ if (!targetId) {
565
+ this.sendError(message, buildError("invalid_request", "Unknown page name", false));
566
+ return;
567
+ }
568
+ const target = session.targets.get(targetId);
569
+ if (target) {
570
+ this.sessions.removeTarget(session.id, targetId);
571
+ await this.closeTabBestEffort(target.tabId);
572
+ if (target.targetId === session.targetId || session.targets.size === 0) {
573
+ this.sendResponse(message, { ok: true });
574
+ this.scheduleSessionCleanup(session.id, "ops_session_closed");
575
+ return;
576
+ }
577
+ }
578
+ this.sendResponse(message, { ok: true });
579
+ }
580
+ async handleGoto(message, session) {
581
+ const payload = isRecord(message.payload) ? message.payload : {};
582
+ const url = typeof payload.url === "string" ? payload.url : null;
583
+ if (!url) {
584
+ this.sendError(message, buildError("invalid_request", "Missing url", false));
585
+ return;
586
+ }
587
+ try {
588
+ const restriction = getRestrictionMessage(new URL(url));
589
+ if (restriction) {
590
+ this.sendError(message, buildError("restricted_url", restriction, false));
591
+ return;
592
+ }
593
+ }
594
+ catch {
595
+ this.sendError(message, buildError("invalid_request", "Invalid url", false));
596
+ return;
597
+ }
598
+ const timeoutMs = typeof payload.timeoutMs === "number" ? payload.timeoutMs : 30000;
599
+ const start = Date.now();
600
+ const target = this.requireActiveTarget(session, message);
601
+ if (!target)
602
+ return;
603
+ await this.tabs.activateTab(target.tabId).catch(() => undefined);
604
+ const updated = await new Promise((resolve) => {
605
+ chrome.tabs.update(target.tabId, { url }, (tab) => {
606
+ resolve(tab ?? null);
607
+ });
608
+ });
609
+ await this.tabs.waitForTabComplete(target.tabId, timeoutMs).catch(() => undefined);
610
+ const refreshed = await this.tabs.getTab(target.tabId);
611
+ const targetRecord = session.targets.get(target.targetId);
612
+ if (targetRecord) {
613
+ targetRecord.url = refreshed?.url ?? updated?.url ?? url;
614
+ targetRecord.title = refreshed?.title ?? updated?.title ?? targetRecord.title;
615
+ }
616
+ this.sendResponse(message, {
617
+ finalUrl: refreshed?.url ?? updated?.url ?? url,
618
+ status: undefined,
619
+ timingMs: Date.now() - start
620
+ });
621
+ }
622
+ async handleWait(message, session) {
623
+ const payload = isRecord(message.payload) ? message.payload : {};
624
+ const timeoutMs = typeof payload.timeoutMs === "number" ? payload.timeoutMs : 30000;
625
+ const start = Date.now();
626
+ const target = this.requireActiveTarget(session, message);
627
+ if (!target)
628
+ return;
629
+ if (typeof payload.ref === "string") {
630
+ const state = payload.state === "visible" || payload.state === "hidden" ? payload.state : "attached";
631
+ const selector = this.resolveSelector(session, payload.ref, message);
632
+ if (!selector)
633
+ return;
634
+ try {
635
+ await this.waitForSelector(target.tabId, selector, state, timeoutMs);
636
+ this.sendResponse(message, { timingMs: Date.now() - start });
637
+ }
638
+ catch (error) {
639
+ this.sendError(message, buildError("timeout", error instanceof Error ? error.message : "Timeout", true));
640
+ }
641
+ return;
642
+ }
643
+ try {
644
+ await this.tabs.waitForTabComplete(target.tabId, timeoutMs);
645
+ this.sendResponse(message, { timingMs: Date.now() - start });
646
+ }
647
+ catch (error) {
648
+ this.sendError(message, buildError("timeout", error instanceof Error ? error.message : "Timeout", true));
649
+ }
650
+ }
651
+ async handleSnapshot(message, session) {
652
+ const payload = isRecord(message.payload) ? message.payload : {};
653
+ const mode = payload.mode === "actionables" ? "actionables" : "outline";
654
+ const maxChars = typeof payload.maxChars === "number" ? payload.maxChars : 16000;
655
+ const cursor = typeof payload.cursor === "string" ? payload.cursor : undefined;
656
+ const maxNodes = typeof payload.maxNodes === "number" ? payload.maxNodes : undefined;
657
+ const target = this.requireActiveTarget(session, message);
658
+ if (!target)
659
+ return;
660
+ const start = Date.now();
661
+ const entriesData = await buildSnapshot((method, params) => this.cdp.sendCommand({ tabId: target.tabId }, method, params), mode, true, maxNodes);
662
+ const snapshot = session.refStore.setSnapshot(target.targetId, entriesData.entries);
663
+ const startIndex = parseCursor(cursor);
664
+ const { content, truncated, nextCursor } = paginate(entriesData.lines, startIndex, maxChars);
665
+ const contentBytes = this.encoder.encode(content).length;
666
+ if (contentBytes > MAX_SNAPSHOT_BYTES) {
667
+ this.sendError(message, buildError("snapshot_too_large", "Snapshot exceeded max size.", false, {
668
+ maxSnapshotBytes: MAX_SNAPSHOT_BYTES,
669
+ actualBytes: contentBytes
670
+ }));
671
+ return;
672
+ }
673
+ const tab = await this.tabs.getTab(target.tabId);
674
+ this.sendResponse(message, {
675
+ snapshotId: snapshot.snapshotId,
676
+ url: tab?.url ?? undefined,
677
+ title: tab?.title ?? undefined,
678
+ content,
679
+ truncated,
680
+ nextCursor,
681
+ refCount: snapshot.count,
682
+ timingMs: Date.now() - start,
683
+ warnings: entriesData.warnings
684
+ });
685
+ }
686
+ async handleClick(message, session) {
687
+ const selector = this.resolveSelector(session, message.payload, message);
688
+ if (!selector)
689
+ return;
690
+ const target = this.requireActiveTarget(session, message);
691
+ if (!target)
692
+ return;
693
+ const start = Date.now();
694
+ const before = await this.tabs.getTab(target.tabId);
695
+ await this.dom.click(target.tabId, selector);
696
+ const after = await this.tabs.getTab(target.tabId);
697
+ const navigated = Boolean(before?.url && after?.url && before.url !== after.url);
698
+ this.sendResponse(message, { timingMs: Date.now() - start, navigated });
699
+ }
700
+ async handleHover(message, session) {
701
+ const selector = this.resolveSelector(session, message.payload, message);
702
+ if (!selector)
703
+ return;
704
+ const target = this.requireActiveTarget(session, message);
705
+ if (!target)
706
+ return;
707
+ const start = Date.now();
708
+ await this.dom.hover(target.tabId, selector);
709
+ this.sendResponse(message, { timingMs: Date.now() - start });
710
+ }
711
+ async handlePress(message, session) {
712
+ const payload = isRecord(message.payload) ? message.payload : {};
713
+ const key = typeof payload.key === "string" ? payload.key : null;
714
+ if (!key) {
715
+ this.sendError(message, buildError("invalid_request", "Missing key", false));
716
+ return;
717
+ }
718
+ const target = this.requireActiveTarget(session, message);
719
+ if (!target)
720
+ return;
721
+ const selector = typeof payload.ref === "string" ? this.resolveSelector(session, payload.ref, message) : null;
722
+ if (payload.ref && !selector)
723
+ return;
724
+ const start = Date.now();
725
+ await this.dom.press(target.tabId, selector, key);
726
+ this.sendResponse(message, { timingMs: Date.now() - start });
727
+ }
728
+ async handleCheck(message, session, checked) {
729
+ const selector = this.resolveSelector(session, message.payload, message);
730
+ if (!selector)
731
+ return;
732
+ const target = this.requireActiveTarget(session, message);
733
+ if (!target)
734
+ return;
735
+ const start = Date.now();
736
+ await this.dom.setChecked(target.tabId, selector, checked);
737
+ this.sendResponse(message, { timingMs: Date.now() - start });
738
+ }
739
+ async handleType(message, session) {
740
+ const payload = isRecord(message.payload) ? message.payload : {};
741
+ const ref = typeof payload.ref === "string" ? payload.ref : null;
742
+ const text = typeof payload.text === "string" ? payload.text : null;
743
+ if (!ref || text === null) {
744
+ this.sendError(message, buildError("invalid_request", "Missing ref or text", false));
745
+ return;
746
+ }
747
+ const selector = this.resolveSelector(session, ref, message);
748
+ if (!selector)
749
+ return;
750
+ const target = this.requireActiveTarget(session, message);
751
+ if (!target)
752
+ return;
753
+ const start = Date.now();
754
+ await this.dom.type(target.tabId, selector, text, payload.clear === true, payload.submit === true);
755
+ this.sendResponse(message, { timingMs: Date.now() - start });
756
+ }
757
+ async handleSelect(message, session) {
758
+ const payload = isRecord(message.payload) ? message.payload : {};
759
+ const ref = typeof payload.ref === "string" ? payload.ref : null;
760
+ const values = Array.isArray(payload.values) ? payload.values.filter((val) => typeof val === "string") : null;
761
+ if (!ref || !values) {
762
+ this.sendError(message, buildError("invalid_request", "Missing ref or values", false));
763
+ return;
764
+ }
765
+ const selector = this.resolveSelector(session, ref, message);
766
+ if (!selector)
767
+ return;
768
+ const target = this.requireActiveTarget(session, message);
769
+ if (!target)
770
+ return;
771
+ await this.dom.select(target.tabId, selector, values);
772
+ this.sendResponse(message, {});
773
+ }
774
+ async handleScroll(message, session) {
775
+ const payload = isRecord(message.payload) ? message.payload : {};
776
+ const dy = typeof payload.dy === "number" ? payload.dy : 0;
777
+ const ref = typeof payload.ref === "string" ? payload.ref : undefined;
778
+ const selector = ref ? this.resolveSelector(session, ref, message) ?? undefined : undefined;
779
+ if (ref && !selector)
780
+ return;
781
+ const target = this.requireActiveTarget(session, message);
782
+ if (!target)
783
+ return;
784
+ await this.dom.scroll(target.tabId, dy, selector);
785
+ this.sendResponse(message, {});
786
+ }
787
+ async handleScrollIntoView(message, session) {
788
+ const selector = this.resolveSelector(session, message.payload, message);
789
+ if (!selector)
790
+ return;
791
+ const target = this.requireActiveTarget(session, message);
792
+ if (!target)
793
+ return;
794
+ const start = Date.now();
795
+ await this.dom.scrollIntoView(target.tabId, selector);
796
+ this.sendResponse(message, { timingMs: Date.now() - start });
797
+ }
798
+ async handleDomGetHtml(message, session) {
799
+ const payload = isRecord(message.payload) ? message.payload : {};
800
+ const ref = typeof payload.ref === "string" ? payload.ref : null;
801
+ const maxChars = typeof payload.maxChars === "number" ? payload.maxChars : 8000;
802
+ if (!ref) {
803
+ this.sendError(message, buildError("invalid_request", "Missing ref", false));
804
+ return;
805
+ }
806
+ const selector = this.resolveSelector(session, ref, message);
807
+ if (!selector)
808
+ return;
809
+ const target = this.requireActiveTarget(session, message);
810
+ if (!target)
811
+ return;
812
+ const html = await this.dom.getOuterHtml(target.tabId, selector);
813
+ const truncated = html.length > maxChars;
814
+ const outerHTML = truncated ? html.slice(0, maxChars) : html;
815
+ this.sendResponse(message, { outerHTML, truncated });
816
+ }
817
+ async handleDomGetText(message, session) {
818
+ const payload = isRecord(message.payload) ? message.payload : {};
819
+ const ref = typeof payload.ref === "string" ? payload.ref : null;
820
+ const maxChars = typeof payload.maxChars === "number" ? payload.maxChars : 8000;
821
+ if (!ref) {
822
+ this.sendError(message, buildError("invalid_request", "Missing ref", false));
823
+ return;
824
+ }
825
+ const selector = this.resolveSelector(session, ref, message);
826
+ if (!selector)
827
+ return;
828
+ const target = this.requireActiveTarget(session, message);
829
+ if (!target)
830
+ return;
831
+ const text = await this.dom.getInnerText(target.tabId, selector);
832
+ const truncated = text.length > maxChars;
833
+ this.sendResponse(message, { text: truncated ? text.slice(0, maxChars) : text, truncated });
834
+ }
835
+ async handleDomGetAttr(message, session) {
836
+ const payload = isRecord(message.payload) ? message.payload : {};
837
+ const ref = typeof payload.ref === "string" ? payload.ref : null;
838
+ const name = typeof payload.name === "string" ? payload.name : null;
839
+ if (!ref || !name) {
840
+ this.sendError(message, buildError("invalid_request", "Missing ref or name", false));
841
+ return;
842
+ }
843
+ const selector = this.resolveSelector(session, ref, message);
844
+ if (!selector)
845
+ return;
846
+ const target = this.requireActiveTarget(session, message);
847
+ if (!target)
848
+ return;
849
+ const value = await this.dom.getAttr(target.tabId, selector, name);
850
+ this.sendResponse(message, { value });
851
+ }
852
+ async handleDomGetValue(message, session) {
853
+ const payload = isRecord(message.payload) ? message.payload : {};
854
+ const ref = typeof payload.ref === "string" ? payload.ref : null;
855
+ if (!ref) {
856
+ this.sendError(message, buildError("invalid_request", "Missing ref", false));
857
+ return;
858
+ }
859
+ const selector = this.resolveSelector(session, ref, message);
860
+ if (!selector)
861
+ return;
862
+ const target = this.requireActiveTarget(session, message);
863
+ if (!target)
864
+ return;
865
+ const value = await this.dom.getValue(target.tabId, selector);
866
+ this.sendResponse(message, { value });
867
+ }
868
+ async handleDomIsVisible(message, session) {
869
+ const selector = this.resolveSelector(session, message.payload, message);
870
+ if (!selector)
871
+ return;
872
+ const target = this.requireActiveTarget(session, message);
873
+ if (!target)
874
+ return;
875
+ const visible = await this.dom.isVisible(target.tabId, selector);
876
+ this.sendResponse(message, { value: visible });
877
+ }
878
+ async handleDomIsEnabled(message, session) {
879
+ const selector = this.resolveSelector(session, message.payload, message);
880
+ if (!selector)
881
+ return;
882
+ const target = this.requireActiveTarget(session, message);
883
+ if (!target)
884
+ return;
885
+ const enabled = await this.dom.isEnabled(target.tabId, selector);
886
+ this.sendResponse(message, { value: enabled });
887
+ }
888
+ async handleDomIsChecked(message, session) {
889
+ const selector = this.resolveSelector(session, message.payload, message);
890
+ if (!selector)
891
+ return;
892
+ const target = this.requireActiveTarget(session, message);
893
+ if (!target)
894
+ return;
895
+ const checked = await this.dom.isChecked(target.tabId, selector);
896
+ this.sendResponse(message, { value: checked });
897
+ }
898
+ async handleClonePage(message, session) {
899
+ const payload = isRecord(message.payload) ? message.payload : {};
900
+ const target = this.requireActiveTarget(session, message);
901
+ if (!target)
902
+ return;
903
+ const capture = await this.dom.captureDom(target.tabId, "body", {
904
+ sanitize: payload.sanitize !== false,
905
+ maxNodes: typeof payload.maxNodes === "number" ? payload.maxNodes : undefined,
906
+ inlineStyles: payload.inlineStyles !== false,
907
+ styleAllowlist: Array.isArray(payload.styleAllowlist) ? payload.styleAllowlist.filter((item) => typeof item === "string") : [],
908
+ skipStyleValues: Array.isArray(payload.skipStyleValues) ? payload.skipStyleValues.filter((item) => typeof item === "string") : []
909
+ });
910
+ this.sendResponse(message, { capture });
911
+ }
912
+ async handleCloneComponent(message, session) {
913
+ const payload = isRecord(message.payload) ? message.payload : {};
914
+ const ref = typeof payload.ref === "string" ? payload.ref : null;
915
+ if (!ref) {
916
+ this.sendError(message, buildError("invalid_request", "Missing ref", false));
917
+ return;
918
+ }
919
+ const selector = this.resolveSelector(session, ref, message);
920
+ if (!selector)
921
+ return;
922
+ const target = this.requireActiveTarget(session, message);
923
+ if (!target)
924
+ return;
925
+ const capture = await this.dom.captureDom(target.tabId, selector, {
926
+ sanitize: payload.sanitize !== false,
927
+ maxNodes: typeof payload.maxNodes === "number" ? payload.maxNodes : undefined,
928
+ inlineStyles: payload.inlineStyles !== false,
929
+ styleAllowlist: Array.isArray(payload.styleAllowlist) ? payload.styleAllowlist.filter((item) => typeof item === "string") : [],
930
+ skipStyleValues: Array.isArray(payload.skipStyleValues) ? payload.skipStyleValues.filter((item) => typeof item === "string") : []
931
+ });
932
+ this.sendResponse(message, { capture });
933
+ }
934
+ async handlePerf(message, session) {
935
+ const target = this.requireActiveTarget(session, message);
936
+ if (!target)
937
+ return;
938
+ const result = await this.cdp.sendCommand({ tabId: target.tabId }, "Performance.getMetrics", {});
939
+ this.sendResponse(message, { metrics: Array.isArray(result.metrics) ? result.metrics : [] });
940
+ }
941
+ async handleScreenshot(message, session) {
942
+ const target = this.requireActiveTarget(session, message);
943
+ if (!target)
944
+ return;
945
+ try {
946
+ const result = await withTimeout(this.cdp.sendCommand({ tabId: target.tabId }, "Page.captureScreenshot", { format: "png" }), SCREENSHOT_TIMEOUT_MS, "Ops screenshot timed out");
947
+ if (result?.data) {
948
+ this.sendResponse(message, { base64: result.data });
949
+ return;
950
+ }
951
+ }
952
+ catch (error) {
953
+ logError("ops.screenshot", error, { code: "screenshot_failed" });
954
+ }
955
+ const fallback = await this.captureVisibleTab(target.tabId);
956
+ if (fallback) {
957
+ this.sendResponse(message, { base64: fallback, warning: "visible_only_fallback" });
958
+ return;
959
+ }
960
+ this.sendError(message, buildError("execution_failed", "Screenshot failed", false));
961
+ }
962
+ async handleConsolePoll(message, session) {
963
+ const payload = isRecord(message.payload) ? message.payload : {};
964
+ const sinceSeq = typeof payload.sinceSeq === "number" ? payload.sinceSeq : 0;
965
+ const max = typeof payload.max === "number" ? payload.max : 50;
966
+ const events = session.consoleEvents.filter((event) => event.seq > sinceSeq).slice(0, max);
967
+ const lastEvent = events.at(-1);
968
+ const nextSeq = lastEvent ? lastEvent.seq : sinceSeq;
969
+ this.sendResponse(message, { events, nextSeq });
970
+ }
971
+ async handleNetworkPoll(message, session) {
972
+ const payload = isRecord(message.payload) ? message.payload : {};
973
+ const sinceSeq = typeof payload.sinceSeq === "number" ? payload.sinceSeq : 0;
974
+ const max = typeof payload.max === "number" ? payload.max : 50;
975
+ const events = session.networkEvents.filter((event) => event.seq > sinceSeq).slice(0, max);
976
+ const lastEvent = events.at(-1);
977
+ const nextSeq = lastEvent ? lastEvent.seq : sinceSeq;
978
+ this.sendResponse(message, { events, nextSeq });
979
+ }
980
+ async handleStorageSetCookies(message, session) {
981
+ const payload = isRecord(message.payload) ? message.payload : {};
982
+ const cookies = Array.isArray(payload.cookies) ? payload.cookies : null;
983
+ if (!cookies) {
984
+ this.sendError(message, buildError("invalid_request", "Missing cookies", false));
985
+ return;
986
+ }
987
+ const strict = payload.strict !== false;
988
+ const requestId = typeof payload.requestId === "string" && payload.requestId.trim().length > 0
989
+ ? payload.requestId
990
+ : createId();
991
+ const normalized = [];
992
+ const rejected = [];
993
+ cookies.forEach((entry, index) => {
994
+ if (!isRecord(entry)) {
995
+ rejected.push({ index, reason: "Invalid cookie entry: expected object." });
996
+ return;
997
+ }
998
+ const validation = validateCookieRecord(entry);
999
+ if (!validation.valid) {
1000
+ rejected.push({ index, reason: validation.reason });
1001
+ return;
1002
+ }
1003
+ normalized.push(validation.cookie);
1004
+ });
1005
+ if (strict && rejected.length > 0) {
1006
+ this.sendError(message, buildError("invalid_request", `Cookie import rejected ${rejected.length} entries.`, false));
1007
+ return;
1008
+ }
1009
+ if (normalized.length > 0) {
1010
+ const target = this.requireActiveTarget(session, message);
1011
+ if (!target)
1012
+ return;
1013
+ try {
1014
+ await this.cdp.sendCommand({ tabId: target.tabId }, "Network.setCookies", { cookies: normalized });
1015
+ }
1016
+ catch (error) {
1017
+ const detail = error instanceof Error ? error.message : "Cookie import failed";
1018
+ this.sendError(message, buildError("execution_failed", detail, false));
1019
+ return;
1020
+ }
1021
+ }
1022
+ this.sendResponse(message, {
1023
+ requestId,
1024
+ imported: normalized.length,
1025
+ rejected
1026
+ });
1027
+ }
1028
+ async handleStorageGetCookies(message, session) {
1029
+ const payload = isRecord(message.payload) ? message.payload : {};
1030
+ const requestId = typeof payload.requestId === "string" && payload.requestId.trim().length > 0
1031
+ ? payload.requestId
1032
+ : createId();
1033
+ let urls;
1034
+ try {
1035
+ urls = parseCookieFilterUrls(payload.urls);
1036
+ }
1037
+ catch (error) {
1038
+ const detail = error instanceof Error ? error.message : "Invalid cookie url filter.";
1039
+ this.sendError(message, buildError("invalid_request", detail, false));
1040
+ return;
1041
+ }
1042
+ const target = this.requireActiveTarget(session, message);
1043
+ if (!target)
1044
+ return;
1045
+ let rawCookies = [];
1046
+ try {
1047
+ const response = await this.cdp.sendCommand({ tabId: target.tabId }, "Network.getCookies", urls ? { urls } : {});
1048
+ rawCookies = Array.isArray(response.cookies) ? response.cookies : [];
1049
+ }
1050
+ catch (error) {
1051
+ const detail = error instanceof Error ? error.message : "Cookie list failed";
1052
+ this.sendError(message, buildError("execution_failed", detail, false));
1053
+ return;
1054
+ }
1055
+ const cookies = rawCookies
1056
+ .map((entry) => toCookieListRecord(entry))
1057
+ .filter((entry) => entry !== null);
1058
+ this.sendResponse(message, {
1059
+ requestId,
1060
+ cookies,
1061
+ count: cookies.length
1062
+ });
1063
+ }
1064
+ async enableSessionDomains(session) {
1065
+ try {
1066
+ await this.cdp.sendCommand({ tabId: session.tabId }, "Runtime.enable", {});
1067
+ await this.cdp.sendCommand({ tabId: session.tabId }, "Network.enable", {});
1068
+ await this.cdp.sendCommand({ tabId: session.tabId }, "Performance.enable", {});
1069
+ }
1070
+ catch (error) {
1071
+ logError("ops.enable_domains", error, { code: "enable_domains_failed" });
1072
+ }
1073
+ }
1074
+ async withSession(message, clientId, handler) {
1075
+ const session = this.getSessionForMessage(message, clientId);
1076
+ if (!session)
1077
+ return;
1078
+ if (!TARGET_SCOPED_COMMANDS.has(message.command)) {
1079
+ session.queue = session.queue.then(() => handler(session), () => handler(session));
1080
+ await session.queue;
1081
+ return;
1082
+ }
1083
+ try {
1084
+ await this.withTargetQueue(message, session, handler);
1085
+ }
1086
+ catch (error) {
1087
+ if (isParallelismBackpressureError(error)) {
1088
+ this.sendError(message, buildError("parallelism_backpressure", error.message, true, error.details));
1089
+ return;
1090
+ }
1091
+ throw error;
1092
+ }
1093
+ }
1094
+ resolveTargetIdForQueue(session, message) {
1095
+ const payload = isRecord(message.payload) ? message.payload : {};
1096
+ const requested = typeof payload.targetId === "string" ? payload.targetId.trim() : "";
1097
+ return requested || session.activeTargetId || session.targetId;
1098
+ }
1099
+ sessionQueueAgeMs(session) {
1100
+ let oldest = null;
1101
+ for (const value of session.targetQueueOldestAt.values()) {
1102
+ if (oldest === null || value < oldest) {
1103
+ oldest = value;
1104
+ }
1105
+ }
1106
+ return oldest === null ? 0 : Math.max(0, Date.now() - oldest);
1107
+ }
1108
+ sampleParallelism(session) {
1109
+ const now = Date.now();
1110
+ const policy = session.parallelismPolicy;
1111
+ if (session.parallelismState.lastSampleAt > 0
1112
+ && now - session.parallelismState.lastSampleAt < policy.sampleIntervalMs) {
1113
+ return {
1114
+ state: session.parallelismState,
1115
+ pressure: session.parallelismState.lastPressure,
1116
+ targetCap: session.parallelismState.effectiveCap,
1117
+ waitQueueDepth: session.pendingParallel,
1118
+ waitQueueAgeMs: this.sessionQueueAgeMs(session)
1119
+ };
1120
+ }
1121
+ const snapshot = evaluateOpsGovernor(policy, session.parallelismState, {
1122
+ hostFreeMemPct: 100,
1123
+ rssUsagePct: 0,
1124
+ queueAgeMs: this.sessionQueueAgeMs(session),
1125
+ queueDepth: session.pendingParallel,
1126
+ discardedSignals: session.discardedSignals,
1127
+ frozenSignals: session.frozenSignals
1128
+ }, now);
1129
+ session.parallelismState = snapshot.state;
1130
+ session.discardedSignals = 0;
1131
+ session.frozenSignals = 0;
1132
+ return snapshot;
1133
+ }
1134
+ wakeParallelWaiters(session) {
1135
+ const waiters = this.parallelWaiters.get(session.id);
1136
+ if (!waiters || waiters.length === 0) {
1137
+ return;
1138
+ }
1139
+ this.sampleParallelism(session);
1140
+ while (waiters.length > 0 && session.parallelInFlight < session.parallelismState.effectiveCap) {
1141
+ const waiter = waiters.shift();
1142
+ if (!waiter)
1143
+ break;
1144
+ if (waiter.timer !== null) {
1145
+ clearTimeout(waiter.timer);
1146
+ waiter.timer = null;
1147
+ }
1148
+ session.parallelInFlight += 1;
1149
+ waiter.resolve();
1150
+ }
1151
+ if (waiters.length === 0) {
1152
+ this.parallelWaiters.delete(session.id);
1153
+ }
1154
+ }
1155
+ createParallelismBackpressureError(session, targetId, timeoutMs) {
1156
+ const snapshot = this.sampleParallelism(session);
1157
+ const details = {
1158
+ sessionId: session.id,
1159
+ targetId,
1160
+ effectiveParallelCap: session.parallelismState.effectiveCap,
1161
+ inFlight: session.parallelInFlight,
1162
+ waitQueueDepth: snapshot.waitQueueDepth,
1163
+ waitQueueAgeMs: snapshot.waitQueueAgeMs,
1164
+ pressure: snapshot.pressure,
1165
+ timeoutMs
1166
+ };
1167
+ const error = new Error(`Parallelism cap reached for target ${targetId}; retry later.`);
1168
+ error.code = "parallelism_backpressure";
1169
+ error.details = details;
1170
+ return error;
1171
+ }
1172
+ async acquireParallelSlot(session, targetId, timeoutMs) {
1173
+ const waiters = this.parallelWaiters.get(session.id) ?? [];
1174
+ this.parallelWaiters.set(session.id, waiters);
1175
+ this.sampleParallelism(session);
1176
+ if (session.parallelInFlight < session.parallelismState.effectiveCap && waiters.length === 0) {
1177
+ session.parallelInFlight += 1;
1178
+ return;
1179
+ }
1180
+ await new Promise((resolve, reject) => {
1181
+ const waiter = {
1182
+ targetId,
1183
+ enqueuedAt: Date.now(),
1184
+ timeoutMs,
1185
+ resolve,
1186
+ reject,
1187
+ timer: null
1188
+ };
1189
+ waiter.timer = setTimeout(() => {
1190
+ const index = waiters.indexOf(waiter);
1191
+ if (index >= 0) {
1192
+ waiters.splice(index, 1);
1193
+ }
1194
+ if (waiters.length === 0) {
1195
+ this.parallelWaiters.delete(session.id);
1196
+ }
1197
+ reject(this.createParallelismBackpressureError(session, targetId, timeoutMs));
1198
+ }, timeoutMs);
1199
+ waiters.push(waiter);
1200
+ this.wakeParallelWaiters(session);
1201
+ });
1202
+ }
1203
+ releaseParallelSlot(session) {
1204
+ session.parallelInFlight = Math.max(0, session.parallelInFlight - 1);
1205
+ this.wakeParallelWaiters(session);
1206
+ }
1207
+ async withTargetQueue(message, session, handler) {
1208
+ const targetId = this.resolveTargetIdForQueue(session, message);
1209
+ const enqueuedAt = Date.now();
1210
+ const previous = session.targetQueues.get(targetId) ?? Promise.resolve();
1211
+ let releaseQueue = () => { };
1212
+ const gate = new Promise((resolve) => {
1213
+ releaseQueue = resolve;
1214
+ });
1215
+ const tail = previous.then(() => gate, () => gate);
1216
+ session.targetQueues.set(targetId, tail);
1217
+ session.pendingParallel += 1;
1218
+ session.targetQueueDepth.set(targetId, (session.targetQueueDepth.get(targetId) ?? 0) + 1);
1219
+ if (!session.targetQueueOldestAt.has(targetId)) {
1220
+ session.targetQueueOldestAt.set(targetId, enqueuedAt);
1221
+ }
1222
+ await previous;
1223
+ let acquired = false;
1224
+ try {
1225
+ await this.acquireParallelSlot(session, targetId, session.parallelismPolicy.backpressureTimeoutMs);
1226
+ acquired = true;
1227
+ await handler(session);
1228
+ }
1229
+ finally {
1230
+ if (acquired) {
1231
+ this.releaseParallelSlot(session);
1232
+ }
1233
+ releaseQueue();
1234
+ const depth = (session.targetQueueDepth.get(targetId) ?? 1) - 1;
1235
+ if (depth <= 0) {
1236
+ session.targetQueueDepth.delete(targetId);
1237
+ session.targetQueueOldestAt.delete(targetId);
1238
+ }
1239
+ else {
1240
+ session.targetQueueDepth.set(targetId, depth);
1241
+ }
1242
+ session.pendingParallel = Math.max(0, session.pendingParallel - 1);
1243
+ if (session.targetQueues.get(targetId) === tail) {
1244
+ session.targetQueues.delete(targetId);
1245
+ }
1246
+ }
1247
+ }
1248
+ getSessionForMessage(message, clientId) {
1249
+ const opsSessionId = message.opsSessionId;
1250
+ if (!opsSessionId) {
1251
+ this.sendError(message, buildError("invalid_request", "Missing opsSessionId", false));
1252
+ return null;
1253
+ }
1254
+ const session = this.sessions.get(opsSessionId);
1255
+ if (!session) {
1256
+ this.sendError(message, buildError("invalid_session", "Unknown ops session", false));
1257
+ return null;
1258
+ }
1259
+ if (session.state === "closing") {
1260
+ const leaseId = typeof message.leaseId === "string" ? message.leaseId : "";
1261
+ if (leaseId && leaseId === session.leaseId) {
1262
+ this.reclaimSession(session, clientId);
1263
+ }
1264
+ else {
1265
+ this.sendError(message, buildError("not_owner", "Client does not own session", false));
1266
+ return null;
1267
+ }
1268
+ }
1269
+ if (session.ownerClientId !== clientId) {
1270
+ this.sendError(message, buildError("not_owner", "Client does not own session", false));
1271
+ return null;
1272
+ }
1273
+ if (typeof message.leaseId !== "string" || message.leaseId !== session.leaseId) {
1274
+ this.sendError(message, buildError("not_owner", "Lease does not match session owner", false));
1275
+ return null;
1276
+ }
1277
+ session.lastUsedAt = Date.now();
1278
+ return session;
1279
+ }
1280
+ requestedTargetId(session, message) {
1281
+ const payload = isRecord(message.payload) ? message.payload : {};
1282
+ if (typeof payload.targetId === "string" && payload.targetId.trim().length > 0) {
1283
+ return payload.targetId.trim();
1284
+ }
1285
+ return session.activeTargetId || null;
1286
+ }
1287
+ requireActiveTarget(session, message) {
1288
+ const targetId = this.requestedTargetId(session, message);
1289
+ if (!targetId) {
1290
+ this.sendError(message, buildError("invalid_request", "No active target", false));
1291
+ return null;
1292
+ }
1293
+ const target = session.targets.get(targetId);
1294
+ if (!target) {
1295
+ this.sendError(message, buildError("invalid_request", "Active target missing", false));
1296
+ return null;
1297
+ }
1298
+ if (target.url) {
1299
+ const restriction = isRestrictedUrl(target.url);
1300
+ if (restriction.restricted) {
1301
+ this.sendError(message, buildError("restricted_url", restriction.message ?? "Restricted tab.", false));
1302
+ return null;
1303
+ }
1304
+ }
1305
+ return { tabId: target.tabId, targetId: target.targetId };
1306
+ }
1307
+ resolveSelector(session, refOrPayload, message) {
1308
+ const ref = typeof refOrPayload === "string"
1309
+ ? refOrPayload
1310
+ : (isRecord(refOrPayload) && typeof refOrPayload.ref === "string" ? refOrPayload.ref : null);
1311
+ if (!ref) {
1312
+ this.sendError(message, buildError("invalid_request", "Missing ref", false));
1313
+ return null;
1314
+ }
1315
+ const targetId = this.requestedTargetId(session, message);
1316
+ if (!targetId) {
1317
+ this.sendError(message, buildError("invalid_request", "No active target", false));
1318
+ return null;
1319
+ }
1320
+ const entry = session.refStore.resolve(targetId, ref);
1321
+ if (!entry) {
1322
+ this.sendError(message, buildError("invalid_request", `Unknown ref: ${ref}`, false));
1323
+ return null;
1324
+ }
1325
+ return entry.selector;
1326
+ }
1327
+ async waitForSelector(tabId, selector, state, timeoutMs) {
1328
+ const start = Date.now();
1329
+ while (Date.now() - start < timeoutMs) {
1330
+ const snapshot = await this.dom.getSelectorState(tabId, selector);
1331
+ if (state === "attached" && snapshot.attached)
1332
+ return;
1333
+ if (state === "visible" && snapshot.visible)
1334
+ return;
1335
+ if (state === "hidden" && (!snapshot.attached || !snapshot.visible))
1336
+ return;
1337
+ await delay(200);
1338
+ }
1339
+ throw new Error("Wait for selector timed out");
1340
+ }
1341
+ cleanupSession(session, event) {
1342
+ this.clearClosingTimer(session.id);
1343
+ const waiters = this.parallelWaiters.get(session.id);
1344
+ if (waiters) {
1345
+ for (const waiter of waiters) {
1346
+ if (waiter.timer !== null) {
1347
+ clearTimeout(waiter.timer);
1348
+ waiter.timer = null;
1349
+ }
1350
+ waiter.reject(new Error("Ops session closed while waiting for parallelism slot."));
1351
+ }
1352
+ this.parallelWaiters.delete(session.id);
1353
+ }
1354
+ this.sessions.delete(session.id);
1355
+ for (const target of session.targets.values()) {
1356
+ void this.cdp.detachTab(target.tabId).catch(() => undefined);
1357
+ }
1358
+ this.sendEvent({
1359
+ type: "ops_event",
1360
+ clientId: session.ownerClientId,
1361
+ opsSessionId: session.id,
1362
+ event,
1363
+ payload: { tabId: session.tabId, targetId: session.targetId }
1364
+ });
1365
+ }
1366
+ handleClosedTarget(tabId, event) {
1367
+ const session = this.sessions.getByTabId(tabId);
1368
+ if (!session)
1369
+ return;
1370
+ const targetId = this.sessions.getTargetIdByTabId(session.id, tabId);
1371
+ if (!targetId)
1372
+ return;
1373
+ const removedTarget = this.sessions.removeTarget(session.id, targetId);
1374
+ if (!removedTarget)
1375
+ return;
1376
+ if (targetId === session.targetId || session.targets.size === 0) {
1377
+ this.cleanupSession(session, event);
1378
+ }
1379
+ }
1380
+ handleDebuggerDetachForTab(tabId) {
1381
+ const session = this.sessions.getByTabId(tabId);
1382
+ if (!session)
1383
+ return;
1384
+ if (tabId === session.tabId) {
1385
+ // Root tab detach can be transient during child-target shutdown; tab removal handler owns root teardown.
1386
+ return;
1387
+ }
1388
+ this.handleClosedTarget(tabId, "ops_session_closed");
1389
+ }
1390
+ async closeTabBestEffort(tabId) {
1391
+ try {
1392
+ await withTimeout(this.tabs.closeTab(tabId), TAB_CLOSE_TIMEOUT_MS, "Ops tab close timed out");
1393
+ }
1394
+ catch (error) {
1395
+ logError("ops.close_tab", error, {
1396
+ code: "close_tab_failed",
1397
+ extra: { tabId }
1398
+ });
1399
+ }
1400
+ }
1401
+ scheduleSessionCleanup(sessionId, event) {
1402
+ setTimeout(() => {
1403
+ const session = this.sessions.get(sessionId);
1404
+ if (!session) {
1405
+ return;
1406
+ }
1407
+ this.cleanupSession(session, event);
1408
+ }, 0);
1409
+ }
1410
+ sendResponse(message, payload) {
1411
+ const response = {
1412
+ type: "ops_response",
1413
+ requestId: message.requestId,
1414
+ clientId: message.clientId,
1415
+ opsSessionId: message.opsSessionId,
1416
+ payload
1417
+ };
1418
+ const serialized = JSON.stringify(payload ?? null);
1419
+ if (this.encoder.encode(serialized).length <= MAX_OPS_PAYLOAD_BYTES) {
1420
+ this.sendEnvelope(response);
1421
+ return;
1422
+ }
1423
+ const payloadId = createId();
1424
+ const chunkSize = Math.max(1024, MAX_OPS_PAYLOAD_BYTES - 1024);
1425
+ const chunks = [];
1426
+ for (let i = 0; i < serialized.length; i += chunkSize) {
1427
+ chunks.push(serialized.slice(i, i + chunkSize));
1428
+ }
1429
+ this.sendEnvelope({
1430
+ type: "ops_response",
1431
+ requestId: message.requestId,
1432
+ clientId: message.clientId,
1433
+ opsSessionId: message.opsSessionId,
1434
+ chunked: true,
1435
+ payloadId,
1436
+ totalChunks: chunks.length
1437
+ });
1438
+ chunks.forEach((data, index) => {
1439
+ const chunk = {
1440
+ type: "ops_chunk",
1441
+ requestId: message.requestId,
1442
+ clientId: message.clientId,
1443
+ opsSessionId: message.opsSessionId,
1444
+ payloadId,
1445
+ chunkIndex: index,
1446
+ totalChunks: chunks.length,
1447
+ data
1448
+ };
1449
+ this.sendEnvelope(chunk);
1450
+ });
1451
+ }
1452
+ sendError(message, error) {
1453
+ const payload = {
1454
+ type: "ops_error",
1455
+ requestId: message.requestId,
1456
+ clientId: message.clientId,
1457
+ opsSessionId: message.opsSessionId,
1458
+ error
1459
+ };
1460
+ this.sendEnvelope(payload);
1461
+ }
1462
+ sendEvent(event) {
1463
+ this.sendEnvelope(event);
1464
+ }
1465
+ markSessionClosing(session, reason) {
1466
+ if (session.state === "closing")
1467
+ return;
1468
+ session.state = "closing";
1469
+ session.closingReason = reason;
1470
+ session.expiresAt = Date.now() + SESSION_TTL_MS;
1471
+ const timeoutId = setTimeout(() => {
1472
+ this.closingTimers.delete(session.id);
1473
+ const current = this.sessions.get(session.id);
1474
+ if (current && current.state === "closing") {
1475
+ this.cleanupSession(current, "ops_session_expired");
1476
+ }
1477
+ }, SESSION_TTL_MS);
1478
+ this.closingTimers.set(session.id, timeoutId);
1479
+ }
1480
+ reclaimSession(session, clientId) {
1481
+ session.ownerClientId = clientId;
1482
+ session.state = "active";
1483
+ session.expiresAt = undefined;
1484
+ session.closingReason = undefined;
1485
+ this.clearClosingTimer(session.id);
1486
+ }
1487
+ clearClosingTimer(sessionId) {
1488
+ const timer = this.closingTimers.get(sessionId);
1489
+ if (timer) {
1490
+ clearTimeout(timer);
1491
+ this.closingTimers.delete(sessionId);
1492
+ }
1493
+ }
1494
+ async captureVisibleTab(tabId) {
1495
+ const tab = await this.tabs.getTab(tabId);
1496
+ const windowId = tab?.windowId ?? chrome.windows.WINDOW_ID_CURRENT;
1497
+ return await new Promise((resolve) => {
1498
+ chrome.tabs.captureVisibleTab(windowId, { format: "png" }, (dataUrl) => {
1499
+ if (chrome.runtime.lastError) {
1500
+ resolve(null);
1501
+ return;
1502
+ }
1503
+ if (!dataUrl) {
1504
+ resolve(null);
1505
+ return;
1506
+ }
1507
+ const match = dataUrl.match(/^data:image\/png;base64,(.+)$/);
1508
+ const base64 = match?.[1] ?? null;
1509
+ resolve(base64);
1510
+ });
1511
+ });
1512
+ }
1513
+ }
1514
+ const numberInRange = (value, fallback, min, max) => {
1515
+ if (typeof value !== "number" || !Number.isFinite(value)) {
1516
+ return fallback;
1517
+ }
1518
+ return Math.min(max, Math.max(min, value));
1519
+ };
1520
+ const parseParallelismPolicy = (value) => {
1521
+ if (!isRecord(value)) {
1522
+ return DEFAULT_OPS_PARALLELISM_POLICY;
1523
+ }
1524
+ const modeCapsInput = isRecord(value.modeCaps) ? value.modeCaps : {};
1525
+ return {
1526
+ floor: numberInRange(value.floor, DEFAULT_OPS_PARALLELISM_POLICY.floor, 1, 32),
1527
+ backpressureTimeoutMs: numberInRange(value.backpressureTimeoutMs, DEFAULT_OPS_PARALLELISM_POLICY.backpressureTimeoutMs, 100, 120000),
1528
+ sampleIntervalMs: numberInRange(value.sampleIntervalMs, DEFAULT_OPS_PARALLELISM_POLICY.sampleIntervalMs, 250, 60000),
1529
+ recoveryStableWindows: numberInRange(value.recoveryStableWindows, DEFAULT_OPS_PARALLELISM_POLICY.recoveryStableWindows, 1, 20),
1530
+ hostFreeMemMediumPct: numberInRange(value.hostFreeMemMediumPct, DEFAULT_OPS_PARALLELISM_POLICY.hostFreeMemMediumPct, 1, 99),
1531
+ hostFreeMemHighPct: numberInRange(value.hostFreeMemHighPct, DEFAULT_OPS_PARALLELISM_POLICY.hostFreeMemHighPct, 1, 99),
1532
+ hostFreeMemCriticalPct: numberInRange(value.hostFreeMemCriticalPct, DEFAULT_OPS_PARALLELISM_POLICY.hostFreeMemCriticalPct, 1, 99),
1533
+ rssBudgetMb: numberInRange(value.rssBudgetMb, DEFAULT_OPS_PARALLELISM_POLICY.rssBudgetMb, 64, 65536),
1534
+ rssSoftPct: numberInRange(value.rssSoftPct, DEFAULT_OPS_PARALLELISM_POLICY.rssSoftPct, 1, 99),
1535
+ rssHighPct: numberInRange(value.rssHighPct, DEFAULT_OPS_PARALLELISM_POLICY.rssHighPct, 1, 99),
1536
+ rssCriticalPct: numberInRange(value.rssCriticalPct, DEFAULT_OPS_PARALLELISM_POLICY.rssCriticalPct, 1, 99),
1537
+ queueAgeHighMs: numberInRange(value.queueAgeHighMs, DEFAULT_OPS_PARALLELISM_POLICY.queueAgeHighMs, 100, 120000),
1538
+ queueAgeCriticalMs: numberInRange(value.queueAgeCriticalMs, DEFAULT_OPS_PARALLELISM_POLICY.queueAgeCriticalMs, 100, 120000),
1539
+ modeCaps: {
1540
+ managedHeaded: numberInRange(modeCapsInput.managedHeaded, DEFAULT_OPS_PARALLELISM_POLICY.modeCaps.managedHeaded, 1, 64),
1541
+ managedHeadless: numberInRange(modeCapsInput.managedHeadless, DEFAULT_OPS_PARALLELISM_POLICY.modeCaps.managedHeadless, 1, 64),
1542
+ cdpConnectHeaded: numberInRange(modeCapsInput.cdpConnectHeaded, DEFAULT_OPS_PARALLELISM_POLICY.modeCaps.cdpConnectHeaded, 1, 64),
1543
+ cdpConnectHeadless: numberInRange(modeCapsInput.cdpConnectHeadless, DEFAULT_OPS_PARALLELISM_POLICY.modeCaps.cdpConnectHeadless, 1, 64),
1544
+ extensionOpsHeaded: numberInRange(modeCapsInput.extensionOpsHeaded, DEFAULT_OPS_PARALLELISM_POLICY.modeCaps.extensionOpsHeaded, 1, 64),
1545
+ extensionLegacyCdpHeaded: numberInRange(modeCapsInput.extensionLegacyCdpHeaded, DEFAULT_OPS_PARALLELISM_POLICY.modeCaps.extensionLegacyCdpHeaded, 1, 64)
1546
+ }
1547
+ };
1548
+ };
1549
+ const isParallelismBackpressureError = (error) => {
1550
+ if (!(error instanceof Error)) {
1551
+ return false;
1552
+ }
1553
+ const typed = error;
1554
+ return typed.code === "parallelism_backpressure" && typeof typed.details === "object" && typed.details !== null;
1555
+ };
1556
+ const buildError = (code, message, retryable, details) => ({
1557
+ code,
1558
+ message,
1559
+ retryable,
1560
+ details
1561
+ });
1562
+ const validateCookieRecord = (cookie) => {
1563
+ const name = cookie.name?.trim();
1564
+ if (!name) {
1565
+ return { valid: false, reason: "Cookie name is required.", cookie };
1566
+ }
1567
+ if (!/^[^\s;=]+$/.test(name)) {
1568
+ return { valid: false, reason: `Invalid cookie name: ${cookie.name}.`, cookie };
1569
+ }
1570
+ if (typeof cookie.value !== "string" || /\r|\n|;/.test(cookie.value)) {
1571
+ return { valid: false, reason: `Invalid cookie value for ${name}.`, cookie };
1572
+ }
1573
+ const hasUrl = typeof cookie.url === "string" && cookie.url.trim().length > 0;
1574
+ const hasDomain = typeof cookie.domain === "string" && cookie.domain.trim().length > 0;
1575
+ if (!hasUrl && !hasDomain) {
1576
+ return { valid: false, reason: `Cookie ${name} requires url or domain.`, cookie };
1577
+ }
1578
+ let normalizedUrl;
1579
+ if (hasUrl) {
1580
+ try {
1581
+ const parsedUrl = new URL(cookie.url);
1582
+ if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
1583
+ return { valid: false, reason: `Cookie ${name} url must be http(s).`, cookie };
1584
+ }
1585
+ normalizedUrl = parsedUrl.toString();
1586
+ }
1587
+ catch {
1588
+ return { valid: false, reason: `Cookie ${name} has invalid url.`, cookie };
1589
+ }
1590
+ }
1591
+ let normalizedDomain;
1592
+ if (hasDomain) {
1593
+ normalizedDomain = String(cookie.domain).trim().toLowerCase();
1594
+ if (!/^\.?[a-z0-9.-]+$/.test(normalizedDomain) || normalizedDomain.includes("..")) {
1595
+ return { valid: false, reason: `Cookie ${name} has invalid domain.`, cookie };
1596
+ }
1597
+ }
1598
+ const normalizedPath = typeof cookie.path === "string" ? cookie.path.trim() : undefined;
1599
+ if (typeof normalizedPath === "string" && !normalizedPath.startsWith("/")) {
1600
+ return { valid: false, reason: `Cookie ${name} path must start with '/'.`, cookie };
1601
+ }
1602
+ if (typeof cookie.expires !== "undefined") {
1603
+ if (!Number.isFinite(cookie.expires) || cookie.expires < -1) {
1604
+ return { valid: false, reason: `Cookie ${name} has invalid expires.`, cookie };
1605
+ }
1606
+ }
1607
+ if (cookie.sameSite === "None" && cookie.secure !== true) {
1608
+ return { valid: false, reason: `Cookie ${name} with SameSite=None must set secure=true.`, cookie };
1609
+ }
1610
+ const normalizedCookie = {
1611
+ name,
1612
+ value: cookie.value,
1613
+ ...(typeof cookie.expires === "number" ? { expires: cookie.expires } : {}),
1614
+ ...(typeof cookie.httpOnly === "boolean" ? { httpOnly: cookie.httpOnly } : {}),
1615
+ ...(typeof cookie.secure === "boolean" ? { secure: cookie.secure } : {}),
1616
+ ...(cookie.sameSite ? { sameSite: cookie.sameSite } : {})
1617
+ };
1618
+ if (normalizedDomain) {
1619
+ normalizedCookie.domain = normalizedDomain;
1620
+ normalizedCookie.path = normalizedPath ?? "/";
1621
+ }
1622
+ else if (normalizedUrl) {
1623
+ normalizedCookie.url = normalizedUrl;
1624
+ }
1625
+ return {
1626
+ valid: true,
1627
+ reason: "",
1628
+ cookie: normalizedCookie
1629
+ };
1630
+ };
1631
+ const parseCookieFilterUrls = (value) => {
1632
+ if (typeof value === "undefined") {
1633
+ return undefined;
1634
+ }
1635
+ if (!Array.isArray(value)) {
1636
+ throw new Error("Cookie url filters must be an array of strings.");
1637
+ }
1638
+ const normalized = [];
1639
+ const seen = new Set();
1640
+ for (const entry of value) {
1641
+ if (typeof entry !== "string") {
1642
+ throw new Error("Cookie url filters must be an array of strings.");
1643
+ }
1644
+ const trimmed = entry.trim();
1645
+ if (!trimmed) {
1646
+ throw new Error("Cookie url filters must be non-empty strings.");
1647
+ }
1648
+ let parsedUrl;
1649
+ try {
1650
+ parsedUrl = new URL(trimmed);
1651
+ }
1652
+ catch {
1653
+ throw new Error(`Cookie url filter is invalid: ${trimmed}`);
1654
+ }
1655
+ if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
1656
+ throw new Error(`Cookie url filter must be http(s): ${trimmed}`);
1657
+ }
1658
+ const normalizedUrl = parsedUrl.toString();
1659
+ if (seen.has(normalizedUrl)) {
1660
+ continue;
1661
+ }
1662
+ seen.add(normalizedUrl);
1663
+ normalized.push(normalizedUrl);
1664
+ }
1665
+ return normalized.length > 0 ? normalized : undefined;
1666
+ };
1667
+ const toCookieListRecord = (entry) => {
1668
+ if (!isRecord(entry)) {
1669
+ return null;
1670
+ }
1671
+ const name = typeof entry.name === "string" ? entry.name : "";
1672
+ const value = typeof entry.value === "string" ? entry.value : "";
1673
+ const domain = typeof entry.domain === "string" ? entry.domain : "";
1674
+ const path = typeof entry.path === "string" ? entry.path : "";
1675
+ const expires = typeof entry.expires === "number" && Number.isFinite(entry.expires) ? entry.expires : -1;
1676
+ const httpOnly = entry.httpOnly === true;
1677
+ const secure = entry.secure === true;
1678
+ if (!name || !domain || !path) {
1679
+ return null;
1680
+ }
1681
+ const sameSiteRaw = entry.sameSite;
1682
+ const sameSite = sameSiteRaw === "Strict" || sameSiteRaw === "Lax" || sameSiteRaw === "None"
1683
+ ? sameSiteRaw
1684
+ : undefined;
1685
+ return {
1686
+ name,
1687
+ value,
1688
+ domain,
1689
+ path,
1690
+ expires,
1691
+ httpOnly,
1692
+ secure,
1693
+ ...(sameSite ? { sameSite } : {})
1694
+ };
1695
+ };
1696
+ const isRecord = (value) => {
1697
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
1698
+ };
1699
+ const parseCursor = (cursor) => {
1700
+ if (!cursor)
1701
+ return 0;
1702
+ const value = Number(cursor);
1703
+ if (!Number.isFinite(value) || value < 0)
1704
+ return 0;
1705
+ return Math.floor(value);
1706
+ };
1707
+ const paginate = (lines, startIndex, maxChars) => {
1708
+ let total = 0;
1709
+ const parts = [];
1710
+ let idx = startIndex;
1711
+ while (idx < lines.length) {
1712
+ const line = lines[idx];
1713
+ if (line === undefined) {
1714
+ break;
1715
+ }
1716
+ if (total + line.length + 1 > maxChars && parts.length > 0) {
1717
+ break;
1718
+ }
1719
+ parts.push(line);
1720
+ total += line.length + 1;
1721
+ idx += 1;
1722
+ }
1723
+ const truncated = idx < lines.length;
1724
+ const nextCursor = truncated ? String(idx) : undefined;
1725
+ return {
1726
+ content: parts.join("\n"),
1727
+ truncated,
1728
+ nextCursor
1729
+ };
1730
+ };
1731
+ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
1732
+ const withTimeout = async (promise, timeoutMs, message) => {
1733
+ return await new Promise((resolve, reject) => {
1734
+ const timeoutId = setTimeout(() => {
1735
+ reject(new Error(message));
1736
+ }, timeoutMs);
1737
+ promise.then((value) => {
1738
+ clearTimeout(timeoutId);
1739
+ resolve(value);
1740
+ }).catch((error) => {
1741
+ clearTimeout(timeoutId);
1742
+ reject(error);
1743
+ });
1744
+ });
1745
+ };
1746
+ const createId = () => {
1747
+ if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
1748
+ return crypto.randomUUID();
1749
+ }
1750
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
1751
+ };