tabwright 1.0.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/bin.js +4 -0
- package/dist/a11y-client.js +204 -0
- package/dist/agent-skills/tabwright/SKILL.md +119 -0
- package/dist/aria-snapshot.d.ts +262 -0
- package/dist/aria-snapshot.d.ts.map +1 -0
- package/dist/aria-snapshot.js +1256 -0
- package/dist/aria-snapshot.js.map +1 -0
- package/dist/aria-snapshot.test.d.ts +2 -0
- package/dist/aria-snapshot.test.d.ts.map +1 -0
- package/dist/aria-snapshot.test.js +270 -0
- package/dist/aria-snapshot.test.js.map +1 -0
- package/dist/aria-snapshot.unit.test.d.ts +2 -0
- package/dist/aria-snapshot.unit.test.d.ts.map +1 -0
- package/dist/aria-snapshot.unit.test.js +542 -0
- package/dist/aria-snapshot.unit.test.js.map +1 -0
- package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.d.ts +5 -0
- package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.d.ts.map +1 -0
- package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.js +5 -0
- package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.js.map +1 -0
- package/dist/bippy.js +894 -0
- package/dist/browser-config.d.ts +13 -0
- package/dist/browser-config.d.ts.map +1 -0
- package/dist/browser-config.js +126 -0
- package/dist/browser-config.js.map +1 -0
- package/dist/browser-install.d.ts +16 -0
- package/dist/browser-install.d.ts.map +1 -0
- package/dist/browser-install.js +238 -0
- package/dist/browser-install.js.map +1 -0
- package/dist/browser-launch.d.ts +17 -0
- package/dist/browser-launch.d.ts.map +1 -0
- package/dist/browser-launch.js +44 -0
- package/dist/browser-launch.js.map +1 -0
- package/dist/builtin-capabilities.d.ts +28 -0
- package/dist/builtin-capabilities.d.ts.map +1 -0
- package/dist/builtin-capabilities.js +2673 -0
- package/dist/builtin-capabilities.js.map +1 -0
- package/dist/capability-agent-skill.d.ts +60 -0
- package/dist/capability-agent-skill.d.ts.map +1 -0
- package/dist/capability-agent-skill.js +225 -0
- package/dist/capability-agent-skill.js.map +1 -0
- package/dist/capability-auth-state.d.ts +34 -0
- package/dist/capability-auth-state.d.ts.map +1 -0
- package/dist/capability-auth-state.js +157 -0
- package/dist/capability-auth-state.js.map +1 -0
- package/dist/capability-auth.d.ts +36 -0
- package/dist/capability-auth.d.ts.map +1 -0
- package/dist/capability-auth.js +198 -0
- package/dist/capability-auth.js.map +1 -0
- package/dist/capability-cli-confirmation.test.d.ts +2 -0
- package/dist/capability-cli-confirmation.test.d.ts.map +1 -0
- package/dist/capability-cli-confirmation.test.js +79 -0
- package/dist/capability-cli-confirmation.test.js.map +1 -0
- package/dist/capability-options.d.ts +20 -0
- package/dist/capability-options.d.ts.map +1 -0
- package/dist/capability-options.js +31 -0
- package/dist/capability-options.js.map +1 -0
- package/dist/capability-options.test.d.ts +2 -0
- package/dist/capability-options.test.d.ts.map +1 -0
- package/dist/capability-options.test.js +81 -0
- package/dist/capability-options.test.js.map +1 -0
- package/dist/capability-package.d.ts +27 -0
- package/dist/capability-package.d.ts.map +1 -0
- package/dist/capability-package.js +444 -0
- package/dist/capability-package.js.map +1 -0
- package/dist/capability-package.test.d.ts +2 -0
- package/dist/capability-package.test.d.ts.map +1 -0
- package/dist/capability-package.test.js +179 -0
- package/dist/capability-package.test.js.map +1 -0
- package/dist/capability-registry.d.ts +262 -0
- package/dist/capability-registry.d.ts.map +1 -0
- package/dist/capability-registry.js +931 -0
- package/dist/capability-registry.js.map +1 -0
- package/dist/capability-registry.test.d.ts +2 -0
- package/dist/capability-registry.test.d.ts.map +1 -0
- package/dist/capability-registry.test.js +1683 -0
- package/dist/capability-registry.test.js.map +1 -0
- package/dist/capability-runner.d.ts +99 -0
- package/dist/capability-runner.d.ts.map +1 -0
- package/dist/capability-runner.js +608 -0
- package/dist/capability-runner.js.map +1 -0
- package/dist/capability-search-relevance.test.d.ts +2 -0
- package/dist/capability-search-relevance.test.d.ts.map +1 -0
- package/dist/capability-search-relevance.test.js +67 -0
- package/dist/capability-search-relevance.test.js.map +1 -0
- package/dist/capability-studio.d.ts +11 -0
- package/dist/capability-studio.d.ts.map +1 -0
- package/dist/capability-studio.js +593 -0
- package/dist/capability-studio.js.map +1 -0
- package/dist/capability-studio.test.d.ts +2 -0
- package/dist/capability-studio.test.d.ts.map +1 -0
- package/dist/capability-studio.test.js +41 -0
- package/dist/capability-studio.test.js.map +1 -0
- package/dist/cdp-log.d.ts +19 -0
- package/dist/cdp-log.d.ts.map +1 -0
- package/dist/cdp-log.js +83 -0
- package/dist/cdp-log.js.map +1 -0
- package/dist/cdp-log.test.d.ts +2 -0
- package/dist/cdp-log.test.d.ts.map +1 -0
- package/dist/cdp-log.test.js +109 -0
- package/dist/cdp-log.test.js.map +1 -0
- package/dist/cdp-relay.d.ts +19 -0
- package/dist/cdp-relay.d.ts.map +1 -0
- package/dist/cdp-relay.js +2375 -0
- package/dist/cdp-relay.js.map +1 -0
- package/dist/cdp-session.d.ts +37 -0
- package/dist/cdp-session.d.ts.map +1 -0
- package/dist/cdp-session.js +36 -0
- package/dist/cdp-session.js.map +1 -0
- package/dist/cdp-types.d.ts +65 -0
- package/dist/cdp-types.d.ts.map +1 -0
- package/dist/cdp-types.js +91 -0
- package/dist/cdp-types.js.map +1 -0
- package/dist/channel-owner-inspect.test.d.ts +2 -0
- package/dist/channel-owner-inspect.test.d.ts.map +1 -0
- package/dist/channel-owner-inspect.test.js +75 -0
- package/dist/channel-owner-inspect.test.js.map +1 -0
- package/dist/chrome-discovery.d.ts +65 -0
- package/dist/chrome-discovery.d.ts.map +1 -0
- package/dist/chrome-discovery.js +173 -0
- package/dist/chrome-discovery.js.map +1 -0
- package/dist/chrome-discovery.test.d.ts +2 -0
- package/dist/chrome-discovery.test.d.ts.map +1 -0
- package/dist/chrome-discovery.test.js +41 -0
- package/dist/chrome-discovery.test.js.map +1 -0
- package/dist/clean-html.d.ts +11 -0
- package/dist/clean-html.d.ts.map +1 -0
- package/dist/clean-html.js +106 -0
- package/dist/clean-html.js.map +1 -0
- package/dist/cli-help.test.d.ts +2 -0
- package/dist/cli-help.test.d.ts.map +1 -0
- package/dist/cli-help.test.js +98 -0
- package/dist/cli-help.test.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +2709 -0
- package/dist/cli.js.map +1 -0
- package/dist/cloud-client.d.ts +56 -0
- package/dist/cloud-client.d.ts.map +1 -0
- package/dist/cloud-client.js +127 -0
- package/dist/cloud-client.js.map +1 -0
- package/dist/create-logger.d.ts +9 -0
- package/dist/create-logger.d.ts.map +1 -0
- package/dist/create-logger.js +27 -0
- package/dist/create-logger.js.map +1 -0
- package/dist/debugger-api.md +458 -0
- package/dist/debugger-examples-types.d.ts +24 -0
- package/dist/debugger-examples-types.d.ts.map +1 -0
- package/dist/debugger-examples-types.js +2 -0
- package/dist/debugger-examples-types.js.map +1 -0
- package/dist/debugger-examples.d.ts +6 -0
- package/dist/debugger-examples.d.ts.map +1 -0
- package/dist/debugger-examples.js +53 -0
- package/dist/debugger-examples.js.map +1 -0
- package/dist/debugger.d.ts +381 -0
- package/dist/debugger.d.ts.map +1 -0
- package/dist/debugger.js +630 -0
- package/dist/debugger.js.map +1 -0
- package/dist/diff-utils.d.ts +20 -0
- package/dist/diff-utils.d.ts.map +1 -0
- package/dist/diff-utils.js +50 -0
- package/dist/diff-utils.js.map +1 -0
- package/dist/diff-utils.test.d.ts +2 -0
- package/dist/diff-utils.test.d.ts.map +1 -0
- package/dist/diff-utils.test.js +137 -0
- package/dist/diff-utils.test.js.map +1 -0
- package/dist/doctor.d.ts +44 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +246 -0
- package/dist/doctor.js.map +1 -0
- package/dist/doctor.test.d.ts +2 -0
- package/dist/doctor.test.d.ts.map +1 -0
- package/dist/doctor.test.js +208 -0
- package/dist/doctor.test.js.map +1 -0
- package/dist/editor-api.md +374 -0
- package/dist/editor-examples.d.ts +11 -0
- package/dist/editor-examples.d.ts.map +1 -0
- package/dist/editor-examples.js +124 -0
- package/dist/editor-examples.js.map +1 -0
- package/dist/editor.d.ts +203 -0
- package/dist/editor.d.ts.map +1 -0
- package/dist/editor.js +363 -0
- package/dist/editor.js.map +1 -0
- package/dist/env-compat.d.ts +2 -0
- package/dist/env-compat.d.ts.map +1 -0
- package/dist/env-compat.js +27 -0
- package/dist/env-compat.js.map +1 -0
- package/dist/executor.d.ts +229 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +1465 -0
- package/dist/executor.js.map +1 -0
- package/dist/executor.unit.test.d.ts +2 -0
- package/dist/executor.unit.test.d.ts.map +1 -0
- package/dist/executor.unit.test.js +143 -0
- package/dist/executor.unit.test.js.map +1 -0
- package/dist/extension/_locales/en/messages.json +278 -0
- package/dist/extension/_locales/zh_CN/messages.json +256 -0
- package/dist/extension/assets/options-CYH96Lcx.css +1349 -0
- package/dist/extension/background.js +3994 -0
- package/dist/extension/icons/SolarCursorSquareBold.png +0 -0
- package/dist/extension/icons/icon-black-128.png +0 -0
- package/dist/extension/icons/icon-black-16.png +0 -0
- package/dist/extension/icons/icon-black-32.png +0 -0
- package/dist/extension/icons/icon-black-48.png +0 -0
- package/dist/extension/icons/icon-gray-128.png +0 -0
- package/dist/extension/icons/icon-gray-16.png +0 -0
- package/dist/extension/icons/icon-gray-32.png +0 -0
- package/dist/extension/icons/icon-gray-48.png +0 -0
- package/dist/extension/icons/icon-green-128.png +0 -0
- package/dist/extension/icons/icon-green-16.png +0 -0
- package/dist/extension/icons/icon-green-32.png +0 -0
- package/dist/extension/icons/icon-green-48.png +0 -0
- package/dist/extension/icons/tabwright-icon-black.png +0 -0
- package/dist/extension/icons/tabwright-icon-gray-disabled.png +0 -0
- package/dist/extension/icons/tabwright-icon-gray-disabled.svg +1 -0
- package/dist/extension/manifest.json +54 -0
- package/dist/extension/options.js +16145 -0
- package/dist/extension/rrweb-recorder.js +12952 -0
- package/dist/extension/src/options.html +165 -0
- package/dist/extension/src/prism-bash.min.js +1 -0
- package/dist/extension/src/prism.min.js +1 -0
- package/dist/extension/src/welcome.html +396 -0
- package/dist/extension-connection.test.d.ts +2 -0
- package/dist/extension-connection.test.d.ts.map +1 -0
- package/dist/extension-connection.test.js +708 -0
- package/dist/extension-connection.test.js.map +1 -0
- package/dist/extension-icon-warning.test.d.ts +2 -0
- package/dist/extension-icon-warning.test.d.ts.map +1 -0
- package/dist/extension-icon-warning.test.js +33 -0
- package/dist/extension-icon-warning.test.js.map +1 -0
- package/dist/ghost-browser.d.ts +62 -0
- package/dist/ghost-browser.d.ts.map +1 -0
- package/dist/ghost-browser.js +107 -0
- package/dist/ghost-browser.js.map +1 -0
- package/dist/ghost-cursor-client.js +374 -0
- package/dist/ghost-cursor-controller.d.ts +46 -0
- package/dist/ghost-cursor-controller.d.ts.map +1 -0
- package/dist/ghost-cursor-controller.js +98 -0
- package/dist/ghost-cursor-controller.js.map +1 -0
- package/dist/ghost-cursor.d.ts +27 -0
- package/dist/ghost-cursor.d.ts.map +1 -0
- package/dist/ghost-cursor.js +79 -0
- package/dist/ghost-cursor.js.map +1 -0
- package/dist/htmlrewrite.d.ts +8 -0
- package/dist/htmlrewrite.d.ts.map +1 -0
- package/dist/htmlrewrite.js +339 -0
- package/dist/htmlrewrite.js.map +1 -0
- package/dist/htmlrewrite.test.d.ts +2 -0
- package/dist/htmlrewrite.test.d.ts.map +1 -0
- package/dist/htmlrewrite.test.js +7975 -0
- package/dist/htmlrewrite.test.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/kill-port.d.ts +29 -0
- package/dist/kill-port.d.ts.map +1 -0
- package/dist/kill-port.js +190 -0
- package/dist/kill-port.js.map +1 -0
- package/dist/kitty-graphics.d.ts +22 -0
- package/dist/kitty-graphics.d.ts.map +1 -0
- package/dist/kitty-graphics.js +71 -0
- package/dist/kitty-graphics.js.map +1 -0
- package/dist/kitty-graphics.test.d.ts +2 -0
- package/dist/kitty-graphics.test.d.ts.map +1 -0
- package/dist/kitty-graphics.test.js +125 -0
- package/dist/kitty-graphics.test.js.map +1 -0
- package/dist/locator-selector.test.d.ts +2 -0
- package/dist/locator-selector.test.d.ts.map +1 -0
- package/dist/locator-selector.test.js +98 -0
- package/dist/locator-selector.test.js.map +1 -0
- package/dist/mcp-client.d.ts +20 -0
- package/dist/mcp-client.d.ts.map +1 -0
- package/dist/mcp-client.js +56 -0
- package/dist/mcp-client.js.map +1 -0
- package/dist/mcp.d.ts +6 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +511 -0
- package/dist/mcp.js.map +1 -0
- package/dist/on-mouse-action.test.d.ts +2 -0
- package/dist/on-mouse-action.test.d.ts.map +1 -0
- package/dist/on-mouse-action.test.js +180 -0
- package/dist/on-mouse-action.test.js.map +1 -0
- package/dist/options-page.test.d.ts +2 -0
- package/dist/options-page.test.d.ts.map +1 -0
- package/dist/options-page.test.js +773 -0
- package/dist/options-page.test.js.map +1 -0
- package/dist/package-paths.d.ts +4 -0
- package/dist/package-paths.d.ts.map +1 -0
- package/dist/package-paths.js +30 -0
- package/dist/package-paths.js.map +1 -0
- package/dist/page-markdown.d.ts +40 -0
- package/dist/page-markdown.d.ts.map +1 -0
- package/dist/page-markdown.js +181 -0
- package/dist/page-markdown.js.map +1 -0
- package/dist/performance-examples.d.ts +5 -0
- package/dist/performance-examples.d.ts.map +1 -0
- package/dist/performance-examples.js +112 -0
- package/dist/performance-examples.js.map +1 -0
- package/dist/performance-profiling.md +417 -0
- package/dist/playwright-import.d.ts +19 -0
- package/dist/playwright-import.d.ts.map +1 -0
- package/dist/playwright-import.js +40 -0
- package/dist/playwright-import.js.map +1 -0
- package/dist/popup-relocation.test.d.ts +7 -0
- package/dist/popup-relocation.test.d.ts.map +1 -0
- package/dist/popup-relocation.test.js +139 -0
- package/dist/popup-relocation.test.js.map +1 -0
- package/dist/product-paths.d.ts +9 -0
- package/dist/product-paths.d.ts.map +1 -0
- package/dist/product-paths.js +24 -0
- package/dist/product-paths.js.map +1 -0
- package/dist/product-paths.test.d.ts +2 -0
- package/dist/product-paths.test.d.ts.map +1 -0
- package/dist/product-paths.test.js +39 -0
- package/dist/product-paths.test.js.map +1 -0
- package/dist/prompt.md +1004 -0
- package/dist/protocol.d.ts +248 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +59 -0
- package/dist/protocol.js.map +1 -0
- package/dist/protocol.test.d.ts +2 -0
- package/dist/protocol.test.d.ts.map +1 -0
- package/dist/protocol.test.js +31 -0
- package/dist/protocol.test.js.map +1 -0
- package/dist/react-source.d.ts +57 -0
- package/dist/react-source.d.ts.map +1 -0
- package/dist/react-source.js +254 -0
- package/dist/react-source.js.map +1 -0
- package/dist/readability.js +1674 -0
- package/dist/relay-client.d.ts +104 -0
- package/dist/relay-client.d.ts.map +1 -0
- package/dist/relay-client.js +426 -0
- package/dist/relay-client.js.map +1 -0
- package/dist/relay-client.test.d.ts +2 -0
- package/dist/relay-client.test.d.ts.map +1 -0
- package/dist/relay-client.test.js +305 -0
- package/dist/relay-client.test.js.map +1 -0
- package/dist/relay-core.test.d.ts +2 -0
- package/dist/relay-core.test.d.ts.map +1 -0
- package/dist/relay-core.test.js +1427 -0
- package/dist/relay-core.test.js.map +1 -0
- package/dist/relay-navigation.test.d.ts +2 -0
- package/dist/relay-navigation.test.d.ts.map +1 -0
- package/dist/relay-navigation.test.js +661 -0
- package/dist/relay-navigation.test.js.map +1 -0
- package/dist/relay-session.test.d.ts +2 -0
- package/dist/relay-session.test.d.ts.map +1 -0
- package/dist/relay-session.test.js +1121 -0
- package/dist/relay-session.test.js.map +1 -0
- package/dist/relay-state.d.ts +178 -0
- package/dist/relay-state.d.ts.map +1 -0
- package/dist/relay-state.js +346 -0
- package/dist/relay-state.js.map +1 -0
- package/dist/relay-state.test.d.ts +2 -0
- package/dist/relay-state.test.d.ts.map +1 -0
- package/dist/relay-state.test.js +594 -0
- package/dist/relay-state.test.js.map +1 -0
- package/dist/replay-ai-index.d.ts +99 -0
- package/dist/replay-ai-index.d.ts.map +1 -0
- package/dist/replay-ai-index.js +677 -0
- package/dist/replay-ai-index.js.map +1 -0
- package/dist/replay-ai-index.test.d.ts +2 -0
- package/dist/replay-ai-index.test.d.ts.map +1 -0
- package/dist/replay-ai-index.test.js +255 -0
- package/dist/replay-ai-index.test.js.map +1 -0
- package/dist/replay-cli.test.d.ts +2 -0
- package/dist/replay-cli.test.d.ts.map +1 -0
- package/dist/replay-cli.test.js +275 -0
- package/dist/replay-cli.test.js.map +1 -0
- package/dist/replay-eval.d.ts +44 -0
- package/dist/replay-eval.d.ts.map +1 -0
- package/dist/replay-eval.js +902 -0
- package/dist/replay-eval.js.map +1 -0
- package/dist/replay-handoff.d.ts +29 -0
- package/dist/replay-handoff.d.ts.map +1 -0
- package/dist/replay-handoff.js +83 -0
- package/dist/replay-handoff.js.map +1 -0
- package/dist/replay-handoff.test.d.ts +2 -0
- package/dist/replay-handoff.test.d.ts.map +1 -0
- package/dist/replay-handoff.test.js +123 -0
- package/dist/replay-handoff.test.js.map +1 -0
- package/dist/replay-workflow-compiler.d.ts +44 -0
- package/dist/replay-workflow-compiler.d.ts.map +1 -0
- package/dist/replay-workflow-compiler.js +601 -0
- package/dist/replay-workflow-compiler.js.map +1 -0
- package/dist/replay-workflow-compiler.test.d.ts +2 -0
- package/dist/replay-workflow-compiler.test.d.ts.map +1 -0
- package/dist/replay-workflow-compiler.test.js +223 -0
- package/dist/replay-workflow-compiler.test.js.map +1 -0
- package/dist/rrweb-recording-relay.d.ts +56 -0
- package/dist/rrweb-recording-relay.d.ts.map +1 -0
- package/dist/rrweb-recording-relay.js +299 -0
- package/dist/rrweb-recording-relay.js.map +1 -0
- package/dist/rrweb-recording-relay.test.d.ts +2 -0
- package/dist/rrweb-recording-relay.test.d.ts.map +1 -0
- package/dist/rrweb-recording-relay.test.js +85 -0
- package/dist/rrweb-recording-relay.test.js.map +1 -0
- package/dist/rrweb-recording.d.ts +104 -0
- package/dist/rrweb-recording.d.ts.map +1 -0
- package/dist/rrweb-recording.js +180 -0
- package/dist/rrweb-recording.js.map +1 -0
- package/dist/scoped-fs.d.ts +95 -0
- package/dist/scoped-fs.d.ts.map +1 -0
- package/dist/scoped-fs.js +358 -0
- package/dist/scoped-fs.js.map +1 -0
- package/dist/scoped-fs.test.d.ts +5 -0
- package/dist/scoped-fs.test.d.ts.map +1 -0
- package/dist/scoped-fs.test.js +73 -0
- package/dist/scoped-fs.test.js.map +1 -0
- package/dist/selector-generator.js +7378 -0
- package/dist/skill-stub.test.d.ts +2 -0
- package/dist/skill-stub.test.d.ts.map +1 -0
- package/dist/skill-stub.test.js +22 -0
- package/dist/skill-stub.test.js.map +1 -0
- package/dist/snapshot-tools.test.d.ts +2 -0
- package/dist/snapshot-tools.test.d.ts.map +1 -0
- package/dist/snapshot-tools.test.js +856 -0
- package/dist/snapshot-tools.test.js.map +1 -0
- package/dist/start-relay-server.d.ts +6 -0
- package/dist/start-relay-server.d.ts.map +1 -0
- package/dist/start-relay-server.js +57 -0
- package/dist/start-relay-server.js.map +1 -0
- package/dist/styles-api.md +124 -0
- package/dist/styles-examples.d.ts +8 -0
- package/dist/styles-examples.d.ts.map +1 -0
- package/dist/styles-examples.js +64 -0
- package/dist/styles-examples.js.map +1 -0
- package/dist/styles.d.ts +27 -0
- package/dist/styles.d.ts.map +1 -0
- package/dist/styles.js +231 -0
- package/dist/styles.js.map +1 -0
- package/dist/tabwright-agent-skill.d.ts +30 -0
- package/dist/tabwright-agent-skill.d.ts.map +1 -0
- package/dist/tabwright-agent-skill.js +74 -0
- package/dist/tabwright-agent-skill.js.map +1 -0
- package/dist/tabwright-agent-skill.test.d.ts +2 -0
- package/dist/tabwright-agent-skill.test.d.ts.map +1 -0
- package/dist/tabwright-agent-skill.test.js +51 -0
- package/dist/tabwright-agent-skill.test.js.map +1 -0
- package/dist/test-declarations.d.ts +13 -0
- package/dist/test-declarations.d.ts.map +1 -0
- package/dist/test-declarations.js +2 -0
- package/dist/test-declarations.js.map +1 -0
- package/dist/test-utils.d.ts +69 -0
- package/dist/test-utils.d.ts.map +1 -0
- package/dist/test-utils.js +448 -0
- package/dist/test-utils.js.map +1 -0
- package/dist/test-utils.test.d.ts +2 -0
- package/dist/test-utils.test.d.ts.map +1 -0
- package/dist/test-utils.test.js +55 -0
- package/dist/test-utils.test.js.map +1 -0
- package/dist/utils.d.ts +26 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +60 -0
- package/dist/utils.js.map +1 -0
- package/dist/wait-for-page-load.d.ts +16 -0
- package/dist/wait-for-page-load.d.ts.map +1 -0
- package/dist/wait-for-page-load.js +146 -0
- package/dist/wait-for-page-load.js.map +1 -0
- package/dist/workflow-capability.d.ts +72 -0
- package/dist/workflow-capability.d.ts.map +1 -0
- package/dist/workflow-capability.js +316 -0
- package/dist/workflow-capability.js.map +1 -0
- package/dist/workflow-script-flow.test.d.ts +2 -0
- package/dist/workflow-script-flow.test.d.ts.map +1 -0
- package/dist/workflow-script-flow.test.js +285 -0
- package/dist/workflow-script-flow.test.js.map +1 -0
- package/package.json +80 -0
- package/src/__snapshots__/x.com.processed.html +1003 -0
- package/src/__snapshots__/x.com.processed.withStyles.html +2720 -0
- package/src/a11y-client.ts +241 -0
- package/src/agent-skills/conan-config/SKILL.md +141 -0
- package/src/agent-skills/conan-config/agents/openai.yaml +4 -0
- package/src/aria-snapshot.test.ts +311 -0
- package/src/aria-snapshot.ts +1681 -0
- package/src/aria-snapshot.unit.test.ts +615 -0
- package/src/aria-snapshots/github-interactive.txt +193 -0
- package/src/aria-snapshots/github-raw.txt +309 -0
- package/src/aria-snapshots/hackernews-interactive.txt +477 -0
- package/src/aria-snapshots/hackernews-raw.txt +428 -0
- package/src/aria-snapshots/prosemirror-interactive.txt +66 -0
- package/src/aria-snapshots/prosemirror-raw.txt +145 -0
- package/src/assets/aria-labels-hacker-news.png +0 -0
- package/src/assets/aria-labels-old-reddit.png +0 -0
- package/src/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.ts +5 -0
- package/src/assets/cursors/screen-studio/pointer-macos-tahoe.svg +18 -0
- package/src/assets/x.com.html +32946 -0
- package/src/browser-config.ts +179 -0
- package/src/browser-install.ts +284 -0
- package/src/browser-launch.ts +75 -0
- package/src/builtin-capabilities.ts +2765 -0
- package/src/capability-agent-skill.ts +305 -0
- package/src/capability-auth-state.ts +197 -0
- package/src/capability-auth.ts +249 -0
- package/src/capability-cli-confirmation.test.ts +90 -0
- package/src/capability-options.test.ts +89 -0
- package/src/capability-options.ts +57 -0
- package/src/capability-package.test.ts +204 -0
- package/src/capability-package.ts +555 -0
- package/src/capability-registry.test.ts +1905 -0
- package/src/capability-registry.ts +1234 -0
- package/src/capability-runner.ts +801 -0
- package/src/capability-search-relevance.test.ts +73 -0
- package/src/capability-studio.test.ts +45 -0
- package/src/capability-studio.ts +641 -0
- package/src/cdp-log.test.ts +131 -0
- package/src/cdp-log.ts +112 -0
- package/src/cdp-relay.ts +2944 -0
- package/src/cdp-session.ts +77 -0
- package/src/cdp-types.ts +158 -0
- package/src/channel-owner-inspect.test.ts +94 -0
- package/src/chrome-discovery.test.ts +50 -0
- package/src/chrome-discovery.ts +220 -0
- package/src/clean-html.ts +141 -0
- package/src/cli-help.test.ts +123 -0
- package/src/cli.ts +3181 -0
- package/src/cloud-client.ts +179 -0
- package/src/create-logger.ts +38 -0
- package/src/debugger-examples-types.ts +16 -0
- package/src/debugger-examples.ts +66 -0
- package/src/debugger.ts +708 -0
- package/src/diff-utils.test.ts +152 -0
- package/src/diff-utils.ts +70 -0
- package/src/doctor.test.ts +244 -0
- package/src/doctor.ts +321 -0
- package/src/editor-examples.ts +158 -0
- package/src/editor.ts +426 -0
- package/src/env-compat.ts +28 -0
- package/src/executor.ts +1824 -0
- package/src/executor.unit.test.ts +168 -0
- package/src/extension-connection.test.ts +855 -0
- package/src/extension-icon-warning.test.ts +45 -0
- package/src/ghost-browser.ts +149 -0
- package/src/ghost-cursor-client.ts +494 -0
- package/src/ghost-cursor-controller.ts +129 -0
- package/src/ghost-cursor.ts +123 -0
- package/src/htmlrewrite.test.ts +8008 -0
- package/src/htmlrewrite.ts +390 -0
- package/src/index.ts +53 -0
- package/src/kill-port.ts +219 -0
- package/src/kitty-graphics.test.ts +139 -0
- package/src/kitty-graphics.ts +79 -0
- package/src/locator-selector.test.ts +119 -0
- package/src/mcp-client.ts +78 -0
- package/src/mcp.ts +629 -0
- package/src/on-mouse-action.test.ts +226 -0
- package/src/options-page.test.ts +839 -0
- package/src/package-paths.ts +38 -0
- package/src/page-markdown.ts +240 -0
- package/src/performance-examples.ts +186 -0
- package/src/playwright-import.ts +59 -0
- package/src/popup-relocation.test.ts +163 -0
- package/src/product-paths.test.ts +47 -0
- package/src/product-paths.ts +28 -0
- package/src/protocol.test.ts +52 -0
- package/src/protocol.ts +351 -0
- package/src/react-source.ts +379 -0
- package/src/relay-client.test.ts +375 -0
- package/src/relay-client.ts +572 -0
- package/src/relay-core.test.ts +1603 -0
- package/src/relay-navigation.test.ts +790 -0
- package/src/relay-session.test.ts +1375 -0
- package/src/relay-state.test.ts +729 -0
- package/src/relay-state.ts +561 -0
- package/src/replay-ai-index.test.ts +278 -0
- package/src/replay-ai-index.ts +872 -0
- package/src/replay-cli.test.ts +325 -0
- package/src/replay-eval.ts +1039 -0
- package/src/replay-handoff.test.ts +163 -0
- package/src/replay-handoff.ts +109 -0
- package/src/replay-workflow-compiler.test.ts +250 -0
- package/src/replay-workflow-compiler.ts +700 -0
- package/src/resource.md +409 -0
- package/src/rrweb-recording-relay.test.ts +99 -0
- package/src/rrweb-recording-relay.ts +400 -0
- package/src/rrweb-recording.ts +322 -0
- package/src/scoped-fs.test.ts +80 -0
- package/src/scoped-fs.ts +420 -0
- package/src/skill-stub.test.ts +24 -0
- package/src/skill.md +1413 -0
- package/src/snapshot-tools.test.ts +1017 -0
- package/src/snapshots/hacker-news-accessibility-full.md +81 -0
- package/src/snapshots/hacker-news-accessibility-interactive.md +43 -0
- package/src/snapshots/page-markdown-output.txt +16 -0
- package/src/snapshots/shadcn-ui-accessibility-full.md +201 -0
- package/src/snapshots/shadcn-ui-accessibility-interactive.md +109 -0
- package/src/start-relay-server.ts +69 -0
- package/src/styles-examples.ts +84 -0
- package/src/styles.ts +343 -0
- package/src/tabwright-agent-skill.test.ts +61 -0
- package/src/tabwright-agent-skill.ts +105 -0
- package/src/test-declarations.ts +13 -0
- package/src/test-utils.test.ts +70 -0
- package/src/test-utils.ts +566 -0
- package/src/utils.ts +78 -0
- package/src/wait-for-page-load.ts +198 -0
- package/src/workflow-capability.ts +417 -0
- package/src/workflow-script-flow.test.ts +388 -0
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for connecting to the relay server.
|
|
3
|
+
* Used by both MCP and CLI.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { spawn } from 'node:child_process'
|
|
7
|
+
import path from 'node:path'
|
|
8
|
+
import { fileURLToPath } from 'node:url'
|
|
9
|
+
import pc from 'picocolors'
|
|
10
|
+
import { getListeningPidsForPort, killPortProcess } from './kill-port.js'
|
|
11
|
+
import { RELAY_FEATURES } from './protocol.js'
|
|
12
|
+
import { VERSION, sleep, LOG_FILE_PATH } from './utils.js'
|
|
13
|
+
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
15
|
+
const __dirname = path.dirname(__filename)
|
|
16
|
+
|
|
17
|
+
export const RELAY_PORT = Number(process.env.TABWRIGHT_PORT || process.env.PLAYWRITER_PORT) || 19988
|
|
18
|
+
export const LOCAL_RELAY_HOSTS = ['127.0.0.1', 'localhost', '[::1]'] as const
|
|
19
|
+
|
|
20
|
+
export type ExtensionStatus = {
|
|
21
|
+
extensionId: string
|
|
22
|
+
stableKey?: string
|
|
23
|
+
browser: string | null
|
|
24
|
+
profile: { email: string; id: string } | null
|
|
25
|
+
activeTargets: number
|
|
26
|
+
playwriterVersion: string | null
|
|
27
|
+
protocolVersion?: number
|
|
28
|
+
features?: string[]
|
|
29
|
+
connectionHealth?: 'ready' | 'limited' | 'legacy'
|
|
30
|
+
missingFeatures?: string[]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Select an extension only when the choice is unambiguous.
|
|
35
|
+
* A single connection remains the backward-compatible default. With multiple
|
|
36
|
+
* connections, an enabled tab is a useful signal only when exactly one
|
|
37
|
+
* extension has one.
|
|
38
|
+
*/
|
|
39
|
+
export function selectImplicitExtension(extensions: ExtensionStatus[]): ExtensionStatus | null {
|
|
40
|
+
if (extensions.length === 1) {
|
|
41
|
+
return extensions[0]!
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const activeExtensions = extensions.filter((extension) => {
|
|
45
|
+
return extension.activeTargets > 0
|
|
46
|
+
})
|
|
47
|
+
if (activeExtensions.length === 1) {
|
|
48
|
+
return activeExtensions[0]!
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function getLocalRelayHttpBaseUrls(port: number = RELAY_PORT): string[] {
|
|
55
|
+
return LOCAL_RELAY_HOSTS.map((host) => {
|
|
56
|
+
return `http://${host}:${port}`
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function fetchLocalRelayPath({
|
|
61
|
+
path,
|
|
62
|
+
port = RELAY_PORT,
|
|
63
|
+
timeoutMs = 2000,
|
|
64
|
+
init,
|
|
65
|
+
accept = () => {
|
|
66
|
+
return true
|
|
67
|
+
},
|
|
68
|
+
}: {
|
|
69
|
+
path: string
|
|
70
|
+
port?: number
|
|
71
|
+
timeoutMs?: number
|
|
72
|
+
init?: RequestInit
|
|
73
|
+
accept?: (response: Response) => boolean
|
|
74
|
+
}): Promise<{ response: Response; httpBaseUrl: string } | null> {
|
|
75
|
+
const attempts = getLocalRelayHttpBaseUrls(port)
|
|
76
|
+
|
|
77
|
+
for (const httpBaseUrl of attempts) {
|
|
78
|
+
try {
|
|
79
|
+
const response = await fetch(`${httpBaseUrl}${path}`, {
|
|
80
|
+
...init,
|
|
81
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
82
|
+
})
|
|
83
|
+
if (accept(response)) {
|
|
84
|
+
return { response, httpBaseUrl }
|
|
85
|
+
}
|
|
86
|
+
} catch {}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return null
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function getLocalRelayHttpBaseUrl(port: number = RELAY_PORT): Promise<string> {
|
|
93
|
+
const result = await fetchLocalRelayPath({
|
|
94
|
+
path: '/version',
|
|
95
|
+
port,
|
|
96
|
+
timeoutMs: 500,
|
|
97
|
+
accept: (response) => {
|
|
98
|
+
return response.ok
|
|
99
|
+
},
|
|
100
|
+
})
|
|
101
|
+
if (result) {
|
|
102
|
+
return result.httpBaseUrl
|
|
103
|
+
}
|
|
104
|
+
return getLocalRelayHttpBaseUrls(port)[0]!
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export async function getRelayServerVersion(port: number = RELAY_PORT): Promise<string | null> {
|
|
108
|
+
const result = await fetchLocalRelayPath({
|
|
109
|
+
path: '/version',
|
|
110
|
+
port,
|
|
111
|
+
timeoutMs: 2000,
|
|
112
|
+
accept: (response) => {
|
|
113
|
+
return response.ok
|
|
114
|
+
},
|
|
115
|
+
})
|
|
116
|
+
if (!result) {
|
|
117
|
+
return null
|
|
118
|
+
}
|
|
119
|
+
const data = (await result.response.json()) as { version: string }
|
|
120
|
+
return data.version
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export async function getRelayServerFeatures(port: number = RELAY_PORT): Promise<string[] | null> {
|
|
124
|
+
const result = await fetchLocalRelayPath({
|
|
125
|
+
path: '/features',
|
|
126
|
+
port,
|
|
127
|
+
timeoutMs: 2000,
|
|
128
|
+
accept: (response) => {
|
|
129
|
+
return response.ok
|
|
130
|
+
},
|
|
131
|
+
})
|
|
132
|
+
if (!result) {
|
|
133
|
+
return null
|
|
134
|
+
}
|
|
135
|
+
const data: unknown = await result.response.json()
|
|
136
|
+
if (!data || typeof data !== 'object') {
|
|
137
|
+
return null
|
|
138
|
+
}
|
|
139
|
+
const features = (data as { features?: unknown }).features
|
|
140
|
+
if (!Array.isArray(features) || !features.every((feature) => typeof feature === 'string')) {
|
|
141
|
+
return null
|
|
142
|
+
}
|
|
143
|
+
return features
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function supportsRelayFeatures(options: {
|
|
147
|
+
features: readonly string[] | null
|
|
148
|
+
requiredFeatures?: readonly string[]
|
|
149
|
+
}): boolean {
|
|
150
|
+
const requiredFeatures = options.requiredFeatures || RELAY_FEATURES
|
|
151
|
+
const features = options.features
|
|
152
|
+
if (!features) {
|
|
153
|
+
return false
|
|
154
|
+
}
|
|
155
|
+
return requiredFeatures.every((feature) => {
|
|
156
|
+
return features.includes(feature)
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function acceptsRelayVersion({
|
|
161
|
+
version,
|
|
162
|
+
expectedVersion,
|
|
163
|
+
minimumVersion,
|
|
164
|
+
}: {
|
|
165
|
+
version: string | null
|
|
166
|
+
expectedVersion?: string
|
|
167
|
+
minimumVersion?: string
|
|
168
|
+
}): boolean {
|
|
169
|
+
if (!version) {
|
|
170
|
+
return false
|
|
171
|
+
}
|
|
172
|
+
if (expectedVersion) {
|
|
173
|
+
return version === expectedVersion
|
|
174
|
+
}
|
|
175
|
+
if (minimumVersion) {
|
|
176
|
+
return compareVersions(version, minimumVersion) >= 0
|
|
177
|
+
}
|
|
178
|
+
return true
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Poll /version until a relay responds or timeout expires.
|
|
183
|
+
* Used during startup races where a relay may have bound the port
|
|
184
|
+
* but isn't serving HTTP yet (issue #75).
|
|
185
|
+
*/
|
|
186
|
+
export async function waitForRelayVersion({
|
|
187
|
+
port = RELAY_PORT,
|
|
188
|
+
timeoutMs = 2000,
|
|
189
|
+
intervalMs = 200,
|
|
190
|
+
expectedVersion,
|
|
191
|
+
minimumVersion,
|
|
192
|
+
}: {
|
|
193
|
+
port?: number
|
|
194
|
+
timeoutMs?: number
|
|
195
|
+
intervalMs?: number
|
|
196
|
+
/** Keep polling until this exact relay version responds. Takes precedence over minimumVersion. */
|
|
197
|
+
expectedVersion?: string
|
|
198
|
+
/** Keep polling until this relay version or a newer one responds. */
|
|
199
|
+
minimumVersion?: string
|
|
200
|
+
} = {}): Promise<string | null> {
|
|
201
|
+
const end = Date.now() + timeoutMs
|
|
202
|
+
while (Date.now() < end) {
|
|
203
|
+
const version = await getRelayServerVersion(port)
|
|
204
|
+
if (acceptsRelayVersion({ version, expectedVersion, minimumVersion })) {
|
|
205
|
+
return version
|
|
206
|
+
}
|
|
207
|
+
await sleep(intervalMs)
|
|
208
|
+
}
|
|
209
|
+
return null
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function waitForCurrentRelayReadiness(options: {
|
|
213
|
+
port: number
|
|
214
|
+
timeoutMs: number
|
|
215
|
+
intervalMs?: number
|
|
216
|
+
}): Promise<string | null> {
|
|
217
|
+
const end = Date.now() + options.timeoutMs
|
|
218
|
+
const intervalMs = options.intervalMs || 200
|
|
219
|
+
while (Date.now() < end) {
|
|
220
|
+
const version = await getRelayServerVersion(options.port)
|
|
221
|
+
if (version && compareVersions(version, VERSION) > 0) {
|
|
222
|
+
return version
|
|
223
|
+
}
|
|
224
|
+
if (version === VERSION) {
|
|
225
|
+
const features = await getRelayServerFeatures(options.port)
|
|
226
|
+
if (supportsRelayFeatures({ features })) {
|
|
227
|
+
return version
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
await sleep(intervalMs)
|
|
231
|
+
}
|
|
232
|
+
return null
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export async function getExtensionStatus(port: number = RELAY_PORT): Promise<{
|
|
236
|
+
connected: boolean
|
|
237
|
+
activeTargets: number
|
|
238
|
+
playwriterVersion: string | null
|
|
239
|
+
protocolVersion?: number
|
|
240
|
+
features?: string[]
|
|
241
|
+
connectionHealth?: 'ready' | 'limited' | 'legacy'
|
|
242
|
+
missingFeatures?: string[]
|
|
243
|
+
} | null> {
|
|
244
|
+
const result = await fetchLocalRelayPath({
|
|
245
|
+
path: '/extension/status',
|
|
246
|
+
port,
|
|
247
|
+
timeoutMs: 500,
|
|
248
|
+
accept: (response) => {
|
|
249
|
+
return response.ok
|
|
250
|
+
},
|
|
251
|
+
})
|
|
252
|
+
if (!result) {
|
|
253
|
+
return null
|
|
254
|
+
}
|
|
255
|
+
return (await result.response.json()) as {
|
|
256
|
+
connected: boolean
|
|
257
|
+
activeTargets: number
|
|
258
|
+
playwriterVersion: string | null
|
|
259
|
+
protocolVersion?: number
|
|
260
|
+
features?: string[]
|
|
261
|
+
connectionHealth?: 'ready' | 'limited' | 'legacy'
|
|
262
|
+
missingFeatures?: string[]
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export async function getExtensionsStatus(port: number = RELAY_PORT): Promise<ExtensionStatus[]> {
|
|
267
|
+
const result = await fetchLocalRelayPath({
|
|
268
|
+
path: '/extensions/status',
|
|
269
|
+
port,
|
|
270
|
+
timeoutMs: 2000,
|
|
271
|
+
accept: (response) => {
|
|
272
|
+
return response.ok
|
|
273
|
+
},
|
|
274
|
+
})
|
|
275
|
+
if (result) {
|
|
276
|
+
const data = (await result.response.json()) as {
|
|
277
|
+
extensions: ExtensionStatus[]
|
|
278
|
+
}
|
|
279
|
+
return data.extensions || []
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const fallbackResult = await fetchLocalRelayPath({
|
|
283
|
+
path: '/extension/status',
|
|
284
|
+
port,
|
|
285
|
+
timeoutMs: 2000,
|
|
286
|
+
accept: (response) => {
|
|
287
|
+
return response.ok
|
|
288
|
+
},
|
|
289
|
+
})
|
|
290
|
+
if (!fallbackResult) {
|
|
291
|
+
return []
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const fallbackData = (await fallbackResult.response.json()) as {
|
|
295
|
+
connected: boolean
|
|
296
|
+
activeTargets: number
|
|
297
|
+
browser: string | null
|
|
298
|
+
profile: { email: string; id: string } | null
|
|
299
|
+
playwriterVersion?: string | null
|
|
300
|
+
protocolVersion?: number
|
|
301
|
+
features?: string[]
|
|
302
|
+
connectionHealth?: 'ready' | 'limited' | 'legacy'
|
|
303
|
+
missingFeatures?: string[]
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (!fallbackData?.connected) {
|
|
307
|
+
return []
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return [
|
|
311
|
+
{
|
|
312
|
+
extensionId: 'default',
|
|
313
|
+
stableKey: undefined,
|
|
314
|
+
browser: fallbackData.browser,
|
|
315
|
+
profile: fallbackData.profile,
|
|
316
|
+
activeTargets: fallbackData.activeTargets,
|
|
317
|
+
playwriterVersion: fallbackData.playwriterVersion || null,
|
|
318
|
+
protocolVersion: fallbackData.protocolVersion,
|
|
319
|
+
features: fallbackData.features,
|
|
320
|
+
connectionHealth: fallbackData.connectionHealth,
|
|
321
|
+
missingFeatures: fallbackData.missingFeatures,
|
|
322
|
+
},
|
|
323
|
+
]
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function getExtensionSettleSignature(extensions: ExtensionStatus[]): string {
|
|
327
|
+
return extensions
|
|
328
|
+
.map((extension) => {
|
|
329
|
+
return JSON.stringify({
|
|
330
|
+
extensionId: extension.extensionId,
|
|
331
|
+
stableKey: extension.stableKey ?? null,
|
|
332
|
+
activeTargets: extension.activeTargets,
|
|
333
|
+
})
|
|
334
|
+
})
|
|
335
|
+
.sort()
|
|
336
|
+
.join('\n')
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Wait for at least one extension to appear in extensions status.
|
|
341
|
+
* Returns connected extension entries, or [] on timeout.
|
|
342
|
+
*/
|
|
343
|
+
export async function waitForConnectedExtensions(
|
|
344
|
+
options: {
|
|
345
|
+
port?: number
|
|
346
|
+
timeoutMs?: number
|
|
347
|
+
pollIntervalMs?: number
|
|
348
|
+
/** Wait for the connection snapshot to remain unchanged before returning. Default: 0. */
|
|
349
|
+
settleMs?: number
|
|
350
|
+
logger?: { log: (...args: any[]) => void }
|
|
351
|
+
} = {},
|
|
352
|
+
): Promise<ExtensionStatus[]> {
|
|
353
|
+
const { port = RELAY_PORT, timeoutMs = 5000, pollIntervalMs = 200, settleMs = 0, logger } = options
|
|
354
|
+
const startTime = Date.now()
|
|
355
|
+
let latestNonEmpty: ExtensionStatus[] = []
|
|
356
|
+
let latestSnapshotWasConnected = false
|
|
357
|
+
let stableSignature: string | null = null
|
|
358
|
+
let stableSince = 0
|
|
359
|
+
|
|
360
|
+
logger?.log(pc.dim('Waiting for extension to connect...'))
|
|
361
|
+
|
|
362
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
363
|
+
const extensions = await getExtensionsStatus(port)
|
|
364
|
+
if (extensions.length > 0) {
|
|
365
|
+
latestSnapshotWasConnected = true
|
|
366
|
+
latestNonEmpty = extensions
|
|
367
|
+
const signature = getExtensionSettleSignature(extensions)
|
|
368
|
+
if (signature !== stableSignature) {
|
|
369
|
+
stableSignature = signature
|
|
370
|
+
stableSince = Date.now()
|
|
371
|
+
}
|
|
372
|
+
if (settleMs === 0 || Date.now() - stableSince >= settleMs) {
|
|
373
|
+
logger?.log(pc.green('Extension connected'))
|
|
374
|
+
return latestNonEmpty
|
|
375
|
+
}
|
|
376
|
+
} else if (latestNonEmpty.length > 0) {
|
|
377
|
+
latestSnapshotWasConnected = false
|
|
378
|
+
stableSignature = null
|
|
379
|
+
}
|
|
380
|
+
await sleep(pollIntervalMs)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (latestSnapshotWasConnected && latestNonEmpty.length > 0) {
|
|
384
|
+
logger?.log(pc.green('Extension connected'))
|
|
385
|
+
return latestNonEmpty
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
logger?.log(pc.yellow('Extension did not connect within timeout'))
|
|
389
|
+
return []
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
async function killRelayServer(options: { port: number; waitForFreeMs?: number }): Promise<void> {
|
|
393
|
+
const { port, waitForFreeMs = 3000 } = options
|
|
394
|
+
|
|
395
|
+
try {
|
|
396
|
+
await killPortProcess({ port })
|
|
397
|
+
} catch {
|
|
398
|
+
return
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const startTime = Date.now()
|
|
402
|
+
while (Date.now() - startTime < waitForFreeMs) {
|
|
403
|
+
const pids = await getListeningPidsForPort({ port }).catch(() => [])
|
|
404
|
+
if (pids.length === 0) {
|
|
405
|
+
return
|
|
406
|
+
}
|
|
407
|
+
await sleep(100)
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Compare two semver versions. Returns:
|
|
413
|
+
* - negative if v1 < v2
|
|
414
|
+
* - 0 if v1 === v2
|
|
415
|
+
* - positive if v1 > v2
|
|
416
|
+
*/
|
|
417
|
+
export function compareVersions(v1: string, v2: string): number {
|
|
418
|
+
const parts1 = v1.split('.').map(Number)
|
|
419
|
+
const parts2 = v2.split('.').map(Number)
|
|
420
|
+
const len = Math.max(parts1.length, parts2.length)
|
|
421
|
+
|
|
422
|
+
for (let i = 0; i < len; i++) {
|
|
423
|
+
const p1 = parts1[i] || 0
|
|
424
|
+
const p2 = parts2[i] || 0
|
|
425
|
+
if (p1 !== p2) {
|
|
426
|
+
return p1 - p2
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
return 0
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Check if the running Tabwright package is older than the version the extension was built with.
|
|
434
|
+
* The extension bundles the Tabwright version at build time. If the extension reports a newer
|
|
435
|
+
* version, it means the user's CLI/MCP needs updating.
|
|
436
|
+
* Returns a warning message if outdated, null otherwise.
|
|
437
|
+
*/
|
|
438
|
+
export function getExtensionOutdatedWarning(extensionTabwrightVersion: string | null | undefined): string | null {
|
|
439
|
+
if (!extensionTabwrightVersion) {
|
|
440
|
+
return null
|
|
441
|
+
}
|
|
442
|
+
if (compareVersions(extensionTabwrightVersion, VERSION) > 0) {
|
|
443
|
+
return `Tabwright ${VERSION} is outdated (extension requires ${extensionTabwrightVersion}). Run \`npm install -g tabwright@latest\` or update the tabwright package in your project.`
|
|
444
|
+
}
|
|
445
|
+
return null
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export interface EnsureRelayServerOptions {
|
|
449
|
+
logger?: { log: (...args: any[]) => void }
|
|
450
|
+
/** If true, will kill and restart server on version mismatch. Default: true */
|
|
451
|
+
restartOnVersionMismatch?: boolean
|
|
452
|
+
/** Pass additional environment variables to the relay server process */
|
|
453
|
+
env?: Record<string, string>
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Module-level dedup: if ensureRelayServer is called concurrently within the
|
|
457
|
+
// same process (e.g. two MCP tool handlers at once), only one spawn runs.
|
|
458
|
+
let pendingEnsure: Promise<true | undefined> | null = null
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Ensures the relay server is running. Starts it if not running.
|
|
462
|
+
* Optionally restarts on version mismatch.
|
|
463
|
+
* Concurrent calls within the same process are deduplicated.
|
|
464
|
+
*/
|
|
465
|
+
export async function ensureRelayServer(options: EnsureRelayServerOptions = {}): Promise<true | undefined> {
|
|
466
|
+
if (pendingEnsure) {
|
|
467
|
+
return pendingEnsure
|
|
468
|
+
}
|
|
469
|
+
pendingEnsure = ensureRelayServerImpl(options).finally(() => {
|
|
470
|
+
pendingEnsure = null
|
|
471
|
+
})
|
|
472
|
+
return pendingEnsure
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
async function ensureRelayServerImpl(options: EnsureRelayServerOptions = {}): Promise<true | undefined> {
|
|
476
|
+
const { logger, restartOnVersionMismatch = true, env: additionalEnv } = options
|
|
477
|
+
const serverVersion = await getRelayServerVersion(RELAY_PORT)
|
|
478
|
+
const sameVersionFeatures = serverVersion === VERSION ? await getRelayServerFeatures(RELAY_PORT) : null
|
|
479
|
+
|
|
480
|
+
if (serverVersion === VERSION && supportsRelayFeatures({ features: sameVersionFeatures })) {
|
|
481
|
+
return
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Don't restart if server version is higher than our version.
|
|
485
|
+
// This prevents older clients from killing a newer server.
|
|
486
|
+
if (serverVersion !== null && compareVersions(serverVersion, VERSION) > 0) {
|
|
487
|
+
return
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (serverVersion !== null) {
|
|
491
|
+
if (restartOnVersionMismatch) {
|
|
492
|
+
const reason =
|
|
493
|
+
serverVersion === VERSION
|
|
494
|
+
? `CDP relay server is missing current features (${RELAY_FEATURES.join(', ')}), restarting...`
|
|
495
|
+
: `CDP relay server version mismatch (server: ${serverVersion}, client: ${VERSION}), restarting...`
|
|
496
|
+
logger?.log(pc.yellow(reason))
|
|
497
|
+
await killRelayServer({ port: RELAY_PORT })
|
|
498
|
+
} else {
|
|
499
|
+
// Server is running but different version, just use it
|
|
500
|
+
return
|
|
501
|
+
}
|
|
502
|
+
} else {
|
|
503
|
+
const listeningPids = await getListeningPidsForPort({ port: RELAY_PORT }).catch(() => [])
|
|
504
|
+
if (listeningPids.length > 0) {
|
|
505
|
+
// Something is on the port but /version didn't respond. It might be a
|
|
506
|
+
// relay that's still starting (race with another CLI/MCP instance).
|
|
507
|
+
// Poll /version briefly before deciding to kill it (issue #75).
|
|
508
|
+
const foundVersion = await waitForRelayVersion({ port: RELAY_PORT })
|
|
509
|
+
if (foundVersion) {
|
|
510
|
+
// A relay came up while we waited; only reuse the current version when
|
|
511
|
+
// it also exposes the feature contract expected by this build.
|
|
512
|
+
if (compareVersions(foundVersion, VERSION) > 0) {
|
|
513
|
+
return
|
|
514
|
+
}
|
|
515
|
+
if (foundVersion === VERSION) {
|
|
516
|
+
const features = await getRelayServerFeatures(RELAY_PORT)
|
|
517
|
+
if (supportsRelayFeatures({ features })) {
|
|
518
|
+
return
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
if (!restartOnVersionMismatch) {
|
|
522
|
+
return
|
|
523
|
+
}
|
|
524
|
+
const reason =
|
|
525
|
+
foundVersion === VERSION
|
|
526
|
+
? `CDP relay server is missing current features (${RELAY_FEATURES.join(', ')}), restarting...`
|
|
527
|
+
: `CDP relay server version mismatch (server: ${foundVersion}, client: ${VERSION}), restarting...`
|
|
528
|
+
logger?.log(pc.yellow(reason))
|
|
529
|
+
} else {
|
|
530
|
+
logger?.log(
|
|
531
|
+
pc.yellow(
|
|
532
|
+
`Port ${RELAY_PORT} is already in use (pid(s): ${listeningPids.join(', ')}). Attempting to stop the existing process...`,
|
|
533
|
+
),
|
|
534
|
+
)
|
|
535
|
+
}
|
|
536
|
+
await killRelayServer({ port: RELAY_PORT })
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
logger?.log(pc.dim('CDP relay server not running, starting it...'))
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// Detect if we're running from source (.ts) or compiled (.js)
|
|
543
|
+
// This handles: tsx, vite-node, ts-node, or direct node on compiled output
|
|
544
|
+
const isRunningFromSource = __filename.endsWith('.ts')
|
|
545
|
+
const scriptPath = isRunningFromSource
|
|
546
|
+
? path.resolve(__dirname, './start-relay-server.ts')
|
|
547
|
+
: path.resolve(__dirname, './start-relay-server.js')
|
|
548
|
+
|
|
549
|
+
const serverProcess = spawn(isRunningFromSource ? 'tsx' : process.execPath, [scriptPath], {
|
|
550
|
+
detached: true,
|
|
551
|
+
stdio: 'ignore',
|
|
552
|
+
env: { ...process.env, ...additionalEnv },
|
|
553
|
+
})
|
|
554
|
+
|
|
555
|
+
serverProcess.unref()
|
|
556
|
+
|
|
557
|
+
const startTimeoutMs = 5000
|
|
558
|
+
const startTime = Date.now()
|
|
559
|
+
|
|
560
|
+
const newVersion = await waitForCurrentRelayReadiness({
|
|
561
|
+
port: RELAY_PORT,
|
|
562
|
+
timeoutMs: startTimeoutMs,
|
|
563
|
+
})
|
|
564
|
+
if (newVersion && compareVersions(newVersion, VERSION) >= 0) {
|
|
565
|
+
logger?.log(pc.green('CDP relay server started successfully'))
|
|
566
|
+
await sleep(1000)
|
|
567
|
+
return true
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
const waitedMs = Date.now() - startTime
|
|
571
|
+
throw new Error(`Failed to start CDP relay server within ${waitedMs}ms. Check logs at: ${LOG_FILE_PATH}`)
|
|
572
|
+
}
|