openchrome-mcp 1.0.2

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 (396) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +180 -0
  3. package/assets/demo.svg +278 -0
  4. package/assets/mascot.png +0 -0
  5. package/dist/cdp/client.d.ts +219 -0
  6. package/dist/cdp/client.d.ts.map +1 -0
  7. package/dist/cdp/client.js +804 -0
  8. package/dist/cdp/client.js.map +1 -0
  9. package/dist/cdp/connection-pool.d.ts +116 -0
  10. package/dist/cdp/connection-pool.d.ts.map +1 -0
  11. package/dist/cdp/connection-pool.js +393 -0
  12. package/dist/cdp/connection-pool.js.map +1 -0
  13. package/dist/cdp/screenshot-scheduler.d.ts +54 -0
  14. package/dist/cdp/screenshot-scheduler.d.ts.map +1 -0
  15. package/dist/cdp/screenshot-scheduler.js +87 -0
  16. package/dist/cdp/screenshot-scheduler.js.map +1 -0
  17. package/dist/chrome/launcher.d.ts +68 -0
  18. package/dist/chrome/launcher.d.ts.map +1 -0
  19. package/dist/chrome/launcher.js +523 -0
  20. package/dist/chrome/launcher.js.map +1 -0
  21. package/dist/chrome/pool.d.ts +54 -0
  22. package/dist/chrome/pool.d.ts.map +1 -0
  23. package/dist/chrome/pool.js +301 -0
  24. package/dist/chrome/pool.js.map +1 -0
  25. package/dist/chrome/profile-detector.d.ts +52 -0
  26. package/dist/chrome/profile-detector.d.ts.map +1 -0
  27. package/dist/chrome/profile-detector.js +246 -0
  28. package/dist/chrome/profile-detector.js.map +1 -0
  29. package/dist/cli/claude-session.d.ts +11 -0
  30. package/dist/cli/claude-session.js +349 -0
  31. package/dist/cli/claude-session.js.map +1 -0
  32. package/dist/cli/index.d.ts +14 -0
  33. package/dist/cli/index.js +858 -0
  34. package/dist/cli/index.js.map +1 -0
  35. package/dist/cli/install.d.ts +16 -0
  36. package/dist/cli/install.js +185 -0
  37. package/dist/cli/install.js.map +1 -0
  38. package/dist/cli/uninstall.d.ts +7 -0
  39. package/dist/cli/uninstall.js +126 -0
  40. package/dist/cli/uninstall.js.map +1 -0
  41. package/dist/cli/update-check.d.ts +9 -0
  42. package/dist/cli/update-check.js +141 -0
  43. package/dist/cli/update-check.js.map +1 -0
  44. package/dist/config/config-recovery.d.ts +69 -0
  45. package/dist/config/config-recovery.d.ts.map +1 -0
  46. package/dist/config/config-recovery.js +302 -0
  47. package/dist/config/config-recovery.js.map +1 -0
  48. package/dist/config/global.d.ts +51 -0
  49. package/dist/config/global.d.ts.map +1 -0
  50. package/dist/config/global.js +24 -0
  51. package/dist/config/global.js.map +1 -0
  52. package/dist/config/index.d.ts +7 -0
  53. package/dist/config/index.d.ts.map +1 -0
  54. package/dist/config/index.js +23 -0
  55. package/dist/config/index.js.map +1 -0
  56. package/dist/config/session-isolator.d.ts +76 -0
  57. package/dist/config/session-isolator.d.ts.map +1 -0
  58. package/dist/config/session-isolator.js +268 -0
  59. package/dist/config/session-isolator.js.map +1 -0
  60. package/dist/dashboard/activity-tracker.d.ts +76 -0
  61. package/dist/dashboard/activity-tracker.d.ts.map +1 -0
  62. package/dist/dashboard/activity-tracker.js +219 -0
  63. package/dist/dashboard/activity-tracker.js.map +1 -0
  64. package/dist/dashboard/ansi.d.ts +117 -0
  65. package/dist/dashboard/ansi.d.ts.map +1 -0
  66. package/dist/dashboard/ansi.js +199 -0
  67. package/dist/dashboard/ansi.js.map +1 -0
  68. package/dist/dashboard/index.d.ts +110 -0
  69. package/dist/dashboard/index.d.ts.map +1 -0
  70. package/dist/dashboard/index.js +412 -0
  71. package/dist/dashboard/index.js.map +1 -0
  72. package/dist/dashboard/keyboard-handler.d.ts +43 -0
  73. package/dist/dashboard/keyboard-handler.d.ts.map +1 -0
  74. package/dist/dashboard/keyboard-handler.js +230 -0
  75. package/dist/dashboard/keyboard-handler.js.map +1 -0
  76. package/dist/dashboard/operation-controller.d.ts +76 -0
  77. package/dist/dashboard/operation-controller.d.ts.map +1 -0
  78. package/dist/dashboard/operation-controller.js +167 -0
  79. package/dist/dashboard/operation-controller.js.map +1 -0
  80. package/dist/dashboard/renderer.d.ts +76 -0
  81. package/dist/dashboard/renderer.d.ts.map +1 -0
  82. package/dist/dashboard/renderer.js +193 -0
  83. package/dist/dashboard/renderer.js.map +1 -0
  84. package/dist/dashboard/types.d.ts +56 -0
  85. package/dist/dashboard/types.d.ts.map +1 -0
  86. package/dist/dashboard/types.js +12 -0
  87. package/dist/dashboard/types.js.map +1 -0
  88. package/dist/dashboard/views/main-view.d.ts +23 -0
  89. package/dist/dashboard/views/main-view.d.ts.map +1 -0
  90. package/dist/dashboard/views/main-view.js +143 -0
  91. package/dist/dashboard/views/main-view.js.map +1 -0
  92. package/dist/dashboard/views/sessions-view.d.ts +22 -0
  93. package/dist/dashboard/views/sessions-view.d.ts.map +1 -0
  94. package/dist/dashboard/views/sessions-view.js +104 -0
  95. package/dist/dashboard/views/sessions-view.js.map +1 -0
  96. package/dist/dashboard/views/tabs-view.d.ts +21 -0
  97. package/dist/dashboard/views/tabs-view.d.ts.map +1 -0
  98. package/dist/dashboard/views/tabs-view.js +92 -0
  99. package/dist/dashboard/views/tabs-view.js.map +1 -0
  100. package/dist/hints/hint-engine.d.ts +77 -0
  101. package/dist/hints/hint-engine.d.ts.map +1 -0
  102. package/dist/hints/hint-engine.js +191 -0
  103. package/dist/hints/hint-engine.js.map +1 -0
  104. package/dist/hints/index.d.ts +8 -0
  105. package/dist/hints/index.d.ts.map +1 -0
  106. package/dist/hints/index.js +11 -0
  107. package/dist/hints/index.js.map +1 -0
  108. package/dist/hints/pattern-learner.d.ts +76 -0
  109. package/dist/hints/pattern-learner.d.ts.map +1 -0
  110. package/dist/hints/pattern-learner.js +254 -0
  111. package/dist/hints/pattern-learner.js.map +1 -0
  112. package/dist/hints/rules/composite-suggestions.d.ts +6 -0
  113. package/dist/hints/rules/composite-suggestions.d.ts.map +1 -0
  114. package/dist/hints/rules/composite-suggestions.js +66 -0
  115. package/dist/hints/rules/composite-suggestions.js.map +1 -0
  116. package/dist/hints/rules/error-recovery.d.ts +7 -0
  117. package/dist/hints/rules/error-recovery.d.ts.map +1 -0
  118. package/dist/hints/rules/error-recovery.js +55 -0
  119. package/dist/hints/rules/error-recovery.js.map +1 -0
  120. package/dist/hints/rules/learned-rules.d.ts +13 -0
  121. package/dist/hints/rules/learned-rules.d.ts.map +1 -0
  122. package/dist/hints/rules/learned-rules.js +27 -0
  123. package/dist/hints/rules/learned-rules.js.map +1 -0
  124. package/dist/hints/rules/repetition-detection.d.ts +7 -0
  125. package/dist/hints/rules/repetition-detection.d.ts.map +1 -0
  126. package/dist/hints/rules/repetition-detection.js +82 -0
  127. package/dist/hints/rules/repetition-detection.js.map +1 -0
  128. package/dist/hints/rules/sequence-detection.d.ts +6 -0
  129. package/dist/hints/rules/sequence-detection.d.ts.map +1 -0
  130. package/dist/hints/rules/sequence-detection.js +89 -0
  131. package/dist/hints/rules/sequence-detection.js.map +1 -0
  132. package/dist/hints/rules/success-hints.d.ts +6 -0
  133. package/dist/hints/rules/success-hints.d.ts.map +1 -0
  134. package/dist/hints/rules/success-hints.js +62 -0
  135. package/dist/hints/rules/success-hints.js.map +1 -0
  136. package/dist/index.d.ts +10 -0
  137. package/dist/index.d.ts.map +1 -0
  138. package/dist/index.js +278 -0
  139. package/dist/index.js.map +1 -0
  140. package/dist/lightpanda/launcher.d.ts +58 -0
  141. package/dist/lightpanda/launcher.d.ts.map +1 -0
  142. package/dist/lightpanda/launcher.js +210 -0
  143. package/dist/lightpanda/launcher.js.map +1 -0
  144. package/dist/mcp-server.d.ts +129 -0
  145. package/dist/mcp-server.d.ts.map +1 -0
  146. package/dist/mcp-server.js +641 -0
  147. package/dist/mcp-server.js.map +1 -0
  148. package/dist/memory/domain-memory.d.ts +68 -0
  149. package/dist/memory/domain-memory.d.ts.map +1 -0
  150. package/dist/memory/domain-memory.js +228 -0
  151. package/dist/memory/domain-memory.js.map +1 -0
  152. package/dist/orchestration/plan-executor.d.ts +19 -0
  153. package/dist/orchestration/plan-executor.d.ts.map +1 -0
  154. package/dist/orchestration/plan-executor.js +284 -0
  155. package/dist/orchestration/plan-executor.js.map +1 -0
  156. package/dist/orchestration/plan-registry.d.ts +55 -0
  157. package/dist/orchestration/plan-registry.d.ts.map +1 -0
  158. package/dist/orchestration/plan-registry.js +255 -0
  159. package/dist/orchestration/plan-registry.js.map +1 -0
  160. package/dist/orchestration/state-manager.d.ts +127 -0
  161. package/dist/orchestration/state-manager.d.ts.map +1 -0
  162. package/dist/orchestration/state-manager.js +438 -0
  163. package/dist/orchestration/state-manager.js.map +1 -0
  164. package/dist/orchestration/workflow-engine.d.ts +162 -0
  165. package/dist/orchestration/workflow-engine.d.ts.map +1 -0
  166. package/dist/orchestration/workflow-engine.js +745 -0
  167. package/dist/orchestration/workflow-engine.js.map +1 -0
  168. package/dist/resources/usage-guide.d.ts +13 -0
  169. package/dist/resources/usage-guide.d.ts.map +1 -0
  170. package/dist/resources/usage-guide.js +101 -0
  171. package/dist/resources/usage-guide.js.map +1 -0
  172. package/dist/router/browser-router.d.ts +51 -0
  173. package/dist/router/browser-router.d.ts.map +1 -0
  174. package/dist/router/browser-router.js +178 -0
  175. package/dist/router/browser-router.js.map +1 -0
  176. package/dist/router/cookie-sync.d.ts +48 -0
  177. package/dist/router/cookie-sync.d.ts.map +1 -0
  178. package/dist/router/cookie-sync.js +106 -0
  179. package/dist/router/cookie-sync.js.map +1 -0
  180. package/dist/router/index.d.ts +5 -0
  181. package/dist/router/index.d.ts.map +1 -0
  182. package/dist/router/index.js +10 -0
  183. package/dist/router/index.js.map +1 -0
  184. package/dist/router/tool-routing-registry.d.ts +21 -0
  185. package/dist/router/tool-routing-registry.d.ts.map +1 -0
  186. package/dist/router/tool-routing-registry.js +90 -0
  187. package/dist/router/tool-routing-registry.js.map +1 -0
  188. package/dist/session-manager.d.ts +251 -0
  189. package/dist/session-manager.d.ts.map +1 -0
  190. package/dist/session-manager.js +953 -0
  191. package/dist/session-manager.js.map +1 -0
  192. package/dist/tools/batch-execute.d.ts +11 -0
  193. package/dist/tools/batch-execute.d.ts.map +1 -0
  194. package/dist/tools/batch-execute.js +226 -0
  195. package/dist/tools/batch-execute.js.map +1 -0
  196. package/dist/tools/click-element.d.ts +8 -0
  197. package/dist/tools/click-element.d.ts.map +1 -0
  198. package/dist/tools/click-element.js +455 -0
  199. package/dist/tools/click-element.js.map +1 -0
  200. package/dist/tools/computer.d.ts +6 -0
  201. package/dist/tools/computer.d.ts.map +1 -0
  202. package/dist/tools/computer.js +638 -0
  203. package/dist/tools/computer.js.map +1 -0
  204. package/dist/tools/console-capture.d.ts +6 -0
  205. package/dist/tools/console-capture.d.ts.map +1 -0
  206. package/dist/tools/console-capture.js +320 -0
  207. package/dist/tools/console-capture.js.map +1 -0
  208. package/dist/tools/cookies.d.ts +6 -0
  209. package/dist/tools/cookies.d.ts.map +1 -0
  210. package/dist/tools/cookies.js +263 -0
  211. package/dist/tools/cookies.js.map +1 -0
  212. package/dist/tools/drag-drop.d.ts +6 -0
  213. package/dist/tools/drag-drop.d.ts.map +1 -0
  214. package/dist/tools/drag-drop.js +252 -0
  215. package/dist/tools/drag-drop.js.map +1 -0
  216. package/dist/tools/emulate-device.d.ts +6 -0
  217. package/dist/tools/emulate-device.d.ts.map +1 -0
  218. package/dist/tools/emulate-device.js +221 -0
  219. package/dist/tools/emulate-device.js.map +1 -0
  220. package/dist/tools/file-upload.d.ts +6 -0
  221. package/dist/tools/file-upload.d.ts.map +1 -0
  222. package/dist/tools/file-upload.js +209 -0
  223. package/dist/tools/file-upload.js.map +1 -0
  224. package/dist/tools/fill-form.d.ts +8 -0
  225. package/dist/tools/fill-form.d.ts.map +1 -0
  226. package/dist/tools/fill-form.js +342 -0
  227. package/dist/tools/fill-form.js.map +1 -0
  228. package/dist/tools/find.d.ts +6 -0
  229. package/dist/tools/find.d.ts.map +1 -0
  230. package/dist/tools/find.js +330 -0
  231. package/dist/tools/find.js.map +1 -0
  232. package/dist/tools/form-input.d.ts +6 -0
  233. package/dist/tools/form-input.d.ts.map +1 -0
  234. package/dist/tools/form-input.js +181 -0
  235. package/dist/tools/form-input.js.map +1 -0
  236. package/dist/tools/geolocation.d.ts +6 -0
  237. package/dist/tools/geolocation.d.ts.map +1 -0
  238. package/dist/tools/geolocation.js +172 -0
  239. package/dist/tools/geolocation.js.map +1 -0
  240. package/dist/tools/http-auth.d.ts +6 -0
  241. package/dist/tools/http-auth.d.ts.map +1 -0
  242. package/dist/tools/http-auth.js +136 -0
  243. package/dist/tools/http-auth.js.map +1 -0
  244. package/dist/tools/index.d.ts +6 -0
  245. package/dist/tools/index.d.ts.map +1 -0
  246. package/dist/tools/index.js +104 -0
  247. package/dist/tools/index.js.map +1 -0
  248. package/dist/tools/javascript.d.ts +6 -0
  249. package/dist/tools/javascript.d.ts.map +1 -0
  250. package/dist/tools/javascript.js +138 -0
  251. package/dist/tools/javascript.js.map +1 -0
  252. package/dist/tools/lightweight-scroll.d.ts +11 -0
  253. package/dist/tools/lightweight-scroll.d.ts.map +1 -0
  254. package/dist/tools/lightweight-scroll.js +266 -0
  255. package/dist/tools/lightweight-scroll.js.map +1 -0
  256. package/dist/tools/memory.d.ts +10 -0
  257. package/dist/tools/memory.d.ts.map +1 -0
  258. package/dist/tools/memory.js +141 -0
  259. package/dist/tools/memory.js.map +1 -0
  260. package/dist/tools/navigate.d.ts +6 -0
  261. package/dist/tools/navigate.d.ts.map +1 -0
  262. package/dist/tools/navigate.js +241 -0
  263. package/dist/tools/navigate.js.map +1 -0
  264. package/dist/tools/network.d.ts +6 -0
  265. package/dist/tools/network.d.ts.map +1 -0
  266. package/dist/tools/network.js +215 -0
  267. package/dist/tools/network.js.map +1 -0
  268. package/dist/tools/orchestration.d.ts +6 -0
  269. package/dist/tools/orchestration.d.ts.map +1 -0
  270. package/dist/tools/orchestration.js +741 -0
  271. package/dist/tools/orchestration.js.map +1 -0
  272. package/dist/tools/page-content.d.ts +6 -0
  273. package/dist/tools/page-content.d.ts.map +1 -0
  274. package/dist/tools/page-content.js +120 -0
  275. package/dist/tools/page-content.js.map +1 -0
  276. package/dist/tools/page-pdf.d.ts +6 -0
  277. package/dist/tools/page-pdf.d.ts.map +1 -0
  278. package/dist/tools/page-pdf.js +246 -0
  279. package/dist/tools/page-pdf.js.map +1 -0
  280. package/dist/tools/page-reload.d.ts +6 -0
  281. package/dist/tools/page-reload.d.ts.map +1 -0
  282. package/dist/tools/page-reload.js +89 -0
  283. package/dist/tools/page-reload.js.map +1 -0
  284. package/dist/tools/performance-metrics.d.ts +6 -0
  285. package/dist/tools/performance-metrics.d.ts.map +1 -0
  286. package/dist/tools/performance-metrics.js +158 -0
  287. package/dist/tools/performance-metrics.js.map +1 -0
  288. package/dist/tools/read-page.d.ts +6 -0
  289. package/dist/tools/read-page.d.ts.map +1 -0
  290. package/dist/tools/read-page.js +287 -0
  291. package/dist/tools/read-page.js.map +1 -0
  292. package/dist/tools/request-intercept.d.ts +6 -0
  293. package/dist/tools/request-intercept.d.ts.map +1 -0
  294. package/dist/tools/request-intercept.js +439 -0
  295. package/dist/tools/request-intercept.js.map +1 -0
  296. package/dist/tools/selector-query.d.ts +6 -0
  297. package/dist/tools/selector-query.d.ts.map +1 -0
  298. package/dist/tools/selector-query.js +206 -0
  299. package/dist/tools/selector-query.js.map +1 -0
  300. package/dist/tools/shutdown.d.ts +12 -0
  301. package/dist/tools/shutdown.d.ts.map +1 -0
  302. package/dist/tools/shutdown.js +120 -0
  303. package/dist/tools/shutdown.js.map +1 -0
  304. package/dist/tools/storage.d.ts +6 -0
  305. package/dist/tools/storage.d.ts.map +1 -0
  306. package/dist/tools/storage.js +264 -0
  307. package/dist/tools/storage.js.map +1 -0
  308. package/dist/tools/tabs-close.d.ts +6 -0
  309. package/dist/tools/tabs-close.d.ts.map +1 -0
  310. package/dist/tools/tabs-close.js +124 -0
  311. package/dist/tools/tabs-close.js.map +1 -0
  312. package/dist/tools/tabs-context.d.ts +6 -0
  313. package/dist/tools/tabs-context.d.ts.map +1 -0
  314. package/dist/tools/tabs-context.js +92 -0
  315. package/dist/tools/tabs-context.js.map +1 -0
  316. package/dist/tools/tabs-create.d.ts +6 -0
  317. package/dist/tools/tabs-create.d.ts.map +1 -0
  318. package/dist/tools/tabs-create.js +73 -0
  319. package/dist/tools/tabs-create.js.map +1 -0
  320. package/dist/tools/user-agent.d.ts +6 -0
  321. package/dist/tools/user-agent.d.ts.map +1 -0
  322. package/dist/tools/user-agent.js +128 -0
  323. package/dist/tools/user-agent.js.map +1 -0
  324. package/dist/tools/wait-and-click.d.ts +8 -0
  325. package/dist/tools/wait-and-click.d.ts.map +1 -0
  326. package/dist/tools/wait-and-click.js +290 -0
  327. package/dist/tools/wait-and-click.js.map +1 -0
  328. package/dist/tools/wait-for.d.ts +6 -0
  329. package/dist/tools/wait-for.d.ts.map +1 -0
  330. package/dist/tools/wait-for.js +248 -0
  331. package/dist/tools/wait-for.js.map +1 -0
  332. package/dist/tools/worker-create.d.ts +7 -0
  333. package/dist/tools/worker-create.d.ts.map +1 -0
  334. package/dist/tools/worker-create.js +62 -0
  335. package/dist/tools/worker-create.js.map +1 -0
  336. package/dist/tools/worker-delete.d.ts +6 -0
  337. package/dist/tools/worker-delete.d.ts.map +1 -0
  338. package/dist/tools/worker-delete.js +80 -0
  339. package/dist/tools/worker-delete.js.map +1 -0
  340. package/dist/tools/worker-list.d.ts +6 -0
  341. package/dist/tools/worker-list.d.ts.map +1 -0
  342. package/dist/tools/worker-list.js +67 -0
  343. package/dist/tools/worker-list.js.map +1 -0
  344. package/dist/tools/xpath-query.d.ts +6 -0
  345. package/dist/tools/xpath-query.d.ts.map +1 -0
  346. package/dist/tools/xpath-query.js +230 -0
  347. package/dist/tools/xpath-query.js.map +1 -0
  348. package/dist/types/browser-backend.d.ts +30 -0
  349. package/dist/types/browser-backend.d.ts.map +1 -0
  350. package/dist/types/browser-backend.js +9 -0
  351. package/dist/types/browser-backend.js.map +1 -0
  352. package/dist/types/index.d.ts +3 -0
  353. package/dist/types/index.d.ts.map +1 -0
  354. package/dist/types/index.js +19 -0
  355. package/dist/types/index.js.map +1 -0
  356. package/dist/types/mcp.d.ts +54 -0
  357. package/dist/types/mcp.d.ts.map +1 -0
  358. package/dist/types/mcp.js +14 -0
  359. package/dist/types/mcp.js.map +1 -0
  360. package/dist/types/plan-cache.d.ts +121 -0
  361. package/dist/types/plan-cache.d.ts.map +1 -0
  362. package/dist/types/plan-cache.js +9 -0
  363. package/dist/types/plan-cache.js.map +1 -0
  364. package/dist/types/profile.d.ts +76 -0
  365. package/dist/types/profile.d.ts.map +1 -0
  366. package/dist/types/profile.js +35 -0
  367. package/dist/types/profile.js.map +1 -0
  368. package/dist/types/session.d.ts +65 -0
  369. package/dist/types/session.d.ts.map +1 -0
  370. package/dist/types/session.js +6 -0
  371. package/dist/types/session.js.map +1 -0
  372. package/dist/types/tool-manifest.d.ts +52 -0
  373. package/dist/types/tool-manifest.d.ts.map +1 -0
  374. package/dist/types/tool-manifest.js +37 -0
  375. package/dist/types/tool-manifest.js.map +1 -0
  376. package/dist/utils/atomic-file.d.ts +50 -0
  377. package/dist/utils/atomic-file.d.ts.map +1 -0
  378. package/dist/utils/atomic-file.js +217 -0
  379. package/dist/utils/atomic-file.js.map +1 -0
  380. package/dist/utils/index.d.ts +6 -0
  381. package/dist/utils/index.d.ts.map +1 -0
  382. package/dist/utils/index.js +22 -0
  383. package/dist/utils/index.js.map +1 -0
  384. package/dist/utils/json-validator.d.ts +40 -0
  385. package/dist/utils/json-validator.d.ts.map +1 -0
  386. package/dist/utils/json-validator.js +295 -0
  387. package/dist/utils/json-validator.js.map +1 -0
  388. package/dist/utils/ref-id-manager.d.ts +26 -0
  389. package/dist/utils/ref-id-manager.d.ts.map +1 -0
  390. package/dist/utils/ref-id-manager.js +81 -0
  391. package/dist/utils/ref-id-manager.js.map +1 -0
  392. package/dist/utils/request-queue.d.ts +37 -0
  393. package/dist/utils/request-queue.d.ts.map +1 -0
  394. package/dist/utils/request-queue.js +110 -0
  395. package/dist/utils/request-queue.js.map +1 -0
  396. package/package.json +78 -0
@@ -0,0 +1,953 @@
1
+ "use strict";
2
+ /**
3
+ * Session Manager - Manages lifecycle of parallel Claude Code sessions
4
+ * Supports multiple Workers within a single session for parallel browser operations
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.SessionManager = void 0;
8
+ exports.getSessionManager = getSessionManager;
9
+ const client_1 = require("./cdp/client");
10
+ const connection_pool_1 = require("./cdp/connection-pool");
11
+ const pool_1 = require("./chrome/pool");
12
+ const global_1 = require("./config/global");
13
+ const request_queue_1 = require("./utils/request-queue");
14
+ const ref_id_manager_1 = require("./utils/ref-id-manager");
15
+ const router_1 = require("./router");
16
+ // Helper to get target ID (internal puppeteer property)
17
+ function getTargetId(target) {
18
+ return target._targetId;
19
+ }
20
+ const DEFAULT_CONFIG = {
21
+ sessionTTL: 30 * 60 * 1000, // 30 minutes
22
+ cleanupInterval: 60 * 1000, // 1 minute
23
+ autoCleanup: true,
24
+ maxSessions: 100,
25
+ maxWorkersPerSession: 50,
26
+ useConnectionPool: true, // Enabled by default for faster page creation
27
+ useDefaultContext: true, // Use Chrome profile's cookies/sessions by default
28
+ usePool: false, // Disabled by default; enable for multi-Chrome origin isolation
29
+ };
30
+ class SessionManager {
31
+ sessions = new Map();
32
+ targetToWorker = new Map();
33
+ cdpClient;
34
+ connectionPool = null;
35
+ chromePool = null;
36
+ cdpFactory;
37
+ queueManager;
38
+ eventListeners = [];
39
+ browserRouter = null;
40
+ // TTL & Stats
41
+ config;
42
+ cleanupTimer = null;
43
+ startTime = Date.now();
44
+ totalSessionsCreated = 0;
45
+ totalSessionsCleaned = 0;
46
+ lastCleanupTime = null;
47
+ constructor(cdpClient, config) {
48
+ this.cdpClient = cdpClient || (0, client_1.getCDPClient)();
49
+ this.queueManager = new request_queue_1.RequestQueueManager();
50
+ this.config = { ...DEFAULT_CONFIG, ...config };
51
+ this.cdpFactory = (0, client_1.getCDPClientFactory)();
52
+ if (this.config.useConnectionPool) {
53
+ this.connectionPool = (0, connection_pool_1.getCDPConnectionPool)();
54
+ }
55
+ if (this.config.usePool) {
56
+ this.chromePool = (0, pool_1.getChromePool)({ autoLaunch: (0, global_1.getGlobalConfig)().autoLaunch });
57
+ }
58
+ if (this.config.autoCleanup) {
59
+ this.startAutoCleanup();
60
+ }
61
+ // Register target destroyed listener
62
+ this.cdpClient.addTargetDestroyedListener((targetId) => {
63
+ this.onTargetClosed(targetId);
64
+ });
65
+ }
66
+ /**
67
+ * Get the CDPClient for a specific worker (may be on a different Chrome instance)
68
+ */
69
+ getCDPClientForWorker(sessionId, workerId) {
70
+ const worker = this.getWorker(sessionId, workerId);
71
+ if (worker?.port) {
72
+ const client = this.cdpFactory.get(worker.port);
73
+ if (client)
74
+ return client;
75
+ }
76
+ return this.cdpClient;
77
+ }
78
+ /**
79
+ * Start automatic cleanup interval
80
+ */
81
+ startAutoCleanup() {
82
+ if (this.cleanupTimer) {
83
+ clearInterval(this.cleanupTimer);
84
+ }
85
+ this.cleanupTimer = setInterval(async () => {
86
+ try {
87
+ const deleted = await this.cleanupInactiveSessions(this.config.sessionTTL);
88
+ if (deleted.length > 0) {
89
+ console.error(`[SessionManager] Auto-cleanup: removed ${deleted.length} inactive session(s)`);
90
+ }
91
+ this.lastCleanupTime = Date.now();
92
+ }
93
+ catch (error) {
94
+ console.error('[SessionManager] Auto-cleanup error:', error);
95
+ }
96
+ }, this.config.cleanupInterval);
97
+ // Don't prevent process exit
98
+ this.cleanupTimer.unref();
99
+ }
100
+ /**
101
+ * Stop automatic cleanup
102
+ */
103
+ stopAutoCleanup() {
104
+ if (this.cleanupTimer) {
105
+ clearInterval(this.cleanupTimer);
106
+ this.cleanupTimer = null;
107
+ }
108
+ }
109
+ /**
110
+ * Get session manager statistics
111
+ */
112
+ getStats() {
113
+ let totalTargets = 0;
114
+ let totalWorkers = 0;
115
+ for (const session of this.sessions.values()) {
116
+ totalWorkers += session.workers.size;
117
+ for (const worker of session.workers.values()) {
118
+ totalTargets += worker.targets.size;
119
+ }
120
+ // Also count legacy targets
121
+ totalTargets += session.targets.size;
122
+ }
123
+ const stats = {
124
+ activeSessions: this.sessions.size,
125
+ totalTargets,
126
+ totalWorkers,
127
+ totalSessionsCreated: this.totalSessionsCreated,
128
+ totalSessionsCleaned: this.totalSessionsCleaned,
129
+ uptime: Date.now() - this.startTime,
130
+ lastCleanup: this.lastCleanupTime,
131
+ memoryUsage: process.memoryUsage().heapUsed,
132
+ };
133
+ if (this.connectionPool) {
134
+ stats.connectionPool = this.connectionPool.getStats();
135
+ }
136
+ return stats;
137
+ }
138
+ /**
139
+ * Get current configuration
140
+ */
141
+ getConfig() {
142
+ return { ...this.config };
143
+ }
144
+ /**
145
+ * Update configuration
146
+ */
147
+ updateConfig(config) {
148
+ this.config = { ...this.config, ...config };
149
+ // Restart cleanup timer if interval changed
150
+ if (config.cleanupInterval !== undefined || config.autoCleanup !== undefined) {
151
+ this.stopAutoCleanup();
152
+ if (this.config.autoCleanup) {
153
+ this.startAutoCleanup();
154
+ }
155
+ }
156
+ }
157
+ /**
158
+ * Ensure connected to Chrome
159
+ */
160
+ async ensureConnected() {
161
+ if (!this.cdpClient.isConnected()) {
162
+ await this.cdpClient.connect();
163
+ }
164
+ }
165
+ // ==================== SESSION MANAGEMENT ====================
166
+ /**
167
+ * Create a new session with a default worker
168
+ */
169
+ async createSession(options = {}) {
170
+ await this.ensureConnected();
171
+ const id = options.id || crypto.randomUUID();
172
+ if (this.sessions.has(id)) {
173
+ return this.sessions.get(id);
174
+ }
175
+ // Check max sessions limit
176
+ if (this.sessions.size >= this.config.maxSessions) {
177
+ const deleted = await this.cleanupInactiveSessions(this.config.sessionTTL);
178
+ if (deleted.length === 0 && this.sessions.size >= this.config.maxSessions) {
179
+ throw new Error(`Maximum session limit (${this.config.maxSessions}) reached.`);
180
+ }
181
+ }
182
+ const name = options.name || `Session ${id.slice(0, 8)}`;
183
+ const defaultWorkerId = 'default';
184
+ // Create default worker - use default context if configured (shares Chrome profile's cookies)
185
+ // or create isolated browser context for session isolation
186
+ const defaultContext = this.config.useDefaultContext
187
+ ? null // null means use default browser context (shares cookies with Chrome profile)
188
+ : await this.cdpClient.createBrowserContext();
189
+ const defaultWorker = {
190
+ id: defaultWorkerId,
191
+ name: 'Default Worker',
192
+ targets: new Set(),
193
+ context: defaultContext,
194
+ createdAt: Date.now(),
195
+ lastActivityAt: Date.now(),
196
+ };
197
+ const session = {
198
+ id,
199
+ workers: new Map([[defaultWorkerId, defaultWorker]]),
200
+ defaultWorkerId,
201
+ targets: new Set(), // Legacy support
202
+ createdAt: Date.now(),
203
+ lastActivityAt: Date.now(),
204
+ name,
205
+ context: defaultContext, // Legacy support
206
+ };
207
+ this.sessions.set(id, session);
208
+ this.totalSessionsCreated++;
209
+ this.emitEvent({ type: 'session:created', sessionId: id, timestamp: Date.now() });
210
+ console.error(`[SessionManager] Created session ${id} with default worker`);
211
+ return session;
212
+ }
213
+ /**
214
+ * Get or create a session
215
+ */
216
+ async getOrCreateSession(sessionId) {
217
+ let session = this.sessions.get(sessionId);
218
+ if (!session) {
219
+ session = await this.createSession({ id: sessionId });
220
+ }
221
+ return session;
222
+ }
223
+ /**
224
+ * Get an existing session
225
+ */
226
+ getSession(sessionId) {
227
+ return this.sessions.get(sessionId);
228
+ }
229
+ /**
230
+ * Update last activity timestamp
231
+ */
232
+ touchSession(sessionId) {
233
+ const session = this.sessions.get(sessionId);
234
+ if (session) {
235
+ session.lastActivityAt = Date.now();
236
+ }
237
+ }
238
+ /**
239
+ * Delete a session and clean up all workers
240
+ */
241
+ async deleteSession(sessionId) {
242
+ const session = this.sessions.get(sessionId);
243
+ if (!session) {
244
+ return;
245
+ }
246
+ // Delete all workers
247
+ for (const workerId of session.workers.keys()) {
248
+ await this.deleteWorkerInternal(session, workerId);
249
+ }
250
+ // Clean up all worker queues
251
+ for (const workerId of session.workers.keys()) {
252
+ this.queueManager.deleteQueue(`${sessionId}:${workerId}`);
253
+ }
254
+ this.queueManager.deleteQueue(sessionId);
255
+ // Clean up ref IDs
256
+ (0, ref_id_manager_1.getRefIdManager)().clearSessionRefs(sessionId);
257
+ // Remove session
258
+ this.sessions.delete(sessionId);
259
+ this.emitEvent({ type: 'session:deleted', sessionId, timestamp: Date.now() });
260
+ console.error(`[SessionManager] Deleted session ${sessionId}`);
261
+ }
262
+ /**
263
+ * Clean up inactive sessions
264
+ */
265
+ async cleanupInactiveSessions(maxAgeMs) {
266
+ const now = Date.now();
267
+ const deletedSessions = [];
268
+ for (const [sessionId, session] of this.sessions) {
269
+ if (now - session.lastActivityAt > maxAgeMs) {
270
+ await this.deleteSession(sessionId);
271
+ deletedSessions.push(sessionId);
272
+ this.totalSessionsCleaned++;
273
+ }
274
+ }
275
+ // Trigger browser-level GC after bulk cleanup
276
+ if (deletedSessions.length > 0) {
277
+ try {
278
+ const pages = await this.cdpClient.getPages();
279
+ if (pages.length > 0) {
280
+ await this.cdpClient.triggerGC(pages[0]);
281
+ }
282
+ }
283
+ catch {
284
+ // Best-effort GC
285
+ }
286
+ }
287
+ return deletedSessions;
288
+ }
289
+ /**
290
+ * Force cleanup all sessions
291
+ */
292
+ async cleanupAllSessions() {
293
+ const count = this.sessions.size;
294
+ const sessionIds = Array.from(this.sessions.keys());
295
+ for (const sessionId of sessionIds) {
296
+ await this.deleteSession(sessionId);
297
+ this.totalSessionsCleaned++;
298
+ }
299
+ // Clean up Chrome pool and factory connections
300
+ if (this.chromePool) {
301
+ await this.chromePool.cleanup();
302
+ }
303
+ await this.cdpFactory.disconnectAll();
304
+ return count;
305
+ }
306
+ // ==================== WORKER MANAGEMENT ====================
307
+ /**
308
+ * Create a new worker within a session
309
+ * Each worker has its own isolated browser context (cookies, localStorage, etc.)
310
+ */
311
+ async createWorker(sessionId, options = {}) {
312
+ await this.ensureConnected();
313
+ const session = await this.getOrCreateSession(sessionId);
314
+ // Check max workers limit
315
+ if (session.workers.size >= this.config.maxWorkersPerSession) {
316
+ throw new Error(`Maximum workers per session (${this.config.maxWorkersPerSession}) reached.`);
317
+ }
318
+ const workerId = options.id || `worker-${crypto.randomUUID().slice(0, 8)}`;
319
+ if (session.workers.has(workerId)) {
320
+ return session.workers.get(workerId);
321
+ }
322
+ const name = options.name || `Worker ${workerId}`;
323
+ // Create browser context: shared (null = copies cookies from Chrome profile) or isolated
324
+ const context = options.shareCookies
325
+ ? null
326
+ : await this.cdpClient.createBrowserContext();
327
+ // If pool is enabled and targetUrl provided, acquire a separate Chrome instance
328
+ let workerPort;
329
+ let workerPoolOrigin;
330
+ if (this.chromePool && options.targetUrl) {
331
+ try {
332
+ const origin = new URL(options.targetUrl).origin;
333
+ const poolInstance = await this.chromePool.acquireInstance(origin);
334
+ workerPort = poolInstance.port;
335
+ workerPoolOrigin = origin;
336
+ // Ensure CDPClient for this port is connected
337
+ const workerCdpClient = this.cdpFactory.getOrCreate(workerPort, {
338
+ autoLaunch: (0, global_1.getGlobalConfig)().autoLaunch,
339
+ });
340
+ if (!workerCdpClient.isConnected()) {
341
+ await workerCdpClient.connect();
342
+ }
343
+ console.error(`[SessionManager] Worker ${workerId} assigned to Chrome instance on port ${workerPort} for origin ${origin}`);
344
+ }
345
+ catch (err) {
346
+ console.error(`[SessionManager] Pool acquisition failed, falling back to default:`, err);
347
+ workerPort = undefined;
348
+ workerPoolOrigin = undefined;
349
+ }
350
+ }
351
+ const worker = {
352
+ id: workerId,
353
+ name,
354
+ targets: new Set(),
355
+ context,
356
+ createdAt: Date.now(),
357
+ lastActivityAt: Date.now(),
358
+ port: workerPort,
359
+ poolOrigin: workerPoolOrigin,
360
+ };
361
+ session.workers.set(workerId, worker);
362
+ this.touchSession(sessionId);
363
+ this.emitEvent({
364
+ type: 'worker:created',
365
+ sessionId,
366
+ workerId,
367
+ timestamp: Date.now(),
368
+ });
369
+ console.error(`[SessionManager] Created worker ${workerId} in session ${sessionId}`);
370
+ return worker;
371
+ }
372
+ /**
373
+ * Get a worker by ID
374
+ */
375
+ getWorker(sessionId, workerId) {
376
+ const session = this.sessions.get(sessionId);
377
+ if (!session)
378
+ return undefined;
379
+ return session.workers.get(workerId);
380
+ }
381
+ /**
382
+ * Get or create a worker
383
+ */
384
+ async getOrCreateWorker(sessionId, workerId) {
385
+ const session = await this.getOrCreateSession(sessionId);
386
+ // If no workerId specified, use default worker
387
+ const targetWorkerId = workerId || session.defaultWorkerId;
388
+ let worker = session.workers.get(targetWorkerId);
389
+ if (!worker) {
390
+ worker = await this.createWorker(sessionId, { id: targetWorkerId });
391
+ }
392
+ return worker;
393
+ }
394
+ /**
395
+ * List all workers in a session
396
+ */
397
+ getWorkers(sessionId) {
398
+ const session = this.sessions.get(sessionId);
399
+ if (!session)
400
+ return [];
401
+ const workers = [];
402
+ for (const worker of session.workers.values()) {
403
+ workers.push({
404
+ id: worker.id,
405
+ name: worker.name,
406
+ targetCount: worker.targets.size,
407
+ createdAt: worker.createdAt,
408
+ lastActivityAt: worker.lastActivityAt,
409
+ });
410
+ }
411
+ return workers;
412
+ }
413
+ /**
414
+ * Delete a worker and its resources
415
+ */
416
+ async deleteWorker(sessionId, workerId) {
417
+ const session = this.sessions.get(sessionId);
418
+ if (!session)
419
+ return;
420
+ // Can't delete default worker
421
+ if (workerId === session.defaultWorkerId) {
422
+ throw new Error('Cannot delete the default worker. Delete the session instead.');
423
+ }
424
+ await this.deleteWorkerInternal(session, workerId);
425
+ this.emitEvent({
426
+ type: 'worker:deleted',
427
+ sessionId,
428
+ workerId,
429
+ timestamp: Date.now(),
430
+ });
431
+ }
432
+ /**
433
+ * Internal worker deletion (also used for cleanup)
434
+ */
435
+ async deleteWorkerInternal(session, workerId) {
436
+ const worker = session.workers.get(workerId);
437
+ if (!worker)
438
+ return;
439
+ // Determine which CDPClient to use for this worker
440
+ const workerCdpClient = worker.port
441
+ ? (this.cdpFactory.get(worker.port) || this.cdpClient)
442
+ : this.cdpClient;
443
+ // Close all pages in this worker (return to pool if available)
444
+ for (const targetId of worker.targets) {
445
+ try {
446
+ if (this.connectionPool && this.config.useConnectionPool) {
447
+ const page = await workerCdpClient.getPageByTargetId(targetId);
448
+ if (page && !page.isClosed()) {
449
+ await this.connectionPool.releasePage(page);
450
+ }
451
+ else {
452
+ await workerCdpClient.closePage(targetId);
453
+ }
454
+ }
455
+ else {
456
+ await workerCdpClient.closePage(targetId);
457
+ }
458
+ }
459
+ catch {
460
+ // Page might already be closed
461
+ }
462
+ this.targetToWorker.delete(targetId);
463
+ }
464
+ // Close the browser context (only if it's an isolated context, not the default)
465
+ if (worker.context) {
466
+ try {
467
+ await workerCdpClient.closeBrowserContext(worker.context);
468
+ }
469
+ catch {
470
+ // Context might already be closed
471
+ }
472
+ }
473
+ // Release Chrome pool instance if worker had one
474
+ if (worker.port && worker.poolOrigin && this.chromePool) {
475
+ this.chromePool.releaseInstance(worker.port, worker.poolOrigin);
476
+ console.error(`[SessionManager] Released pool instance port ${worker.port} for origin ${worker.poolOrigin}`);
477
+ }
478
+ // Clean up ref IDs for this worker
479
+ for (const targetId of worker.targets) {
480
+ (0, ref_id_manager_1.getRefIdManager)().clearTargetRefs(session.id, targetId);
481
+ }
482
+ session.workers.delete(workerId);
483
+ console.error(`[SessionManager] Deleted worker ${workerId} from session ${session.id}`);
484
+ }
485
+ // ==================== TARGET/PAGE MANAGEMENT ====================
486
+ /**
487
+ * Create a new page/target for a worker
488
+ * @param sessionId Session ID
489
+ * @param url Optional URL to navigate to
490
+ * @param workerId Optional worker ID (uses default worker if not specified)
491
+ */
492
+ async createTarget(sessionId, url, workerId) {
493
+ await this.ensureConnected();
494
+ const worker = await this.getOrCreateWorker(sessionId, workerId);
495
+ // Create page — try connection pool first for pre-warmed pages, fall back to direct creation
496
+ const cdpClient = this.getCDPClientForWorker(sessionId, worker.id);
497
+ let page;
498
+ // Snapshot existing target IDs before page creation.
499
+ // Chrome's Site Isolation can create orphan about:blank targets during cross-origin
500
+ // navigation (renderer process swap). We detect and close these after navigation.
501
+ const existingTargetIds = new Set(cdpClient.getBrowser().targets()
502
+ .filter(t => t.type() === 'page')
503
+ .map(t => getTargetId(t)));
504
+ if (this.connectionPool && this.config.useConnectionPool) {
505
+ let poolPage = null;
506
+ try {
507
+ poolPage = await this.connectionPool.acquirePage();
508
+ // Navigate the pre-warmed page to the target URL
509
+ if (url) {
510
+ await poolPage.goto(url, { waitUntil: 'domcontentloaded' });
511
+ }
512
+ // Copy cookies from the worker's browser context if available
513
+ // (pool pages start blank — replicate what cdpClient.createPage() does for contexts)
514
+ if (worker.context) {
515
+ try {
516
+ const cookies = await worker.context.cookies();
517
+ if (cookies.length > 0) {
518
+ await poolPage.setCookie(...cookies);
519
+ }
520
+ }
521
+ catch {
522
+ // Best-effort cookie copy
523
+ }
524
+ }
525
+ page = poolPage;
526
+ console.error(`[SessionManager] Acquired page from pool for session ${sessionId}`);
527
+ }
528
+ catch (err) {
529
+ // Close the acquired pool page to prevent about:blank ghost tabs.
530
+ // Close first (removes from Chrome), then release (cleans pool tracking).
531
+ // Do NOT just releasePage — that returns it to pool as about:blank.
532
+ if (poolPage) {
533
+ await poolPage.close().catch(() => { });
534
+ this.connectionPool.releasePage(poolPage).catch(() => { });
535
+ }
536
+ console.error(`[SessionManager] Pool acquire/navigate failed, falling back to direct creation:`, err);
537
+ page = await cdpClient.createPage(url, worker.context);
538
+ }
539
+ }
540
+ else {
541
+ page = await cdpClient.createPage(url, worker.context);
542
+ }
543
+ const targetId = getTargetId(page.target());
544
+ // Clean up orphan about:blank targets created by Chrome during navigation.
545
+ // Chrome's Site Isolation creates temporary renderer targets during cross-origin
546
+ // navigation (about:blank → real URL) that can persist as ghost tabs.
547
+ // Runs after a brief delay to catch async target creation by Chrome.
548
+ const cleanupExistingIds = existingTargetIds;
549
+ const cleanupTargetId = targetId;
550
+ const cleanupBrowser = cdpClient.getBrowser();
551
+ setTimeout(async () => {
552
+ try {
553
+ const orphans = cleanupBrowser.targets().filter(t => t.type() === 'page' &&
554
+ t.url() === 'about:blank' &&
555
+ !cleanupExistingIds.has(getTargetId(t)) &&
556
+ getTargetId(t) !== cleanupTargetId &&
557
+ !this.targetToWorker.has(getTargetId(t)));
558
+ for (const t of orphans) {
559
+ try {
560
+ const orphanPage = await t.page();
561
+ if (orphanPage && !orphanPage.isClosed()) {
562
+ await orphanPage.close();
563
+ console.error(`[SessionManager] Closed orphan about:blank ghost tab: ${getTargetId(t)}`);
564
+ }
565
+ }
566
+ catch { /* target may already be destroyed */ }
567
+ }
568
+ }
569
+ catch { /* best-effort cleanup */ }
570
+ }, 500);
571
+ worker.targets.add(targetId);
572
+ worker.lastActivityAt = Date.now();
573
+ this.targetToWorker.set(targetId, { sessionId, workerId: worker.id });
574
+ this.emitEvent({
575
+ type: 'session:target-added',
576
+ sessionId,
577
+ workerId: worker.id,
578
+ targetId,
579
+ timestamp: Date.now(),
580
+ });
581
+ this.touchSession(sessionId);
582
+ return { targetId, page, workerId: worker.id };
583
+ }
584
+ /**
585
+ * Register a pre-acquired page as a target for a worker.
586
+ * Used by workflow engine when pages are batch-acquired from the pool
587
+ * to avoid per-page replenishment (about:blank proliferation fix).
588
+ */
589
+ registerExistingTarget(sessionId, workerId, targetId) {
590
+ const session = this.sessions.get(sessionId);
591
+ if (!session) {
592
+ throw new Error(`Session ${sessionId} not found`);
593
+ }
594
+ const worker = session.workers.get(workerId);
595
+ if (!worker) {
596
+ throw new Error(`Worker ${workerId} not found in session ${sessionId}`);
597
+ }
598
+ worker.targets.add(targetId);
599
+ worker.lastActivityAt = Date.now();
600
+ this.targetToWorker.set(targetId, { sessionId, workerId });
601
+ this.emitEvent({
602
+ type: 'session:target-added',
603
+ sessionId,
604
+ workerId,
605
+ targetId,
606
+ timestamp: Date.now(),
607
+ });
608
+ this.touchSession(sessionId);
609
+ }
610
+ /**
611
+ * Check if a target is still valid (page not closed)
612
+ */
613
+ async isTargetValid(targetId) {
614
+ try {
615
+ const page = await this.cdpClient.getPageByTargetId(targetId);
616
+ return page !== null && !page.isClosed();
617
+ }
618
+ catch {
619
+ return false;
620
+ }
621
+ }
622
+ /**
623
+ * Get page for a target
624
+ * @param sessionId Session ID
625
+ * @param targetId Target/Tab ID
626
+ * @param workerId Optional worker ID for validation
627
+ * @param toolName Optional MCP tool name for hybrid BrowserRouter routing
628
+ */
629
+ async getPage(sessionId, targetId, workerId, toolName) {
630
+ const ownerInfo = this.targetToWorker.get(targetId);
631
+ if (!ownerInfo || ownerInfo.sessionId !== sessionId) {
632
+ throw new Error(`Target ${targetId} does not belong to session ${sessionId}`);
633
+ }
634
+ if (workerId && ownerInfo.workerId !== workerId) {
635
+ throw new Error(`Target ${targetId} does not belong to worker ${workerId}`);
636
+ }
637
+ const cdpClient = this.getCDPClientForWorker(sessionId, ownerInfo.workerId);
638
+ // Validate target is still valid
639
+ try {
640
+ const page = await cdpClient.getPageByTargetId(targetId);
641
+ if (!page || page.isClosed()) {
642
+ this.onTargetClosed(targetId);
643
+ return null;
644
+ }
645
+ // Route through BrowserRouter if hybrid mode is active and toolName provided
646
+ if (this.browserRouter && toolName) {
647
+ const result = await this.browserRouter.route(toolName, page);
648
+ return result.page;
649
+ }
650
+ return page;
651
+ }
652
+ catch {
653
+ this.onTargetClosed(targetId);
654
+ return null;
655
+ }
656
+ }
657
+ /**
658
+ * Get all pages for a worker
659
+ */
660
+ async getWorkerPages(sessionId, workerId) {
661
+ const worker = this.getWorker(sessionId, workerId);
662
+ if (!worker)
663
+ return [];
664
+ const cdpClient = this.getCDPClientForWorker(sessionId, workerId);
665
+ const pages = [];
666
+ for (const targetId of worker.targets) {
667
+ const page = await cdpClient.getPageByTargetId(targetId);
668
+ if (page) {
669
+ pages.push(page);
670
+ }
671
+ }
672
+ return pages;
673
+ }
674
+ /**
675
+ * Get target IDs for a session (all workers)
676
+ */
677
+ getSessionTargetIds(sessionId) {
678
+ const session = this.sessions.get(sessionId);
679
+ if (!session)
680
+ return [];
681
+ const allTargets = [];
682
+ for (const worker of session.workers.values()) {
683
+ allTargets.push(...worker.targets);
684
+ }
685
+ return allTargets;
686
+ }
687
+ /**
688
+ * Get target IDs for a specific worker
689
+ */
690
+ getWorkerTargetIds(sessionId, workerId) {
691
+ const worker = this.getWorker(sessionId, workerId);
692
+ if (!worker)
693
+ return [];
694
+ return Array.from(worker.targets);
695
+ }
696
+ /**
697
+ * Validate target ownership (legacy method, checks session only)
698
+ */
699
+ validateTargetOwnership(sessionId, targetId) {
700
+ const ownerInfo = this.targetToWorker.get(targetId);
701
+ return ownerInfo?.sessionId === sessionId;
702
+ }
703
+ /**
704
+ * Get the worker ID that owns a target
705
+ */
706
+ getTargetWorkerId(targetId) {
707
+ return this.targetToWorker.get(targetId)?.workerId;
708
+ }
709
+ /**
710
+ * Close a specific target/tab
711
+ * @param sessionId Session ID
712
+ * @param targetId Target/Tab ID to close
713
+ * @returns true if closed, false if not found
714
+ */
715
+ async closeTarget(sessionId, targetId) {
716
+ const ownerInfo = this.targetToWorker.get(targetId);
717
+ if (!ownerInfo || ownerInfo.sessionId !== sessionId) {
718
+ return false;
719
+ }
720
+ try {
721
+ // Close the page via CDP (use worker's CDPClient if on pool)
722
+ const cdpClient = this.getCDPClientForWorker(sessionId, ownerInfo.workerId);
723
+ if (this.connectionPool && this.config.useConnectionPool) {
724
+ // Return the page to the pool for reuse instead of destroying it
725
+ try {
726
+ const page = await cdpClient.getPageByTargetId(targetId);
727
+ if (page && !page.isClosed()) {
728
+ await this.connectionPool.releasePage(page);
729
+ }
730
+ else {
731
+ await cdpClient.closePage(targetId);
732
+ }
733
+ }
734
+ catch {
735
+ // If pool release fails, fall back to direct close
736
+ await cdpClient.closePage(targetId);
737
+ }
738
+ }
739
+ else {
740
+ // closePage() already triggers GC internally before closing
741
+ await cdpClient.closePage(targetId);
742
+ }
743
+ // Clean up internal state
744
+ const session = this.sessions.get(sessionId);
745
+ if (session) {
746
+ const worker = session.workers.get(ownerInfo.workerId);
747
+ if (worker) {
748
+ worker.targets.delete(targetId);
749
+ }
750
+ }
751
+ // Clean up ref IDs
752
+ (0, ref_id_manager_1.getRefIdManager)().clearTargetRefs(sessionId, targetId);
753
+ // Remove from mapping
754
+ this.targetToWorker.delete(targetId);
755
+ this.emitEvent({
756
+ type: 'session:target-closed',
757
+ sessionId,
758
+ workerId: ownerInfo.workerId,
759
+ targetId,
760
+ timestamp: Date.now(),
761
+ });
762
+ return true;
763
+ }
764
+ catch (error) {
765
+ // Page might already be closed
766
+ this.onTargetClosed(targetId);
767
+ return true;
768
+ }
769
+ }
770
+ /**
771
+ * Close all tabs in a worker (without deleting the worker)
772
+ * @param sessionId Session ID
773
+ * @param workerId Worker ID
774
+ * @returns Number of tabs closed
775
+ */
776
+ async closeWorkerTabs(sessionId, workerId) {
777
+ const worker = this.getWorker(sessionId, workerId);
778
+ if (!worker)
779
+ return 0;
780
+ const targetIds = Array.from(worker.targets);
781
+ let closedCount = 0;
782
+ for (const targetId of targetIds) {
783
+ if (await this.closeTarget(sessionId, targetId)) {
784
+ closedCount++;
785
+ }
786
+ }
787
+ return closedCount;
788
+ }
789
+ /**
790
+ * Execute a CDP command through the session's queue
791
+ */
792
+ async executeCDP(sessionId, targetId, method, params) {
793
+ if (!this.validateTargetOwnership(sessionId, targetId)) {
794
+ throw new Error(`Target ${targetId} does not belong to session ${sessionId}`);
795
+ }
796
+ this.touchSession(sessionId);
797
+ const ownerInfo = this.targetToWorker.get(targetId);
798
+ const cdpClient = ownerInfo
799
+ ? this.getCDPClientForWorker(sessionId, ownerInfo.workerId)
800
+ : this.cdpClient;
801
+ const workerQueueKey = ownerInfo ? `${sessionId}:${ownerInfo.workerId}` : sessionId;
802
+ return this.queueManager.enqueue(workerQueueKey, async () => {
803
+ const page = await cdpClient.getPageByTargetId(targetId);
804
+ if (!page) {
805
+ throw new Error(`Page not found for target ${targetId}`);
806
+ }
807
+ return cdpClient.send(page, method, params);
808
+ });
809
+ }
810
+ /**
811
+ * Handle target closed event
812
+ */
813
+ onTargetClosed(targetId) {
814
+ const ownerInfo = this.targetToWorker.get(targetId);
815
+ if (ownerInfo) {
816
+ const session = this.sessions.get(ownerInfo.sessionId);
817
+ if (session) {
818
+ const worker = session.workers.get(ownerInfo.workerId);
819
+ if (worker) {
820
+ worker.targets.delete(targetId);
821
+ }
822
+ this.targetToWorker.delete(targetId);
823
+ this.emitEvent({
824
+ type: 'session:target-removed',
825
+ sessionId: ownerInfo.sessionId,
826
+ workerId: ownerInfo.workerId,
827
+ targetId,
828
+ timestamp: Date.now(),
829
+ });
830
+ }
831
+ }
832
+ }
833
+ // ==================== SESSION INFO ====================
834
+ /**
835
+ * Get session info (for serialization)
836
+ */
837
+ getSessionInfo(sessionId) {
838
+ const session = this.sessions.get(sessionId);
839
+ if (!session)
840
+ return undefined;
841
+ let totalTargets = 0;
842
+ const workers = [];
843
+ for (const worker of session.workers.values()) {
844
+ totalTargets += worker.targets.size;
845
+ workers.push({
846
+ id: worker.id,
847
+ name: worker.name,
848
+ targetCount: worker.targets.size,
849
+ createdAt: worker.createdAt,
850
+ lastActivityAt: worker.lastActivityAt,
851
+ });
852
+ }
853
+ return {
854
+ id: session.id,
855
+ targetCount: totalTargets,
856
+ workerCount: session.workers.size,
857
+ workers,
858
+ createdAt: session.createdAt,
859
+ lastActivityAt: session.lastActivityAt,
860
+ name: session.name,
861
+ };
862
+ }
863
+ /**
864
+ * Get all session infos
865
+ */
866
+ getAllSessionInfos() {
867
+ const infos = [];
868
+ for (const sessionId of this.sessions.keys()) {
869
+ const info = this.getSessionInfo(sessionId);
870
+ if (info) {
871
+ infos.push(info);
872
+ }
873
+ }
874
+ return infos;
875
+ }
876
+ // ==================== EVENT HANDLING ====================
877
+ /**
878
+ * Add event listener
879
+ */
880
+ addEventListener(listener) {
881
+ this.eventListeners.push(listener);
882
+ }
883
+ /**
884
+ * Remove event listener
885
+ */
886
+ removeEventListener(listener) {
887
+ const index = this.eventListeners.indexOf(listener);
888
+ if (index !== -1) {
889
+ this.eventListeners.splice(index, 1);
890
+ }
891
+ }
892
+ /**
893
+ * Emit event to all listeners
894
+ */
895
+ emitEvent(event) {
896
+ for (const listener of this.eventListeners) {
897
+ try {
898
+ listener(event);
899
+ }
900
+ catch (e) {
901
+ console.error('Session event listener error:', e);
902
+ }
903
+ }
904
+ }
905
+ /**
906
+ * Get the number of active sessions
907
+ */
908
+ get sessionCount() {
909
+ return this.sessions.size;
910
+ }
911
+ /**
912
+ * Get CDPClient
913
+ */
914
+ getCDPClient() {
915
+ return this.cdpClient;
916
+ }
917
+ /**
918
+ * Initialize hybrid mode with BrowserRouter
919
+ */
920
+ async initHybrid(config) {
921
+ if (this.browserRouter)
922
+ return; // Already initialized
923
+ this.browserRouter = new router_1.BrowserRouter(config);
924
+ await this.browserRouter.initialize();
925
+ console.error('[SessionManager] Hybrid mode initialized');
926
+ }
927
+ /**
928
+ * Get the BrowserRouter (for stats/escalation)
929
+ */
930
+ getBrowserRouter() {
931
+ return this.browserRouter;
932
+ }
933
+ /**
934
+ * Cleanup hybrid mode
935
+ */
936
+ async cleanupHybrid() {
937
+ if (this.browserRouter) {
938
+ await this.browserRouter.cleanup();
939
+ this.browserRouter = null;
940
+ console.error('[SessionManager] Hybrid mode cleaned up');
941
+ }
942
+ }
943
+ }
944
+ exports.SessionManager = SessionManager;
945
+ // Singleton instance
946
+ let sessionManagerInstance = null;
947
+ function getSessionManager() {
948
+ if (!sessionManagerInstance) {
949
+ sessionManagerInstance = new SessionManager();
950
+ }
951
+ return sessionManagerInstance;
952
+ }
953
+ //# sourceMappingURL=session-manager.js.map