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
@@ -1,11 +1,351 @@
1
1
  import {
2
+ DaemonClient,
3
+ ScriptRunner,
4
+ buildAnnotateResult,
5
+ buildBlockerArtifacts,
6
+ classifyBlockerSignal,
7
+ createBrowserFallbackPort,
8
+ createConfiguredProviderRuntime,
2
9
  createOpenDevBrowserCore,
3
- extractExtension
4
- } from "./chunk-WTFSMBVH.js";
10
+ createRequestId,
11
+ executeMacroResolution,
12
+ extractExtension,
13
+ fetchDaemonStatusFromMetadata,
14
+ runProductVideoWorkflow,
15
+ runResearchWorkflow,
16
+ runShoppingWorkflow,
17
+ shapeExecutionPayload,
18
+ startDaemon
19
+ } from "./chunk-ST7CO5FA.js";
20
+ import "./chunk-7W3SPXIB.js";
21
+
22
+ // src/cli/remote-manager.ts
23
+ function isLegacyRelayEndpoint(wsEndpoint) {
24
+ try {
25
+ const url = new URL(wsEndpoint);
26
+ const path = url.pathname.endsWith("/") ? url.pathname.slice(0, -1) : url.pathname;
27
+ return path === "/cdp";
28
+ } catch {
29
+ return false;
30
+ }
31
+ }
32
+ var RemoteManager = class {
33
+ client;
34
+ constructor(client) {
35
+ this.client = client;
36
+ }
37
+ launch(options) {
38
+ return this.client.call("session.launch", options);
39
+ }
40
+ connect(options) {
41
+ return this.client.call("session.connect", options);
42
+ }
43
+ connectRelay(wsEndpoint) {
44
+ return this.client.call(
45
+ "session.connect",
46
+ isLegacyRelayEndpoint(wsEndpoint) ? { wsEndpoint, extensionLegacy: true } : { wsEndpoint }
47
+ );
48
+ }
49
+ disconnect(sessionId, closeBrowser = false) {
50
+ return this.client.call("session.disconnect", { sessionId, closeBrowser });
51
+ }
52
+ status(sessionId) {
53
+ return this.client.call("session.status", { sessionId });
54
+ }
55
+ cookieImport(sessionId, cookies, strict = true, requestId) {
56
+ return this.client.call("session.cookieImport", {
57
+ sessionId,
58
+ cookies,
59
+ strict,
60
+ requestId
61
+ });
62
+ }
63
+ cookieList(sessionId, urls, requestId) {
64
+ return this.client.call("session.cookieList", {
65
+ sessionId,
66
+ ...urls && urls.length > 0 ? { urls } : {},
67
+ requestId
68
+ });
69
+ }
70
+ goto(sessionId, url, waitUntil = "load", timeoutMs = 3e4, _sessionOverride, targetId) {
71
+ return this.client.call("nav.goto", {
72
+ sessionId,
73
+ url,
74
+ waitUntil,
75
+ timeoutMs,
76
+ ...typeof targetId === "string" ? { targetId } : {}
77
+ });
78
+ }
79
+ waitForLoad(sessionId, until, timeoutMs = 3e4, targetId) {
80
+ return this.client.call("nav.wait", {
81
+ sessionId,
82
+ until,
83
+ timeoutMs,
84
+ ...typeof targetId === "string" ? { targetId } : {}
85
+ });
86
+ }
87
+ waitForRef(sessionId, ref, state = "attached", timeoutMs = 3e4, targetId) {
88
+ return this.client.call("nav.wait", {
89
+ sessionId,
90
+ ref,
91
+ state,
92
+ timeoutMs,
93
+ ...typeof targetId === "string" ? { targetId } : {}
94
+ });
95
+ }
96
+ snapshot(sessionId, mode, maxChars, cursor, targetId) {
97
+ return this.client.call("nav.snapshot", {
98
+ sessionId,
99
+ mode,
100
+ maxChars,
101
+ cursor,
102
+ ...typeof targetId === "string" ? { targetId } : {}
103
+ });
104
+ }
105
+ click(sessionId, ref, targetId) {
106
+ return this.client.call("interact.click", {
107
+ sessionId,
108
+ ref,
109
+ ...typeof targetId === "string" ? { targetId } : {}
110
+ });
111
+ }
112
+ hover(sessionId, ref, targetId) {
113
+ return this.client.call("interact.hover", {
114
+ sessionId,
115
+ ref,
116
+ ...typeof targetId === "string" ? { targetId } : {}
117
+ });
118
+ }
119
+ press(sessionId, key, ref, targetId) {
120
+ return this.client.call("interact.press", {
121
+ sessionId,
122
+ key,
123
+ ref,
124
+ ...typeof targetId === "string" ? { targetId } : {}
125
+ });
126
+ }
127
+ check(sessionId, ref, targetId) {
128
+ return this.client.call("interact.check", {
129
+ sessionId,
130
+ ref,
131
+ ...typeof targetId === "string" ? { targetId } : {}
132
+ });
133
+ }
134
+ uncheck(sessionId, ref, targetId) {
135
+ return this.client.call("interact.uncheck", {
136
+ sessionId,
137
+ ref,
138
+ ...typeof targetId === "string" ? { targetId } : {}
139
+ });
140
+ }
141
+ type(sessionId, ref, text, clear = false, submit = false, targetId) {
142
+ return this.client.call("interact.type", {
143
+ sessionId,
144
+ ref,
145
+ text,
146
+ clear,
147
+ submit,
148
+ ...typeof targetId === "string" ? { targetId } : {}
149
+ });
150
+ }
151
+ select(sessionId, ref, values, targetId) {
152
+ return this.client.call("interact.select", {
153
+ sessionId,
154
+ ref,
155
+ values,
156
+ ...typeof targetId === "string" ? { targetId } : {}
157
+ });
158
+ }
159
+ scroll(sessionId, dy, ref, targetId) {
160
+ return this.client.call("interact.scroll", {
161
+ sessionId,
162
+ dy,
163
+ ref,
164
+ ...typeof targetId === "string" ? { targetId } : {}
165
+ });
166
+ }
167
+ scrollIntoView(sessionId, ref, targetId) {
168
+ return this.client.call("interact.scrollIntoView", {
169
+ sessionId,
170
+ ref,
171
+ ...typeof targetId === "string" ? { targetId } : {}
172
+ });
173
+ }
174
+ domGetHtml(sessionId, ref, maxChars = 8e3, targetId) {
175
+ return this.client.call("dom.getHtml", {
176
+ sessionId,
177
+ ref,
178
+ maxChars,
179
+ ...typeof targetId === "string" ? { targetId } : {}
180
+ });
181
+ }
182
+ domGetText(sessionId, ref, maxChars = 8e3, targetId) {
183
+ return this.client.call("dom.getText", {
184
+ sessionId,
185
+ ref,
186
+ maxChars,
187
+ ...typeof targetId === "string" ? { targetId } : {}
188
+ });
189
+ }
190
+ domGetAttr(sessionId, ref, name, targetId) {
191
+ return this.client.call("dom.getAttr", {
192
+ sessionId,
193
+ ref,
194
+ name,
195
+ ...typeof targetId === "string" ? { targetId } : {}
196
+ });
197
+ }
198
+ domGetValue(sessionId, ref, targetId) {
199
+ return this.client.call("dom.getValue", {
200
+ sessionId,
201
+ ref,
202
+ ...typeof targetId === "string" ? { targetId } : {}
203
+ });
204
+ }
205
+ domIsVisible(sessionId, ref, targetId) {
206
+ return this.client.call("dom.isVisible", {
207
+ sessionId,
208
+ ref,
209
+ ...typeof targetId === "string" ? { targetId } : {}
210
+ });
211
+ }
212
+ domIsEnabled(sessionId, ref, targetId) {
213
+ return this.client.call("dom.isEnabled", {
214
+ sessionId,
215
+ ref,
216
+ ...typeof targetId === "string" ? { targetId } : {}
217
+ });
218
+ }
219
+ domIsChecked(sessionId, ref, targetId) {
220
+ return this.client.call("dom.isChecked", {
221
+ sessionId,
222
+ ref,
223
+ ...typeof targetId === "string" ? { targetId } : {}
224
+ });
225
+ }
226
+ clonePage(sessionId, targetId) {
227
+ return this.client.call("export.clonePage", {
228
+ sessionId,
229
+ ...typeof targetId === "string" ? { targetId } : {}
230
+ });
231
+ }
232
+ cloneComponent(sessionId, ref, targetId) {
233
+ return this.client.call("export.cloneComponent", {
234
+ sessionId,
235
+ ref,
236
+ ...typeof targetId === "string" ? { targetId } : {}
237
+ });
238
+ }
239
+ perfMetrics(sessionId, targetId) {
240
+ return this.client.call("devtools.perf", {
241
+ sessionId,
242
+ ...typeof targetId === "string" ? { targetId } : {}
243
+ });
244
+ }
245
+ screenshot(sessionId, path, targetId) {
246
+ return this.client.call("page.screenshot", {
247
+ sessionId,
248
+ path,
249
+ ...typeof targetId === "string" ? { targetId } : {}
250
+ });
251
+ }
252
+ consolePoll(sessionId, sinceSeq, max = 50) {
253
+ return this.client.call("devtools.consolePoll", { sessionId, sinceSeq, max });
254
+ }
255
+ networkPoll(sessionId, sinceSeq, max = 50) {
256
+ return this.client.call("devtools.networkPoll", { sessionId, sinceSeq, max });
257
+ }
258
+ listTargets(sessionId, includeUrls = false) {
259
+ return this.client.call("targets.list", { sessionId, includeUrls });
260
+ }
261
+ useTarget(sessionId, targetId) {
262
+ return this.client.call("targets.use", { sessionId, targetId });
263
+ }
264
+ newTarget(sessionId, url) {
265
+ return this.client.call("targets.new", { sessionId, url });
266
+ }
267
+ closeTarget(sessionId, targetId) {
268
+ return this.client.call("targets.close", { sessionId, targetId });
269
+ }
270
+ page(sessionId, name, url) {
271
+ return this.client.call("page.open", { sessionId, name, url });
272
+ }
273
+ listPages(sessionId) {
274
+ return this.client.call("page.list", { sessionId });
275
+ }
276
+ closePage(sessionId, name) {
277
+ return this.client.call("page.close", { sessionId, name });
278
+ }
279
+ async withPage(_sessionId, _targetId, _fn) {
280
+ throw new Error("Direct annotate is unavailable via daemon-managed sessions.");
281
+ }
282
+ };
283
+
284
+ // src/cli/remote-relay.ts
285
+ var emptyStatus = {
286
+ running: false,
287
+ extensionConnected: false,
288
+ extensionHandshakeComplete: false,
289
+ cdpConnected: false,
290
+ annotationConnected: false,
291
+ opsConnected: false,
292
+ pairingRequired: false,
293
+ instanceId: "",
294
+ epoch: 0,
295
+ health: {
296
+ ok: false,
297
+ reason: "relay_down",
298
+ extensionConnected: false,
299
+ extensionHandshakeComplete: false,
300
+ cdpConnected: false,
301
+ annotationConnected: false,
302
+ opsConnected: false,
303
+ pairingRequired: false
304
+ }
305
+ };
306
+ var RemoteRelay = class {
307
+ client;
308
+ lastStatus = emptyStatus;
309
+ lastCdpUrl = null;
310
+ lastAnnotationUrl = null;
311
+ lastOpsUrl = null;
312
+ constructor(client) {
313
+ this.client = client;
314
+ }
315
+ async refresh() {
316
+ try {
317
+ const status = await this.client.call("relay.status");
318
+ this.lastStatus = status;
319
+ const cdpUrl = await this.client.call("relay.cdpUrl");
320
+ this.lastCdpUrl = typeof cdpUrl === "string" ? cdpUrl : null;
321
+ const annotationUrl = await this.client.call("relay.annotationUrl");
322
+ this.lastAnnotationUrl = typeof annotationUrl === "string" ? annotationUrl : null;
323
+ const opsUrl = await this.client.call("relay.opsUrl");
324
+ this.lastOpsUrl = typeof opsUrl === "string" ? opsUrl : null;
325
+ } catch {
326
+ this.lastStatus = emptyStatus;
327
+ this.lastCdpUrl = null;
328
+ this.lastAnnotationUrl = null;
329
+ this.lastOpsUrl = null;
330
+ }
331
+ }
332
+ status() {
333
+ return this.lastStatus;
334
+ }
335
+ getCdpUrl() {
336
+ return this.lastCdpUrl;
337
+ }
338
+ getAnnotationUrl() {
339
+ return this.lastAnnotationUrl;
340
+ }
341
+ getOpsUrl() {
342
+ return this.lastOpsUrl;
343
+ }
344
+ };
5
345
 
6
346
  // src/skills/skill-nudge.ts
7
347
  var SKILL_NUDGE_MARKER = "[opendevbrowser:skill-nudge]";
8
- var SKILL_NUDGE_MESSAGE = `${SKILL_NUDGE_MARKER} If this task likely matches a skill, call the OpenCode skill tool early (skill("<skill-name>", topic?)) and load only the most relevant skill before using browser tools. If unsure which skill applies, check the available skills first.`;
348
+ var SKILL_NUDGE_MESSAGE = `${SKILL_NUDGE_MARKER} If this task likely matches a skill, start with skill("opendevbrowser-best-practices", "quick start"). Use another skill only when it is more relevant.`;
9
349
  function createSkillNudgeState() {
10
350
  return { pending: false, requestedAtMs: null };
11
351
  }
@@ -68,7 +408,7 @@ function consumeContinuityNudge(state, nowMs, maxAgeMs) {
68
408
  }
69
409
  function buildContinuityNudgeMessage(filePath) {
70
410
  const target = filePath?.trim() || DEFAULT_FILE_PATH;
71
- return `${CONTINUITY_NUDGE_MARKER} For long-running tasks, create or update ${target} at the repo root with Goal, Constraints/Assumptions, Key decisions, State (Done/Now/Next), Open questions, and Working set. Keep it brief.`;
411
+ return `${CONTINUITY_NUDGE_MARKER} For long-running tasks, load skill("opendevbrowser-continuity-ledger") and create or update ${target} at the repo root with Goal, Constraints/Assumptions, Key decisions, State (Done/Now/Next), Open questions, and Working set. Keep it brief.`;
72
412
  }
73
413
 
74
414
  // src/tools/launch.ts
@@ -98,7 +438,7 @@ function serializeError(error) {
98
438
  var z = tool.schema;
99
439
  function createLaunchTool(deps) {
100
440
  return tool({
101
- description: "Launch a managed Chrome session and return a sessionId.",
441
+ description: "Launch a browser session (extension relay first) and return a sessionId.",
102
442
  args: {
103
443
  profile: z.string().optional().describe("Profile name for persistent browsing"),
104
444
  headless: z.boolean().optional().describe("Run Chrome in headless mode"),
@@ -108,102 +448,356 @@ function createLaunchTool(deps) {
108
448
  persistProfile: z.boolean().optional().describe("Persist profile data between sessions"),
109
449
  noExtension: z.boolean().optional().describe("Skip extension relay and launch a new browser"),
110
450
  extensionOnly: z.boolean().optional().describe("Require extension relay or fail"),
451
+ extensionLegacy: z.boolean().optional().describe("Use legacy extension relay (/cdp) instead of ops"),
111
452
  waitForExtension: z.boolean().optional().describe("Wait for extension to connect before launching"),
112
453
  waitTimeoutMs: z.number().int().optional().describe("Timeout for waiting on extension (ms)")
113
454
  },
114
455
  async execute(args) {
115
- try {
116
- let relayStatus = deps.relay?.status();
117
- const relayUrl = deps.relay?.getCdpUrl();
118
- const waitTimeoutMs = args.waitTimeoutMs ?? 3e4;
119
- if (args.waitForExtension && deps.relay) {
120
- const connected = await waitForExtension(deps.relay, waitTimeoutMs);
121
- if (connected) {
122
- relayStatus = deps.relay.status();
456
+ let attemptedRebind = false;
457
+ while (true) {
458
+ try {
459
+ await deps.relay?.refresh?.();
460
+ const config = deps.config.get();
461
+ const extensionLegacy = args.extensionLegacy === true;
462
+ let relayStatus = deps.relay?.status();
463
+ let relayUrl = extensionLegacy ? deps.relay?.getCdpUrl() ?? null : deps.relay?.getOpsUrl?.() ?? null;
464
+ const relayPort = relayStatus?.port;
465
+ if (!relayUrl && isValidPort(relayPort)) {
466
+ relayUrl = `ws://127.0.0.1:${relayPort}/${extensionLegacy ? "cdp" : "ops"}`;
123
467
  }
124
- }
125
- const useRelay = Boolean(!args.noExtension && relayStatus?.extensionConnected && relayUrl);
126
- let usedRelay = false;
127
- let relayWarning = null;
128
- let result = null;
129
- if (args.extensionOnly && !useRelay) {
130
- return failure("Extension not connected; use --no-extension to launch a new browser.", "extension_not_connected");
131
- }
132
- if (useRelay && relayUrl) {
133
- try {
134
- result = await deps.manager.connectRelay(relayUrl);
135
- usedRelay = true;
136
- } catch {
137
- if (args.extensionOnly) {
138
- return failure("Extension relay connection failed.", "extension_connect_failed");
468
+ const waitTimeoutMs = clampWaitTimeout(args.waitTimeoutMs ?? 3e4);
469
+ const headlessExplicit = args.headless === true;
470
+ const managedExplicit = Boolean(args.noExtension || headlessExplicit);
471
+ const managedHeadless = headlessExplicit ? true : false;
472
+ if (headlessExplicit && !args.noExtension) {
473
+ return failure(
474
+ "Extension mode does not support headless launches. Use noExtension=true with headless=true for managed mode.",
475
+ "unsupported_mode"
476
+ );
477
+ }
478
+ if (args.waitForExtension && !managedExplicit) {
479
+ const observedPort2 = resolveObservedPort(relayStatus, config.relayPort);
480
+ const connected = await waitForExtensionHandshake(deps.relay, observedPort2, waitTimeoutMs);
481
+ if (connected) {
482
+ relayStatus = deps.relay?.status() ?? relayStatus;
483
+ relayUrl = extensionLegacy ? deps.relay?.getCdpUrl() ?? relayUrl : deps.relay?.getOpsUrl?.() ?? relayUrl;
139
484
  }
140
- relayWarning = "Relay connection failed; falling back to managed Chrome.";
141
485
  }
142
- }
143
- if (!result) {
144
- if (relayUrl && !args.noExtension) {
145
- relayWarning ??= "Extension not connected; launching managed Chrome instead.";
486
+ const observedPort = resolveObservedPort(relayStatus, config.relayPort);
487
+ const shouldFetchObserved = !managedExplicit && (!relayUrl || !(relayStatus?.extensionHandshakeComplete || relayStatus?.extensionConnected));
488
+ const observedStatus = shouldFetchObserved ? await fetchRelayObservedStatus(observedPort) : null;
489
+ if (!relayUrl) {
490
+ const fallbackPort = isValidPort(observedStatus?.port) ? observedStatus?.port : observedPort;
491
+ relayUrl = fallbackPort ? `ws://127.0.0.1:${fallbackPort}/${extensionLegacy ? "cdp" : "ops"}` : null;
492
+ }
493
+ const extensionReady = Boolean(
494
+ relayUrl && (relayStatus?.extensionHandshakeComplete || relayStatus?.extensionConnected || observedStatus?.extensionHandshakeComplete || observedStatus?.extensionConnected)
495
+ );
496
+ let usedRelay = false;
497
+ let result = null;
498
+ if (args.extensionOnly && !extensionReady) {
499
+ const diagnostics = buildRelayNotReadyDiagnostics("Extension not connected.", {
500
+ relayUrl,
501
+ relayStatus,
502
+ observedStatus,
503
+ observedPort
504
+ });
505
+ if (await maybeRetryHubMismatch(diagnostics.hint, attemptedRebind, deps)) {
506
+ attemptedRebind = true;
507
+ continue;
508
+ }
509
+ return failure(buildExtensionMissingMessage(diagnostics.message), "extension_not_connected");
510
+ }
511
+ if (!managedExplicit) {
512
+ if (!extensionReady || !relayUrl) {
513
+ const diagnostics = buildRelayNotReadyDiagnostics("Extension not connected.", {
514
+ relayUrl,
515
+ relayStatus,
516
+ observedStatus,
517
+ observedPort
518
+ });
519
+ if (await maybeRetryHubMismatch(diagnostics.hint, attemptedRebind, deps)) {
520
+ attemptedRebind = true;
521
+ continue;
522
+ }
523
+ return failure(buildExtensionMissingMessage(diagnostics.message), "extension_not_connected");
524
+ }
525
+ try {
526
+ result = await deps.manager.connectRelay(relayUrl);
527
+ usedRelay = true;
528
+ } catch (error) {
529
+ const errorMessage = serializeError(error).message;
530
+ const unauthorized = errorMessage.toLowerCase().includes("unauthorized") || errorMessage.includes("401");
531
+ const relayLabel = extensionLegacy ? "/cdp" : "/ops";
532
+ const errorObservedStatus = observedStatus ?? await fetchRelayObservedStatus(observedPort);
533
+ const diagnostics = buildRelayNotReadyDiagnostics(
534
+ unauthorized ? `Extension relay connection failed: relay ${relayLabel} unauthorized (token mismatch).` : `Extension relay connection failed: ${errorMessage}`,
535
+ {
536
+ relayUrl,
537
+ relayStatus,
538
+ observedStatus: errorObservedStatus,
539
+ observedPort
540
+ }
541
+ );
542
+ if (await maybeRetryHubMismatch(diagnostics.hint, attemptedRebind, deps)) {
543
+ attemptedRebind = true;
544
+ continue;
545
+ }
546
+ return failure(buildExtensionMissingMessage(diagnostics.message), "extension_connect_failed");
547
+ }
548
+ }
549
+ if (!result) {
550
+ try {
551
+ result = await deps.manager.launch({
552
+ profile: args.profile,
553
+ headless: managedHeadless,
554
+ startUrl: args.startUrl,
555
+ chromePath: args.chromePath,
556
+ flags: args.flags,
557
+ persistProfile: args.persistProfile,
558
+ noExtension: args.noExtension
559
+ });
560
+ } catch (error) {
561
+ return failure(buildManagedFailureMessage(error), "launch_failed");
562
+ }
563
+ }
564
+ if (usedRelay && args.startUrl && result.activeTargetId) {
565
+ await deps.manager.goto(result.sessionId, args.startUrl, "load", 3e4);
146
566
  }
147
- result = await deps.manager.launch({
148
- profile: args.profile,
149
- headless: args.headless,
150
- startUrl: args.startUrl,
151
- chromePath: args.chromePath,
152
- flags: args.flags,
153
- persistProfile: args.persistProfile
567
+ const warnings = result.warnings ?? [];
568
+ return ok({
569
+ sessionId: result.sessionId,
570
+ mode: result.mode,
571
+ browserWsEndpoint: result.wsEndpoint,
572
+ activeTargetId: result.activeTargetId,
573
+ warnings: warnings.length ? warnings : void 0
154
574
  });
575
+ } catch (error) {
576
+ return failure(serializeError(error).message, "launch_failed");
155
577
  }
156
- if (usedRelay && args.startUrl && result.activeTargetId) {
157
- await deps.manager.goto(result.sessionId, args.startUrl, "load", 3e4);
158
- }
159
- const warnings = [
160
- ...result.warnings ?? [],
161
- ...relayWarning ? [relayWarning] : []
162
- ];
163
- return ok({
164
- sessionId: result.sessionId,
165
- mode: result.mode,
166
- browserWsEndpoint: result.wsEndpoint,
167
- activeTargetId: result.activeTargetId,
168
- warnings: warnings.length ? warnings : void 0
169
- });
170
- } catch (error) {
171
- return failure(serializeError(error).message, "launch_failed");
172
578
  }
173
579
  }
174
580
  });
175
581
  }
176
- async function waitForExtension(relay, timeoutMs) {
582
+ var buildExtensionMissingMessage = (reason) => {
583
+ return [
584
+ reason,
585
+ "Connect the extension: open the Chrome extension popup and click Connect, then retry.",
586
+ "Tip: If the popup says Connected, it may be connected to a different relay instance/port than this tool expects.",
587
+ "Legend: ext=extension websocket, handshake=extension handshake, ops=active /ops client, cdp=active /cdp client, pairing=token required.",
588
+ "",
589
+ "Other options (explicit):",
590
+ "- Managed (headed): npx opendevbrowser launch --no-extension",
591
+ "- Managed (headless): npx opendevbrowser launch --no-extension --headless",
592
+ "- Legacy extension relay: npx opendevbrowser launch --extension-legacy",
593
+ "- CDPConnect (default port): npx opendevbrowser connect --cdp-port 9222",
594
+ "- CDPConnect (explicit WS): npx opendevbrowser connect --ws-endpoint ws://127.0.0.1:9222/devtools/browser/<id>",
595
+ "Note: CDPConnect requires Chrome started with --remote-debugging-port=9222."
596
+ ].join("\n");
597
+ };
598
+ var buildManagedFailureMessage = (error) => {
599
+ const detail = serializeError(error).message;
600
+ return [
601
+ `Managed session failed: ${detail}`,
602
+ "",
603
+ "Final option (explicit):",
604
+ "- CDPConnect (default port): npx opendevbrowser connect --cdp-port 9222",
605
+ "- CDPConnect (explicit WS): npx opendevbrowser connect --ws-endpoint ws://127.0.0.1:9222/devtools/browser/<id>"
606
+ ].join("\n");
607
+ };
608
+ var MIN_WAIT_TIMEOUT_MS = 3e3;
609
+ var WAIT_MIN_DELAY_MS = 250;
610
+ var WAIT_MAX_DELAY_MS = 2e3;
611
+ function clampWaitTimeout(timeoutMs) {
612
+ if (!Number.isFinite(timeoutMs)) {
613
+ return MIN_WAIT_TIMEOUT_MS;
614
+ }
615
+ return Math.max(timeoutMs, MIN_WAIT_TIMEOUT_MS);
616
+ }
617
+ async function waitForExtensionHandshake(relay, observedPort, timeoutMs) {
177
618
  const start = Date.now();
619
+ let delay = WAIT_MIN_DELAY_MS;
178
620
  while (Date.now() - start < timeoutMs) {
179
- if (relay.status().extensionConnected) {
621
+ if (relay?.status().extensionHandshakeComplete) {
622
+ return true;
623
+ }
624
+ const observedStatus = await fetchRelayObservedStatus(observedPort);
625
+ if (observedStatus?.extensionHandshakeComplete) {
180
626
  return true;
181
627
  }
182
- await new Promise((resolve) => setTimeout(resolve, 500));
628
+ await new Promise((resolve) => setTimeout(resolve, delay));
629
+ delay = Math.min(delay * 2, WAIT_MAX_DELAY_MS);
183
630
  }
184
631
  return false;
185
632
  }
633
+ function resolveObservedPort(relayStatus, configPort) {
634
+ const relayPort = relayStatus?.port;
635
+ if (isValidPort(relayPort)) return relayPort;
636
+ if (isValidPort(configPort)) return configPort;
637
+ return null;
638
+ }
639
+ function isValidPort(port) {
640
+ return typeof port === "number" && Number.isInteger(port) && port > 0 && port <= 65535;
641
+ }
642
+ function shortInstanceId(value) {
643
+ if (!value) return "?";
644
+ return value.slice(0, 8);
645
+ }
646
+ function formatRelayUrl(relayUrl) {
647
+ return relayUrl ?? "null";
648
+ }
649
+ function formatLocalStatus(status) {
650
+ return [
651
+ "local(instance=",
652
+ shortInstanceId(status?.instanceId),
653
+ " port=",
654
+ typeof status?.port === "number" ? String(status.port) : "?",
655
+ " ext=",
656
+ String(Boolean(status?.extensionConnected)),
657
+ " handshake=",
658
+ String(Boolean(status?.extensionHandshakeComplete)),
659
+ " ops=",
660
+ String(Boolean(status?.opsConnected)),
661
+ " cdp=",
662
+ String(Boolean(status?.cdpConnected)),
663
+ " pairing=",
664
+ String(Boolean(status?.pairingRequired)),
665
+ ")"
666
+ ].join("");
667
+ }
668
+ function formatObservedStatus(status, port) {
669
+ const label = port ?? "?";
670
+ if (!status) {
671
+ return `observed@${label}=none`;
672
+ }
673
+ return [
674
+ "observed@",
675
+ label,
676
+ "=instance=",
677
+ shortInstanceId(status.instanceId),
678
+ " ext=",
679
+ String(Boolean(status.extensionConnected)),
680
+ " handshake=",
681
+ String(Boolean(status.extensionHandshakeComplete)),
682
+ " ops=",
683
+ String(Boolean(status.opsConnected)),
684
+ " cdp=",
685
+ String(Boolean(status.cdpConnected)),
686
+ " pairing=",
687
+ String(Boolean(status.pairingRequired))
688
+ ].join("");
689
+ }
690
+ function buildRelayNotReadyDiagnostics(baseReason, detail) {
691
+ const localExt = Boolean(detail.relayStatus?.extensionConnected);
692
+ const observedExt = Boolean(detail.observedStatus?.extensionConnected);
693
+ let hint = "none";
694
+ if (detail.relayUrl === null) {
695
+ hint = "relayUrl_null";
696
+ } else if (detail.observedStatus && !localExt && observedExt) {
697
+ hint = "possible_mismatch";
698
+ } else if (detail.relayStatus?.instanceId && detail.observedStatus?.instanceId && detail.relayStatus.instanceId !== detail.observedStatus.instanceId) {
699
+ hint = "possible_mismatch";
700
+ }
701
+ const diagnostics = [
702
+ "Diagnostics: relayUrl=",
703
+ formatRelayUrl(detail.relayUrl),
704
+ " ",
705
+ formatLocalStatus(detail.relayStatus),
706
+ " ",
707
+ formatObservedStatus(detail.observedStatus, detail.observedPort),
708
+ " hint=",
709
+ hint
710
+ ].join("");
711
+ return { message: [baseReason, diagnostics].join("\n"), hint };
712
+ }
713
+ async function maybeRetryHubMismatch(hint, attempted, deps) {
714
+ if (attempted) return false;
715
+ if (hint !== "possible_mismatch") return false;
716
+ if (!deps.ensureHub) return false;
717
+ await deps.ensureHub();
718
+ await deps.relay?.refresh?.();
719
+ return true;
720
+ }
721
+ async function fetchRelayObservedStatus(port) {
722
+ if (!isValidPort(port)) return null;
723
+ if (typeof fetch !== "function") return null;
724
+ const controller = new AbortController();
725
+ const timeoutId = setTimeout(() => controller.abort(), 500);
726
+ try {
727
+ const response = await fetch(`http://127.0.0.1:${port}/status`, { signal: controller.signal });
728
+ if (!response.ok) return null;
729
+ const payload = await response.json();
730
+ if (!payload || typeof payload.instanceId !== "string") return null;
731
+ return {
732
+ instanceId: payload.instanceId,
733
+ running: Boolean(payload.running),
734
+ port: typeof payload.port === "number" ? payload.port : void 0,
735
+ extensionConnected: Boolean(payload.extensionConnected),
736
+ extensionHandshakeComplete: Boolean(payload.extensionHandshakeComplete),
737
+ cdpConnected: Boolean(payload.cdpConnected),
738
+ opsConnected: Boolean(payload.opsConnected),
739
+ pairingRequired: Boolean(payload.pairingRequired)
740
+ };
741
+ } catch {
742
+ return null;
743
+ } finally {
744
+ clearTimeout(timeoutId);
745
+ }
746
+ }
186
747
 
187
748
  // src/tools/connect.ts
188
749
  import { tool as tool2 } from "@opencode-ai/plugin";
189
750
  var z2 = tool2.schema;
751
+ function normalizeRelayEndpoint(wsEndpoint, path, allowBase) {
752
+ if (!wsEndpoint) return null;
753
+ try {
754
+ const url = new URL(wsEndpoint);
755
+ if (url.protocol !== "ws:" && url.protocol !== "wss:") return null;
756
+ if (url.hostname !== "127.0.0.1" && url.hostname !== "localhost") return null;
757
+ if (!url.port || !/^\d+$/.test(url.port)) return null;
758
+ const normalizedPath = url.pathname.endsWith("/") ? url.pathname.slice(0, -1) : url.pathname;
759
+ if (!allowBase && normalizedPath === "") return null;
760
+ if (normalizedPath && normalizedPath !== `/${path}`) return null;
761
+ return `${url.protocol}//${url.hostname}:${url.port}/${path}`;
762
+ } catch {
763
+ return null;
764
+ }
765
+ }
190
766
  function createConnectTool(deps) {
191
767
  return tool2({
192
- description: "Connect to an existing Chrome CDP endpoint.",
768
+ description: "Connect to an existing Chrome CDP endpoint or extension relay.",
193
769
  args: {
194
770
  wsEndpoint: z2.string().optional().describe("Full WebSocket endpoint to connect to"),
195
771
  host: z2.string().optional().describe("Host for /json/version lookup"),
196
- port: z2.number().int().optional().describe("Port for /json/version lookup")
772
+ port: z2.number().int().optional().describe("Port for /json/version lookup"),
773
+ extensionLegacy: z2.boolean().optional().describe("Use legacy extension relay (/cdp) instead of ops")
197
774
  },
198
775
  async execute(args) {
199
776
  try {
200
- const relayUrl = deps.relay?.getCdpUrl();
201
- const useRelay = Boolean(relayUrl && args.wsEndpoint === relayUrl);
202
- const result = useRelay && relayUrl ? await deps.manager.connectRelay(relayUrl) : await deps.manager.connect({
203
- wsEndpoint: args.wsEndpoint,
204
- host: args.host,
205
- port: args.port
206
- });
777
+ await deps.relay?.refresh?.();
778
+ const wsEndpoint = args.wsEndpoint;
779
+ const extensionLegacy = args.extensionLegacy === true;
780
+ const hasExplicitCdp = Boolean(wsEndpoint || args.host || args.port);
781
+ const relayUrl = extensionLegacy ? deps.relay?.getCdpUrl() ?? null : deps.relay?.getOpsUrl?.() ?? null;
782
+ const normalizedOpsEndpoint = normalizeRelayEndpoint(wsEndpoint, "ops", true);
783
+ const normalizedLegacyEndpoint = normalizeRelayEndpoint(wsEndpoint, "cdp", extensionLegacy);
784
+ if (normalizedLegacyEndpoint && !extensionLegacy) {
785
+ return failure("Legacy extension relay (/cdp) requires extensionLegacy=true.", "extension_legacy_required");
786
+ }
787
+ const relayEndpoint = relayUrl && wsEndpoint === relayUrl ? relayUrl : extensionLegacy ? normalizedLegacyEndpoint ?? normalizedOpsEndpoint : normalizedOpsEndpoint;
788
+ let result;
789
+ if (relayEndpoint || !hasExplicitCdp && relayUrl) {
790
+ result = await deps.manager.connectRelay(relayEndpoint ?? relayUrl ?? "");
791
+ } else {
792
+ if (!hasExplicitCdp) {
793
+ return failure("Extension relay not available. Connect the extension or pass wsEndpoint/host/port.", "extension_not_connected");
794
+ }
795
+ result = await deps.manager.connect({
796
+ wsEndpoint,
797
+ host: args.host,
798
+ port: args.port
799
+ });
800
+ }
207
801
  return ok({
208
802
  sessionId: result.sessionId,
209
803
  mode: result.mode,
@@ -244,6 +838,13 @@ import { readFileSync } from "fs";
244
838
  import { dirname, join } from "path";
245
839
  import { fileURLToPath } from "url";
246
840
  import { tool as tool4 } from "@opencode-ai/plugin";
841
+
842
+ // src/utils/hub-enabled.ts
843
+ var isHubEnabled = (config) => {
844
+ return config.relayToken !== false && config.relayPort > 0;
845
+ };
846
+
847
+ // src/tools/status.ts
247
848
  var z4 = tool4.schema;
248
849
  function getPackageVersion() {
249
850
  try {
@@ -285,17 +886,45 @@ async function fetchLatestVersion(packageName) {
285
886
  }
286
887
  function createStatusTool(deps) {
287
888
  return tool4({
288
- description: "Get status of a browser session.",
889
+ description: "Get daemon or session status.",
289
890
  args: {
290
- sessionId: z4.string().describe("Session id")
891
+ sessionId: z4.string().optional().describe("Session id (required when hub is disabled)")
291
892
  },
292
893
  async execute(args) {
293
894
  try {
294
- const status = await deps.manager.status(args.sessionId);
295
- const extensionPath = deps.getExtensionPath?.() ?? null;
296
895
  const config = deps.config.get();
896
+ const hubEnabled = isHubEnabled(config);
897
+ const extensionPath = deps.getExtensionPath?.() ?? null;
297
898
  const version = getPackageVersion();
298
899
  let updateHint;
900
+ let sessionStatus = null;
901
+ if (hubEnabled) {
902
+ const daemonStatus = await fetchDaemonStatusFromMetadata();
903
+ if (!daemonStatus) {
904
+ return failure("Daemon not running. Start with `npx opendevbrowser serve`.", "status_failed");
905
+ }
906
+ if (args.sessionId) {
907
+ sessionStatus = await deps.manager.status(args.sessionId);
908
+ }
909
+ if (config.checkForUpdates && version) {
910
+ const latest = await fetchLatestVersion("opendevbrowser");
911
+ if (latest && latest !== version) {
912
+ updateHint = `Update available: ${version} -> ${latest}`;
913
+ }
914
+ }
915
+ return ok({
916
+ ...sessionStatus ?? {},
917
+ daemon: daemonStatus,
918
+ hubEnabled: true,
919
+ extensionPath: extensionPath ?? void 0,
920
+ version,
921
+ updateHint
922
+ });
923
+ }
924
+ if (!args.sessionId) {
925
+ return failure("Missing sessionId for status.", "status_failed");
926
+ }
927
+ sessionStatus = await deps.manager.status(args.sessionId);
299
928
  if (config.checkForUpdates && version) {
300
929
  const latest = await fetchLatestVersion("opendevbrowser");
301
930
  if (latest && latest !== version) {
@@ -303,10 +932,10 @@ function createStatusTool(deps) {
303
932
  }
304
933
  }
305
934
  return ok({
306
- mode: status.mode,
307
- activeTargetId: status.activeTargetId,
308
- url: status.url,
309
- title: status.title,
935
+ mode: sessionStatus.mode,
936
+ activeTargetId: sessionStatus.activeTargetId,
937
+ url: sessionStatus.url,
938
+ title: sessionStatus.title,
310
939
  extensionPath: extensionPath ?? void 0,
311
940
  version,
312
941
  updateHint
@@ -599,266 +1228,921 @@ function createClickTool(deps) {
599
1228
  });
600
1229
  }
601
1230
 
602
- // src/tools/type.ts
1231
+ // src/tools/hover.ts
603
1232
  import { tool as tool16 } from "@opencode-ai/plugin";
604
1233
  var z16 = tool16.schema;
605
- function createTypeTool(deps) {
1234
+ function createHoverTool(deps) {
606
1235
  return tool16({
607
- description: "Type text into a referenced input.",
1236
+ description: "Hover over an element by ref.",
608
1237
  args: {
609
- sessionId: z16.string().describe("Session id"),
610
- ref: z16.string().describe("Element ref"),
611
- text: z16.string().describe("Text to type"),
612
- clear: z16.boolean().optional().describe("Clear before typing"),
613
- submit: z16.boolean().optional().describe("Press Enter after typing")
1238
+ sessionId: z16.string().describe("Active browser session id"),
1239
+ ref: z16.string().describe("Element ref from snapshot")
614
1240
  },
615
1241
  async execute(args) {
616
1242
  try {
617
- const result = await deps.manager.type(
618
- args.sessionId,
619
- args.ref,
620
- args.text,
621
- Boolean(args.clear),
622
- Boolean(args.submit)
623
- );
1243
+ const result = await deps.manager.hover(args.sessionId, args.ref);
624
1244
  return ok(result);
625
1245
  } catch (error) {
626
- return failure(serializeError(error).message, "type_failed");
1246
+ return failure(serializeError(error).message, "hover_failed");
627
1247
  }
628
1248
  }
629
1249
  });
630
1250
  }
631
1251
 
632
- // src/tools/select.ts
1252
+ // src/tools/press.ts
633
1253
  import { tool as tool17 } from "@opencode-ai/plugin";
634
1254
  var z17 = tool17.schema;
635
- function createSelectTool(deps) {
1255
+ function createPressTool(deps) {
636
1256
  return tool17({
637
- description: "Select options in a referenced select element.",
1257
+ description: "Press a keyboard key, optionally focusing a ref first.",
638
1258
  args: {
639
- sessionId: z17.string().describe("Session id"),
640
- ref: z17.string().describe("Element ref"),
641
- values: z17.array(z17.string()).describe("Values to select")
1259
+ sessionId: z17.string().describe("Active browser session id"),
1260
+ key: z17.string().describe("Keyboard key to press, e.g. Enter or ArrowDown"),
1261
+ ref: z17.string().optional().describe("Optional element ref to focus first")
642
1262
  },
643
1263
  async execute(args) {
644
1264
  try {
645
- await deps.manager.select(args.sessionId, args.ref, args.values);
646
- return ok({});
1265
+ const result = await deps.manager.press(args.sessionId, args.key, args.ref);
1266
+ return ok(result);
647
1267
  } catch (error) {
648
- return failure(serializeError(error).message, "select_failed");
1268
+ return failure(serializeError(error).message, "press_failed");
649
1269
  }
650
1270
  }
651
1271
  });
652
1272
  }
653
1273
 
654
- // src/tools/scroll.ts
1274
+ // src/tools/check.ts
655
1275
  import { tool as tool18 } from "@opencode-ai/plugin";
656
1276
  var z18 = tool18.schema;
657
- function createScrollTool(deps) {
1277
+ function createCheckTool(deps) {
658
1278
  return tool18({
659
- description: "Scroll the page or a referenced element.",
1279
+ description: "Check a checkbox or toggle by ref.",
660
1280
  args: {
661
- sessionId: z18.string().describe("Session id"),
662
- dy: z18.number().describe("Scroll delta in pixels"),
663
- ref: z18.string().optional().describe("Optional element ref to scroll")
1281
+ sessionId: z18.string().describe("Active browser session id"),
1282
+ ref: z18.string().describe("Element ref from snapshot")
664
1283
  },
665
1284
  async execute(args) {
666
1285
  try {
667
- await deps.manager.scroll(args.sessionId, args.dy, args.ref);
668
- return ok({});
1286
+ const result = await deps.manager.check(args.sessionId, args.ref);
1287
+ return ok(result);
669
1288
  } catch (error) {
670
- return failure(serializeError(error).message, "scroll_failed");
1289
+ return failure(serializeError(error).message, "check_failed");
671
1290
  }
672
1291
  }
673
1292
  });
674
1293
  }
675
1294
 
676
- // src/tools/dom_get_html.ts
1295
+ // src/tools/uncheck.ts
677
1296
  import { tool as tool19 } from "@opencode-ai/plugin";
678
1297
  var z19 = tool19.schema;
679
- function createDomGetHtmlTool(deps) {
1298
+ function createUncheckTool(deps) {
680
1299
  return tool19({
681
- description: "Get outerHTML for a referenced element.",
1300
+ description: "Uncheck a checkbox or toggle by ref.",
682
1301
  args: {
683
- sessionId: z19.string().describe("Session id"),
684
- ref: z19.string().describe("Element ref"),
685
- maxChars: z19.number().int().optional().describe("Max characters")
1302
+ sessionId: z19.string().describe("Active browser session id"),
1303
+ ref: z19.string().describe("Element ref from snapshot")
686
1304
  },
687
1305
  async execute(args) {
688
1306
  try {
689
- const result = await deps.manager.domGetHtml(
690
- args.sessionId,
691
- args.ref,
692
- args.maxChars ?? 8e3
693
- );
694
- return ok({
695
- ref: args.ref,
696
- outerHTML: result.outerHTML,
697
- truncated: result.truncated
698
- });
1307
+ const result = await deps.manager.uncheck(args.sessionId, args.ref);
1308
+ return ok(result);
699
1309
  } catch (error) {
700
- return failure(serializeError(error).message, "dom_get_html_failed");
1310
+ return failure(serializeError(error).message, "uncheck_failed");
701
1311
  }
702
1312
  }
703
1313
  });
704
1314
  }
705
1315
 
706
- // src/tools/dom_get_text.ts
1316
+ // src/tools/type.ts
707
1317
  import { tool as tool20 } from "@opencode-ai/plugin";
708
1318
  var z20 = tool20.schema;
709
- function createDomGetTextTool(deps) {
1319
+ function createTypeTool(deps) {
710
1320
  return tool20({
711
- description: "Get inner text for a referenced element.",
1321
+ description: "Type text into a referenced input.",
712
1322
  args: {
713
1323
  sessionId: z20.string().describe("Session id"),
714
1324
  ref: z20.string().describe("Element ref"),
715
- maxChars: z20.number().int().optional().describe("Max characters")
1325
+ text: z20.string().describe("Text to type"),
1326
+ clear: z20.boolean().optional().describe("Clear before typing"),
1327
+ submit: z20.boolean().optional().describe("Press Enter after typing")
716
1328
  },
717
1329
  async execute(args) {
718
1330
  try {
719
- const result = await deps.manager.domGetText(
1331
+ const result = await deps.manager.type(
720
1332
  args.sessionId,
721
1333
  args.ref,
722
- args.maxChars ?? 8e3
1334
+ args.text,
1335
+ Boolean(args.clear),
1336
+ Boolean(args.submit)
723
1337
  );
724
- return ok({
725
- ref: args.ref,
726
- text: result.text,
727
- truncated: result.truncated
728
- });
1338
+ return ok(result);
729
1339
  } catch (error) {
730
- return failure(serializeError(error).message, "dom_get_text_failed");
1340
+ return failure(serializeError(error).message, "type_failed");
731
1341
  }
732
1342
  }
733
1343
  });
734
1344
  }
735
1345
 
736
- // src/tools/run.ts
1346
+ // src/tools/select.ts
737
1347
  import { tool as tool21 } from "@opencode-ai/plugin";
738
1348
  var z21 = tool21.schema;
739
- var stepSchema = z21.object({
740
- action: z21.string().describe("Action name"),
741
- args: z21.record(z21.string(), z21.unknown()).optional().describe("Action arguments")
742
- });
743
- function createRunTool(deps) {
1349
+ function createSelectTool(deps) {
744
1350
  return tool21({
745
- description: "Run multiple actions in a single tool call.",
1351
+ description: "Select options in a referenced select element.",
746
1352
  args: {
747
1353
  sessionId: z21.string().describe("Session id"),
748
- steps: z21.array(stepSchema).describe("Steps to execute"),
749
- stopOnError: z21.boolean().optional().describe("Stop when a step fails"),
750
- maxSnapshotChars: z21.number().int().optional().describe("Default maxChars for snapshot steps")
1354
+ ref: z21.string().describe("Element ref"),
1355
+ values: z21.array(z21.string()).describe("Values to select")
751
1356
  },
752
1357
  async execute(args) {
753
1358
  try {
754
- const steps = normalizeSteps(args.steps, args.maxSnapshotChars);
755
- const result = await deps.runner.run(
756
- args.sessionId,
757
- steps,
758
- args.stopOnError ?? true
759
- );
760
- return ok(result);
1359
+ await deps.manager.select(args.sessionId, args.ref, args.values);
1360
+ return ok({});
761
1361
  } catch (error) {
762
- return failure(serializeError(error).message, "run_failed");
1362
+ return failure(serializeError(error).message, "select_failed");
763
1363
  }
764
1364
  }
765
1365
  });
766
1366
  }
767
- function normalizeSteps(steps, maxSnapshotChars) {
768
- if (!maxSnapshotChars) return steps;
769
- return steps.map((step) => {
770
- if (step.action !== "snapshot") return step;
771
- if (step.args && typeof step.args.maxChars === "number") return step;
772
- return {
773
- ...step,
774
- args: {
775
- ...step.args,
776
- maxChars: maxSnapshotChars
777
- }
778
- };
779
- });
780
- }
781
1367
 
782
- // src/tools/prompting_guide.ts
1368
+ // src/tools/scroll.ts
783
1369
  import { tool as tool22 } from "@opencode-ai/plugin";
784
1370
  var z22 = tool22.schema;
785
- function createPromptingGuideTool(deps) {
1371
+ function createScrollTool(deps) {
786
1372
  return tool22({
787
- description: "Return best-practice prompting guidance for OpenDevBrowser.",
1373
+ description: "Scroll the page or a referenced element.",
788
1374
  args: {
789
- topic: z22.string().optional().describe("Optional topic for guidance")
1375
+ sessionId: z22.string().describe("Session id"),
1376
+ dy: z22.number().describe("Scroll delta in pixels"),
1377
+ ref: z22.string().optional().describe("Optional element ref to scroll")
790
1378
  },
791
1379
  async execute(args) {
792
1380
  try {
793
- const guide = await deps.skills.loadBestPractices(args.topic);
794
- return ok({ guide });
1381
+ await deps.manager.scroll(args.sessionId, args.dy, args.ref);
1382
+ return ok({});
795
1383
  } catch (error) {
796
- return failure(serializeError(error).message, "prompting_guide_failed");
1384
+ return failure(serializeError(error).message, "scroll_failed");
797
1385
  }
798
1386
  }
799
1387
  });
800
1388
  }
801
1389
 
802
- // src/tools/console_poll.ts
1390
+ // src/tools/scroll_into_view.ts
803
1391
  import { tool as tool23 } from "@opencode-ai/plugin";
804
1392
  var z23 = tool23.schema;
805
- function createConsolePollTool(deps) {
1393
+ function createScrollIntoViewTool(deps) {
806
1394
  return tool23({
807
- description: "Poll console events for the active target.",
1395
+ description: "Scroll an element into view by ref.",
808
1396
  args: {
809
- sessionId: z23.string().describe("Session id"),
810
- sinceSeq: z23.number().int().optional().describe("Sequence to resume from"),
811
- max: z23.number().int().optional().describe("Max events to return")
1397
+ sessionId: z23.string().describe("Active browser session id"),
1398
+ ref: z23.string().describe("Element ref from snapshot")
812
1399
  },
813
1400
  async execute(args) {
814
1401
  try {
815
- const result = deps.manager.consolePoll(
816
- args.sessionId,
817
- args.sinceSeq,
818
- args.max ?? 50
819
- );
1402
+ const result = await deps.manager.scrollIntoView(args.sessionId, args.ref);
820
1403
  return ok(result);
821
1404
  } catch (error) {
822
- return failure(serializeError(error).message, "console_poll_failed");
1405
+ return failure(serializeError(error).message, "scroll_into_view_failed");
823
1406
  }
824
1407
  }
825
1408
  });
826
1409
  }
827
1410
 
828
- // src/tools/network_poll.ts
1411
+ // src/tools/dom_get_html.ts
829
1412
  import { tool as tool24 } from "@opencode-ai/plugin";
830
1413
  var z24 = tool24.schema;
831
- function createNetworkPollTool(deps) {
1414
+ function createDomGetHtmlTool(deps) {
832
1415
  return tool24({
833
- description: "Poll network events for the active target.",
1416
+ description: "Get outerHTML for a referenced element.",
834
1417
  args: {
835
1418
  sessionId: z24.string().describe("Session id"),
836
- sinceSeq: z24.number().int().optional().describe("Sequence to resume from"),
837
- max: z24.number().int().optional().describe("Max events to return")
1419
+ ref: z24.string().describe("Element ref"),
1420
+ maxChars: z24.number().int().optional().describe("Max characters")
838
1421
  },
839
1422
  async execute(args) {
840
1423
  try {
841
- const result = deps.manager.networkPoll(
1424
+ const result = await deps.manager.domGetHtml(
842
1425
  args.sessionId,
843
- args.sinceSeq,
844
- args.max ?? 50
1426
+ args.ref,
1427
+ args.maxChars ?? 8e3
845
1428
  );
846
- return ok(result);
847
- } catch (error) {
848
- return failure(serializeError(error).message, "network_poll_failed");
849
- }
1429
+ return ok({
1430
+ ref: args.ref,
1431
+ outerHTML: result.outerHTML,
1432
+ truncated: result.truncated
1433
+ });
1434
+ } catch (error) {
1435
+ return failure(serializeError(error).message, "dom_get_html_failed");
1436
+ }
850
1437
  }
851
1438
  });
852
1439
  }
853
1440
 
854
- // src/tools/clone_page.ts
1441
+ // src/tools/dom_get_text.ts
855
1442
  import { tool as tool25 } from "@opencode-ai/plugin";
856
1443
  var z25 = tool25.schema;
857
- function createClonePageTool(deps) {
1444
+ function createDomGetTextTool(deps) {
858
1445
  return tool25({
1446
+ description: "Get inner text for a referenced element.",
1447
+ args: {
1448
+ sessionId: z25.string().describe("Session id"),
1449
+ ref: z25.string().describe("Element ref"),
1450
+ maxChars: z25.number().int().optional().describe("Max characters")
1451
+ },
1452
+ async execute(args) {
1453
+ try {
1454
+ const result = await deps.manager.domGetText(
1455
+ args.sessionId,
1456
+ args.ref,
1457
+ args.maxChars ?? 8e3
1458
+ );
1459
+ return ok({
1460
+ ref: args.ref,
1461
+ text: result.text,
1462
+ truncated: result.truncated
1463
+ });
1464
+ } catch (error) {
1465
+ return failure(serializeError(error).message, "dom_get_text_failed");
1466
+ }
1467
+ }
1468
+ });
1469
+ }
1470
+
1471
+ // src/tools/get_attr.ts
1472
+ import { tool as tool26 } from "@opencode-ai/plugin";
1473
+ var z26 = tool26.schema;
1474
+ function createGetAttrTool(deps) {
1475
+ return tool26({
1476
+ description: "Get a DOM attribute value by ref.",
1477
+ args: {
1478
+ sessionId: z26.string().describe("Active browser session id"),
1479
+ ref: z26.string().describe("Element ref from snapshot"),
1480
+ name: z26.string().describe("Attribute name, e.g. href or aria-label")
1481
+ },
1482
+ async execute(args) {
1483
+ try {
1484
+ const result = await deps.manager.domGetAttr(args.sessionId, args.ref, args.name);
1485
+ return ok(result);
1486
+ } catch (error) {
1487
+ return failure(serializeError(error).message, "get_attr_failed");
1488
+ }
1489
+ }
1490
+ });
1491
+ }
1492
+
1493
+ // src/tools/get_value.ts
1494
+ import { tool as tool27 } from "@opencode-ai/plugin";
1495
+ var z27 = tool27.schema;
1496
+ function createGetValueTool(deps) {
1497
+ return tool27({
1498
+ description: "Get the input value for an element by ref.",
1499
+ args: {
1500
+ sessionId: z27.string().describe("Active browser session id"),
1501
+ ref: z27.string().describe("Element ref from snapshot")
1502
+ },
1503
+ async execute(args) {
1504
+ try {
1505
+ const result = await deps.manager.domGetValue(args.sessionId, args.ref);
1506
+ return ok(result);
1507
+ } catch (error) {
1508
+ return failure(serializeError(error).message, "get_value_failed");
1509
+ }
1510
+ }
1511
+ });
1512
+ }
1513
+
1514
+ // src/tools/is_visible.ts
1515
+ import { tool as tool28 } from "@opencode-ai/plugin";
1516
+ var z28 = tool28.schema;
1517
+ function createIsVisibleTool(deps) {
1518
+ return tool28({
1519
+ description: "Check if an element is visible by ref.",
1520
+ args: {
1521
+ sessionId: z28.string().describe("Active browser session id"),
1522
+ ref: z28.string().describe("Element ref from snapshot")
1523
+ },
1524
+ async execute(args) {
1525
+ try {
1526
+ const result = await deps.manager.domIsVisible(args.sessionId, args.ref);
1527
+ return ok(result);
1528
+ } catch (error) {
1529
+ return failure(serializeError(error).message, "is_visible_failed");
1530
+ }
1531
+ }
1532
+ });
1533
+ }
1534
+
1535
+ // src/tools/is_enabled.ts
1536
+ import { tool as tool29 } from "@opencode-ai/plugin";
1537
+ var z29 = tool29.schema;
1538
+ function createIsEnabledTool(deps) {
1539
+ return tool29({
1540
+ description: "Check if an element is enabled by ref.",
1541
+ args: {
1542
+ sessionId: z29.string().describe("Active browser session id"),
1543
+ ref: z29.string().describe("Element ref from snapshot")
1544
+ },
1545
+ async execute(args) {
1546
+ try {
1547
+ const result = await deps.manager.domIsEnabled(args.sessionId, args.ref);
1548
+ return ok(result);
1549
+ } catch (error) {
1550
+ return failure(serializeError(error).message, "is_enabled_failed");
1551
+ }
1552
+ }
1553
+ });
1554
+ }
1555
+
1556
+ // src/tools/is_checked.ts
1557
+ import { tool as tool30 } from "@opencode-ai/plugin";
1558
+ var z30 = tool30.schema;
1559
+ function createIsCheckedTool(deps) {
1560
+ return tool30({
1561
+ description: "Check if an element is checked by ref.",
1562
+ args: {
1563
+ sessionId: z30.string().describe("Active browser session id"),
1564
+ ref: z30.string().describe("Element ref from snapshot")
1565
+ },
1566
+ async execute(args) {
1567
+ try {
1568
+ const result = await deps.manager.domIsChecked(args.sessionId, args.ref);
1569
+ return ok(result);
1570
+ } catch (error) {
1571
+ return failure(serializeError(error).message, "is_checked_failed");
1572
+ }
1573
+ }
1574
+ });
1575
+ }
1576
+
1577
+ // src/tools/run.ts
1578
+ import { tool as tool31 } from "@opencode-ai/plugin";
1579
+ var z31 = tool31.schema;
1580
+ var stepSchema = z31.object({
1581
+ action: z31.string().describe("Action name"),
1582
+ args: z31.record(z31.string(), z31.unknown()).optional().describe("Action arguments")
1583
+ });
1584
+ function createRunTool(deps) {
1585
+ return tool31({
1586
+ description: "Run multiple actions in a single tool call.",
1587
+ args: {
1588
+ sessionId: z31.string().describe("Session id"),
1589
+ steps: z31.array(stepSchema).describe("Steps to execute"),
1590
+ stopOnError: z31.boolean().optional().describe("Stop when a step fails"),
1591
+ maxSnapshotChars: z31.number().int().optional().describe("Default maxChars for snapshot steps")
1592
+ },
1593
+ async execute(args) {
1594
+ try {
1595
+ const steps = normalizeSteps(args.steps, args.maxSnapshotChars);
1596
+ const result = await deps.runner.run(
1597
+ args.sessionId,
1598
+ steps,
1599
+ args.stopOnError ?? true
1600
+ );
1601
+ return ok(result);
1602
+ } catch (error) {
1603
+ return failure(serializeError(error).message, "run_failed");
1604
+ }
1605
+ }
1606
+ });
1607
+ }
1608
+ function normalizeSteps(steps, maxSnapshotChars) {
1609
+ if (!maxSnapshotChars) return steps;
1610
+ return steps.map((step) => {
1611
+ if (step.action !== "snapshot") return step;
1612
+ if (step.args && typeof step.args.maxChars === "number") return step;
1613
+ return {
1614
+ ...step,
1615
+ args: {
1616
+ ...step.args,
1617
+ maxChars: maxSnapshotChars
1618
+ }
1619
+ };
1620
+ });
1621
+ }
1622
+
1623
+ // src/tools/prompting_guide.ts
1624
+ import { tool as tool32 } from "@opencode-ai/plugin";
1625
+ var z32 = tool32.schema;
1626
+ function createPromptingGuideTool(deps) {
1627
+ return tool32({
1628
+ description: "Return best-practice prompting guidance for OpenDevBrowser.",
1629
+ args: {
1630
+ topic: z32.string().optional().describe("Optional topic for guidance")
1631
+ },
1632
+ async execute(args) {
1633
+ try {
1634
+ const guide = await deps.skills.loadBestPractices(args.topic);
1635
+ return ok({ guide });
1636
+ } catch (error) {
1637
+ return failure(serializeError(error).message, "prompting_guide_failed");
1638
+ }
1639
+ }
1640
+ });
1641
+ }
1642
+
1643
+ // src/tools/console_poll.ts
1644
+ import { tool as tool33 } from "@opencode-ai/plugin";
1645
+ var z33 = tool33.schema;
1646
+ function createConsolePollTool(deps) {
1647
+ return tool33({
1648
+ description: "Poll console events for the active target.",
1649
+ args: {
1650
+ sessionId: z33.string().describe("Session id"),
1651
+ sinceSeq: z33.number().int().optional().describe("Sequence to resume from"),
1652
+ max: z33.number().int().optional().describe("Max events to return")
1653
+ },
1654
+ async execute(args) {
1655
+ try {
1656
+ const result = await deps.manager.consolePoll(
1657
+ args.sessionId,
1658
+ args.sinceSeq,
1659
+ args.max ?? 50
1660
+ );
1661
+ return ok(result);
1662
+ } catch (error) {
1663
+ return failure(serializeError(error).message, "console_poll_failed");
1664
+ }
1665
+ }
1666
+ });
1667
+ }
1668
+
1669
+ // src/tools/network_poll.ts
1670
+ import { tool as tool34 } from "@opencode-ai/plugin";
1671
+ var z34 = tool34.schema;
1672
+ function createNetworkPollTool(deps) {
1673
+ return tool34({
1674
+ description: "Poll network events for the active target.",
1675
+ args: {
1676
+ sessionId: z34.string().describe("Session id"),
1677
+ sinceSeq: z34.number().int().optional().describe("Sequence to resume from"),
1678
+ max: z34.number().int().optional().describe("Max events to return")
1679
+ },
1680
+ async execute(args) {
1681
+ try {
1682
+ const result = await deps.manager.networkPoll(
1683
+ args.sessionId,
1684
+ args.sinceSeq,
1685
+ args.max ?? 50
1686
+ );
1687
+ return ok(result);
1688
+ } catch (error) {
1689
+ return failure(serializeError(error).message, "network_poll_failed");
1690
+ }
1691
+ }
1692
+ });
1693
+ }
1694
+
1695
+ // src/tools/debug_trace_snapshot.ts
1696
+ import { tool as tool35 } from "@opencode-ai/plugin";
1697
+ var z35 = tool35.schema;
1698
+ function createDebugTraceSnapshotTool(deps) {
1699
+ return tool35({
1700
+ description: "Capture a combined debug trace snapshot (page + console + network + exceptions).",
1701
+ args: {
1702
+ sessionId: z35.string().describe("Session id"),
1703
+ sinceConsoleSeq: z35.number().int().optional().describe("Resume cursor for console events"),
1704
+ sinceNetworkSeq: z35.number().int().optional().describe("Resume cursor for network events"),
1705
+ sinceExceptionSeq: z35.number().int().optional().describe("Resume cursor for exception events"),
1706
+ max: z35.number().int().optional().describe("Max events per channel"),
1707
+ requestId: z35.string().optional().describe("Optional trace request id")
1708
+ },
1709
+ async execute(args) {
1710
+ try {
1711
+ const manager = deps.manager;
1712
+ const options = {
1713
+ sinceConsoleSeq: args.sinceConsoleSeq,
1714
+ sinceNetworkSeq: args.sinceNetworkSeq,
1715
+ sinceExceptionSeq: args.sinceExceptionSeq,
1716
+ max: args.max,
1717
+ requestId: args.requestId
1718
+ };
1719
+ if (typeof manager.debugTraceSnapshot === "function") {
1720
+ const result = await manager.debugTraceSnapshot(args.sessionId, options);
1721
+ return ok(result);
1722
+ }
1723
+ const max = args.max ?? 500;
1724
+ const requestId = args.requestId ?? createRequestId();
1725
+ const [page, consoleChannel, networkChannel] = await Promise.all([
1726
+ deps.manager.status(args.sessionId),
1727
+ deps.manager.consolePoll(args.sessionId, args.sinceConsoleSeq, max),
1728
+ deps.manager.networkPoll(args.sessionId, args.sinceNetworkSeq, max)
1729
+ ]);
1730
+ const exceptionChannel = typeof manager.exceptionPoll === "function" ? await manager.exceptionPoll(args.sessionId, args.sinceExceptionSeq, max) : {
1731
+ events: [],
1732
+ nextSeq: args.sinceExceptionSeq ?? 0,
1733
+ truncated: false
1734
+ };
1735
+ const annotateTraceContext = (events) => events.map((event) => ({
1736
+ ...event,
1737
+ requestId,
1738
+ sessionId: args.sessionId
1739
+ }));
1740
+ const blocker = classifyBlockerSignal({
1741
+ source: "network",
1742
+ url: typeof page.url === "string" ? page.url : void 0,
1743
+ finalUrl: typeof page.url === "string" ? page.url : void 0,
1744
+ title: typeof page.title === "string" ? page.title : void 0,
1745
+ status: findLatestStatus(networkChannel.events),
1746
+ traceRequestId: requestId,
1747
+ networkHosts: extractHosts(networkChannel.events),
1748
+ threshold: deps.config.get().blockerDetectionThreshold,
1749
+ promptGuardEnabled: deps.config.get().security.promptInjectionGuard?.enabled ?? true
1750
+ });
1751
+ const blockerArtifacts = blocker ? buildBlockerArtifacts({
1752
+ networkEvents: networkChannel.events,
1753
+ consoleEvents: consoleChannel.events,
1754
+ exceptionEvents: exceptionChannel.events,
1755
+ promptGuardEnabled: deps.config.get().security.promptInjectionGuard?.enabled ?? true,
1756
+ caps: deps.config.get().blockerArtifactCaps
1757
+ }) : void 0;
1758
+ return ok({
1759
+ requestId,
1760
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1761
+ page,
1762
+ channels: {
1763
+ console: {
1764
+ nextSeq: consoleChannel.nextSeq,
1765
+ truncated: consoleChannel.truncated,
1766
+ events: annotateTraceContext(consoleChannel.events)
1767
+ },
1768
+ network: {
1769
+ nextSeq: networkChannel.nextSeq,
1770
+ truncated: networkChannel.truncated,
1771
+ events: annotateTraceContext(networkChannel.events)
1772
+ },
1773
+ exception: {
1774
+ nextSeq: exceptionChannel.nextSeq,
1775
+ truncated: exceptionChannel.truncated,
1776
+ events: annotateTraceContext(exceptionChannel.events)
1777
+ }
1778
+ },
1779
+ meta: {
1780
+ blockerState: blocker ? "active" : "clear",
1781
+ ...blocker ? { blocker } : {},
1782
+ ...blockerArtifacts ? { blockerArtifacts } : {}
1783
+ }
1784
+ });
1785
+ } catch (error) {
1786
+ return failure(serializeError(error).message, "debug_trace_snapshot_failed");
1787
+ }
1788
+ }
1789
+ });
1790
+ }
1791
+ function findLatestStatus(events) {
1792
+ for (let index = events.length - 1; index >= 0; index -= 1) {
1793
+ const status = events[index]?.status;
1794
+ if (typeof status === "number") {
1795
+ return status;
1796
+ }
1797
+ }
1798
+ return void 0;
1799
+ }
1800
+ function extractHosts(events) {
1801
+ const hosts = [];
1802
+ const seen = /* @__PURE__ */ new Set();
1803
+ for (const event of events) {
1804
+ if (typeof event.url !== "string") continue;
1805
+ try {
1806
+ const host = new URL(event.url).hostname.toLowerCase();
1807
+ if (!host || seen.has(host)) continue;
1808
+ seen.add(host);
1809
+ hosts.push(host);
1810
+ } catch {
1811
+ }
1812
+ }
1813
+ return hosts;
1814
+ }
1815
+
1816
+ // src/tools/cookie_import.ts
1817
+ import { tool as tool36 } from "@opencode-ai/plugin";
1818
+ var z36 = tool36.schema;
1819
+ function validateCookieRecord(cookie) {
1820
+ const name = cookie.name?.trim();
1821
+ if (!name) {
1822
+ return { valid: false, reason: "Cookie name is required.", cookie };
1823
+ }
1824
+ if (!/^[^\s;=]+$/.test(name)) {
1825
+ return { valid: false, reason: `Invalid cookie name: ${cookie.name}.`, cookie };
1826
+ }
1827
+ if (typeof cookie.value !== "string") {
1828
+ return { valid: false, reason: `Invalid cookie value for ${name}.`, cookie };
1829
+ }
1830
+ const value = cookie.value;
1831
+ if (/\r|\n|;/.test(value)) {
1832
+ return { valid: false, reason: `Invalid cookie value for ${name}.`, cookie };
1833
+ }
1834
+ const hasUrl = typeof cookie.url === "string" && cookie.url.trim().length > 0;
1835
+ const hasDomain = typeof cookie.domain === "string" && cookie.domain.trim().length > 0;
1836
+ if (!hasUrl && !hasDomain) {
1837
+ return { valid: false, reason: `Cookie ${name} requires url or domain.`, cookie };
1838
+ }
1839
+ let normalizedUrl;
1840
+ if (hasUrl) {
1841
+ try {
1842
+ const parsedUrl = new URL(cookie.url);
1843
+ if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
1844
+ return { valid: false, reason: `Cookie ${name} url must be http(s).`, cookie };
1845
+ }
1846
+ normalizedUrl = parsedUrl.toString();
1847
+ } catch {
1848
+ return { valid: false, reason: `Cookie ${name} has invalid url.`, cookie };
1849
+ }
1850
+ }
1851
+ let normalizedDomain;
1852
+ if (hasDomain) {
1853
+ normalizedDomain = String(cookie.domain).trim().toLowerCase();
1854
+ if (!/^\.?[a-z0-9.-]+$/.test(normalizedDomain)) {
1855
+ return { valid: false, reason: `Cookie ${name} has invalid domain.`, cookie };
1856
+ }
1857
+ if (normalizedDomain.includes("..")) {
1858
+ return { valid: false, reason: `Cookie ${name} has invalid domain.`, cookie };
1859
+ }
1860
+ }
1861
+ const normalizedPath = typeof cookie.path === "string" ? cookie.path.trim() : void 0;
1862
+ if (typeof normalizedPath === "string" && !normalizedPath.startsWith("/")) {
1863
+ return { valid: false, reason: `Cookie ${name} path must start with '/'.`, cookie };
1864
+ }
1865
+ if (typeof cookie.expires !== "undefined") {
1866
+ if (!Number.isFinite(cookie.expires)) {
1867
+ return { valid: false, reason: `Cookie ${name} has invalid expires.`, cookie };
1868
+ }
1869
+ if (cookie.expires < -1) {
1870
+ return { valid: false, reason: `Cookie ${name} has invalid expires.`, cookie };
1871
+ }
1872
+ }
1873
+ if (cookie.sameSite === "None" && cookie.secure !== true) {
1874
+ return { valid: false, reason: `Cookie ${name} with SameSite=None must set secure=true.`, cookie };
1875
+ }
1876
+ const normalizedCookie = {
1877
+ name,
1878
+ value,
1879
+ ...typeof cookie.expires === "number" ? { expires: cookie.expires } : {},
1880
+ ...typeof cookie.httpOnly === "boolean" ? { httpOnly: cookie.httpOnly } : {},
1881
+ ...typeof cookie.secure === "boolean" ? { secure: cookie.secure } : {},
1882
+ ...cookie.sameSite ? { sameSite: cookie.sameSite } : {}
1883
+ };
1884
+ if (normalizedDomain) {
1885
+ normalizedCookie.domain = normalizedDomain;
1886
+ normalizedCookie.path = normalizedPath ?? "/";
1887
+ } else if (normalizedUrl) {
1888
+ normalizedCookie.url = normalizedUrl;
1889
+ }
1890
+ return {
1891
+ valid: true,
1892
+ reason: "",
1893
+ cookie: normalizedCookie
1894
+ };
1895
+ }
1896
+ function createCookieImportTool(deps) {
1897
+ return tool36({
1898
+ description: "Import validated cookies into the current session context.",
1899
+ args: {
1900
+ sessionId: z36.string().describe("Session id"),
1901
+ cookies: z36.array(z36.object({
1902
+ name: z36.string().min(1),
1903
+ value: z36.string(),
1904
+ url: z36.string().optional(),
1905
+ domain: z36.string().optional(),
1906
+ path: z36.string().optional(),
1907
+ expires: z36.number().optional(),
1908
+ httpOnly: z36.boolean().optional(),
1909
+ secure: z36.boolean().optional(),
1910
+ sameSite: z36.enum(["Strict", "Lax", "None"]).optional()
1911
+ })).min(1).describe("Cookies to import"),
1912
+ strict: z36.boolean().optional().describe("Reject on first invalid cookie (default true)"),
1913
+ requestId: z36.string().optional().describe("Optional trace request id")
1914
+ },
1915
+ async execute(args) {
1916
+ try {
1917
+ const strict = args.strict ?? true;
1918
+ const requestId = args.requestId ?? createRequestId();
1919
+ const manager = deps.manager;
1920
+ const normalized = [];
1921
+ const rejected = [];
1922
+ args.cookies.forEach((cookie, index) => {
1923
+ const validation = validateCookieRecord(cookie);
1924
+ if (!validation.valid) {
1925
+ rejected.push({ index, reason: validation.reason });
1926
+ return;
1927
+ }
1928
+ normalized.push(validation.cookie);
1929
+ });
1930
+ if (typeof manager.cookieImport === "function") {
1931
+ return ok(await manager.cookieImport(args.sessionId, normalized, strict, requestId));
1932
+ }
1933
+ if (strict && rejected.length > 0) {
1934
+ return failure(`Cookie import rejected ${rejected.length} entries.`, "cookie_import_failed");
1935
+ }
1936
+ if (normalized.length > 0) {
1937
+ await deps.manager.withPage(args.sessionId, null, async (page) => {
1938
+ await page.context().addCookies(normalized);
1939
+ return void 0;
1940
+ });
1941
+ }
1942
+ return ok({
1943
+ requestId,
1944
+ imported: normalized.length,
1945
+ rejected
1946
+ });
1947
+ } catch (error) {
1948
+ return failure(serializeError(error).message, "cookie_import_failed");
1949
+ }
1950
+ }
1951
+ });
1952
+ }
1953
+
1954
+ // src/tools/cookie_list.ts
1955
+ import { tool as tool37 } from "@opencode-ai/plugin";
1956
+ var z37 = tool37.schema;
1957
+ function normalizeCookieUrls(urls) {
1958
+ if (!urls || urls.length === 0) {
1959
+ return void 0;
1960
+ }
1961
+ const normalized = [];
1962
+ const seen = /* @__PURE__ */ new Set();
1963
+ for (const value of urls) {
1964
+ const trimmed = value.trim();
1965
+ if (!trimmed) {
1966
+ throw new Error("Cookie list urls must be non-empty strings.");
1967
+ }
1968
+ let parsedUrl;
1969
+ try {
1970
+ parsedUrl = new URL(trimmed);
1971
+ } catch {
1972
+ throw new Error(`Cookie list url is invalid: ${trimmed}`);
1973
+ }
1974
+ if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
1975
+ throw new Error(`Cookie list url must be http(s): ${trimmed}`);
1976
+ }
1977
+ const normalizedUrl = parsedUrl.toString();
1978
+ if (seen.has(normalizedUrl)) {
1979
+ continue;
1980
+ }
1981
+ seen.add(normalizedUrl);
1982
+ normalized.push(normalizedUrl);
1983
+ }
1984
+ return normalized;
1985
+ }
1986
+ function createCookieListTool(deps) {
1987
+ return tool37({
1988
+ description: "List cookies in the current session context with optional URL filtering.",
1989
+ args: {
1990
+ sessionId: z37.string().describe("Session id"),
1991
+ urls: z37.array(z37.string().min(1)).optional().describe("Optional URL filters for cookie scoping"),
1992
+ requestId: z37.string().optional().describe("Optional trace request id")
1993
+ },
1994
+ async execute(args) {
1995
+ try {
1996
+ const manager = deps.manager;
1997
+ const normalizedUrls = normalizeCookieUrls(args.urls);
1998
+ const requestId = args.requestId ?? createRequestId();
1999
+ if (typeof manager.cookieList === "function") {
2000
+ return ok(await manager.cookieList(args.sessionId, normalizedUrls, requestId));
2001
+ }
2002
+ const cookies = await deps.manager.withPage(
2003
+ args.sessionId,
2004
+ null,
2005
+ async (page) => {
2006
+ const listed = normalizedUrls ? await page.context().cookies(normalizedUrls) : await page.context().cookies();
2007
+ return listed.map((cookie) => ({
2008
+ name: cookie.name,
2009
+ value: cookie.value,
2010
+ domain: cookie.domain,
2011
+ path: cookie.path,
2012
+ expires: cookie.expires,
2013
+ httpOnly: cookie.httpOnly,
2014
+ secure: cookie.secure,
2015
+ ...cookie.sameSite ? { sameSite: cookie.sameSite } : {}
2016
+ }));
2017
+ }
2018
+ );
2019
+ return ok({
2020
+ requestId,
2021
+ cookies,
2022
+ count: cookies.length
2023
+ });
2024
+ } catch (error) {
2025
+ return failure(serializeError(error).message, "cookie_list_failed");
2026
+ }
2027
+ }
2028
+ });
2029
+ }
2030
+
2031
+ // src/tools/macro_resolve.ts
2032
+ import { tool as tool38 } from "@opencode-ai/plugin";
2033
+ var z38 = tool38.schema;
2034
+ async function loadMacroRuntime() {
2035
+ try {
2036
+ const module = await import("./macros-NUBRM44Y.js");
2037
+ return module;
2038
+ } catch {
2039
+ return null;
2040
+ }
2041
+ }
2042
+ function parseFallbackMacro(expression, defaultProvider) {
2043
+ const raw = expression.trim();
2044
+ if (!raw.startsWith("@")) {
2045
+ throw new Error("Macro expressions must start with '@'");
2046
+ }
2047
+ const body = raw.slice(1).trim();
2048
+ if (!body) {
2049
+ throw new Error("Macro name is required");
2050
+ }
2051
+ const openParen = body.indexOf("(");
2052
+ const closeParen = body.endsWith(")") ? body.length - 1 : -1;
2053
+ const macroName = openParen >= 0 ? body.slice(0, openParen).trim() : body;
2054
+ const argsBody = openParen >= 0 && closeParen > openParen ? body.slice(openParen + 1, closeParen).trim() : "";
2055
+ const positional = argsBody ? argsBody.split(",").map((part) => part.trim().replace(/^['\"]|['\"]$/g, "")).filter(Boolean) : [];
2056
+ const query = positional[0] ?? macroName;
2057
+ const provider = defaultProvider ?? "web/default";
2058
+ return {
2059
+ action: {
2060
+ source: "web",
2061
+ operation: "search",
2062
+ input: {
2063
+ query,
2064
+ limit: 10,
2065
+ providerId: provider
2066
+ }
2067
+ },
2068
+ provenance: {
2069
+ macro: macroName,
2070
+ provider,
2071
+ resolvedQuery: query,
2072
+ pack: "fallback",
2073
+ args: {
2074
+ positional,
2075
+ named: {}
2076
+ }
2077
+ }
2078
+ };
2079
+ }
2080
+ function createMacroResolveTool(deps) {
2081
+ return tool38({
2082
+ description: "Resolve a macro expression to a provider action with provenance metadata.",
2083
+ args: {
2084
+ expression: z38.string().min(2).describe('Macro expression, e.g. @web.search("query")'),
2085
+ defaultProvider: z38.string().optional().describe("Default provider fallback"),
2086
+ includeCatalog: z38.boolean().optional().describe("Include available runtime macro names"),
2087
+ execute: z38.boolean().optional().describe("Execute the resolved provider action and include execution payload")
2088
+ },
2089
+ async execute(args) {
2090
+ try {
2091
+ const runtime = await loadMacroRuntime();
2092
+ const registry = runtime?.createDefaultMacroRegistry?.();
2093
+ let resolvedRuntime = "fallback";
2094
+ let resolution;
2095
+ let catalog;
2096
+ if (registry) {
2097
+ resolvedRuntime = "macros";
2098
+ resolution = await registry.resolve(args.expression, {
2099
+ defaultProvider: args.defaultProvider
2100
+ });
2101
+ catalog = args.includeCatalog ? registry.list().map((entry) => ({
2102
+ name: entry.name,
2103
+ pack: entry.pack,
2104
+ description: entry.description
2105
+ })) : void 0;
2106
+ } else {
2107
+ resolution = parseFallbackMacro(args.expression, args.defaultProvider);
2108
+ }
2109
+ if (!args.execute) {
2110
+ return ok({
2111
+ runtime: resolvedRuntime,
2112
+ resolution,
2113
+ ...catalog ? { catalog } : {}
2114
+ });
2115
+ }
2116
+ const runtimeConfig = deps.config?.get?.();
2117
+ const providerRuntime = deps.providerRuntime ?? createConfiguredProviderRuntime({
2118
+ config: runtimeConfig,
2119
+ manager: deps.manager,
2120
+ browserFallbackPort: deps.browserFallbackPort
2121
+ });
2122
+ const execution = shapeExecutionPayload(
2123
+ await executeMacroResolution(resolution, providerRuntime)
2124
+ );
2125
+ return ok({
2126
+ runtime: resolvedRuntime,
2127
+ resolution,
2128
+ ...catalog ? { catalog } : {},
2129
+ execution
2130
+ });
2131
+ } catch (error) {
2132
+ return failure(serializeError(error).message, "macro_resolve_failed");
2133
+ }
2134
+ }
2135
+ });
2136
+ }
2137
+
2138
+ // src/tools/clone_page.ts
2139
+ import { tool as tool39 } from "@opencode-ai/plugin";
2140
+ var z39 = tool39.schema;
2141
+ function createClonePageTool(deps) {
2142
+ return tool39({
859
2143
  description: "Export the active page as a React component and CSS bundle.",
860
2144
  args: {
861
- sessionId: z25.string().describe("Active browser session id")
2145
+ sessionId: z39.string().describe("Active browser session id")
862
2146
  },
863
2147
  async execute(args) {
864
2148
  try {
@@ -872,14 +2156,14 @@ function createClonePageTool(deps) {
872
2156
  }
873
2157
 
874
2158
  // src/tools/clone_component.ts
875
- import { tool as tool26 } from "@opencode-ai/plugin";
876
- var z26 = tool26.schema;
2159
+ import { tool as tool40 } from "@opencode-ai/plugin";
2160
+ var z40 = tool40.schema;
877
2161
  function createCloneComponentTool(deps) {
878
- return tool26({
2162
+ return tool40({
879
2163
  description: "Export a selected element subtree as a React component and CSS bundle.",
880
2164
  args: {
881
- sessionId: z26.string().describe("Active browser session id"),
882
- ref: z26.string().describe("Element ref from snapshot")
2165
+ sessionId: z40.string().describe("Active browser session id"),
2166
+ ref: z40.string().describe("Element ref from snapshot")
883
2167
  },
884
2168
  async execute(args) {
885
2169
  try {
@@ -893,13 +2177,13 @@ function createCloneComponentTool(deps) {
893
2177
  }
894
2178
 
895
2179
  // src/tools/perf.ts
896
- import { tool as tool27 } from "@opencode-ai/plugin";
897
- var z27 = tool27.schema;
2180
+ import { tool as tool41 } from "@opencode-ai/plugin";
2181
+ var z41 = tool41.schema;
898
2182
  function createPerfTool(deps) {
899
- return tool27({
2183
+ return tool41({
900
2184
  description: "Fetch lightweight performance metrics from the active page.",
901
2185
  args: {
902
- sessionId: z27.string().describe("Active browser session id")
2186
+ sessionId: z41.string().describe("Active browser session id")
903
2187
  },
904
2188
  async execute(args) {
905
2189
  try {
@@ -913,14 +2197,14 @@ function createPerfTool(deps) {
913
2197
  }
914
2198
 
915
2199
  // src/tools/screenshot.ts
916
- import { tool as tool28 } from "@opencode-ai/plugin";
917
- var z28 = tool28.schema;
2200
+ import { tool as tool42 } from "@opencode-ai/plugin";
2201
+ var z42 = tool42.schema;
918
2202
  function createScreenshotTool(deps) {
919
- return tool28({
2203
+ return tool42({
920
2204
  description: "Capture a screenshot of the active page.",
921
2205
  args: {
922
- sessionId: z28.string().describe("Active browser session id"),
923
- path: z28.string().optional().describe("Optional output file path")
2206
+ sessionId: z42.string().describe("Active browser session id"),
2207
+ path: z42.string().optional().describe("Optional output file path")
924
2208
  },
925
2209
  async execute(args) {
926
2210
  try {
@@ -933,10 +2217,243 @@ function createScreenshotTool(deps) {
933
2217
  });
934
2218
  }
935
2219
 
2220
+ // src/tools/annotate.ts
2221
+ import { tool as tool43 } from "@opencode-ai/plugin";
2222
+ var z43 = tool43.schema;
2223
+ var screenshotModeSchema = z43.enum(["visible", "full", "none"]);
2224
+ var transportSchema = z43.enum(["auto", "direct", "relay"]);
2225
+ function createAnnotateTool(deps) {
2226
+ return tool43({
2227
+ description: "Request interactive annotations via direct (CDP) or relay transport.",
2228
+ args: {
2229
+ sessionId: z43.string().describe("Session id"),
2230
+ transport: transportSchema.optional().describe("auto | direct | relay (default: auto)"),
2231
+ targetId: z43.string().optional().describe("Optional target id for direct mode"),
2232
+ tabId: z43.number().int().optional().describe("Optional Chrome tab id for relay mode"),
2233
+ url: z43.string().optional().describe("Optional URL to open before annotating"),
2234
+ screenshotMode: screenshotModeSchema.optional().describe("visible | full | none (default: visible)"),
2235
+ debug: z43.boolean().optional().describe("Include debug metadata"),
2236
+ context: z43.string().optional().describe("Optional context for the annotator"),
2237
+ timeoutMs: z43.number().int().optional().describe("Timeout in milliseconds")
2238
+ },
2239
+ async execute(args) {
2240
+ try {
2241
+ const transport = args.transport ?? "auto";
2242
+ if (transport === "relay") {
2243
+ const status = await deps.manager.status(args.sessionId);
2244
+ if (status.mode !== "extension") {
2245
+ return failure("Annotations require extension mode (relay).", "annotate_requires_extension");
2246
+ }
2247
+ }
2248
+ const response = await deps.annotationManager.requestAnnotation({
2249
+ sessionId: args.sessionId,
2250
+ transport,
2251
+ targetId: args.targetId,
2252
+ tabId: args.tabId,
2253
+ url: args.url,
2254
+ screenshotMode: args.screenshotMode ?? "visible",
2255
+ debug: args.debug ?? false,
2256
+ context: args.context,
2257
+ timeoutMs: args.timeoutMs
2258
+ });
2259
+ if (response.status !== "ok" || !response.payload) {
2260
+ const message2 = response.error?.message ?? "Annotation failed.";
2261
+ const code = response.error?.code ?? "annotate_failed";
2262
+ return failure(message2, code);
2263
+ }
2264
+ const { message, details, screenshots } = await buildAnnotateResult(response.payload);
2265
+ return ok({
2266
+ message,
2267
+ details,
2268
+ screenshots
2269
+ });
2270
+ } catch (error) {
2271
+ return failure(serializeError(error).message, "annotate_failed");
2272
+ }
2273
+ }
2274
+ });
2275
+ }
2276
+
2277
+ // src/tools/research_run.ts
2278
+ import { tool as tool44 } from "@opencode-ai/plugin";
2279
+
2280
+ // src/tools/workflow-runtime.ts
2281
+ var resolveProviderRuntime = (deps) => {
2282
+ if (deps.providerRuntime) {
2283
+ return deps.providerRuntime;
2284
+ }
2285
+ return createConfiguredProviderRuntime({
2286
+ config: deps.config?.get?.(),
2287
+ manager: deps.manager,
2288
+ browserFallbackPort: deps.browserFallbackPort
2289
+ });
2290
+ };
2291
+
2292
+ // src/tools/research_run.ts
2293
+ var z44 = tool44.schema;
2294
+ var sourceSelectionSchema = z44.enum(["auto", "web", "community", "social", "shopping", "all"]);
2295
+ var sourceSchema = z44.enum(["web", "community", "social", "shopping"]);
2296
+ var modeSchema = z44.enum(["compact", "json", "md", "context", "path"]);
2297
+ var cookiePolicySchema = z44.enum(["off", "auto", "required"]);
2298
+ function createResearchRunTool(deps) {
2299
+ return tool44({
2300
+ description: "Run cross-source research with strict timebox and artifact outputs.",
2301
+ args: {
2302
+ topic: z44.string().min(1).describe("Research topic"),
2303
+ days: z44.number().int().positive().optional().describe("Timebox in days"),
2304
+ from: z44.string().optional().describe("ISO start date"),
2305
+ to: z44.string().optional().describe("ISO end date"),
2306
+ sourceSelection: sourceSelectionSchema.optional().describe("auto|web|community|social|shopping|all"),
2307
+ sources: z44.array(sourceSchema).optional().describe("Explicit source list"),
2308
+ mode: modeSchema.optional().describe("compact|json|md|context|path"),
2309
+ includeEngagement: z44.boolean().optional().describe("Include engagement enrichment"),
2310
+ limitPerSource: z44.number().int().positive().optional().describe("Result limit per source"),
2311
+ outputDir: z44.string().optional().describe("Optional artifact output directory"),
2312
+ ttlHours: z44.number().int().positive().optional().describe("Artifact retention TTL in hours"),
2313
+ useCookies: z44.boolean().optional().describe("Enable/disable provider cookie injection for this run"),
2314
+ cookiePolicyOverride: cookiePolicySchema.optional().describe("Override cookie policy: off|auto|required")
2315
+ },
2316
+ async execute(args) {
2317
+ try {
2318
+ const runtime = resolveProviderRuntime(deps);
2319
+ const result = await runResearchWorkflow(runtime, {
2320
+ topic: args.topic,
2321
+ days: args.days,
2322
+ from: args.from,
2323
+ to: args.to,
2324
+ sourceSelection: args.sourceSelection,
2325
+ sources: args.sources,
2326
+ mode: args.mode ?? "compact",
2327
+ includeEngagement: args.includeEngagement,
2328
+ limitPerSource: args.limitPerSource,
2329
+ outputDir: args.outputDir,
2330
+ ttlHours: args.ttlHours,
2331
+ useCookies: args.useCookies,
2332
+ cookiePolicyOverride: args.cookiePolicyOverride
2333
+ });
2334
+ return ok(result);
2335
+ } catch (error) {
2336
+ return failure(serializeError(error).message, "research_run_failed");
2337
+ }
2338
+ }
2339
+ });
2340
+ }
2341
+
2342
+ // src/tools/shopping_run.ts
2343
+ import { tool as tool45 } from "@opencode-ai/plugin";
2344
+ var z45 = tool45.schema;
2345
+ var sortSchema = z45.enum(["best_deal", "lowest_price", "highest_rating", "fastest_shipping"]);
2346
+ var modeSchema2 = z45.enum(["compact", "json", "md", "context", "path"]);
2347
+ var cookiePolicySchema2 = z45.enum(["off", "auto", "required"]);
2348
+ function createShoppingRunTool(deps) {
2349
+ return tool45({
2350
+ description: "Run shopping/deal intelligence across shopping providers.",
2351
+ args: {
2352
+ query: z45.string().min(1).describe("Shopping query"),
2353
+ providers: z45.array(z45.string()).optional().describe("Optional provider allow-list"),
2354
+ budget: z45.number().positive().optional().describe("Optional budget amount"),
2355
+ region: z45.string().optional().describe("Region hint"),
2356
+ sort: sortSchema.optional().describe("best_deal|lowest_price|highest_rating|fastest_shipping"),
2357
+ mode: modeSchema2.optional().describe("compact|json|md|context|path"),
2358
+ outputDir: z45.string().optional().describe("Optional artifact output directory"),
2359
+ ttlHours: z45.number().int().positive().optional().describe("Artifact retention TTL in hours"),
2360
+ useCookies: z45.boolean().optional().describe("Enable/disable provider cookie injection for this run"),
2361
+ cookiePolicyOverride: cookiePolicySchema2.optional().describe("Override cookie policy: off|auto|required")
2362
+ },
2363
+ async execute(args) {
2364
+ try {
2365
+ const runtime = resolveProviderRuntime(deps);
2366
+ const result = await runShoppingWorkflow(runtime, {
2367
+ query: args.query,
2368
+ providers: args.providers,
2369
+ budget: args.budget,
2370
+ region: args.region,
2371
+ sort: args.sort,
2372
+ mode: args.mode ?? "compact",
2373
+ outputDir: args.outputDir,
2374
+ ttlHours: args.ttlHours,
2375
+ useCookies: args.useCookies,
2376
+ cookiePolicyOverride: args.cookiePolicyOverride
2377
+ });
2378
+ return ok(result);
2379
+ } catch (error) {
2380
+ return failure(serializeError(error).message, "shopping_run_failed");
2381
+ }
2382
+ }
2383
+ });
2384
+ }
2385
+
2386
+ // src/tools/product_video_run.ts
2387
+ import { tool as tool46 } from "@opencode-ai/plugin";
2388
+ var z46 = tool46.schema;
2389
+ var cookiePolicySchema3 = z46.enum(["off", "auto", "required"]);
2390
+ async function captureScreenshotBuffer(deps, url) {
2391
+ let sessionId = null;
2392
+ try {
2393
+ const launched = await deps.manager.launch({
2394
+ headless: true,
2395
+ startUrl: url
2396
+ });
2397
+ sessionId = launched.sessionId;
2398
+ const screenshot = await deps.manager.screenshot(sessionId);
2399
+ if (typeof screenshot.base64 === "string" && screenshot.base64.length > 0) {
2400
+ return Buffer.from(screenshot.base64, "base64");
2401
+ }
2402
+ return null;
2403
+ } catch {
2404
+ return null;
2405
+ } finally {
2406
+ if (sessionId) {
2407
+ await deps.manager.disconnect(sessionId, true).catch(() => {
2408
+ });
2409
+ }
2410
+ }
2411
+ }
2412
+ function createProductVideoRunTool(deps) {
2413
+ return tool46({
2414
+ description: "Collect a product presentation asset pack for video/UGC workflows.",
2415
+ args: {
2416
+ product_url: z46.string().optional().describe("Product URL"),
2417
+ product_name: z46.string().optional().describe("Product name"),
2418
+ provider_hint: z46.string().optional().describe("Optional provider hint"),
2419
+ include_screenshots: z46.boolean().optional().describe("Include screenshots (default true)"),
2420
+ include_all_images: z46.boolean().optional().describe("Include all discovered images (default true)"),
2421
+ include_copy: z46.boolean().optional().describe("Include product copy extraction (default true)"),
2422
+ output_dir: z46.string().optional().describe("Optional output directory"),
2423
+ ttl_hours: z46.number().int().positive().optional().describe("Artifact retention TTL in hours"),
2424
+ useCookies: z46.boolean().optional().describe("Enable/disable provider cookie injection for this run"),
2425
+ cookiePolicyOverride: cookiePolicySchema3.optional().describe("Override cookie policy: off|auto|required")
2426
+ },
2427
+ async execute(args) {
2428
+ try {
2429
+ const runtime = resolveProviderRuntime(deps);
2430
+ const includeScreenshots = args.include_screenshots ?? true;
2431
+ const result = await runProductVideoWorkflow(runtime, {
2432
+ product_url: args.product_url,
2433
+ product_name: args.product_name,
2434
+ provider_hint: args.provider_hint,
2435
+ include_screenshots: includeScreenshots,
2436
+ include_all_images: args.include_all_images,
2437
+ include_copy: args.include_copy,
2438
+ output_dir: args.output_dir,
2439
+ ttl_hours: args.ttl_hours,
2440
+ useCookies: args.useCookies,
2441
+ cookiePolicyOverride: args.cookiePolicyOverride
2442
+ }, {
2443
+ captureScreenshot: includeScreenshots ? async (url) => captureScreenshotBuffer(deps, url) : void 0
2444
+ });
2445
+ return ok(result);
2446
+ } catch (error) {
2447
+ return failure(serializeError(error).message, "product_video_run_failed");
2448
+ }
2449
+ }
2450
+ });
2451
+ }
2452
+
936
2453
  // src/tools/skill_list.ts
937
- import { tool as tool29 } from "@opencode-ai/plugin";
2454
+ import { tool as tool47 } from "@opencode-ai/plugin";
938
2455
  function createSkillListTool(deps) {
939
- return tool29({
2456
+ return tool47({
940
2457
  description: "List available skills from OpenCode skill directories (compatibility wrapper)",
941
2458
  args: {},
942
2459
  async execute() {
@@ -952,14 +2469,14 @@ function createSkillListTool(deps) {
952
2469
  }
953
2470
 
954
2471
  // src/tools/skill_load.ts
955
- import { tool as tool30 } from "@opencode-ai/plugin";
956
- var z29 = tool30.schema;
2472
+ import { tool as tool48 } from "@opencode-ai/plugin";
2473
+ var z47 = tool48.schema;
957
2474
  function createSkillLoadTool(deps) {
958
- return tool30({
2475
+ return tool48({
959
2476
  description: "Load a specific skill by name from OpenCode skill directories (compatibility wrapper)",
960
2477
  args: {
961
- name: z29.string().describe("Name of the skill to load (e.g., 'login-automation', 'form-testing')"),
962
- topic: z29.string().optional().describe("Optional topic to filter the skill content")
2478
+ name: z47.string().describe("Name of the skill to load (e.g., 'opendevbrowser-login-automation', 'opendevbrowser-form-testing')"),
2479
+ topic: z47.string().optional().describe("Optional topic to filter the skill content")
963
2480
  },
964
2481
  async execute(args) {
965
2482
  try {
@@ -975,44 +2492,83 @@ function createSkillLoadTool(deps) {
975
2492
 
976
2493
  // src/tools/index.ts
977
2494
  function createTools(deps) {
2495
+ const wrap = (definition) => {
2496
+ if (!deps.ensureHub) return definition;
2497
+ return {
2498
+ ...definition,
2499
+ execute: async (args, context) => {
2500
+ try {
2501
+ await deps.ensureHub?.();
2502
+ } catch {
2503
+ }
2504
+ return definition.execute(args, context);
2505
+ }
2506
+ };
2507
+ };
978
2508
  return {
979
- opendevbrowser_launch: createLaunchTool(deps),
980
- opendevbrowser_connect: createConnectTool(deps),
981
- opendevbrowser_disconnect: createDisconnectTool(deps),
982
- opendevbrowser_status: createStatusTool(deps),
983
- opendevbrowser_targets_list: createTargetsListTool(deps),
984
- opendevbrowser_target_use: createTargetUseTool(deps),
985
- opendevbrowser_target_new: createTargetNewTool(deps),
986
- opendevbrowser_target_close: createTargetCloseTool(deps),
987
- opendevbrowser_page: createPageTool(deps),
988
- opendevbrowser_list: createListTool(deps),
989
- opendevbrowser_close: createCloseTool(deps),
990
- opendevbrowser_goto: createGotoTool(deps),
991
- opendevbrowser_wait: createWaitTool(deps),
992
- opendevbrowser_snapshot: createSnapshotTool(deps),
993
- opendevbrowser_click: createClickTool(deps),
994
- opendevbrowser_type: createTypeTool(deps),
995
- opendevbrowser_select: createSelectTool(deps),
996
- opendevbrowser_scroll: createScrollTool(deps),
997
- opendevbrowser_dom_get_html: createDomGetHtmlTool(deps),
998
- opendevbrowser_dom_get_text: createDomGetTextTool(deps),
999
- opendevbrowser_run: createRunTool(deps),
1000
- opendevbrowser_prompting_guide: createPromptingGuideTool(deps),
1001
- opendevbrowser_console_poll: createConsolePollTool(deps),
1002
- opendevbrowser_network_poll: createNetworkPollTool(deps),
1003
- opendevbrowser_clone_page: createClonePageTool(deps),
1004
- opendevbrowser_clone_component: createCloneComponentTool(deps),
1005
- opendevbrowser_perf: createPerfTool(deps),
1006
- opendevbrowser_screenshot: createScreenshotTool(deps),
1007
- opendevbrowser_skill_list: createSkillListTool(deps),
1008
- opendevbrowser_skill_load: createSkillLoadTool(deps)
2509
+ opendevbrowser_launch: wrap(createLaunchTool(deps)),
2510
+ opendevbrowser_connect: wrap(createConnectTool(deps)),
2511
+ opendevbrowser_disconnect: wrap(createDisconnectTool(deps)),
2512
+ opendevbrowser_status: wrap(createStatusTool(deps)),
2513
+ opendevbrowser_targets_list: wrap(createTargetsListTool(deps)),
2514
+ opendevbrowser_target_use: wrap(createTargetUseTool(deps)),
2515
+ opendevbrowser_target_new: wrap(createTargetNewTool(deps)),
2516
+ opendevbrowser_target_close: wrap(createTargetCloseTool(deps)),
2517
+ opendevbrowser_page: wrap(createPageTool(deps)),
2518
+ opendevbrowser_list: wrap(createListTool(deps)),
2519
+ opendevbrowser_close: wrap(createCloseTool(deps)),
2520
+ opendevbrowser_goto: wrap(createGotoTool(deps)),
2521
+ opendevbrowser_wait: wrap(createWaitTool(deps)),
2522
+ opendevbrowser_snapshot: wrap(createSnapshotTool(deps)),
2523
+ opendevbrowser_click: wrap(createClickTool(deps)),
2524
+ opendevbrowser_hover: wrap(createHoverTool(deps)),
2525
+ opendevbrowser_press: wrap(createPressTool(deps)),
2526
+ opendevbrowser_check: wrap(createCheckTool(deps)),
2527
+ opendevbrowser_uncheck: wrap(createUncheckTool(deps)),
2528
+ opendevbrowser_type: wrap(createTypeTool(deps)),
2529
+ opendevbrowser_select: wrap(createSelectTool(deps)),
2530
+ opendevbrowser_scroll: wrap(createScrollTool(deps)),
2531
+ opendevbrowser_scroll_into_view: wrap(createScrollIntoViewTool(deps)),
2532
+ opendevbrowser_dom_get_html: wrap(createDomGetHtmlTool(deps)),
2533
+ opendevbrowser_dom_get_text: wrap(createDomGetTextTool(deps)),
2534
+ opendevbrowser_get_attr: wrap(createGetAttrTool(deps)),
2535
+ opendevbrowser_get_value: wrap(createGetValueTool(deps)),
2536
+ opendevbrowser_is_visible: wrap(createIsVisibleTool(deps)),
2537
+ opendevbrowser_is_enabled: wrap(createIsEnabledTool(deps)),
2538
+ opendevbrowser_is_checked: wrap(createIsCheckedTool(deps)),
2539
+ opendevbrowser_run: wrap(createRunTool(deps)),
2540
+ opendevbrowser_prompting_guide: wrap(createPromptingGuideTool(deps)),
2541
+ opendevbrowser_console_poll: wrap(createConsolePollTool(deps)),
2542
+ opendevbrowser_network_poll: wrap(createNetworkPollTool(deps)),
2543
+ opendevbrowser_debug_trace_snapshot: wrap(createDebugTraceSnapshotTool(deps)),
2544
+ opendevbrowser_cookie_import: wrap(createCookieImportTool(deps)),
2545
+ opendevbrowser_cookie_list: wrap(createCookieListTool(deps)),
2546
+ opendevbrowser_macro_resolve: wrap(createMacroResolveTool(deps)),
2547
+ opendevbrowser_research_run: wrap(createResearchRunTool(deps)),
2548
+ opendevbrowser_shopping_run: wrap(createShoppingRunTool(deps)),
2549
+ opendevbrowser_product_video_run: wrap(createProductVideoRunTool(deps)),
2550
+ opendevbrowser_clone_page: wrap(createClonePageTool(deps)),
2551
+ opendevbrowser_clone_component: wrap(createCloneComponentTool(deps)),
2552
+ opendevbrowser_perf: wrap(createPerfTool(deps)),
2553
+ opendevbrowser_screenshot: wrap(createScreenshotTool(deps)),
2554
+ opendevbrowser_annotate: wrap(createAnnotateTool(deps)),
2555
+ opendevbrowser_skill_list: wrap(createSkillListTool(deps)),
2556
+ opendevbrowser_skill_load: wrap(createSkillLoadTool(deps))
1009
2557
  };
1010
2558
  }
1011
2559
 
1012
2560
  // src/index.ts
1013
2561
  var OpenDevBrowserPlugin = async ({ directory, worktree }) => {
1014
2562
  const core = createOpenDevBrowserCore({ directory, worktree });
1015
- const { config, configStore, manager, runner, skills, relay, ensureRelay, cleanup, getExtensionPath } = core;
2563
+ const { config, configStore, skills, ensureRelay, cleanup, getExtensionPath } = core;
2564
+ let relay = core.relay;
2565
+ let manager = core.manager;
2566
+ let runner = core.runner;
2567
+ let annotationManager = core.annotationManager;
2568
+ let providerRuntime = core.providerRuntime;
2569
+ let browserFallbackPort = core.browserFallbackPort;
2570
+ let hubStop = null;
2571
+ let daemonClient = null;
1016
2572
  const skillNudgeState = createSkillNudgeState();
1017
2573
  const continuityNudgeState = createContinuityNudgeState();
1018
2574
  console.info(
@@ -1023,12 +2579,99 @@ var OpenDevBrowserPlugin = async ({ directory, worktree }) => {
1023
2579
  } catch (error) {
1024
2580
  console.warn("Extension extraction failed:", error instanceof Error ? error.message : error);
1025
2581
  }
1026
- process.on("SIGINT", cleanup);
1027
- process.on("SIGTERM", cleanup);
1028
- process.on("beforeExit", cleanup);
1029
- await ensureRelay(config.relayPort);
2582
+ const toolDeps = {
2583
+ manager,
2584
+ annotationManager,
2585
+ runner,
2586
+ config: configStore,
2587
+ skills,
2588
+ providerRuntime,
2589
+ browserFallbackPort,
2590
+ relay,
2591
+ getExtensionPath
2592
+ };
2593
+ const bindRemote = () => {
2594
+ if (!daemonClient) {
2595
+ daemonClient = new DaemonClient({ autoRenew: true });
2596
+ }
2597
+ manager = new RemoteManager(daemonClient);
2598
+ relay = new RemoteRelay(daemonClient);
2599
+ annotationManager.setRelay(relay);
2600
+ annotationManager.setBrowserManager(manager);
2601
+ runner = new ScriptRunner(manager);
2602
+ browserFallbackPort = createBrowserFallbackPort(manager);
2603
+ providerRuntime = createConfiguredProviderRuntime({
2604
+ config: configStore.get(),
2605
+ manager,
2606
+ browserFallbackPort
2607
+ });
2608
+ toolDeps.manager = manager;
2609
+ toolDeps.relay = relay;
2610
+ toolDeps.runner = runner;
2611
+ toolDeps.providerRuntime = providerRuntime;
2612
+ toolDeps.browserFallbackPort = browserFallbackPort;
2613
+ };
2614
+ const ensureHub = async () => {
2615
+ const currentConfig = configStore.get();
2616
+ if (!isHubEnabled(currentConfig)) {
2617
+ return;
2618
+ }
2619
+ if (!daemonClient) {
2620
+ daemonClient = new DaemonClient({ autoRenew: true });
2621
+ }
2622
+ const deadline = Date.now() + 2e3;
2623
+ let attempt = 0;
2624
+ let lastError = null;
2625
+ while (attempt < 2 && Date.now() < deadline) {
2626
+ attempt += 1;
2627
+ const status = await fetchDaemonStatusFromMetadata(currentConfig);
2628
+ if (status?.ok) {
2629
+ bindRemote();
2630
+ await relay?.refresh?.();
2631
+ return;
2632
+ }
2633
+ try {
2634
+ const { stop } = await startDaemon({ config: currentConfig, directory, worktree });
2635
+ hubStop = stop;
2636
+ } catch (error) {
2637
+ lastError = error instanceof Error ? error : new Error(String(error));
2638
+ }
2639
+ if (Date.now() < deadline) {
2640
+ await new Promise((resolve) => setTimeout(resolve, 500));
2641
+ }
2642
+ }
2643
+ if (lastError) {
2644
+ throw lastError;
2645
+ }
2646
+ throw new Error("Hub daemon unavailable.");
2647
+ };
2648
+ toolDeps.ensureHub = ensureHub;
2649
+ const hubEnabled = isHubEnabled(config);
2650
+ if (hubEnabled) {
2651
+ bindRemote();
2652
+ try {
2653
+ await ensureHub();
2654
+ } catch (error) {
2655
+ const message = error instanceof Error ? error.message : String(error);
2656
+ console.warn(`[opendevbrowser] Hub daemon unavailable: ${message}`);
2657
+ }
2658
+ } else {
2659
+ await ensureRelay(config.relayPort);
2660
+ }
2661
+ const cleanupAll = () => {
2662
+ if (hubStop) {
2663
+ hubStop().catch(() => {
2664
+ });
2665
+ }
2666
+ daemonClient?.releaseBinding().catch(() => {
2667
+ });
2668
+ cleanup();
2669
+ };
2670
+ process.on("SIGINT", cleanupAll);
2671
+ process.on("SIGTERM", cleanupAll);
2672
+ process.on("beforeExit", cleanupAll);
1030
2673
  return {
1031
- tool: createTools({ manager, runner, config: configStore, skills, relay, getExtensionPath }),
2674
+ tool: createTools(toolDeps),
1032
2675
  "chat.message": async (_input, output) => {
1033
2676
  const config2 = configStore.get();
1034
2677
  if (output.message.role !== "user") return;