devharness 0.8.0

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 (627) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +185 -0
  3. package/build/atomic-write.d.ts +28 -0
  4. package/build/atomic-write.d.ts.map +1 -0
  5. package/build/atomic-write.js +126 -0
  6. package/build/atomic-write.js.map +1 -0
  7. package/build/cdp-manager.d.ts +524 -0
  8. package/build/cdp-manager.d.ts.map +1 -0
  9. package/build/cdp-manager.js +1828 -0
  10. package/build/cdp-manager.js.map +1 -0
  11. package/build/cdp-manager.test.d.ts +6 -0
  12. package/build/cdp-manager.test.d.ts.map +1 -0
  13. package/build/cdp-manager.test.js +546 -0
  14. package/build/cdp-manager.test.js.map +1 -0
  15. package/build/chrome-launcher.d.ts +400 -0
  16. package/build/chrome-launcher.d.ts.map +1 -0
  17. package/build/chrome-launcher.js +1071 -0
  18. package/build/chrome-launcher.js.map +1 -0
  19. package/build/chrome-launcher.test.d.ts +11 -0
  20. package/build/chrome-launcher.test.d.ts.map +1 -0
  21. package/build/chrome-launcher.test.js +204 -0
  22. package/build/chrome-launcher.test.js.map +1 -0
  23. package/build/clickable-cache.d.ts +73 -0
  24. package/build/clickable-cache.d.ts.map +1 -0
  25. package/build/clickable-cache.js +90 -0
  26. package/build/clickable-cache.js.map +1 -0
  27. package/build/command-recorder.d.ts +401 -0
  28. package/build/command-recorder.d.ts.map +1 -0
  29. package/build/command-recorder.js +701 -0
  30. package/build/command-recorder.js.map +1 -0
  31. package/build/config.d.ts +390 -0
  32. package/build/config.d.ts.map +1 -0
  33. package/build/config.js +826 -0
  34. package/build/config.js.map +1 -0
  35. package/build/config.test.d.ts +2 -0
  36. package/build/config.test.d.ts.map +1 -0
  37. package/build/config.test.js +112 -0
  38. package/build/config.test.js.map +1 -0
  39. package/build/connection-manager.d.ts +159 -0
  40. package/build/connection-manager.d.ts.map +1 -0
  41. package/build/connection-manager.js +432 -0
  42. package/build/connection-manager.js.map +1 -0
  43. package/build/connection-manager.test.d.ts +11 -0
  44. package/build/connection-manager.test.d.ts.map +1 -0
  45. package/build/connection-manager.test.js +114 -0
  46. package/build/connection-manager.test.js.map +1 -0
  47. package/build/console-monitor.d.ts +150 -0
  48. package/build/console-monitor.d.ts.map +1 -0
  49. package/build/console-monitor.js +454 -0
  50. package/build/console-monitor.js.map +1 -0
  51. package/build/dashboard/bundle.js +1 -0
  52. package/build/dashboard/dashboard-client.d.ts +42 -0
  53. package/build/dashboard/dashboard-client.d.ts.map +1 -0
  54. package/build/dashboard/dashboard-client.js +184 -0
  55. package/build/dashboard/dashboard-client.js.map +1 -0
  56. package/build/dashboard/dashboard-hub.d.ts +50 -0
  57. package/build/dashboard/dashboard-hub.d.ts.map +1 -0
  58. package/build/dashboard/dashboard-hub.js +654 -0
  59. package/build/dashboard/dashboard-hub.js.map +1 -0
  60. package/build/dashboard/hub-lock.d.ts +16 -0
  61. package/build/dashboard/hub-lock.d.ts.map +1 -0
  62. package/build/dashboard/hub-lock.js +77 -0
  63. package/build/dashboard/hub-lock.js.map +1 -0
  64. package/build/dashboard/index.d.ts +25 -0
  65. package/build/dashboard/index.d.ts.map +1 -0
  66. package/build/dashboard/index.js +77 -0
  67. package/build/dashboard/index.js.map +1 -0
  68. package/build/dashboard/types.d.ts +83 -0
  69. package/build/dashboard/types.d.ts.map +1 -0
  70. package/build/dashboard/types.js +6 -0
  71. package/build/dashboard/types.js.map +1 -0
  72. package/build/debug-logger.d.ts +85 -0
  73. package/build/debug-logger.d.ts.map +1 -0
  74. package/build/debug-logger.js +195 -0
  75. package/build/debug-logger.js.map +1 -0
  76. package/build/debugger-aware-wrapper.d.ts +37 -0
  77. package/build/debugger-aware-wrapper.d.ts.map +1 -0
  78. package/build/debugger-aware-wrapper.js +128 -0
  79. package/build/debugger-aware-wrapper.js.map +1 -0
  80. package/build/dom-change-monitor.d.ts +92 -0
  81. package/build/dom-change-monitor.d.ts.map +1 -0
  82. package/build/dom-change-monitor.js +437 -0
  83. package/build/dom-change-monitor.js.map +1 -0
  84. package/build/element-collector.d.ts +16 -0
  85. package/build/element-collector.d.ts.map +1 -0
  86. package/build/element-collector.js +207 -0
  87. package/build/element-collector.js.map +1 -0
  88. package/build/error-helpers.d.ts +43 -0
  89. package/build/error-helpers.d.ts.map +1 -0
  90. package/build/error-helpers.js +143 -0
  91. package/build/error-helpers.js.map +1 -0
  92. package/build/formatters/console-formatter.d.ts +160 -0
  93. package/build/formatters/console-formatter.d.ts.map +1 -0
  94. package/build/formatters/console-formatter.js +338 -0
  95. package/build/formatters/console-formatter.js.map +1 -0
  96. package/build/helpers/parser-plugins.d.ts +44 -0
  97. package/build/helpers/parser-plugins.d.ts.map +1 -0
  98. package/build/helpers/parser-plugins.js +102 -0
  99. package/build/helpers/parser-plugins.js.map +1 -0
  100. package/build/helpers/paths.d.ts +79 -0
  101. package/build/helpers/paths.d.ts.map +1 -0
  102. package/build/helpers/paths.js +168 -0
  103. package/build/helpers/paths.js.map +1 -0
  104. package/build/helpers/process-liveness.d.ts +24 -0
  105. package/build/helpers/process-liveness.d.ts.map +1 -0
  106. package/build/helpers/process-liveness.js +36 -0
  107. package/build/helpers/process-liveness.js.map +1 -0
  108. package/build/helpers/process-liveness.test.d.ts +2 -0
  109. package/build/helpers/process-liveness.test.d.ts.map +1 -0
  110. package/build/helpers/process-liveness.test.js +40 -0
  111. package/build/helpers/process-liveness.test.js.map +1 -0
  112. package/build/helpers/sequence-tree.d.ts +27 -0
  113. package/build/helpers/sequence-tree.d.ts.map +1 -0
  114. package/build/helpers/sequence-tree.js +80 -0
  115. package/build/helpers/sequence-tree.js.map +1 -0
  116. package/build/helpers/sequence-tree.test.d.ts +2 -0
  117. package/build/helpers/sequence-tree.test.d.ts.map +1 -0
  118. package/build/helpers/sequence-tree.test.js +122 -0
  119. package/build/helpers/sequence-tree.test.js.map +1 -0
  120. package/build/index.d.ts +3 -0
  121. package/build/index.d.ts.map +1 -0
  122. package/build/index.js +2007 -0
  123. package/build/index.js.map +1 -0
  124. package/build/interaction-recorder.d.ts +214 -0
  125. package/build/interaction-recorder.d.ts.map +1 -0
  126. package/build/interaction-recorder.js +2007 -0
  127. package/build/interaction-recorder.js.map +1 -0
  128. package/build/issue-tracker.d.ts +107 -0
  129. package/build/issue-tracker.d.ts.map +1 -0
  130. package/build/issue-tracker.js +582 -0
  131. package/build/issue-tracker.js.map +1 -0
  132. package/build/issue-tracker.test.d.ts +2 -0
  133. package/build/issue-tracker.test.d.ts.map +1 -0
  134. package/build/issue-tracker.test.js +88 -0
  135. package/build/issue-tracker.test.js.map +1 -0
  136. package/build/launch-chrome-port.test.d.ts +14 -0
  137. package/build/launch-chrome-port.test.d.ts.map +1 -0
  138. package/build/launch-chrome-port.test.js +97 -0
  139. package/build/launch-chrome-port.test.js.map +1 -0
  140. package/build/log-processor/orchestrator.d.ts +32 -0
  141. package/build/log-processor/orchestrator.d.ts.map +1 -0
  142. package/build/log-processor/orchestrator.js +27 -0
  143. package/build/log-processor/orchestrator.js.map +1 -0
  144. package/build/logpoint-execution-tracker.d.ts +57 -0
  145. package/build/logpoint-execution-tracker.d.ts.map +1 -0
  146. package/build/logpoint-execution-tracker.js +112 -0
  147. package/build/logpoint-execution-tracker.js.map +1 -0
  148. package/build/mcp-supervisor.d.ts +3 -0
  149. package/build/mcp-supervisor.d.ts.map +1 -0
  150. package/build/mcp-supervisor.js +211 -0
  151. package/build/mcp-supervisor.js.map +1 -0
  152. package/build/messages.d.ts +165 -0
  153. package/build/messages.d.ts.map +1 -0
  154. package/build/messages.js +574 -0
  155. package/build/messages.js.map +1 -0
  156. package/build/network-monitor.d.ts +172 -0
  157. package/build/network-monitor.d.ts.map +1 -0
  158. package/build/network-monitor.js +350 -0
  159. package/build/network-monitor.js.map +1 -0
  160. package/build/overlays.d.ts +49 -0
  161. package/build/overlays.d.ts.map +1 -0
  162. package/build/overlays.js +409 -0
  163. package/build/overlays.js.map +1 -0
  164. package/build/persistent-profiles.test.d.ts +15 -0
  165. package/build/persistent-profiles.test.d.ts.map +1 -0
  166. package/build/persistent-profiles.test.js +470 -0
  167. package/build/persistent-profiles.test.js.map +1 -0
  168. package/build/port-reserver.d.ts +32 -0
  169. package/build/port-reserver.d.ts.map +1 -0
  170. package/build/port-reserver.js +118 -0
  171. package/build/port-reserver.js.map +1 -0
  172. package/build/puppeteer-manager.d.ts +47 -0
  173. package/build/puppeteer-manager.d.ts.map +1 -0
  174. package/build/puppeteer-manager.js +116 -0
  175. package/build/puppeteer-manager.js.map +1 -0
  176. package/build/recording-coverage.test.d.ts +12 -0
  177. package/build/recording-coverage.test.d.ts.map +1 -0
  178. package/build/recording-coverage.test.js +72 -0
  179. package/build/recording-coverage.test.js.map +1 -0
  180. package/build/reference-validator.d.ts +47 -0
  181. package/build/reference-validator.d.ts.map +1 -0
  182. package/build/reference-validator.js +86 -0
  183. package/build/reference-validator.js.map +1 -0
  184. package/build/reference-validator.test.d.ts +5 -0
  185. package/build/reference-validator.test.d.ts.map +1 -0
  186. package/build/reference-validator.test.js +243 -0
  187. package/build/reference-validator.test.js.map +1 -0
  188. package/build/replay-cursor.d.ts +27 -0
  189. package/build/replay-cursor.d.ts.map +1 -0
  190. package/build/replay-cursor.js +240 -0
  191. package/build/replay-cursor.js.map +1 -0
  192. package/build/runners/docker-compose-runner.d.ts +69 -0
  193. package/build/runners/docker-compose-runner.d.ts.map +1 -0
  194. package/build/runners/docker-compose-runner.js +438 -0
  195. package/build/runners/docker-compose-runner.js.map +1 -0
  196. package/build/runners/docker-runner.d.ts +65 -0
  197. package/build/runners/docker-runner.d.ts.map +1 -0
  198. package/build/runners/docker-runner.js +414 -0
  199. package/build/runners/docker-runner.js.map +1 -0
  200. package/build/runners/index.d.ts +19 -0
  201. package/build/runners/index.d.ts.map +1 -0
  202. package/build/runners/index.js +35 -0
  203. package/build/runners/index.js.map +1 -0
  204. package/build/runners/native-runner.d.ts +95 -0
  205. package/build/runners/native-runner.d.ts.map +1 -0
  206. package/build/runners/native-runner.js +370 -0
  207. package/build/runners/native-runner.js.map +1 -0
  208. package/build/runners/types.d.ts +147 -0
  209. package/build/runners/types.d.ts.map +1 -0
  210. package/build/runners/types.js +25 -0
  211. package/build/runners/types.js.map +1 -0
  212. package/build/self-restart.d.ts +12 -0
  213. package/build/self-restart.d.ts.map +1 -0
  214. package/build/self-restart.js +41 -0
  215. package/build/self-restart.js.map +1 -0
  216. package/build/self-restart.test.d.ts +2 -0
  217. package/build/self-restart.test.d.ts.map +1 -0
  218. package/build/self-restart.test.js +34 -0
  219. package/build/self-restart.test.js.map +1 -0
  220. package/build/server-claims.d.ts +161 -0
  221. package/build/server-claims.d.ts.map +1 -0
  222. package/build/server-claims.js +382 -0
  223. package/build/server-claims.js.map +1 -0
  224. package/build/server-claims.test.d.ts +2 -0
  225. package/build/server-claims.test.d.ts.map +1 -0
  226. package/build/server-claims.test.js +197 -0
  227. package/build/server-claims.test.js.map +1 -0
  228. package/build/server-manager.d.ts +438 -0
  229. package/build/server-manager.d.ts.map +1 -0
  230. package/build/server-manager.js +1674 -0
  231. package/build/server-manager.js.map +1 -0
  232. package/build/server-manager.test.d.ts +17 -0
  233. package/build/server-manager.test.d.ts.map +1 -0
  234. package/build/server-manager.test.js +122 -0
  235. package/build/server-manager.test.js.map +1 -0
  236. package/build/server-ownership.test.d.ts +2 -0
  237. package/build/server-ownership.test.d.ts.map +1 -0
  238. package/build/server-ownership.test.js +238 -0
  239. package/build/server-ownership.test.js.map +1 -0
  240. package/build/server-watcher.d.ts +21 -0
  241. package/build/server-watcher.d.ts.map +1 -0
  242. package/build/server-watcher.js +105 -0
  243. package/build/server-watcher.js.map +1 -0
  244. package/build/session-detector.d.ts +47 -0
  245. package/build/session-detector.d.ts.map +1 -0
  246. package/build/session-detector.js +229 -0
  247. package/build/session-detector.js.map +1 -0
  248. package/build/session-detector.test.d.ts +9 -0
  249. package/build/session-detector.test.d.ts.map +1 -0
  250. package/build/session-detector.test.js +45 -0
  251. package/build/session-detector.test.js.map +1 -0
  252. package/build/sourcemap-handler.d.ts +114 -0
  253. package/build/sourcemap-handler.d.ts.map +1 -0
  254. package/build/sourcemap-handler.js +479 -0
  255. package/build/sourcemap-handler.js.map +1 -0
  256. package/build/supervisor/child-manager.d.ts +43 -0
  257. package/build/supervisor/child-manager.d.ts.map +1 -0
  258. package/build/supervisor/child-manager.js +148 -0
  259. package/build/supervisor/child-manager.js.map +1 -0
  260. package/build/supervisor/child-manager.test.d.ts +2 -0
  261. package/build/supervisor/child-manager.test.d.ts.map +1 -0
  262. package/build/supervisor/child-manager.test.js +107 -0
  263. package/build/supervisor/child-manager.test.js.map +1 -0
  264. package/build/supervisor/client-watcher.d.ts +48 -0
  265. package/build/supervisor/client-watcher.d.ts.map +1 -0
  266. package/build/supervisor/client-watcher.js +186 -0
  267. package/build/supervisor/client-watcher.js.map +1 -0
  268. package/build/supervisor/client-watcher.test.d.ts +2 -0
  269. package/build/supervisor/client-watcher.test.d.ts.map +1 -0
  270. package/build/supervisor/client-watcher.test.js +121 -0
  271. package/build/supervisor/client-watcher.test.js.map +1 -0
  272. package/build/supervisor/idle-config.d.ts +30 -0
  273. package/build/supervisor/idle-config.d.ts.map +1 -0
  274. package/build/supervisor/idle-config.js +76 -0
  275. package/build/supervisor/idle-config.js.map +1 -0
  276. package/build/supervisor/idle-config.test.d.ts +2 -0
  277. package/build/supervisor/idle-config.test.d.ts.map +1 -0
  278. package/build/supervisor/idle-config.test.js +119 -0
  279. package/build/supervisor/idle-config.test.js.map +1 -0
  280. package/build/supervisor/ndjson-reader.d.ts +47 -0
  281. package/build/supervisor/ndjson-reader.d.ts.map +1 -0
  282. package/build/supervisor/ndjson-reader.js +75 -0
  283. package/build/supervisor/ndjson-reader.js.map +1 -0
  284. package/build/supervisor/ndjson-reader.test.d.ts +2 -0
  285. package/build/supervisor/ndjson-reader.test.d.ts.map +1 -0
  286. package/build/supervisor/ndjson-reader.test.js +92 -0
  287. package/build/supervisor/ndjson-reader.test.js.map +1 -0
  288. package/build/supervisor/pidfile.d.ts +11 -0
  289. package/build/supervisor/pidfile.d.ts.map +1 -0
  290. package/build/supervisor/pidfile.js +29 -0
  291. package/build/supervisor/pidfile.js.map +1 -0
  292. package/build/supervisor/pidfile.test.d.ts +8 -0
  293. package/build/supervisor/pidfile.test.d.ts.map +1 -0
  294. package/build/supervisor/pidfile.test.js +46 -0
  295. package/build/supervisor/pidfile.test.js.map +1 -0
  296. package/build/supervisor/restart-coordinator.d.ts +73 -0
  297. package/build/supervisor/restart-coordinator.d.ts.map +1 -0
  298. package/build/supervisor/restart-coordinator.js +300 -0
  299. package/build/supervisor/restart-coordinator.js.map +1 -0
  300. package/build/supervisor/restart-coordinator.test.d.ts +2 -0
  301. package/build/supervisor/restart-coordinator.test.d.ts.map +1 -0
  302. package/build/supervisor/restart-coordinator.test.js +434 -0
  303. package/build/supervisor/restart-coordinator.test.js.map +1 -0
  304. package/build/test-support/fake-execute-tool-call.d.ts +11 -0
  305. package/build/test-support/fake-execute-tool-call.d.ts.map +1 -0
  306. package/build/test-support/fake-execute-tool-call.js +19 -0
  307. package/build/test-support/fake-execute-tool-call.js.map +1 -0
  308. package/build/tool-error.d.ts +14 -0
  309. package/build/tool-error.d.ts.map +1 -0
  310. package/build/tool-error.js +18 -0
  311. package/build/tool-error.js.map +1 -0
  312. package/build/tool-response.d.ts +293 -0
  313. package/build/tool-response.d.ts.map +1 -0
  314. package/build/tool-response.js +365 -0
  315. package/build/tool-response.js.map +1 -0
  316. package/build/tools/assert-tools.d.ts +62 -0
  317. package/build/tools/assert-tools.d.ts.map +1 -0
  318. package/build/tools/assert-tools.js +330 -0
  319. package/build/tools/assert-tools.js.map +1 -0
  320. package/build/tools/breakpoint-await-abort.test.d.ts +2 -0
  321. package/build/tools/breakpoint-await-abort.test.d.ts.map +1 -0
  322. package/build/tools/breakpoint-await-abort.test.js +124 -0
  323. package/build/tools/breakpoint-await-abort.test.js.map +1 -0
  324. package/build/tools/breakpoint-tools.d.ts +99 -0
  325. package/build/tools/breakpoint-tools.d.ts.map +1 -0
  326. package/build/tools/breakpoint-tools.js +1197 -0
  327. package/build/tools/breakpoint-tools.js.map +1 -0
  328. package/build/tools/config-tools.d.ts +58 -0
  329. package/build/tools/config-tools.d.ts.map +1 -0
  330. package/build/tools/config-tools.js +180 -0
  331. package/build/tools/config-tools.js.map +1 -0
  332. package/build/tools/connection-routing.test.d.ts +12 -0
  333. package/build/tools/connection-routing.test.d.ts.map +1 -0
  334. package/build/tools/connection-routing.test.js +206 -0
  335. package/build/tools/connection-routing.test.js.map +1 -0
  336. package/build/tools/console-tools.d.ts +84 -0
  337. package/build/tools/console-tools.d.ts.map +1 -0
  338. package/build/tools/console-tools.js +241 -0
  339. package/build/tools/console-tools.js.map +1 -0
  340. package/build/tools/content-tools.d.ts +74 -0
  341. package/build/tools/content-tools.d.ts.map +1 -0
  342. package/build/tools/content-tools.js +582 -0
  343. package/build/tools/content-tools.js.map +1 -0
  344. package/build/tools/dashboard-tools.d.ts +38 -0
  345. package/build/tools/dashboard-tools.d.ts.map +1 -0
  346. package/build/tools/dashboard-tools.js +123 -0
  347. package/build/tools/dashboard-tools.js.map +1 -0
  348. package/build/tools/dom-tools.d.ts +41 -0
  349. package/build/tools/dom-tools.d.ts.map +1 -0
  350. package/build/tools/dom-tools.js +277 -0
  351. package/build/tools/dom-tools.js.map +1 -0
  352. package/build/tools/download-tools.d.ts +55 -0
  353. package/build/tools/download-tools.d.ts.map +1 -0
  354. package/build/tools/download-tools.js +280 -0
  355. package/build/tools/download-tools.js.map +1 -0
  356. package/build/tools/execution-tools.d.ts +40 -0
  357. package/build/tools/execution-tools.d.ts.map +1 -0
  358. package/build/tools/execution-tools.js +121 -0
  359. package/build/tools/execution-tools.js.map +1 -0
  360. package/build/tools/input-abort.test.d.ts +2 -0
  361. package/build/tools/input-abort.test.d.ts.map +1 -0
  362. package/build/tools/input-abort.test.js +227 -0
  363. package/build/tools/input-abort.test.js.map +1 -0
  364. package/build/tools/input-ambiguous-selector.test.d.ts +2 -0
  365. package/build/tools/input-ambiguous-selector.test.d.ts.map +1 -0
  366. package/build/tools/input-ambiguous-selector.test.js +34 -0
  367. package/build/tools/input-ambiguous-selector.test.js.map +1 -0
  368. package/build/tools/input-tools.d.ts +168 -0
  369. package/build/tools/input-tools.d.ts.map +1 -0
  370. package/build/tools/input-tools.js +1397 -0
  371. package/build/tools/input-tools.js.map +1 -0
  372. package/build/tools/inspection-tools.d.ts +112 -0
  373. package/build/tools/inspection-tools.d.ts.map +1 -0
  374. package/build/tools/inspection-tools.js +637 -0
  375. package/build/tools/inspection-tools.js.map +1 -0
  376. package/build/tools/interpolation.d.ts +30 -0
  377. package/build/tools/interpolation.d.ts.map +1 -0
  378. package/build/tools/interpolation.js +143 -0
  379. package/build/tools/interpolation.js.map +1 -0
  380. package/build/tools/issues-abort-hygiene.test.d.ts +2 -0
  381. package/build/tools/issues-abort-hygiene.test.d.ts.map +1 -0
  382. package/build/tools/issues-abort-hygiene.test.js +79 -0
  383. package/build/tools/issues-abort-hygiene.test.js.map +1 -0
  384. package/build/tools/issues-tools.d.ts +98 -0
  385. package/build/tools/issues-tools.d.ts.map +1 -0
  386. package/build/tools/issues-tools.js +821 -0
  387. package/build/tools/issues-tools.js.map +1 -0
  388. package/build/tools/issues-tools.test.d.ts +8 -0
  389. package/build/tools/issues-tools.test.d.ts.map +1 -0
  390. package/build/tools/issues-tools.test.js +184 -0
  391. package/build/tools/issues-tools.test.js.map +1 -0
  392. package/build/tools/modal-tools.d.ts +73 -0
  393. package/build/tools/modal-tools.d.ts.map +1 -0
  394. package/build/tools/modal-tools.js +161 -0
  395. package/build/tools/modal-tools.js.map +1 -0
  396. package/build/tools/network-tools.d.ts +72 -0
  397. package/build/tools/network-tools.d.ts.map +1 -0
  398. package/build/tools/network-tools.js +432 -0
  399. package/build/tools/network-tools.js.map +1 -0
  400. package/build/tools/page-tools.d.ts +78 -0
  401. package/build/tools/page-tools.d.ts.map +1 -0
  402. package/build/tools/page-tools.js +255 -0
  403. package/build/tools/page-tools.js.map +1 -0
  404. package/build/tools/plugin-tools.d.ts +16 -0
  405. package/build/tools/plugin-tools.d.ts.map +1 -0
  406. package/build/tools/plugin-tools.js +17 -0
  407. package/build/tools/plugin-tools.js.map +1 -0
  408. package/build/tools/replay-abort.test.d.ts +2 -0
  409. package/build/tools/replay-abort.test.d.ts.map +1 -0
  410. package/build/tools/replay-abort.test.js +259 -0
  411. package/build/tools/replay-abort.test.js.map +1 -0
  412. package/build/tools/replay-add-conditional.test.d.ts +2 -0
  413. package/build/tools/replay-add-conditional.test.d.ts.map +1 -0
  414. package/build/tools/replay-add-conditional.test.js +205 -0
  415. package/build/tools/replay-add-conditional.test.js.map +1 -0
  416. package/build/tools/replay-background-run.test.d.ts +2 -0
  417. package/build/tools/replay-background-run.test.d.ts.map +1 -0
  418. package/build/tools/replay-background-run.test.js +176 -0
  419. package/build/tools/replay-background-run.test.js.map +1 -0
  420. package/build/tools/replay-capture-variables.test.d.ts +2 -0
  421. package/build/tools/replay-capture-variables.test.d.ts.map +1 -0
  422. package/build/tools/replay-capture-variables.test.js +331 -0
  423. package/build/tools/replay-capture-variables.test.js.map +1 -0
  424. package/build/tools/replay-conditional-idb-and-launch.test.d.ts +11 -0
  425. package/build/tools/replay-conditional-idb-and-launch.test.d.ts.map +1 -0
  426. package/build/tools/replay-conditional-idb-and-launch.test.js +429 -0
  427. package/build/tools/replay-conditional-idb-and-launch.test.js.map +1 -0
  428. package/build/tools/replay-conditional-timeout.test.d.ts +14 -0
  429. package/build/tools/replay-conditional-timeout.test.d.ts.map +1 -0
  430. package/build/tools/replay-conditional-timeout.test.js +126 -0
  431. package/build/tools/replay-conditional-timeout.test.js.map +1 -0
  432. package/build/tools/replay-declare.test.d.ts +2 -0
  433. package/build/tools/replay-declare.test.d.ts.map +1 -0
  434. package/build/tools/replay-declare.test.js +106 -0
  435. package/build/tools/replay-declare.test.js.map +1 -0
  436. package/build/tools/replay-declared-browser-cleanup.test.d.ts +2 -0
  437. package/build/tools/replay-declared-browser-cleanup.test.d.ts.map +1 -0
  438. package/build/tools/replay-declared-browser-cleanup.test.js +103 -0
  439. package/build/tools/replay-declared-browser-cleanup.test.js.map +1 -0
  440. package/build/tools/replay-declared-profile.test.d.ts +2 -0
  441. package/build/tools/replay-declared-profile.test.d.ts.map +1 -0
  442. package/build/tools/replay-declared-profile.test.js +116 -0
  443. package/build/tools/replay-declared-profile.test.js.map +1 -0
  444. package/build/tools/replay-executor.d.ts +556 -0
  445. package/build/tools/replay-executor.d.ts.map +1 -0
  446. package/build/tools/replay-executor.js +2347 -0
  447. package/build/tools/replay-executor.js.map +1 -0
  448. package/build/tools/replay-folder-save.test.d.ts +2 -0
  449. package/build/tools/replay-folder-save.test.d.ts.map +1 -0
  450. package/build/tools/replay-folder-save.test.js +63 -0
  451. package/build/tools/replay-folder-save.test.js.map +1 -0
  452. package/build/tools/replay-formatters.d.ts +148 -0
  453. package/build/tools/replay-formatters.d.ts.map +1 -0
  454. package/build/tools/replay-formatters.js +856 -0
  455. package/build/tools/replay-formatters.js.map +1 -0
  456. package/build/tools/replay-kill-chrome-on-finish.test.d.ts +2 -0
  457. package/build/tools/replay-kill-chrome-on-finish.test.d.ts.map +1 -0
  458. package/build/tools/replay-kill-chrome-on-finish.test.js +175 -0
  459. package/build/tools/replay-kill-chrome-on-finish.test.js.map +1 -0
  460. package/build/tools/replay-multi-connection.test.d.ts +2 -0
  461. package/build/tools/replay-multi-connection.test.d.ts.map +1 -0
  462. package/build/tools/replay-multi-connection.test.js +547 -0
  463. package/build/tools/replay-multi-connection.test.js.map +1 -0
  464. package/build/tools/replay-output-and-record-options.test.d.ts +2 -0
  465. package/build/tools/replay-output-and-record-options.test.d.ts.map +1 -0
  466. package/build/tools/replay-output-and-record-options.test.js +240 -0
  467. package/build/tools/replay-output-and-record-options.test.js.map +1 -0
  468. package/build/tools/replay-production-error-shape.test.d.ts +7 -0
  469. package/build/tools/replay-production-error-shape.test.d.ts.map +1 -0
  470. package/build/tools/replay-production-error-shape.test.js +93 -0
  471. package/build/tools/replay-production-error-shape.test.js.map +1 -0
  472. package/build/tools/replay-rebase.test.d.ts +2 -0
  473. package/build/tools/replay-rebase.test.d.ts.map +1 -0
  474. package/build/tools/replay-rebase.test.js +58 -0
  475. package/build/tools/replay-rebase.test.js.map +1 -0
  476. package/build/tools/replay-retry-classification.test.d.ts +9 -0
  477. package/build/tools/replay-retry-classification.test.d.ts.map +1 -0
  478. package/build/tools/replay-retry-classification.test.js +57 -0
  479. package/build/tools/replay-retry-classification.test.js.map +1 -0
  480. package/build/tools/replay-run-registry.d.ts +55 -0
  481. package/build/tools/replay-run-registry.d.ts.map +1 -0
  482. package/build/tools/replay-run-registry.js +65 -0
  483. package/build/tools/replay-run-registry.js.map +1 -0
  484. package/build/tools/replay-sequence-reload.test.d.ts +2 -0
  485. package/build/tools/replay-sequence-reload.test.d.ts.map +1 -0
  486. package/build/tools/replay-sequence-reload.test.js +82 -0
  487. package/build/tools/replay-sequence-reload.test.js.map +1 -0
  488. package/build/tools/replay-socket-scope.test.d.ts +2 -0
  489. package/build/tools/replay-socket-scope.test.d.ts.map +1 -0
  490. package/build/tools/replay-socket-scope.test.js +75 -0
  491. package/build/tools/replay-socket-scope.test.js.map +1 -0
  492. package/build/tools/replay-step-connection.test.d.ts +2 -0
  493. package/build/tools/replay-step-connection.test.d.ts.map +1 -0
  494. package/build/tools/replay-step-connection.test.js +292 -0
  495. package/build/tools/replay-step-connection.test.js.map +1 -0
  496. package/build/tools/replay-step-timeout.test.d.ts +2 -0
  497. package/build/tools/replay-step-timeout.test.d.ts.map +1 -0
  498. package/build/tools/replay-step-timeout.test.js +204 -0
  499. package/build/tools/replay-step-timeout.test.js.map +1 -0
  500. package/build/tools/replay-tags.test.d.ts +2 -0
  501. package/build/tools/replay-tags.test.d.ts.map +1 -0
  502. package/build/tools/replay-tags.test.js +99 -0
  503. package/build/tools/replay-tags.test.js.map +1 -0
  504. package/build/tools/replay-teardown-and-foreach.test.d.ts +13 -0
  505. package/build/tools/replay-teardown-and-foreach.test.d.ts.map +1 -0
  506. package/build/tools/replay-teardown-and-foreach.test.js +318 -0
  507. package/build/tools/replay-teardown-and-foreach.test.js.map +1 -0
  508. package/build/tools/replay-tool-name-validation.test.d.ts +2 -0
  509. package/build/tools/replay-tool-name-validation.test.d.ts.map +1 -0
  510. package/build/tools/replay-tool-name-validation.test.js +280 -0
  511. package/build/tools/replay-tool-name-validation.test.js.map +1 -0
  512. package/build/tools/replay-tools.d.ts +288 -0
  513. package/build/tools/replay-tools.d.ts.map +1 -0
  514. package/build/tools/replay-tools.js +2994 -0
  515. package/build/tools/replay-tools.js.map +1 -0
  516. package/build/tools/request-abort.test.d.ts +2 -0
  517. package/build/tools/request-abort.test.d.ts.map +1 -0
  518. package/build/tools/request-abort.test.js +130 -0
  519. package/build/tools/request-abort.test.js.map +1 -0
  520. package/build/tools/request-tools.d.ts +56 -0
  521. package/build/tools/request-tools.d.ts.map +1 -0
  522. package/build/tools/request-tools.js +174 -0
  523. package/build/tools/request-tools.js.map +1 -0
  524. package/build/tools/screenshot-tools.d.ts +135 -0
  525. package/build/tools/screenshot-tools.d.ts.map +1 -0
  526. package/build/tools/screenshot-tools.js +636 -0
  527. package/build/tools/screenshot-tools.js.map +1 -0
  528. package/build/tools/server-tools.d.ts +88 -0
  529. package/build/tools/server-tools.d.ts.map +1 -0
  530. package/build/tools/server-tools.js +485 -0
  531. package/build/tools/server-tools.js.map +1 -0
  532. package/build/tools/source-tools.d.ts +46 -0
  533. package/build/tools/source-tools.d.ts.map +1 -0
  534. package/build/tools/source-tools.js +47 -0
  535. package/build/tools/source-tools.js.map +1 -0
  536. package/build/tools/storage-tools.d.ts +141 -0
  537. package/build/tools/storage-tools.d.ts.map +1 -0
  538. package/build/tools/storage-tools.js +894 -0
  539. package/build/tools/storage-tools.js.map +1 -0
  540. package/build/tools/storage-tools.test.d.ts +13 -0
  541. package/build/tools/storage-tools.test.d.ts.map +1 -0
  542. package/build/tools/storage-tools.test.js +707 -0
  543. package/build/tools/storage-tools.test.js.map +1 -0
  544. package/build/tools/tab-tools.d.ts +42 -0
  545. package/build/tools/tab-tools.d.ts.map +1 -0
  546. package/build/tools/tab-tools.js +296 -0
  547. package/build/tools/tab-tools.js.map +1 -0
  548. package/build/tools/wait-tools.d.ts +85 -0
  549. package/build/tools/wait-tools.d.ts.map +1 -0
  550. package/build/tools/wait-tools.js +236 -0
  551. package/build/tools/wait-tools.js.map +1 -0
  552. package/build/tools/wait-tools.test.d.ts +9 -0
  553. package/build/tools/wait-tools.test.d.ts.map +1 -0
  554. package/build/tools/wait-tools.test.js +193 -0
  555. package/build/tools/wait-tools.test.js.map +1 -0
  556. package/build/types.d.ts +133 -0
  557. package/build/types.d.ts.map +1 -0
  558. package/build/types.js +5 -0
  559. package/build/types.js.map +1 -0
  560. package/build/ui-verifier.d.ts +88 -0
  561. package/build/ui-verifier.d.ts.map +1 -0
  562. package/build/ui-verifier.js +540 -0
  563. package/build/ui-verifier.js.map +1 -0
  564. package/build/utils/abort.d.ts +68 -0
  565. package/build/utils/abort.d.ts.map +1 -0
  566. package/build/utils/abort.js +159 -0
  567. package/build/utils/abort.js.map +1 -0
  568. package/build/utils/abort.test.d.ts +2 -0
  569. package/build/utils/abort.test.d.ts.map +1 -0
  570. package/build/utils/abort.test.js +174 -0
  571. package/build/utils/abort.test.js.map +1 -0
  572. package/build/utils/modal-detection-core.d.ts +150 -0
  573. package/build/utils/modal-detection-core.d.ts.map +1 -0
  574. package/build/utils/modal-detection-core.js +332 -0
  575. package/build/utils/modal-detection-core.js.map +1 -0
  576. package/build/utils/modal-detection-core.test.d.ts +5 -0
  577. package/build/utils/modal-detection-core.test.d.ts.map +1 -0
  578. package/build/utils/modal-detection-core.test.js +492 -0
  579. package/build/utils/modal-detection-core.test.js.map +1 -0
  580. package/build/utils/modal-detector.d.ts +29 -0
  581. package/build/utils/modal-detector.d.ts.map +1 -0
  582. package/build/utils/modal-detector.js +281 -0
  583. package/build/utils/modal-detector.js.map +1 -0
  584. package/build/utils/modal-dismissal.d.ts +32 -0
  585. package/build/utils/modal-dismissal.d.ts.map +1 -0
  586. package/build/utils/modal-dismissal.js +190 -0
  587. package/build/utils/modal-dismissal.js.map +1 -0
  588. package/build/utils/port-check.d.ts +31 -0
  589. package/build/utils/port-check.d.ts.map +1 -0
  590. package/build/utils/port-check.js +87 -0
  591. package/build/utils/port-check.js.map +1 -0
  592. package/build/utils/port-check.test.d.ts +2 -0
  593. package/build/utils/port-check.test.d.ts.map +1 -0
  594. package/build/utils/port-check.test.js +66 -0
  595. package/build/utils/port-check.test.js.map +1 -0
  596. package/build/utils/selector-resolver.d.ts +66 -0
  597. package/build/utils/selector-resolver.d.ts.map +1 -0
  598. package/build/utils/selector-resolver.js +200 -0
  599. package/build/utils/selector-resolver.js.map +1 -0
  600. package/build/validation-helpers.d.ts +41 -0
  601. package/build/validation-helpers.d.ts.map +1 -0
  602. package/build/validation-helpers.js +236 -0
  603. package/build/validation-helpers.js.map +1 -0
  604. package/build/window-sizing.d.ts +26 -0
  605. package/build/window-sizing.d.ts.map +1 -0
  606. package/build/window-sizing.js +87 -0
  607. package/build/window-sizing.js.map +1 -0
  608. package/build/window-sizing.test.d.ts +8 -0
  609. package/build/window-sizing.test.d.ts.map +1 -0
  610. package/build/window-sizing.test.js +113 -0
  611. package/build/window-sizing.test.js.map +1 -0
  612. package/docs/README.md +74 -0
  613. package/docs/automation.md +562 -0
  614. package/docs/debugging.md +229 -0
  615. package/docs/installation.md +133 -0
  616. package/docs/instructions.md +227 -0
  617. package/docs/mcp-instructions.md +15 -0
  618. package/docs/message-system.md +327 -0
  619. package/docs/messages.md +3480 -0
  620. package/docs/parser-plugins.md +132 -0
  621. package/docs/replay.md +1411 -0
  622. package/docs/troubleshooting.md +272 -0
  623. package/docs/working_with_llm_notes.md +5 -0
  624. package/package.json +87 -0
  625. package/skills/devharness/SKILL.md +195 -0
  626. package/skills/devharness/references/sequences.md +613 -0
  627. package/skills/devharness/references/tool-categories.md +99 -0
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Spawns and kills the real MCP server as a child process.
3
+ *
4
+ * Mirrors the SIGTERM(process-group)->grace-period->SIGKILL(process-group)
5
+ * escalation pattern used elsewhere in this repo (src/runners/native-runner.ts
6
+ * `stop()`), but - unlike that runner, which only ever holds a bare pid it
7
+ * may have recovered across a process restart - this always has a live
8
+ * ChildProcess handle, so completion is driven by Node's own 'exit' event
9
+ * rather than external process.kill(pid, 0) polling. That avoids any race
10
+ * between two independent "is it dead yet" mechanisms.
11
+ *
12
+ * Deliberately does NOT call `.unref()` on the spawned child (unlike
13
+ * native-runner.ts, whose children are meant to outlive it) - the child here
14
+ * must never outlive this supervisor.
15
+ */
16
+ import { spawn } from 'child_process';
17
+ export class ChildManager {
18
+ opts;
19
+ child = null;
20
+ onExitCallback = null;
21
+ killGraceMs;
22
+ suspendGraceMs;
23
+ constructor(opts) {
24
+ this.opts = opts;
25
+ this.killGraceMs = opts.killGraceMs ?? 3000;
26
+ this.suspendGraceMs = opts.suspendGraceMs ?? 15000;
27
+ }
28
+ /** Registered once; fires for every child this instance ever spawns. */
29
+ onExit(callback) {
30
+ this.onExitCallback = callback;
31
+ }
32
+ spawn() {
33
+ const child = spawn(this.opts.execPath, [this.opts.scriptPath, ...this.opts.extraArgs], {
34
+ cwd: this.opts.cwd,
35
+ stdio: ['pipe', 'pipe', 'inherit'],
36
+ detached: true,
37
+ });
38
+ this.child = child;
39
+ child.on('exit', (code, signal) => {
40
+ if (this.child === child) {
41
+ this.child = null;
42
+ }
43
+ this.onExitCallback?.({ code, signal });
44
+ });
45
+ if (!child.stdout || !child.stdin) {
46
+ throw new Error('Child process did not provide piped stdio streams');
47
+ }
48
+ return { stdout: child.stdout, stdin: child.stdin };
49
+ }
50
+ isRunning() {
51
+ return this.child !== null;
52
+ }
53
+ /**
54
+ * Ask the child to release everything it holds - Chrome, managed dev
55
+ * servers, monitors - and exit, resolving once it has actually gone.
56
+ *
57
+ * SIGUSR2 rather than SIGUSR1 because Node reserves SIGUSR1 for starting its
58
+ * own inspector, and it is sent to the child alone rather than to its
59
+ * process group: the point is to give the child a chance to shut its own
60
+ * children down in order, not to kill them out from under it. If it doesn't
61
+ * finish in time, kill() escalates through the usual SIGTERM/SIGKILL path.
62
+ */
63
+ async suspend() {
64
+ const child = this.child;
65
+ if (!child || child.pid === undefined) {
66
+ return;
67
+ }
68
+ const exited = await new Promise((resolve) => {
69
+ let settled = false;
70
+ const onExit = () => {
71
+ if (settled)
72
+ return;
73
+ settled = true;
74
+ clearTimeout(timer);
75
+ resolve(true);
76
+ };
77
+ child.once('exit', onExit);
78
+ const timer = setTimeout(() => {
79
+ if (settled)
80
+ return;
81
+ settled = true;
82
+ child.off('exit', onExit);
83
+ resolve(false);
84
+ }, this.suspendGraceMs);
85
+ try {
86
+ process.kill(child.pid, 'SIGUSR2');
87
+ }
88
+ catch {
89
+ settled = true;
90
+ clearTimeout(timer);
91
+ child.off('exit', onExit);
92
+ resolve(false); // already gone, or unsignalable - let kill() settle it
93
+ }
94
+ });
95
+ if (!exited) {
96
+ await this.kill();
97
+ }
98
+ }
99
+ /** Kill the current child (if any) and resolve once it has actually exited. */
100
+ async kill() {
101
+ const child = this.child;
102
+ if (!child || child.pid === undefined) {
103
+ return;
104
+ }
105
+ const pid = child.pid;
106
+ await new Promise((resolve) => {
107
+ let settled = false;
108
+ let killTimer;
109
+ const onExit = () => {
110
+ if (settled)
111
+ return;
112
+ settled = true;
113
+ clearTimeout(killTimer);
114
+ resolve();
115
+ };
116
+ child.once('exit', onExit);
117
+ try {
118
+ process.kill(-pid, 'SIGTERM');
119
+ }
120
+ catch {
121
+ try {
122
+ process.kill(pid, 'SIGTERM');
123
+ }
124
+ catch {
125
+ // Already dead
126
+ }
127
+ }
128
+ killTimer = setTimeout(() => {
129
+ if (settled)
130
+ return;
131
+ try {
132
+ process.kill(-pid, 'SIGKILL');
133
+ }
134
+ catch {
135
+ try {
136
+ process.kill(pid, 'SIGKILL');
137
+ }
138
+ catch {
139
+ // Already dead
140
+ }
141
+ }
142
+ // Deliberately don't resolve here - wait for the real 'exit' event
143
+ // above so we never consider kill() done before the process is gone.
144
+ }, this.killGraceMs);
145
+ });
146
+ }
147
+ }
148
+ //# sourceMappingURL=child-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child-manager.js","sourceRoot":"","sources":["../../src/supervisor/child-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=child-manager.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child-manager.test.d.ts","sourceRoot":"","sources":["../../src/supervisor/child-manager.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,107 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import * as path from 'path';
3
+ import * as url from 'url';
4
+ import { ChildManager } from './child-manager.js';
5
+ const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
6
+ const FIXTURE_PATH = path.join(__dirname, '__fixtures__', 'test-child.mjs');
7
+ describe('ChildManager (real subprocess)', () => {
8
+ it('spawns a child and kill() resolves once it exits on SIGTERM', async () => {
9
+ const manager = new ChildManager({
10
+ execPath: process.execPath,
11
+ scriptPath: FIXTURE_PATH,
12
+ extraArgs: [],
13
+ cwd: __dirname,
14
+ killGraceMs: 3000,
15
+ });
16
+ manager.spawn();
17
+ expect(manager.isRunning()).toBe(true);
18
+ await manager.kill();
19
+ expect(manager.isRunning()).toBe(false);
20
+ }, 10000);
21
+ it('escalates to SIGKILL after the grace period for a child that ignores SIGTERM', async () => {
22
+ const manager = new ChildManager({
23
+ execPath: process.execPath,
24
+ scriptPath: FIXTURE_PATH,
25
+ extraArgs: ['ignore-sigterm'],
26
+ cwd: __dirname,
27
+ killGraceMs: 300, // short for test speed
28
+ });
29
+ manager.spawn();
30
+ // Give the child a moment to actually register its SIGTERM-ignoring handler
31
+ // before we send the signal, or we'd race its own startup.
32
+ await new Promise((resolve) => setTimeout(resolve, 200));
33
+ const start = Date.now();
34
+ await manager.kill();
35
+ const elapsed = Date.now() - start;
36
+ expect(manager.isRunning()).toBe(false);
37
+ // Should have waited roughly the grace period before SIGKILL landed, not exited instantly.
38
+ expect(elapsed).toBeGreaterThanOrEqual(250);
39
+ }, 10000);
40
+ it('fires onExit for a child that crashes on its own', async () => {
41
+ const manager = new ChildManager({
42
+ execPath: process.execPath,
43
+ scriptPath: FIXTURE_PATH,
44
+ extraArgs: ['crash'],
45
+ cwd: __dirname,
46
+ });
47
+ const exitInfo = await new Promise((resolve) => {
48
+ manager.onExit((info) => resolve(info));
49
+ manager.spawn();
50
+ });
51
+ expect(exitInfo.code).toBe(1);
52
+ }, 10000);
53
+ it('suspend() signals the child and resolves once it has released and exited', async () => {
54
+ const manager = new ChildManager({
55
+ execPath: process.execPath,
56
+ scriptPath: FIXTURE_PATH,
57
+ extraArgs: [],
58
+ cwd: __dirname,
59
+ suspendGraceMs: 3000,
60
+ });
61
+ manager.spawn();
62
+ await new Promise((resolve) => setTimeout(resolve, 200)); // let its handler register
63
+ await manager.suspend();
64
+ expect(manager.isRunning()).toBe(false);
65
+ }, 10000);
66
+ it('suspend() escalates to a kill for a child that never finishes releasing', async () => {
67
+ const manager = new ChildManager({
68
+ execPath: process.execPath,
69
+ scriptPath: FIXTURE_PATH,
70
+ extraArgs: ['ignore-suspend'],
71
+ cwd: __dirname,
72
+ suspendGraceMs: 300,
73
+ killGraceMs: 300,
74
+ });
75
+ manager.spawn();
76
+ await new Promise((resolve) => setTimeout(resolve, 200));
77
+ await manager.suspend();
78
+ expect(manager.isRunning()).toBe(false);
79
+ }, 10000);
80
+ it('suspend() on an already-dead child resolves immediately without throwing', async () => {
81
+ const manager = new ChildManager({
82
+ execPath: process.execPath,
83
+ scriptPath: FIXTURE_PATH,
84
+ extraArgs: ['crash'],
85
+ cwd: __dirname,
86
+ });
87
+ await new Promise((resolve) => {
88
+ manager.onExit(() => resolve());
89
+ manager.spawn();
90
+ });
91
+ await expect(manager.suspend()).resolves.toBeUndefined();
92
+ }, 10000);
93
+ it('kill() on an already-dead child resolves immediately without throwing', async () => {
94
+ const manager = new ChildManager({
95
+ execPath: process.execPath,
96
+ scriptPath: FIXTURE_PATH,
97
+ extraArgs: ['crash'],
98
+ cwd: __dirname,
99
+ });
100
+ await new Promise((resolve) => {
101
+ manager.onExit(() => resolve());
102
+ manager.spawn();
103
+ });
104
+ await expect(manager.kill()).resolves.toBeUndefined();
105
+ }, 10000);
106
+ });
107
+ //# sourceMappingURL=child-manager.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child-manager.test.js","sourceRoot":"","sources":["../../src/supervisor/child-manager.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ export interface ProcessInfo {
2
+ ppid: number;
3
+ command: string;
4
+ }
5
+ export interface ProcessProbe {
6
+ /** Parent pid + command for a pid, or null if the pid is gone/unreadable. */
7
+ info(pid: number): ProcessInfo | null;
8
+ /** Whether the pid still exists. */
9
+ isAlive(pid: number): boolean;
10
+ }
11
+ export interface ClientIdentity {
12
+ pid: number;
13
+ command: string;
14
+ }
15
+ /** Reads process info via `ps`. Unavailable on Windows, which has no `ps`. */
16
+ export declare const systemProcessProbe: ProcessProbe;
17
+ /**
18
+ * Walk up from `startPid`'s parent and return the first ancestor that isn't
19
+ * launch plumbing, or null if there is none.
20
+ */
21
+ export declare function resolveClientIdentity(startPid: number, probe: ProcessProbe, maxDepth?: number): ClientIdentity | null;
22
+ export interface ClientWatcherOptions {
23
+ /** How often to check the client is still alive (default: 60s). */
24
+ pollIntervalMs?: number;
25
+ probe?: ProcessProbe;
26
+ logStderr?: (message: string) => void;
27
+ }
28
+ /**
29
+ * Polls the resolved client's liveness and calls back once, when it dies.
30
+ * Does nothing at all when no client could be resolved.
31
+ */
32
+ export declare class ClientWatcher {
33
+ private readonly options;
34
+ private timer;
35
+ private readonly probe;
36
+ private readonly pollIntervalMs;
37
+ private readonly logStderr;
38
+ private client;
39
+ constructor(options?: ClientWatcherOptions);
40
+ getClient(): ClientIdentity | null;
41
+ /**
42
+ * Resolve the client and start watching. Returns the client it will watch,
43
+ * or null if none could be resolved (in which case nothing is watched).
44
+ */
45
+ start(startPid: number, onClientGone: (client: ClientIdentity) => void): ClientIdentity | null;
46
+ stop(): void;
47
+ }
48
+ //# sourceMappingURL=client-watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-watcher.d.ts","sourceRoot":"","sources":["../../src/supervisor/client-watcher.ts"],"names":[],"mappings":"AA+BA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC;IACtC,oCAAoC;IACpC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AA4DD,8EAA8E;AAC9E,eAAO,MAAM,kBAAkB,EAAE,YAwBhC,CAAC;AAEF;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,YAAY,EACnB,QAAQ,SAAK,GACZ,cAAc,GAAG,IAAI,CAYvB;AAED,MAAM,WAAW,oBAAoB;IACnC,mEAAmE;IACnE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED;;;GAGG;AACH,qBAAa,aAAa;IAOZ,OAAO,CAAC,QAAQ,CAAC,OAAO;IANpC,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IACtD,OAAO,CAAC,MAAM,CAA+B;gBAEhB,OAAO,GAAE,oBAAyB;IAM/D,SAAS,IAAI,cAAc,GAAG,IAAI;IAIlC;;;OAGG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,GAAG,cAAc,GAAG,IAAI;IA0B9F,IAAI,IAAI,IAAI;CAMb"}
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Watches the MCP client (Claude Code, the Claude desktop app, an IDE) that
3
+ * this supervisor was launched to serve, and reports when it is gone.
4
+ *
5
+ * WHY THIS EXISTS
6
+ * The supervisor already shuts down on stdin end/close, which is supposed to
7
+ * catch a client that dies without signalling. It does not fire when the
8
+ * client was launched through `npx`: the `npm exec` wrapper sits between the
9
+ * client and this process, survives the client's death (reparented to init),
10
+ * and keeps holding the write end of our stdin pipe. No EOF ever arrives, so
11
+ * the whole tree - supervisor, real server, its Chrome and dev servers - lives
12
+ * on for days holding memory nobody is asking it to hold (issue #138).
13
+ *
14
+ * So instead of waiting to be told, we find out who the client actually is and
15
+ * check whether it is still alive.
16
+ *
17
+ * FINDING THE CLIENT
18
+ * Walk up the process ancestry from this process and take the first ancestor
19
+ * that isn't part of the launch plumbing - Node itself, npm/npx, a shell. For
20
+ * the three shapes seen in the wild that lands on:
21
+ * node <- npm exec <- claude => claude
22
+ * node <- npm exec <- disclaimer <- Claude.app => disclaimer (the
23
+ * Claude app's own helper - it dies with the app, which is what matters)
24
+ * node <- code helper => the IDE helper
25
+ *
26
+ * If the walk finds nothing but plumbing all the way to pid 1, there is no
27
+ * client to watch and reaping is disabled - better to leak than to kill a tree
28
+ * that is still serving someone.
29
+ */
30
+ import { execFileSync } from 'child_process';
31
+ /**
32
+ * Package managers and shells that are only ever launch plumbing, never the
33
+ * client itself. Matched against the command's leading token's basename.
34
+ */
35
+ const PLUMBING = new Set([
36
+ 'npm',
37
+ 'npx',
38
+ 'pnpm',
39
+ 'yarn',
40
+ 'bunx',
41
+ 'sh',
42
+ 'bash',
43
+ 'zsh',
44
+ 'fish',
45
+ 'dash',
46
+ 'ksh',
47
+ 'env',
48
+ 'exec',
49
+ 'login',
50
+ ]);
51
+ /** Runtimes that are plumbing or client depending on what they are running. */
52
+ const RUNTIMES = new Set(['node', 'node.exe', 'nodejs', 'bun', 'deno']);
53
+ /**
54
+ * Scripts that mean a runtime process is npm/npx machinery rather than the
55
+ * client. Anything else a runtime is running is treated as the client itself:
56
+ * an npm-installed MCP host appears in `ps` as `node .../cli.js`, and calling
57
+ * every node process plumbing would walk straight past it to the user's shell
58
+ * and terminal - which live for weeks, so the tree would never be reaped.
59
+ */
60
+ const RUNTIME_PLUMBING_SCRIPTS = [
61
+ 'npm-cli.js',
62
+ 'npx-cli.js',
63
+ 'npm-prefix.js',
64
+ '/node_modules/.bin/',
65
+ '/npm/bin/',
66
+ '/_npx/',
67
+ '/corepack/',
68
+ ];
69
+ function isPlumbing(command) {
70
+ const tokens = command.trim().split(/\s+/);
71
+ const basename = (tokens[0] ?? '').split('/').pop() ?? '';
72
+ if (PLUMBING.has(basename))
73
+ return true;
74
+ if (RUNTIMES.has(basename)) {
75
+ // A bare runtime with no script (a REPL, or an unreadable command) tells
76
+ // us nothing; treat it as plumbing, as before.
77
+ const script = tokens.slice(1).find((token) => !token.startsWith('-'));
78
+ if (!script)
79
+ return true;
80
+ return RUNTIME_PLUMBING_SCRIPTS.some((marker) => script.includes(marker));
81
+ }
82
+ return false;
83
+ }
84
+ /** Reads process info via `ps`. Unavailable on Windows, which has no `ps`. */
85
+ export const systemProcessProbe = {
86
+ info(pid) {
87
+ try {
88
+ const out = execFileSync('ps', ['-o', 'ppid=,command=', '-p', String(pid)], {
89
+ encoding: 'utf-8',
90
+ timeout: 2000,
91
+ }).trim();
92
+ if (!out)
93
+ return null;
94
+ const match = out.match(/^\s*(\d+)\s+(.*)$/);
95
+ if (!match)
96
+ return null;
97
+ return { ppid: Number(match[1]), command: match[2] };
98
+ }
99
+ catch {
100
+ return null;
101
+ }
102
+ },
103
+ isAlive(pid) {
104
+ try {
105
+ process.kill(pid, 0);
106
+ return true;
107
+ }
108
+ catch (err) {
109
+ // EPERM means it exists but belongs to another user - still alive.
110
+ return err?.code === 'EPERM';
111
+ }
112
+ },
113
+ };
114
+ /**
115
+ * Walk up from `startPid`'s parent and return the first ancestor that isn't
116
+ * launch plumbing, or null if there is none.
117
+ */
118
+ export function resolveClientIdentity(startPid, probe, maxDepth = 12) {
119
+ let current = probe.info(startPid);
120
+ for (let depth = 0; depth < maxDepth; depth++) {
121
+ if (!current || current.ppid <= 1)
122
+ return null;
123
+ const parent = probe.info(current.ppid);
124
+ if (!parent)
125
+ return null;
126
+ if (!isPlumbing(parent.command)) {
127
+ return { pid: current.ppid, command: parent.command };
128
+ }
129
+ current = { ppid: parent.ppid, command: parent.command };
130
+ }
131
+ return null;
132
+ }
133
+ /**
134
+ * Polls the resolved client's liveness and calls back once, when it dies.
135
+ * Does nothing at all when no client could be resolved.
136
+ */
137
+ export class ClientWatcher {
138
+ options;
139
+ timer = null;
140
+ probe;
141
+ pollIntervalMs;
142
+ logStderr;
143
+ client = null;
144
+ constructor(options = {}) {
145
+ this.options = options;
146
+ this.probe = options.probe ?? systemProcessProbe;
147
+ this.pollIntervalMs = options.pollIntervalMs ?? 60_000;
148
+ this.logStderr = options.logStderr ?? (() => { });
149
+ }
150
+ getClient() {
151
+ return this.client;
152
+ }
153
+ /**
154
+ * Resolve the client and start watching. Returns the client it will watch,
155
+ * or null if none could be resolved (in which case nothing is watched).
156
+ */
157
+ start(startPid, onClientGone) {
158
+ if (process.platform === 'win32') {
159
+ this.logStderr('Client watcher disabled on Windows (no ps); relying on stdin close');
160
+ return null;
161
+ }
162
+ this.client = resolveClientIdentity(startPid, this.probe);
163
+ if (!this.client) {
164
+ this.logStderr('Could not identify the MCP client process; orphan reaping disabled');
165
+ return null;
166
+ }
167
+ const client = this.client;
168
+ this.logStderr(`Watching client PID ${client.pid} (${client.command.slice(0, 80)})`);
169
+ this.timer = setInterval(() => {
170
+ if (this.probe.isAlive(client.pid))
171
+ return;
172
+ this.stop();
173
+ this.logStderr(`Client PID ${client.pid} is gone; shutting down`);
174
+ onClientGone(client);
175
+ }, this.pollIntervalMs);
176
+ this.timer.unref?.();
177
+ return client;
178
+ }
179
+ stop() {
180
+ if (this.timer) {
181
+ clearInterval(this.timer);
182
+ this.timer = null;
183
+ }
184
+ }
185
+ }
186
+ //# sourceMappingURL=client-watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-watcher.js","sourceRoot":"","sources":["../../src/supervisor/client-watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAmB7C;;;GAGG;AACH,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC;IACvB,KAAK;IACL,KAAK;IACL,MAAM;IACN,MAAM;IACN,MAAM;IACN,IAAI;IACJ,MAAM;IACN,KAAK;IACL,MAAM;IACN,MAAM;IACN,KAAK;IACL,KAAK;IACL,MAAM;IACN,OAAO;CACR,CAAC,CAAC;AAEH,+EAA+E;AAC/E,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;AAExE;;;;;;GAMG;AACH,MAAM,wBAAwB,GAAG;IAC/B,YAAY;IACZ,YAAY;IACZ,eAAe;IACf,qBAAqB;IACrB,WAAW;IACX,QAAQ;IACR,YAAY;CACb,CAAC;AAEF,SAAS,UAAU,CAAC,OAAe;IACjC,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;IAE1D,IAAI,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAExC,IAAI,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3B,yEAAyE;QACzE,+CAA+C;QAC/C,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;QACvE,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACzB,OAAO,wBAAwB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5E,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,8EAA8E;AAC9E,MAAM,CAAC,MAAM,kBAAkB,GAAiB;IAC9C,IAAI,CAAC,GAAW;QACd,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE;gBAC1E,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,IAAI;aACd,CAAC,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,CAAC,GAAG;gBAAE,OAAO,IAAI,CAAC;YACtB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;YAC7C,IAAI,CAAC,KAAK;gBAAE,OAAO,IAAI,CAAC;YACxB,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAW;QACjB,IAAI,CAAC;YACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACrB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,mEAAmE;YACnE,OAAQ,GAA6B,EAAE,IAAI,KAAK,OAAO,CAAC;QAC1D,CAAC;IACH,CAAC;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CACnC,QAAgB,EAChB,KAAmB,EACnB,QAAQ,GAAG,EAAE;IAEb,IAAI,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACnC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,QAAQ,EAAE,KAAK,EAAE,EAAE,CAAC;QAC9C,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QAC/C,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACzB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YAChC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;QACxD,CAAC;QACD,OAAO,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;IAC3D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AASD;;;GAGG;AACH,MAAM,OAAO,aAAa;IAOK;IANrB,KAAK,GAA0C,IAAI,CAAC;IAC3C,KAAK,CAAe;IACpB,cAAc,CAAS;IACvB,SAAS,CAA4B;IAC9C,MAAM,GAA0B,IAAI,CAAC;IAE7C,YAA6B,UAAgC,EAAE;QAAlC,YAAO,GAAP,OAAO,CAA2B;QAC7D,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,kBAAkB,CAAC;QACjD,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,MAAM,CAAC;QACvD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACnD,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,QAAgB,EAAE,YAA8C;QACpE,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YACjC,IAAI,CAAC,SAAS,CAAC,oEAAoE,CAAC,CAAC;YACrF,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,qBAAqB,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1D,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,IAAI,CAAC,SAAS,CAAC,oEAAoE,CAAC,CAAC;YACrF,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,SAAS,CAAC,uBAAuB,MAAM,CAAC,GAAG,KAAK,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;QAErF,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;YAC5B,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC;gBAAE,OAAO;YAC3C,IAAI,CAAC,IAAI,EAAE,CAAC;YACZ,IAAI,CAAC,SAAS,CAAC,cAAc,MAAM,CAAC,GAAG,yBAAyB,CAAC,CAAC;YAClE,YAAY,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QACxB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;QAErB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACpB,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=client-watcher.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-watcher.test.d.ts","sourceRoot":"","sources":["../../src/supervisor/client-watcher.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,121 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import { ClientWatcher, resolveClientIdentity } from './client-watcher.js';
3
+ /** Builds a probe over a fake process table: pid -> { ppid, command }. */
4
+ function makeProbe(table, dead = new Set()) {
5
+ return {
6
+ info: (pid) => (dead.has(pid) ? null : table[pid] ?? null),
7
+ isAlive: (pid) => !dead.has(pid) && pid in table,
8
+ };
9
+ }
10
+ describe('resolveClientIdentity', () => {
11
+ it('finds the client through an npm exec wrapper', () => {
12
+ // node <- npm exec <- claude, the shape a `npx cdp-tools-mcp` launch takes
13
+ const probe = makeProbe({
14
+ 100: { ppid: 200, command: '/opt/node/bin/node /path/.bin/cdp-tools-mcp' },
15
+ 200: { ppid: 300, command: 'npm exec cdp-tools-mcp@latest' },
16
+ 300: { ppid: 400, command: 'claude' },
17
+ 400: { ppid: 1, command: '/bin/zsh' },
18
+ });
19
+ expect(resolveClientIdentity(100, probe)).toEqual({ pid: 300, command: 'claude' });
20
+ });
21
+ it('finds the desktop app helper below the app itself', () => {
22
+ const probe = makeProbe({
23
+ 100: { ppid: 200, command: 'node .bin/cdp-tools-mcp' },
24
+ 200: { ppid: 300, command: 'npm exec cdp-tools-mcp@latest' },
25
+ 300: { ppid: 400, command: '/Applications/Claude.app/Contents/Helpers/disclaimer' },
26
+ 400: { ppid: 1, command: '/Applications/Claude.app/Contents/MacOS/Claude' },
27
+ });
28
+ const client = resolveClientIdentity(100, probe);
29
+ expect(client?.pid).toBe(300);
30
+ });
31
+ it('treats an npm-installed node host as the client, not as plumbing', () => {
32
+ // The host itself is a node script here, so a rule that called every node
33
+ // process plumbing would walk past it to the shell and terminal - which
34
+ // outlive the client, leaving the tree unreaped.
35
+ const probe = makeProbe({
36
+ 100: { ppid: 200, command: 'node /Users/x/.npm/_npx/abc/node_modules/.bin/cdp-tools-mcp' },
37
+ 200: { ppid: 300, command: 'npm exec cdp-tools-mcp@latest' },
38
+ 300: { ppid: 400, command: 'node /usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js' },
39
+ 400: { ppid: 500, command: '/bin/zsh -l' },
40
+ 500: { ppid: 1, command: '/Applications/iTerm.app/Contents/MacOS/iTerm2' },
41
+ });
42
+ expect(resolveClientIdentity(100, probe)?.pid).toBe(300);
43
+ });
44
+ it('still walks past npm and npx machinery running under node', () => {
45
+ const probe = makeProbe({
46
+ 100: { ppid: 200, command: 'node /path/node_modules/.bin/cdp-tools-mcp' },
47
+ 200: { ppid: 300, command: 'node /opt/homebrew/lib/node_modules/npm/bin/npm-cli.js exec' },
48
+ 300: { ppid: 1, command: 'claude' },
49
+ });
50
+ expect(resolveClientIdentity(100, probe)?.pid).toBe(300);
51
+ });
52
+ it('treats a bare runtime with no script as plumbing', () => {
53
+ const probe = makeProbe({
54
+ 100: { ppid: 200, command: 'node /path/.bin/cdp-tools-mcp' },
55
+ 200: { ppid: 300, command: 'node' },
56
+ 300: { ppid: 1, command: 'code-helper' },
57
+ });
58
+ expect(resolveClientIdentity(100, probe)?.pid).toBe(300);
59
+ });
60
+ it('returns null when every ancestor is launch plumbing', () => {
61
+ const probe = makeProbe({
62
+ 100: { ppid: 200, command: 'node /path/node_modules/.bin/cdp-tools-mcp' },
63
+ 200: { ppid: 300, command: 'npm exec cdp-tools-mcp@latest' },
64
+ 300: { ppid: 1, command: '/bin/bash' },
65
+ });
66
+ expect(resolveClientIdentity(100, probe)).toBeNull();
67
+ });
68
+ it('returns null when the ancestry is unreadable', () => {
69
+ expect(resolveClientIdentity(100, makeProbe({}))).toBeNull();
70
+ });
71
+ it('gives up rather than looping on a deep ancestry', () => {
72
+ // Every entry is plumbing, so a bounded walk is the only thing that stops it.
73
+ const table = {};
74
+ for (let pid = 100; pid < 200; pid++) {
75
+ table[pid] = { ppid: pid + 1, command: 'npm exec something' };
76
+ }
77
+ expect(resolveClientIdentity(100, makeProbe(table), 5)).toBeNull();
78
+ });
79
+ });
80
+ describe('ClientWatcher', () => {
81
+ beforeEach(() => vi.useFakeTimers());
82
+ afterEach(() => vi.useRealTimers());
83
+ const TABLE = {
84
+ 100: { ppid: 200, command: 'node .bin/cdp-tools-mcp' },
85
+ 200: { ppid: 300, command: 'npm exec cdp-tools-mcp@latest' },
86
+ 300: { ppid: 1, command: 'claude' },
87
+ };
88
+ it('calls back once the client is gone', () => {
89
+ const dead = new Set();
90
+ const watcher = new ClientWatcher({ pollIntervalMs: 1000, probe: makeProbe(TABLE, dead) });
91
+ const onGone = vi.fn();
92
+ expect(watcher.start(100, onGone)).toEqual({ pid: 300, command: 'claude' });
93
+ vi.advanceTimersByTime(3000);
94
+ expect(onGone).not.toHaveBeenCalled();
95
+ dead.add(300);
96
+ vi.advanceTimersByTime(1000);
97
+ expect(onGone).toHaveBeenCalledTimes(1);
98
+ expect(onGone).toHaveBeenCalledWith({ pid: 300, command: 'claude' });
99
+ // Stops polling after firing - one shutdown is enough.
100
+ vi.advanceTimersByTime(10_000);
101
+ expect(onGone).toHaveBeenCalledTimes(1);
102
+ });
103
+ it('watches nothing when no client can be identified', () => {
104
+ const watcher = new ClientWatcher({ pollIntervalMs: 1000, probe: makeProbe({}) });
105
+ const onGone = vi.fn();
106
+ expect(watcher.start(100, onGone)).toBeNull();
107
+ vi.advanceTimersByTime(60_000);
108
+ expect(onGone).not.toHaveBeenCalled();
109
+ });
110
+ it('stops polling when stopped', () => {
111
+ const dead = new Set();
112
+ const watcher = new ClientWatcher({ pollIntervalMs: 1000, probe: makeProbe(TABLE, dead) });
113
+ const onGone = vi.fn();
114
+ watcher.start(100, onGone);
115
+ watcher.stop();
116
+ dead.add(300);
117
+ vi.advanceTimersByTime(10_000);
118
+ expect(onGone).not.toHaveBeenCalled();
119
+ });
120
+ });
121
+ //# sourceMappingURL=client-watcher.test.js.map