opendevbrowser 0.0.17 → 0.0.19

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 (529) hide show
  1. package/README.md +172 -73
  2. package/dist/annotate/agent-inbox-store.d.ts +58 -0
  3. package/dist/annotate/agent-inbox-store.d.ts.map +1 -0
  4. package/dist/annotate/agent-inbox.d.ts +25 -0
  5. package/dist/annotate/agent-inbox.d.ts.map +1 -0
  6. package/dist/annotate/direct-annotator.d.ts.map +1 -1
  7. package/dist/annotate/timeout-messages.d.ts +4 -0
  8. package/dist/annotate/timeout-messages.d.ts.map +1 -0
  9. package/dist/automation/coordinator.d.ts +55 -0
  10. package/dist/automation/coordinator.d.ts.map +1 -0
  11. package/dist/browser/annotation-manager.d.ts +4 -1
  12. package/dist/browser/annotation-manager.d.ts.map +1 -1
  13. package/dist/browser/browser-manager.d.ts +147 -47
  14. package/dist/browser/browser-manager.d.ts.map +1 -1
  15. package/dist/browser/canvas-client.d.ts +1 -0
  16. package/dist/browser/canvas-client.d.ts.map +1 -1
  17. package/dist/browser/canvas-code-sync-manager.d.ts +9 -1
  18. package/dist/browser/canvas-code-sync-manager.d.ts.map +1 -1
  19. package/dist/browser/canvas-manager.d.ts +29 -1
  20. package/dist/browser/canvas-manager.d.ts.map +1 -1
  21. package/dist/browser/global-challenge-coordinator.d.ts +27 -0
  22. package/dist/browser/global-challenge-coordinator.d.ts.map +1 -0
  23. package/dist/browser/manager-types.d.ts +167 -1
  24. package/dist/browser/manager-types.d.ts.map +1 -1
  25. package/dist/browser/ops-browser-manager.d.ts +103 -3
  26. package/dist/browser/ops-browser-manager.d.ts.map +1 -1
  27. package/dist/browser/ops-client.d.ts +17 -1
  28. package/dist/browser/ops-client.d.ts.map +1 -1
  29. package/dist/browser/playwright-runtime.d.ts +4 -0
  30. package/dist/browser/playwright-runtime.d.ts.map +1 -0
  31. package/dist/browser/review-surface.d.ts +9 -0
  32. package/dist/browser/review-surface.d.ts.map +1 -0
  33. package/dist/browser/screencast-recorder.d.ts +57 -0
  34. package/dist/browser/screencast-recorder.d.ts.map +1 -0
  35. package/dist/browser/session-inspector.d.ts +71 -0
  36. package/dist/browser/session-inspector.d.ts.map +1 -0
  37. package/dist/browser/session-store.d.ts +5 -1
  38. package/dist/browser/session-store.d.ts.map +1 -1
  39. package/dist/browser/system-chrome-cookies.d.ts +46 -0
  40. package/dist/browser/system-chrome-cookies.d.ts.map +1 -0
  41. package/dist/browser/target-manager.d.ts +1 -0
  42. package/dist/browser/target-manager.d.ts.map +1 -1
  43. package/dist/cache/chrome-locator.d.ts.map +1 -1
  44. package/dist/cache/chrome-user-data.d.ts +17 -0
  45. package/dist/cache/chrome-user-data.d.ts.map +1 -0
  46. package/dist/canvas/adapter-plugins/loader.d.ts +13 -0
  47. package/dist/canvas/adapter-plugins/loader.d.ts.map +1 -0
  48. package/dist/canvas/adapter-plugins/manifest.d.ts +146 -0
  49. package/dist/canvas/adapter-plugins/manifest.d.ts.map +1 -0
  50. package/dist/canvas/adapter-plugins/types.d.ts +83 -0
  51. package/dist/canvas/adapter-plugins/types.d.ts.map +1 -0
  52. package/dist/canvas/adapter-plugins/validator.d.ts +10 -0
  53. package/dist/canvas/adapter-plugins/validator.d.ts.map +1 -0
  54. package/dist/canvas/code-sync/apply-tsx.d.ts +3 -1
  55. package/dist/canvas/code-sync/apply-tsx.d.ts.map +1 -1
  56. package/dist/canvas/code-sync/import.d.ts +1 -0
  57. package/dist/canvas/code-sync/import.d.ts.map +1 -1
  58. package/dist/canvas/code-sync/manifest.d.ts +2 -1
  59. package/dist/canvas/code-sync/manifest.d.ts.map +1 -1
  60. package/dist/canvas/code-sync/tsx-adapter.d.ts.map +1 -1
  61. package/dist/canvas/code-sync/types.d.ts +102 -10
  62. package/dist/canvas/code-sync/types.d.ts.map +1 -1
  63. package/dist/canvas/document-store.d.ts +11 -1
  64. package/dist/canvas/document-store.d.ts.map +1 -1
  65. package/dist/canvas/export.d.ts.map +1 -1
  66. package/dist/canvas/framework-adapters/custom-elements-v1.d.ts +3 -0
  67. package/dist/canvas/framework-adapters/custom-elements-v1.d.ts.map +1 -0
  68. package/dist/canvas/framework-adapters/html-static-v1.d.ts +3 -0
  69. package/dist/canvas/framework-adapters/html-static-v1.d.ts.map +1 -0
  70. package/dist/canvas/framework-adapters/markup.d.ts +9 -0
  71. package/dist/canvas/framework-adapters/markup.d.ts.map +1 -0
  72. package/dist/canvas/framework-adapters/react-tsx-v2.d.ts +3 -0
  73. package/dist/canvas/framework-adapters/react-tsx-v2.d.ts.map +1 -0
  74. package/dist/canvas/framework-adapters/registry.d.ts +12 -0
  75. package/dist/canvas/framework-adapters/registry.d.ts.map +1 -0
  76. package/dist/canvas/framework-adapters/svelte-sfc-v1.d.ts +3 -0
  77. package/dist/canvas/framework-adapters/svelte-sfc-v1.d.ts.map +1 -0
  78. package/dist/canvas/framework-adapters/types.d.ts +57 -0
  79. package/dist/canvas/framework-adapters/types.d.ts.map +1 -0
  80. package/dist/canvas/framework-adapters/vue-sfc-v1.d.ts +3 -0
  81. package/dist/canvas/framework-adapters/vue-sfc-v1.d.ts.map +1 -0
  82. package/dist/canvas/kits/catalog.d.ts +5 -0
  83. package/dist/canvas/kits/catalog.d.ts.map +1 -0
  84. package/dist/canvas/library-adapters/react/index.d.ts +3 -0
  85. package/dist/canvas/library-adapters/react/index.d.ts.map +1 -0
  86. package/dist/canvas/library-adapters/registry.d.ts +11 -0
  87. package/dist/canvas/library-adapters/registry.d.ts.map +1 -0
  88. package/dist/canvas/library-adapters/types.d.ts +43 -0
  89. package/dist/canvas/library-adapters/types.d.ts.map +1 -0
  90. package/dist/canvas/repo-store.d.ts +2 -0
  91. package/dist/canvas/repo-store.d.ts.map +1 -1
  92. package/dist/canvas/starters/catalog.d.ts +34 -0
  93. package/dist/canvas/starters/catalog.d.ts.map +1 -0
  94. package/dist/canvas/token-references.d.ts +22 -0
  95. package/dist/canvas/token-references.d.ts.map +1 -0
  96. package/dist/canvas/types.d.ts +345 -6
  97. package/dist/canvas/types.d.ts.map +1 -1
  98. package/dist/challenges/action-loop.d.ts +13 -0
  99. package/dist/challenges/action-loop.d.ts.map +1 -0
  100. package/dist/challenges/capability-matrix.d.ts +3 -0
  101. package/dist/challenges/capability-matrix.d.ts.map +1 -0
  102. package/dist/challenges/evidence-bundle.d.ts +48 -0
  103. package/dist/challenges/evidence-bundle.d.ts.map +1 -0
  104. package/dist/challenges/governed-adapter-gateway.d.ts +4 -0
  105. package/dist/challenges/governed-adapter-gateway.d.ts.map +1 -0
  106. package/dist/challenges/human-yield-gate.d.ts +20 -0
  107. package/dist/challenges/human-yield-gate.d.ts.map +1 -0
  108. package/dist/challenges/index.d.ts +15 -0
  109. package/dist/challenges/index.d.ts.map +1 -0
  110. package/dist/challenges/interpreter.d.ts +3 -0
  111. package/dist/challenges/interpreter.d.ts.map +1 -0
  112. package/dist/challenges/optional-computer-use-bridge.d.ts +9 -0
  113. package/dist/challenges/optional-computer-use-bridge.d.ts.map +1 -0
  114. package/dist/challenges/orchestrator.d.ts +32 -0
  115. package/dist/challenges/orchestrator.d.ts.map +1 -0
  116. package/dist/challenges/outcome-recorder.d.ts +8 -0
  117. package/dist/challenges/outcome-recorder.d.ts.map +1 -0
  118. package/dist/challenges/owned-environment-lane.d.ts +3 -0
  119. package/dist/challenges/owned-environment-lane.d.ts.map +1 -0
  120. package/dist/challenges/policy-gate.d.ts +9 -0
  121. package/dist/challenges/policy-gate.d.ts.map +1 -0
  122. package/dist/challenges/sanctioned-identity-lane.d.ts +3 -0
  123. package/dist/challenges/sanctioned-identity-lane.d.ts.map +1 -0
  124. package/dist/challenges/service-adapter-lane.d.ts +3 -0
  125. package/dist/challenges/service-adapter-lane.d.ts.map +1 -0
  126. package/dist/challenges/strategy-selector.d.ts +10 -0
  127. package/dist/challenges/strategy-selector.d.ts.map +1 -0
  128. package/dist/challenges/types.d.ts +277 -0
  129. package/dist/challenges/types.d.ts.map +1 -0
  130. package/dist/challenges/verification-gate.d.ts +15 -0
  131. package/dist/challenges/verification-gate.d.ts.map +1 -0
  132. package/dist/chunk-5FZQJRBQ.js +15256 -0
  133. package/dist/chunk-5FZQJRBQ.js.map +1 -0
  134. package/dist/chunk-W4IHGDXV.js +33519 -0
  135. package/dist/chunk-W4IHGDXV.js.map +1 -0
  136. package/dist/chunk-YBQECXZX.js +409 -0
  137. package/dist/chunk-YBQECXZX.js.map +1 -0
  138. package/dist/cli/args.d.ts +4 -4
  139. package/dist/cli/args.d.ts.map +1 -1
  140. package/dist/cli/commands/artifacts.d.ts.map +1 -1
  141. package/dist/cli/commands/canvas.d.ts +7 -7
  142. package/dist/cli/commands/canvas.d.ts.map +1 -1
  143. package/dist/cli/commands/daemon.d.ts +7 -0
  144. package/dist/cli/commands/daemon.d.ts.map +1 -1
  145. package/dist/cli/commands/desktop/accessibility-snapshot.d.ts +3 -0
  146. package/dist/cli/commands/desktop/accessibility-snapshot.d.ts.map +1 -0
  147. package/dist/cli/commands/desktop/active-window.d.ts +3 -0
  148. package/dist/cli/commands/desktop/active-window.d.ts.map +1 -0
  149. package/dist/cli/commands/desktop/capture-desktop.d.ts +3 -0
  150. package/dist/cli/commands/desktop/capture-desktop.d.ts.map +1 -0
  151. package/dist/cli/commands/desktop/capture-window.d.ts +3 -0
  152. package/dist/cli/commands/desktop/capture-window.d.ts.map +1 -0
  153. package/dist/cli/commands/desktop/shared.d.ts +19 -0
  154. package/dist/cli/commands/desktop/shared.d.ts.map +1 -0
  155. package/dist/cli/commands/desktop/status.d.ts +3 -0
  156. package/dist/cli/commands/desktop/status.d.ts.map +1 -0
  157. package/dist/cli/commands/desktop/windows.d.ts +3 -0
  158. package/dist/cli/commands/desktop/windows.d.ts.map +1 -0
  159. package/dist/cli/commands/devtools/dialog.d.ts +19 -0
  160. package/dist/cli/commands/devtools/dialog.d.ts.map +1 -0
  161. package/dist/cli/commands/devtools/screencast-start.d.ts +20 -0
  162. package/dist/cli/commands/devtools/screencast-start.d.ts.map +1 -0
  163. package/dist/cli/commands/devtools/screencast-stop.d.ts +17 -0
  164. package/dist/cli/commands/devtools/screencast-stop.d.ts.map +1 -0
  165. package/dist/cli/commands/devtools/screenshot.d.ts +2 -0
  166. package/dist/cli/commands/devtools/screenshot.d.ts.map +1 -1
  167. package/dist/cli/commands/interact/click.d.ts.map +1 -1
  168. package/dist/cli/commands/interact/pointer-down.d.ts +7 -0
  169. package/dist/cli/commands/interact/pointer-down.d.ts.map +1 -0
  170. package/dist/cli/commands/interact/pointer-drag.d.ts +7 -0
  171. package/dist/cli/commands/interact/pointer-drag.d.ts.map +1 -0
  172. package/dist/cli/commands/interact/pointer-move.d.ts +7 -0
  173. package/dist/cli/commands/interact/pointer-move.d.ts.map +1 -0
  174. package/dist/cli/commands/interact/pointer-shared.d.ts +6 -0
  175. package/dist/cli/commands/interact/pointer-shared.d.ts.map +1 -0
  176. package/dist/cli/commands/interact/pointer-up.d.ts +7 -0
  177. package/dist/cli/commands/interact/pointer-up.d.ts.map +1 -0
  178. package/dist/cli/commands/interact/upload.d.ts +18 -0
  179. package/dist/cli/commands/interact/upload.d.ts.map +1 -0
  180. package/dist/cli/commands/macro-resolve.d.ts +2 -0
  181. package/dist/cli/commands/macro-resolve.d.ts.map +1 -1
  182. package/dist/cli/commands/native.d.ts +10 -7
  183. package/dist/cli/commands/native.d.ts.map +1 -1
  184. package/dist/cli/commands/nav/review.d.ts +7 -0
  185. package/dist/cli/commands/nav/review.d.ts.map +1 -0
  186. package/dist/cli/commands/nav/snapshot.d.ts.map +1 -1
  187. package/dist/cli/commands/pages/open.d.ts.map +1 -1
  188. package/dist/cli/commands/product-video.d.ts +2 -0
  189. package/dist/cli/commands/product-video.d.ts.map +1 -1
  190. package/dist/cli/commands/research.d.ts +3 -0
  191. package/dist/cli/commands/research.d.ts.map +1 -1
  192. package/dist/cli/commands/run.d.ts +14 -0
  193. package/dist/cli/commands/run.d.ts.map +1 -1
  194. package/dist/cli/commands/serve.d.ts +1 -26
  195. package/dist/cli/commands/serve.d.ts.map +1 -1
  196. package/dist/cli/commands/session/connect.d.ts.map +1 -1
  197. package/dist/cli/commands/session/disconnect.d.ts.map +1 -1
  198. package/dist/cli/commands/session/inspector.d.ts +21 -0
  199. package/dist/cli/commands/session/inspector.d.ts.map +1 -0
  200. package/dist/cli/commands/session/launch.d.ts.map +1 -1
  201. package/dist/cli/commands/shopping.d.ts +5 -0
  202. package/dist/cli/commands/shopping.d.ts.map +1 -1
  203. package/dist/cli/commands/status.d.ts +2 -14
  204. package/dist/cli/commands/status.d.ts.map +1 -1
  205. package/dist/cli/commands/targets/new.d.ts.map +1 -1
  206. package/dist/cli/daemon-autostart.d.ts +11 -0
  207. package/dist/cli/daemon-autostart.d.ts.map +1 -1
  208. package/dist/cli/daemon-client.d.ts +3 -0
  209. package/dist/cli/daemon-client.d.ts.map +1 -1
  210. package/dist/cli/daemon-commands.d.ts.map +1 -1
  211. package/dist/cli/daemon-state.d.ts +16 -0
  212. package/dist/cli/daemon-state.d.ts.map +1 -1
  213. package/dist/cli/daemon-status.d.ts +7 -2
  214. package/dist/cli/daemon-status.d.ts.map +1 -1
  215. package/dist/cli/daemon.d.ts +1 -0
  216. package/dist/cli/daemon.d.ts.map +1 -1
  217. package/dist/cli/help.d.ts +15 -4
  218. package/dist/cli/help.d.ts.map +1 -1
  219. package/dist/cli/index.js +2476 -1036
  220. package/dist/cli/index.js.map +1 -1
  221. package/dist/cli/install-autostart-output.d.ts +6 -0
  222. package/dist/cli/install-autostart-output.d.ts.map +1 -0
  223. package/dist/cli/install-autostart-reconciliation.d.ts +23 -0
  224. package/dist/cli/install-autostart-reconciliation.d.ts.map +1 -0
  225. package/dist/cli/installers/skills.d.ts +42 -6
  226. package/dist/cli/installers/skills.d.ts.map +1 -1
  227. package/dist/cli/output.d.ts +3 -0
  228. package/dist/cli/output.d.ts.map +1 -1
  229. package/dist/cli/remote-desktop-runtime.d.ts +15 -0
  230. package/dist/cli/remote-desktop-runtime.d.ts.map +1 -0
  231. package/dist/cli/remote-manager.d.ts +24 -2
  232. package/dist/cli/remote-manager.d.ts.map +1 -1
  233. package/dist/cli/transport-timeouts.d.ts +8 -0
  234. package/dist/cli/transport-timeouts.d.ts.map +1 -0
  235. package/dist/cli/utils/http.d.ts +9 -0
  236. package/dist/cli/utils/http.d.ts.map +1 -1
  237. package/dist/cli/utils/parse.d.ts +2 -0
  238. package/dist/cli/utils/parse.d.ts.map +1 -1
  239. package/dist/cli/utils/skills.d.ts +1 -2
  240. package/dist/cli/utils/skills.d.ts.map +1 -1
  241. package/dist/cli/utils/workflow-message.d.ts +2 -0
  242. package/dist/cli/utils/workflow-message.d.ts.map +1 -0
  243. package/dist/config.d.ts +47 -0
  244. package/dist/config.d.ts.map +1 -1
  245. package/dist/core/bootstrap.d.ts.map +1 -1
  246. package/dist/core/index.d.ts +1 -0
  247. package/dist/core/index.d.ts.map +1 -1
  248. package/dist/core/logging.d.ts +3 -1
  249. package/dist/core/logging.d.ts.map +1 -1
  250. package/dist/core/runtime-assemblies.d.ts +22 -0
  251. package/dist/core/runtime-assemblies.d.ts.map +1 -0
  252. package/dist/core/types.d.ts +15 -0
  253. package/dist/core/types.d.ts.map +1 -1
  254. package/dist/desktop/audit.d.ts +37 -0
  255. package/dist/desktop/audit.d.ts.map +1 -0
  256. package/dist/desktop/errors.d.ts +7 -0
  257. package/dist/desktop/errors.d.ts.map +1 -0
  258. package/dist/desktop/index.d.ts +6 -0
  259. package/dist/desktop/index.d.ts.map +1 -0
  260. package/dist/desktop/runtime.d.ts +26 -0
  261. package/dist/desktop/runtime.d.ts.map +1 -0
  262. package/dist/desktop/types.d.ts +76 -0
  263. package/dist/desktop/types.d.ts.map +1 -0
  264. package/dist/extension-extractor.d.ts +6 -0
  265. package/dist/extension-extractor.d.ts.map +1 -1
  266. package/dist/index.d.ts.map +1 -1
  267. package/dist/index.js +1103 -467
  268. package/dist/index.js.map +1 -1
  269. package/dist/integrations/figma/assets.d.ts +13 -0
  270. package/dist/integrations/figma/assets.d.ts.map +1 -0
  271. package/dist/integrations/figma/auth.d.ts +3 -0
  272. package/dist/integrations/figma/auth.d.ts.map +1 -0
  273. package/dist/integrations/figma/client.d.ts +42 -0
  274. package/dist/integrations/figma/client.d.ts.map +1 -0
  275. package/dist/integrations/figma/mappers.d.ts +23 -0
  276. package/dist/integrations/figma/mappers.d.ts.map +1 -0
  277. package/dist/integrations/figma/normalize.d.ts +99 -0
  278. package/dist/integrations/figma/normalize.d.ts.map +1 -0
  279. package/dist/integrations/figma/url.d.ts +17 -0
  280. package/dist/integrations/figma/url.d.ts.map +1 -0
  281. package/dist/integrations/figma/variables.d.ts +21 -0
  282. package/dist/integrations/figma/variables.d.ts.map +1 -0
  283. package/dist/macros/execute-runtime.d.ts +19 -0
  284. package/dist/macros/execute-runtime.d.ts.map +1 -0
  285. package/dist/macros/execute.d.ts +3 -1
  286. package/dist/macros/execute.d.ts.map +1 -1
  287. package/dist/opendevbrowser.d.ts.map +1 -1
  288. package/dist/opendevbrowser.js +1103 -467
  289. package/dist/opendevbrowser.js.map +1 -1
  290. package/dist/providers/blocker.d.ts.map +1 -1
  291. package/dist/providers/browser-fallback.d.ts +30 -0
  292. package/dist/providers/browser-fallback.d.ts.map +1 -0
  293. package/dist/providers/constraint.d.ts +45 -0
  294. package/dist/providers/constraint.d.ts.map +1 -0
  295. package/dist/providers/index.d.ts +11 -2
  296. package/dist/providers/index.d.ts.map +1 -1
  297. package/dist/providers/policy.d.ts.map +1 -1
  298. package/dist/providers/product-video-compiler.d.ts +92 -0
  299. package/dist/providers/product-video-compiler.d.ts.map +1 -0
  300. package/dist/providers/registry.d.ts +37 -1
  301. package/dist/providers/registry.d.ts.map +1 -1
  302. package/dist/providers/renderer.d.ts.map +1 -1
  303. package/dist/providers/research-compiler.d.ts +64 -0
  304. package/dist/providers/research-compiler.d.ts.map +1 -0
  305. package/dist/providers/research-executor.d.ts +27 -0
  306. package/dist/providers/research-executor.d.ts.map +1 -0
  307. package/dist/providers/runtime-bundle.d.ts +26 -0
  308. package/dist/providers/runtime-bundle.d.ts.map +1 -0
  309. package/dist/providers/runtime-factory.d.ts +6 -1
  310. package/dist/providers/runtime-factory.d.ts.map +1 -1
  311. package/dist/providers/runtime-policy.d.ts +24 -0
  312. package/dist/providers/runtime-policy.d.ts.map +1 -0
  313. package/dist/providers/shared/anti-bot-policy.d.ts +3 -2
  314. package/dist/providers/shared/anti-bot-policy.d.ts.map +1 -1
  315. package/dist/providers/shopping/index.d.ts +11 -1
  316. package/dist/providers/shopping/index.d.ts.map +1 -1
  317. package/dist/providers/shopping-compiler.d.ts +51 -0
  318. package/dist/providers/shopping-compiler.d.ts.map +1 -0
  319. package/dist/providers/shopping-executor.d.ts +18 -0
  320. package/dist/providers/shopping-executor.d.ts.map +1 -0
  321. package/dist/providers/shopping-postprocess.d.ts +46 -0
  322. package/dist/providers/shopping-postprocess.d.ts.map +1 -0
  323. package/dist/providers/shopping-workflow.d.ts +33 -0
  324. package/dist/providers/shopping-workflow.d.ts.map +1 -0
  325. package/dist/providers/social/platform.d.ts +2 -1
  326. package/dist/providers/social/platform.d.ts.map +1 -1
  327. package/dist/providers/social/search-quality.d.ts +16 -0
  328. package/dist/providers/social/search-quality.d.ts.map +1 -0
  329. package/dist/providers/social/youtube-resolver.d.ts +2 -1
  330. package/dist/providers/social/youtube-resolver.d.ts.map +1 -1
  331. package/dist/providers/social/youtube.d.ts.map +1 -1
  332. package/dist/providers/types.d.ts +116 -4
  333. package/dist/providers/types.d.ts.map +1 -1
  334. package/dist/providers/web/crawl-worker.d.ts.map +1 -1
  335. package/dist/providers/web/extract.d.ts +16 -0
  336. package/dist/providers/web/extract.d.ts.map +1 -1
  337. package/dist/providers/web/index.d.ts.map +1 -1
  338. package/dist/providers/workflow-contracts.d.ts +53 -0
  339. package/dist/providers/workflow-contracts.d.ts.map +1 -0
  340. package/dist/providers/workflows.d.ts +30 -6
  341. package/dist/providers/workflows.d.ts.map +1 -1
  342. package/dist/{providers-G3LRHQXX.js → providers-G36AM3Z2.js} +2 -2
  343. package/dist/public-surface/generated-manifest.d.ts +1168 -0
  344. package/dist/public-surface/generated-manifest.d.ts.map +1 -0
  345. package/dist/public-surface/source.d.ts +437 -0
  346. package/dist/public-surface/source.d.ts.map +1 -0
  347. package/dist/relay/protocol.d.ts +25 -3
  348. package/dist/relay/protocol.d.ts.map +1 -1
  349. package/dist/relay/relay-endpoints.d.ts +21 -0
  350. package/dist/relay/relay-endpoints.d.ts.map +1 -1
  351. package/dist/relay/relay-server.d.ts +18 -0
  352. package/dist/relay/relay-server.d.ts.map +1 -1
  353. package/dist/skills/bundled-skill-directories.d.ts +8 -0
  354. package/dist/skills/bundled-skill-directories.d.ts.map +1 -0
  355. package/dist/skills/skill-loader.d.ts +9 -1
  356. package/dist/skills/skill-loader.d.ts.map +1 -1
  357. package/dist/skills/skill-loader.js +7 -0
  358. package/dist/skills/skill-nudge.d.ts.map +1 -1
  359. package/dist/skills/types.d.ts +31 -0
  360. package/dist/skills/types.d.ts.map +1 -1
  361. package/dist/snapshot/ops-snapshot.d.ts +1 -1
  362. package/dist/snapshot/ops-snapshot.d.ts.map +1 -1
  363. package/dist/snapshot/refs.d.ts +6 -1
  364. package/dist/snapshot/refs.d.ts.map +1 -1
  365. package/dist/snapshot/snapshotter.d.ts.map +1 -1
  366. package/dist/tools/connect.d.ts.map +1 -1
  367. package/dist/tools/deps.d.ts +4 -0
  368. package/dist/tools/deps.d.ts.map +1 -1
  369. package/dist/tools/desktop-shared.d.ts +6 -0
  370. package/dist/tools/desktop-shared.d.ts.map +1 -0
  371. package/dist/tools/desktop_accessibility_snapshot.d.ts +4 -0
  372. package/dist/tools/desktop_accessibility_snapshot.d.ts.map +1 -0
  373. package/dist/tools/desktop_active_window.d.ts +4 -0
  374. package/dist/tools/desktop_active_window.d.ts.map +1 -0
  375. package/dist/tools/desktop_capture_desktop.d.ts +4 -0
  376. package/dist/tools/desktop_capture_desktop.d.ts.map +1 -0
  377. package/dist/tools/desktop_capture_window.d.ts +4 -0
  378. package/dist/tools/desktop_capture_window.d.ts.map +1 -0
  379. package/dist/tools/desktop_status.d.ts +4 -0
  380. package/dist/tools/desktop_status.d.ts.map +1 -0
  381. package/dist/tools/desktop_windows.d.ts +4 -0
  382. package/dist/tools/desktop_windows.d.ts.map +1 -0
  383. package/dist/tools/dialog.d.ts +4 -0
  384. package/dist/tools/dialog.d.ts.map +1 -0
  385. package/dist/tools/index.d.ts +3 -0
  386. package/dist/tools/index.d.ts.map +1 -1
  387. package/dist/tools/launch.d.ts.map +1 -1
  388. package/dist/tools/macro_resolve.d.ts.map +1 -1
  389. package/dist/tools/pointer_down.d.ts +4 -0
  390. package/dist/tools/pointer_down.d.ts.map +1 -0
  391. package/dist/tools/pointer_drag.d.ts +4 -0
  392. package/dist/tools/pointer_drag.d.ts.map +1 -0
  393. package/dist/tools/pointer_move.d.ts +4 -0
  394. package/dist/tools/pointer_move.d.ts.map +1 -0
  395. package/dist/tools/pointer_up.d.ts +4 -0
  396. package/dist/tools/pointer_up.d.ts.map +1 -0
  397. package/dist/tools/product_video_run.d.ts.map +1 -1
  398. package/dist/tools/prompting_guide.d.ts.map +1 -1
  399. package/dist/tools/research_run.d.ts.map +1 -1
  400. package/dist/tools/review.d.ts +4 -0
  401. package/dist/tools/review.d.ts.map +1 -0
  402. package/dist/tools/screencast_start.d.ts +4 -0
  403. package/dist/tools/screencast_start.d.ts.map +1 -0
  404. package/dist/tools/screencast_stop.d.ts +4 -0
  405. package/dist/tools/screencast_stop.d.ts.map +1 -0
  406. package/dist/tools/screenshot.d.ts.map +1 -1
  407. package/dist/tools/session_inspector.d.ts +4 -0
  408. package/dist/tools/session_inspector.d.ts.map +1 -0
  409. package/dist/tools/shopping_run.d.ts.map +1 -1
  410. package/dist/tools/skill_list.d.ts.map +1 -1
  411. package/dist/tools/skill_load.d.ts.map +1 -1
  412. package/dist/tools/upload.d.ts +4 -0
  413. package/dist/tools/upload.d.ts.map +1 -0
  414. package/dist/tools/workflow-runtime.d.ts +4 -1
  415. package/dist/tools/workflow-runtime.d.ts.map +1 -1
  416. package/dist/utils/package-assets.d.ts +4 -0
  417. package/dist/utils/package-assets.d.ts.map +1 -0
  418. package/extension/canvas.html +379 -9
  419. package/extension/dist/annotate-content.js +62 -32
  420. package/extension/dist/annotation-payload.js +57 -21
  421. package/extension/dist/background.js +406 -61
  422. package/extension/dist/canvas/canvas-runtime.js +481 -52
  423. package/extension/dist/canvas/model.js +129 -1
  424. package/extension/dist/canvas-page.js +1882 -74
  425. package/extension/dist/ops/dom-bridge.js +139 -0
  426. package/extension/dist/ops/ops-runtime.js +2854 -295
  427. package/extension/dist/ops/ops-session-store.js +83 -5
  428. package/extension/dist/ops/snapshot-builder.js +2 -2
  429. package/extension/dist/ops/snapshot-shared.js +2 -2
  430. package/extension/dist/ops/target-session-coordinator.js +5 -3
  431. package/extension/dist/popup.js +50 -15
  432. package/extension/dist/services/CDPRouter.js +1567 -63
  433. package/extension/dist/services/ConnectionManager.js +436 -78
  434. package/extension/dist/services/RelayClient.js +70 -30
  435. package/extension/dist/services/TabManager.js +83 -10
  436. package/extension/dist/services/TargetSessionMap.js +127 -3
  437. package/extension/dist/services/attach-errors.js +20 -0
  438. package/extension/dist/services/cdp-router-commands.js +135 -8
  439. package/extension/dist/services/url-restrictions.js +9 -13
  440. package/extension/manifest.json +2 -2
  441. package/extension/popup.html +7 -6
  442. package/package.json +15 -7
  443. package/skills/AGENTS.md +9 -8
  444. package/skills/opendevbrowser-best-practices/SKILL.md +118 -9
  445. package/skills/opendevbrowser-best-practices/artifacts/browser-agent-known-issues-matrix.md +1 -0
  446. package/skills/opendevbrowser-best-practices/artifacts/command-channel-reference.md +26 -12
  447. package/skills/opendevbrowser-best-practices/artifacts/parity-gates.md +9 -2
  448. package/skills/opendevbrowser-best-practices/artifacts/provider-workflows.md +6 -0
  449. package/skills/opendevbrowser-best-practices/artifacts/skill-runtime-surface-matrix.md +58 -0
  450. package/skills/opendevbrowser-best-practices/assets/templates/skill-runtime-pack-matrix.json +674 -0
  451. package/skills/opendevbrowser-best-practices/assets/templates/surface-audit-checklist.json +9 -4
  452. package/skills/opendevbrowser-best-practices/scripts/odb-workflow.sh +89 -20
  453. package/skills/opendevbrowser-best-practices/scripts/resolve-odb-cli.sh +100 -0
  454. package/skills/opendevbrowser-best-practices/scripts/run-robustness-audit.sh +1 -0
  455. package/skills/opendevbrowser-best-practices/scripts/validate-skill-assets.sh +256 -116
  456. package/skills/opendevbrowser-best-practices/scripts/validator-fixture-cli.sh +208 -0
  457. package/skills/opendevbrowser-continuity-ledger/SKILL.md +14 -1
  458. package/skills/opendevbrowser-continuity-ledger/scripts/validate-skill-assets.sh +61 -0
  459. package/skills/opendevbrowser-data-extraction/SKILL.md +6 -0
  460. package/skills/opendevbrowser-data-extraction/scripts/validate-skill-assets.sh +112 -0
  461. package/skills/opendevbrowser-design-agent/SKILL.md +275 -0
  462. package/skills/opendevbrowser-design-agent/artifacts/app-shell-and-state-wiring.md +84 -0
  463. package/skills/opendevbrowser-design-agent/artifacts/async-search-state-ownership.md +58 -0
  464. package/skills/opendevbrowser-design-agent/artifacts/component-pattern-index.md +130 -0
  465. package/skills/opendevbrowser-design-agent/artifacts/design-contract-playbook.md +157 -0
  466. package/skills/opendevbrowser-design-agent/artifacts/design-release-gate.md +40 -0
  467. package/skills/opendevbrowser-design-agent/artifacts/design-workflows.md +153 -0
  468. package/skills/opendevbrowser-design-agent/artifacts/existing-surface-adaptation.md +56 -0
  469. package/skills/opendevbrowser-design-agent/artifacts/external-pattern-synthesis.md +103 -0
  470. package/skills/opendevbrowser-design-agent/artifacts/frontend-evaluation-rubric.md +61 -0
  471. package/skills/opendevbrowser-design-agent/artifacts/implementation-anti-patterns.md +163 -0
  472. package/skills/opendevbrowser-design-agent/artifacts/isolated-preview-validation.md +68 -0
  473. package/skills/opendevbrowser-design-agent/artifacts/loading-and-feedback-surfaces.md +56 -0
  474. package/skills/opendevbrowser-design-agent/artifacts/opendevbrowser-ui-example-map.md +44 -0
  475. package/skills/opendevbrowser-design-agent/artifacts/performance-audit-playbook.md +70 -0
  476. package/skills/opendevbrowser-design-agent/artifacts/research-harvest-workflow.md +81 -0
  477. package/skills/opendevbrowser-design-agent/artifacts/scroll-reveal-surface-planning.md +64 -0
  478. package/skills/opendevbrowser-design-agent/artifacts/state-ownership-matrix.md +36 -0
  479. package/skills/opendevbrowser-design-agent/artifacts/theming-and-token-ownership.md +43 -0
  480. package/skills/opendevbrowser-design-agent/assets/templates/canvas-generation-plan.design.v1.json +58 -0
  481. package/skills/opendevbrowser-design-agent/assets/templates/design-audit-report.v1.md +34 -0
  482. package/skills/opendevbrowser-design-agent/assets/templates/design-brief.v1.md +40 -0
  483. package/skills/opendevbrowser-design-agent/assets/templates/design-contract.v1.json +226 -0
  484. package/skills/opendevbrowser-design-agent/assets/templates/design-release-gate.v1.json +35 -0
  485. package/skills/opendevbrowser-design-agent/assets/templates/design-review-checklist.json +57 -0
  486. package/skills/opendevbrowser-design-agent/assets/templates/real-surface-design-matrix.json +32 -0
  487. package/skills/opendevbrowser-design-agent/assets/templates/reference-pattern-board.v1.json +31 -0
  488. package/skills/opendevbrowser-design-agent/scripts/design-workflow.sh +171 -0
  489. package/skills/opendevbrowser-design-agent/scripts/extract-canvas-plan.sh +56 -0
  490. package/skills/opendevbrowser-design-agent/scripts/validate-skill-assets.sh +223 -0
  491. package/skills/opendevbrowser-form-testing/SKILL.md +19 -3
  492. package/skills/opendevbrowser-form-testing/artifacts/form-workflows.md +5 -4
  493. package/skills/opendevbrowser-form-testing/assets/templates/challenge-decision-tree.json +2 -0
  494. package/skills/opendevbrowser-form-testing/scripts/validate-skill-assets.sh +109 -0
  495. package/skills/opendevbrowser-login-automation/SKILL.md +21 -3
  496. package/skills/opendevbrowser-login-automation/artifacts/login-workflows.md +5 -4
  497. package/skills/opendevbrowser-login-automation/assets/templates/auth-signals.json +5 -0
  498. package/skills/opendevbrowser-login-automation/assets/templates/login-scenario-matrix.json +3 -2
  499. package/skills/opendevbrowser-login-automation/scripts/run-login-workflow.sh +17 -1
  500. package/skills/opendevbrowser-login-automation/scripts/validate-skill-assets.sh +133 -0
  501. package/skills/opendevbrowser-product-presentation-asset/SKILL.md +23 -11
  502. package/skills/opendevbrowser-product-presentation-asset/artifacts/asset-pack-assembly.md +5 -3
  503. package/skills/opendevbrowser-product-presentation-asset/assets/templates/shot-list.md +2 -0
  504. package/skills/opendevbrowser-product-presentation-asset/assets/templates/video-assembly.md +3 -2
  505. package/skills/opendevbrowser-product-presentation-asset/scripts/capture-screenshots.sh +5 -1
  506. package/skills/opendevbrowser-product-presentation-asset/scripts/collect-product.sh +6 -2
  507. package/skills/opendevbrowser-product-presentation-asset/scripts/download-images.sh +5 -1
  508. package/skills/opendevbrowser-product-presentation-asset/scripts/render-video-brief.sh +20 -7
  509. package/skills/opendevbrowser-product-presentation-asset/scripts/validate-skill-assets.sh +39 -0
  510. package/skills/opendevbrowser-product-presentation-asset/scripts/write-manifest.sh +5 -1
  511. package/skills/opendevbrowser-research/SKILL.md +14 -6
  512. package/skills/opendevbrowser-research/scripts/render-output.sh +5 -1
  513. package/skills/opendevbrowser-research/scripts/run-research.sh +5 -1
  514. package/skills/opendevbrowser-research/scripts/validate-skill-assets.sh +45 -0
  515. package/skills/opendevbrowser-research/scripts/write-artifacts.sh +5 -1
  516. package/skills/opendevbrowser-shopping/SKILL.md +20 -1
  517. package/skills/opendevbrowser-shopping/scripts/normalize-offers.sh +6 -2
  518. package/skills/opendevbrowser-shopping/scripts/run-deal-hunt.sh +5 -1
  519. package/skills/opendevbrowser-shopping/scripts/run-shopping.sh +5 -1
  520. package/skills/opendevbrowser-shopping/scripts/validate-skill-assets.sh +54 -0
  521. package/dist/chunk-5J3IFL3X.js +0 -16706
  522. package/dist/chunk-5J3IFL3X.js.map +0 -1
  523. package/dist/chunk-D633UO34.js +0 -8149
  524. package/dist/chunk-D633UO34.js.map +0 -1
  525. package/dist/chunk-V7KUDHDG.js +0 -276
  526. package/dist/chunk-V7KUDHDG.js.map +0 -1
  527. package/dist/runtime-factory-BICHDPE7.js +0 -13
  528. /package/dist/{providers-G3LRHQXX.js.map → providers-G36AM3Z2.js.map} +0 -0
  529. /package/dist/{runtime-factory-BICHDPE7.js.map → skills/skill-loader.js.map} +0 -0
@@ -1,15 +1,30 @@
1
1
  import { TabManager } from "./TabManager.js";
2
2
  import { TargetSessionMap } from "./TargetSessionMap.js";
3
3
  import { logError } from "../logging.js";
4
+ import { getRestrictionMessage } from "./url-restrictions.js";
5
+ import { isAttachBlockedError } from "./attach-errors.js";
4
6
  import { handleSetDiscoverTargets, handleSetAutoAttach, handleCreateTarget, handleCloseTarget, handleActivateTarget, handleAttachToTarget, handleRoutedCommand } from "./cdp-router-commands.js";
5
7
  const FLAT_SESSION_ERROR = "Chrome 125+ required for extension relay (flat sessions).";
6
8
  const DEPRECATED_SEND_MESSAGE = "Target.sendMessageToTarget is deprecated in flat session mode. Use sessionId routing.";
7
9
  const DEFAULT_BROWSER_CONTEXT_ID = "default";
10
+ const DEFAULT_BROWSER_TARGET_ID = "browser";
11
+ const STALE_TAB_ERROR_MARKERS = [
12
+ "No tab with given id",
13
+ "Debugger is not attached",
14
+ "Detached while handling command"
15
+ ];
8
16
  export class CDPRouter {
9
17
  debuggees = new Map();
18
+ rootTargetTabIds = new Map();
10
19
  sessions = new TargetSessionMap();
11
20
  tabManager = new TabManager();
12
21
  rootAttachedSessions = new Set();
22
+ pendingTargetTabIds = new Map();
23
+ childAttachDiagnostics = new Map();
24
+ rootAttachDiagnostics = new Map();
25
+ rootRefreshDiagnostics = new Map();
26
+ expectedRootDetachDeadlines = new Map();
27
+ eventListeners = new Set();
13
28
  callbacks = null;
14
29
  autoAttachOptions = { autoAttach: false, waitForDebuggerOnStart: false, flatten: true };
15
30
  discoverTargets = false;
@@ -22,6 +37,7 @@ export class CDPRouter {
22
37
  churnTracker = new Map();
23
38
  churnWindowMs = 5000;
24
39
  churnThreshold = 3;
40
+ clientResetPending = false;
25
41
  handleEventBound = (source, method, params) => {
26
42
  this.handleEvent(source, method, params);
27
43
  };
@@ -31,38 +47,364 @@ export class CDPRouter {
31
47
  setCallbacks(callbacks) {
32
48
  this.callbacks = callbacks;
33
49
  }
50
+ addEventListener(listener) {
51
+ this.eventListeners.add(listener);
52
+ }
53
+ removeEventListener(listener) {
54
+ this.eventListeners.delete(listener);
55
+ }
56
+ async setDiscoverTargetsEnabled(discover) {
57
+ await this.prepareForNextClientIfNeeded();
58
+ const shouldEmit = discover && !this.discoverTargets;
59
+ this.discoverTargets = discover;
60
+ for (const debuggee of this.debuggees.values()) {
61
+ await this.applyDiscoverTargets(debuggee, discover);
62
+ }
63
+ if (!shouldEmit) {
64
+ return;
65
+ }
66
+ for (const targetInfo of this.sessions.listTargetInfos()) {
67
+ const tabId = this.sessions.getByTargetId(targetInfo.targetId)?.tabId
68
+ ?? this.rootTargetTabIds.get(targetInfo.targetId)
69
+ ?? this.primaryTabId;
70
+ if (typeof tabId === "number") {
71
+ this.emitTargetCreated(tabId, targetInfo);
72
+ }
73
+ }
74
+ }
75
+ async configureAutoAttach(options) {
76
+ await this.prepareForNextClientIfNeeded();
77
+ if (options.flatten === false) {
78
+ throw new Error(FLAT_SESSION_ERROR);
79
+ }
80
+ this.autoAttachOptions = { ...options, flatten: true };
81
+ if (this.autoAttachOptions.autoAttach) {
82
+ this.resetRootAttached();
83
+ }
84
+ for (const debuggee of this.debuggees.values()) {
85
+ await this.applyAutoAttach(debuggee);
86
+ }
87
+ if (!this.autoAttachOptions.autoAttach) {
88
+ this.emitRootDetached();
89
+ return;
90
+ }
91
+ for (const tabId of this.sessions.listTabIds()) {
92
+ await this.refreshRootTargetInfo(tabId);
93
+ }
94
+ for (const targetInfo of this.sessions.listTargetInfos()) {
95
+ this.emitRootAttached(targetInfo);
96
+ }
97
+ }
34
98
  async attach(tabId) {
99
+ await this.prepareForNextClientIfNeeded(tabId);
35
100
  await this.attachInternal(tabId, true);
36
101
  }
102
+ async refreshTabAttachment(tabId) {
103
+ const preparedTabId = await this.prepareForNextClientIfNeeded(tabId);
104
+ const resetPreparedSameTabRoot = preparedTabId === tabId && this.debuggees.has(tabId);
105
+ const path = this.debuggees.has(tabId)
106
+ ? "reattach_root_debuggee"
107
+ : "attach_internal";
108
+ try {
109
+ if (resetPreparedSameTabRoot) {
110
+ // Reset preflight already rebuilt this root; probing it is enough.
111
+ }
112
+ else if (path === "reattach_root_debuggee") {
113
+ await this.reattachRootDebuggee(tabId);
114
+ }
115
+ else {
116
+ await this.attachInternal(tabId, false);
117
+ }
118
+ }
119
+ catch (error) {
120
+ await this.captureRootRefreshDiagnostic(tabId, path, false, error);
121
+ throw error;
122
+ }
123
+ await this.captureRootRefreshDiagnostic(tabId, path, true);
124
+ }
125
+ async primeAttachedRootSession(tabId) {
126
+ await this.prepareForNextClientIfNeeded(tabId);
127
+ if (this.sessions.getAttachedRootSession(tabId)) {
128
+ return;
129
+ }
130
+ await this.ensureAttachedRootSession(tabId);
131
+ }
132
+ async resolveTabTargetId(tabId) {
133
+ await this.prepareForNextClientIfNeeded(tabId);
134
+ return (await this.readDebuggerTargetInfo(tabId))?.id ?? null;
135
+ }
136
+ async resolveTabOpenerTargetId(tabId) {
137
+ await this.prepareForNextClientIfNeeded();
138
+ const { tab, pageTargets } = await this.readDebuggerPageTargets(tabId);
139
+ if (pageTargets.length === 0) {
140
+ return null;
141
+ }
142
+ const preferredTarget = this.selectPreferredDebuggerTargetInfo(tab, pageTargets);
143
+ const candidates = preferredTarget
144
+ ? [preferredTarget, ...pageTargets.filter((target) => target !== preferredTarget)]
145
+ : pageTargets;
146
+ for (const candidate of candidates) {
147
+ const targetInfo = this.resolveTargetInfo(candidate.id);
148
+ const openerTabId = this.pendingTargetTabIds.get(candidate.id)
149
+ ?? this.resolveLinkedTargetTabId(candidate.openerId)
150
+ ?? this.resolveLinkedTargetTabId(targetInfo?.openerId)
151
+ ?? null;
152
+ if (openerTabId !== null) {
153
+ return `tab-${openerTabId}`;
154
+ }
155
+ }
156
+ return null;
157
+ }
158
+ async attachChildTarget(tabId, targetId) {
159
+ await this.prepareForNextClientIfNeeded(tabId);
160
+ let rootDebuggee;
161
+ try {
162
+ rootDebuggee = await this.resolveRootSessionDebuggee(tabId);
163
+ }
164
+ catch (error) {
165
+ this.recordChildAttachDiagnostic(tabId, targetId, {
166
+ stage: "raw_attach_failed",
167
+ reason: getErrorMessage(error)
168
+ });
169
+ throw error;
170
+ }
171
+ let initialStage;
172
+ let directError = null;
173
+ let rootTargetRetryStage;
174
+ const captureRootRecordState = () => {
175
+ const record = this.sessions.getByTabId(tabId);
176
+ return {
177
+ present: Boolean(record),
178
+ rootSessionId: record?.rootSessionId ?? null,
179
+ targetId: record?.targetInfo.targetId ?? null,
180
+ attachTargetId: record?.attachTargetId ?? null
181
+ };
182
+ };
183
+ try {
184
+ const directSessionId = await this.attachChildTargetWithDebuggee(rootDebuggee, targetId);
185
+ if (directSessionId) {
186
+ this.clearChildAttachDiagnostic(tabId, targetId);
187
+ return directSessionId;
188
+ }
189
+ const directTargetRetry = await this.attachChildTargetWithRootTargetId(rootDebuggee, targetId);
190
+ if (directTargetRetry.sessionId) {
191
+ this.clearChildAttachDiagnostic(tabId, targetId);
192
+ return directTargetRetry.sessionId;
193
+ }
194
+ if (directTargetRetry.stage !== "attached") {
195
+ rootTargetRetryStage = directTargetRetry.stage;
196
+ }
197
+ initialStage = "raw_attach_null";
198
+ }
199
+ catch (error) {
200
+ const attachBlocked = isAttachBlockedError(error);
201
+ const staleRootDebuggee = this.isStaleTabError(error);
202
+ if (!attachBlocked && !staleRootDebuggee) {
203
+ this.recordChildAttachDiagnostic(tabId, targetId, {
204
+ stage: "raw_attach_failed",
205
+ reason: getErrorMessage(error)
206
+ });
207
+ throw error;
208
+ }
209
+ if (staleRootDebuggee) {
210
+ directError = error;
211
+ }
212
+ if (attachBlocked || staleRootDebuggee) {
213
+ let directTargetRetry;
214
+ try {
215
+ directTargetRetry = await this.attachChildTargetWithRootTargetId(rootDebuggee, targetId, error);
216
+ }
217
+ catch (fallbackError) {
218
+ this.recordChildAttachDiagnostic(tabId, targetId, {
219
+ stage: "raw_attach_failed",
220
+ reason: getErrorMessage(fallbackError)
221
+ });
222
+ throw fallbackError;
223
+ }
224
+ if (directTargetRetry.sessionId) {
225
+ this.clearChildAttachDiagnostic(tabId, targetId);
226
+ return directTargetRetry.sessionId;
227
+ }
228
+ if (directTargetRetry.stage !== "attached") {
229
+ rootTargetRetryStage = directTargetRetry.stage;
230
+ }
231
+ if (attachBlocked) {
232
+ initialStage = "raw_attach_blocked";
233
+ directError = error;
234
+ }
235
+ }
236
+ }
237
+ let attachedRootRecovery = await this.ensureAttachedRootSessionWithDiagnostic(tabId);
238
+ let attachedRootRecoveryRetriedAfterRegisterRoot = false;
239
+ let attachedRootRecoveryRegisterRootChanged;
240
+ let attachedRootRecoveryRegisterRootAttachTargetChanged;
241
+ if (!attachedRootRecovery.debuggerSession) {
242
+ const beforeRegisterRoot = captureRootRecordState();
243
+ await this.registerRootTab(tabId);
244
+ const afterRegisterRoot = captureRootRecordState();
245
+ attachedRootRecoveryRetriedAfterRegisterRoot = true;
246
+ attachedRootRecoveryRegisterRootChanged = beforeRegisterRoot.present !== afterRegisterRoot.present
247
+ || beforeRegisterRoot.rootSessionId !== afterRegisterRoot.rootSessionId
248
+ || beforeRegisterRoot.targetId !== afterRegisterRoot.targetId
249
+ || beforeRegisterRoot.attachTargetId !== afterRegisterRoot.attachTargetId;
250
+ attachedRootRecoveryRegisterRootAttachTargetChanged = beforeRegisterRoot.attachTargetId !== afterRegisterRoot.attachTargetId;
251
+ attachedRootRecovery = await this.ensureAttachedRootSessionWithDiagnostic(tabId);
252
+ }
253
+ if (!attachedRootRecovery.debuggerSession) {
254
+ const reattachedChild = await this.reattachRootAndAttachChildTarget(tabId, targetId);
255
+ if (reattachedChild.sessionId) {
256
+ this.clearChildAttachDiagnostic(tabId, targetId);
257
+ return reattachedChild.sessionId;
258
+ }
259
+ const terminalBranch = reattachedChild.terminalBranch
260
+ ?? (attachedRootRecoveryRetriedAfterRegisterRoot
261
+ ? "post_register_root_recovery"
262
+ : "initial_attached_root_recovery");
263
+ this.recordChildAttachDiagnostic(tabId, targetId, {
264
+ stage: "attached_root_unavailable",
265
+ ...(initialStage ? { initialStage } : {}),
266
+ ...(rootTargetRetryStage ? { rootTargetRetryStage } : {}),
267
+ ...(attachedRootRecovery.stage !== "attached"
268
+ ? { attachedRootRecoveryStage: attachedRootRecovery.stage }
269
+ : {}),
270
+ ...(attachedRootRecovery.attachTargetSource
271
+ ? { attachedRootRecoverySource: attachedRootRecovery.attachTargetSource }
272
+ : {}),
273
+ ...(attachedRootRecovery.attachTargetId
274
+ ? { attachedRootRecoveryAttachTargetId: attachedRootRecovery.attachTargetId }
275
+ : {}),
276
+ ...(attachedRootRecoveryRetriedAfterRegisterRoot
277
+ ? { attachedRootRecoveryRetriedAfterRegisterRoot: true }
278
+ : {}),
279
+ ...(typeof attachedRootRecoveryRegisterRootChanged === "boolean"
280
+ ? { attachedRootRecoveryRegisterRootChanged }
281
+ : {}),
282
+ ...(typeof attachedRootRecoveryRegisterRootAttachTargetChanged === "boolean"
283
+ ? { attachedRootRecoveryRegisterRootAttachTargetChanged }
284
+ : {}),
285
+ ...(typeof attachedRootRecovery.registerAttachedRootSessionCalled === "boolean"
286
+ ? { attachedRootRecoveryRegisterAttachedRootSessionCalled: attachedRootRecovery.registerAttachedRootSessionCalled }
287
+ : {}),
288
+ attachedRootUnavailableTerminalBranch: terminalBranch,
289
+ ...(reattachedChild.stage ? { reattachRecoveryStage: reattachedChild.stage } : {}),
290
+ ...(reattachedChild.reason ? { reattachRecoveryReason: reattachedChild.reason } : {}),
291
+ ...(attachedRootRecovery.reason
292
+ ? { attachedRootRecoveryReason: attachedRootRecovery.reason }
293
+ : {}),
294
+ ...((directError || attachedRootRecovery.reason || reattachedChild.reason)
295
+ ? {
296
+ reason: directError
297
+ ? getErrorMessage(directError)
298
+ : (attachedRootRecovery.reason ?? reattachedChild.reason)
299
+ }
300
+ : {})
301
+ });
302
+ if (directError) {
303
+ throw directError;
304
+ }
305
+ return null;
306
+ }
307
+ try {
308
+ const attachedRootSessionId = await this.attachChildTargetWithDebuggee(attachedRootRecovery.debuggerSession, targetId);
309
+ if (attachedRootSessionId) {
310
+ this.clearChildAttachDiagnostic(tabId, targetId);
311
+ return attachedRootSessionId;
312
+ }
313
+ await this.restoreRootAfterChildAttachFailure(tabId);
314
+ this.recordChildAttachDiagnostic(tabId, targetId, {
315
+ stage: "attached_root_attach_null",
316
+ ...(initialStage ? { initialStage } : {}),
317
+ ...(rootTargetRetryStage ? { rootTargetRetryStage } : {}),
318
+ ...(attachedRootRecovery.attachTargetSource
319
+ ? { attachedRootRecoverySource: attachedRootRecovery.attachTargetSource }
320
+ : {}),
321
+ ...(attachedRootRecovery.attachTargetId
322
+ ? { attachedRootRecoveryAttachTargetId: attachedRootRecovery.attachTargetId }
323
+ : {}),
324
+ ...(typeof attachedRootRecovery.registerAttachedRootSessionCalled === "boolean"
325
+ ? { attachedRootRecoveryRegisterAttachedRootSessionCalled: attachedRootRecovery.registerAttachedRootSessionCalled }
326
+ : {})
327
+ });
328
+ return null;
329
+ }
330
+ catch (error) {
331
+ await this.restoreRootAfterChildAttachFailure(tabId);
332
+ this.recordChildAttachDiagnostic(tabId, targetId, {
333
+ stage: "attached_root_attach_failed",
334
+ ...(initialStage ? { initialStage } : {}),
335
+ ...(rootTargetRetryStage ? { rootTargetRetryStage } : {}),
336
+ ...(attachedRootRecovery.attachTargetSource
337
+ ? { attachedRootRecoverySource: attachedRootRecovery.attachTargetSource }
338
+ : {}),
339
+ ...(attachedRootRecovery.attachTargetId
340
+ ? { attachedRootRecoveryAttachTargetId: attachedRootRecovery.attachTargetId }
341
+ : {}),
342
+ ...(typeof attachedRootRecovery.registerAttachedRootSessionCalled === "boolean"
343
+ ? { attachedRootRecoveryRegisterAttachedRootSessionCalled: attachedRootRecovery.registerAttachedRootSessionCalled }
344
+ : {}),
345
+ reason: getErrorMessage(error)
346
+ });
347
+ throw error;
348
+ }
349
+ }
350
+ getLastChildAttachDiagnostic(tabId, targetId) {
351
+ return this.childAttachDiagnostics.get(this.childAttachDiagnosticKey(tabId, targetId)) ?? null;
352
+ }
353
+ getLastRootRefreshDiagnostic(tabId) {
354
+ return this.rootRefreshDiagnostics.get(tabId) ?? null;
355
+ }
356
+ getLastRootAttachDiagnostic(tabId) {
357
+ return this.rootAttachDiagnostics.get(tabId) ?? null;
358
+ }
359
+ hasDebuggerSession(sessionId) {
360
+ return this.sessions.hasSession(sessionId);
361
+ }
362
+ registerChildSession(tabId, targetInfo, sessionId) {
363
+ this.sessions.registerChildSession(tabId, targetInfo, sessionId);
364
+ }
37
365
  async attachInternal(tabId, allowRetry) {
38
366
  if (this.debuggees.has(tabId)) {
367
+ this.clearRootAttachDiagnostic(tabId);
39
368
  this.updatePrimaryTab(tabId);
369
+ await this.pruneRootDebuggees(tabId);
40
370
  return;
41
371
  }
42
- const debuggee = { tabId };
372
+ const debuggee = await this.resolveRootDebuggee(tabId);
373
+ let attachedDebuggee = debuggee;
374
+ const displacedRoots = await this.detachConflictingRootDebuggees(tabId);
43
375
  this.debuggees.set(tabId, debuggee);
44
376
  this.ensureListeners();
45
377
  try {
46
- await this.runDebuggerAction((done) => {
47
- chrome.debugger.attach(debuggee, "1.3", done);
48
- });
49
- await this.ensureFlatSessionSupport(debuggee);
378
+ attachedDebuggee = await this.attachRootDebuggeeWithFallback(tabId, debuggee);
50
379
  const targetInfo = await this.registerRootTab(tabId);
51
380
  if (this.discoverTargets) {
52
- this.emitTargetCreated(targetInfo);
381
+ await this.applyDiscoverTargets(attachedDebuggee, true);
382
+ this.emitTargetCreated(tabId, targetInfo);
53
383
  }
54
384
  if (this.autoAttachOptions.autoAttach) {
55
- await this.applyAutoAttach(debuggee);
385
+ await this.applyAutoAttach(attachedDebuggee);
56
386
  this.emitRootAttached(targetInfo);
57
387
  }
58
388
  this.updatePrimaryTab(tabId);
389
+ this.commitDetachedRootDebuggees(displacedRoots);
390
+ await this.pruneRootDebuggees(tabId);
391
+ this.clearRootAttachDiagnostic(tabId);
59
392
  }
60
393
  catch (error) {
394
+ if (isAttachBlockedError(error) && await this.reuseAlreadyAttachedRootDebuggee(tabId)) {
395
+ this.commitDetachedRootDebuggees(displacedRoots);
396
+ await this.pruneRootDebuggees(tabId);
397
+ this.clearRootAttachDiagnostic(tabId);
398
+ return;
399
+ }
61
400
  this.debuggees.delete(tabId);
401
+ if (typeof attachedDebuggee.targetId === "string") {
402
+ this.rootTargetTabIds.delete(attachedDebuggee.targetId);
403
+ }
62
404
  if (this.debuggees.size === 0) {
63
405
  this.removeListeners();
64
406
  }
65
- await this.safeDetach(debuggee);
407
+ await this.safeDetach(attachedDebuggee);
66
408
  if (allowRetry && this.isStaleTabError(error)) {
67
409
  const attemptedTabIds = new Set([tabId]);
68
410
  let lastStaleError = error;
@@ -70,7 +412,9 @@ export class CDPRouter {
70
412
  if (activeTabId && !attemptedTabIds.has(activeTabId)) {
71
413
  attemptedTabIds.add(activeTabId);
72
414
  try {
73
- return await this.attachInternal(activeTabId, false);
415
+ await this.attachInternal(activeTabId, false);
416
+ this.commitDetachedRootDebuggees(displacedRoots);
417
+ return;
74
418
  }
75
419
  catch (candidateError) {
76
420
  if (!this.isStaleTabError(candidateError)) {
@@ -83,7 +427,9 @@ export class CDPRouter {
83
427
  if (fallbackTabId && !attemptedTabIds.has(fallbackTabId)) {
84
428
  attemptedTabIds.add(fallbackTabId);
85
429
  try {
86
- return await this.attachInternal(fallbackTabId, false);
430
+ await this.attachInternal(fallbackTabId, false);
431
+ this.commitDetachedRootDebuggees(displacedRoots);
432
+ return;
87
433
  }
88
434
  catch (candidateError) {
89
435
  if (!this.isStaleTabError(candidateError)) {
@@ -95,7 +441,9 @@ export class CDPRouter {
95
441
  try {
96
442
  const createdTab = await this.tabManager.createTab("about:blank", true);
97
443
  if (typeof createdTab.id === "number" && !attemptedTabIds.has(createdTab.id)) {
98
- return await this.attachInternal(createdTab.id, false);
444
+ await this.attachInternal(createdTab.id, false);
445
+ this.commitDetachedRootDebuggees(displacedRoots);
446
+ return;
99
447
  }
100
448
  }
101
449
  catch (candidateError) {
@@ -104,11 +452,122 @@ export class CDPRouter {
104
452
  }
105
453
  lastStaleError = candidateError;
106
454
  }
455
+ await this.restoreDetachedRootDebuggees(displacedRoots);
107
456
  throw lastStaleError;
108
457
  }
458
+ await this.restoreDetachedRootDebuggees(displacedRoots);
459
+ throw error;
460
+ }
461
+ }
462
+ async reuseAlreadyAttachedRootDebuggee(tabId) {
463
+ const targetInfo = await this.readDebuggerTargetInfo(tabId);
464
+ if (!targetInfo?.attached) {
465
+ return false;
466
+ }
467
+ const reusedDebuggee = {
468
+ tabId,
469
+ targetId: targetInfo.id,
470
+ attachBy: "targetId"
471
+ };
472
+ this.debuggees.set(tabId, reusedDebuggee);
473
+ this.ensureListeners();
474
+ try {
475
+ await this.ensureFlatSessionSupport(reusedDebuggee);
476
+ const registeredTarget = await this.registerRootTab(tabId);
477
+ if (this.discoverTargets) {
478
+ await this.applyDiscoverTargets(reusedDebuggee, true);
479
+ this.emitTargetCreated(tabId, registeredTarget);
480
+ }
481
+ if (this.autoAttachOptions.autoAttach) {
482
+ await this.applyAutoAttach(reusedDebuggee);
483
+ this.emitRootAttached(registeredTarget);
484
+ }
485
+ this.updatePrimaryTab(tabId);
486
+ return true;
487
+ }
488
+ catch {
489
+ this.debuggees.delete(tabId);
490
+ this.rootTargetTabIds.delete(targetInfo.id);
491
+ if (this.debuggees.size === 0) {
492
+ this.removeListeners();
493
+ }
494
+ return false;
495
+ }
496
+ }
497
+ async attachRootDebuggeeWithFallback(tabId, debuggee) {
498
+ await this.attachRootDebuggee(debuggee);
499
+ try {
500
+ await this.ensureFlatSessionSupport(debuggee);
501
+ return debuggee;
502
+ }
503
+ catch (error) {
504
+ this.recordRootAttachDiagnostic(tabId, {
505
+ origin: "flat_session_bootstrap",
506
+ stage: "flat_session_probe_failed",
507
+ attachBy: this.resolveRootAttachBy(debuggee),
508
+ probeMethod: "Target.setAutoAttach",
509
+ reason: getErrorMessage(error)
510
+ });
511
+ const targetAttachDebuggee = this.createTargetAttachRootDebuggee(tabId, debuggee, error);
512
+ if (!targetAttachDebuggee) {
513
+ throw error;
514
+ }
515
+ this.markExpectedRootDetach(tabId);
516
+ await this.safeDetach(debuggee);
517
+ this.debuggees.set(tabId, targetAttachDebuggee);
518
+ await this.attachRootDebuggee(targetAttachDebuggee);
519
+ try {
520
+ await this.ensureFlatSessionSupport(targetAttachDebuggee);
521
+ }
522
+ catch (fallbackError) {
523
+ this.recordRootAttachDiagnostic(tabId, {
524
+ origin: "flat_session_bootstrap",
525
+ stage: "fallback_flat_session_probe_failed",
526
+ attachBy: this.resolveRootAttachBy(targetAttachDebuggee),
527
+ probeMethod: "Target.setAutoAttach",
528
+ reason: getErrorMessage(fallbackError)
529
+ });
530
+ throw fallbackError;
531
+ }
532
+ return targetAttachDebuggee;
533
+ }
534
+ }
535
+ async attachRootDebuggee(debuggee) {
536
+ try {
537
+ await this.runDebuggerAction((done) => {
538
+ chrome.debugger.attach(this.toChromeDebuggee(debuggee), "1.3", done);
539
+ });
540
+ }
541
+ catch (error) {
542
+ if (typeof debuggee.tabId === "number") {
543
+ this.recordRootAttachDiagnostic(debuggee.tabId, {
544
+ origin: "root_attach",
545
+ stage: debuggee.attachBy === "targetId"
546
+ ? "fallback_root_debugger_attach_failed"
547
+ : "root_debugger_attach_failed",
548
+ attachBy: this.resolveRootAttachBy(debuggee),
549
+ reason: getErrorMessage(error)
550
+ });
551
+ }
109
552
  throw error;
110
553
  }
111
554
  }
555
+ createTargetAttachRootDebuggee(tabId, debuggee, error) {
556
+ if (!isAttachBlockedError(error)) {
557
+ return null;
558
+ }
559
+ const targetId = typeof debuggee.targetId === "string" && debuggee.targetId.length > 0
560
+ ? debuggee.targetId
561
+ : null;
562
+ if (!targetId || debuggee.attachBy === "targetId") {
563
+ return null;
564
+ }
565
+ return {
566
+ tabId,
567
+ targetId,
568
+ attachBy: "targetId"
569
+ };
570
+ }
112
571
  async detachAll() {
113
572
  const entries = Array.from(this.debuggees.entries());
114
573
  this.debuggees.clear();
@@ -145,7 +604,22 @@ export class CDPRouter {
145
604
  getAttachedTabIds() {
146
605
  return Array.from(this.debuggees.keys());
147
606
  }
607
+ isTabAttached(tabId) {
608
+ return this.debuggees.has(tabId);
609
+ }
610
+ getTabDebuggee(tabId) {
611
+ return this.sessions.getAttachedRootSession(tabId)?.debuggerSession
612
+ ?? this.debuggees.get(tabId)
613
+ ?? (() => {
614
+ const rootRecord = this.sessions.getByTabId(tabId);
615
+ if (!rootRecord) {
616
+ return null;
617
+ }
618
+ return this.sessions.getBySessionId(rootRecord.rootSessionId)?.debuggerSession ?? null;
619
+ })();
620
+ }
148
621
  async handleCommand(command) {
622
+ await this.prepareForNextClientIfNeeded();
149
623
  if (!this.callbacks)
150
624
  return;
151
625
  if (this.debuggees.size === 0) {
@@ -179,7 +653,8 @@ export class CDPRouter {
179
653
  this.respondError(command.id, "No tab attached");
180
654
  return;
181
655
  }
182
- this.respond(command.id, { sessionId: rootSession.sessionId });
656
+ const browserSessionId = await this.ensureBrowserSession(rootSession.tabId);
657
+ this.respond(command.id, { sessionId: browserSessionId ?? rootSession.sessionId });
183
658
  return;
184
659
  }
185
660
  case "Target.sendMessageToTarget":
@@ -193,9 +668,7 @@ export class CDPRouter {
193
668
  return;
194
669
  case "Target.getTargetInfo": {
195
670
  const targetId = typeof commandParams.targetId === "string" ? commandParams.targetId : "";
196
- const record = targetId ? this.sessions.getByTargetId(targetId) : null;
197
- const targetInfo = record?.targetInfo
198
- ?? (record?.kind === "root" ? this.sessions.getByTabId(record.tabId)?.targetInfo ?? null : null);
671
+ const targetInfo = this.resolveTargetInfo(targetId, sessionId);
199
672
  this.respond(command.id, { targetInfo });
200
673
  return;
201
674
  }
@@ -218,6 +691,9 @@ export class CDPRouter {
218
691
  await handleRoutedCommand(ctx, command.id, method, commandParams, sessionId);
219
692
  }
220
693
  }
694
+ markClientClosed() {
695
+ this.clientResetPending = true;
696
+ }
221
697
  buildCommandContext() {
222
698
  return {
223
699
  debuggees: this.debuggees,
@@ -232,9 +708,25 @@ export class CDPRouter {
232
708
  setDiscoverTargets: (value) => {
233
709
  this.discoverTargets = value;
234
710
  },
711
+ applyDiscoverTargets: this.applyDiscoverTargets.bind(this),
235
712
  respond: this.respond.bind(this),
236
713
  respondError: this.respondError.bind(this),
237
- emitTargetCreated: this.emitTargetCreated.bind(this),
714
+ emitEvent: (method, params, sessionId) => {
715
+ const tabId = sessionId
716
+ ? this.sessions.getBySessionId(sessionId)?.tabId ?? this.primaryTabId
717
+ : this.primaryTabId;
718
+ if (typeof tabId === "number") {
719
+ this.emitEvent(tabId, method, params, sessionId);
720
+ }
721
+ },
722
+ emitTargetCreated: (targetInfo) => {
723
+ const tabId = this.sessions.getByTargetId(targetInfo.targetId)?.tabId
724
+ ?? this.rootTargetTabIds.get(targetInfo.targetId)
725
+ ?? this.primaryTabId;
726
+ if (typeof tabId === "number") {
727
+ this.emitTargetCreated(tabId, targetInfo);
728
+ }
729
+ },
238
730
  emitRootAttached: this.emitRootAttached.bind(this),
239
731
  emitRootDetached: this.emitRootDetached.bind(this),
240
732
  resetRootAttached: this.resetRootAttached.bind(this),
@@ -243,18 +735,137 @@ export class CDPRouter {
243
735
  safeDetach: this.safeDetach.bind(this),
244
736
  attach: this.attach.bind(this),
245
737
  registerRootTab: this.registerRootTab.bind(this),
738
+ refreshRootTargetInfo: this.refreshRootTargetInfo.bind(this),
246
739
  applyAutoAttach: this.applyAutoAttach.bind(this),
247
740
  sendCommand: this.sendCommand.bind(this),
248
- getPrimaryDebuggee: this.getPrimaryDebuggee.bind(this)
741
+ getPrimaryDebuggee: this.getPrimaryDebuggee.bind(this),
742
+ resolveCommandDebuggee: this.resolveCommandDebuggee.bind(this)
249
743
  };
250
744
  }
251
745
  async registerRootTab(tabId) {
252
746
  const existing = this.sessions.getByTabId(tabId);
253
747
  const sessionId = existing?.rootSessionId ?? this.createRootSessionId();
254
748
  const targetInfo = await this.buildTargetInfo(tabId);
255
- this.sessions.registerRootTab(tabId, targetInfo, sessionId);
749
+ const debuggerSession = await this.resolveRootSessionDebuggee(tabId);
750
+ this.sessions.registerRootTab(tabId, targetInfo, sessionId, debuggerSession.targetId, debuggerSession);
256
751
  return targetInfo;
257
752
  }
753
+ async refreshRootTargetInfo(tabId) {
754
+ const existing = this.sessions.getByTabId(tabId);
755
+ const sessionId = existing?.rootSessionId ?? this.createRootSessionId();
756
+ const targetInfo = await this.buildTargetInfo(tabId);
757
+ const debuggerSession = await this.resolveRootSessionDebuggee(tabId);
758
+ const rootFrame = await this.readRootFrameInfo(tabId);
759
+ const refreshed = rootFrame
760
+ ? {
761
+ ...targetInfo,
762
+ targetId: rootFrame.id,
763
+ ...(rootFrame.url ? { url: rootFrame.url } : {})
764
+ }
765
+ : targetInfo;
766
+ this.sessions.registerRootTab(tabId, refreshed, sessionId, debuggerSession.targetId, debuggerSession);
767
+ return refreshed;
768
+ }
769
+ async prepareForNextClientIfNeeded(preferredTabIdHint) {
770
+ if (!this.clientResetPending) {
771
+ return null;
772
+ }
773
+ const preferredTabId = await this.resolvePreferredResetTabId(preferredTabIdHint);
774
+ const retainedPreferredRootTargetId = preferredTabId !== null
775
+ ? this.resolveRetainedRootTargetId(preferredTabId)
776
+ : null;
777
+ this.clientResetPending = false;
778
+ this.autoAttachOptions = { autoAttach: false, waitForDebuggerOnStart: false, flatten: true };
779
+ this.discoverTargets = false;
780
+ this.rootAttachedSessions.clear();
781
+ this.pendingTargetTabIds.clear();
782
+ this.quarantinedSessions.clear();
783
+ this.churnTracker.clear();
784
+ this.sessions.reset();
785
+ for (const [tabId, debuggee] of Array.from(this.debuggees.entries())) {
786
+ if (preferredTabId !== null && tabId === preferredTabId) {
787
+ continue;
788
+ }
789
+ this.debuggees.delete(tabId);
790
+ await this.safeDetach(debuggee);
791
+ }
792
+ if (preferredTabId === null) {
793
+ this.primaryTabId = null;
794
+ this.lastActiveTabId = null;
795
+ if (this.debuggees.size === 0) {
796
+ this.removeListeners();
797
+ }
798
+ return null;
799
+ }
800
+ const attachedPrimary = this.debuggees.get(preferredTabId);
801
+ if (attachedPrimary) {
802
+ this.updatePrimaryTab(preferredTabId);
803
+ // After a client reset, a preserved root tab is only a candidate anchor.
804
+ // Rebuild the real root attachment before any same-tab reuse is considered healthy.
805
+ await this.reattachRootDebuggee(preferredTabId, true);
806
+ const refreshedRoot = this.sessions.getByTabId(preferredTabId);
807
+ if (retainedPreferredRootTargetId && !refreshedRoot?.attachTargetId) {
808
+ this.sessions.setRootAttachTargetId(preferredTabId, retainedPreferredRootTargetId);
809
+ const refreshedDebuggee = this.debuggees.get(preferredTabId);
810
+ if (refreshedDebuggee && !refreshedDebuggee.targetId) {
811
+ refreshedDebuggee.targetId = retainedPreferredRootTargetId;
812
+ }
813
+ const refreshedRootSession = refreshedRoot
814
+ ? this.sessions.getBySessionId(refreshedRoot.rootSessionId)
815
+ : null;
816
+ if (refreshedRootSession?.debuggerSession && !refreshedRootSession.debuggerSession.targetId) {
817
+ refreshedRootSession.debuggerSession.targetId = retainedPreferredRootTargetId;
818
+ }
819
+ }
820
+ return preferredTabId;
821
+ }
822
+ await this.attachInternal(preferredTabId, true);
823
+ return preferredTabId;
824
+ }
825
+ async resolvePreferredResetTabId(preferredTabIdHint) {
826
+ const candidateTabIds = [];
827
+ const pushCandidate = (tabId) => {
828
+ if (typeof tabId === "number" && !candidateTabIds.includes(tabId)) {
829
+ candidateTabIds.push(tabId);
830
+ }
831
+ };
832
+ pushCandidate(preferredTabIdHint ?? null);
833
+ pushCandidate(await this.tabManager.getActiveTabId());
834
+ pushCandidate(this.lastActiveTabId);
835
+ pushCandidate(this.primaryTabId);
836
+ for (const tabId of candidateTabIds) {
837
+ if (await this.isUsableResetTab(tabId)) {
838
+ return tabId;
839
+ }
840
+ }
841
+ const firstHttpTabId = await this.tabManager.getFirstHttpTabId();
842
+ if (firstHttpTabId !== null) {
843
+ return firstHttpTabId;
844
+ }
845
+ for (const tabId of candidateTabIds) {
846
+ if (this.debuggees.has(tabId)) {
847
+ return tabId;
848
+ }
849
+ const tab = await this.tabManager.getTab(tabId);
850
+ if (tab) {
851
+ return tabId;
852
+ }
853
+ }
854
+ const [firstAttachedTabId] = this.debuggees.keys();
855
+ return typeof firstAttachedTabId === "number" ? firstAttachedTabId : null;
856
+ }
857
+ async isUsableResetTab(tabId) {
858
+ const tab = await this.tabManager.getTab(tabId);
859
+ if (!tab?.url) {
860
+ return false;
861
+ }
862
+ try {
863
+ return getRestrictionMessage(new URL(tab.url)) === null;
864
+ }
865
+ catch {
866
+ return false;
867
+ }
868
+ }
258
869
  updatePrimaryTab(tabId) {
259
870
  if (tabId === this.primaryTabId)
260
871
  return;
@@ -264,6 +875,50 @@ export class CDPRouter {
264
875
  }
265
876
  this.callbacks?.onPrimaryTabChange?.(tabId);
266
877
  }
878
+ async pruneRootDebuggees(primaryTabId) {
879
+ const displacedRoots = await this.detachConflictingRootDebuggees(primaryTabId);
880
+ this.commitDetachedRootDebuggees(displacedRoots);
881
+ }
882
+ async detachConflictingRootDebuggees(nextTabId) {
883
+ const staleTabIds = Array.from(this.debuggees.keys()).filter((tabId) => tabId !== nextTabId);
884
+ const displacedRoots = [];
885
+ for (const staleTabId of staleTabIds) {
886
+ const debuggee = this.debuggees.get(staleTabId);
887
+ if (!debuggee) {
888
+ continue;
889
+ }
890
+ this.debuggees.delete(staleTabId);
891
+ this.markExpectedRootDetach(staleTabId);
892
+ await this.safeDetach(debuggee);
893
+ displacedRoots.push({ tabId: staleTabId });
894
+ }
895
+ return displacedRoots;
896
+ }
897
+ commitDetachedRootDebuggees(detachedRoots) {
898
+ for (const { tabId } of detachedRoots) {
899
+ if (this.debuggees.has(tabId)) {
900
+ continue;
901
+ }
902
+ this.detachTabState(tabId);
903
+ }
904
+ }
905
+ async restoreDetachedRootDebuggees(detachedRoots) {
906
+ for (const { tabId } of detachedRoots) {
907
+ if (this.debuggees.has(tabId)) {
908
+ return;
909
+ }
910
+ try {
911
+ await this.attachInternal(tabId, false);
912
+ return;
913
+ }
914
+ catch (error) {
915
+ logError("cdp.restore_root_attach", error, {
916
+ code: "restore_root_attach_failed",
917
+ extra: { tabId }
918
+ });
919
+ }
920
+ }
921
+ }
267
922
  selectFallbackPrimary() {
268
923
  if (this.lastActiveTabId && this.debuggees.has(this.lastActiveTabId)) {
269
924
  return this.lastActiveTabId;
@@ -272,27 +927,467 @@ export class CDPRouter {
272
927
  return first ?? null;
273
928
  }
274
929
  getPrimaryDebuggee() {
275
- if (this.primaryTabId !== null && this.debuggees.has(this.primaryTabId)) {
276
- return { tabId: this.primaryTabId };
930
+ if (this.primaryTabId !== null) {
931
+ const primary = this.debuggees.get(this.primaryTabId);
932
+ if (primary) {
933
+ return primary;
934
+ }
277
935
  }
278
- const [first] = this.debuggees.keys();
279
- return typeof first === "number" ? { tabId: first } : null;
936
+ const [first] = this.debuggees.values();
937
+ return first ?? null;
938
+ }
939
+ async resolveCommandDebuggee(sessionId) {
940
+ if (!sessionId) {
941
+ return this.getPrimaryDebuggee();
942
+ }
943
+ const session = this.sessions.getBySessionId(sessionId);
944
+ if (!session) {
945
+ return null;
946
+ }
947
+ if (session.kind !== "root") {
948
+ return session.debuggerSession;
949
+ }
950
+ if (typeof session.debuggerSession.targetId === "string" && session.debuggerSession.targetId.length > 0) {
951
+ return session.debuggerSession;
952
+ }
953
+ const attached = await this.ensureAttachedRootSession(session.tabId);
954
+ return attached ?? session.debuggerSession;
280
955
  }
281
956
  async ensureRootSessionForPrimary() {
282
- const debuggee = this.getPrimaryDebuggee();
283
- if (!debuggee || typeof debuggee.tabId !== "number") {
957
+ const tabId = this.primaryTabId ?? this.resolveSourceTabId(this.getPrimaryDebuggee() ?? {});
958
+ if (typeof tabId !== "number") {
284
959
  return null;
285
960
  }
286
- const existing = this.sessions.getByTabId(debuggee.tabId);
961
+ const existing = this.sessions.getByTabId(tabId);
287
962
  if (existing) {
288
- return { sessionId: existing.rootSessionId, targetInfo: existing.targetInfo };
963
+ return { tabId, sessionId: existing.rootSessionId, targetInfo: existing.targetInfo };
289
964
  }
290
- const targetInfo = await this.registerRootTab(debuggee.tabId);
291
- const refreshed = this.sessions.getByTabId(debuggee.tabId);
965
+ const targetInfo = await this.registerRootTab(tabId);
966
+ const refreshed = this.sessions.getByTabId(tabId);
292
967
  if (!refreshed) {
293
968
  return null;
294
969
  }
295
- return { sessionId: refreshed.rootSessionId, targetInfo: targetInfo ?? refreshed.targetInfo };
970
+ return { tabId, sessionId: refreshed.rootSessionId, targetInfo: targetInfo ?? refreshed.targetInfo };
971
+ }
972
+ async ensureAttachedRootSession(tabId) {
973
+ return (await this.ensureAttachedRootSessionWithDiagnostic(tabId)).debuggerSession;
974
+ }
975
+ async ensureAttachedRootSessionWithDiagnostic(tabId) {
976
+ const existing = this.sessions.getAttachedRootSession(tabId);
977
+ if (existing) {
978
+ return {
979
+ debuggerSession: existing.debuggerSession,
980
+ stage: "attached",
981
+ registerAttachedRootSessionCalled: false
982
+ };
983
+ }
984
+ const record = this.sessions.getByTabId(tabId);
985
+ if (!record) {
986
+ return {
987
+ debuggerSession: null,
988
+ stage: "record_missing",
989
+ registerAttachedRootSessionCalled: false
990
+ };
991
+ }
992
+ const recordAttachTargetId = typeof record.attachTargetId === "string" && record.attachTargetId.length > 0
993
+ ? record.attachTargetId
994
+ : null;
995
+ const retainedAttachTargetId = recordAttachTargetId ?? this.resolveRetainedRootTargetId(tabId);
996
+ const liveAttachTargetId = await this.readDebuggerTargetId(tabId);
997
+ const attachTargetId = recordAttachTargetId ?? liveAttachTargetId ?? retainedAttachTargetId;
998
+ const attachTargetSource = recordAttachTargetId
999
+ ? "record"
1000
+ : liveAttachTargetId
1001
+ ? "debugger"
1002
+ : retainedAttachTargetId
1003
+ ? "debuggee"
1004
+ : undefined;
1005
+ if (!attachTargetId) {
1006
+ return {
1007
+ debuggerSession: null,
1008
+ stage: "session_missing",
1009
+ registerAttachedRootSessionCalled: false,
1010
+ reason: "attach_target_id_unavailable"
1011
+ };
1012
+ }
1013
+ if (recordAttachTargetId !== attachTargetId) {
1014
+ this.sessions.setRootAttachTargetId(tabId, attachTargetId);
1015
+ }
1016
+ const attachRootSession = async (debuggee) => {
1017
+ try {
1018
+ const attached = await this.sendCommandOnce(debuggee, "Target.attachToTarget", {
1019
+ targetId: attachTargetId,
1020
+ flatten: true
1021
+ });
1022
+ const sessionRecord = isRecord(attached) ? attached : {};
1023
+ return {
1024
+ sessionId: typeof sessionRecord.sessionId === "string" ? sessionRecord.sessionId : null
1025
+ };
1026
+ }
1027
+ catch (error) {
1028
+ return {
1029
+ sessionId: null,
1030
+ error
1031
+ };
1032
+ }
1033
+ };
1034
+ const initialAttachDebuggee = liveAttachTargetId
1035
+ ? { tabId }
1036
+ : { targetId: attachTargetId, attachBy: "targetId" };
1037
+ let attachAttempt = await attachRootSession(initialAttachDebuggee);
1038
+ if (typeof initialAttachDebuggee.tabId === "number"
1039
+ && !attachAttempt.sessionId
1040
+ && (!attachAttempt.error
1041
+ || isAttachBlockedError(attachAttempt.error)
1042
+ || this.isStaleTabError(attachAttempt.error))) {
1043
+ const initialFailure = attachAttempt;
1044
+ const retriedAttachAttempt = await attachRootSession({ targetId: attachTargetId, attachBy: "targetId" });
1045
+ attachAttempt = retriedAttachAttempt.sessionId
1046
+ || retriedAttachAttempt.error
1047
+ || !initialFailure.error
1048
+ ? retriedAttachAttempt
1049
+ : initialFailure;
1050
+ }
1051
+ try {
1052
+ const attachedSessionId = attachAttempt.sessionId;
1053
+ if (!attachedSessionId) {
1054
+ if (attachAttempt.error) {
1055
+ throw attachAttempt.error;
1056
+ }
1057
+ return {
1058
+ debuggerSession: null,
1059
+ stage: "attach_null",
1060
+ ...(attachTargetSource ? { attachTargetSource } : {}),
1061
+ attachTargetId,
1062
+ registerAttachedRootSessionCalled: false
1063
+ };
1064
+ }
1065
+ const attachedRecord = this.sessions.registerAttachedRootSession(tabId, attachedSessionId);
1066
+ if (attachedRecord?.debuggerSession) {
1067
+ attachedRecord.debuggerSession.targetId = attachTargetId;
1068
+ }
1069
+ return {
1070
+ debuggerSession: attachedRecord?.debuggerSession ?? {
1071
+ tabId,
1072
+ sessionId: attachedSessionId,
1073
+ targetId: attachTargetId
1074
+ },
1075
+ stage: "attached",
1076
+ ...(attachTargetSource ? { attachTargetSource } : {}),
1077
+ attachTargetId,
1078
+ registerAttachedRootSessionCalled: true
1079
+ };
1080
+ }
1081
+ catch (error) {
1082
+ return {
1083
+ debuggerSession: null,
1084
+ stage: "attach_failed",
1085
+ ...(attachTargetSource ? { attachTargetSource } : {}),
1086
+ attachTargetId,
1087
+ registerAttachedRootSessionCalled: false,
1088
+ reason: getErrorMessage(error)
1089
+ };
1090
+ }
1091
+ }
1092
+ async resolveRootSessionDebuggee(tabId) {
1093
+ await this.syncLiveRootTargetId(tabId);
1094
+ const attachedDebuggee = this.debuggees.get(tabId);
1095
+ if (attachedDebuggee?.targetId) {
1096
+ return attachedDebuggee;
1097
+ }
1098
+ const attachedRootSession = this.sessions.getAttachedRootSession(tabId);
1099
+ if (attachedRootSession?.debuggerSession) {
1100
+ return attachedRootSession.debuggerSession;
1101
+ }
1102
+ const existing = this.sessions.getByTabId(tabId);
1103
+ const existingSession = existing ? this.sessions.getBySessionId(existing.rootSessionId) : null;
1104
+ if (existingSession?.debuggerSession?.targetId) {
1105
+ return existingSession.debuggerSession;
1106
+ }
1107
+ return await this.resolveRootDebuggee(tabId);
1108
+ }
1109
+ async syncLiveRootTargetId(tabId) {
1110
+ const liveTargetId = await this.readDebuggerTargetId(tabId);
1111
+ if (!liveTargetId) {
1112
+ return;
1113
+ }
1114
+ const staleTargetIds = new Set();
1115
+ const attachedDebuggee = this.debuggees.get(tabId);
1116
+ if (typeof attachedDebuggee?.targetId === "string" && attachedDebuggee.targetId.length > 0) {
1117
+ staleTargetIds.add(attachedDebuggee.targetId);
1118
+ }
1119
+ const rootRecord = this.sessions.getByTabId(tabId);
1120
+ if (typeof rootRecord?.targetInfo.targetId === "string" && rootRecord.targetInfo.targetId.length > 0) {
1121
+ staleTargetIds.add(rootRecord.targetInfo.targetId);
1122
+ }
1123
+ if (typeof rootRecord?.attachTargetId === "string" && rootRecord.attachTargetId.length > 0) {
1124
+ staleTargetIds.add(rootRecord.attachTargetId);
1125
+ }
1126
+ for (const targetId of staleTargetIds) {
1127
+ if (targetId !== liveTargetId && this.rootTargetTabIds.get(targetId) === tabId) {
1128
+ this.rootTargetTabIds.delete(targetId);
1129
+ }
1130
+ }
1131
+ this.rootTargetTabIds.set(liveTargetId, tabId);
1132
+ this.sessions.setRootAttachTargetId(tabId, liveTargetId);
1133
+ if (attachedDebuggee) {
1134
+ attachedDebuggee.targetId = liveTargetId;
1135
+ }
1136
+ const rootSession = rootRecord
1137
+ ? this.sessions.getBySessionId(rootRecord.rootSessionId)
1138
+ : null;
1139
+ if (rootSession?.debuggerSession) {
1140
+ rootSession.debuggerSession.targetId = liveTargetId;
1141
+ }
1142
+ const attachedRootSession = this.sessions.getAttachedRootSession(tabId);
1143
+ if (attachedRootSession?.debuggerSession) {
1144
+ attachedRootSession.debuggerSession.targetId = liveTargetId;
1145
+ }
1146
+ }
1147
+ async resolveRootDebuggee(tabId) {
1148
+ const attachTargetId = await this.readDebuggerTargetId(tabId);
1149
+ if (attachTargetId) {
1150
+ this.rootTargetTabIds.set(attachTargetId, tabId);
1151
+ return { tabId, targetId: attachTargetId };
1152
+ }
1153
+ return { tabId };
1154
+ }
1155
+ async attachChildTargetWithDebuggee(debuggee, targetId) {
1156
+ const result = await this.sendCommandOnce(debuggee, "Target.attachToTarget", { targetId, flatten: true });
1157
+ const record = isRecord(result) ? result : {};
1158
+ const childSessionId = typeof record.sessionId === "string" ? record.sessionId : null;
1159
+ if (!childSessionId || typeof debuggee.tabId !== "number") {
1160
+ return childSessionId;
1161
+ }
1162
+ const targetInfo = this.resolveTargetInfo(targetId) ?? {
1163
+ targetId,
1164
+ type: "page",
1165
+ browserContextId: DEFAULT_BROWSER_CONTEXT_ID
1166
+ };
1167
+ this.sessions.registerChildSession(debuggee.tabId, targetInfo, childSessionId);
1168
+ return childSessionId;
1169
+ }
1170
+ async attachChildTargetWithRootTargetId(debuggee, targetId, priorError) {
1171
+ if (typeof debuggee.sessionId === "string" || typeof debuggee.tabId !== "number") {
1172
+ return {
1173
+ sessionId: null,
1174
+ stage: "missing_root_target_id"
1175
+ };
1176
+ }
1177
+ const rootTargetId = typeof debuggee.targetId === "string" && debuggee.targetId.length > 0
1178
+ ? debuggee.targetId
1179
+ : null;
1180
+ if (!rootTargetId) {
1181
+ return {
1182
+ sessionId: null,
1183
+ stage: "missing_root_target_id"
1184
+ };
1185
+ }
1186
+ try {
1187
+ const result = await this.sendCommandOnce({ targetId: rootTargetId }, "Target.attachToTarget", {
1188
+ targetId,
1189
+ flatten: true
1190
+ });
1191
+ const record = isRecord(result) ? result : {};
1192
+ const childSessionId = typeof record.sessionId === "string" ? record.sessionId : null;
1193
+ if (!childSessionId) {
1194
+ return {
1195
+ sessionId: null,
1196
+ stage: "attach_null"
1197
+ };
1198
+ }
1199
+ const targetInfo = this.resolveTargetInfo(targetId) ?? {
1200
+ targetId,
1201
+ type: "page",
1202
+ browserContextId: DEFAULT_BROWSER_CONTEXT_ID
1203
+ };
1204
+ this.sessions.registerChildSession(debuggee.tabId, targetInfo, childSessionId);
1205
+ return {
1206
+ sessionId: childSessionId,
1207
+ stage: "attached"
1208
+ };
1209
+ }
1210
+ catch (error) {
1211
+ if (priorError && isAttachBlockedError(priorError) && isAttachBlockedError(error)) {
1212
+ return {
1213
+ sessionId: null,
1214
+ stage: "attach_blocked",
1215
+ reason: getErrorMessage(error)
1216
+ };
1217
+ }
1218
+ if (priorError && this.isStaleTabError(priorError) && this.isStaleTabError(error)) {
1219
+ return {
1220
+ sessionId: null,
1221
+ stage: "attach_blocked",
1222
+ reason: getErrorMessage(error)
1223
+ };
1224
+ }
1225
+ throw error;
1226
+ }
1227
+ }
1228
+ async reattachRootAndAttachChildTarget(tabId, targetId) {
1229
+ let result = { sessionId: null };
1230
+ try {
1231
+ let tabScopedRootDebuggee;
1232
+ try {
1233
+ tabScopedRootDebuggee = await this.reattachTabScopedRootDebuggeeForPopup(tabId);
1234
+ }
1235
+ catch (error) {
1236
+ result = {
1237
+ sessionId: null,
1238
+ terminalBranch: "tab_scoped_root_reattach",
1239
+ stage: "tab_scoped_root_reattach_failed",
1240
+ reason: getErrorMessage(error)
1241
+ };
1242
+ return result;
1243
+ }
1244
+ try {
1245
+ const tabScopedSessionId = await this.attachChildTargetWithDebuggee(tabScopedRootDebuggee, targetId);
1246
+ if (tabScopedSessionId) {
1247
+ result = { sessionId: tabScopedSessionId };
1248
+ return result;
1249
+ }
1250
+ }
1251
+ catch (tabScopedError) {
1252
+ if (!isAttachBlockedError(tabScopedError) && !this.isStaleTabError(tabScopedError)) {
1253
+ result = {
1254
+ sessionId: null,
1255
+ terminalBranch: "tab_scoped_root_reattach",
1256
+ stage: "tab_scoped_attach_failed",
1257
+ reason: getErrorMessage(tabScopedError)
1258
+ };
1259
+ return result;
1260
+ }
1261
+ }
1262
+ try {
1263
+ await this.reattachRootDebuggee(tabId);
1264
+ const rootDebuggee = await this.resolveRootSessionDebuggee(tabId);
1265
+ const rootSessionId = await this.attachChildTargetWithDebuggee(rootDebuggee, targetId);
1266
+ result = rootSessionId
1267
+ ? { sessionId: rootSessionId }
1268
+ : {
1269
+ sessionId: null,
1270
+ terminalBranch: "root_debuggee_reattach",
1271
+ stage: "root_debuggee_attach_null"
1272
+ };
1273
+ }
1274
+ catch (error) {
1275
+ const blocked = isAttachBlockedError(error) || this.isStaleTabError(error);
1276
+ result = {
1277
+ sessionId: null,
1278
+ terminalBranch: "root_debuggee_reattach",
1279
+ stage: blocked ? "root_debuggee_attach_blocked" : "root_debuggee_attach_failed",
1280
+ reason: getErrorMessage(error)
1281
+ };
1282
+ }
1283
+ return result;
1284
+ }
1285
+ catch (error) {
1286
+ result = {
1287
+ sessionId: null,
1288
+ terminalBranch: "root_debuggee_reattach",
1289
+ stage: "root_debuggee_reattach_failed",
1290
+ reason: getErrorMessage(error)
1291
+ };
1292
+ return result;
1293
+ }
1294
+ finally {
1295
+ if (!result.sessionId) {
1296
+ await this.restoreRootAfterChildAttachFailure(tabId);
1297
+ }
1298
+ }
1299
+ }
1300
+ async reattachTabScopedRootDebuggeeForPopup(tabId) {
1301
+ const existing = this.debuggees.get(tabId);
1302
+ this.debuggees.delete(tabId);
1303
+ this.detachTabState(tabId);
1304
+ if (existing) {
1305
+ this.markExpectedRootDetach(tabId);
1306
+ await this.safeDetach(existing);
1307
+ }
1308
+ this.ensureListeners();
1309
+ const resolvedRootDebuggee = await this.resolveRootDebuggee(tabId);
1310
+ const tabScopedDebuggee = typeof resolvedRootDebuggee.targetId === "string" && resolvedRootDebuggee.targetId.length > 0
1311
+ ? { tabId, targetId: resolvedRootDebuggee.targetId }
1312
+ : { tabId };
1313
+ await this.attachRootDebuggee(tabScopedDebuggee);
1314
+ this.debuggees.set(tabId, tabScopedDebuggee);
1315
+ await this.registerRootTab(tabId);
1316
+ this.updatePrimaryTab(tabId);
1317
+ return await this.resolveRootSessionDebuggee(tabId);
1318
+ }
1319
+ async restoreRootAfterChildAttachFailure(tabId) {
1320
+ try {
1321
+ await this.refreshTabAttachment(tabId);
1322
+ }
1323
+ catch {
1324
+ // Best-effort root restoration only. The original popup attach error remains authoritative.
1325
+ }
1326
+ }
1327
+ async reattachRootDebuggee(tabId, allowRetry = false) {
1328
+ const existing = this.debuggees.get(tabId);
1329
+ this.debuggees.delete(tabId);
1330
+ this.detachTabState(tabId);
1331
+ if (existing) {
1332
+ this.markExpectedRootDetach(tabId);
1333
+ await this.safeDetach(existing);
1334
+ }
1335
+ await this.attachInternal(tabId, allowRetry);
1336
+ }
1337
+ async readDebuggerPageTargets(tabId) {
1338
+ const tab = await this.tabManager.getTab(tabId);
1339
+ const targets = await new Promise((resolve) => {
1340
+ chrome.debugger.getTargets((records) => {
1341
+ const lastError = chrome.runtime.lastError;
1342
+ if (lastError || !Array.isArray(records)) {
1343
+ resolve([]);
1344
+ return;
1345
+ }
1346
+ resolve(records);
1347
+ });
1348
+ });
1349
+ const pageTargets = targets.filter((target) => target.tabId === tabId && target.type === "page");
1350
+ return { tab: tab ?? null, pageTargets };
1351
+ }
1352
+ selectPreferredDebuggerTargetInfo(tab, pageTargets) {
1353
+ if (pageTargets.length === 0) {
1354
+ return null;
1355
+ }
1356
+ const liveTargets = pageTargets.some((target) => target.attached)
1357
+ ? pageTargets.filter((target) => target.attached)
1358
+ : pageTargets;
1359
+ const preferredByUrl = typeof tab?.url === "string"
1360
+ ? liveTargets.find((target) => target.url === tab.url)
1361
+ : null;
1362
+ const preferredByTitle = typeof tab?.title === "string"
1363
+ ? liveTargets.find((target) => target.title === tab.title)
1364
+ : null;
1365
+ return preferredByUrl ?? preferredByTitle ?? liveTargets[0] ?? null;
1366
+ }
1367
+ async readDebuggerTargetInfo(tabId) {
1368
+ const { tab, pageTargets } = await this.readDebuggerPageTargets(tabId);
1369
+ return this.selectPreferredDebuggerTargetInfo(tab, pageTargets);
1370
+ }
1371
+ async readDebuggerTargetId(tabId) {
1372
+ return (await this.readDebuggerTargetInfo(tabId))?.id ?? null;
1373
+ }
1374
+ resolveTargetInfo(targetId, sessionId) {
1375
+ if (targetId) {
1376
+ const record = this.sessions.getByTargetId(targetId);
1377
+ return record?.targetInfo
1378
+ ?? (record?.kind === "root" ? this.sessions.getByTabId(record.tabId)?.targetInfo ?? null : null);
1379
+ }
1380
+ if (sessionId) {
1381
+ const session = this.sessions.getBySessionId(sessionId);
1382
+ return session?.targetInfo
1383
+ ?? (session?.kind === "root" ? this.sessions.getByTabId(session.tabId)?.targetInfo ?? null : null);
1384
+ }
1385
+ return {
1386
+ targetId: DEFAULT_BROWSER_TARGET_ID,
1387
+ type: "browser",
1388
+ title: "OpenDevBrowser Relay",
1389
+ url: ""
1390
+ };
296
1391
  }
297
1392
  ensureListeners() {
298
1393
  if (this.listenersActive)
@@ -316,7 +1411,7 @@ export class CDPRouter {
316
1411
  autoAttach: false,
317
1412
  waitForDebuggerOnStart: false,
318
1413
  flatten: true
319
- });
1414
+ }, { preserveTab: true, refreshPreparedDebuggee: false });
320
1415
  this.flatSessionValidated = true;
321
1416
  }
322
1417
  catch (error) {
@@ -325,7 +1420,12 @@ export class CDPRouter {
325
1420
  throw new Error(`${FLAT_SESSION_ERROR} (${detail})`);
326
1421
  }
327
1422
  }
1423
+ async applyDiscoverTargets(debuggee, discover) {
1424
+ const rootTrackingDebuggee = this.resolveRootTrackingDebuggee(debuggee);
1425
+ await this.sendCommand(rootTrackingDebuggee, "Target.setDiscoverTargets", { discover }, { preserveTab: true, refreshPreparedDebuggee: false });
1426
+ }
328
1427
  async applyAutoAttach(debuggee) {
1428
+ const rootTrackingDebuggee = this.resolveRootTrackingDebuggee(debuggee);
329
1429
  const params = {
330
1430
  autoAttach: this.autoAttachOptions.autoAttach,
331
1431
  waitForDebuggerOnStart: this.autoAttachOptions.waitForDebuggerOnStart,
@@ -335,7 +1435,7 @@ export class CDPRouter {
335
1435
  params.filter = this.autoAttachOptions.filter;
336
1436
  }
337
1437
  try {
338
- await this.sendCommand(debuggee, "Target.setAutoAttach", params);
1438
+ await this.sendCommand(rootTrackingDebuggee, "Target.setAutoAttach", params, { preserveTab: true, refreshPreparedDebuggee: false });
339
1439
  }
340
1440
  catch (error) {
341
1441
  const detail = getErrorMessage(error);
@@ -343,6 +1443,24 @@ export class CDPRouter {
343
1443
  throw new Error(`${FLAT_SESSION_ERROR} (${detail})`);
344
1444
  }
345
1445
  }
1446
+ resolveRootTrackingDebuggee(debuggee) {
1447
+ const rootDebuggee = typeof debuggee.tabId === "number"
1448
+ ? this.debuggees.get(debuggee.tabId) ?? debuggee
1449
+ : debuggee;
1450
+ if (rootDebuggee.attachBy === "targetId"
1451
+ && typeof rootDebuggee.targetId === "string"
1452
+ && rootDebuggee.targetId.length > 0) {
1453
+ return {
1454
+ ...(typeof rootDebuggee.tabId === "number" ? { tabId: rootDebuggee.tabId } : {}),
1455
+ targetId: rootDebuggee.targetId,
1456
+ attachBy: "targetId"
1457
+ };
1458
+ }
1459
+ if (typeof rootDebuggee.sessionId === "string" && typeof rootDebuggee.tabId === "number") {
1460
+ return { tabId: rootDebuggee.tabId };
1461
+ }
1462
+ return rootDebuggee;
1463
+ }
346
1464
  async applyAutoAttachToChild(tabId, sessionId) {
347
1465
  if (!this.autoAttachOptions.autoAttach)
348
1466
  return;
@@ -403,16 +1521,29 @@ export class CDPRouter {
403
1521
  handleEvent(source, method, params) {
404
1522
  if (!this.callbacks)
405
1523
  return;
406
- const tabId = typeof source.tabId === "number" ? source.tabId : null;
1524
+ const tabId = this.resolveEventTabId(source, params);
407
1525
  if (tabId === null || !this.debuggees.has(tabId))
408
1526
  return;
409
1527
  if (method === "Target.receivedMessageFromTarget")
410
1528
  return;
1529
+ if (method === "Target.targetCreated" && params && isRecord(params)) {
1530
+ const targetInfo = isTargetInfo(params.targetInfo) ? params.targetInfo : null;
1531
+ if (targetInfo) {
1532
+ this.pendingTargetTabIds.set(targetInfo.targetId, tabId);
1533
+ }
1534
+ }
411
1535
  if (method === "Target.attachedToTarget" && params && isRecord(params)) {
412
1536
  const sessionId = typeof params.sessionId === "string" ? params.sessionId : null;
413
1537
  const targetInfo = isTargetInfo(params.targetInfo) ? params.targetInfo : null;
414
1538
  if (sessionId && targetInfo) {
415
- this.sessions.registerChildSession(tabId, targetInfo, sessionId);
1539
+ this.pendingTargetTabIds.delete(targetInfo.targetId);
1540
+ if (this.isAttachedRootTarget(tabId, targetInfo)) {
1541
+ this.sessions.setRootAttachTargetId(tabId, targetInfo.targetId);
1542
+ this.sessions.registerAttachedRootSession(tabId, sessionId);
1543
+ }
1544
+ else {
1545
+ this.sessions.registerChildSession(tabId, targetInfo, sessionId);
1546
+ }
416
1547
  this.quarantinedSessions.delete(sessionId);
417
1548
  this.applyAutoAttachToChild(tabId, sessionId).catch((error) => {
418
1549
  logError("cdp.apply_auto_attach_child", error, { code: "auto_attach_failed" });
@@ -424,6 +1555,10 @@ export class CDPRouter {
424
1555
  }
425
1556
  if (method === "Target.detachedFromTarget" && params && isRecord(params)) {
426
1557
  const detachedSessionId = typeof params.sessionId === "string" ? params.sessionId : null;
1558
+ const detachedTargetId = typeof params.targetId === "string" ? params.targetId : null;
1559
+ if (detachedTargetId) {
1560
+ this.pendingTargetTabIds.delete(detachedTargetId);
1561
+ }
427
1562
  if (detachedSessionId) {
428
1563
  const removed = this.sessions.removeBySessionId(detachedSessionId);
429
1564
  if (!removed) {
@@ -433,16 +1568,26 @@ export class CDPRouter {
433
1568
  }
434
1569
  }
435
1570
  }
1571
+ if (method === "Target.targetDestroyed" && params && isRecord(params)) {
1572
+ const targetId = typeof params.targetId === "string" ? params.targetId : null;
1573
+ if (targetId) {
1574
+ this.pendingTargetTabIds.delete(targetId);
1575
+ }
1576
+ }
436
1577
  const sourceSessionId = source.sessionId;
437
- if (typeof sourceSessionId === "string" && !this.sessions.hasSession(sourceSessionId)) {
1578
+ if (typeof sourceSessionId === "string"
1579
+ && !this.sessions.hasSession(sourceSessionId)
1580
+ && !this.shouldAllowUnknownSourceSession(method, source, tabId)) {
438
1581
  this.quarantineUnknownSession(tabId, sourceSessionId, method);
439
1582
  return;
440
1583
  }
441
1584
  const forwardSessionId = this.resolveForwardSessionId(method, source);
442
- this.emitEvent(method, params, forwardSessionId);
1585
+ this.emitEvent(tabId, method, params, forwardSessionId);
443
1586
  }
444
1587
  handleDetach(source, reason) {
445
- const tabId = typeof source.tabId === "number" ? source.tabId : null;
1588
+ if (this.consumeExpectedRootDetach(source))
1589
+ return;
1590
+ const tabId = this.resolveSourceTabId(source);
446
1591
  if (tabId === null || !this.debuggees.has(tabId))
447
1592
  return;
448
1593
  this.debuggees.delete(tabId);
@@ -453,14 +1598,29 @@ export class CDPRouter {
453
1598
  }
454
1599
  }
455
1600
  detachTabState(tabId) {
1601
+ this.expectedRootDetachDeadlines.delete(tabId);
1602
+ this.clearRootAttachDiagnostic(tabId);
456
1603
  const record = this.sessions.removeByTabId(tabId);
1604
+ for (const [key, diagnostic] of this.childAttachDiagnostics.entries()) {
1605
+ if (diagnostic.tabId === tabId) {
1606
+ this.childAttachDiagnostics.delete(key);
1607
+ }
1608
+ }
1609
+ for (const [targetId, mappedTabId] of this.pendingTargetTabIds.entries()) {
1610
+ if (mappedTabId === tabId) {
1611
+ this.pendingTargetTabIds.delete(targetId);
1612
+ }
1613
+ }
457
1614
  if (record) {
1615
+ if (record.attachTargetId) {
1616
+ this.rootTargetTabIds.delete(record.attachTargetId);
1617
+ }
458
1618
  this.rootAttachedSessions.delete(record.rootSessionId);
459
1619
  if (this.autoAttachOptions.autoAttach) {
460
- this.emitTargetDetached(record.rootSessionId, record.targetInfo.targetId);
1620
+ this.emitTargetDetached(record.tabId, record.rootSessionId, record.targetInfo.targetId);
461
1621
  }
462
1622
  if (this.discoverTargets) {
463
- this.emitTargetDestroyed(record.targetInfo.targetId);
1623
+ this.emitTargetDestroyed(record.tabId, record.targetInfo.targetId);
464
1624
  }
465
1625
  }
466
1626
  if (tabId === this.primaryTabId) {
@@ -476,14 +1636,186 @@ export class CDPRouter {
476
1636
  if (typeof sessionId === "string") {
477
1637
  return this.sessions.getBySessionId(sessionId) ? sessionId : undefined;
478
1638
  }
479
- const tabId = typeof source.tabId === "number" ? source.tabId : null;
1639
+ const tabId = this.resolveSourceTabId(source);
480
1640
  if (tabId === null)
481
1641
  return undefined;
482
1642
  const record = this.sessions.getByTabId(tabId);
483
1643
  if (!record)
484
1644
  return undefined;
1645
+ const browserSession = this.sessions.getBrowserSession(tabId);
1646
+ if (browserSession) {
1647
+ return browserSession.sessionId;
1648
+ }
485
1649
  return this.rootAttachedSessions.has(record.rootSessionId) ? record.rootSessionId : undefined;
486
1650
  }
1651
+ resolveEventTabId(source, params) {
1652
+ const sourceTabId = this.resolveSourceTabId(source);
1653
+ if (sourceTabId !== null) {
1654
+ return sourceTabId;
1655
+ }
1656
+ if (!params || !isRecord(params)) {
1657
+ return null;
1658
+ }
1659
+ const targetInfo = isTargetInfo(params.targetInfo) ? params.targetInfo : null;
1660
+ const openerTabId = this.resolveLinkedTargetTabId(targetInfo?.openerId);
1661
+ if (openerTabId !== null) {
1662
+ if (targetInfo) {
1663
+ this.pendingTargetTabIds.set(targetInfo.targetId, openerTabId);
1664
+ }
1665
+ return openerTabId;
1666
+ }
1667
+ if (targetInfo) {
1668
+ const targetTabId = this.resolveLinkedTargetTabId(targetInfo.targetId);
1669
+ if (targetTabId !== null) {
1670
+ return targetTabId;
1671
+ }
1672
+ }
1673
+ const targetId = typeof params.targetId === "string" ? params.targetId : null;
1674
+ if (targetId) {
1675
+ return this.resolveLinkedTargetTabId(targetId);
1676
+ }
1677
+ const sessionId = typeof params.sessionId === "string" ? params.sessionId : null;
1678
+ if (sessionId) {
1679
+ return this.sessions.getBySessionId(sessionId)?.tabId ?? null;
1680
+ }
1681
+ return null;
1682
+ }
1683
+ resolveSourceTabId(source) {
1684
+ if (typeof source.tabId === "number") {
1685
+ return source.tabId;
1686
+ }
1687
+ const sourceSessionId = source.sessionId;
1688
+ if (typeof sourceSessionId === "string") {
1689
+ const knownTabId = this.sessions.getBySessionId(sourceSessionId)?.tabId
1690
+ ?? this.quarantinedSessions.get(sourceSessionId)?.tabId
1691
+ ?? null;
1692
+ if (typeof knownTabId === "number") {
1693
+ return knownTabId;
1694
+ }
1695
+ if (this.debuggees.size === 1) {
1696
+ const nextAttachedTab = this.debuggees.keys().next();
1697
+ return nextAttachedTab.done ? null : nextAttachedTab.value;
1698
+ }
1699
+ return null;
1700
+ }
1701
+ if (typeof source.targetId === "string") {
1702
+ return this.resolveLinkedTargetTabId(source.targetId);
1703
+ }
1704
+ return null;
1705
+ }
1706
+ shouldAllowUnknownSourceSession(method, source, tabId) {
1707
+ if (method !== "Page.javascriptDialogOpening"
1708
+ && method !== "Page.javascriptDialogClosed"
1709
+ && method !== "Page.fileChooserOpened") {
1710
+ return false;
1711
+ }
1712
+ if (typeof source.tabId === "number") {
1713
+ return source.tabId === tabId;
1714
+ }
1715
+ if (typeof source.targetId === "string") {
1716
+ return this.resolveLinkedTargetTabId(source.targetId) === tabId;
1717
+ }
1718
+ if (typeof source.sessionId === "string") {
1719
+ return this.resolveSourceTabId(source) === tabId;
1720
+ }
1721
+ return false;
1722
+ }
1723
+ resolveLinkedTargetTabId(targetId) {
1724
+ if (typeof targetId !== "string" || targetId.length === 0) {
1725
+ return null;
1726
+ }
1727
+ return parseTabTargetAlias(targetId)
1728
+ ?? this.rootTargetTabIds.get(targetId)
1729
+ ?? this.sessions.getByTargetId(targetId)?.tabId
1730
+ ?? this.sessions.getTabIdByTargetAlias(targetId)
1731
+ ?? this.pendingTargetTabIds.get(targetId)
1732
+ ?? null;
1733
+ }
1734
+ isAttachedRootTarget(tabId, targetInfo) {
1735
+ if (targetInfo.type !== "page") {
1736
+ return false;
1737
+ }
1738
+ const record = this.sessions.getByTabId(tabId);
1739
+ if (!record) {
1740
+ return false;
1741
+ }
1742
+ if (record.attachTargetId && targetInfo.targetId === record.attachTargetId) {
1743
+ return true;
1744
+ }
1745
+ if (targetInfo.targetId === record.targetInfo.targetId) {
1746
+ return true;
1747
+ }
1748
+ if (record.targetInfo.url && targetInfo.url === record.targetInfo.url) {
1749
+ return true;
1750
+ }
1751
+ if (record.targetInfo.title && targetInfo.title === record.targetInfo.title) {
1752
+ return true;
1753
+ }
1754
+ return false;
1755
+ }
1756
+ childAttachDiagnosticKey(tabId, targetId) {
1757
+ return `${tabId}:${targetId}`;
1758
+ }
1759
+ resolveRootAttachBy(debuggee) {
1760
+ return debuggee.attachBy === "targetId" ? "targetId" : "tabId";
1761
+ }
1762
+ clearRootAttachDiagnostic(tabId) {
1763
+ this.rootAttachDiagnostics.delete(tabId);
1764
+ }
1765
+ recordRootAttachDiagnostic(tabId, diagnostic) {
1766
+ this.rootAttachDiagnostics.set(tabId, {
1767
+ tabId,
1768
+ at: Date.now(),
1769
+ ...diagnostic
1770
+ });
1771
+ }
1772
+ clearChildAttachDiagnostic(tabId, targetId) {
1773
+ this.childAttachDiagnostics.delete(this.childAttachDiagnosticKey(tabId, targetId));
1774
+ }
1775
+ recordChildAttachDiagnostic(tabId, targetId, diagnostic) {
1776
+ this.childAttachDiagnostics.set(this.childAttachDiagnosticKey(tabId, targetId), {
1777
+ tabId,
1778
+ targetId,
1779
+ at: Date.now(),
1780
+ ...diagnostic
1781
+ });
1782
+ }
1783
+ async captureRootRefreshDiagnostic(tabId, path, refreshCompleted, error) {
1784
+ const attachedDebuggee = this.debuggees.get(tabId) ?? null;
1785
+ const rootRecord = this.sessions.getByTabId(tabId);
1786
+ const rootSession = rootRecord
1787
+ ? this.sessions.getBySessionId(rootRecord.rootSessionId)
1788
+ : null;
1789
+ const debuggee = attachedDebuggee ?? rootSession?.debuggerSession ?? null;
1790
+ const rootTargetIdAfterRefresh = typeof debuggee?.targetId === "string" && debuggee.targetId.length > 0
1791
+ ? debuggee.targetId
1792
+ : undefined;
1793
+ let probeStage = refreshCompleted ? "missing_debuggee" : "skipped";
1794
+ let probeReason;
1795
+ if (refreshCompleted && debuggee) {
1796
+ try {
1797
+ await this.sendCommandOnce(debuggee, "Target.getTargets", {});
1798
+ probeStage = "succeeded";
1799
+ }
1800
+ catch (probeError) {
1801
+ probeStage = "failed";
1802
+ probeReason = getErrorMessage(probeError);
1803
+ }
1804
+ }
1805
+ this.rootRefreshDiagnostics.set(tabId, {
1806
+ tabId,
1807
+ path,
1808
+ refreshCompleted,
1809
+ debuggeePresentAfterRefresh: attachedDebuggee !== null,
1810
+ rootSessionPresentAfterRefresh: rootSession !== null,
1811
+ ...(rootTargetIdAfterRefresh ? { rootTargetIdAfterRefresh } : {}),
1812
+ probeMethod: "Target.getTargets",
1813
+ probeStage,
1814
+ ...(probeReason ? { probeReason } : {}),
1815
+ ...(error ? { reason: getErrorMessage(error) } : {}),
1816
+ at: Date.now()
1817
+ });
1818
+ }
487
1819
  async buildTargetInfo(tabId) {
488
1820
  const tab = await this.tabManager.getTab(tabId);
489
1821
  return {
@@ -494,14 +1826,32 @@ export class CDPRouter {
494
1826
  url: tab?.url ?? undefined
495
1827
  };
496
1828
  }
497
- emitTargetCreated(targetInfo) {
498
- this.emitEvent("Target.targetCreated", { targetInfo });
1829
+ async readRootFrameInfo(tabId) {
1830
+ try {
1831
+ const result = await Promise.race([
1832
+ this.sendCommand({ tabId }, "Page.getFrameTree", {}, { preserveTab: true }),
1833
+ new Promise((resolve) => {
1834
+ setTimeout(() => resolve(null), 750);
1835
+ })
1836
+ ]);
1837
+ const rootFrame = readRootFrame(result);
1838
+ if (!rootFrame) {
1839
+ return null;
1840
+ }
1841
+ return rootFrame;
1842
+ }
1843
+ catch {
1844
+ return null;
1845
+ }
1846
+ }
1847
+ emitTargetCreated(tabId, targetInfo) {
1848
+ this.emitEvent(tabId, "Target.targetCreated", { targetInfo });
499
1849
  }
500
- emitTargetDestroyed(targetId) {
501
- this.emitEvent("Target.targetDestroyed", { targetId });
1850
+ emitTargetDestroyed(tabId, targetId) {
1851
+ this.emitEvent(tabId, "Target.targetDestroyed", { targetId });
502
1852
  }
503
- emitTargetDetached(sessionId, targetId) {
504
- this.emitEvent("Target.detachedFromTarget", { sessionId, targetId });
1853
+ emitTargetDetached(tabId, sessionId, targetId) {
1854
+ this.emitEvent(tabId, "Target.detachedFromTarget", { sessionId, targetId });
505
1855
  }
506
1856
  emitRootAttached(targetInfo) {
507
1857
  const record = this.sessions.getByTargetId(targetInfo.targetId);
@@ -510,7 +1860,7 @@ export class CDPRouter {
510
1860
  if (this.rootAttachedSessions.has(record.sessionId))
511
1861
  return;
512
1862
  this.rootAttachedSessions.add(record.sessionId);
513
- this.emitEvent("Target.attachedToTarget", {
1863
+ this.emitEvent(record.tabId, "Target.attachedToTarget", {
514
1864
  sessionId: record.sessionId,
515
1865
  targetInfo,
516
1866
  waitingForDebugger: false
@@ -524,7 +1874,7 @@ export class CDPRouter {
524
1874
  if (!this.rootAttachedSessions.has(record.sessionId))
525
1875
  continue;
526
1876
  this.rootAttachedSessions.delete(record.sessionId);
527
- this.emitTargetDetached(record.sessionId, targetInfo.targetId);
1877
+ this.emitTargetDetached(record.tabId, record.sessionId, targetInfo.targetId);
528
1878
  }
529
1879
  }
530
1880
  resetRootAttached() {
@@ -535,25 +1885,76 @@ export class CDPRouter {
535
1885
  this.sessionCounter += 1;
536
1886
  return sessionId;
537
1887
  }
538
- async sendCommand(debuggee, method, params) {
1888
+ createBrowserSessionId() {
1889
+ const sessionId = `pw-browser-${this.sessionCounter}`;
1890
+ this.sessionCounter += 1;
1891
+ return sessionId;
1892
+ }
1893
+ async ensureBrowserSession(tabId) {
1894
+ const existing = this.sessions.getBrowserSession(tabId);
1895
+ if (existing) {
1896
+ return existing.sessionId;
1897
+ }
1898
+ const browserSessionId = this.createBrowserSessionId();
1899
+ return this.sessions.registerBrowserSession(tabId, browserSessionId)?.sessionId ?? null;
1900
+ }
1901
+ async sendCommand(debuggee, method, params, options = {}) {
1902
+ const preferredTabId = this.resolveSourceTabId(debuggee);
1903
+ const refreshCommandDebuggee = options.refreshPreparedDebuggee !== false
1904
+ && this.shouldRefreshPreparedCommandDebuggee(debuggee, preferredTabId);
1905
+ await this.prepareForNextClientIfNeeded(preferredTabId);
1906
+ const commandDebuggee = refreshCommandDebuggee && preferredTabId !== null
1907
+ ? (this.getTabDebuggee(preferredTabId) ?? debuggee)
1908
+ : debuggee;
539
1909
  try {
540
- return await this.sendCommandOnce(debuggee, method, params);
1910
+ return await this.sendCommandOnce(commandDebuggee, method, params);
541
1911
  }
542
1912
  catch (error) {
543
- const hasChildSession = typeof debuggee.sessionId === "string";
1913
+ const hasChildSession = typeof commandDebuggee.sessionId === "string";
544
1914
  if (!this.isStaleTabError(error) || hasChildSession) {
545
1915
  throw error;
546
1916
  }
547
- const recovered = await this.recoverFromStaleTab(debuggee);
1917
+ const retainedRootDebuggee = this.resolveRetainedRootTargetDebuggee(commandDebuggee);
1918
+ if (retainedRootDebuggee) {
1919
+ try {
1920
+ return await this.sendCommandOnce(retainedRootDebuggee, method, params);
1921
+ }
1922
+ catch (retainedError) {
1923
+ if (!this.isStaleTabError(retainedError)) {
1924
+ throw retainedError;
1925
+ }
1926
+ error = retainedError;
1927
+ }
1928
+ }
1929
+ const recovered = await this.recoverFromStaleTab(commandDebuggee, options.preserveTab === true);
548
1930
  if (!recovered) {
549
1931
  throw error;
550
1932
  }
551
1933
  return await this.sendCommandOnce(recovered, method, params);
552
1934
  }
553
1935
  }
1936
+ shouldRefreshPreparedCommandDebuggee(debuggee, preferredTabId) {
1937
+ if (preferredTabId === null) {
1938
+ return false;
1939
+ }
1940
+ if (typeof debuggee.sessionId !== "string") {
1941
+ return true;
1942
+ }
1943
+ const sessionRecord = this.sessions.getBySessionId(debuggee.sessionId);
1944
+ const rootRecord = this.sessions.getByTabId(preferredTabId);
1945
+ return Boolean(sessionRecord
1946
+ && rootRecord
1947
+ && sessionRecord.tabId === preferredTabId
1948
+ && sessionRecord.targetId === rootRecord.targetInfo.targetId);
1949
+ }
554
1950
  async sendCommandOnce(debuggee, method, params) {
1951
+ const chromeDebuggee = this.toChromeDebuggee(debuggee);
1952
+ const sendCommandFn = chrome.debugger.sendCommand;
1953
+ if (!("mock" in sendCommandFn) || chrome.debugger.sendCommand.length < 4) {
1954
+ return await chrome.debugger.sendCommand(chromeDebuggee, method, params);
1955
+ }
555
1956
  return await new Promise((resolve, reject) => {
556
- chrome.debugger.sendCommand(debuggee, method, params, (result) => {
1957
+ chrome.debugger.sendCommand(chromeDebuggee, method, params, (result) => {
557
1958
  const lastError = chrome.runtime.lastError;
558
1959
  if (lastError) {
559
1960
  reject(new Error(lastError.message));
@@ -563,25 +1964,96 @@ export class CDPRouter {
563
1964
  });
564
1965
  });
565
1966
  }
566
- async recoverFromStaleTab(debuggee) {
567
- const staleTabId = typeof debuggee.tabId === "number" ? debuggee.tabId : null;
1967
+ resolveRetainedRootTargetDebuggee(debuggee) {
1968
+ if (typeof debuggee.sessionId === "string") {
1969
+ return null;
1970
+ }
1971
+ const staleTabId = typeof debuggee.tabId === "number"
1972
+ ? debuggee.tabId
1973
+ : (typeof debuggee.targetId === "string" ? this.rootTargetTabIds.get(debuggee.targetId) ?? null : null);
1974
+ if (staleTabId === null) {
1975
+ return null;
1976
+ }
1977
+ const targetId = this.resolveRetainedRootTargetId(staleTabId);
1978
+ if (!targetId) {
1979
+ return null;
1980
+ }
1981
+ if (typeof debuggee.targetId === "string" && debuggee.targetId === targetId && typeof debuggee.tabId !== "number") {
1982
+ return null;
1983
+ }
1984
+ return { targetId };
1985
+ }
1986
+ resolveRetainedRootTargetId(tabId) {
1987
+ const attachedDebuggee = this.debuggees.get(tabId);
1988
+ if (typeof attachedDebuggee?.targetId === "string" && attachedDebuggee.targetId.length > 0) {
1989
+ return attachedDebuggee.targetId;
1990
+ }
1991
+ const rootRecord = this.sessions.getByTabId(tabId);
1992
+ const rootSession = rootRecord
1993
+ ? this.sessions.getBySessionId(rootRecord.rootSessionId)
1994
+ : null;
1995
+ if (typeof rootSession?.debuggerSession?.targetId === "string" && rootSession.debuggerSession.targetId.length > 0) {
1996
+ return rootSession.debuggerSession.targetId;
1997
+ }
1998
+ if (typeof rootRecord?.attachTargetId === "string" && rootRecord.attachTargetId.length > 0) {
1999
+ return rootRecord.attachTargetId;
2000
+ }
2001
+ return null;
2002
+ }
2003
+ async recoverFromStaleTab(debuggee, preserveTab) {
2004
+ const staleTabId = typeof debuggee.tabId === "number"
2005
+ ? debuggee.tabId
2006
+ : (typeof debuggee.targetId === "string" ? this.rootTargetTabIds.get(debuggee.targetId) ?? null : null);
568
2007
  if (staleTabId === null) {
569
2008
  return null;
570
2009
  }
571
- this.debuggees.delete(staleTabId);
572
- this.detachTabState(staleTabId);
573
- await this.safeDetach({ tabId: staleTabId });
574
2010
  try {
575
- await this.attachInternal(staleTabId, true);
2011
+ await this.reattachRootDebuggee(staleTabId, !preserveTab);
576
2012
  }
577
2013
  catch {
578
2014
  return null;
579
2015
  }
580
- return this.getPrimaryDebuggee();
2016
+ return preserveTab
2017
+ ? this.debuggees.get(staleTabId) ?? null
2018
+ : this.debuggees.get(staleTabId) ?? this.getPrimaryDebuggee();
581
2019
  }
582
2020
  isStaleTabError(error) {
583
2021
  const message = error instanceof Error ? error.message : String(error);
584
- return message.includes("No tab with given id");
2022
+ return STALE_TAB_ERROR_MARKERS.some((marker) => message.includes(marker));
2023
+ }
2024
+ markExpectedRootDetach(tabId) {
2025
+ this.expectedRootDetachDeadlines.set(tabId, Date.now() + 1000);
2026
+ }
2027
+ consumeExpectedRootDetach(source) {
2028
+ const tabId = this.resolveSourceTabId(source);
2029
+ if (tabId === null) {
2030
+ return false;
2031
+ }
2032
+ const deadline = this.expectedRootDetachDeadlines.get(tabId);
2033
+ if (typeof deadline !== "number") {
2034
+ return false;
2035
+ }
2036
+ const sourceSessionId = source.sessionId;
2037
+ if (typeof sourceSessionId === "string") {
2038
+ return false;
2039
+ }
2040
+ this.expectedRootDetachDeadlines.delete(tabId);
2041
+ return deadline >= Date.now();
2042
+ }
2043
+ toChromeDebuggee(debuggee) {
2044
+ if (typeof debuggee.sessionId === "string") {
2045
+ if (typeof debuggee.targetId === "string" && debuggee.targetId.length > 0) {
2046
+ return { targetId: debuggee.targetId, sessionId: debuggee.sessionId };
2047
+ }
2048
+ return { tabId: debuggee.tabId, sessionId: debuggee.sessionId };
2049
+ }
2050
+ if (debuggee.attachBy === "targetId" && typeof debuggee.targetId === "string" && debuggee.targetId.length > 0) {
2051
+ return { targetId: debuggee.targetId };
2052
+ }
2053
+ if (typeof debuggee.tabId === "number") {
2054
+ return { tabId: debuggee.tabId };
2055
+ }
2056
+ return { targetId: debuggee.targetId };
585
2057
  }
586
2058
  async runDebuggerAction(action) {
587
2059
  return new Promise((resolve, reject) => {
@@ -598,7 +2070,7 @@ export class CDPRouter {
598
2070
  async safeDetach(debuggee) {
599
2071
  try {
600
2072
  await this.runDebuggerAction((done) => {
601
- chrome.debugger.detach(debuggee, done);
2073
+ chrome.debugger.detach(this.toChromeDebuggee(debuggee), done);
602
2074
  });
603
2075
  }
604
2076
  catch (error) {
@@ -615,10 +2087,14 @@ export class CDPRouter {
615
2087
  return;
616
2088
  this.callbacks.onResponse({ id, error: { message }, ...(sessionId ? { sessionId } : {}) });
617
2089
  }
618
- emitEvent(method, params, sessionId) {
2090
+ emitEvent(tabId, method, params, sessionId) {
2091
+ const event = { tabId, method, ...(typeof params !== "undefined" ? { params } : {}), ...(sessionId ? { sessionId } : {}) };
2092
+ for (const listener of this.eventListeners) {
2093
+ listener(event);
2094
+ }
619
2095
  if (!this.callbacks)
620
2096
  return;
621
- const payload = { method, params };
2097
+ const payload = { method, ...(typeof params !== "undefined" ? { params } : {}) };
622
2098
  if (sessionId) {
623
2099
  payload.sessionId = sessionId;
624
2100
  }
@@ -628,6 +2104,17 @@ export class CDPRouter {
628
2104
  const isTargetInfo = (value) => {
629
2105
  return isRecord(value) && typeof value.targetId === "string" && typeof value.type === "string";
630
2106
  };
2107
+ const parseTabTargetAlias = (targetId) => {
2108
+ if (typeof targetId !== "string") {
2109
+ return null;
2110
+ }
2111
+ const match = /^tab-(\d+)$/.exec(targetId);
2112
+ if (!match) {
2113
+ return null;
2114
+ }
2115
+ const parsed = Number.parseInt(match[1] ?? "", 10);
2116
+ return Number.isFinite(parsed) ? parsed : null;
2117
+ };
631
2118
  const getErrorMessage = (error) => {
632
2119
  if (error instanceof Error) {
633
2120
  return error.message;
@@ -637,3 +2124,20 @@ const getErrorMessage = (error) => {
637
2124
  const isRecord = (value) => {
638
2125
  return typeof value === "object" && value !== null;
639
2126
  };
2127
+ const readRootFrame = (value) => {
2128
+ if (!isRecord(value)) {
2129
+ return null;
2130
+ }
2131
+ const frameTree = value.frameTree;
2132
+ if (!isRecord(frameTree)) {
2133
+ return null;
2134
+ }
2135
+ const frame = frameTree.frame;
2136
+ if (!isRecord(frame) || typeof frame.id !== "string") {
2137
+ return null;
2138
+ }
2139
+ return {
2140
+ id: frame.id,
2141
+ ...(typeof frame.url === "string" ? { url: frame.url } : {})
2142
+ };
2143
+ };