opendevbrowser 0.0.28 → 0.0.29

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 (313) hide show
  1. package/README.md +2 -2
  2. package/dist/accessibility-snapshot-PA6NWNS7.js +39 -0
  3. package/dist/accessibility-snapshot-PA6NWNS7.js.map +1 -0
  4. package/dist/active-window-YNYTIPZN.js +37 -0
  5. package/dist/active-window-YNYTIPZN.js.map +1 -0
  6. package/dist/annotate-STYHXZYJ.js +205 -0
  7. package/dist/annotate-STYHXZYJ.js.map +1 -0
  8. package/dist/artifacts-KJ6RNDO2.js +120 -0
  9. package/dist/artifacts-KJ6RNDO2.js.map +1 -0
  10. package/dist/attr-GHFZZ4SA.js +84 -0
  11. package/dist/attr-GHFZZ4SA.js.map +1 -0
  12. package/dist/browser/ops-client.d.ts +1 -0
  13. package/dist/browser/ops-client.d.ts.map +1 -1
  14. package/dist/canvas-54FBOEGP.js +309 -0
  15. package/dist/canvas-54FBOEGP.js.map +1 -0
  16. package/dist/capture-desktop-SNABC24E.js +38 -0
  17. package/dist/capture-desktop-SNABC24E.js.map +1 -0
  18. package/dist/capture-window-UJSB5AMP.js +40 -0
  19. package/dist/capture-window-UJSB5AMP.js.map +1 -0
  20. package/dist/check-ST5UQ2F5.js +71 -0
  21. package/dist/check-ST5UQ2F5.js.map +1 -0
  22. package/dist/checked-IEMWI5CU.js +71 -0
  23. package/dist/checked-IEMWI5CU.js.map +1 -0
  24. package/dist/chunk-2CG4SW3E.js +64 -0
  25. package/dist/chunk-2CG4SW3E.js.map +1 -0
  26. package/dist/chunk-2SIMIPLY.js +67 -0
  27. package/dist/chunk-2SIMIPLY.js.map +1 -0
  28. package/dist/chunk-37VSRUW4.js +141 -0
  29. package/dist/chunk-37VSRUW4.js.map +1 -0
  30. package/dist/chunk-5SWZDVOW.js +144 -0
  31. package/dist/chunk-5SWZDVOW.js.map +1 -0
  32. package/dist/chunk-6PVZ2ABC.js +429 -0
  33. package/dist/chunk-6PVZ2ABC.js.map +1 -0
  34. package/dist/chunk-7GVOUZMQ.js +64 -0
  35. package/dist/chunk-7GVOUZMQ.js.map +1 -0
  36. package/dist/chunk-7THCPS52.js +84 -0
  37. package/dist/chunk-7THCPS52.js.map +1 -0
  38. package/dist/chunk-ASMHEEKY.js +10 -0
  39. package/dist/chunk-ASMHEEKY.js.map +1 -0
  40. package/dist/chunk-DBF5OKH3.js +111 -0
  41. package/dist/chunk-DBF5OKH3.js.map +1 -0
  42. package/dist/chunk-DW4TX7MU.js +54 -0
  43. package/dist/chunk-DW4TX7MU.js.map +1 -0
  44. package/dist/chunk-IPE7TF2P.js +54 -0
  45. package/dist/chunk-IPE7TF2P.js.map +1 -0
  46. package/dist/chunk-IQTJHXZJ.js +126 -0
  47. package/dist/chunk-IQTJHXZJ.js.map +1 -0
  48. package/dist/chunk-J47N77VG.js +2969 -0
  49. package/dist/chunk-J47N77VG.js.map +1 -0
  50. package/dist/chunk-JZXD6FWR.js +25 -0
  51. package/dist/chunk-JZXD6FWR.js.map +1 -0
  52. package/dist/{chunk-QVWOPIZJ.js → chunk-KDSNXS6N.js} +75 -149
  53. package/dist/chunk-KDSNXS6N.js.map +1 -0
  54. package/dist/chunk-KZ2IXVQT.js +219 -0
  55. package/dist/chunk-KZ2IXVQT.js.map +1 -0
  56. package/dist/chunk-LBPELU7L.js +3649 -0
  57. package/dist/chunk-LBPELU7L.js.map +1 -0
  58. package/dist/chunk-MX3NFLCE.js +940 -0
  59. package/dist/chunk-MX3NFLCE.js.map +1 -0
  60. package/dist/chunk-N44UXKIB.js +26 -0
  61. package/dist/chunk-N44UXKIB.js.map +1 -0
  62. package/dist/chunk-OW5HMYMI.js +19 -0
  63. package/dist/chunk-OW5HMYMI.js.map +1 -0
  64. package/dist/chunk-OYNLAZQU.js +838 -0
  65. package/dist/chunk-OYNLAZQU.js.map +1 -0
  66. package/dist/chunk-PDPJN2OP.js +17 -0
  67. package/dist/chunk-PDPJN2OP.js.map +1 -0
  68. package/dist/chunk-RCZZGGJS.js +226 -0
  69. package/dist/chunk-RCZZGGJS.js.map +1 -0
  70. package/dist/chunk-RJNI3BHT.js +1 -0
  71. package/dist/chunk-RPXWUCQQ.js +112 -0
  72. package/dist/chunk-RPXWUCQQ.js.map +1 -0
  73. package/dist/chunk-S5KZQJJI.js +107 -0
  74. package/dist/chunk-S5KZQJJI.js.map +1 -0
  75. package/dist/{chunk-T3VVHJTK.js → chunk-S6S2UP6U.js} +1074 -1459
  76. package/dist/chunk-S6S2UP6U.js.map +1 -0
  77. package/dist/{chunk-I5ZCOZZV.js → chunk-SXAGSEKZ.js} +1202 -9561
  78. package/dist/chunk-SXAGSEKZ.js.map +1 -0
  79. package/dist/chunk-T4GMCW6Z.js +46 -0
  80. package/dist/chunk-T4GMCW6Z.js.map +1 -0
  81. package/dist/chunk-WHQZBUNY.js +982 -0
  82. package/dist/chunk-WHQZBUNY.js.map +1 -0
  83. package/dist/chunk-WOXBLP7V.js +610 -0
  84. package/dist/chunk-WOXBLP7V.js.map +1 -0
  85. package/dist/cli/commands/inspiredesign.d.ts.map +1 -1
  86. package/dist/cli/commands/macro-resolve.d.ts +4 -1
  87. package/dist/cli/commands/macro-resolve.d.ts.map +1 -1
  88. package/dist/cli/commands/product-video.d.ts.map +1 -1
  89. package/dist/cli/commands/research.d.ts.map +1 -1
  90. package/dist/cli/commands/serve.d.ts.map +1 -1
  91. package/dist/cli/commands/shopping.d.ts.map +1 -1
  92. package/dist/cli/commands/workflow-output.d.ts +2 -0
  93. package/dist/cli/commands/workflow-output.d.ts.map +1 -0
  94. package/dist/cli/daemon-commands.d.ts.map +1 -1
  95. package/dist/cli/daemon.d.ts.map +1 -1
  96. package/dist/cli/index.js +204 -8123
  97. package/dist/cli/index.js.map +1 -1
  98. package/dist/cli/installers/postinstall-skill-sync.js +2 -1
  99. package/dist/cli/installers/postinstall-skill-sync.js.map +1 -1
  100. package/dist/cli/remote-relay.d.ts.map +1 -1
  101. package/dist/click-TENZA3Y6.js +81 -0
  102. package/dist/click-TENZA3Y6.js.map +1 -0
  103. package/dist/clone-component-STH5AR6M.js +82 -0
  104. package/dist/clone-component-STH5AR6M.js.map +1 -0
  105. package/dist/clone-page-BSTWAPAJ.js +69 -0
  106. package/dist/clone-page-BSTWAPAJ.js.map +1 -0
  107. package/dist/close-CEMMAAM7.js +63 -0
  108. package/dist/close-CEMMAAM7.js.map +1 -0
  109. package/dist/close-QCWUNRAI.js +63 -0
  110. package/dist/close-QCWUNRAI.js.map +1 -0
  111. package/dist/connect-J3RVSEZF.js +107 -0
  112. package/dist/connect-J3RVSEZF.js.map +1 -0
  113. package/dist/console-poll-HL7BVIVX.js +76 -0
  114. package/dist/console-poll-HL7BVIVX.js.map +1 -0
  115. package/dist/cookie-import-WMUCIIHN.js +177 -0
  116. package/dist/cookie-import-WMUCIIHN.js.map +1 -0
  117. package/dist/cookie-list-PB2N4RPH.js +117 -0
  118. package/dist/cookie-list-PB2N4RPH.js.map +1 -0
  119. package/dist/daemon-5KSVMGN4.js +194 -0
  120. package/dist/daemon-5KSVMGN4.js.map +1 -0
  121. package/dist/daemon-fingerprint.json +1 -1
  122. package/dist/debug-trace-snapshot-RK7KDXA5.js +136 -0
  123. package/dist/debug-trace-snapshot-RK7KDXA5.js.map +1 -0
  124. package/dist/dialog-P6P4U7XE.js +75 -0
  125. package/dist/dialog-P6P4U7XE.js.map +1 -0
  126. package/dist/disconnect-32F7IDIP.js +58 -0
  127. package/dist/disconnect-32F7IDIP.js.map +1 -0
  128. package/dist/enabled-A6C6ZM2O.js +71 -0
  129. package/dist/enabled-A6C6ZM2O.js.map +1 -0
  130. package/dist/extension-extractor-GKWSFHPN.js +11 -0
  131. package/dist/extension-extractor-GKWSFHPN.js.map +1 -0
  132. package/dist/global-D6WLWBXA.js +56 -0
  133. package/dist/global-D6WLWBXA.js.map +1 -0
  134. package/dist/goto-ULTSABDM.js +98 -0
  135. package/dist/goto-ULTSABDM.js.map +1 -0
  136. package/dist/help-EKKKEDL5.js +491 -0
  137. package/dist/help-EKKKEDL5.js.map +1 -0
  138. package/dist/hover-UF2ZUMTQ.js +71 -0
  139. package/dist/hover-UF2ZUMTQ.js.map +1 -0
  140. package/dist/html-B6TX7GK7.js +84 -0
  141. package/dist/html-B6TX7GK7.js.map +1 -0
  142. package/dist/index.js +68 -34
  143. package/dist/index.js.map +1 -1
  144. package/dist/inspector-6S5FKUZQ.js +62 -0
  145. package/dist/inspector-6S5FKUZQ.js.map +1 -0
  146. package/dist/inspector-audit-ARGEGOS7.js +84 -0
  147. package/dist/inspector-audit-ARGEGOS7.js.map +1 -0
  148. package/dist/inspector-plan-CSG5HZOC.js +69 -0
  149. package/dist/inspector-plan-CSG5HZOC.js.map +1 -0
  150. package/dist/inspiredesign-7VRMMZN4.js +234 -0
  151. package/dist/inspiredesign-7VRMMZN4.js.map +1 -0
  152. package/dist/install-autostart-output-5DOMKCQL.js +41 -0
  153. package/dist/install-autostart-output-5DOMKCQL.js.map +1 -0
  154. package/dist/install-autostart-reconciliation-NHKOFYTD.js +73 -0
  155. package/dist/install-autostart-reconciliation-NHKOFYTD.js.map +1 -0
  156. package/dist/launch-REYCIR3Z.js +225 -0
  157. package/dist/launch-REYCIR3Z.js.map +1 -0
  158. package/dist/list-NPRXRQY2.js +51 -0
  159. package/dist/list-NPRXRQY2.js.map +1 -0
  160. package/dist/list-STYD2ZWA.js +54 -0
  161. package/dist/list-STYD2ZWA.js.map +1 -0
  162. package/dist/local-HXJLUUNT.js +54 -0
  163. package/dist/local-HXJLUUNT.js.map +1 -0
  164. package/dist/macro-resolve-ZIJZ65QI.js +253 -0
  165. package/dist/macro-resolve-ZIJZ65QI.js.map +1 -0
  166. package/dist/macros/execute-runtime.d.ts +3 -1
  167. package/dist/macros/execute-runtime.d.ts.map +1 -1
  168. package/dist/macros/execute.d.ts +2 -0
  169. package/dist/macros/execute.d.ts.map +1 -1
  170. package/dist/native-UPLVQ2SG.js +22 -0
  171. package/dist/native-UPLVQ2SG.js.map +1 -0
  172. package/dist/network-poll-HLDOSC72.js +76 -0
  173. package/dist/network-poll-HLDOSC72.js.map +1 -0
  174. package/dist/new-HXLLN6UT.js +69 -0
  175. package/dist/new-HXLLN6UT.js.map +1 -0
  176. package/dist/onboarding-metadata-7E3KLYSZ.js +27 -0
  177. package/dist/onboarding-metadata-7E3KLYSZ.js.map +1 -0
  178. package/dist/open-KDR25LQZ.js +81 -0
  179. package/dist/open-KDR25LQZ.js.map +1 -0
  180. package/dist/opendevbrowser.js +68 -34
  181. package/dist/opendevbrowser.js.map +1 -1
  182. package/dist/perf-EM6SWFJ6.js +58 -0
  183. package/dist/perf-EM6SWFJ6.js.map +1 -0
  184. package/dist/pointer-down-ZYWRZNCH.js +55 -0
  185. package/dist/pointer-down-ZYWRZNCH.js.map +1 -0
  186. package/dist/pointer-drag-LVEAVJO4.js +54 -0
  187. package/dist/pointer-drag-LVEAVJO4.js.map +1 -0
  188. package/dist/pointer-move-7SRKUS66.js +52 -0
  189. package/dist/pointer-move-7SRKUS66.js.map +1 -0
  190. package/dist/pointer-up-KLDBSK37.js +55 -0
  191. package/dist/pointer-up-KLDBSK37.js.map +1 -0
  192. package/dist/press-UIIXFTD7.js +83 -0
  193. package/dist/press-UIIXFTD7.js.map +1 -0
  194. package/dist/product-video-PYOXJVAI.js +235 -0
  195. package/dist/product-video-PYOXJVAI.js.map +1 -0
  196. package/dist/providers/artifacts.d.ts +0 -2
  197. package/dist/providers/artifacts.d.ts.map +1 -1
  198. package/dist/providers/blocker.d.ts.map +1 -1
  199. package/dist/providers/bounded-map.d.ts +2 -0
  200. package/dist/providers/bounded-map.d.ts.map +1 -0
  201. package/dist/providers/community/index.d.ts.map +1 -1
  202. package/dist/providers/constraint.d.ts.map +1 -1
  203. package/dist/providers/index.d.ts +1 -0
  204. package/dist/providers/index.d.ts.map +1 -1
  205. package/dist/providers/renderer.d.ts.map +1 -1
  206. package/dist/providers/research-compiler.d.ts +1 -1
  207. package/dist/providers/research-compiler.d.ts.map +1 -1
  208. package/dist/providers/research-executor.d.ts.map +1 -1
  209. package/dist/providers/runtime-factory.d.ts.map +1 -1
  210. package/dist/providers/shared/traversal-url.d.ts +3 -0
  211. package/dist/providers/shared/traversal-url.d.ts.map +1 -1
  212. package/dist/providers/shopping/index.d.ts.map +1 -1
  213. package/dist/providers/social/search-quality.d.ts.map +1 -1
  214. package/dist/providers/workflow-handoff.d.ts +4 -0
  215. package/dist/providers/workflow-handoff.d.ts.map +1 -1
  216. package/dist/providers/workflows.d.ts.map +1 -1
  217. package/dist/{providers-QF2RFB4J.js → providers-4YY2BLXG.js} +19 -14
  218. package/dist/providers-4YY2BLXG.js.map +1 -0
  219. package/dist/public-surface/generated-manifest.d.ts +2 -2
  220. package/dist/public-surface/generated-manifest.d.ts.map +1 -1
  221. package/dist/public-surface/source.d.ts +2 -2
  222. package/dist/public-surface/source.d.ts.map +1 -1
  223. package/dist/relay/protocol.d.ts +3 -1
  224. package/dist/relay/protocol.d.ts.map +1 -1
  225. package/dist/relay/relay-server.d.ts +6 -0
  226. package/dist/relay/relay-server.d.ts.map +1 -1
  227. package/dist/research-CKXMJ2DK.js +295 -0
  228. package/dist/research-CKXMJ2DK.js.map +1 -0
  229. package/dist/review-7HWJPZOD.js +48 -0
  230. package/dist/review-7HWJPZOD.js.map +1 -0
  231. package/dist/review-desktop-2IBJHFB5.js +54 -0
  232. package/dist/review-desktop-2IBJHFB5.js.map +1 -0
  233. package/dist/rpc-3HGIEJUO.js +159 -0
  234. package/dist/rpc-3HGIEJUO.js.map +1 -0
  235. package/dist/run-ADRYI3MS.js +180 -0
  236. package/dist/run-ADRYI3MS.js.map +1 -0
  237. package/dist/screencast-start-DTLUHD5H.js +67 -0
  238. package/dist/screencast-start-DTLUHD5H.js.map +1 -0
  239. package/dist/screencast-stop-54C5LRSS.js +59 -0
  240. package/dist/screencast-stop-54C5LRSS.js.map +1 -0
  241. package/dist/screenshot-HOAKR7P7.js +68 -0
  242. package/dist/screenshot-HOAKR7P7.js.map +1 -0
  243. package/dist/scroll-IAOO5COY.js +84 -0
  244. package/dist/scroll-IAOO5COY.js.map +1 -0
  245. package/dist/scroll-into-view-RKWSLAPH.js +71 -0
  246. package/dist/scroll-into-view-RKWSLAPH.js.map +1 -0
  247. package/dist/select-IGD3T6X4.js +86 -0
  248. package/dist/select-IGD3T6X4.js.map +1 -0
  249. package/dist/serve-7X4INUCU.js +498 -0
  250. package/dist/serve-7X4INUCU.js.map +1 -0
  251. package/dist/shopping-FC6DRW76.js +273 -0
  252. package/dist/shopping-FC6DRW76.js.map +1 -0
  253. package/dist/skill-lifecycle-5UAZGKSN.js +89 -0
  254. package/dist/skill-lifecycle-5UAZGKSN.js.map +1 -0
  255. package/dist/skills-NSXDX6YM.js +26 -0
  256. package/dist/skills-NSXDX6YM.js.map +1 -0
  257. package/dist/snapshot-X22GG324.js +113 -0
  258. package/dist/snapshot-X22GG324.js.map +1 -0
  259. package/dist/status-SP55LMNW.js +132 -0
  260. package/dist/status-SP55LMNW.js.map +1 -0
  261. package/dist/status-VH2WXIDG.js +35 -0
  262. package/dist/status-VH2WXIDG.js.map +1 -0
  263. package/dist/status-capabilities-YBERLRRA.js +57 -0
  264. package/dist/status-capabilities-YBERLRRA.js.map +1 -0
  265. package/dist/text-6TB5WNLI.js +84 -0
  266. package/dist/text-6TB5WNLI.js.map +1 -0
  267. package/dist/tools/macro_resolve.d.ts.map +1 -1
  268. package/dist/type-3UI3TQH3.js +94 -0
  269. package/dist/type-3UI3TQH3.js.map +1 -0
  270. package/dist/uncheck-5L3D2D4U.js +71 -0
  271. package/dist/uncheck-5L3D2D4U.js.map +1 -0
  272. package/dist/uninstall-KYKGJAX7.js +91 -0
  273. package/dist/uninstall-KYKGJAX7.js.map +1 -0
  274. package/dist/update-SMXPYGXS.js +305 -0
  275. package/dist/update-SMXPYGXS.js.map +1 -0
  276. package/dist/update-skill-modes-BVX7IVMW.js +38 -0
  277. package/dist/update-skill-modes-BVX7IVMW.js.map +1 -0
  278. package/dist/upload-YG4J2EMI.js +56 -0
  279. package/dist/upload-YG4J2EMI.js.map +1 -0
  280. package/dist/use-V3LGFP3K.js +63 -0
  281. package/dist/use-V3LGFP3K.js.map +1 -0
  282. package/dist/value-3247D57X.js +71 -0
  283. package/dist/value-3247D57X.js.map +1 -0
  284. package/dist/visible-A7HEV36U.js +71 -0
  285. package/dist/visible-A7HEV36U.js.map +1 -0
  286. package/dist/wait-UZPP4Y4R.js +109 -0
  287. package/dist/wait-UZPP4Y4R.js.map +1 -0
  288. package/dist/windows-76TR3AIP.js +37 -0
  289. package/dist/windows-76TR3AIP.js.map +1 -0
  290. package/extension/dist/background.js +99 -22
  291. package/extension/dist/ops/ops-runtime.js +85 -7
  292. package/extension/dist/ops/ops-session-store.js +3 -0
  293. package/extension/dist/ops/target-session-coordinator.js +3 -0
  294. package/extension/dist/services/CDPRouter.js +9 -0
  295. package/extension/manifest.json +1 -1
  296. package/package.json +1 -1
  297. package/skills/opendevbrowser-best-practices/SKILL.md +8 -6
  298. package/skills/opendevbrowser-best-practices/artifacts/skill-runtime-surface-matrix.md +1 -1
  299. package/skills/opendevbrowser-best-practices/scripts/odb-workflow.sh +3 -2
  300. package/skills/opendevbrowser-best-practices/scripts/validator-fixture-cli.sh +39 -2
  301. package/skills/opendevbrowser-research/SKILL.md +64 -12
  302. package/skills/opendevbrowser-research/artifacts/research-workflows.md +56 -19
  303. package/skills/opendevbrowser-research/assets/templates/compact.md +31 -5
  304. package/skills/opendevbrowser-research/assets/templates/context.json +52 -1
  305. package/skills/opendevbrowser-research/assets/templates/report.md +57 -4
  306. package/skills/opendevbrowser-research/examples/sample-input.json +1 -1
  307. package/skills/opendevbrowser-research/examples/sample-output.md +27 -2
  308. package/skills/opendevbrowser-research/scripts/run-research.sh +2 -6
  309. package/skills/opendevbrowser-research/scripts/validate-skill-assets.sh +115 -1
  310. package/dist/chunk-I5ZCOZZV.js.map +0 -1
  311. package/dist/chunk-QVWOPIZJ.js.map +0 -1
  312. package/dist/chunk-T3VVHJTK.js.map +0 -1
  313. /package/dist/{providers-QF2RFB4J.js.map → chunk-RJNI3BHT.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/dom/visible.ts"],"sourcesContent":["import type { ParsedArgs } from \"../../args\";\nimport { callDaemon } from \"../../client\";\nimport { createUsageError } from \"../../errors\";\nimport { parseOptionalStringFlag } from \"../../utils/parse\";\n\nfunction parseDomVisibleArgs(rawArgs: string[]): { sessionId?: string; ref?: string } {\n const parsed: { sessionId?: string; ref?: string } = {};\n for (let i = 0; i < rawArgs.length; i += 1) {\n const arg = rawArgs[i];\n if (arg === \"--session-id\") {\n const value = rawArgs[i + 1];\n if (!value) throw createUsageError(\"Missing value for --session-id\");\n parsed.sessionId = value;\n i += 1;\n continue;\n }\n if (arg?.startsWith(\"--session-id=\")) {\n parsed.sessionId = arg.split(\"=\", 2)[1];\n continue;\n }\n if (arg === \"--ref\") {\n const value = rawArgs[i + 1];\n if (!value) throw createUsageError(\"Missing value for --ref\");\n parsed.ref = value;\n i += 1;\n continue;\n }\n if (arg?.startsWith(\"--ref=\")) {\n parsed.ref = arg.split(\"=\", 2)[1];\n continue;\n }\n }\n return parsed;\n}\n\nexport async function runDomVisible(args: ParsedArgs) {\n const { sessionId, ref } = parseDomVisibleArgs(args.rawArgs);\n const targetId = parseOptionalStringFlag(args.rawArgs, \"--target-id\");\n if (!sessionId) throw createUsageError(\"Missing --session-id\");\n if (!ref) throw createUsageError(\"Missing --ref\");\n const result = await callDaemon(\"dom.isVisible\", {\n sessionId,\n ref,\n ...(typeof targetId === \"string\" ? { targetId } : {})\n });\n return { success: true, message: \"Visibility checked.\", data: result };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAKA,SAAS,oBAAoB,SAAyD;AACpF,QAAM,SAA+C,CAAC;AACtD,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK,GAAG;AAC1C,UAAM,MAAM,QAAQ,CAAC;AACrB,QAAI,QAAQ,gBAAgB;AAC1B,YAAM,QAAQ,QAAQ,IAAI,CAAC;AAC3B,UAAI,CAAC,MAAO,OAAM,iBAAiB,gCAAgC;AACnE,aAAO,YAAY;AACnB,WAAK;AACL;AAAA,IACF;AACA,QAAI,KAAK,WAAW,eAAe,GAAG;AACpC,aAAO,YAAY,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;AACtC;AAAA,IACF;AACA,QAAI,QAAQ,SAAS;AACnB,YAAM,QAAQ,QAAQ,IAAI,CAAC;AAC3B,UAAI,CAAC,MAAO,OAAM,iBAAiB,yBAAyB;AAC5D,aAAO,MAAM;AACb,WAAK;AACL;AAAA,IACF;AACA,QAAI,KAAK,WAAW,QAAQ,GAAG;AAC7B,aAAO,MAAM,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;AAChC;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,cAAc,MAAkB;AACpD,QAAM,EAAE,WAAW,IAAI,IAAI,oBAAoB,KAAK,OAAO;AAC3D,QAAM,WAAW,wBAAwB,KAAK,SAAS,aAAa;AACpE,MAAI,CAAC,UAAW,OAAM,iBAAiB,sBAAsB;AAC7D,MAAI,CAAC,IAAK,OAAM,iBAAiB,eAAe;AAChD,QAAM,SAAS,MAAM,WAAW,iBAAiB;AAAA,IAC/C;AAAA,IACA;AAAA,IACA,GAAI,OAAO,aAAa,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,EACrD,CAAC;AACD,SAAO,EAAE,SAAS,MAAM,SAAS,uBAAuB,MAAM,OAAO;AACvE;","names":[]}
@@ -0,0 +1,109 @@
1
+ import "./chunk-RJNI3BHT.js";
2
+ import {
3
+ parseNumberFlag,
4
+ parseOptionalStringFlag
5
+ } from "./chunk-RPXWUCQQ.js";
6
+ import {
7
+ callDaemon
8
+ } from "./chunk-OYNLAZQU.js";
9
+ import "./chunk-LBPELU7L.js";
10
+ import "./chunk-SXAGSEKZ.js";
11
+ import "./chunk-MX3NFLCE.js";
12
+ import "./chunk-ASMHEEKY.js";
13
+ import {
14
+ createUsageError
15
+ } from "./chunk-IPE7TF2P.js";
16
+ import "./chunk-STGGGVYT.js";
17
+ import "./chunk-3ILXPKSJ.js";
18
+ import "./chunk-TBUCZX4A.js";
19
+ import "./chunk-Y2KL55OG.js";
20
+ import "./chunk-5SWZDVOW.js";
21
+ import "./chunk-S6S2UP6U.js";
22
+ import "./chunk-S5KZQJJI.js";
23
+ import "./chunk-KZ2IXVQT.js";
24
+ import "./chunk-WHQZBUNY.js";
25
+ import "./chunk-FUSXMW3G.js";
26
+
27
+ // src/cli/commands/nav/wait.ts
28
+ function parseWaitArgs(rawArgs) {
29
+ const parsed = {};
30
+ for (let i = 0; i < rawArgs.length; i += 1) {
31
+ const arg = rawArgs[i];
32
+ if (arg === "--session-id") {
33
+ const value = rawArgs[i + 1];
34
+ if (!value) throw createUsageError("Missing value for --session-id");
35
+ parsed.sessionId = value;
36
+ i += 1;
37
+ continue;
38
+ }
39
+ if (arg?.startsWith("--session-id=")) {
40
+ parsed.sessionId = arg.split("=", 2)[1];
41
+ continue;
42
+ }
43
+ if (arg === "--ref") {
44
+ const value = rawArgs[i + 1];
45
+ if (!value) throw createUsageError("Missing value for --ref");
46
+ parsed.ref = value;
47
+ i += 1;
48
+ continue;
49
+ }
50
+ if (arg?.startsWith("--ref=")) {
51
+ parsed.ref = arg.split("=", 2)[1];
52
+ continue;
53
+ }
54
+ if (arg === "--state") {
55
+ const value = rawArgs[i + 1];
56
+ if (!value) throw createUsageError("Missing value for --state");
57
+ parsed.state = value;
58
+ i += 1;
59
+ continue;
60
+ }
61
+ if (arg?.startsWith("--state=")) {
62
+ parsed.state = arg.split("=", 2)[1];
63
+ continue;
64
+ }
65
+ if (arg === "--until") {
66
+ const value = rawArgs[i + 1];
67
+ if (!value) throw createUsageError("Missing value for --until");
68
+ parsed.until = value;
69
+ i += 1;
70
+ continue;
71
+ }
72
+ if (arg?.startsWith("--until=")) {
73
+ parsed.until = arg.split("=", 2)[1];
74
+ continue;
75
+ }
76
+ if (arg === "--timeout-ms") {
77
+ const value = rawArgs[i + 1];
78
+ if (!value) throw createUsageError("Missing value for --timeout-ms");
79
+ parsed.timeoutMs = parseNumberFlag(value, "--timeout-ms", { min: 1 });
80
+ i += 1;
81
+ continue;
82
+ }
83
+ if (arg?.startsWith("--timeout-ms=")) {
84
+ const value = arg.split("=", 2)[1];
85
+ if (!value) throw createUsageError("Missing value for --timeout-ms");
86
+ parsed.timeoutMs = parseNumberFlag(value, "--timeout-ms", { min: 1 });
87
+ continue;
88
+ }
89
+ }
90
+ return parsed;
91
+ }
92
+ async function runWait(args) {
93
+ const { sessionId, ref, state, until, timeoutMs } = parseWaitArgs(args.rawArgs);
94
+ const targetId = parseOptionalStringFlag(args.rawArgs, "--target-id");
95
+ if (!sessionId) throw createUsageError("Missing --session-id");
96
+ const result = await callDaemon("nav.wait", {
97
+ sessionId,
98
+ ref,
99
+ state,
100
+ until,
101
+ timeoutMs,
102
+ ...typeof targetId === "string" ? { targetId } : {}
103
+ });
104
+ return { success: true, message: "Wait complete.", data: result };
105
+ }
106
+ export {
107
+ runWait
108
+ };
109
+ //# sourceMappingURL=wait-UZPP4Y4R.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/nav/wait.ts"],"sourcesContent":["import type { ParsedArgs } from \"../../args\";\nimport { callDaemon } from \"../../client\";\nimport { createUsageError } from \"../../errors\";\nimport { parseNumberFlag, parseOptionalStringFlag } from \"../../utils/parse\";\n\nfunction parseWaitArgs(rawArgs: string[]): { sessionId?: string; ref?: string; state?: string; until?: string; timeoutMs?: number } {\n const parsed: { sessionId?: string; ref?: string; state?: string; until?: string; timeoutMs?: number } = {};\n for (let i = 0; i < rawArgs.length; i += 1) {\n const arg = rawArgs[i];\n if (arg === \"--session-id\") {\n const value = rawArgs[i + 1];\n if (!value) throw createUsageError(\"Missing value for --session-id\");\n parsed.sessionId = value;\n i += 1;\n continue;\n }\n if (arg?.startsWith(\"--session-id=\")) {\n parsed.sessionId = arg.split(\"=\", 2)[1];\n continue;\n }\n if (arg === \"--ref\") {\n const value = rawArgs[i + 1];\n if (!value) throw createUsageError(\"Missing value for --ref\");\n parsed.ref = value;\n i += 1;\n continue;\n }\n if (arg?.startsWith(\"--ref=\")) {\n parsed.ref = arg.split(\"=\", 2)[1];\n continue;\n }\n if (arg === \"--state\") {\n const value = rawArgs[i + 1];\n if (!value) throw createUsageError(\"Missing value for --state\");\n parsed.state = value;\n i += 1;\n continue;\n }\n if (arg?.startsWith(\"--state=\")) {\n parsed.state = arg.split(\"=\", 2)[1];\n continue;\n }\n if (arg === \"--until\") {\n const value = rawArgs[i + 1];\n if (!value) throw createUsageError(\"Missing value for --until\");\n parsed.until = value;\n i += 1;\n continue;\n }\n if (arg?.startsWith(\"--until=\")) {\n parsed.until = arg.split(\"=\", 2)[1];\n continue;\n }\n if (arg === \"--timeout-ms\") {\n const value = rawArgs[i + 1];\n if (!value) throw createUsageError(\"Missing value for --timeout-ms\");\n parsed.timeoutMs = parseNumberFlag(value, \"--timeout-ms\", { min: 1 });\n i += 1;\n continue;\n }\n if (arg?.startsWith(\"--timeout-ms=\")) {\n const value = arg.split(\"=\", 2)[1];\n if (!value) throw createUsageError(\"Missing value for --timeout-ms\");\n parsed.timeoutMs = parseNumberFlag(value, \"--timeout-ms\", { min: 1 });\n continue;\n }\n }\n return parsed;\n}\n\nexport async function runWait(args: ParsedArgs) {\n const { sessionId, ref, state, until, timeoutMs } = parseWaitArgs(args.rawArgs);\n const targetId = parseOptionalStringFlag(args.rawArgs, \"--target-id\");\n if (!sessionId) throw createUsageError(\"Missing --session-id\");\n const result = await callDaemon(\"nav.wait\", {\n sessionId,\n ref,\n state,\n until,\n timeoutMs,\n ...(typeof targetId === \"string\" ? { targetId } : {})\n });\n return { success: true, message: \"Wait complete.\", data: result };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAKA,SAAS,cAAc,SAA6G;AAClI,QAAM,SAAmG,CAAC;AAC1G,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK,GAAG;AAC1C,UAAM,MAAM,QAAQ,CAAC;AACrB,QAAI,QAAQ,gBAAgB;AAC1B,YAAM,QAAQ,QAAQ,IAAI,CAAC;AAC3B,UAAI,CAAC,MAAO,OAAM,iBAAiB,gCAAgC;AACnE,aAAO,YAAY;AACnB,WAAK;AACL;AAAA,IACF;AACA,QAAI,KAAK,WAAW,eAAe,GAAG;AACpC,aAAO,YAAY,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;AACtC;AAAA,IACF;AACA,QAAI,QAAQ,SAAS;AACnB,YAAM,QAAQ,QAAQ,IAAI,CAAC;AAC3B,UAAI,CAAC,MAAO,OAAM,iBAAiB,yBAAyB;AAC5D,aAAO,MAAM;AACb,WAAK;AACL;AAAA,IACF;AACA,QAAI,KAAK,WAAW,QAAQ,GAAG;AAC7B,aAAO,MAAM,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;AAChC;AAAA,IACF;AACA,QAAI,QAAQ,WAAW;AACrB,YAAM,QAAQ,QAAQ,IAAI,CAAC;AAC3B,UAAI,CAAC,MAAO,OAAM,iBAAiB,2BAA2B;AAC9D,aAAO,QAAQ;AACf,WAAK;AACL;AAAA,IACF;AACA,QAAI,KAAK,WAAW,UAAU,GAAG;AAC/B,aAAO,QAAQ,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;AAClC;AAAA,IACF;AACA,QAAI,QAAQ,WAAW;AACrB,YAAM,QAAQ,QAAQ,IAAI,CAAC;AAC3B,UAAI,CAAC,MAAO,OAAM,iBAAiB,2BAA2B;AAC9D,aAAO,QAAQ;AACf,WAAK;AACL;AAAA,IACF;AACA,QAAI,KAAK,WAAW,UAAU,GAAG;AAC/B,aAAO,QAAQ,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;AAClC;AAAA,IACF;AACA,QAAI,QAAQ,gBAAgB;AAC1B,YAAM,QAAQ,QAAQ,IAAI,CAAC;AAC3B,UAAI,CAAC,MAAO,OAAM,iBAAiB,gCAAgC;AACnE,aAAO,YAAY,gBAAgB,OAAO,gBAAgB,EAAE,KAAK,EAAE,CAAC;AACpE,WAAK;AACL;AAAA,IACF;AACA,QAAI,KAAK,WAAW,eAAe,GAAG;AACpC,YAAM,QAAQ,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;AACjC,UAAI,CAAC,MAAO,OAAM,iBAAiB,gCAAgC;AACnE,aAAO,YAAY,gBAAgB,OAAO,gBAAgB,EAAE,KAAK,EAAE,CAAC;AACpE;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,QAAQ,MAAkB;AAC9C,QAAM,EAAE,WAAW,KAAK,OAAO,OAAO,UAAU,IAAI,cAAc,KAAK,OAAO;AAC9E,QAAM,WAAW,wBAAwB,KAAK,SAAS,aAAa;AACpE,MAAI,CAAC,UAAW,OAAM,iBAAiB,sBAAsB;AAC7D,QAAM,SAAS,MAAM,WAAW,YAAY;AAAA,IAC1C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,OAAO,aAAa,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,EACrD,CAAC;AACD,SAAO,EAAE,SAAS,MAAM,SAAS,kBAAkB,MAAM,OAAO;AAClE;","names":[]}
@@ -0,0 +1,37 @@
1
+ import {
2
+ callDesktopCommand,
3
+ desktopCommandResult,
4
+ parseDesktopReasonArgs
5
+ } from "./chunk-2CG4SW3E.js";
6
+ import "./chunk-RJNI3BHT.js";
7
+ import "./chunk-RPXWUCQQ.js";
8
+ import "./chunk-OW5HMYMI.js";
9
+ import "./chunk-OYNLAZQU.js";
10
+ import "./chunk-LBPELU7L.js";
11
+ import "./chunk-SXAGSEKZ.js";
12
+ import "./chunk-MX3NFLCE.js";
13
+ import "./chunk-ASMHEEKY.js";
14
+ import "./chunk-IPE7TF2P.js";
15
+ import "./chunk-STGGGVYT.js";
16
+ import "./chunk-3ILXPKSJ.js";
17
+ import "./chunk-TBUCZX4A.js";
18
+ import "./chunk-Y2KL55OG.js";
19
+ import "./chunk-5SWZDVOW.js";
20
+ import "./chunk-S6S2UP6U.js";
21
+ import "./chunk-S5KZQJJI.js";
22
+ import "./chunk-KZ2IXVQT.js";
23
+ import "./chunk-WHQZBUNY.js";
24
+ import "./chunk-FUSXMW3G.js";
25
+
26
+ // src/cli/commands/desktop/windows.ts
27
+ async function runDesktopWindows(args) {
28
+ const { reason, timeoutMs } = parseDesktopReasonArgs(args.rawArgs);
29
+ const result = await callDesktopCommand("desktop.windows.list", {
30
+ ...typeof reason === "string" ? { reason } : {}
31
+ }, timeoutMs);
32
+ return desktopCommandResult("Desktop windows listed.", result);
33
+ }
34
+ export {
35
+ runDesktopWindows
36
+ };
37
+ //# sourceMappingURL=windows-76TR3AIP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/desktop/windows.ts"],"sourcesContent":["import type { ParsedArgs } from \"../../args\";\nimport { callDesktopCommand, desktopCommandResult, parseDesktopReasonArgs } from \"./shared\";\n\nexport async function runDesktopWindows(args: ParsedArgs) {\n const { reason, timeoutMs } = parseDesktopReasonArgs(args.rawArgs);\n const result = await callDesktopCommand(\"desktop.windows.list\", {\n ...(typeof reason === \"string\" ? { reason } : {})\n }, timeoutMs);\n return desktopCommandResult(\"Desktop windows listed.\", result);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAGA,eAAsB,kBAAkB,MAAkB;AACxD,QAAM,EAAE,QAAQ,UAAU,IAAI,uBAAuB,KAAK,OAAO;AACjE,QAAM,SAAS,MAAM,mBAAmB,wBAAwB;AAAA,IAC9D,GAAI,OAAO,WAAW,WAAW,EAAE,OAAO,IAAI,CAAC;AAAA,EACjD,GAAG,SAAS;AACZ,SAAO,qBAAqB,2BAA2B,MAAM;AAC/D;","names":[]}
@@ -36,8 +36,11 @@ let retryScheduled = false;
36
36
  let retryDelayMs = 5000;
37
37
  let autoConnectEnabled = DEFAULT_AUTO_CONNECT;
38
38
  let nativeEnabled = DEFAULT_NATIVE_ENABLED;
39
+ let lastRelayCleanupStatus = null;
39
40
  const RETRY_ALARM_NAME = "opendevbrowser-auto-connect";
41
+ const WATCHDOG_ALARM_NAME = "opendevbrowser-auto-connect-watchdog";
40
42
  const RETRY_MAX_MS = 60_000;
43
+ const WATCHDOG_PERIOD_MINUTES = 1;
41
44
  const ANNOTATION_CONTENT_SCRIPT = "dist/annotate-content.js";
42
45
  const ANNOTATION_CONTENT_STYLE = "dist/annotate-content.css";
43
46
  const ANNOTATION_MAX_PAYLOAD_BYTES = 10 * 1024 * 1024;
@@ -92,21 +95,27 @@ const getEffectiveStatus = () => {
92
95
  return "disconnected";
93
96
  };
94
97
  const hasReadyRelayHandshake = (health) => {
95
- return health?.extensionConnected === true && health.extensionHandshakeComplete === true;
98
+ return health?.ok !== false && health?.extensionConnected === true && health.extensionHandshakeComplete === true;
96
99
  };
97
100
  const deriveBackgroundStatus = (relayStatus, nativeHealth, relayHealth, reconnectSuppressed) => {
98
101
  if (relayStatus === "connected") {
99
- return "connected";
100
- }
101
- if (nativeHealth?.status === "connected") {
102
+ if (relayHealth?.ok === false) {
103
+ return "disconnected";
104
+ }
102
105
  return "connected";
103
106
  }
104
107
  if (reconnectSuppressed) {
105
108
  return "disconnected";
106
109
  }
110
+ if (nativeHealth?.status === "connected") {
111
+ return "connected";
112
+ }
107
113
  return hasReadyRelayHandshake(relayHealth) ? "connected" : "disconnected";
108
114
  };
109
115
  const deriveBadgeTone = (status, relayHealth) => {
116
+ if (relayHealth?.ok === false) {
117
+ return status === "connected" ? "warning" : "disconnected";
118
+ }
110
119
  if (status === "connected") {
111
120
  return "connected";
112
121
  }
@@ -139,9 +148,9 @@ const buildStatusMessage = async () => {
139
148
  note = `Connected to 127.0.0.1:${identity.relayPort}`;
140
149
  }
141
150
  relayHealth = await connection.relayHealthCheck();
142
- }
143
- else if (nativeHealth?.status === "connected") {
144
- note = "Connected via native host.";
151
+ if (relayHealth?.ok === false) {
152
+ note = statusNoteOverride ?? buildRelayHealthNote(relayHealth);
153
+ }
145
154
  }
146
155
  else {
147
156
  const stored = await new Promise((resolve) => {
@@ -149,9 +158,16 @@ const buildStatusMessage = async () => {
149
158
  });
150
159
  const port = parsePort(stored.relayPort) ?? DEFAULT_RELAY_PORT;
151
160
  relayHealth = await fetchRelayHealth(port);
152
- if (hasReadyRelayHandshake(relayHealth)) {
161
+ if (reconnectSuppressed) {
162
+ note = statusNoteOverride
163
+ ?? "Another extension client took over the relay connection. This client will stay disconnected until you reconnect it explicitly.";
164
+ }
165
+ else if (hasReadyRelayHandshake(relayHealth)) {
153
166
  note = `Connected to 127.0.0.1:${port}`;
154
167
  }
168
+ else if (nativeHealth?.status === "connected") {
169
+ note = "Connected via native host.";
170
+ }
155
171
  else {
156
172
  note = statusNoteOverride ?? buildRelayHealthNote(relayHealth);
157
173
  }
@@ -230,6 +246,8 @@ const buildRelayHealthNote = (health) => {
230
246
  return "Canvas channel disconnected. Reopen the design canvas command and retry.";
231
247
  case "cdp_disconnected":
232
248
  return "No CDP clients connected. Start a session and retry.";
249
+ case "relay_dirty":
250
+ return "Relay has active CDP, annotation, canvas clients, or ops-owned targets. Disconnect the current run and retry.";
233
251
  case "relay_down":
234
252
  return "Relay down. Start the daemon and retry.";
235
253
  default:
@@ -262,6 +280,16 @@ const clearRetry = () => {
262
280
  chrome.alarms.clear(RETRY_ALARM_NAME);
263
281
  }
264
282
  };
283
+ const scheduleWatchdog = () => {
284
+ if (chrome.alarms?.create) {
285
+ chrome.alarms.create(WATCHDOG_ALARM_NAME, { periodInMinutes: WATCHDOG_PERIOD_MINUTES });
286
+ }
287
+ };
288
+ const clearWatchdog = () => {
289
+ if (chrome.alarms?.clear) {
290
+ chrome.alarms.clear(WATCHDOG_ALARM_NAME);
291
+ }
292
+ };
265
293
  const scheduleRetry = () => {
266
294
  if (retryScheduled) {
267
295
  return;
@@ -357,24 +385,37 @@ const fetchRelayHealth = async (port) => {
357
385
  }
358
386
  const data = await response.json();
359
387
  if (data.health && typeof data.health === "object") {
360
- return data.health;
388
+ const health = data.health;
389
+ return typeof health.opsOwnedTargetCount === "number" && Number.isFinite(health.opsOwnedTargetCount)
390
+ ? health
391
+ : null;
361
392
  }
362
393
  const extensionConnected = data.extensionConnected === true;
363
394
  const handshake = data.extensionHandshakeComplete === true;
364
395
  const cdpConnected = data.cdpConnected === true;
365
396
  const annotationConnected = data.annotationConnected === true;
366
397
  const opsConnected = data.opsConnected === true;
398
+ const opsOwnedTargetCount = typeof data.opsOwnedTargetCount === "number" && Number.isFinite(data.opsOwnedTargetCount)
399
+ ? data.opsOwnedTargetCount
400
+ : null;
401
+ if (opsOwnedTargetCount === null || opsOwnedTargetCount < 0) {
402
+ return null;
403
+ }
367
404
  const canvasConnected = data.canvasConnected === true;
368
405
  const pairingRequired = data.pairingRequired === true;
369
- const ok = extensionConnected && handshake;
406
+ const dirty = cdpConnected || annotationConnected || canvasConnected || opsOwnedTargetCount > 0;
407
+ const ok = extensionConnected && handshake && !dirty;
370
408
  return {
371
409
  ok,
372
- reason: ok ? "ok" : (extensionConnected ? "handshake_incomplete" : "extension_disconnected"),
410
+ reason: ok
411
+ ? "ok"
412
+ : (dirty ? "relay_dirty" : (extensionConnected ? "handshake_incomplete" : "extension_disconnected")),
373
413
  extensionConnected,
374
414
  extensionHandshakeComplete: handshake,
375
415
  cdpConnected,
376
416
  annotationConnected,
377
417
  opsConnected,
418
+ opsOwnedTargetCount,
378
419
  canvasConnected,
379
420
  pairingRequired
380
421
  };
@@ -384,6 +425,10 @@ const fetchRelayHealth = async (port) => {
384
425
  return null;
385
426
  }
386
427
  };
428
+ const relayHasActiveExtensionClient = async (storedRelayPort) => {
429
+ const storedHealth = await fetchRelayHealth(storedRelayPort);
430
+ return hasReadyRelayHandshake(storedHealth);
431
+ };
387
432
  const sendAnnotationResponse = (payload, transport = "relay") => {
388
433
  if (transport === "popup") {
389
434
  return;
@@ -1292,11 +1337,8 @@ const fetchTokenFromPlugin = async (port) => {
1292
1337
  return null;
1293
1338
  }
1294
1339
  };
1295
- const clearStoredRelayState = async () => {
1340
+ const clearStoredRelayCredentials = async () => {
1296
1341
  await setStorage({
1297
- relayPort: null,
1298
- relayInstanceId: null,
1299
- relayEpoch: null,
1300
1342
  pairingToken: null,
1301
1343
  tokenEpoch: null
1302
1344
  });
@@ -1319,10 +1361,17 @@ const attemptAutoConnect = async () => {
1319
1361
  });
1320
1362
  const autoConnect = typeof data.autoConnect === "boolean" ? data.autoConnect : DEFAULT_AUTO_CONNECT;
1321
1363
  autoConnectEnabled = autoConnect;
1322
- if (!autoConnect || connection.getStatus() === "connected" || connection.isReconnectSuppressed()) {
1364
+ if (!autoConnect || connection.getStatus() === "connected") {
1323
1365
  clearRetry();
1366
+ if (!autoConnect) {
1367
+ clearWatchdog();
1368
+ }
1369
+ else {
1370
+ scheduleWatchdog();
1371
+ }
1324
1372
  return;
1325
1373
  }
1374
+ scheduleWatchdog();
1326
1375
  const autoPair = typeof data.autoPair === "boolean" ? data.autoPair : DEFAULT_AUTO_PAIR;
1327
1376
  const pairingEnabled = typeof data.pairingEnabled === "boolean" ? data.pairingEnabled : DEFAULT_PAIRING_ENABLED;
1328
1377
  const storedRelayPort = parsePort(data.relayPort) ?? DEFAULT_RELAY_PORT;
@@ -1331,6 +1380,15 @@ const attemptAutoConnect = async () => {
1331
1380
  if (typeof data.nativeEnabled !== "boolean") {
1332
1381
  await setStorage({ nativeEnabled });
1333
1382
  }
1383
+ if (connection.isReconnectSuppressed()) {
1384
+ const activeExtensionClient = await relayHasActiveExtensionClient(storedRelayPort);
1385
+ if (activeExtensionClient) {
1386
+ setStatusNoteOverride("Another extension client is connected. Retrying when the relay is free.");
1387
+ scheduleRetry();
1388
+ return;
1389
+ }
1390
+ setStatusNoteOverride("Relay no longer has an active extension client. Reconnecting.");
1391
+ }
1334
1392
  if (autoPair && pairingEnabled) {
1335
1393
  let config = await fetchRelayConfig(DEFAULT_DISCOVERY_PORT);
1336
1394
  if (!config && storedRelayPort !== DEFAULT_DISCOVERY_PORT) {
@@ -1355,12 +1413,12 @@ const attemptAutoConnect = async () => {
1355
1413
  });
1356
1414
  }
1357
1415
  if (hasEpoch && storedRelayEpoch !== null && storedRelayEpoch !== configEpoch) {
1358
- await clearStoredRelayState();
1416
+ await clearStoredRelayCredentials();
1359
1417
  storedPairingToken = null;
1360
1418
  setStatusNoteOverride("Relay restarted. Refresh the connection.");
1361
1419
  }
1362
1420
  if (config.instanceId && storedRelayInstanceId && config.instanceId !== storedRelayInstanceId) {
1363
- await clearStoredRelayState();
1421
+ await clearStoredRelayCredentials();
1364
1422
  storedPairingToken = null;
1365
1423
  setStatusNoteOverride("Relay instance mismatch. Open the popup and click Connect.");
1366
1424
  }
@@ -1428,6 +1486,8 @@ const autoConnect = async () => {
1428
1486
  }
1429
1487
  };
1430
1488
  connection.onStatus((status) => {
1489
+ const shouldCleanupRelayDisconnect = status === "disconnected" && lastRelayCleanupStatus !== "disconnected";
1490
+ lastRelayCleanupStatus = status;
1431
1491
  const effectiveStatus = status === "connected" ? "connected" : (nativeEnabled && nativePort.isConnected()) ? "connected" : "disconnected";
1432
1492
  updateBadge(effectiveStatus);
1433
1493
  void refreshBadgeFromBackgroundStatus();
@@ -1436,11 +1496,21 @@ connection.onStatus((status) => {
1436
1496
  setStatusNoteOverride(null);
1437
1497
  clearRetry();
1438
1498
  }
1439
- if (status === "disconnected" && !nativePort.isConnected()) {
1440
- for (const session of annotationSessions.values()) {
1441
- clearTimeout(session.timeoutId);
1499
+ if (status === "disconnected") {
1500
+ if (shouldCleanupRelayDisconnect) {
1501
+ for (const session of annotationSessions.values()) {
1502
+ clearTimeout(session.timeoutId);
1503
+ }
1504
+ annotationSessions.clear();
1505
+ opsRuntime.handleRelayDisconnected();
1506
+ }
1507
+ if (nativePort.isConnected()) {
1508
+ return;
1509
+ }
1510
+ if (connection.isReconnectSuppressed() && autoConnectEnabled) {
1511
+ scheduleRetry();
1512
+ return;
1442
1513
  }
1443
- annotationSessions.clear();
1444
1514
  if (connection.isReconnectSuppressed()) {
1445
1515
  clearRetry();
1446
1516
  return;
@@ -1469,6 +1539,11 @@ if (chrome.alarms?.onAlarm) {
1469
1539
  logError("auto_connect.alarm", error, { code: "auto_connect_failed" });
1470
1540
  });
1471
1541
  }
1542
+ if (alarm.name === WATCHDOG_ALARM_NAME) {
1543
+ autoConnect().catch((error) => {
1544
+ logError("auto_connect.watchdog", error, { code: "auto_connect_failed" });
1545
+ });
1546
+ }
1472
1547
  });
1473
1548
  }
1474
1549
  if (chrome.commands?.onCommand) {
@@ -1531,12 +1606,14 @@ chrome.storage.onChanged.addListener((changes, area) => {
1531
1606
  typeof changes.autoConnect.newValue === "boolean" ? changes.autoConnect.newValue : DEFAULT_AUTO_CONNECT;
1532
1607
  }
1533
1608
  if (changes.autoConnect?.newValue === true) {
1609
+ scheduleWatchdog();
1534
1610
  autoConnect().catch((error) => {
1535
1611
  logError("auto_connect.setting", error, { code: "auto_connect_failed" });
1536
1612
  });
1537
1613
  }
1538
1614
  else if (changes.autoConnect?.newValue === false) {
1539
1615
  clearRetry();
1616
+ clearWatchdog();
1540
1617
  }
1541
1618
  if (changes.pairingToken) {
1542
1619
  autoConnect().catch((error) => {
@@ -13,7 +13,10 @@ const MAX_NETWORK_EVENTS = 300;
13
13
  const SESSION_TTL_MS = 20_000;
14
14
  const SCREENSHOT_TIMEOUT_MS = 8000;
15
15
  const TAB_CLOSE_TIMEOUT_MS = 5000;
16
+ const OPS_SESSION_DETACH_TIMEOUT_MS = 3000;
16
17
  const POPUP_ATTACH_RETRY_DELAY_MS = 100;
18
+ const ROOT_DETACH_VERIFY_DELAY_MS = 250;
19
+ const ROOT_DETACH_VERIFY_ATTEMPTS = 4;
17
20
  const STALE_REF_ERROR_SUFFIX = "Take a new snapshot first.";
18
21
  const DOM_OUTER_HTML_DECLARATION = `
19
22
  function() {
@@ -305,6 +308,12 @@ export class OpsRuntime {
305
308
  });
306
309
  }
307
310
  }
311
+ handleRelayDisconnected() {
312
+ this.cdp.markClientClosed();
313
+ for (const session of this.sessions.list()) {
314
+ void this.cleanupSession(session, "ops_session_expired");
315
+ }
316
+ }
308
317
  handleHello(message) {
309
318
  if (message.version !== OPS_PROTOCOL_VERSION) {
310
319
  const error = {
@@ -2576,10 +2585,19 @@ export class OpsRuntime {
2576
2585
  await this.cdp.setDiscoverTargetsEnabled?.(true);
2577
2586
  }
2578
2587
  catch (error) {
2588
+ if (isAttachBlockedError(error)) {
2589
+ return;
2590
+ }
2579
2591
  logError("ops.discover_targets", error, {
2580
2592
  code: "discover_targets_enable_failed",
2581
2593
  extra: baseDetails
2582
2594
  });
2595
+ if (baseDetails.strict) {
2596
+ throw this.decorateCdpFailure(error, {
2597
+ ...baseDetails,
2598
+ enablementStage: "set_discover_targets"
2599
+ });
2600
+ }
2583
2601
  }
2584
2602
  }
2585
2603
  async enableTargetDomainsOnDebuggee(debuggee, baseDetails) {
@@ -4006,7 +4024,7 @@ export class OpsRuntime {
4006
4024
  }
4007
4025
  throw new Error("Wait for selector timed out");
4008
4026
  }
4009
- cleanupSession(session, event) {
4027
+ async cleanupSession(session, event) {
4010
4028
  this.clearClosingTimer(session.id);
4011
4029
  const waiters = this.parallelWaiters.get(session.id);
4012
4030
  if (waiters) {
@@ -4020,8 +4038,25 @@ export class OpsRuntime {
4020
4038
  this.parallelWaiters.delete(session.id);
4021
4039
  }
4022
4040
  this.sessions.delete(session.id);
4023
- for (const target of session.targets.values()) {
4024
- void this.cdp.detachTab(target.tabId).catch(() => undefined);
4041
+ const targets = Array.from(session.targets.values());
4042
+ try {
4043
+ const results = await withTimeout(Promise.allSettled(targets.map(async (target) => this.cdp.detachTab(target.tabId))), OPS_SESSION_DETACH_TIMEOUT_MS, "Ops session detach timed out");
4044
+ const failedTabIds = results.flatMap((result, index) => {
4045
+ const target = targets[index];
4046
+ return result.status === "rejected" && target ? [target.tabId] : [];
4047
+ });
4048
+ if (failedTabIds.length > 0) {
4049
+ logError("ops.session_detach", new Error("One or more ops session targets failed to detach"), {
4050
+ code: "session_detach_failed",
4051
+ extra: { sessionId: session.id, event, failedTabIds }
4052
+ });
4053
+ }
4054
+ }
4055
+ catch (error) {
4056
+ logError("ops.session_detach", error, {
4057
+ code: "session_detach_failed",
4058
+ extra: { sessionId: session.id, event }
4059
+ });
4025
4060
  }
4026
4061
  this.emitSessionEvent(session, event);
4027
4062
  }
@@ -4036,7 +4071,7 @@ export class OpsRuntime {
4036
4071
  if (!removedTarget)
4037
4072
  return;
4038
4073
  if (targetId === session.targetId || session.targets.size === 0) {
4039
- this.cleanupSession(session, event);
4074
+ void this.cleanupSession(session, event);
4040
4075
  }
4041
4076
  }
4042
4077
  async handleDebuggerDetachForTab(tabId) {
@@ -4044,7 +4079,8 @@ export class OpsRuntime {
4044
4079
  if (!session)
4045
4080
  return;
4046
4081
  if (tabId === session.tabId) {
4047
- // Root tab detach can be transient during child-target shutdown; tab removal handler owns root teardown.
4082
+ // Root tab detach can be transient during child-target shutdown, but it must not retain ownership forever.
4083
+ this.scheduleRootDebuggerDetachVerification(session.id, tabId);
4048
4084
  return;
4049
4085
  }
4050
4086
  const targetId = this.sessions.getTargetIdByTabId(session.id, tabId);
@@ -4064,6 +4100,48 @@ export class OpsRuntime {
4064
4100
  }
4065
4101
  this.handleClosedTarget(tabId, "ops_session_closed");
4066
4102
  }
4103
+ scheduleRootDebuggerDetachVerification(sessionId, tabId) {
4104
+ if (typeof this.cdp.getTabDebuggee !== "function") {
4105
+ return;
4106
+ }
4107
+ setTimeout(() => {
4108
+ void this.verifyRootDebuggerDetach(sessionId, tabId, ROOT_DETACH_VERIFY_ATTEMPTS);
4109
+ }, ROOT_DETACH_VERIFY_DELAY_MS);
4110
+ }
4111
+ async verifyRootDebuggerDetach(sessionId, tabId, attemptsRemaining) {
4112
+ const session = this.sessions.get(sessionId);
4113
+ if (!session || session.tabId !== tabId) {
4114
+ return;
4115
+ }
4116
+ if (this.isConcreteDebuggee(this.cdp.getTabDebuggee?.(tabId))) {
4117
+ return;
4118
+ }
4119
+ let liveTab = null;
4120
+ try {
4121
+ liveTab = await this.tabs.getTab(tabId);
4122
+ }
4123
+ catch {
4124
+ liveTab = null;
4125
+ }
4126
+ const current = this.sessions.get(sessionId);
4127
+ if (!current || current.tabId !== tabId) {
4128
+ return;
4129
+ }
4130
+ if (!liveTab) {
4131
+ void this.cleanupSession(current, "ops_session_closed");
4132
+ return;
4133
+ }
4134
+ if (this.isConcreteDebuggee(this.cdp.getTabDebuggee?.(tabId))) {
4135
+ return;
4136
+ }
4137
+ if (attemptsRemaining > 1) {
4138
+ setTimeout(() => {
4139
+ void this.verifyRootDebuggerDetach(sessionId, tabId, attemptsRemaining - 1);
4140
+ }, ROOT_DETACH_VERIFY_DELAY_MS);
4141
+ return;
4142
+ }
4143
+ void this.cleanupSession(current, "ops_session_closed");
4144
+ }
4067
4145
  async closeTabBestEffort(tabId) {
4068
4146
  try {
4069
4147
  await withTimeout(this.tabs.closeTab(tabId), TAB_CLOSE_TIMEOUT_MS, "Ops tab close timed out");
@@ -4081,7 +4159,7 @@ export class OpsRuntime {
4081
4159
  if (!session) {
4082
4160
  return;
4083
4161
  }
4084
- this.cleanupSession(session, event);
4162
+ void this.cleanupSession(session, event);
4085
4163
  }, 0);
4086
4164
  }
4087
4165
  sendResponse(message, payload) {
@@ -4159,7 +4237,7 @@ export class OpsRuntime {
4159
4237
  this.closingTimers.delete(session.id);
4160
4238
  const current = this.sessions.get(session.id);
4161
4239
  if (current && current.state === "closing") {
4162
- this.cleanupSession(current, "ops_session_expired");
4240
+ void this.cleanupSession(current, "ops_session_expired");
4163
4241
  }
4164
4242
  }, SESSION_TTL_MS);
4165
4243
  this.closingTimers.set(session.id, timeoutId);
@@ -75,6 +75,9 @@ export class OpsSessionStore {
75
75
  listOwnedBy(clientId) {
76
76
  return this.coordinator.listOwnedBy(clientId);
77
77
  }
78
+ list() {
79
+ return this.coordinator.list();
80
+ }
78
81
  delete(sessionId) {
79
82
  return this.coordinator.delete(sessionId);
80
83
  }
@@ -44,6 +44,9 @@ export class TargetSessionCoordinator {
44
44
  listOwnedBy(clientId) {
45
45
  return Array.from(this.sessions.values()).filter((session) => session.ownerClientId === clientId);
46
46
  }
47
+ list() {
48
+ return Array.from(this.sessions.values());
49
+ }
47
50
  delete(sessionId) {
48
51
  const session = this.sessions.get(sessionId) ?? null;
49
52
  if (!session) {
@@ -912,6 +912,9 @@ export class CDPRouter {
912
912
  return;
913
913
  }
914
914
  catch (error) {
915
+ if (this.isExpectedDebuggerCleanupError(error)) {
916
+ continue;
917
+ }
915
918
  logError("cdp.restore_root_attach", error, {
916
919
  code: "restore_root_attach_failed",
917
920
  extra: { tabId }
@@ -2021,6 +2024,9 @@ export class CDPRouter {
2021
2024
  const message = error instanceof Error ? error.message : String(error);
2022
2025
  return STALE_TAB_ERROR_MARKERS.some((marker) => message.includes(marker));
2023
2026
  }
2027
+ isExpectedDebuggerCleanupError(error) {
2028
+ return this.isStaleTabError(error);
2029
+ }
2024
2030
  markExpectedRootDetach(tabId) {
2025
2031
  this.expectedRootDetachDeadlines.set(tabId, Date.now() + 1000);
2026
2032
  }
@@ -2074,6 +2080,9 @@ export class CDPRouter {
2074
2080
  });
2075
2081
  }
2076
2082
  catch (error) {
2083
+ if (this.isExpectedDebuggerCleanupError(error)) {
2084
+ return;
2085
+ }
2077
2086
  logError("cdp.safe_detach", error, { code: "detach_failed" });
2078
2087
  }
2079
2088
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "OpenDevBrowser Relay",
4
- "version": "0.0.28",
4
+ "version": "0.0.29",
5
5
  "description": "Optional bridge to reuse existing Chrome tabs with OpenDevBrowser.",
6
6
  "permissions": [
7
7
  "debugger",