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
package/src/mcp.ts
ADDED
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
import './env-compat.js'
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
4
|
+
import { z } from 'zod'
|
|
5
|
+
import fs from 'node:fs'
|
|
6
|
+
import path from 'node:path'
|
|
7
|
+
import util from 'node:util'
|
|
8
|
+
import { fileURLToPath } from 'node:url'
|
|
9
|
+
import { createRequire } from 'node:module'
|
|
10
|
+
|
|
11
|
+
// Prevent Buffers from dumping hex bytes in util.inspect output.
|
|
12
|
+
// Without this, returning a screenshot Buffer would log ~400+ chars of useless hex.
|
|
13
|
+
Buffer.prototype[util.inspect.custom] = function () {
|
|
14
|
+
return `<Buffer ${this.length} bytes>`
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
import dedent from 'string-dedent'
|
|
18
|
+
import { LOG_FILE_PATH, VERSION, parseRelayHost } from './utils.js'
|
|
19
|
+
import { ensureRelayServer, getLocalRelayHttpBaseUrl, RELAY_PORT } from './relay-client.js'
|
|
20
|
+
import { PlaywrightExecutor, CodeExecutionTimeoutError, type ExecuteResult } from './executor.js'
|
|
21
|
+
import { discoverChromeInstances, resolveDirectInput, appendSessionToWsUrl } from './chrome-discovery.js'
|
|
22
|
+
import crypto from 'node:crypto'
|
|
23
|
+
import {
|
|
24
|
+
listCapabilities,
|
|
25
|
+
readCapabilityScript,
|
|
26
|
+
routeCapabilities,
|
|
27
|
+
searchCapabilities,
|
|
28
|
+
toCapabilityContract,
|
|
29
|
+
toCapabilitySummary,
|
|
30
|
+
} from './capability-registry.js'
|
|
31
|
+
import { refreshCapabilityAuthWithExecutor } from './capability-auth.js'
|
|
32
|
+
import { runCapabilityWithExecutor, runNodeCapability } from './capability-runner.js'
|
|
33
|
+
|
|
34
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
35
|
+
const __dirname = path.dirname(__filename)
|
|
36
|
+
const require = createRequire(import.meta.url)
|
|
37
|
+
|
|
38
|
+
// Single executor instance for MCP (created lazily)
|
|
39
|
+
let executor: PlaywrightExecutor | null = null
|
|
40
|
+
|
|
41
|
+
interface RemoteConfig {
|
|
42
|
+
host: string
|
|
43
|
+
port: number
|
|
44
|
+
token?: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getRemoteConfig(): RemoteConfig | null {
|
|
48
|
+
const host = process.env.TABWRIGHT_HOST
|
|
49
|
+
if (!host) {
|
|
50
|
+
return null
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
host,
|
|
54
|
+
port: RELAY_PORT,
|
|
55
|
+
token: process.env.TABWRIGHT_TOKEN,
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function getLogServerUrl(): string {
|
|
60
|
+
const remote = getRemoteConfig()
|
|
61
|
+
if (remote) {
|
|
62
|
+
const { httpBaseUrl } = parseRelayHost(remote.host, remote.port)
|
|
63
|
+
return `${httpBaseUrl}/mcp-log`
|
|
64
|
+
}
|
|
65
|
+
return `http://127.0.0.1:${RELAY_PORT}/mcp-log`
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function sendLogToRelayServer(level: string, ...args: any[]) {
|
|
69
|
+
try {
|
|
70
|
+
const headers: Record<string, string> = { 'Content-Type': 'application/json' }
|
|
71
|
+
const token = process.env.TABWRIGHT_TOKEN
|
|
72
|
+
if (token) {
|
|
73
|
+
headers['Authorization'] = `Bearer ${token}`
|
|
74
|
+
}
|
|
75
|
+
await fetch(getLogServerUrl(), {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
headers,
|
|
78
|
+
body: JSON.stringify({ level, args }),
|
|
79
|
+
signal: AbortSignal.timeout(1000),
|
|
80
|
+
})
|
|
81
|
+
} catch {
|
|
82
|
+
// Silently fail if relay server is not available
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Log to both console.error (for early startup) and relay server log file.
|
|
88
|
+
* Fire-and-forget to avoid blocking.
|
|
89
|
+
*/
|
|
90
|
+
function mcpLog(...args: any[]) {
|
|
91
|
+
console.error(...args)
|
|
92
|
+
sendLogToRelayServer('log', ...args)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** MCP-specific logger for executor */
|
|
96
|
+
const mcpLogger = {
|
|
97
|
+
log: (...args: any[]) => mcpLog(...args),
|
|
98
|
+
error: (...args: any[]) => {
|
|
99
|
+
console.error(...args)
|
|
100
|
+
sendLogToRelayServer('error', ...args)
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function ensureRelayServerForMcp(): Promise<void> {
|
|
105
|
+
await ensureRelayServer({ logger: mcpLogger })
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Resolve direct CDP config from TABWRIGHT_DIRECT env var.
|
|
110
|
+
* - "auto" / "1" / "true": auto-discover Chrome on default port 9222
|
|
111
|
+
* - "ws://..." / "wss://...": use explicit WebSocket endpoint
|
|
112
|
+
* - "host:port": resolve to ws:// URL via HTTP probe + DevToolsActivePort fallback
|
|
113
|
+
*/
|
|
114
|
+
async function getDirectCdpConfig(): Promise<{ directCdpUrl: string } | null> {
|
|
115
|
+
const directEnv = process.env.TABWRIGHT_DIRECT
|
|
116
|
+
if (!directEnv) {
|
|
117
|
+
return null
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Auto-discover: check default port 9222
|
|
121
|
+
if (directEnv === '1') {
|
|
122
|
+
const instances = await discoverChromeInstances()
|
|
123
|
+
if (instances.length === 0) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
'TABWRIGHT_DIRECT is set but no Chrome found on port 9222. ' +
|
|
126
|
+
'Enable debugging at chrome://inspect/#remote-debugging or launch with --remote-debugging-port=9222.',
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
const sessionId = crypto.randomUUID()
|
|
130
|
+
const wsUrl = appendSessionToWsUrl(instances[0].wsUrl, sessionId)
|
|
131
|
+
mcpLog(`Direct CDP: using ${instances[0].browser} on port ${instances[0].port}`)
|
|
132
|
+
return { directCdpUrl: wsUrl }
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ws://, wss://, or host:port — resolveDirectInput handles all three
|
|
136
|
+
const resolved = await resolveDirectInput(directEnv)
|
|
137
|
+
const sessionId = crypto.randomUUID()
|
|
138
|
+
const directCdpUrl = appendSessionToWsUrl(resolved, sessionId)
|
|
139
|
+
mcpLog(`Direct CDP: resolved ${directEnv} → ${directCdpUrl}`)
|
|
140
|
+
return { directCdpUrl }
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function getOrCreateExecutor(): Promise<PlaywrightExecutor> {
|
|
144
|
+
if (executor) {
|
|
145
|
+
return executor
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Direct CDP mode takes priority over relay/remote
|
|
149
|
+
const directConfig = await getDirectCdpConfig()
|
|
150
|
+
if (directConfig) {
|
|
151
|
+
executor = new PlaywrightExecutor({
|
|
152
|
+
cdpConfig: directConfig,
|
|
153
|
+
logger: mcpLogger,
|
|
154
|
+
cwd: process.cwd(),
|
|
155
|
+
})
|
|
156
|
+
return executor
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const remote = getRemoteConfig()
|
|
160
|
+
if (!remote) {
|
|
161
|
+
await ensureRelayServerForMcp()
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Pass config instead of pre-generated URL so executor can generate unique URLs for each connection
|
|
165
|
+
const cdpConfig = remote || { host: await getLocalRelayHttpBaseUrl(RELAY_PORT), port: RELAY_PORT }
|
|
166
|
+
executor = new PlaywrightExecutor({
|
|
167
|
+
cdpConfig,
|
|
168
|
+
logger: mcpLogger,
|
|
169
|
+
cwd: process.cwd(),
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
return executor
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async function checkRemoteServer({ host, port }: { host: string; port: number }): Promise<void> {
|
|
176
|
+
const { httpBaseUrl } = parseRelayHost(host, port)
|
|
177
|
+
const versionUrl = `${httpBaseUrl}/version`
|
|
178
|
+
try {
|
|
179
|
+
const response = await fetch(versionUrl, { signal: AbortSignal.timeout(3000) })
|
|
180
|
+
if (!response.ok) {
|
|
181
|
+
throw new Error(`Server responded with status ${response.status}`)
|
|
182
|
+
}
|
|
183
|
+
} catch (error: any) {
|
|
184
|
+
const isConnectionError = error.cause?.code === 'ECONNREFUSED' || error.name === 'TimeoutError'
|
|
185
|
+
if (isConnectionError) {
|
|
186
|
+
throw new Error(
|
|
187
|
+
`Cannot connect to remote relay server at ${host}. ` +
|
|
188
|
+
`Make sure 'npx -y tabwright serve' is running on the host machine.`,
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
throw new Error(`Failed to connect to remote relay server: ${error.message}`)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const server = new McpServer({
|
|
196
|
+
name: 'tabwright',
|
|
197
|
+
title: 'The better playwright MCP: works as a browser extension. No context bloat. More capable.',
|
|
198
|
+
version: VERSION,
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
const promptContent =
|
|
202
|
+
fs.readFileSync(path.join(__dirname, '..', 'dist', 'prompt.md'), 'utf-8') +
|
|
203
|
+
`\n\nfor debugging internal Tabwright errors, check Tabwright relay server logs at: ${LOG_FILE_PATH}`
|
|
204
|
+
|
|
205
|
+
server.resource(
|
|
206
|
+
'debugger-api',
|
|
207
|
+
'https://playwriter.dev/resources/debugger-api.md',
|
|
208
|
+
{ mimeType: 'text/plain' },
|
|
209
|
+
async () => {
|
|
210
|
+
const packageJsonPath = require.resolve('tabwright/package.json')
|
|
211
|
+
const packageDir = path.dirname(packageJsonPath)
|
|
212
|
+
const content = fs.readFileSync(path.join(packageDir, 'dist', 'debugger-api.md'), 'utf-8')
|
|
213
|
+
return {
|
|
214
|
+
contents: [{ uri: 'https://playwriter.dev/resources/debugger-api.md', text: content, mimeType: 'text/plain' }],
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
server.resource(
|
|
220
|
+
'editor-api',
|
|
221
|
+
'https://playwriter.dev/resources/editor-api.md',
|
|
222
|
+
{ mimeType: 'text/plain' },
|
|
223
|
+
async () => {
|
|
224
|
+
const packageJsonPath = require.resolve('tabwright/package.json')
|
|
225
|
+
const packageDir = path.dirname(packageJsonPath)
|
|
226
|
+
const content = fs.readFileSync(path.join(packageDir, 'dist', 'editor-api.md'), 'utf-8')
|
|
227
|
+
return {
|
|
228
|
+
contents: [{ uri: 'https://playwriter.dev/resources/editor-api.md', text: content, mimeType: 'text/plain' }],
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
server.resource(
|
|
234
|
+
'styles-api',
|
|
235
|
+
'https://playwriter.dev/resources/styles-api.md',
|
|
236
|
+
{ mimeType: 'text/plain' },
|
|
237
|
+
async () => {
|
|
238
|
+
const packageJsonPath = require.resolve('tabwright/package.json')
|
|
239
|
+
const packageDir = path.dirname(packageJsonPath)
|
|
240
|
+
const content = fs.readFileSync(path.join(packageDir, 'dist', 'styles-api.md'), 'utf-8')
|
|
241
|
+
return {
|
|
242
|
+
contents: [{ uri: 'https://playwriter.dev/resources/styles-api.md', text: content, mimeType: 'text/plain' }],
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
server.resource(
|
|
248
|
+
'capabilities',
|
|
249
|
+
'tabwright://capabilities',
|
|
250
|
+
{ mimeType: 'application/json' },
|
|
251
|
+
async () => {
|
|
252
|
+
const capabilities = listCapabilities({ cwd: process.cwd() }).map(toCapabilityContract)
|
|
253
|
+
return {
|
|
254
|
+
contents: [
|
|
255
|
+
{
|
|
256
|
+
uri: 'tabwright://capabilities',
|
|
257
|
+
text: JSON.stringify(capabilities, null, 2),
|
|
258
|
+
mimeType: 'application/json',
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
function executeResultToMcpContent(options: {
|
|
266
|
+
result: ExecuteResult
|
|
267
|
+
prefix?: string
|
|
268
|
+
}): Array<{ type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string }> {
|
|
269
|
+
const MAX_TEXT = 10000
|
|
270
|
+
let text = options.prefix ? `${options.prefix}\n\n${options.result.text}` : options.result.text
|
|
271
|
+
for (const s of options.result.screenshots) {
|
|
272
|
+
text += `\nScreenshot saved to: ${s.path} (image included below, ${s.labelCount} labels)\n`
|
|
273
|
+
text += `Accessibility snapshot:\n${s.snapshot}\n`
|
|
274
|
+
}
|
|
275
|
+
if (text.length > MAX_TEXT) {
|
|
276
|
+
text = text.slice(0, MAX_TEXT) + '\n\n[Truncated]'
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const content: Array<{ type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string }> = [
|
|
280
|
+
{ type: 'text', text },
|
|
281
|
+
]
|
|
282
|
+
for (const image of options.result.images) {
|
|
283
|
+
content.push({ type: 'image', data: image.data, mimeType: image.mimeType })
|
|
284
|
+
}
|
|
285
|
+
return content
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
server.tool(
|
|
289
|
+
'capability',
|
|
290
|
+
dedent`
|
|
291
|
+
Search, inspect, refresh auth for, or run saved Tabwright capabilities. Capabilities are reusable Tabwright scripts with AI-readable contracts, local secrets, and run logs.
|
|
292
|
+
|
|
293
|
+
For concrete user tasks, use action "route" first. If it returns an exact-match direct-run capability, use action "run" directly with the returned input; do not search, describe, or open a page first.
|
|
294
|
+
Capability ids are not shell commands. If route output includes shellCommand, use that exact Tabwright CLI command; never run the capability id directly as a binary.
|
|
295
|
+
Do not treat every URL as a direct-run signal. If "route" returns no match, use action "search" before creating new browser code and action "describe" before running. Only use action "refresh_auth" after explicit user confirmation because it updates local credentials.
|
|
296
|
+
If a capability operation requires confirmation, stop and ask the user. Only after explicit approval may you retry action "run" with confirmation set to the operation's exact confirmationToken. force never bypasses this gate.
|
|
297
|
+
`,
|
|
298
|
+
{
|
|
299
|
+
action: z.enum(['list', 'route', 'search', 'show', 'describe', 'run', 'refresh_auth']).describe('Capability action'),
|
|
300
|
+
id: z.string().optional().describe('Capability id for show/describe/run/refresh_auth'),
|
|
301
|
+
query: z.string().optional().describe('Task or URL for action "route"; search query for action "search"'),
|
|
302
|
+
limit: z.number().default(10).describe('Maximum number of search results'),
|
|
303
|
+
input: z.record(z.string(), z.unknown()).optional().describe('JSON input for run'),
|
|
304
|
+
force: z.boolean().optional().describe('Run draft capabilities or bypass URL match checks'),
|
|
305
|
+
confirmation: z.string().optional().describe('Exact confirmationToken, supplied only after explicit user approval'),
|
|
306
|
+
timeout: z.number().default(10000).describe('Timeout in milliseconds'),
|
|
307
|
+
},
|
|
308
|
+
async ({ action, id, query, limit, input, force, confirmation, timeout }) => {
|
|
309
|
+
try {
|
|
310
|
+
if (action === 'list') {
|
|
311
|
+
return {
|
|
312
|
+
content: [
|
|
313
|
+
{
|
|
314
|
+
type: 'text',
|
|
315
|
+
text: JSON.stringify(listCapabilities({ cwd: process.cwd() }).map(toCapabilityContract), null, 2),
|
|
316
|
+
},
|
|
317
|
+
],
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (action === 'search') {
|
|
322
|
+
return {
|
|
323
|
+
content: [
|
|
324
|
+
{
|
|
325
|
+
type: 'text',
|
|
326
|
+
text: JSON.stringify(
|
|
327
|
+
searchCapabilities({ query: query || '', cwd: process.cwd(), limit }).map((result) => {
|
|
328
|
+
return {
|
|
329
|
+
score: result.score,
|
|
330
|
+
reasons: result.reasons,
|
|
331
|
+
...toCapabilityContract(result.capability),
|
|
332
|
+
}
|
|
333
|
+
}),
|
|
334
|
+
null,
|
|
335
|
+
2,
|
|
336
|
+
),
|
|
337
|
+
},
|
|
338
|
+
],
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (action === 'route') {
|
|
343
|
+
return {
|
|
344
|
+
content: [
|
|
345
|
+
{
|
|
346
|
+
type: 'text',
|
|
347
|
+
text: JSON.stringify(
|
|
348
|
+
routeCapabilities({ task: query || '', cwd: process.cwd(), limit }).map((route) => {
|
|
349
|
+
return {
|
|
350
|
+
shellCommand: route.shellCommand,
|
|
351
|
+
command: route.command,
|
|
352
|
+
capabilityId: route.capability.manifest.id,
|
|
353
|
+
operation: route.operation,
|
|
354
|
+
id: route.capability.manifest.id,
|
|
355
|
+
title: route.capability.manifest.title,
|
|
356
|
+
location: route.capability.location,
|
|
357
|
+
routingHint:
|
|
358
|
+
route.operation === undefined
|
|
359
|
+
? route.capability.manifest.routingHint
|
|
360
|
+
: route.capability.manifest.operations[route.operation]?.routingHint,
|
|
361
|
+
input: route.input,
|
|
362
|
+
commandWarning: route.commandWarning,
|
|
363
|
+
executionHint: route.executionHint,
|
|
364
|
+
reasons: route.reasons,
|
|
365
|
+
matchedText: route.matchedText,
|
|
366
|
+
}
|
|
367
|
+
}),
|
|
368
|
+
null,
|
|
369
|
+
2,
|
|
370
|
+
),
|
|
371
|
+
},
|
|
372
|
+
],
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (!id) {
|
|
377
|
+
return { content: [{ type: 'text', text: 'id is required' }], isError: true }
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (action === 'show' || action === 'describe') {
|
|
381
|
+
const capability = listCapabilities({ cwd: process.cwd() }).find((candidate) => {
|
|
382
|
+
return candidate.manifest.id === id
|
|
383
|
+
})
|
|
384
|
+
if (!capability) {
|
|
385
|
+
return { content: [{ type: 'text', text: `Capability not found: ${id}` }], isError: true }
|
|
386
|
+
}
|
|
387
|
+
return {
|
|
388
|
+
content: [
|
|
389
|
+
{
|
|
390
|
+
type: 'text',
|
|
391
|
+
text: JSON.stringify(
|
|
392
|
+
{
|
|
393
|
+
...(action === 'describe' ? toCapabilityContract(capability) : toCapabilitySummary(capability)),
|
|
394
|
+
...(action === 'show' ? { script: readCapabilityScript({ id, cwd: process.cwd() }) } : {}),
|
|
395
|
+
},
|
|
396
|
+
null,
|
|
397
|
+
2,
|
|
398
|
+
),
|
|
399
|
+
},
|
|
400
|
+
],
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (action === 'refresh_auth') {
|
|
405
|
+
const exec = await getOrCreateExecutor()
|
|
406
|
+
const result = await refreshCapabilityAuthWithExecutor({
|
|
407
|
+
executor: exec,
|
|
408
|
+
id,
|
|
409
|
+
cwd: process.cwd(),
|
|
410
|
+
timeout,
|
|
411
|
+
})
|
|
412
|
+
return {
|
|
413
|
+
content: [
|
|
414
|
+
{
|
|
415
|
+
type: 'text',
|
|
416
|
+
text: JSON.stringify(
|
|
417
|
+
{
|
|
418
|
+
capability: result.capability.manifest.id,
|
|
419
|
+
saved: result.saved,
|
|
420
|
+
secretKey: result.secretKey,
|
|
421
|
+
cookieCount: result.cookieCount,
|
|
422
|
+
cookieNames: result.cookieNames,
|
|
423
|
+
urls: result.urls,
|
|
424
|
+
expiresAt: result.expiresAt,
|
|
425
|
+
path: result.path,
|
|
426
|
+
},
|
|
427
|
+
null,
|
|
428
|
+
2,
|
|
429
|
+
),
|
|
430
|
+
},
|
|
431
|
+
],
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const capability = listCapabilities({ cwd: process.cwd() }).find((candidate) => {
|
|
436
|
+
return candidate.manifest.id === id
|
|
437
|
+
})
|
|
438
|
+
if (!capability) {
|
|
439
|
+
return { content: [{ type: 'text', text: `Capability not found: ${id}` }], isError: true }
|
|
440
|
+
}
|
|
441
|
+
if (capability.manifest.runtime === 'node') {
|
|
442
|
+
const result = await runNodeCapability({
|
|
443
|
+
id,
|
|
444
|
+
input: input || {},
|
|
445
|
+
cwd: process.cwd(),
|
|
446
|
+
timeout,
|
|
447
|
+
force,
|
|
448
|
+
confirmation,
|
|
449
|
+
})
|
|
450
|
+
return {
|
|
451
|
+
content: [
|
|
452
|
+
{
|
|
453
|
+
type: 'text',
|
|
454
|
+
text: `Capability output:\n${JSON.stringify(result.output, null, 2)}\n\n${result.text}`,
|
|
455
|
+
},
|
|
456
|
+
],
|
|
457
|
+
isError: result.isError,
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const exec = await getOrCreateExecutor()
|
|
462
|
+
const result = await runCapabilityWithExecutor({
|
|
463
|
+
executor: exec,
|
|
464
|
+
id,
|
|
465
|
+
input: input || {},
|
|
466
|
+
cwd: process.cwd(),
|
|
467
|
+
timeout,
|
|
468
|
+
force,
|
|
469
|
+
confirmation,
|
|
470
|
+
})
|
|
471
|
+
return {
|
|
472
|
+
content: executeResultToMcpContent({
|
|
473
|
+
result: result.executeResult,
|
|
474
|
+
prefix: `Capability output:\n${JSON.stringify(result.output, null, 2)}`,
|
|
475
|
+
}),
|
|
476
|
+
isError: result.executeResult.isError,
|
|
477
|
+
}
|
|
478
|
+
} catch (error) {
|
|
479
|
+
const errorStack = error instanceof Error ? error.stack || error.message : String(error)
|
|
480
|
+
console.error('Error in capability tool:', errorStack)
|
|
481
|
+
return {
|
|
482
|
+
content: [{ type: 'text', text: `Error running capability: ${error instanceof Error ? error.message : String(error)}` }],
|
|
483
|
+
isError: true,
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
},
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
server.tool(
|
|
490
|
+
'execute',
|
|
491
|
+
promptContent,
|
|
492
|
+
{
|
|
493
|
+
code: z
|
|
494
|
+
.string()
|
|
495
|
+
.describe(
|
|
496
|
+
'js playwright code, has {page, state, context} in scope. Should be one line, using ; to execute multiple statements. you MUST call execute multiple times instead of writing complex scripts in a single tool call.',
|
|
497
|
+
),
|
|
498
|
+
timeout: z.number().default(10000).describe('Timeout in milliseconds for code execution (default: 10000ms)'),
|
|
499
|
+
},
|
|
500
|
+
async ({ code, timeout }) => {
|
|
501
|
+
try {
|
|
502
|
+
// Check relay server on every execute to auto-recover from crashes
|
|
503
|
+
// (skip in direct CDP mode — no relay involved)
|
|
504
|
+
if (!process.env.TABWRIGHT_DIRECT) {
|
|
505
|
+
const remote = getRemoteConfig()
|
|
506
|
+
if (!remote) {
|
|
507
|
+
await ensureRelayServerForMcp()
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const exec = await getOrCreateExecutor()
|
|
512
|
+
const result = await exec.execute(code, timeout)
|
|
513
|
+
|
|
514
|
+
// Transform executor result to MCP format
|
|
515
|
+
// Append screenshot metadata to text for MCP (image is included inline as content)
|
|
516
|
+
const MAX_TEXT = 10000
|
|
517
|
+
let text = result.text
|
|
518
|
+
for (const s of result.screenshots) {
|
|
519
|
+
text += `\nScreenshot saved to: ${s.path} (image included below, ${s.labelCount} labels)\n`
|
|
520
|
+
text += `Accessibility snapshot:\n${s.snapshot}\n`
|
|
521
|
+
}
|
|
522
|
+
if (text.length > MAX_TEXT) {
|
|
523
|
+
text = text.slice(0, MAX_TEXT) + '\n\n[Truncated]'
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const content: Array<{ type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string }> = [
|
|
527
|
+
{ type: 'text', text },
|
|
528
|
+
]
|
|
529
|
+
|
|
530
|
+
for (const image of result.images) {
|
|
531
|
+
content.push({ type: 'image', data: image.data, mimeType: image.mimeType })
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (result.isError) {
|
|
535
|
+
return { content, isError: true }
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
return { content }
|
|
539
|
+
} catch (error: any) {
|
|
540
|
+
const errorStack = error.stack || error.message
|
|
541
|
+
const isTimeoutError =
|
|
542
|
+
error instanceof CodeExecutionTimeoutError || error?.name === 'TimeoutError' || error?.name === 'AbortError'
|
|
543
|
+
|
|
544
|
+
console.error('Error in execute tool:', errorStack)
|
|
545
|
+
if (!isTimeoutError) {
|
|
546
|
+
sendLogToRelayServer('error', 'Error in execute tool:', errorStack)
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const resetHint = isTimeoutError
|
|
550
|
+
? ''
|
|
551
|
+
: '\n\n[HINT: If this is an internal Playwright error, page/browser closed, or connection issue, call the `reset` tool to reconnect. Do NOT reset for other non-connection non-internal errors.]'
|
|
552
|
+
|
|
553
|
+
// timeout stacks are internal noise (Promise.race / setTimeout); only show the message
|
|
554
|
+
const errorText = isTimeoutError ? error.message : errorStack
|
|
555
|
+
return {
|
|
556
|
+
content: [{ type: 'text', text: `Error executing code: ${errorText}${resetHint}` }],
|
|
557
|
+
isError: true,
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
},
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
server.tool(
|
|
564
|
+
'reset',
|
|
565
|
+
dedent`
|
|
566
|
+
Recreates the CDP connection and resets the browser/page/context. Use this when the MCP stops responding, you get connection errors, if there are no pages in context, assertion failures, page closed, or other issues.
|
|
567
|
+
|
|
568
|
+
After calling this tool, the page and context variables are automatically updated in the execution environment.
|
|
569
|
+
|
|
570
|
+
This tools also removes any custom properties you may have added to the global scope AND clearing all keys from the \`state\` object. Only \`page\`, \`context\`, \`state\` (empty), \`console\`, and utility functions will remain.
|
|
571
|
+
|
|
572
|
+
if playwright always returns all pages as about:blank urls and evaluate does not work you should ask the user to restart Chrome. This is a known Chrome bug.
|
|
573
|
+
`,
|
|
574
|
+
{},
|
|
575
|
+
async () => {
|
|
576
|
+
try {
|
|
577
|
+
// Check relay server to auto-recover from crashes
|
|
578
|
+
// (skip in direct CDP mode — no relay involved)
|
|
579
|
+
if (!process.env.TABWRIGHT_DIRECT) {
|
|
580
|
+
const remote = getRemoteConfig()
|
|
581
|
+
if (!remote) {
|
|
582
|
+
await ensureRelayServerForMcp()
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
const exec = await getOrCreateExecutor()
|
|
587
|
+
const { page, context } = await exec.reset()
|
|
588
|
+
const pagesCount = context.pages().length
|
|
589
|
+
return {
|
|
590
|
+
content: [
|
|
591
|
+
{
|
|
592
|
+
type: 'text',
|
|
593
|
+
text: `Connection reset successfully. ${pagesCount} page(s) available. Current page URL: ${page.url()}`,
|
|
594
|
+
},
|
|
595
|
+
],
|
|
596
|
+
}
|
|
597
|
+
} catch (error: any) {
|
|
598
|
+
return {
|
|
599
|
+
content: [{ type: 'text', text: `Failed to reset connection: ${error.message}` }],
|
|
600
|
+
isError: true,
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
},
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
export async function startMcp(options: { host?: string; token?: string } = {}) {
|
|
607
|
+
if (options.host) {
|
|
608
|
+
process.env.TABWRIGHT_HOST = options.host
|
|
609
|
+
}
|
|
610
|
+
if (options.token) {
|
|
611
|
+
process.env.TABWRIGHT_TOKEN = options.token
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// In direct CDP mode (TABWRIGHT_DIRECT env var), no relay server needed
|
|
615
|
+
if (process.env.TABWRIGHT_DIRECT) {
|
|
616
|
+
mcpLog(`Using direct CDP connection: ${process.env.TABWRIGHT_DIRECT}`)
|
|
617
|
+
} else {
|
|
618
|
+
const remote = getRemoteConfig()
|
|
619
|
+
if (!remote) {
|
|
620
|
+
await ensureRelayServerForMcp()
|
|
621
|
+
} else {
|
|
622
|
+
mcpLog(`Using remote CDP relay server: ${remote.host}`)
|
|
623
|
+
await checkRemoteServer(remote)
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
const transport = new StdioServerTransport()
|
|
628
|
+
await server.connect(transport)
|
|
629
|
+
}
|