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,826 @@
1
+ /**
2
+ * Configuration Manager
3
+ * Manages user-editable configuration in .cdp-tools/config.json
4
+ */
5
+ import * as fs from 'fs';
6
+ import { dirname, isAbsolute, join, resolve } from 'path';
7
+ import { homedir } from 'os';
8
+ import { getConfigSavePath, getOutputPath, setWorkingDirOverride } from './helpers/paths.js';
9
+ import { debugLog, enableDebugLogging, disableDebugLogging, isDebugEnabled, enableHistoryLogging, disableHistoryLogging, isHistoryLogEnabled, } from './debug-logger.js';
10
+ import { atomicWriteFile } from './atomic-write.js';
11
+ /**
12
+ * List of tools that can be toggled via config
13
+ * New tools added here will be auto-discovered and added to enabled list on startup
14
+ */
15
+ export const TOGGLEABLE_TOOLS = [
16
+ 'connection', // Core Chrome/debugger connection
17
+ 'tab', // Tab management
18
+ 'breakpoint', // Breakpoints, logpoints
19
+ 'execution', // Pause, resume, step
20
+ 'inspection', // Call stack, variables, evaluate
21
+ 'source', // Source maps, code search
22
+ 'console', // Console monitoring
23
+ 'network', // Network monitoring
24
+ 'page', // Navigation
25
+ 'dom', // DOM queries
26
+ 'screenshot', // Screenshots, PDF
27
+ 'input', // Click, type, hover
28
+ 'content', // Text extraction, interactive elements
29
+ 'modal', // Modal detection/dismissal
30
+ 'storage', // Cookies, localStorage
31
+ 'download', // File downloads
32
+ 'request', // HTTP requests (node or browser) as sequence steps
33
+ 'assert', // Inline assertions as sequence steps (compare {{var:...}} values)
34
+ 'wait', // Wait primitive for sequences (selector/expression polling, sleep)
35
+ 'replay', // Sequence recording/playback
36
+ 'server', // Dev server management
37
+ 'issues', // Issue tracking
38
+ 'dashboard', // Web dashboard for monitoring sessions
39
+ // Note: 'config' is NOT toggleable - always enabled
40
+ ];
41
+ /**
42
+ * Tool dependencies - key depends on values
43
+ * If a dependency is disabled, the dependent tool cannot function
44
+ */
45
+ export const TOOL_DEPENDENCIES = {
46
+ tab: ['connection'],
47
+ breakpoint: ['connection'],
48
+ execution: ['connection'],
49
+ inspection: ['connection'],
50
+ source: ['connection'],
51
+ console: ['connection'],
52
+ network: ['connection'],
53
+ page: ['connection'],
54
+ dom: ['connection'],
55
+ screenshot: ['connection'],
56
+ input: ['connection'],
57
+ content: ['connection'],
58
+ modal: ['connection'],
59
+ storage: ['connection'],
60
+ replay: ['connection', 'input', 'page'],
61
+ issues: ['replay'],
62
+ };
63
+ /**
64
+ * Check for dependency conflicts in tools config
65
+ * Returns array of conflict descriptions (grouped by disabled dependency), empty if no conflicts
66
+ */
67
+ export function checkToolDependencyConflicts(enabled, disabled) {
68
+ // Group dependents by their disabled dependency
69
+ const dependentsByDisabled = {};
70
+ for (const toolName of enabled) {
71
+ const deps = TOOL_DEPENDENCIES[toolName];
72
+ if (!deps)
73
+ continue;
74
+ for (const dep of deps) {
75
+ if (disabled.includes(dep)) {
76
+ if (!dependentsByDisabled[dep]) {
77
+ dependentsByDisabled[dep] = [];
78
+ }
79
+ dependentsByDisabled[dep].push(toolName);
80
+ }
81
+ }
82
+ }
83
+ // Build conflict messages grouped by disabled dependency
84
+ const conflicts = [];
85
+ for (const [disabledTool, dependents] of Object.entries(dependentsByDisabled)) {
86
+ const count = dependents.length;
87
+ const toolWord = count === 1 ? 'tool' : 'tools';
88
+ const dependentsList = dependents.join(', ');
89
+ conflicts.push(`'${disabledTool}' is disabled but required for (${count}) ${toolWord}:\n\t${dependentsList}`);
90
+ }
91
+ return conflicts;
92
+ }
93
+ /**
94
+ * Default configuration values
95
+ */
96
+ const DEFAULT_CONFIG = {
97
+ version: 1,
98
+ configLocation: 'local',
99
+ session: {
100
+ idleSuspendMinutes: 120, // Suspend after 2h of client silence (0 = never)
101
+ clientPollSeconds: 60, // Check the client is still alive every minute
102
+ },
103
+ chrome: {
104
+ startingDebugPort: 9222,
105
+ inactivityTimeoutMinutes: 5,
106
+ inactivityPollingMinutes: 2,
107
+ persistentProfileRoot: '', // '' = global ~/.cdp-tools/profiles
108
+ },
109
+ portMonitoring: {
110
+ portMonitoringFreqMs: {
111
+ block: 1000, // Fast detection for blocking
112
+ error: 2000, // Standard
113
+ inform: 5000, // Lower overhead for informational
114
+ },
115
+ },
116
+ replay: {
117
+ maxConditionalDepth: 10, // Maximum nesting depth for conditional commands
118
+ maxRegexLength: 500, // Maximum regex pattern length for url:matches
119
+ showCursor: true, // Show visual cursor during replay
120
+ playwrightExportPath: './tests/e2e', // Export path for Playwright tests
121
+ puppeteerExportPath: './tests/puppeteer', // Export path for Puppeteer tests
122
+ maxDelayMs: 1000, // Cap recorded delays at 1 second (0 = no limit)
123
+ },
124
+ changeDetection: {
125
+ enabled: true, // Detect DOM changes by default
126
+ settleTimeout: 2000, // Max wait for mutations to settle
127
+ quietPeriod: 300, // No mutations for 300ms = settled
128
+ navigationTimeout: 3000, // Longer timeout for page loads
129
+ },
130
+ clickValidation: {
131
+ enabled: true, // Enable click validation in replay
132
+ validateNavigation: true, // Check navigation success
133
+ requireDomChanges: false, // Don't require DOM mutations by default
134
+ domChangesFailMode: 'warn', // Just warn if no DOM changes
135
+ failOnConsoleErrors: true, // Check for console errors
136
+ consoleErrorsFailMode: 'error', // Fail on console errors
137
+ validateNetworkPayload: false, // Don't validate network by default
138
+ networkFailMode: 'warn', // Just warn on network failures
139
+ postClickDelayMs: 100, // Small delay before validation
140
+ },
141
+ debug: {
142
+ enabled: false, // Debug logging disabled by default
143
+ historyLogEnabled: false, // History log disabled by default
144
+ },
145
+ tools: {
146
+ enabled: ['issues'], // All tools enabled by default
147
+ disabled: [],
148
+ },
149
+ };
150
+ /**
151
+ * Configuration Manager
152
+ * Loads and saves configuration from .cdp-tools/config.json
153
+ * Also tracks runtime port state
154
+ */
155
+ export class ConfigManager {
156
+ config = { ...DEFAULT_CONFIG };
157
+ loaded = false;
158
+ loadedFromPath = null;
159
+ // Runtime port state (not persisted to config file)
160
+ currentPort = DEFAULT_CONFIG.chrome.startingDebugPort;
161
+ // Dependency conflict state - blocks all tool access if set
162
+ dependencyConflicts = [];
163
+ // Live-reload watcher state
164
+ configWatchers = [];
165
+ reloadTimer = null;
166
+ static RELOAD_DEBOUNCE_MS = 250;
167
+ constructor() {
168
+ // Sync load config at construction for early access (e.g., tool registration)
169
+ this.loadSync();
170
+ }
171
+ /**
172
+ * Synchronously load config for early access during module initialization
173
+ */
174
+ loadSync() {
175
+ const localConfigPath = getOutputPath('config.json');
176
+ const globalConfigPath = join(homedir(), '.cdp-tools', 'config.json');
177
+ try {
178
+ if (fs.existsSync(localConfigPath)) {
179
+ const content = fs.readFileSync(localConfigPath, 'utf-8');
180
+ const loaded = JSON.parse(content);
181
+ this.config = this.mergeConfig(DEFAULT_CONFIG, loaded);
182
+ this.loadedFromPath = localConfigPath;
183
+ this.loaded = true;
184
+ // Auto-discover new tools
185
+ if (this.discoverTools()) {
186
+ this.saveSync();
187
+ }
188
+ }
189
+ else {
190
+ // No local config - create one
191
+ // Seed from global if it exists, otherwise use defaults
192
+ if (fs.existsSync(globalConfigPath)) {
193
+ const content = fs.readFileSync(globalConfigPath, 'utf-8');
194
+ const loaded = JSON.parse(content);
195
+ this.config = this.mergeConfig(DEFAULT_CONFIG, loaded);
196
+ }
197
+ else {
198
+ this.config = { ...DEFAULT_CONFIG };
199
+ }
200
+ // Auto-discover new tools (populates enabled list)
201
+ this.discoverTools();
202
+ // Save to local
203
+ this.loadedFromPath = this.getPreferredConfigPath();
204
+ this.saveSync();
205
+ this.loaded = true;
206
+ }
207
+ }
208
+ catch {
209
+ // Ignore errors, use defaults
210
+ this.config = { ...DEFAULT_CONFIG };
211
+ this.discoverTools();
212
+ }
213
+ // Validate dependencies after config is fully loaded
214
+ this.validateDependencies();
215
+ }
216
+ /**
217
+ * Validate tool dependencies and store any conflicts
218
+ */
219
+ validateDependencies() {
220
+ this.dependencyConflicts = checkToolDependencyConflicts(this.config.tools.enabled, this.config.tools.disabled);
221
+ }
222
+ /**
223
+ * Check if there are dependency conflicts blocking tool access
224
+ */
225
+ hasDependencyConflicts() {
226
+ return this.dependencyConflicts.length > 0;
227
+ }
228
+ /**
229
+ * Get the list of dependency conflicts
230
+ */
231
+ getDependencyConflicts() {
232
+ return [...this.dependencyConflicts];
233
+ }
234
+ /**
235
+ * Get preferred path for creating new config
236
+ * Prefers working directory if .cdp-tools folder exists or can be created
237
+ */
238
+ getPreferredConfigPath() {
239
+ try {
240
+ const wdConfigPath = getOutputPath('config.json');
241
+ const wdBase = dirname(wdConfigPath);
242
+ // If .cdp-tools dir exists in working directory, use it
243
+ if (fs.existsSync(wdBase)) {
244
+ return wdConfigPath;
245
+ }
246
+ // Try to create .cdp-tools dir in working directory
247
+ fs.mkdirSync(wdBase, { recursive: true });
248
+ return wdConfigPath;
249
+ }
250
+ catch {
251
+ // Fall back to global if working directory is not writable
252
+ return getConfigSavePath();
253
+ }
254
+ }
255
+ /**
256
+ * Load configuration from disk
257
+ * Checks local config first for configLocation preference.
258
+ * If configLocation is 'global', uses global config.
259
+ * Otherwise creates/uses local config (seeding from global if available).
260
+ */
261
+ async load() {
262
+ const localConfigPath = getOutputPath('config.json');
263
+ const globalConfigPath = join(homedir(), '.cdp-tools', 'config.json');
264
+ try {
265
+ // Check if local config exists and has configLocation preference
266
+ if (fs.existsSync(localConfigPath)) {
267
+ const content = await fs.promises.readFile(localConfigPath, 'utf-8');
268
+ const loaded = JSON.parse(content);
269
+ // If local config says to use global, switch to global
270
+ if (loaded.configLocation === 'global' && fs.existsSync(globalConfigPath)) {
271
+ const globalContent = await fs.promises.readFile(globalConfigPath, 'utf-8');
272
+ const globalLoaded = JSON.parse(globalContent);
273
+ this.config = this.mergeConfig(DEFAULT_CONFIG, globalLoaded);
274
+ this.loadedFromPath = globalConfigPath;
275
+ // Auto-discover new tools
276
+ if (this.discoverTools()) {
277
+ await debugLog('ConfigManager', `Discovered new tools, updating config`);
278
+ }
279
+ await debugLog('ConfigManager', `Using global config (per local configLocation setting)`);
280
+ // Clean up local config to just have the pointer (atomic write)
281
+ await atomicWriteFile(localConfigPath, JSON.stringify({ configLocation: 'global' }, null, 2));
282
+ await this.save();
283
+ return;
284
+ }
285
+ // Use local config
286
+ this.config = this.mergeConfig(DEFAULT_CONFIG, loaded);
287
+ this.loadedFromPath = localConfigPath;
288
+ // Auto-discover new tools
289
+ if (this.discoverTools()) {
290
+ await debugLog('ConfigManager', `Discovered new tools, updating config`);
291
+ }
292
+ await debugLog('ConfigManager', `Loaded config from ${localConfigPath}`);
293
+ await this.save();
294
+ }
295
+ else {
296
+ // No local config - create one
297
+ // Seed from global if it exists, otherwise use defaults
298
+ if (fs.existsSync(globalConfigPath)) {
299
+ const content = await fs.promises.readFile(globalConfigPath, 'utf-8');
300
+ const loaded = JSON.parse(content);
301
+ this.config = this.mergeConfig(DEFAULT_CONFIG, loaded);
302
+ await debugLog('ConfigManager', `Seeding local config from global ${globalConfigPath}`);
303
+ }
304
+ else {
305
+ this.config = { ...DEFAULT_CONFIG };
306
+ }
307
+ // Auto-discover new tools
308
+ if (this.discoverTools()) {
309
+ await debugLog('ConfigManager', `Discovered new tools, updating config`);
310
+ }
311
+ // Save to local (getPreferredConfigPath will fall back to global if local not writable)
312
+ this.loadedFromPath = this.getPreferredConfigPath();
313
+ await this.save();
314
+ await debugLog('ConfigManager', `Created config at ${this.loadedFromPath}`);
315
+ }
316
+ }
317
+ catch (err) {
318
+ await debugLog('ConfigManager', `Failed to load config: ${err}, using defaults`);
319
+ this.config = { ...DEFAULT_CONFIG };
320
+ this.loadedFromPath = null;
321
+ }
322
+ // Validate dependencies after config is fully loaded
323
+ this.validateDependencies();
324
+ if (this.hasDependencyConflicts()) {
325
+ await debugLog('ConfigManager', `Tool dependency conflicts detected: ${this.dependencyConflicts.join(', ')}`);
326
+ }
327
+ this.loaded = true;
328
+ }
329
+ /**
330
+ * Re-read config.json from disk and apply it to the running process.
331
+ * Unlike load(), this never writes back (no discover-and-persist) - it's
332
+ * meant to pick up a manual edit live, not to run the one-time bootstrap,
333
+ * and writing back here would re-trigger the file watcher that calls it.
334
+ */
335
+ async reload() {
336
+ const localConfigPath = getOutputPath('config.json');
337
+ const globalConfigPath = join(homedir(), '.cdp-tools', 'config.json');
338
+ const previousSnapshot = JSON.stringify(this.config);
339
+ const previousDebug = { ...this.config.debug };
340
+ let nextConfig;
341
+ let nextPath;
342
+ try {
343
+ if (fs.existsSync(localConfigPath)) {
344
+ const content = await fs.promises.readFile(localConfigPath, 'utf-8');
345
+ const loaded = JSON.parse(content);
346
+ if (loaded.configLocation === 'global' && fs.existsSync(globalConfigPath)) {
347
+ const globalContent = await fs.promises.readFile(globalConfigPath, 'utf-8');
348
+ nextConfig = this.mergeConfig(DEFAULT_CONFIG, JSON.parse(globalContent));
349
+ nextPath = globalConfigPath;
350
+ }
351
+ else {
352
+ nextConfig = this.mergeConfig(DEFAULT_CONFIG, loaded);
353
+ nextPath = localConfigPath;
354
+ }
355
+ }
356
+ else if (fs.existsSync(globalConfigPath)) {
357
+ const content = await fs.promises.readFile(globalConfigPath, 'utf-8');
358
+ nextConfig = this.mergeConfig(DEFAULT_CONFIG, JSON.parse(content));
359
+ nextPath = globalConfigPath;
360
+ }
361
+ else {
362
+ // Nothing on disk (e.g. deleted) - keep the current in-memory config.
363
+ return { changed: false, path: this.loadedFromPath };
364
+ }
365
+ }
366
+ catch (err) {
367
+ await debugLog('ConfigManager', `Config reload failed: ${err}`);
368
+ return { changed: false, path: this.loadedFromPath };
369
+ }
370
+ this.config = nextConfig;
371
+ this.loadedFromPath = nextPath;
372
+ this.validateDependencies();
373
+ const changed = JSON.stringify(this.config) !== previousSnapshot;
374
+ if (changed) {
375
+ await debugLog('ConfigManager', `Config reloaded from ${nextPath}`);
376
+ this.applyLiveDebugSettings(previousDebug);
377
+ }
378
+ return { changed, path: nextPath };
379
+ }
380
+ /**
381
+ * debug.enabled/historyLogEnabled are otherwise only applied once at
382
+ * server startup (see main() in index.ts) - mirror that here so a live
383
+ * edit actually flips debug-logger.ts's module state.
384
+ */
385
+ applyLiveDebugSettings(previousDebug) {
386
+ const next = this.config.debug;
387
+ if (next.enabled !== previousDebug.enabled) {
388
+ if (next.enabled && !isDebugEnabled()) {
389
+ void enableDebugLogging();
390
+ }
391
+ else if (!next.enabled && isDebugEnabled()) {
392
+ disableDebugLogging();
393
+ }
394
+ }
395
+ if (next.historyLogEnabled !== previousDebug.historyLogEnabled) {
396
+ if (next.historyLogEnabled && !isHistoryLogEnabled()) {
397
+ enableHistoryLogging();
398
+ }
399
+ else if (!next.historyLogEnabled && isHistoryLogEnabled()) {
400
+ disableHistoryLogging();
401
+ }
402
+ }
403
+ }
404
+ /**
405
+ * Watch the local and global config directories for edits and hot-reload
406
+ * config.json into the running process. Watches the parent directory
407
+ * (not the file itself) and ignores the event payload, debouncing to a
408
+ * full reload() - same pattern as issue-tracker.ts's watcher, and safe
409
+ * against atomic saves (write-temp + rename) losing the watch descriptor.
410
+ *
411
+ * Note: tools.enabled/tools.disabled cannot be hot-applied this way - the
412
+ * MCP tool list is built once at server startup. Everything else this
413
+ * class exposes (portMonitoring, replay, changeDetection, clickValidation,
414
+ * debug) is read live from getConfig() and picks up a reload immediately.
415
+ */
416
+ startWatching() {
417
+ if (this.configWatchers.length > 0)
418
+ return;
419
+ const dirsToWatch = new Set([
420
+ dirname(getOutputPath('config.json')),
421
+ dirname(join(homedir(), '.cdp-tools', 'config.json')),
422
+ ]);
423
+ for (const dir of dirsToWatch) {
424
+ try {
425
+ const watcher = fs.watch(dir, () => this.scheduleReload());
426
+ this.configWatchers.push(watcher);
427
+ }
428
+ catch {
429
+ // Directory doesn't exist yet / not watchable on this platform - skip silently.
430
+ }
431
+ }
432
+ }
433
+ stopWatching() {
434
+ for (const watcher of this.configWatchers) {
435
+ watcher.close();
436
+ }
437
+ this.configWatchers = [];
438
+ if (this.reloadTimer) {
439
+ clearTimeout(this.reloadTimer);
440
+ this.reloadTimer = null;
441
+ }
442
+ }
443
+ /**
444
+ * Coalesce a burst of filesystem events into one reload, firing a fixed
445
+ * window after the FIRST event rather than the last.
446
+ *
447
+ * This used to clear and re-arm the timer on every event, which is the
448
+ * textbook debounce - and the wrong shape here. startWatching() watches the
449
+ * global ~/.cdp-tools directory as well as the project one, and that
450
+ * directory is shared by every cdp-tools process on the machine (dashboard
451
+ * locks, downloads, sequences). Under sustained unrelated writes there, the
452
+ * timer was reset before it could ever fire, so an edit to config.json was
453
+ * postponed indefinitely - live reload silently stopped working, and stayed
454
+ * broken for as long as the other process kept writing.
455
+ *
456
+ * Firing from the first event bounds the latency at RELOAD_DEBOUNCE_MS no
457
+ * matter how busy the directory is, while still collapsing the rapid
458
+ * write/rename pairs an atomic save produces.
459
+ */
460
+ scheduleReload() {
461
+ if (this.reloadTimer)
462
+ return;
463
+ this.reloadTimer = setTimeout(() => {
464
+ this.reloadTimer = null;
465
+ void this.reload();
466
+ }, ConfigManager.RELOAD_DEBOUNCE_MS);
467
+ }
468
+ /**
469
+ * Deep merge two config objects
470
+ */
471
+ mergeConfig(defaults, loaded) {
472
+ return {
473
+ version: loaded.version ?? defaults.version,
474
+ configLocation: loaded.configLocation ?? defaults.configLocation,
475
+ session: {
476
+ idleSuspendMinutes: loaded.session?.idleSuspendMinutes ?? defaults.session.idleSuspendMinutes,
477
+ clientPollSeconds: loaded.session?.clientPollSeconds ?? defaults.session.clientPollSeconds,
478
+ },
479
+ chrome: {
480
+ startingDebugPort: loaded.chrome?.startingDebugPort ?? defaults.chrome.startingDebugPort,
481
+ inactivityTimeoutMinutes: loaded.chrome?.inactivityTimeoutMinutes ?? defaults.chrome.inactivityTimeoutMinutes,
482
+ inactivityPollingMinutes: loaded.chrome?.inactivityPollingMinutes ?? defaults.chrome.inactivityPollingMinutes,
483
+ persistentProfileRoot: loaded.chrome?.persistentProfileRoot ?? defaults.chrome.persistentProfileRoot,
484
+ },
485
+ portMonitoring: {
486
+ portMonitoringFreqMs: {
487
+ block: loaded.portMonitoring?.portMonitoringFreqMs?.block ?? defaults.portMonitoring.portMonitoringFreqMs.block,
488
+ error: loaded.portMonitoring?.portMonitoringFreqMs?.error ?? defaults.portMonitoring.portMonitoringFreqMs.error,
489
+ inform: loaded.portMonitoring?.portMonitoringFreqMs?.inform ?? defaults.portMonitoring.portMonitoringFreqMs.inform,
490
+ },
491
+ },
492
+ replay: {
493
+ maxConditionalDepth: loaded.replay?.maxConditionalDepth ?? defaults.replay.maxConditionalDepth,
494
+ maxRegexLength: loaded.replay?.maxRegexLength ?? defaults.replay.maxRegexLength,
495
+ showCursor: loaded.replay?.showCursor ?? defaults.replay.showCursor,
496
+ playwrightExportPath: loaded.replay?.playwrightExportPath ?? defaults.replay.playwrightExportPath,
497
+ puppeteerExportPath: loaded.replay?.puppeteerExportPath ?? defaults.replay.puppeteerExportPath,
498
+ maxDelayMs: loaded.replay?.maxDelayMs ?? defaults.replay.maxDelayMs,
499
+ },
500
+ changeDetection: {
501
+ enabled: loaded.changeDetection?.enabled ?? defaults.changeDetection.enabled,
502
+ settleTimeout: loaded.changeDetection?.settleTimeout ?? defaults.changeDetection.settleTimeout,
503
+ quietPeriod: loaded.changeDetection?.quietPeriod ?? defaults.changeDetection.quietPeriod,
504
+ navigationTimeout: loaded.changeDetection?.navigationTimeout ?? defaults.changeDetection.navigationTimeout,
505
+ },
506
+ clickValidation: {
507
+ enabled: loaded.clickValidation?.enabled ?? defaults.clickValidation.enabled,
508
+ validateNavigation: loaded.clickValidation?.validateNavigation ?? defaults.clickValidation.validateNavigation,
509
+ requireDomChanges: loaded.clickValidation?.requireDomChanges ?? defaults.clickValidation.requireDomChanges,
510
+ domChangesFailMode: loaded.clickValidation?.domChangesFailMode ?? defaults.clickValidation.domChangesFailMode,
511
+ failOnConsoleErrors: loaded.clickValidation?.failOnConsoleErrors ?? defaults.clickValidation.failOnConsoleErrors,
512
+ consoleErrorsFailMode: loaded.clickValidation?.consoleErrorsFailMode ?? defaults.clickValidation.consoleErrorsFailMode,
513
+ validateNetworkPayload: loaded.clickValidation?.validateNetworkPayload ?? defaults.clickValidation.validateNetworkPayload,
514
+ networkFailMode: loaded.clickValidation?.networkFailMode ?? defaults.clickValidation.networkFailMode,
515
+ postClickDelayMs: loaded.clickValidation?.postClickDelayMs ?? defaults.clickValidation.postClickDelayMs,
516
+ },
517
+ debug: {
518
+ enabled: loaded.debug?.enabled ?? defaults.debug.enabled,
519
+ historyLogEnabled: loaded.debug?.historyLogEnabled ?? defaults.debug.historyLogEnabled,
520
+ },
521
+ tools: {
522
+ enabled: loaded.tools?.enabled ?? defaults.tools.enabled,
523
+ disabled: loaded.tools?.disabled ?? defaults.tools.disabled,
524
+ },
525
+ };
526
+ }
527
+ /**
528
+ * Save configuration to disk
529
+ * Saves to the same location it was loaded from, or global if new
530
+ */
531
+ async save() {
532
+ const configPath = this.loadedFromPath || getConfigSavePath();
533
+ // Atomic write handles directory creation and prevents corruption
534
+ await atomicWriteFile(configPath, JSON.stringify(this.config, null, 2));
535
+ }
536
+ /**
537
+ * Synchronously save configuration to disk
538
+ * Used during loadSync() to persist config before async code runs
539
+ */
540
+ saveSync() {
541
+ const configPath = this.loadedFromPath || getConfigSavePath();
542
+ const dir = dirname(configPath);
543
+ if (!fs.existsSync(dir)) {
544
+ fs.mkdirSync(dir, { recursive: true });
545
+ }
546
+ fs.writeFileSync(configPath, JSON.stringify(this.config, null, 2), 'utf-8');
547
+ }
548
+ /**
549
+ * Get the full configuration
550
+ */
551
+ getConfig() {
552
+ if (!this.loaded) {
553
+ // Synchronous fallback - return defaults
554
+ return { ...DEFAULT_CONFIG };
555
+ }
556
+ return this.config;
557
+ }
558
+ /**
559
+ * Get port monitoring configuration
560
+ */
561
+ getPortMonitoringConfig() {
562
+ return this.getConfig().portMonitoring;
563
+ }
564
+ /**
565
+ * Get the interval for a specific monitoring level
566
+ */
567
+ getIntervalForLevel(level) {
568
+ return this.getPortMonitoringConfig().portMonitoringFreqMs[level];
569
+ }
570
+ /**
571
+ * Get Chrome configuration
572
+ */
573
+ getChromeConfig() {
574
+ return this.getConfig().chrome;
575
+ }
576
+ /**
577
+ * Absolute directory that named persistent Chrome profiles live in
578
+ * (`launchChrome({ profile })`, `config({action:'resetProfile'})`).
579
+ *
580
+ * Defaults to the global `~/.cdp-tools/profiles` so a named profile is shared
581
+ * across projects. `chrome.persistentProfileRoot` in a project-local config
582
+ * overrides it; relative values resolve against the working directory and a
583
+ * leading `~/` is expanded.
584
+ */
585
+ getPersistentProfileRoot() {
586
+ const configured = (this.getChromeConfig().persistentProfileRoot || '').trim();
587
+ if (!configured) {
588
+ return join(homedir(), '.cdp-tools', 'profiles');
589
+ }
590
+ if (configured === '~') {
591
+ return homedir();
592
+ }
593
+ if (configured.startsWith('~/')) {
594
+ return join(homedir(), configured.slice(2));
595
+ }
596
+ return isAbsolute(configured) ? configured : resolve(process.cwd(), configured);
597
+ }
598
+ /**
599
+ * Get replay system configuration
600
+ */
601
+ getReplayConfig() {
602
+ return this.getConfig().replay;
603
+ }
604
+ /**
605
+ * Get change detection configuration
606
+ */
607
+ getChangeDetectionConfig() {
608
+ return this.getConfig().changeDetection;
609
+ }
610
+ /**
611
+ * Get click validation configuration for replay sequences
612
+ */
613
+ getClickValidationConfig() {
614
+ return this.getConfig().clickValidation;
615
+ }
616
+ /**
617
+ * Get debug configuration
618
+ */
619
+ getDebugConfig() {
620
+ return this.getConfig().debug;
621
+ }
622
+ /**
623
+ * Get tools configuration
624
+ */
625
+ getToolsConfig() {
626
+ return this.getConfig().tools;
627
+ }
628
+ /**
629
+ * Check if a tool is enabled
630
+ * A tool is enabled if it's in the enabled list and not in the disabled list
631
+ */
632
+ isToolEnabled(toolName) {
633
+ const tools = this.getToolsConfig();
634
+ // If in disabled list, it's disabled
635
+ if (tools.disabled.includes(toolName))
636
+ return false;
637
+ // If in enabled list, it's enabled
638
+ if (tools.enabled.includes(toolName))
639
+ return true;
640
+ // Default to disabled if not in either list
641
+ return false;
642
+ }
643
+ /**
644
+ * Auto-discover and enable new tools
645
+ * Any tool in TOGGLEABLE_TOOLS that isn't in disabled will be added to enabled
646
+ * Also removes tools from enabled if they are in disabled
647
+ * Returns true if config was modified
648
+ */
649
+ discoverTools() {
650
+ let modified = false;
651
+ // Remove any tools from enabled that are in disabled
652
+ for (const toolName of this.config.tools.disabled) {
653
+ const enabledIndex = this.config.tools.enabled.indexOf(toolName);
654
+ if (enabledIndex !== -1) {
655
+ this.config.tools.enabled.splice(enabledIndex, 1);
656
+ modified = true;
657
+ }
658
+ }
659
+ // Auto-add new tools to enabled (if not in disabled)
660
+ for (const toolName of TOGGLEABLE_TOOLS) {
661
+ // Skip if already in enabled or disabled
662
+ if (this.config.tools.enabled.includes(toolName))
663
+ continue;
664
+ if (this.config.tools.disabled.includes(toolName))
665
+ continue;
666
+ // Auto-add to enabled
667
+ this.config.tools.enabled.push(toolName);
668
+ modified = true;
669
+ }
670
+ return modified;
671
+ }
672
+ /**
673
+ * Get list of available toggleable tools with their current state
674
+ */
675
+ getToggleableTools() {
676
+ return TOGGLEABLE_TOOLS.map(name => ({
677
+ name,
678
+ enabled: this.isToolEnabled(name),
679
+ dependencies: TOOL_DEPENDENCIES[name] || [],
680
+ }));
681
+ }
682
+ /**
683
+ * Update port monitoring frequency configuration
684
+ */
685
+ async updatePortMonitoringFreqMs(updates) {
686
+ this.config.portMonitoring.portMonitoringFreqMs = {
687
+ ...this.config.portMonitoring.portMonitoringFreqMs,
688
+ ...updates,
689
+ };
690
+ await this.save();
691
+ }
692
+ // Runtime port state methods (not persisted)
693
+ /**
694
+ * Get the current port in use (runtime state)
695
+ */
696
+ getCurrentPort() {
697
+ return this.currentPort;
698
+ }
699
+ /**
700
+ * Set the current port (runtime state)
701
+ */
702
+ setCurrentPort(port) {
703
+ this.currentPort = port;
704
+ }
705
+ // Config management methods
706
+ /**
707
+ * Get info about current config location and status
708
+ */
709
+ getStatus() {
710
+ const localPath = getOutputPath('config.json');
711
+ const globalPath = join(homedir(), '.cdp-tools', 'config.json');
712
+ return {
713
+ loadedFrom: this.loadedFromPath,
714
+ isLocal: this.loadedFromPath === localPath,
715
+ localPath,
716
+ globalPath,
717
+ localExists: fs.existsSync(localPath),
718
+ globalExists: fs.existsSync(globalPath),
719
+ };
720
+ }
721
+ /**
722
+ * Switch to using local config (creates if needed, optionally seeds from global)
723
+ *
724
+ * @param seedFromGlobal - seed new local config from global if it exists
725
+ * @param projectPath - explicit project directory to treat as "local".
726
+ * Needed when the MCP server's process.cwd() doesn't reflect the
727
+ * project the user is currently working in (e.g. a shared long-lived
728
+ * server process spawned from the home directory).
729
+ */
730
+ async useLocal(seedFromGlobal = true, projectPath) {
731
+ if (projectPath) {
732
+ setWorkingDirOverride(projectPath);
733
+ }
734
+ const localPath = getOutputPath('config.json');
735
+ const globalPath = join(homedir(), '.cdp-tools', 'config.json');
736
+ const localDir = dirname(localPath);
737
+ // Create directory if needed
738
+ if (!fs.existsSync(localDir)) {
739
+ await fs.promises.mkdir(localDir, { recursive: true });
740
+ }
741
+ let seeded = false;
742
+ if (!fs.existsSync(localPath) && seedFromGlobal && fs.existsSync(globalPath)) {
743
+ // Seed from global
744
+ const content = await fs.promises.readFile(globalPath, 'utf-8');
745
+ const loaded = JSON.parse(content);
746
+ this.config = this.mergeConfig(DEFAULT_CONFIG, loaded);
747
+ seeded = true;
748
+ }
749
+ // Set configLocation to local explicitly
750
+ this.config.configLocation = 'local';
751
+ this.loadedFromPath = localPath;
752
+ await this.save();
753
+ return { path: localPath, seeded };
754
+ }
755
+ /**
756
+ * Switch to using global config
757
+ * Writes a minimal local config with just configLocation: 'global'
758
+ */
759
+ async useGlobal() {
760
+ const localPath = getOutputPath('config.json');
761
+ const globalPath = join(homedir(), '.cdp-tools', 'config.json');
762
+ const localDir = dirname(localPath);
763
+ const globalDir = dirname(globalPath);
764
+ // Ensure global directory exists (for reading config below)
765
+ if (!fs.existsSync(globalDir)) {
766
+ await fs.promises.mkdir(globalDir, { recursive: true });
767
+ }
768
+ // Write minimal local config with just the preference (atomic write)
769
+ const minimalConfig = { configLocation: 'global' };
770
+ await atomicWriteFile(localPath, JSON.stringify(minimalConfig, null, 2));
771
+ // Load and use global config
772
+ if (fs.existsSync(globalPath)) {
773
+ const content = await fs.promises.readFile(globalPath, 'utf-8');
774
+ const loaded = JSON.parse(content);
775
+ this.config = this.mergeConfig(DEFAULT_CONFIG, loaded);
776
+ }
777
+ else {
778
+ this.config = { ...DEFAULT_CONFIG };
779
+ }
780
+ this.loadedFromPath = globalPath;
781
+ await this.save();
782
+ return { path: globalPath };
783
+ }
784
+ /**
785
+ * Reset config to defaults
786
+ */
787
+ async reset() {
788
+ this.config = { ...DEFAULT_CONFIG };
789
+ await this.save();
790
+ }
791
+ /**
792
+ * Create a backup of current config
793
+ */
794
+ async backup() {
795
+ if (!this.loadedFromPath || !fs.existsSync(this.loadedFromPath)) {
796
+ return null;
797
+ }
798
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
799
+ const backupPath = this.loadedFromPath.replace('.json', `.backup-${timestamp}.json`);
800
+ await fs.promises.copyFile(this.loadedFromPath, backupPath);
801
+ return { path: backupPath };
802
+ }
803
+ /**
804
+ * Clone global config to local
805
+ */
806
+ async cloneFromGlobal() {
807
+ const globalPath = join(homedir(), '.cdp-tools', 'config.json');
808
+ if (!fs.existsSync(globalPath)) {
809
+ return { error: 'No global config exists to clone from' };
810
+ }
811
+ const content = await fs.promises.readFile(globalPath, 'utf-8');
812
+ const loaded = JSON.parse(content);
813
+ this.config = this.mergeConfig(DEFAULT_CONFIG, loaded);
814
+ const localPath = getOutputPath('config.json');
815
+ const localDir = dirname(localPath);
816
+ if (!fs.existsSync(localDir)) {
817
+ await fs.promises.mkdir(localDir, { recursive: true });
818
+ }
819
+ this.loadedFromPath = localPath;
820
+ await this.save();
821
+ return { path: localPath };
822
+ }
823
+ }
824
+ // Export singleton instance
825
+ export const configManager = new ConfigManager();
826
+ //# sourceMappingURL=config.js.map