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.
- package/LICENSE +21 -0
- package/README.md +185 -0
- package/build/atomic-write.d.ts +28 -0
- package/build/atomic-write.d.ts.map +1 -0
- package/build/atomic-write.js +126 -0
- package/build/atomic-write.js.map +1 -0
- package/build/cdp-manager.d.ts +524 -0
- package/build/cdp-manager.d.ts.map +1 -0
- package/build/cdp-manager.js +1828 -0
- package/build/cdp-manager.js.map +1 -0
- package/build/cdp-manager.test.d.ts +6 -0
- package/build/cdp-manager.test.d.ts.map +1 -0
- package/build/cdp-manager.test.js +546 -0
- package/build/cdp-manager.test.js.map +1 -0
- package/build/chrome-launcher.d.ts +400 -0
- package/build/chrome-launcher.d.ts.map +1 -0
- package/build/chrome-launcher.js +1071 -0
- package/build/chrome-launcher.js.map +1 -0
- package/build/chrome-launcher.test.d.ts +11 -0
- package/build/chrome-launcher.test.d.ts.map +1 -0
- package/build/chrome-launcher.test.js +204 -0
- package/build/chrome-launcher.test.js.map +1 -0
- package/build/clickable-cache.d.ts +73 -0
- package/build/clickable-cache.d.ts.map +1 -0
- package/build/clickable-cache.js +90 -0
- package/build/clickable-cache.js.map +1 -0
- package/build/command-recorder.d.ts +401 -0
- package/build/command-recorder.d.ts.map +1 -0
- package/build/command-recorder.js +701 -0
- package/build/command-recorder.js.map +1 -0
- package/build/config.d.ts +390 -0
- package/build/config.d.ts.map +1 -0
- package/build/config.js +826 -0
- package/build/config.js.map +1 -0
- package/build/config.test.d.ts +2 -0
- package/build/config.test.d.ts.map +1 -0
- package/build/config.test.js +112 -0
- package/build/config.test.js.map +1 -0
- package/build/connection-manager.d.ts +159 -0
- package/build/connection-manager.d.ts.map +1 -0
- package/build/connection-manager.js +432 -0
- package/build/connection-manager.js.map +1 -0
- package/build/connection-manager.test.d.ts +11 -0
- package/build/connection-manager.test.d.ts.map +1 -0
- package/build/connection-manager.test.js +114 -0
- package/build/connection-manager.test.js.map +1 -0
- package/build/console-monitor.d.ts +150 -0
- package/build/console-monitor.d.ts.map +1 -0
- package/build/console-monitor.js +454 -0
- package/build/console-monitor.js.map +1 -0
- package/build/dashboard/bundle.js +1 -0
- package/build/dashboard/dashboard-client.d.ts +42 -0
- package/build/dashboard/dashboard-client.d.ts.map +1 -0
- package/build/dashboard/dashboard-client.js +184 -0
- package/build/dashboard/dashboard-client.js.map +1 -0
- package/build/dashboard/dashboard-hub.d.ts +50 -0
- package/build/dashboard/dashboard-hub.d.ts.map +1 -0
- package/build/dashboard/dashboard-hub.js +654 -0
- package/build/dashboard/dashboard-hub.js.map +1 -0
- package/build/dashboard/hub-lock.d.ts +16 -0
- package/build/dashboard/hub-lock.d.ts.map +1 -0
- package/build/dashboard/hub-lock.js +77 -0
- package/build/dashboard/hub-lock.js.map +1 -0
- package/build/dashboard/index.d.ts +25 -0
- package/build/dashboard/index.d.ts.map +1 -0
- package/build/dashboard/index.js +77 -0
- package/build/dashboard/index.js.map +1 -0
- package/build/dashboard/types.d.ts +83 -0
- package/build/dashboard/types.d.ts.map +1 -0
- package/build/dashboard/types.js +6 -0
- package/build/dashboard/types.js.map +1 -0
- package/build/debug-logger.d.ts +85 -0
- package/build/debug-logger.d.ts.map +1 -0
- package/build/debug-logger.js +195 -0
- package/build/debug-logger.js.map +1 -0
- package/build/debugger-aware-wrapper.d.ts +37 -0
- package/build/debugger-aware-wrapper.d.ts.map +1 -0
- package/build/debugger-aware-wrapper.js +128 -0
- package/build/debugger-aware-wrapper.js.map +1 -0
- package/build/dom-change-monitor.d.ts +92 -0
- package/build/dom-change-monitor.d.ts.map +1 -0
- package/build/dom-change-monitor.js +437 -0
- package/build/dom-change-monitor.js.map +1 -0
- package/build/element-collector.d.ts +16 -0
- package/build/element-collector.d.ts.map +1 -0
- package/build/element-collector.js +207 -0
- package/build/element-collector.js.map +1 -0
- package/build/error-helpers.d.ts +43 -0
- package/build/error-helpers.d.ts.map +1 -0
- package/build/error-helpers.js +143 -0
- package/build/error-helpers.js.map +1 -0
- package/build/formatters/console-formatter.d.ts +160 -0
- package/build/formatters/console-formatter.d.ts.map +1 -0
- package/build/formatters/console-formatter.js +338 -0
- package/build/formatters/console-formatter.js.map +1 -0
- package/build/helpers/parser-plugins.d.ts +44 -0
- package/build/helpers/parser-plugins.d.ts.map +1 -0
- package/build/helpers/parser-plugins.js +102 -0
- package/build/helpers/parser-plugins.js.map +1 -0
- package/build/helpers/paths.d.ts +79 -0
- package/build/helpers/paths.d.ts.map +1 -0
- package/build/helpers/paths.js +168 -0
- package/build/helpers/paths.js.map +1 -0
- package/build/helpers/process-liveness.d.ts +24 -0
- package/build/helpers/process-liveness.d.ts.map +1 -0
- package/build/helpers/process-liveness.js +36 -0
- package/build/helpers/process-liveness.js.map +1 -0
- package/build/helpers/process-liveness.test.d.ts +2 -0
- package/build/helpers/process-liveness.test.d.ts.map +1 -0
- package/build/helpers/process-liveness.test.js +40 -0
- package/build/helpers/process-liveness.test.js.map +1 -0
- package/build/helpers/sequence-tree.d.ts +27 -0
- package/build/helpers/sequence-tree.d.ts.map +1 -0
- package/build/helpers/sequence-tree.js +80 -0
- package/build/helpers/sequence-tree.js.map +1 -0
- package/build/helpers/sequence-tree.test.d.ts +2 -0
- package/build/helpers/sequence-tree.test.d.ts.map +1 -0
- package/build/helpers/sequence-tree.test.js +122 -0
- package/build/helpers/sequence-tree.test.js.map +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +2007 -0
- package/build/index.js.map +1 -0
- package/build/interaction-recorder.d.ts +214 -0
- package/build/interaction-recorder.d.ts.map +1 -0
- package/build/interaction-recorder.js +2007 -0
- package/build/interaction-recorder.js.map +1 -0
- package/build/issue-tracker.d.ts +107 -0
- package/build/issue-tracker.d.ts.map +1 -0
- package/build/issue-tracker.js +582 -0
- package/build/issue-tracker.js.map +1 -0
- package/build/issue-tracker.test.d.ts +2 -0
- package/build/issue-tracker.test.d.ts.map +1 -0
- package/build/issue-tracker.test.js +88 -0
- package/build/issue-tracker.test.js.map +1 -0
- package/build/launch-chrome-port.test.d.ts +14 -0
- package/build/launch-chrome-port.test.d.ts.map +1 -0
- package/build/launch-chrome-port.test.js +97 -0
- package/build/launch-chrome-port.test.js.map +1 -0
- package/build/log-processor/orchestrator.d.ts +32 -0
- package/build/log-processor/orchestrator.d.ts.map +1 -0
- package/build/log-processor/orchestrator.js +27 -0
- package/build/log-processor/orchestrator.js.map +1 -0
- package/build/logpoint-execution-tracker.d.ts +57 -0
- package/build/logpoint-execution-tracker.d.ts.map +1 -0
- package/build/logpoint-execution-tracker.js +112 -0
- package/build/logpoint-execution-tracker.js.map +1 -0
- package/build/mcp-supervisor.d.ts +3 -0
- package/build/mcp-supervisor.d.ts.map +1 -0
- package/build/mcp-supervisor.js +211 -0
- package/build/mcp-supervisor.js.map +1 -0
- package/build/messages.d.ts +165 -0
- package/build/messages.d.ts.map +1 -0
- package/build/messages.js +574 -0
- package/build/messages.js.map +1 -0
- package/build/network-monitor.d.ts +172 -0
- package/build/network-monitor.d.ts.map +1 -0
- package/build/network-monitor.js +350 -0
- package/build/network-monitor.js.map +1 -0
- package/build/overlays.d.ts +49 -0
- package/build/overlays.d.ts.map +1 -0
- package/build/overlays.js +409 -0
- package/build/overlays.js.map +1 -0
- package/build/persistent-profiles.test.d.ts +15 -0
- package/build/persistent-profiles.test.d.ts.map +1 -0
- package/build/persistent-profiles.test.js +470 -0
- package/build/persistent-profiles.test.js.map +1 -0
- package/build/port-reserver.d.ts +32 -0
- package/build/port-reserver.d.ts.map +1 -0
- package/build/port-reserver.js +118 -0
- package/build/port-reserver.js.map +1 -0
- package/build/puppeteer-manager.d.ts +47 -0
- package/build/puppeteer-manager.d.ts.map +1 -0
- package/build/puppeteer-manager.js +116 -0
- package/build/puppeteer-manager.js.map +1 -0
- package/build/recording-coverage.test.d.ts +12 -0
- package/build/recording-coverage.test.d.ts.map +1 -0
- package/build/recording-coverage.test.js +72 -0
- package/build/recording-coverage.test.js.map +1 -0
- package/build/reference-validator.d.ts +47 -0
- package/build/reference-validator.d.ts.map +1 -0
- package/build/reference-validator.js +86 -0
- package/build/reference-validator.js.map +1 -0
- package/build/reference-validator.test.d.ts +5 -0
- package/build/reference-validator.test.d.ts.map +1 -0
- package/build/reference-validator.test.js +243 -0
- package/build/reference-validator.test.js.map +1 -0
- package/build/replay-cursor.d.ts +27 -0
- package/build/replay-cursor.d.ts.map +1 -0
- package/build/replay-cursor.js +240 -0
- package/build/replay-cursor.js.map +1 -0
- package/build/runners/docker-compose-runner.d.ts +69 -0
- package/build/runners/docker-compose-runner.d.ts.map +1 -0
- package/build/runners/docker-compose-runner.js +438 -0
- package/build/runners/docker-compose-runner.js.map +1 -0
- package/build/runners/docker-runner.d.ts +65 -0
- package/build/runners/docker-runner.d.ts.map +1 -0
- package/build/runners/docker-runner.js +414 -0
- package/build/runners/docker-runner.js.map +1 -0
- package/build/runners/index.d.ts +19 -0
- package/build/runners/index.d.ts.map +1 -0
- package/build/runners/index.js +35 -0
- package/build/runners/index.js.map +1 -0
- package/build/runners/native-runner.d.ts +95 -0
- package/build/runners/native-runner.d.ts.map +1 -0
- package/build/runners/native-runner.js +370 -0
- package/build/runners/native-runner.js.map +1 -0
- package/build/runners/types.d.ts +147 -0
- package/build/runners/types.d.ts.map +1 -0
- package/build/runners/types.js +25 -0
- package/build/runners/types.js.map +1 -0
- package/build/self-restart.d.ts +12 -0
- package/build/self-restart.d.ts.map +1 -0
- package/build/self-restart.js +41 -0
- package/build/self-restart.js.map +1 -0
- package/build/self-restart.test.d.ts +2 -0
- package/build/self-restart.test.d.ts.map +1 -0
- package/build/self-restart.test.js +34 -0
- package/build/self-restart.test.js.map +1 -0
- package/build/server-claims.d.ts +161 -0
- package/build/server-claims.d.ts.map +1 -0
- package/build/server-claims.js +382 -0
- package/build/server-claims.js.map +1 -0
- package/build/server-claims.test.d.ts +2 -0
- package/build/server-claims.test.d.ts.map +1 -0
- package/build/server-claims.test.js +197 -0
- package/build/server-claims.test.js.map +1 -0
- package/build/server-manager.d.ts +438 -0
- package/build/server-manager.d.ts.map +1 -0
- package/build/server-manager.js +1674 -0
- package/build/server-manager.js.map +1 -0
- package/build/server-manager.test.d.ts +17 -0
- package/build/server-manager.test.d.ts.map +1 -0
- package/build/server-manager.test.js +122 -0
- package/build/server-manager.test.js.map +1 -0
- package/build/server-ownership.test.d.ts +2 -0
- package/build/server-ownership.test.d.ts.map +1 -0
- package/build/server-ownership.test.js +238 -0
- package/build/server-ownership.test.js.map +1 -0
- package/build/server-watcher.d.ts +21 -0
- package/build/server-watcher.d.ts.map +1 -0
- package/build/server-watcher.js +105 -0
- package/build/server-watcher.js.map +1 -0
- package/build/session-detector.d.ts +47 -0
- package/build/session-detector.d.ts.map +1 -0
- package/build/session-detector.js +229 -0
- package/build/session-detector.js.map +1 -0
- package/build/session-detector.test.d.ts +9 -0
- package/build/session-detector.test.d.ts.map +1 -0
- package/build/session-detector.test.js +45 -0
- package/build/session-detector.test.js.map +1 -0
- package/build/sourcemap-handler.d.ts +114 -0
- package/build/sourcemap-handler.d.ts.map +1 -0
- package/build/sourcemap-handler.js +479 -0
- package/build/sourcemap-handler.js.map +1 -0
- package/build/supervisor/child-manager.d.ts +43 -0
- package/build/supervisor/child-manager.d.ts.map +1 -0
- package/build/supervisor/child-manager.js +148 -0
- package/build/supervisor/child-manager.js.map +1 -0
- package/build/supervisor/child-manager.test.d.ts +2 -0
- package/build/supervisor/child-manager.test.d.ts.map +1 -0
- package/build/supervisor/child-manager.test.js +107 -0
- package/build/supervisor/child-manager.test.js.map +1 -0
- package/build/supervisor/client-watcher.d.ts +48 -0
- package/build/supervisor/client-watcher.d.ts.map +1 -0
- package/build/supervisor/client-watcher.js +186 -0
- package/build/supervisor/client-watcher.js.map +1 -0
- package/build/supervisor/client-watcher.test.d.ts +2 -0
- package/build/supervisor/client-watcher.test.d.ts.map +1 -0
- package/build/supervisor/client-watcher.test.js +121 -0
- package/build/supervisor/client-watcher.test.js.map +1 -0
- package/build/supervisor/idle-config.d.ts +30 -0
- package/build/supervisor/idle-config.d.ts.map +1 -0
- package/build/supervisor/idle-config.js +76 -0
- package/build/supervisor/idle-config.js.map +1 -0
- package/build/supervisor/idle-config.test.d.ts +2 -0
- package/build/supervisor/idle-config.test.d.ts.map +1 -0
- package/build/supervisor/idle-config.test.js +119 -0
- package/build/supervisor/idle-config.test.js.map +1 -0
- package/build/supervisor/ndjson-reader.d.ts +47 -0
- package/build/supervisor/ndjson-reader.d.ts.map +1 -0
- package/build/supervisor/ndjson-reader.js +75 -0
- package/build/supervisor/ndjson-reader.js.map +1 -0
- package/build/supervisor/ndjson-reader.test.d.ts +2 -0
- package/build/supervisor/ndjson-reader.test.d.ts.map +1 -0
- package/build/supervisor/ndjson-reader.test.js +92 -0
- package/build/supervisor/ndjson-reader.test.js.map +1 -0
- package/build/supervisor/pidfile.d.ts +11 -0
- package/build/supervisor/pidfile.d.ts.map +1 -0
- package/build/supervisor/pidfile.js +29 -0
- package/build/supervisor/pidfile.js.map +1 -0
- package/build/supervisor/pidfile.test.d.ts +8 -0
- package/build/supervisor/pidfile.test.d.ts.map +1 -0
- package/build/supervisor/pidfile.test.js +46 -0
- package/build/supervisor/pidfile.test.js.map +1 -0
- package/build/supervisor/restart-coordinator.d.ts +73 -0
- package/build/supervisor/restart-coordinator.d.ts.map +1 -0
- package/build/supervisor/restart-coordinator.js +300 -0
- package/build/supervisor/restart-coordinator.js.map +1 -0
- package/build/supervisor/restart-coordinator.test.d.ts +2 -0
- package/build/supervisor/restart-coordinator.test.d.ts.map +1 -0
- package/build/supervisor/restart-coordinator.test.js +434 -0
- package/build/supervisor/restart-coordinator.test.js.map +1 -0
- package/build/test-support/fake-execute-tool-call.d.ts +11 -0
- package/build/test-support/fake-execute-tool-call.d.ts.map +1 -0
- package/build/test-support/fake-execute-tool-call.js +19 -0
- package/build/test-support/fake-execute-tool-call.js.map +1 -0
- package/build/tool-error.d.ts +14 -0
- package/build/tool-error.d.ts.map +1 -0
- package/build/tool-error.js +18 -0
- package/build/tool-error.js.map +1 -0
- package/build/tool-response.d.ts +293 -0
- package/build/tool-response.d.ts.map +1 -0
- package/build/tool-response.js +365 -0
- package/build/tool-response.js.map +1 -0
- package/build/tools/assert-tools.d.ts +62 -0
- package/build/tools/assert-tools.d.ts.map +1 -0
- package/build/tools/assert-tools.js +330 -0
- package/build/tools/assert-tools.js.map +1 -0
- package/build/tools/breakpoint-await-abort.test.d.ts +2 -0
- package/build/tools/breakpoint-await-abort.test.d.ts.map +1 -0
- package/build/tools/breakpoint-await-abort.test.js +124 -0
- package/build/tools/breakpoint-await-abort.test.js.map +1 -0
- package/build/tools/breakpoint-tools.d.ts +99 -0
- package/build/tools/breakpoint-tools.d.ts.map +1 -0
- package/build/tools/breakpoint-tools.js +1197 -0
- package/build/tools/breakpoint-tools.js.map +1 -0
- package/build/tools/config-tools.d.ts +58 -0
- package/build/tools/config-tools.d.ts.map +1 -0
- package/build/tools/config-tools.js +180 -0
- package/build/tools/config-tools.js.map +1 -0
- package/build/tools/connection-routing.test.d.ts +12 -0
- package/build/tools/connection-routing.test.d.ts.map +1 -0
- package/build/tools/connection-routing.test.js +206 -0
- package/build/tools/connection-routing.test.js.map +1 -0
- package/build/tools/console-tools.d.ts +84 -0
- package/build/tools/console-tools.d.ts.map +1 -0
- package/build/tools/console-tools.js +241 -0
- package/build/tools/console-tools.js.map +1 -0
- package/build/tools/content-tools.d.ts +74 -0
- package/build/tools/content-tools.d.ts.map +1 -0
- package/build/tools/content-tools.js +582 -0
- package/build/tools/content-tools.js.map +1 -0
- package/build/tools/dashboard-tools.d.ts +38 -0
- package/build/tools/dashboard-tools.d.ts.map +1 -0
- package/build/tools/dashboard-tools.js +123 -0
- package/build/tools/dashboard-tools.js.map +1 -0
- package/build/tools/dom-tools.d.ts +41 -0
- package/build/tools/dom-tools.d.ts.map +1 -0
- package/build/tools/dom-tools.js +277 -0
- package/build/tools/dom-tools.js.map +1 -0
- package/build/tools/download-tools.d.ts +55 -0
- package/build/tools/download-tools.d.ts.map +1 -0
- package/build/tools/download-tools.js +280 -0
- package/build/tools/download-tools.js.map +1 -0
- package/build/tools/execution-tools.d.ts +40 -0
- package/build/tools/execution-tools.d.ts.map +1 -0
- package/build/tools/execution-tools.js +121 -0
- package/build/tools/execution-tools.js.map +1 -0
- package/build/tools/input-abort.test.d.ts +2 -0
- package/build/tools/input-abort.test.d.ts.map +1 -0
- package/build/tools/input-abort.test.js +227 -0
- package/build/tools/input-abort.test.js.map +1 -0
- package/build/tools/input-ambiguous-selector.test.d.ts +2 -0
- package/build/tools/input-ambiguous-selector.test.d.ts.map +1 -0
- package/build/tools/input-ambiguous-selector.test.js +34 -0
- package/build/tools/input-ambiguous-selector.test.js.map +1 -0
- package/build/tools/input-tools.d.ts +168 -0
- package/build/tools/input-tools.d.ts.map +1 -0
- package/build/tools/input-tools.js +1397 -0
- package/build/tools/input-tools.js.map +1 -0
- package/build/tools/inspection-tools.d.ts +112 -0
- package/build/tools/inspection-tools.d.ts.map +1 -0
- package/build/tools/inspection-tools.js +637 -0
- package/build/tools/inspection-tools.js.map +1 -0
- package/build/tools/interpolation.d.ts +30 -0
- package/build/tools/interpolation.d.ts.map +1 -0
- package/build/tools/interpolation.js +143 -0
- package/build/tools/interpolation.js.map +1 -0
- package/build/tools/issues-abort-hygiene.test.d.ts +2 -0
- package/build/tools/issues-abort-hygiene.test.d.ts.map +1 -0
- package/build/tools/issues-abort-hygiene.test.js +79 -0
- package/build/tools/issues-abort-hygiene.test.js.map +1 -0
- package/build/tools/issues-tools.d.ts +98 -0
- package/build/tools/issues-tools.d.ts.map +1 -0
- package/build/tools/issues-tools.js +821 -0
- package/build/tools/issues-tools.js.map +1 -0
- package/build/tools/issues-tools.test.d.ts +8 -0
- package/build/tools/issues-tools.test.d.ts.map +1 -0
- package/build/tools/issues-tools.test.js +184 -0
- package/build/tools/issues-tools.test.js.map +1 -0
- package/build/tools/modal-tools.d.ts +73 -0
- package/build/tools/modal-tools.d.ts.map +1 -0
- package/build/tools/modal-tools.js +161 -0
- package/build/tools/modal-tools.js.map +1 -0
- package/build/tools/network-tools.d.ts +72 -0
- package/build/tools/network-tools.d.ts.map +1 -0
- package/build/tools/network-tools.js +432 -0
- package/build/tools/network-tools.js.map +1 -0
- package/build/tools/page-tools.d.ts +78 -0
- package/build/tools/page-tools.d.ts.map +1 -0
- package/build/tools/page-tools.js +255 -0
- package/build/tools/page-tools.js.map +1 -0
- package/build/tools/plugin-tools.d.ts +16 -0
- package/build/tools/plugin-tools.d.ts.map +1 -0
- package/build/tools/plugin-tools.js +17 -0
- package/build/tools/plugin-tools.js.map +1 -0
- package/build/tools/replay-abort.test.d.ts +2 -0
- package/build/tools/replay-abort.test.d.ts.map +1 -0
- package/build/tools/replay-abort.test.js +259 -0
- package/build/tools/replay-abort.test.js.map +1 -0
- package/build/tools/replay-add-conditional.test.d.ts +2 -0
- package/build/tools/replay-add-conditional.test.d.ts.map +1 -0
- package/build/tools/replay-add-conditional.test.js +205 -0
- package/build/tools/replay-add-conditional.test.js.map +1 -0
- package/build/tools/replay-background-run.test.d.ts +2 -0
- package/build/tools/replay-background-run.test.d.ts.map +1 -0
- package/build/tools/replay-background-run.test.js +176 -0
- package/build/tools/replay-background-run.test.js.map +1 -0
- package/build/tools/replay-capture-variables.test.d.ts +2 -0
- package/build/tools/replay-capture-variables.test.d.ts.map +1 -0
- package/build/tools/replay-capture-variables.test.js +331 -0
- package/build/tools/replay-capture-variables.test.js.map +1 -0
- package/build/tools/replay-conditional-idb-and-launch.test.d.ts +11 -0
- package/build/tools/replay-conditional-idb-and-launch.test.d.ts.map +1 -0
- package/build/tools/replay-conditional-idb-and-launch.test.js +429 -0
- package/build/tools/replay-conditional-idb-and-launch.test.js.map +1 -0
- package/build/tools/replay-conditional-timeout.test.d.ts +14 -0
- package/build/tools/replay-conditional-timeout.test.d.ts.map +1 -0
- package/build/tools/replay-conditional-timeout.test.js +126 -0
- package/build/tools/replay-conditional-timeout.test.js.map +1 -0
- package/build/tools/replay-declare.test.d.ts +2 -0
- package/build/tools/replay-declare.test.d.ts.map +1 -0
- package/build/tools/replay-declare.test.js +106 -0
- package/build/tools/replay-declare.test.js.map +1 -0
- package/build/tools/replay-declared-browser-cleanup.test.d.ts +2 -0
- package/build/tools/replay-declared-browser-cleanup.test.d.ts.map +1 -0
- package/build/tools/replay-declared-browser-cleanup.test.js +103 -0
- package/build/tools/replay-declared-browser-cleanup.test.js.map +1 -0
- package/build/tools/replay-declared-profile.test.d.ts +2 -0
- package/build/tools/replay-declared-profile.test.d.ts.map +1 -0
- package/build/tools/replay-declared-profile.test.js +116 -0
- package/build/tools/replay-declared-profile.test.js.map +1 -0
- package/build/tools/replay-executor.d.ts +556 -0
- package/build/tools/replay-executor.d.ts.map +1 -0
- package/build/tools/replay-executor.js +2347 -0
- package/build/tools/replay-executor.js.map +1 -0
- package/build/tools/replay-folder-save.test.d.ts +2 -0
- package/build/tools/replay-folder-save.test.d.ts.map +1 -0
- package/build/tools/replay-folder-save.test.js +63 -0
- package/build/tools/replay-folder-save.test.js.map +1 -0
- package/build/tools/replay-formatters.d.ts +148 -0
- package/build/tools/replay-formatters.d.ts.map +1 -0
- package/build/tools/replay-formatters.js +856 -0
- package/build/tools/replay-formatters.js.map +1 -0
- package/build/tools/replay-kill-chrome-on-finish.test.d.ts +2 -0
- package/build/tools/replay-kill-chrome-on-finish.test.d.ts.map +1 -0
- package/build/tools/replay-kill-chrome-on-finish.test.js +175 -0
- package/build/tools/replay-kill-chrome-on-finish.test.js.map +1 -0
- package/build/tools/replay-multi-connection.test.d.ts +2 -0
- package/build/tools/replay-multi-connection.test.d.ts.map +1 -0
- package/build/tools/replay-multi-connection.test.js +547 -0
- package/build/tools/replay-multi-connection.test.js.map +1 -0
- package/build/tools/replay-output-and-record-options.test.d.ts +2 -0
- package/build/tools/replay-output-and-record-options.test.d.ts.map +1 -0
- package/build/tools/replay-output-and-record-options.test.js +240 -0
- package/build/tools/replay-output-and-record-options.test.js.map +1 -0
- package/build/tools/replay-production-error-shape.test.d.ts +7 -0
- package/build/tools/replay-production-error-shape.test.d.ts.map +1 -0
- package/build/tools/replay-production-error-shape.test.js +93 -0
- package/build/tools/replay-production-error-shape.test.js.map +1 -0
- package/build/tools/replay-rebase.test.d.ts +2 -0
- package/build/tools/replay-rebase.test.d.ts.map +1 -0
- package/build/tools/replay-rebase.test.js +58 -0
- package/build/tools/replay-rebase.test.js.map +1 -0
- package/build/tools/replay-retry-classification.test.d.ts +9 -0
- package/build/tools/replay-retry-classification.test.d.ts.map +1 -0
- package/build/tools/replay-retry-classification.test.js +57 -0
- package/build/tools/replay-retry-classification.test.js.map +1 -0
- package/build/tools/replay-run-registry.d.ts +55 -0
- package/build/tools/replay-run-registry.d.ts.map +1 -0
- package/build/tools/replay-run-registry.js +65 -0
- package/build/tools/replay-run-registry.js.map +1 -0
- package/build/tools/replay-sequence-reload.test.d.ts +2 -0
- package/build/tools/replay-sequence-reload.test.d.ts.map +1 -0
- package/build/tools/replay-sequence-reload.test.js +82 -0
- package/build/tools/replay-sequence-reload.test.js.map +1 -0
- package/build/tools/replay-socket-scope.test.d.ts +2 -0
- package/build/tools/replay-socket-scope.test.d.ts.map +1 -0
- package/build/tools/replay-socket-scope.test.js +75 -0
- package/build/tools/replay-socket-scope.test.js.map +1 -0
- package/build/tools/replay-step-connection.test.d.ts +2 -0
- package/build/tools/replay-step-connection.test.d.ts.map +1 -0
- package/build/tools/replay-step-connection.test.js +292 -0
- package/build/tools/replay-step-connection.test.js.map +1 -0
- package/build/tools/replay-step-timeout.test.d.ts +2 -0
- package/build/tools/replay-step-timeout.test.d.ts.map +1 -0
- package/build/tools/replay-step-timeout.test.js +204 -0
- package/build/tools/replay-step-timeout.test.js.map +1 -0
- package/build/tools/replay-tags.test.d.ts +2 -0
- package/build/tools/replay-tags.test.d.ts.map +1 -0
- package/build/tools/replay-tags.test.js +99 -0
- package/build/tools/replay-tags.test.js.map +1 -0
- package/build/tools/replay-teardown-and-foreach.test.d.ts +13 -0
- package/build/tools/replay-teardown-and-foreach.test.d.ts.map +1 -0
- package/build/tools/replay-teardown-and-foreach.test.js +318 -0
- package/build/tools/replay-teardown-and-foreach.test.js.map +1 -0
- package/build/tools/replay-tool-name-validation.test.d.ts +2 -0
- package/build/tools/replay-tool-name-validation.test.d.ts.map +1 -0
- package/build/tools/replay-tool-name-validation.test.js +280 -0
- package/build/tools/replay-tool-name-validation.test.js.map +1 -0
- package/build/tools/replay-tools.d.ts +288 -0
- package/build/tools/replay-tools.d.ts.map +1 -0
- package/build/tools/replay-tools.js +2994 -0
- package/build/tools/replay-tools.js.map +1 -0
- package/build/tools/request-abort.test.d.ts +2 -0
- package/build/tools/request-abort.test.d.ts.map +1 -0
- package/build/tools/request-abort.test.js +130 -0
- package/build/tools/request-abort.test.js.map +1 -0
- package/build/tools/request-tools.d.ts +56 -0
- package/build/tools/request-tools.d.ts.map +1 -0
- package/build/tools/request-tools.js +174 -0
- package/build/tools/request-tools.js.map +1 -0
- package/build/tools/screenshot-tools.d.ts +135 -0
- package/build/tools/screenshot-tools.d.ts.map +1 -0
- package/build/tools/screenshot-tools.js +636 -0
- package/build/tools/screenshot-tools.js.map +1 -0
- package/build/tools/server-tools.d.ts +88 -0
- package/build/tools/server-tools.d.ts.map +1 -0
- package/build/tools/server-tools.js +485 -0
- package/build/tools/server-tools.js.map +1 -0
- package/build/tools/source-tools.d.ts +46 -0
- package/build/tools/source-tools.d.ts.map +1 -0
- package/build/tools/source-tools.js +47 -0
- package/build/tools/source-tools.js.map +1 -0
- package/build/tools/storage-tools.d.ts +141 -0
- package/build/tools/storage-tools.d.ts.map +1 -0
- package/build/tools/storage-tools.js +894 -0
- package/build/tools/storage-tools.js.map +1 -0
- package/build/tools/storage-tools.test.d.ts +13 -0
- package/build/tools/storage-tools.test.d.ts.map +1 -0
- package/build/tools/storage-tools.test.js +707 -0
- package/build/tools/storage-tools.test.js.map +1 -0
- package/build/tools/tab-tools.d.ts +42 -0
- package/build/tools/tab-tools.d.ts.map +1 -0
- package/build/tools/tab-tools.js +296 -0
- package/build/tools/tab-tools.js.map +1 -0
- package/build/tools/wait-tools.d.ts +85 -0
- package/build/tools/wait-tools.d.ts.map +1 -0
- package/build/tools/wait-tools.js +236 -0
- package/build/tools/wait-tools.js.map +1 -0
- package/build/tools/wait-tools.test.d.ts +9 -0
- package/build/tools/wait-tools.test.d.ts.map +1 -0
- package/build/tools/wait-tools.test.js +193 -0
- package/build/tools/wait-tools.test.js.map +1 -0
- package/build/types.d.ts +133 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +5 -0
- package/build/types.js.map +1 -0
- package/build/ui-verifier.d.ts +88 -0
- package/build/ui-verifier.d.ts.map +1 -0
- package/build/ui-verifier.js +540 -0
- package/build/ui-verifier.js.map +1 -0
- package/build/utils/abort.d.ts +68 -0
- package/build/utils/abort.d.ts.map +1 -0
- package/build/utils/abort.js +159 -0
- package/build/utils/abort.js.map +1 -0
- package/build/utils/abort.test.d.ts +2 -0
- package/build/utils/abort.test.d.ts.map +1 -0
- package/build/utils/abort.test.js +174 -0
- package/build/utils/abort.test.js.map +1 -0
- package/build/utils/modal-detection-core.d.ts +150 -0
- package/build/utils/modal-detection-core.d.ts.map +1 -0
- package/build/utils/modal-detection-core.js +332 -0
- package/build/utils/modal-detection-core.js.map +1 -0
- package/build/utils/modal-detection-core.test.d.ts +5 -0
- package/build/utils/modal-detection-core.test.d.ts.map +1 -0
- package/build/utils/modal-detection-core.test.js +492 -0
- package/build/utils/modal-detection-core.test.js.map +1 -0
- package/build/utils/modal-detector.d.ts +29 -0
- package/build/utils/modal-detector.d.ts.map +1 -0
- package/build/utils/modal-detector.js +281 -0
- package/build/utils/modal-detector.js.map +1 -0
- package/build/utils/modal-dismissal.d.ts +32 -0
- package/build/utils/modal-dismissal.d.ts.map +1 -0
- package/build/utils/modal-dismissal.js +190 -0
- package/build/utils/modal-dismissal.js.map +1 -0
- package/build/utils/port-check.d.ts +31 -0
- package/build/utils/port-check.d.ts.map +1 -0
- package/build/utils/port-check.js +87 -0
- package/build/utils/port-check.js.map +1 -0
- package/build/utils/port-check.test.d.ts +2 -0
- package/build/utils/port-check.test.d.ts.map +1 -0
- package/build/utils/port-check.test.js +66 -0
- package/build/utils/port-check.test.js.map +1 -0
- package/build/utils/selector-resolver.d.ts +66 -0
- package/build/utils/selector-resolver.d.ts.map +1 -0
- package/build/utils/selector-resolver.js +200 -0
- package/build/utils/selector-resolver.js.map +1 -0
- package/build/validation-helpers.d.ts +41 -0
- package/build/validation-helpers.d.ts.map +1 -0
- package/build/validation-helpers.js +236 -0
- package/build/validation-helpers.js.map +1 -0
- package/build/window-sizing.d.ts +26 -0
- package/build/window-sizing.d.ts.map +1 -0
- package/build/window-sizing.js +87 -0
- package/build/window-sizing.js.map +1 -0
- package/build/window-sizing.test.d.ts +8 -0
- package/build/window-sizing.test.d.ts.map +1 -0
- package/build/window-sizing.test.js +113 -0
- package/build/window-sizing.test.js.map +1 -0
- package/docs/README.md +74 -0
- package/docs/automation.md +562 -0
- package/docs/debugging.md +229 -0
- package/docs/installation.md +133 -0
- package/docs/instructions.md +227 -0
- package/docs/mcp-instructions.md +15 -0
- package/docs/message-system.md +327 -0
- package/docs/messages.md +3480 -0
- package/docs/parser-plugins.md +132 -0
- package/docs/replay.md +1411 -0
- package/docs/troubleshooting.md +272 -0
- package/docs/working_with_llm_notes.md +5 -0
- package/package.json +87 -0
- package/skills/devharness/SKILL.md +195 -0
- package/skills/devharness/references/sequences.md +613 -0
- package/skills/devharness/references/tool-categories.md +99 -0
|
@@ -0,0 +1,1197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Breakpoint Management Tools
|
|
3
|
+
*/
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { createTool } from '../validation-helpers.js';
|
|
6
|
+
import { createSuccessResponse, createErrorResponse, getErrorMessage } from '../messages.js';
|
|
7
|
+
import { abortErrorFor, isAbortError, throwIfAborted } from '../utils/abort.js';
|
|
8
|
+
/**
|
|
9
|
+
* Helper to resolve TypeScript source maps for breakpoint locations.
|
|
10
|
+
* Handles Vite-style serving (skip translation) vs traditional builds (translate).
|
|
11
|
+
*/
|
|
12
|
+
async function resolveBreakpointLocation(url, lineNumber, columnNumber, cdpManager, sourceMapHandler) {
|
|
13
|
+
let targetUrl = url;
|
|
14
|
+
let targetLine = lineNumber;
|
|
15
|
+
let targetColumn = columnNumber;
|
|
16
|
+
if (url.endsWith('.ts') || url.endsWith('.tsx')) {
|
|
17
|
+
// Check if this .ts/.tsx URL is already loaded as a script (Vite dev server)
|
|
18
|
+
const tsScriptLoaded = cdpManager.isScriptLoaded(url);
|
|
19
|
+
if (!tsScriptLoaded) {
|
|
20
|
+
// Traditional build: .ts files compiled to .js, need source map translation
|
|
21
|
+
const mapped = await sourceMapHandler.mapToGenerated(url, lineNumber, columnNumber || 0);
|
|
22
|
+
if (mapped) {
|
|
23
|
+
targetUrl = mapped.generatedFile;
|
|
24
|
+
targetLine = mapped.line;
|
|
25
|
+
targetColumn = mapped.column;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// If tsScriptLoaded is true, use url and lineNumber directly
|
|
29
|
+
// CDP will handle source map translation via inline source maps
|
|
30
|
+
}
|
|
31
|
+
return { url: targetUrl, line: targetLine, column: targetColumn };
|
|
32
|
+
}
|
|
33
|
+
// Schema definitions
|
|
34
|
+
const breakpointSchema = z.object({
|
|
35
|
+
action: z.enum([
|
|
36
|
+
'set', 'remove', 'list', 'setLogpoint', 'validate', 'resetCounter', 'waitForScript',
|
|
37
|
+
'setDOMBreakpoint', 'setEventBreakpoint', 'setXHRBreakpoint', 'await'
|
|
38
|
+
]),
|
|
39
|
+
connectionReason: z.string().optional(),
|
|
40
|
+
// Location
|
|
41
|
+
url: z.string().optional(),
|
|
42
|
+
lineNumber: z.number().optional(),
|
|
43
|
+
columnNumber: z.number().optional(),
|
|
44
|
+
condition: z.string().optional(),
|
|
45
|
+
// Logpoint
|
|
46
|
+
logMessage: z.string().optional().describe('Message with {expr} interpolation'),
|
|
47
|
+
includeCallStack: z.boolean().optional(),
|
|
48
|
+
includeVariables: z.boolean().optional(),
|
|
49
|
+
maxExecutions: z.number().int().min(1).optional(),
|
|
50
|
+
// Timing
|
|
51
|
+
timeout: z.number().optional().describe('Timeout ms'),
|
|
52
|
+
// Management
|
|
53
|
+
breakpointId: z.string().optional(),
|
|
54
|
+
// DOM breakpoint
|
|
55
|
+
selector: z.string().optional(),
|
|
56
|
+
domBreakpointType: z.enum(['subtree-modified', 'attribute-modified', 'node-removed']).optional(),
|
|
57
|
+
// Event breakpoint
|
|
58
|
+
eventName: z.string().optional().describe('Event: click, submit, input, keydown...'),
|
|
59
|
+
targetName: z.string().optional().describe('Filter by element type'),
|
|
60
|
+
// XHR breakpoint
|
|
61
|
+
urlPattern: z.string().optional().describe('URL substring to match'),
|
|
62
|
+
}).strict();
|
|
63
|
+
export function createBreakpointTools(cdpManager, sourceMapHandler, logpointTracker, resolveConnectionFromReason) {
|
|
64
|
+
return {
|
|
65
|
+
breakpoint: createTool('Manage breakpoints and logpoints. Actions: set (line breakpoint), remove (remove by ID), list (list all), setLogpoint (log without pausing), validate (test expressions), resetCounter (reset logpoint counter), waitForScript (wait for script load), setDOMBreakpoint (pause when element changes), setEventBreakpoint (pause when event fires), setXHRBreakpoint (pause on network requests), await (set breakpoint and wait for hit - user can abort)', breakpointSchema, async (args, abortSignal) => {
|
|
66
|
+
const { action } = args;
|
|
67
|
+
// Resolve connection if connectionReason is provided
|
|
68
|
+
let targetCdpManager = cdpManager;
|
|
69
|
+
if (args.connectionReason && resolveConnectionFromReason) {
|
|
70
|
+
const resolved = await resolveConnectionFromReason(args.connectionReason);
|
|
71
|
+
if (!resolved) {
|
|
72
|
+
return createErrorResponse('CONNECTION_NOT_FOUND');
|
|
73
|
+
}
|
|
74
|
+
targetCdpManager = resolved.cdpManager;
|
|
75
|
+
}
|
|
76
|
+
switch (action) {
|
|
77
|
+
case 'set': {
|
|
78
|
+
if (!args.url || args.lineNumber === undefined) {
|
|
79
|
+
return createErrorResponse('INVALID_PARAMS', { message: 'url and lineNumber are required for set action' });
|
|
80
|
+
}
|
|
81
|
+
// Check connection and runtime type
|
|
82
|
+
const runtimeType = targetCdpManager.getRuntimeType();
|
|
83
|
+
const isConnected = targetCdpManager.isConnected();
|
|
84
|
+
if (!isConnected) {
|
|
85
|
+
return createErrorResponse('DEBUGGER_NOT_CONNECTED');
|
|
86
|
+
}
|
|
87
|
+
// Resolve source maps for TypeScript files
|
|
88
|
+
const resolved = await resolveBreakpointLocation(args.url, args.lineNumber, args.columnNumber, targetCdpManager, sourceMapHandler);
|
|
89
|
+
const targetUrl = resolved.url;
|
|
90
|
+
const targetLine = resolved.line;
|
|
91
|
+
const targetColumn = resolved.column;
|
|
92
|
+
try {
|
|
93
|
+
const breakpoint = await targetCdpManager.setBreakpoint(targetUrl, targetLine, targetColumn, args.condition);
|
|
94
|
+
// Check if breakpoint is pending (script not loaded yet)
|
|
95
|
+
if (breakpoint.status === 'pending') {
|
|
96
|
+
// Don't inject console link for pending breakpoints (script not loaded)
|
|
97
|
+
let markdown = `## Breakpoint Set (Pending)\n\n`;
|
|
98
|
+
markdown += `**Breakpoint ID:** \`${breakpoint.breakpointId}\`\n`;
|
|
99
|
+
markdown += `**URL:** \`${targetUrl}\`\n`;
|
|
100
|
+
markdown += `**Line:** ${targetLine}\n`;
|
|
101
|
+
if (args.condition) {
|
|
102
|
+
markdown += `**Condition:** \`${args.condition}\`\n`;
|
|
103
|
+
}
|
|
104
|
+
markdown += `\n**Status:** ⏳ Pending - Script not loaded yet\n\n`;
|
|
105
|
+
markdown += `**Note:** The breakpoint has been set and will activate automatically when the script loads. `;
|
|
106
|
+
markdown += `Use \`navigate({ action: 'goto' })\` or \`navigate({ action: 'reload' })\` to load the page.\n\n`;
|
|
107
|
+
markdown += `**TIP:** Use \`breakpoint({ action: 'waitForScript', url: '${targetUrl}' })\` to wait for the script to load.`;
|
|
108
|
+
return {
|
|
109
|
+
content: [{
|
|
110
|
+
type: 'text',
|
|
111
|
+
text: markdown,
|
|
112
|
+
}],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
// Get actual resolved location (CDP returns 0-based, convert to 1-based)
|
|
116
|
+
const resolvedLine = breakpoint.location.lineNumber + 1;
|
|
117
|
+
const resolvedColumn = breakpoint.location.columnNumber !== undefined
|
|
118
|
+
? breakpoint.location.columnNumber + 1
|
|
119
|
+
: undefined;
|
|
120
|
+
// Check if location was adjusted (line or column)
|
|
121
|
+
// targetColumn was set earlier (line 82) and may have been modified by source mapping
|
|
122
|
+
const lineAdjusted = resolvedLine !== targetLine;
|
|
123
|
+
const columnAdjusted = targetColumn !== undefined && resolvedColumn !== undefined && resolvedColumn !== targetColumn;
|
|
124
|
+
const wasAdjusted = lineAdjusted || columnAdjusted;
|
|
125
|
+
// Inject clickable console link at resolved location
|
|
126
|
+
const icon = args.condition ? '🔶' : '🔴';
|
|
127
|
+
const label = args.condition ? 'Conditional breakpoint set at' : 'Breakpoint set at';
|
|
128
|
+
await targetCdpManager.injectConsoleLink(targetUrl, resolvedLine, `${icon} ${label}`);
|
|
129
|
+
// Build location strings for message
|
|
130
|
+
const resolvedLocation = resolvedColumn !== undefined
|
|
131
|
+
? `line ${resolvedLine}:${resolvedColumn}`
|
|
132
|
+
: `line ${resolvedLine}`;
|
|
133
|
+
const requestedLocation = targetColumn !== undefined
|
|
134
|
+
? `line ${targetLine}:${targetColumn}`
|
|
135
|
+
: `line ${targetLine}`;
|
|
136
|
+
// Return markdown-only success response with resolved location info
|
|
137
|
+
return createSuccessResponse('BREAKPOINT_SET_SUCCESS', {
|
|
138
|
+
url: targetUrl,
|
|
139
|
+
resolvedLine: resolvedLine,
|
|
140
|
+
resolvedLocation: resolvedLocation,
|
|
141
|
+
requestedLocation: requestedLocation,
|
|
142
|
+
wasAdjusted: wasAdjusted,
|
|
143
|
+
breakpointId: breakpoint.breakpointId,
|
|
144
|
+
condition: args.condition,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
// Build context-aware error message
|
|
149
|
+
let markdown = getErrorMessage('BREAKPOINT_SET_FAILED', {
|
|
150
|
+
url: targetUrl,
|
|
151
|
+
lineNumber: targetLine,
|
|
152
|
+
error: error.message,
|
|
153
|
+
});
|
|
154
|
+
// Add runtime-specific TIP if applicable
|
|
155
|
+
if (runtimeType === 'chrome' && (args.url.includes('/dist/') || args.url.includes('index.js'))) {
|
|
156
|
+
markdown += '\n\n**TIP:** You are connected to Chrome (browser) but trying to set a breakpoint on what looks like server code. ' +
|
|
157
|
+
'If this is Node.js server code, you need to connect to the Node.js debugger separately using `connectDebugger({port: 9229})`.';
|
|
158
|
+
}
|
|
159
|
+
else if (runtimeType === 'node' && args.url.includes('/public/')) {
|
|
160
|
+
markdown += '\n\n**TIP:** You are connected to Node.js but trying to set a breakpoint on what looks like browser code. ' +
|
|
161
|
+
'You may need to connect to Chrome using `connectDebugger({port: 9222})` for client-side debugging.';
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
content: [
|
|
165
|
+
{
|
|
166
|
+
type: 'text',
|
|
167
|
+
text: markdown,
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
isError: true,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
case 'remove': {
|
|
175
|
+
if (!args.breakpointId) {
|
|
176
|
+
return createErrorResponse('INVALID_PARAMS', { message: 'breakpointId is required for remove action' });
|
|
177
|
+
}
|
|
178
|
+
const bpId = args.breakpointId;
|
|
179
|
+
// Dispatch to appropriate remove method based on ID prefix
|
|
180
|
+
if (bpId.startsWith('dom-bp-')) {
|
|
181
|
+
await targetCdpManager.removeDOMBreakpoint(bpId);
|
|
182
|
+
}
|
|
183
|
+
else if (bpId.startsWith('event-bp-')) {
|
|
184
|
+
await targetCdpManager.removeEventListenerBreakpoint(bpId);
|
|
185
|
+
}
|
|
186
|
+
else if (bpId.startsWith('xhr-bp-')) {
|
|
187
|
+
await targetCdpManager.removeXHRBreakpoint(bpId);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
// Line breakpoint or logpoint - unregister from tracker first
|
|
191
|
+
if (logpointTracker) {
|
|
192
|
+
logpointTracker.unregisterLogpoint(bpId);
|
|
193
|
+
}
|
|
194
|
+
await targetCdpManager.removeBreakpoint(bpId);
|
|
195
|
+
}
|
|
196
|
+
return createSuccessResponse('BREAKPOINT_REMOVE_SUCCESS', { breakpointId: bpId });
|
|
197
|
+
}
|
|
198
|
+
case 'list': {
|
|
199
|
+
const breakpoints = targetCdpManager.getBreakpoints();
|
|
200
|
+
const counts = targetCdpManager.getBreakpointCounts();
|
|
201
|
+
const domBreakpoints = targetCdpManager.getDOMBreakpoints();
|
|
202
|
+
const eventBreakpoints = targetCdpManager.getEventListenerBreakpoints();
|
|
203
|
+
const xhrBreakpoints = targetCdpManager.getXHRBreakpoints();
|
|
204
|
+
// Count pending breakpoints
|
|
205
|
+
const pendingCount = breakpoints.filter(bp => bp.status === 'pending').length;
|
|
206
|
+
// Calculate total across all types
|
|
207
|
+
const totalAll = counts.total + domBreakpoints.length + eventBreakpoints.length + xhrBreakpoints.length;
|
|
208
|
+
// Build markdown response
|
|
209
|
+
let markdown = `## Active Breakpoints\n\n`;
|
|
210
|
+
markdown += `**Total:** ${totalAll}`;
|
|
211
|
+
if (totalAll > 0) {
|
|
212
|
+
const parts = [];
|
|
213
|
+
if (counts.breakpoints > 0)
|
|
214
|
+
parts.push(`${counts.breakpoints} line`);
|
|
215
|
+
if (counts.logpoints > 0)
|
|
216
|
+
parts.push(`${counts.logpoints} logpoint`);
|
|
217
|
+
if (domBreakpoints.length > 0)
|
|
218
|
+
parts.push(`${domBreakpoints.length} DOM`);
|
|
219
|
+
if (eventBreakpoints.length > 0)
|
|
220
|
+
parts.push(`${eventBreakpoints.length} event`);
|
|
221
|
+
if (xhrBreakpoints.length > 0)
|
|
222
|
+
parts.push(`${xhrBreakpoints.length} XHR`);
|
|
223
|
+
markdown += ` (${parts.join(', ')}`;
|
|
224
|
+
if (pendingCount > 0) {
|
|
225
|
+
markdown += `, ${pendingCount} pending`;
|
|
226
|
+
}
|
|
227
|
+
markdown += `)`;
|
|
228
|
+
}
|
|
229
|
+
markdown += `\n\n`;
|
|
230
|
+
if (totalAll === 0) {
|
|
231
|
+
markdown += 'No active breakpoints.\n\n';
|
|
232
|
+
markdown += '**TIP:** Use `breakpoint({ action: \'set\' })` to set a line breakpoint, `breakpoint({ action: \'setDOMBreakpoint\' })` for DOM changes, or `breakpoint({ action: \'setEventBreakpoint\' })` for events.';
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
markdown += '| ID | Type | Status | Details |\n';
|
|
236
|
+
markdown += '|---|---|---|---|\n';
|
|
237
|
+
// Line breakpoints and logpoints
|
|
238
|
+
breakpoints.forEach(bp => {
|
|
239
|
+
const type = bp.isLogpoint ? 'logpoint' : 'line';
|
|
240
|
+
const status = bp.status === 'pending' ? '⏳ pending' : '✓ resolved';
|
|
241
|
+
let location;
|
|
242
|
+
if (bp.originalLocation) {
|
|
243
|
+
location = `${bp.originalLocation.url}:${bp.originalLocation.lineNumber}${bp.originalLocation.columnNumber !== undefined ? `:${bp.originalLocation.columnNumber}` : ''}`;
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
location = `scriptId:${bp.location.scriptId}:${bp.location.lineNumber + 1}${bp.location.columnNumber !== undefined ? `:${bp.location.columnNumber + 1}` : ''}`;
|
|
247
|
+
}
|
|
248
|
+
markdown += `| \`${bp.breakpointId}\` | ${type} | ${status} | \`${location}\` |\n`;
|
|
249
|
+
});
|
|
250
|
+
// DOM breakpoints
|
|
251
|
+
domBreakpoints.forEach(bp => {
|
|
252
|
+
markdown += `| \`${bp.breakpointId}\` | DOM | ✓ active | \`${bp.selector}\` (${bp.domBreakpointType}) |\n`;
|
|
253
|
+
});
|
|
254
|
+
// Event breakpoints
|
|
255
|
+
eventBreakpoints.forEach(bp => {
|
|
256
|
+
const target = bp.targetName ? ` on ${bp.targetName}` : '';
|
|
257
|
+
markdown += `| \`${bp.breakpointId}\` | event | ✓ active | ${bp.eventName}${target} |\n`;
|
|
258
|
+
});
|
|
259
|
+
// XHR breakpoints
|
|
260
|
+
xhrBreakpoints.forEach(bp => {
|
|
261
|
+
markdown += `| \`${bp.breakpointId}\` | XHR | ✓ active | URL contains \`${bp.urlPattern}\` |\n`;
|
|
262
|
+
});
|
|
263
|
+
if (pendingCount > 0) {
|
|
264
|
+
markdown += `\n**Note:** Pending breakpoints will activate when their scripts load.`;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return {
|
|
268
|
+
content: [
|
|
269
|
+
{
|
|
270
|
+
type: 'text',
|
|
271
|
+
text: markdown,
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
case 'resetCounter': {
|
|
277
|
+
if (!args.breakpointId) {
|
|
278
|
+
return createErrorResponse('INVALID_PARAMS', { message: 'breakpointId is required for resetCounter action' });
|
|
279
|
+
}
|
|
280
|
+
// Reset the counter in the tracker
|
|
281
|
+
if (!logpointTracker) {
|
|
282
|
+
return createErrorResponse('DEBUGGER_NOT_CONNECTED');
|
|
283
|
+
}
|
|
284
|
+
const metadata = logpointTracker.getLogpoint(args.breakpointId);
|
|
285
|
+
if (!metadata) {
|
|
286
|
+
return createErrorResponse('BREAKPOINT_NOT_FOUND', { breakpointId: args.breakpointId });
|
|
287
|
+
}
|
|
288
|
+
// Reset the counter in the tracker
|
|
289
|
+
const previousCount = metadata.executionCount;
|
|
290
|
+
logpointTracker.resetCounter(args.breakpointId);
|
|
291
|
+
// Reset the global counter in the page context
|
|
292
|
+
const logpointKey = `${metadata.url}:${metadata.lineNumber}`;
|
|
293
|
+
try {
|
|
294
|
+
await targetCdpManager.evaluateExpression(`
|
|
295
|
+
if (typeof globalThis.__llmCdpLogpointCounters !== 'undefined') {
|
|
296
|
+
globalThis.__llmCdpLogpointCounters['${logpointKey.replace(/'/g, "\\'")}'] = 0;
|
|
297
|
+
}
|
|
298
|
+
`);
|
|
299
|
+
}
|
|
300
|
+
catch (error) {
|
|
301
|
+
// Ignore errors - counter may not exist yet
|
|
302
|
+
}
|
|
303
|
+
// Clear the logpoint limit exceeded state in CDPManager
|
|
304
|
+
targetCdpManager.clearLogpointLimitExceeded();
|
|
305
|
+
// Build markdown response with details
|
|
306
|
+
let markdown = getErrorMessage('LOGPOINT_COUNTER_RESET', {
|
|
307
|
+
breakpointId: args.breakpointId,
|
|
308
|
+
maxExecutions: metadata.maxExecutions,
|
|
309
|
+
});
|
|
310
|
+
markdown += `\n\n**Logpoint Details:**\n`;
|
|
311
|
+
markdown += `- **Location:** \`${metadata.url}:${metadata.lineNumber}\`\n`;
|
|
312
|
+
markdown += `- **Log Message:** \`${metadata.logMessage}\`\n`;
|
|
313
|
+
markdown += `- **Previous Count:** ${previousCount}\n`;
|
|
314
|
+
markdown += `- **New Count:** 0\n`;
|
|
315
|
+
markdown += `\n**Next Step:** Use \`execution({ action: 'resume' })\` to continue execution.`;
|
|
316
|
+
return {
|
|
317
|
+
content: [
|
|
318
|
+
{
|
|
319
|
+
type: 'text',
|
|
320
|
+
text: markdown,
|
|
321
|
+
},
|
|
322
|
+
],
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
case 'validate': {
|
|
326
|
+
if (!args.url || args.lineNumber === undefined || !args.logMessage) {
|
|
327
|
+
return createErrorResponse('INVALID_PARAMS', { message: 'url, lineNumber, and logMessage are required for validate action' });
|
|
328
|
+
}
|
|
329
|
+
const timeout = args.timeout || 2000;
|
|
330
|
+
// Parse logMessage to extract expressions
|
|
331
|
+
const expressionMatches = args.logMessage.matchAll(/\{([^}]+)\}/g);
|
|
332
|
+
const expressions = [];
|
|
333
|
+
for (const match of expressionMatches) {
|
|
334
|
+
expressions.push(match[1]);
|
|
335
|
+
}
|
|
336
|
+
if (expressions.length === 0) {
|
|
337
|
+
const markdown = `## Logpoint Validation\n\n` +
|
|
338
|
+
`**Status:** Valid\n` +
|
|
339
|
+
`**Message:** No expressions to validate in log message\n` +
|
|
340
|
+
`**Log Message:** \`${args.logMessage}\`\n\n` +
|
|
341
|
+
`**Note:** This log message contains no variable interpolations. It will output as-is.`;
|
|
342
|
+
return {
|
|
343
|
+
content: [
|
|
344
|
+
{
|
|
345
|
+
type: 'text',
|
|
346
|
+
text: markdown,
|
|
347
|
+
},
|
|
348
|
+
],
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
// Set a temporary breakpoint to test the expressions
|
|
352
|
+
try {
|
|
353
|
+
const tempBreakpoint = await targetCdpManager.setBreakpoint(args.url, args.lineNumber, args.columnNumber);
|
|
354
|
+
// Get actual location from CDP (0-based)
|
|
355
|
+
const actualCdpLine = tempBreakpoint.location.lineNumber;
|
|
356
|
+
const actualCdpColumn = tempBreakpoint.location.columnNumber;
|
|
357
|
+
// Convert to 1-based for user display
|
|
358
|
+
const actualLineUser = actualCdpLine + 1;
|
|
359
|
+
const actualColumnUser = actualCdpColumn !== undefined ? actualCdpColumn + 1 : undefined;
|
|
360
|
+
// Check if location differs
|
|
361
|
+
const lineDiffers = actualLineUser !== args.lineNumber;
|
|
362
|
+
const columnDiffers = args.columnNumber !== undefined && actualColumnUser !== args.columnNumber;
|
|
363
|
+
const locationDiffers = lineDiffers || columnDiffers;
|
|
364
|
+
// Wait for the breakpoint to potentially be hit (configurable timeout)
|
|
365
|
+
await new Promise(resolve => setTimeout(resolve, timeout));
|
|
366
|
+
// Check if we're paused at the breakpoint
|
|
367
|
+
if (!targetCdpManager.isPaused()) {
|
|
368
|
+
// Remove temp breakpoint
|
|
369
|
+
await targetCdpManager.removeBreakpoint(tempBreakpoint.breakpointId);
|
|
370
|
+
let markdown = `## Logpoint Validation\n\n`;
|
|
371
|
+
markdown += `**Status:** Unknown\n`;
|
|
372
|
+
markdown += `**Message:** Unable to validate - code at this location has not been executed yet\n\n`;
|
|
373
|
+
markdown += `**Expressions:** ${expressions.map(e => `\`${e}\``).join(', ')}\n`;
|
|
374
|
+
markdown += `**Log Message:** \`${args.logMessage}\`\n\n`;
|
|
375
|
+
markdown += `**Location:**\n`;
|
|
376
|
+
markdown += `- **Requested:** Line ${args.lineNumber}${args.columnNumber ? `:${args.columnNumber}` : ''}\n`;
|
|
377
|
+
markdown += `- **Actual:** Line ${actualLineUser}${actualColumnUser ? `:${actualColumnUser}` : ''}\n`;
|
|
378
|
+
markdown += `- **Matched:** ${!locationDiffers ? 'Yes' : 'No'}\n\n`;
|
|
379
|
+
if (locationDiffers) {
|
|
380
|
+
markdown += `**Warning:** CDP mapped your requested location ${args.lineNumber}:${args.columnNumber || 'auto'} to ${actualLineUser}:${actualColumnUser || 'auto'}\n\n`;
|
|
381
|
+
}
|
|
382
|
+
markdown += `**Suggestion:** Trigger the code path that contains this line, or set the logpoint and check console for errors`;
|
|
383
|
+
return {
|
|
384
|
+
content: [
|
|
385
|
+
{
|
|
386
|
+
type: 'text',
|
|
387
|
+
text: markdown,
|
|
388
|
+
},
|
|
389
|
+
],
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
// Try to evaluate each expression and collect available variables
|
|
393
|
+
const results = [];
|
|
394
|
+
let availableVariables = [];
|
|
395
|
+
const callFrame = targetCdpManager.getCallStack()?.[0];
|
|
396
|
+
if (callFrame) {
|
|
397
|
+
// Get available variables at this location
|
|
398
|
+
try {
|
|
399
|
+
const result = await targetCdpManager.getVariables(callFrame.callFrameId, false);
|
|
400
|
+
// Extract variable names from grouped data structure
|
|
401
|
+
const { data, responseType } = result;
|
|
402
|
+
if (responseType === 'full' || responseType === 'depth_reduced') {
|
|
403
|
+
// data is Record<string, {name, value, type}[]>
|
|
404
|
+
for (const vars of Object.values(data)) {
|
|
405
|
+
for (const v of vars) {
|
|
406
|
+
availableVariables.push(v.name);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
else if (responseType === 'names_only') {
|
|
411
|
+
// data is Record<string, string[]>
|
|
412
|
+
for (const names of Object.values(data)) {
|
|
413
|
+
availableVariables.push(...names);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
// For counts_only, we don't have names to suggest
|
|
417
|
+
}
|
|
418
|
+
catch (err) {
|
|
419
|
+
// Ignore errors getting variables
|
|
420
|
+
}
|
|
421
|
+
// Evaluate each expression
|
|
422
|
+
for (const expr of expressions) {
|
|
423
|
+
try {
|
|
424
|
+
const value = await targetCdpManager.evaluateExpression(expr, callFrame.callFrameId);
|
|
425
|
+
results.push({
|
|
426
|
+
expression: expr,
|
|
427
|
+
valid: true,
|
|
428
|
+
value,
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
catch (error) {
|
|
432
|
+
results.push({
|
|
433
|
+
expression: expr,
|
|
434
|
+
valid: false,
|
|
435
|
+
error: String(error),
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
// No call frame available
|
|
442
|
+
for (const expr of expressions) {
|
|
443
|
+
results.push({
|
|
444
|
+
expression: expr,
|
|
445
|
+
valid: false,
|
|
446
|
+
error: 'No call frame available',
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
// Resume execution
|
|
451
|
+
await targetCdpManager.resume();
|
|
452
|
+
// Remove temp breakpoint
|
|
453
|
+
await targetCdpManager.removeBreakpoint(tempBreakpoint.breakpointId);
|
|
454
|
+
const allValid = results.every(r => r.valid);
|
|
455
|
+
const invalidExpressions = results.filter(r => !r.valid);
|
|
456
|
+
// Get code snippet (3 lines context around actual location)
|
|
457
|
+
let codeContext;
|
|
458
|
+
try {
|
|
459
|
+
const startLine = Math.max(1, actualLineUser - 1);
|
|
460
|
+
const endLine = actualLineUser + 1;
|
|
461
|
+
const sourceResult = await targetCdpManager.getSourceCode(args.url, startLine, endLine);
|
|
462
|
+
codeContext = sourceResult.code;
|
|
463
|
+
}
|
|
464
|
+
catch (err) {
|
|
465
|
+
// Ignore errors getting code snippet
|
|
466
|
+
}
|
|
467
|
+
// Build markdown response
|
|
468
|
+
let markdown = `## Logpoint Validation\n\n`;
|
|
469
|
+
markdown += `**Status:** ${allValid ? 'Valid ✓' : 'Failed ✗'}\n`;
|
|
470
|
+
markdown += `**Message:** ${allValid ? 'All expressions are valid at this location' : `${invalidExpressions.length} expression(s) failed to evaluate`}\n\n`;
|
|
471
|
+
// Location info
|
|
472
|
+
markdown += `**Location:**\n`;
|
|
473
|
+
markdown += `- **Requested:** Line ${args.lineNumber}${args.columnNumber ? `:${args.columnNumber}` : ''}\n`;
|
|
474
|
+
markdown += `- **Actual:** Line ${actualLineUser}${actualColumnUser ? `:${actualColumnUser}` : ''}\n`;
|
|
475
|
+
markdown += `- **Matched:** ${!locationDiffers ? 'Yes' : 'No'}\n\n`;
|
|
476
|
+
if (locationDiffers) {
|
|
477
|
+
markdown += `**Warning:** CDP mapped your requested location ${args.lineNumber}:${args.columnNumber || 'auto'} to ${actualLineUser}:${actualColumnUser || 'auto'}\n\n`;
|
|
478
|
+
}
|
|
479
|
+
// Expression results
|
|
480
|
+
markdown += `**Expression Results:**\n\n`;
|
|
481
|
+
markdown += `| Expression | Valid | Value/Error |\n`;
|
|
482
|
+
markdown += `|---|---|---|\n`;
|
|
483
|
+
results.forEach(r => {
|
|
484
|
+
const status = r.valid ? '✓' : '✗';
|
|
485
|
+
const valueStr = r.valid ? JSON.stringify(r.value) : r.error;
|
|
486
|
+
markdown += `| \`${r.expression}\` | ${status} | ${valueStr} |\n`;
|
|
487
|
+
});
|
|
488
|
+
markdown += `\n`;
|
|
489
|
+
// Available variables
|
|
490
|
+
if (availableVariables.length > 0) {
|
|
491
|
+
markdown += `**Available Variables:** ${availableVariables.map(v => `\`${v}\``).join(', ')}\n\n`;
|
|
492
|
+
}
|
|
493
|
+
// Code context
|
|
494
|
+
if (codeContext) {
|
|
495
|
+
markdown += `**Code Context:**\n\`\`\`javascript\n${codeContext}\n\`\`\`\n\n`;
|
|
496
|
+
}
|
|
497
|
+
// If validation failed, search for better locations
|
|
498
|
+
if (!allValid) {
|
|
499
|
+
try {
|
|
500
|
+
const suggestions = await targetCdpManager.findBestLogpointLocation(args.url, args.lineNumber, args.columnNumber, expressions, 2, // searchRadius ±2 lines
|
|
501
|
+
1000 // 1 second timeout per candidate
|
|
502
|
+
);
|
|
503
|
+
if (suggestions.length > 0) {
|
|
504
|
+
markdown += `**Suggestions:**\n`;
|
|
505
|
+
suggestions.slice(0, 3).forEach((s) => {
|
|
506
|
+
markdown += `- Line ${s.line}${s.column ? `:${s.column}` : ''} - ${s.score}% of expressions valid\n`;
|
|
507
|
+
});
|
|
508
|
+
markdown += `\n**Recommendation:** Consider using line ${suggestions[0].line} where ${suggestions[0].score}% of expressions are valid`;
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
markdown += `**Suggestion:** Check variable names and scopes. Variables must be in scope at the logpoint location.`;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
catch (err) {
|
|
515
|
+
markdown += `**Suggestion:** Check variable names and scopes. Variables must be in scope at the logpoint location.`;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return {
|
|
519
|
+
content: [
|
|
520
|
+
{
|
|
521
|
+
type: 'text',
|
|
522
|
+
text: markdown,
|
|
523
|
+
},
|
|
524
|
+
],
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
catch (error) {
|
|
528
|
+
return createErrorResponse('LOGPOINT_VALIDATE_FAILED', {
|
|
529
|
+
error: String(error),
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
case 'setLogpoint': {
|
|
534
|
+
if (!args.url || args.lineNumber === undefined || !args.logMessage) {
|
|
535
|
+
return createErrorResponse('INVALID_PARAMS', { message: 'url, lineNumber, and logMessage are required for setLogpoint action' });
|
|
536
|
+
}
|
|
537
|
+
const includeCallStack = args.includeCallStack || false;
|
|
538
|
+
const includeVariables = args.includeVariables || false;
|
|
539
|
+
const maxExecutions = args.maxExecutions || 20;
|
|
540
|
+
// Resolve source maps for TypeScript files
|
|
541
|
+
const resolved = await resolveBreakpointLocation(args.url, args.lineNumber, args.columnNumber, targetCdpManager, sourceMapHandler);
|
|
542
|
+
const targetUrl = resolved.url;
|
|
543
|
+
const targetLine = resolved.line;
|
|
544
|
+
const targetColumn = resolved.column;
|
|
545
|
+
// Parse logMessage to extract expressions in {}
|
|
546
|
+
const expressionMatches = args.logMessage.matchAll(/\{([^}]+)\}/g);
|
|
547
|
+
const expressions = [];
|
|
548
|
+
for (const match of expressionMatches) {
|
|
549
|
+
expressions.push(match[1]);
|
|
550
|
+
}
|
|
551
|
+
// Build the log expression in Chrome DevTools' own native logpoint format:
|
|
552
|
+
// a `console.log(...)` call immediately preceded by the exact
|
|
553
|
+
// `/** DEVTOOLS_LOGPOINT */` sentinel comment, with a trailing
|
|
554
|
+
// `//# sourceURL=debugger://logpoint` marker - verified against
|
|
555
|
+
// devtools-frontend's BreakpointManager.ts (LOGPOINT_PREFIX/LOGPOINT_SUFFIX
|
|
556
|
+
// + addSourceUrl()). This is exactly how Chrome's Sources panel implements
|
|
557
|
+
// logpoints, so the resulting console output is attributed/broadcast the
|
|
558
|
+
// same way for any DevTools/CDP client attached to the page, not just this
|
|
559
|
+
// tool's own connection.
|
|
560
|
+
const logpointKey = `${targetUrl}:${targetLine}`;
|
|
561
|
+
const escapedKey = logpointKey.replace(/'/g, "\\'");
|
|
562
|
+
// Computes the formatted log message; always returns a string so the
|
|
563
|
+
// outer console.log() call fires exactly once per (allowed) hit.
|
|
564
|
+
const messageExpression = `(function() {
|
|
565
|
+
try {
|
|
566
|
+
const values = {};
|
|
567
|
+
${expressions.map(expr => {
|
|
568
|
+
const escapedExpr = expr.replace(/'/g, "\\'");
|
|
569
|
+
return `
|
|
570
|
+
try {
|
|
571
|
+
values['${escapedExpr}'] = ${expr};
|
|
572
|
+
} catch (e) {
|
|
573
|
+
values['${escapedExpr}'] = '[Error: ' + e.message + ']';
|
|
574
|
+
}`;
|
|
575
|
+
}).join('')}
|
|
576
|
+
|
|
577
|
+
const safeStringify = (value) => {
|
|
578
|
+
if (value === null) return 'null';
|
|
579
|
+
if (value === undefined) return 'undefined';
|
|
580
|
+
if (typeof value === 'string') return value;
|
|
581
|
+
if (typeof value === 'number' || typeof value === 'boolean') return String(value);
|
|
582
|
+
try {
|
|
583
|
+
return JSON.stringify(value, null, 2);
|
|
584
|
+
} catch (e) {
|
|
585
|
+
return String(value);
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
let message = ${JSON.stringify(args.logMessage)};
|
|
590
|
+
${expressions.map(expr => {
|
|
591
|
+
const escapedExpr = expr.replace(/'/g, "\\'");
|
|
592
|
+
return `message = message.replace('{${expr}}', safeStringify(values['${escapedExpr}']));`;
|
|
593
|
+
}).join('\n ')}
|
|
594
|
+
|
|
595
|
+
${includeCallStack ? `
|
|
596
|
+
message += '\\n Call stack: ' + new Error().stack.split('\\n').slice(2, 5).join(' | ');
|
|
597
|
+
` : ''}
|
|
598
|
+
${includeVariables ? `
|
|
599
|
+
message += '\\n Variables: ' + safeStringify(values);
|
|
600
|
+
` : ''}
|
|
601
|
+
|
|
602
|
+
if (globalThis.__llmCdpLogpointCounters['${escapedKey}'] === ${maxExecutions}) {
|
|
603
|
+
message += '\\n [Logpoint] Execution limit reached (${maxExecutions}/${maxExecutions}). Further hits will not be logged.';
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
return message;
|
|
607
|
+
} catch (e) {
|
|
608
|
+
if (typeof globalThis.__llmCdpLogpointErrors === 'undefined') {
|
|
609
|
+
globalThis.__llmCdpLogpointErrors = [];
|
|
610
|
+
}
|
|
611
|
+
globalThis.__llmCdpLogpointErrors.push({
|
|
612
|
+
type: 'logpoint-error',
|
|
613
|
+
location: '${targetUrl}:${targetLine}:${targetColumn || 'auto'}',
|
|
614
|
+
expressions: ${JSON.stringify(expressions)},
|
|
615
|
+
error: e.message,
|
|
616
|
+
stack: e.stack || e.toString(),
|
|
617
|
+
timestamp: new Date().toISOString()
|
|
618
|
+
});
|
|
619
|
+
if (globalThis.__llmCdpLogpointErrors.length > 50) {
|
|
620
|
+
globalThis.__llmCdpLogpointErrors.shift();
|
|
621
|
+
}
|
|
622
|
+
return '[Logpoint Error] ' + e.message;
|
|
623
|
+
}
|
|
624
|
+
})()`;
|
|
625
|
+
// Counting/capping happens in a guard evaluated before the real
|
|
626
|
+
// console.log call, so we stop logging once maxExecutions is hit
|
|
627
|
+
// instead of spamming the console forever. The Node-side
|
|
628
|
+
// LogpointExecutionTracker (see logpoint-execution-tracker.ts) still
|
|
629
|
+
// detects the maxExecutions-th message and pauses the debuggee as a
|
|
630
|
+
// safety net - this condition itself never returns true anymore,
|
|
631
|
+
// since its overall value must stay falsy to match the native
|
|
632
|
+
// never-pause logpoint semantics above.
|
|
633
|
+
let logExpression = `(function() {
|
|
634
|
+
if (typeof globalThis.__llmCdpLogpointCounters === 'undefined') {
|
|
635
|
+
globalThis.__llmCdpLogpointCounters = {};
|
|
636
|
+
}
|
|
637
|
+
const key = '${escapedKey}';
|
|
638
|
+
globalThis.__llmCdpLogpointCounters[key] = (globalThis.__llmCdpLogpointCounters[key] || 0) + 1;
|
|
639
|
+
return globalThis.__llmCdpLogpointCounters[key] <= ${maxExecutions};
|
|
640
|
+
})() && (/** DEVTOOLS_LOGPOINT */ console.log('[Logpoint] ${targetUrl}:${targetLine}:${targetColumn || 'auto'}:', ${messageExpression}))
|
|
641
|
+
|
|
642
|
+
//# sourceURL=debugger://logpoint`;
|
|
643
|
+
// If a user condition is provided, only log/count when it's true
|
|
644
|
+
if (args.condition) {
|
|
645
|
+
logExpression = `(${args.condition}) && ${logExpression}`;
|
|
646
|
+
}
|
|
647
|
+
// Use targetCdpManager.setBreakpoint to ensure proper state management
|
|
648
|
+
// This ensures state.breakpoints Map is updated immediately
|
|
649
|
+
let breakpoint;
|
|
650
|
+
try {
|
|
651
|
+
breakpoint = await targetCdpManager.setBreakpoint(targetUrl, targetLine, // targetCdpManager.setBreakpoint expects 1-based numbers
|
|
652
|
+
targetColumn, logExpression);
|
|
653
|
+
}
|
|
654
|
+
catch (error) {
|
|
655
|
+
let markdown = `## Failed to Set Logpoint\n\n`;
|
|
656
|
+
markdown += `**Error:** ${error.message}\n\n`;
|
|
657
|
+
markdown += `**Details:**\n`;
|
|
658
|
+
markdown += `- **Requested:** \`${args.url}:${args.lineNumber}\`\n`;
|
|
659
|
+
markdown += `- **Target:** \`${targetUrl}:${targetLine}\`\n\n`;
|
|
660
|
+
if (error.message.includes('not loaded')) {
|
|
661
|
+
markdown += `**Suggestion:** The script has not been loaded by the runtime yet. Try navigating to the page or reloading.`;
|
|
662
|
+
}
|
|
663
|
+
else {
|
|
664
|
+
markdown += `**Suggestion:** Verify the file has been loaded and the line number is valid.`;
|
|
665
|
+
}
|
|
666
|
+
return {
|
|
667
|
+
content: [{
|
|
668
|
+
type: 'text',
|
|
669
|
+
text: markdown,
|
|
670
|
+
}],
|
|
671
|
+
isError: true
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
// Mark as logpoint in the breakpoint info (state is already updated by setBreakpoint)
|
|
675
|
+
breakpoint.isLogpoint = true;
|
|
676
|
+
cdpManager.state.breakpoints.set(breakpoint.breakpointId, breakpoint);
|
|
677
|
+
// AUTOMATIC LINE/COLUMN MAPPING VALIDATION
|
|
678
|
+
// Get actual location from CDP (0-based)
|
|
679
|
+
const actualCdpLine = breakpoint.location.lineNumber;
|
|
680
|
+
const actualCdpColumn = breakpoint.location.columnNumber;
|
|
681
|
+
// Convert to 1-based for comparison with user input
|
|
682
|
+
const actualLineUser = actualCdpLine + 1;
|
|
683
|
+
const actualColumnUser = actualCdpColumn !== undefined ? actualCdpColumn + 1 : undefined;
|
|
684
|
+
// Check if location differs from what user requested
|
|
685
|
+
const lineDiffers = actualLineUser !== targetLine;
|
|
686
|
+
const columnDiffers = targetColumn !== undefined && actualColumnUser !== targetColumn;
|
|
687
|
+
const locationDiffers = lineDiffers || columnDiffers;
|
|
688
|
+
// If location differs AND we have expressions to validate
|
|
689
|
+
if (locationDiffers && expressions.length > 0) {
|
|
690
|
+
// Validate expressions at actual location
|
|
691
|
+
const validation = await targetCdpManager.validateLogpointAtActualLocation(targetUrl, actualLineUser, // 1-based
|
|
692
|
+
actualColumnUser, // 1-based
|
|
693
|
+
expressions, 2000 // 2 second timeout
|
|
694
|
+
);
|
|
695
|
+
// If validation failed (expressions not valid at actual location)
|
|
696
|
+
if (!validation.allValid) {
|
|
697
|
+
// Remove the breakpoint - don't keep a broken logpoint
|
|
698
|
+
// Unregister from tracker first
|
|
699
|
+
if (logpointTracker) {
|
|
700
|
+
logpointTracker.unregisterLogpoint(breakpoint.breakpointId);
|
|
701
|
+
}
|
|
702
|
+
try {
|
|
703
|
+
await targetCdpManager.removeBreakpoint(breakpoint.breakpointId);
|
|
704
|
+
}
|
|
705
|
+
catch (removeError) {
|
|
706
|
+
// Log but continue - state might already be cleaned up
|
|
707
|
+
console.error(`[cdp-tools] Warning: Failed to remove invalid logpoint: ${removeError.message}`);
|
|
708
|
+
}
|
|
709
|
+
// Get code snippet at actual location (3 lines context)
|
|
710
|
+
let codeContext = '';
|
|
711
|
+
try {
|
|
712
|
+
const sourceCode = await targetCdpManager.getSourceCode(targetUrl, Math.max(1, actualLineUser - 1), // 1 line before
|
|
713
|
+
actualLineUser + 1 // 1 line after
|
|
714
|
+
);
|
|
715
|
+
codeContext = sourceCode.code;
|
|
716
|
+
}
|
|
717
|
+
catch (e) {
|
|
718
|
+
codeContext = '(Could not fetch source code)';
|
|
719
|
+
}
|
|
720
|
+
// Search for better locations
|
|
721
|
+
let suggestions = [];
|
|
722
|
+
try {
|
|
723
|
+
suggestions = await targetCdpManager.findBestLogpointLocation(targetUrl, args.lineNumber, args.columnNumber, expressions, 2, // Search ±2 lines
|
|
724
|
+
1000 // 1 second timeout per candidate
|
|
725
|
+
);
|
|
726
|
+
}
|
|
727
|
+
catch (e) {
|
|
728
|
+
// If search fails, provide a simple suggestion
|
|
729
|
+
suggestions = [{
|
|
730
|
+
line: actualLineUser - 1,
|
|
731
|
+
reason: 'Try the line before where variables might be in scope',
|
|
732
|
+
note: 'Use validateLogpoint first to test expressions'
|
|
733
|
+
}];
|
|
734
|
+
}
|
|
735
|
+
// Return detailed error response
|
|
736
|
+
let errorMarkdown = `## Logpoint Validation Failed\n\n`;
|
|
737
|
+
errorMarkdown += `**Error:** Logpoint expressions failed validation at actual CDP location\n\n`;
|
|
738
|
+
errorMarkdown += `**Requested Location:**\n`;
|
|
739
|
+
errorMarkdown += `- **URL:** \`${args.url}\`\n`;
|
|
740
|
+
errorMarkdown += `- **Line:** ${args.lineNumber}${args.columnNumber ? `:${args.columnNumber}` : ''}\n\n`;
|
|
741
|
+
errorMarkdown += `**Actual Location (CDP Mapped):**\n`;
|
|
742
|
+
errorMarkdown += `- **Line:** ${actualLineUser}${actualColumnUser ? `:${actualColumnUser}` : ''}\n`;
|
|
743
|
+
errorMarkdown += `- **Offset:** ${actualLineUser - args.lineNumber > 0 ? '+' : ''}${actualLineUser - args.lineNumber} lines\n`;
|
|
744
|
+
errorMarkdown += `- **Reason:** V8 mapped to nearest valid breakpoint location\n\n`;
|
|
745
|
+
const failedExprs = validation.results.filter((r) => !r.valid).map((r) => r.expression);
|
|
746
|
+
errorMarkdown += `**Failed Expressions:** ${failedExprs.map((e) => `\`${e}\``).join(', ')}\n\n`;
|
|
747
|
+
errorMarkdown += `**Code Context:**\n\`\`\`javascript\n${codeContext}\n\`\`\`\n\n`;
|
|
748
|
+
if (validation.availableVariables && validation.availableVariables.length > 0) {
|
|
749
|
+
errorMarkdown += `**Available Variables:** ${validation.availableVariables.map((v) => `\`${v}\``).join(', ')}\n\n`;
|
|
750
|
+
}
|
|
751
|
+
if (suggestions.length > 0) {
|
|
752
|
+
errorMarkdown += `**Suggestions:**\n`;
|
|
753
|
+
suggestions.forEach((s) => {
|
|
754
|
+
errorMarkdown += `- Line ${s.line}${s.column ? `:${s.column}` : ''} - ${s.score}% of expressions valid\n`;
|
|
755
|
+
});
|
|
756
|
+
errorMarkdown += `\n`;
|
|
757
|
+
if (suggestions[0].score === 100) {
|
|
758
|
+
errorMarkdown += `**Recommendation:** Set logpoint at line ${suggestions[0].line}:${suggestions[0].column || 'auto'} instead where all expressions are in scope.`;
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
errorMarkdown += `**Recommendation:** Variables not in scope at actual location ${actualLineUser}:${actualColumnUser || 'auto'}. Try using validateLogpoint to find a better location.`;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
else {
|
|
765
|
+
errorMarkdown += `**Recommendation:** Variables not in scope at actual location ${actualLineUser}:${actualColumnUser || 'auto'}. Try using validateLogpoint to find a better location.`;
|
|
766
|
+
}
|
|
767
|
+
return {
|
|
768
|
+
content: [{
|
|
769
|
+
type: 'text',
|
|
770
|
+
text: errorMarkdown,
|
|
771
|
+
}],
|
|
772
|
+
isError: true
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
// Validation passed but location differs - will show warning in success response below
|
|
776
|
+
}
|
|
777
|
+
// Register with logpoint execution tracker
|
|
778
|
+
if (logpointTracker) {
|
|
779
|
+
logpointTracker.registerLogpoint(breakpoint.breakpointId, targetUrl, actualLineUser, // Use the actual line where it was set (1-based)
|
|
780
|
+
args.logMessage, maxExecutions);
|
|
781
|
+
}
|
|
782
|
+
// Inject console notification at resolved location
|
|
783
|
+
await targetCdpManager.injectConsoleLink(targetUrl, actualLineUser, '📝 Logpoint set at');
|
|
784
|
+
// Parse expressions to include in the response
|
|
785
|
+
const expressionMatchesForResponse = args.logMessage.matchAll(/\{([^}]+)\}/g);
|
|
786
|
+
const expressionsForResponse = [];
|
|
787
|
+
for (const match of expressionMatchesForResponse) {
|
|
788
|
+
expressionsForResponse.push(match[1]);
|
|
789
|
+
}
|
|
790
|
+
// Build markdown success response
|
|
791
|
+
let markdown = `## Logpoint Set Successfully\n\n`;
|
|
792
|
+
markdown += `**Breakpoint ID:** \`${breakpoint.breakpointId}\`\n`;
|
|
793
|
+
markdown += `**Location:** \`${targetUrl}:${actualLineUser}${actualColumnUser ? `:${actualColumnUser}` : ''}\`\n`;
|
|
794
|
+
markdown += `**Log Message:** \`${args.logMessage}\`\n`;
|
|
795
|
+
if (expressionsForResponse.length > 0) {
|
|
796
|
+
markdown += `**Expressions:** ${expressionsForResponse.map(e => `\`${e}\``).join(', ')}\n`;
|
|
797
|
+
}
|
|
798
|
+
if (args.condition) {
|
|
799
|
+
markdown += `**Condition:** \`${args.condition}\`\n`;
|
|
800
|
+
}
|
|
801
|
+
markdown += `**Max Executions:** ${maxExecutions}\n\n`;
|
|
802
|
+
// If location differs, add warning and validation info
|
|
803
|
+
if (locationDiffers && expressions.length > 0) {
|
|
804
|
+
markdown += `**⚠️ Warning:** Logpoint was set at line ${actualLineUser}:${actualColumnUser || 'auto'} (not ${args.lineNumber}:${args.columnNumber || 'auto'}) due to V8 line mapping. All expressions validated successfully at this location.\n\n`;
|
|
805
|
+
}
|
|
806
|
+
else if (locationDiffers) {
|
|
807
|
+
markdown += `**⚠️ Note:** CDP mapped your requested location ${args.lineNumber}:${args.columnNumber || 'auto'} to ${actualLineUser}:${actualColumnUser || 'auto'}\n\n`;
|
|
808
|
+
}
|
|
809
|
+
markdown += `**Note:** This logpoint will log to the browser console without pausing execution`;
|
|
810
|
+
if (expressionsForResponse.length > 0) {
|
|
811
|
+
markdown += `\n\n**TIP:** Each expression is wrapped in try-catch. If an expression fails, it will show \`[Error: message]\` in the log.`;
|
|
812
|
+
markdown += `\nTo see logpoint errors, use: \`console({ action: 'search', pattern: "Logpoint Error" })\``;
|
|
813
|
+
}
|
|
814
|
+
return {
|
|
815
|
+
content: [
|
|
816
|
+
{
|
|
817
|
+
type: 'text',
|
|
818
|
+
text: markdown,
|
|
819
|
+
},
|
|
820
|
+
],
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
case 'waitForScript': {
|
|
824
|
+
if (!args.url) {
|
|
825
|
+
return createErrorResponse('INVALID_PARAMS', { message: 'url is required for waitForScript action' });
|
|
826
|
+
}
|
|
827
|
+
const timeout = args.timeout || 10000;
|
|
828
|
+
// Check connection
|
|
829
|
+
if (!targetCdpManager.isConnected()) {
|
|
830
|
+
return createErrorResponse('DEBUGGER_NOT_CONNECTED');
|
|
831
|
+
}
|
|
832
|
+
try {
|
|
833
|
+
// First check if script is already loaded
|
|
834
|
+
const existingScript = targetCdpManager.findLoadedScript(args.url);
|
|
835
|
+
if (existingScript) {
|
|
836
|
+
let markdown = `## Script Already Loaded\n\n`;
|
|
837
|
+
markdown += `**Pattern:** \`${args.url}\`\n`;
|
|
838
|
+
markdown += `**Matched URL:** \`${existingScript}\`\n`;
|
|
839
|
+
markdown += `**Status:** ✓ Script is already loaded\n\n`;
|
|
840
|
+
markdown += `**Next Step:** You can now set breakpoints on this script using \`breakpoint({ action: 'set', url: '${existingScript}', lineNumber: <line> })\``;
|
|
841
|
+
return {
|
|
842
|
+
content: [{
|
|
843
|
+
type: 'text',
|
|
844
|
+
text: markdown,
|
|
845
|
+
}],
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
// Script not loaded, wait for it
|
|
849
|
+
const matchedUrl = await targetCdpManager.waitForScript(args.url, timeout);
|
|
850
|
+
let markdown = `## Script Loaded\n\n`;
|
|
851
|
+
markdown += `**Pattern:** \`${args.url}\`\n`;
|
|
852
|
+
markdown += `**Matched URL:** \`${matchedUrl}\`\n`;
|
|
853
|
+
markdown += `**Status:** ✓ Script loaded successfully\n\n`;
|
|
854
|
+
markdown += `**Next Step:** You can now set breakpoints on this script using \`breakpoint({ action: 'set', url: '${matchedUrl}', lineNumber: <line> })\``;
|
|
855
|
+
return {
|
|
856
|
+
content: [{
|
|
857
|
+
type: 'text',
|
|
858
|
+
text: markdown,
|
|
859
|
+
}],
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
catch (error) {
|
|
863
|
+
// Timeout or other error
|
|
864
|
+
let markdown = `## Script Wait Timeout\n\n`;
|
|
865
|
+
markdown += `**Pattern:** \`${args.url}\`\n`;
|
|
866
|
+
markdown += `**Timeout:** ${timeout}ms\n`;
|
|
867
|
+
markdown += `**Error:** ${error.message}\n\n`;
|
|
868
|
+
// List some loaded scripts as hints
|
|
869
|
+
const loadedScripts = targetCdpManager.getLoadedScripts();
|
|
870
|
+
if (loadedScripts.length > 0) {
|
|
871
|
+
markdown += `**Currently Loaded Scripts (first 10):**\n`;
|
|
872
|
+
loadedScripts.slice(0, 10).forEach(url => {
|
|
873
|
+
markdown += `- \`${url}\`\n`;
|
|
874
|
+
});
|
|
875
|
+
if (loadedScripts.length > 10) {
|
|
876
|
+
markdown += `- ... and ${loadedScripts.length - 10} more\n`;
|
|
877
|
+
}
|
|
878
|
+
markdown += `\n`;
|
|
879
|
+
}
|
|
880
|
+
markdown += `**Suggestions:**\n`;
|
|
881
|
+
markdown += `- Check that the URL pattern is correct\n`;
|
|
882
|
+
markdown += `- Use \`navigate({ action: 'goto' })\` to load the page containing the script\n`;
|
|
883
|
+
markdown += `- Increase the timeout if the script loads slowly`;
|
|
884
|
+
return {
|
|
885
|
+
content: [{
|
|
886
|
+
type: 'text',
|
|
887
|
+
text: markdown,
|
|
888
|
+
}],
|
|
889
|
+
isError: true,
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
case 'setDOMBreakpoint': {
|
|
894
|
+
if (!args.selector) {
|
|
895
|
+
return createErrorResponse('INVALID_PARAMS', { message: 'selector is required for setDOMBreakpoint action' });
|
|
896
|
+
}
|
|
897
|
+
if (!args.domBreakpointType) {
|
|
898
|
+
return createErrorResponse('INVALID_PARAMS', { message: 'domBreakpointType is required for setDOMBreakpoint action' });
|
|
899
|
+
}
|
|
900
|
+
// Check connection
|
|
901
|
+
if (!targetCdpManager.isConnected()) {
|
|
902
|
+
return createErrorResponse('DEBUGGER_NOT_CONNECTED');
|
|
903
|
+
}
|
|
904
|
+
try {
|
|
905
|
+
// Resolve selector to nodeId
|
|
906
|
+
const nodeId = await targetCdpManager.resolveSelector(args.selector);
|
|
907
|
+
// Set the DOM breakpoint
|
|
908
|
+
const breakpoint = await targetCdpManager.setDOMBreakpoint(nodeId, args.domBreakpointType, args.selector);
|
|
909
|
+
// Build success response
|
|
910
|
+
let markdown = `## DOM Breakpoint Set\n\n`;
|
|
911
|
+
markdown += `**Breakpoint ID:** \`${breakpoint.breakpointId}\`\n`;
|
|
912
|
+
markdown += `**Selector:** \`${args.selector}\`\n`;
|
|
913
|
+
markdown += `**Break on:** ${args.domBreakpointType}\n`;
|
|
914
|
+
markdown += `**Node ID:** ${nodeId}\n\n`;
|
|
915
|
+
const breakTypeDesc = {
|
|
916
|
+
'subtree-modified': 'Execution will pause when children of this element are added, removed, or modified.',
|
|
917
|
+
'attribute-modified': 'Execution will pause when any attribute of this element changes (class, style, data-*, etc.).',
|
|
918
|
+
'node-removed': 'Execution will pause when this element is removed from the DOM.',
|
|
919
|
+
};
|
|
920
|
+
markdown += `**Note:** ${breakTypeDesc[args.domBreakpointType]}\n\n`;
|
|
921
|
+
markdown += `**Warning:** DOM breakpoints use internal nodeIds which are invalidated on page reload. You'll need to re-set this breakpoint after navigating.`;
|
|
922
|
+
return {
|
|
923
|
+
content: [{
|
|
924
|
+
type: 'text',
|
|
925
|
+
text: markdown,
|
|
926
|
+
}],
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
catch (error) {
|
|
930
|
+
let markdown = `## Failed to Set DOM Breakpoint\n\n`;
|
|
931
|
+
markdown += `**Error:** ${error.message}\n\n`;
|
|
932
|
+
markdown += `**Selector:** \`${args.selector}\`\n`;
|
|
933
|
+
markdown += `**Break Type:** ${args.domBreakpointType}\n\n`;
|
|
934
|
+
if (error.message.includes('not found')) {
|
|
935
|
+
markdown += `**Suggestion:** The element was not found. Verify the selector matches an element currently in the DOM.`;
|
|
936
|
+
}
|
|
937
|
+
else {
|
|
938
|
+
markdown += `**Suggestion:** Ensure the page is loaded and the element exists in the DOM.`;
|
|
939
|
+
}
|
|
940
|
+
return {
|
|
941
|
+
content: [{
|
|
942
|
+
type: 'text',
|
|
943
|
+
text: markdown,
|
|
944
|
+
}],
|
|
945
|
+
isError: true,
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
case 'setEventBreakpoint': {
|
|
950
|
+
if (!args.eventName) {
|
|
951
|
+
return createErrorResponse('INVALID_PARAMS', { message: 'eventName is required for setEventBreakpoint action' });
|
|
952
|
+
}
|
|
953
|
+
// Check connection
|
|
954
|
+
if (!targetCdpManager.isConnected()) {
|
|
955
|
+
return createErrorResponse('DEBUGGER_NOT_CONNECTED');
|
|
956
|
+
}
|
|
957
|
+
try {
|
|
958
|
+
const breakpoint = await targetCdpManager.setEventListenerBreakpoint(args.eventName, args.targetName);
|
|
959
|
+
// Build success response
|
|
960
|
+
let markdown = `## Event Listener Breakpoint Set\n\n`;
|
|
961
|
+
markdown += `**Breakpoint ID:** \`${breakpoint.breakpointId}\`\n`;
|
|
962
|
+
markdown += `**Event:** \`${args.eventName}\`\n`;
|
|
963
|
+
if (args.targetName) {
|
|
964
|
+
markdown += `**Target:** ${args.targetName}\n`;
|
|
965
|
+
}
|
|
966
|
+
else {
|
|
967
|
+
markdown += `**Target:** All elements\n`;
|
|
968
|
+
}
|
|
969
|
+
markdown += `\n**Note:** Execution will pause whenever a \`${args.eventName}\` event is dispatched.\n\n`;
|
|
970
|
+
markdown += `**Common events:** click, submit, input, change, keydown, keyup, mousedown, touchstart, load, DOMContentLoaded`;
|
|
971
|
+
return {
|
|
972
|
+
content: [{
|
|
973
|
+
type: 'text',
|
|
974
|
+
text: markdown,
|
|
975
|
+
}],
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
catch (error) {
|
|
979
|
+
return {
|
|
980
|
+
content: [{
|
|
981
|
+
type: 'text',
|
|
982
|
+
text: `## Failed to Set Event Breakpoint\n\n**Error:** ${error.message}\n\n**Event:** \`${args.eventName}\``,
|
|
983
|
+
}],
|
|
984
|
+
isError: true,
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
case 'setXHRBreakpoint': {
|
|
989
|
+
if (!args.urlPattern) {
|
|
990
|
+
return createErrorResponse('INVALID_PARAMS', { message: 'urlPattern is required for setXHRBreakpoint action' });
|
|
991
|
+
}
|
|
992
|
+
// Check connection
|
|
993
|
+
if (!targetCdpManager.isConnected()) {
|
|
994
|
+
return createErrorResponse('DEBUGGER_NOT_CONNECTED');
|
|
995
|
+
}
|
|
996
|
+
try {
|
|
997
|
+
const breakpoint = await targetCdpManager.setXHRBreakpoint(args.urlPattern);
|
|
998
|
+
// Build success response
|
|
999
|
+
let markdown = `## XHR/Fetch Breakpoint Set\n\n`;
|
|
1000
|
+
markdown += `**Breakpoint ID:** \`${breakpoint.breakpointId}\`\n`;
|
|
1001
|
+
markdown += `**URL Pattern:** \`${args.urlPattern}\`\n\n`;
|
|
1002
|
+
markdown += `**Note:** Execution will pause when any XHR or Fetch request URL contains \`${args.urlPattern}\`.\n\n`;
|
|
1003
|
+
markdown += `**Examples:** If pattern is \`/api/users\`, it will match:\n`;
|
|
1004
|
+
markdown += `- \`https://example.com/api/users\`\n`;
|
|
1005
|
+
markdown += `- \`https://example.com/api/users/123\`\n`;
|
|
1006
|
+
markdown += `- \`/api/users?page=1\``;
|
|
1007
|
+
return {
|
|
1008
|
+
content: [{
|
|
1009
|
+
type: 'text',
|
|
1010
|
+
text: markdown,
|
|
1011
|
+
}],
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
catch (error) {
|
|
1015
|
+
return {
|
|
1016
|
+
content: [{
|
|
1017
|
+
type: 'text',
|
|
1018
|
+
text: `## Failed to Set XHR Breakpoint\n\n**Error:** ${error.message}\n\n**URL Pattern:** \`${args.urlPattern}\``,
|
|
1019
|
+
}],
|
|
1020
|
+
isError: true,
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
case 'await': {
|
|
1025
|
+
// Wait for any breakpoint to be hit, or set one first if url/lineNumber provided
|
|
1026
|
+
if (!targetCdpManager.isConnected()) {
|
|
1027
|
+
return createErrorResponse('DEBUGGER_NOT_CONNECTED');
|
|
1028
|
+
}
|
|
1029
|
+
// Already cancelled: never start waiting. (An already-aborted
|
|
1030
|
+
// signal fires no 'abort' event, so the listener below would
|
|
1031
|
+
// never resolve and this would block until pause/timeout.)
|
|
1032
|
+
throwIfAborted(abortSignal);
|
|
1033
|
+
// Track if we created a breakpoint (for cleanup on abort/timeout)
|
|
1034
|
+
let createdBreakpoint = null;
|
|
1035
|
+
const timeout = args.timeout || 300000; // Default 5 minutes
|
|
1036
|
+
// IMPORTANT: Start listening for pause BEFORE setting breakpoint
|
|
1037
|
+
// This prevents race condition where breakpoint triggers immediately
|
|
1038
|
+
// and pause event fires before we've registered the resolver
|
|
1039
|
+
let onAbort;
|
|
1040
|
+
const waitPromise = new Promise((resolve) => {
|
|
1041
|
+
// Set up abort handler (detached in the finally below - the
|
|
1042
|
+
// signal may be a long-lived run signal that outlives this step)
|
|
1043
|
+
if (abortSignal) {
|
|
1044
|
+
onAbort = () => resolve({ type: 'aborted' });
|
|
1045
|
+
abortSignal.addEventListener('abort', onAbort, { once: true });
|
|
1046
|
+
}
|
|
1047
|
+
// Wait for pause - registered before breakpoint is set
|
|
1048
|
+
targetCdpManager.waitForPause(timeout)
|
|
1049
|
+
.then(() => resolve({ type: 'paused' }))
|
|
1050
|
+
.catch(() => resolve({ type: 'timeout' }));
|
|
1051
|
+
});
|
|
1052
|
+
// Everything below runs inside a try/finally so the abort listener
|
|
1053
|
+
// is detached on EVERY exit (including the early return when
|
|
1054
|
+
// setBreakpoint fails): abortSignal is typically the replay run's
|
|
1055
|
+
// long-lived signal, and a leaked listener per step accumulates
|
|
1056
|
+
// for the whole run.
|
|
1057
|
+
try {
|
|
1058
|
+
// If url and lineNumber provided, set a breakpoint (after listener is registered)
|
|
1059
|
+
if (args.url && args.lineNumber !== undefined) {
|
|
1060
|
+
// Resolve source maps for TypeScript files
|
|
1061
|
+
const resolved = await resolveBreakpointLocation(args.url, args.lineNumber, args.columnNumber, targetCdpManager, sourceMapHandler);
|
|
1062
|
+
try {
|
|
1063
|
+
const breakpoint = await targetCdpManager.setBreakpoint(resolved.url, resolved.line, resolved.column, args.condition);
|
|
1064
|
+
const resolvedLine = breakpoint.location.lineNumber + 1;
|
|
1065
|
+
const resolvedColumn = breakpoint.location.columnNumber !== undefined
|
|
1066
|
+
? breakpoint.location.columnNumber + 1
|
|
1067
|
+
: undefined;
|
|
1068
|
+
createdBreakpoint = {
|
|
1069
|
+
breakpointId: breakpoint.breakpointId,
|
|
1070
|
+
url: resolved.url,
|
|
1071
|
+
line: resolvedLine,
|
|
1072
|
+
column: resolvedColumn
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
catch (error) {
|
|
1076
|
+
if (isAbortError(error))
|
|
1077
|
+
throw error;
|
|
1078
|
+
return {
|
|
1079
|
+
content: [{
|
|
1080
|
+
type: 'text',
|
|
1081
|
+
text: `## Failed to Set Await Breakpoint\n\n**Error:** ${error.message}\n\n**Location:** \`${resolved.url}:${resolved.line}\``,
|
|
1082
|
+
}],
|
|
1083
|
+
isError: true,
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
try {
|
|
1088
|
+
const result = await waitPromise;
|
|
1089
|
+
if (result.type === 'aborted') {
|
|
1090
|
+
// Clean up breakpoint on abort (only if we created one) BEFORE
|
|
1091
|
+
// throwing: a one-shot await breakpoint left behind would pause
|
|
1092
|
+
// the target the next time that line runs, long after the user
|
|
1093
|
+
// cancelled.
|
|
1094
|
+
if (createdBreakpoint) {
|
|
1095
|
+
try {
|
|
1096
|
+
await targetCdpManager.removeBreakpoint(createdBreakpoint.breakpointId);
|
|
1097
|
+
}
|
|
1098
|
+
catch {
|
|
1099
|
+
// Ignore cleanup errors
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
// THROW, don't return (#110): this used to resolve a
|
|
1103
|
+
// success-SHAPED response (no isError), so a cancelled
|
|
1104
|
+
// `breakpoint.await` step was recorded `success: true` - a
|
|
1105
|
+
// cancelled step reported as passed. Throwing abort-shaped is
|
|
1106
|
+
// the same contract every other cancellable handler uses, and
|
|
1107
|
+
// the replay executor classifies it as
|
|
1108
|
+
// "Replay aborted by user".
|
|
1109
|
+
throw abortErrorFor(abortSignal);
|
|
1110
|
+
}
|
|
1111
|
+
if (result.type === 'timeout') {
|
|
1112
|
+
let timeoutMsg = `## Breakpoint Await Timeout\n\n`;
|
|
1113
|
+
timeoutMsg += `No breakpoint was hit within ${timeout / 1000} seconds.\n\n`;
|
|
1114
|
+
if (createdBreakpoint) {
|
|
1115
|
+
timeoutMsg += `**Note:** The breakpoint at \`${createdBreakpoint.url}:${createdBreakpoint.line}\` is still active.\n`;
|
|
1116
|
+
timeoutMsg += `Use \`breakpoint({ action: 'remove', breakpointId: '${createdBreakpoint.breakpointId}' })\` to remove it.`;
|
|
1117
|
+
}
|
|
1118
|
+
return {
|
|
1119
|
+
content: [{
|
|
1120
|
+
type: 'text',
|
|
1121
|
+
text: timeoutMsg,
|
|
1122
|
+
}],
|
|
1123
|
+
isError: true,
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
// Paused - get call stack info
|
|
1127
|
+
const pauseInfo = targetCdpManager.getPausedInfo();
|
|
1128
|
+
// If we created a breakpoint, remove it so it doesn't hit again on resume
|
|
1129
|
+
if (createdBreakpoint) {
|
|
1130
|
+
try {
|
|
1131
|
+
await targetCdpManager.removeBreakpoint(createdBreakpoint.breakpointId);
|
|
1132
|
+
}
|
|
1133
|
+
catch {
|
|
1134
|
+
// Ignore removal errors
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
let markdown = `## Breakpoint Hit!\n\n`;
|
|
1138
|
+
if (pauseInfo.paused && pauseInfo.callStack && pauseInfo.callStack.length > 0) {
|
|
1139
|
+
const topFrame = pauseInfo.callStack[0];
|
|
1140
|
+
const hitUrl = targetCdpManager.getScriptUrl(topFrame.location.scriptId) || 'unknown';
|
|
1141
|
+
const hitLine = topFrame.location.lineNumber + 1;
|
|
1142
|
+
const hitColumn = topFrame.location.columnNumber !== undefined ? topFrame.location.columnNumber + 1 : undefined;
|
|
1143
|
+
markdown += `**Location:** \`${hitUrl}:${hitLine}${hitColumn ? `:${hitColumn}` : ''}\`\n`;
|
|
1144
|
+
if (createdBreakpoint) {
|
|
1145
|
+
markdown += `**Created breakpoint removed** (one-shot)\n\n`;
|
|
1146
|
+
}
|
|
1147
|
+
markdown += `**Paused at:** \`${topFrame.functionName || '(anonymous)'}\`\n`;
|
|
1148
|
+
markdown += `**Call Frame ID:** \`${topFrame.callFrameId}\`\n\n`;
|
|
1149
|
+
markdown += `**Next steps:**\n`;
|
|
1150
|
+
markdown += `- \`inspect({ action: 'getVariables', callFrameId: '${topFrame.callFrameId}' })\` - View variables\n`;
|
|
1151
|
+
markdown += `- \`inspect({ action: 'evaluateExpression', expression: '...' })\` - Evaluate code\n`;
|
|
1152
|
+
markdown += `- \`execution({ action: 'stepOver' })\` - Step to next line\n`;
|
|
1153
|
+
markdown += `- \`execution({ action: 'resume' })\` - Continue execution\n`;
|
|
1154
|
+
}
|
|
1155
|
+
else {
|
|
1156
|
+
markdown += `Execution paused but no call stack available.\n`;
|
|
1157
|
+
}
|
|
1158
|
+
return {
|
|
1159
|
+
content: [{
|
|
1160
|
+
type: 'text',
|
|
1161
|
+
text: markdown,
|
|
1162
|
+
}],
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
catch (error) {
|
|
1166
|
+
// The universal trap: this broad catch would otherwise swallow
|
|
1167
|
+
// the abort thrown just above and report it as an await
|
|
1168
|
+
// *failure*. A cancellation is not a failure of this tool.
|
|
1169
|
+
if (isAbortError(error))
|
|
1170
|
+
throw error;
|
|
1171
|
+
return {
|
|
1172
|
+
content: [{
|
|
1173
|
+
type: 'text',
|
|
1174
|
+
text: `## Await Breakpoint Error\n\n**Error:** ${error.message}`,
|
|
1175
|
+
}],
|
|
1176
|
+
isError: true,
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
finally {
|
|
1181
|
+
// Detach the abort listener. The waitForPause resolver it raced
|
|
1182
|
+
// stays registered inside cdp-manager until its own timeout
|
|
1183
|
+
// fires; that is harmless (resolving an already-settled promise
|
|
1184
|
+
// is a no-op and its rejection is caught) but it does mean the
|
|
1185
|
+
// 'await' step's timer outlives the cancel.
|
|
1186
|
+
if (abortSignal && onAbort) {
|
|
1187
|
+
abortSignal.removeEventListener('abort', onAbort);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
default:
|
|
1192
|
+
return createErrorResponse('INVALID_ACTION', { action });
|
|
1193
|
+
}
|
|
1194
|
+
}),
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
//# sourceMappingURL=breakpoint-tools.js.map
|