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,179 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import os from 'node:os'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { chromium } from '@xmorse/playwright-core'
|
|
5
|
+
import { findInstalledChrome } from './browser-install.js'
|
|
6
|
+
// NOTE: browser-config uses chromium synchronously (executablePath) during browser start.
|
|
7
|
+
// Patchright mode only affects connectOverCDP in executor.ts, not browser binary lookup.
|
|
8
|
+
|
|
9
|
+
type BrowserLookupOptions = {
|
|
10
|
+
browserPath?: string
|
|
11
|
+
env?: NodeJS.ProcessEnv
|
|
12
|
+
platform?: NodeJS.Platform
|
|
13
|
+
homeDir?: string
|
|
14
|
+
existsSync?: (filePath: string) => boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function expandHomeDirectory({ filePath, homeDir }: { filePath: string; homeDir: string }): string {
|
|
18
|
+
if (!filePath.startsWith('~/')) {
|
|
19
|
+
return filePath
|
|
20
|
+
}
|
|
21
|
+
return path.join(homeDir, filePath.slice(2))
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function dedupePaths(pathsToCheck: string[]): string[] {
|
|
25
|
+
return Array.from(new Set(pathsToCheck))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getPlaywrightChromiumCandidate(): string[] {
|
|
29
|
+
const executablePath = chromium.executablePath()
|
|
30
|
+
if (!executablePath) {
|
|
31
|
+
return []
|
|
32
|
+
}
|
|
33
|
+
return [executablePath]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function getPathEntries(env: NodeJS.ProcessEnv): string[] {
|
|
37
|
+
const rawPath = env.PATH || env.Path || ''
|
|
38
|
+
return rawPath
|
|
39
|
+
.split(path.delimiter)
|
|
40
|
+
.filter(Boolean)
|
|
41
|
+
.map((entry) => {
|
|
42
|
+
return entry.trim()
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getExecutableNames(platform: NodeJS.Platform): string[] {
|
|
47
|
+
if (platform === 'win32') {
|
|
48
|
+
return ['chrome.exe', 'chromium.exe']
|
|
49
|
+
}
|
|
50
|
+
return ['chrome', 'chromium', 'chromium-browser']
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getPathExecutableCandidates({
|
|
54
|
+
platform,
|
|
55
|
+
env,
|
|
56
|
+
}: {
|
|
57
|
+
platform: NodeJS.Platform
|
|
58
|
+
env: NodeJS.ProcessEnv
|
|
59
|
+
}): string[] {
|
|
60
|
+
const executableNames = getExecutableNames(platform)
|
|
61
|
+
return getPathEntries(env).flatMap((entry) => {
|
|
62
|
+
return executableNames.map((name) => {
|
|
63
|
+
return path.join(entry, name)
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function getBrowserExecutableCandidates({
|
|
69
|
+
platform = os.platform(),
|
|
70
|
+
env = process.env,
|
|
71
|
+
homeDir = os.homedir(),
|
|
72
|
+
}: Omit<BrowserLookupOptions, 'browserPath' | 'existsSync'> = {}): string[] {
|
|
73
|
+
const platformCandidates = (() => {
|
|
74
|
+
if (platform === 'darwin') {
|
|
75
|
+
return [
|
|
76
|
+
'/Applications/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing',
|
|
77
|
+
'~/Applications/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing',
|
|
78
|
+
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
|
79
|
+
'~/Applications/Chromium.app/Contents/MacOS/Chromium',
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (platform === 'win32') {
|
|
84
|
+
const localAppData = env.LOCALAPPDATA || ''
|
|
85
|
+
const programFiles = env.PROGRAMFILES || 'C:\\Program Files'
|
|
86
|
+
const programFilesX86 = env['PROGRAMFILES(X86)'] || 'C:\\Program Files (x86)'
|
|
87
|
+
|
|
88
|
+
return [
|
|
89
|
+
path.join(programFiles, 'Google', 'Chrome for Testing', 'Application', 'chrome.exe'),
|
|
90
|
+
path.join(programFilesX86, 'Google', 'Chrome for Testing', 'Application', 'chrome.exe'),
|
|
91
|
+
path.join(localAppData, 'Google', 'Chrome for Testing', 'Application', 'chrome.exe'),
|
|
92
|
+
path.join(programFiles, 'Chromium', 'Application', 'chromium.exe'),
|
|
93
|
+
path.join(programFilesX86, 'Chromium', 'Application', 'chromium.exe'),
|
|
94
|
+
path.join(localAppData, 'Chromium', 'Application', 'chromium.exe'),
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return [
|
|
99
|
+
'/opt/google/chrome-for-testing/chrome',
|
|
100
|
+
'/usr/local/bin/chrome',
|
|
101
|
+
'/usr/bin/chrome',
|
|
102
|
+
'/usr/bin/chromium',
|
|
103
|
+
'/usr/bin/chromium-browser',
|
|
104
|
+
'/snap/bin/chromium',
|
|
105
|
+
]
|
|
106
|
+
})()
|
|
107
|
+
|
|
108
|
+
const pathCandidates = getPathExecutableCandidates({ platform, env })
|
|
109
|
+
return dedupePaths(
|
|
110
|
+
[...platformCandidates, ...pathCandidates, ...getPlaywrightChromiumCandidate()].map((filePath) => {
|
|
111
|
+
return expandHomeDirectory({ filePath, homeDir })
|
|
112
|
+
}),
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function resolveBrowserExecutablePath({
|
|
117
|
+
browserPath,
|
|
118
|
+
env = process.env,
|
|
119
|
+
platform = os.platform(),
|
|
120
|
+
homeDir = os.homedir(),
|
|
121
|
+
existsSync = fs.existsSync,
|
|
122
|
+
}: BrowserLookupOptions = {}): string {
|
|
123
|
+
const explicitPath = browserPath?.trim()
|
|
124
|
+
if (explicitPath) {
|
|
125
|
+
const resolvedExplicitPath = expandHomeDirectory({ filePath: explicitPath, homeDir })
|
|
126
|
+
if (!existsSync(resolvedExplicitPath)) {
|
|
127
|
+
throw new Error(`Browser binary not found at: ${resolvedExplicitPath}`)
|
|
128
|
+
}
|
|
129
|
+
return resolvedExplicitPath
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const envPath = env.TABWRIGHT_BROWSER_PATH?.trim() || env.PLAYWRITER_BROWSER_PATH?.trim()
|
|
133
|
+
if (envPath) {
|
|
134
|
+
const resolvedEnvPath = expandHomeDirectory({ filePath: envPath, homeDir })
|
|
135
|
+
if (!existsSync(resolvedEnvPath)) {
|
|
136
|
+
throw new Error(`TABWRIGHT_BROWSER_PATH does not exist: ${resolvedEnvPath}`)
|
|
137
|
+
}
|
|
138
|
+
return resolvedEnvPath
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Check Chrome downloaded by `tabwright browser install` first (highest priority)
|
|
142
|
+
const installedChrome = findInstalledChrome()
|
|
143
|
+
if (installedChrome) {
|
|
144
|
+
return installedChrome
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const candidates = getBrowserExecutableCandidates({ platform, env, homeDir })
|
|
148
|
+
const resolvedPath = candidates.find((candidate) => {
|
|
149
|
+
return existsSync(candidate)
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
if (resolvedPath) {
|
|
153
|
+
return resolvedPath
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const searchedPathsText = candidates.map((candidate) => {
|
|
157
|
+
return `- ${candidate}`
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
throw new Error(
|
|
161
|
+
'Could not find a supported browser binary. Run `tabwright browser install` to download Chrome for Testing, or pass a binary path.' +
|
|
162
|
+
`\n\nSearched paths:\n- ~/.tabwright/browsers/ (tabwright browser install)\n${searchedPathsText.join('\n')}`,
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function shouldUseHeadlessByDefault({
|
|
167
|
+
platform = os.platform(),
|
|
168
|
+
env = process.env,
|
|
169
|
+
}: Omit<BrowserLookupOptions, 'browserPath' | 'homeDir' | 'existsSync'> = {}): boolean {
|
|
170
|
+
if (platform !== 'linux') {
|
|
171
|
+
return false
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return !env.DISPLAY && !env.WAYLAND_DISPLAY
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function getBrowserExecutablePath(browserPath?: string): string {
|
|
178
|
+
return resolveBrowserExecutablePath({ browserPath })
|
|
179
|
+
}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Download and install Chrome for Testing into ~/.tabwright/browsers/.
|
|
3
|
+
* Similar to agent-browser's install command: fetches the latest stable
|
|
4
|
+
* Chrome for Testing build from Google's official automation channel.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from 'node:fs'
|
|
8
|
+
import os from 'node:os'
|
|
9
|
+
import path from 'node:path'
|
|
10
|
+
import { pipeline } from 'node:stream/promises'
|
|
11
|
+
import { Readable } from 'node:stream'
|
|
12
|
+
import { execFileSync } from 'node:child_process'
|
|
13
|
+
import { getTabwrightUserDataDir } from './product-paths.js'
|
|
14
|
+
|
|
15
|
+
const LAST_KNOWN_GOOD_URL =
|
|
16
|
+
'https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions-with-downloads.json'
|
|
17
|
+
|
|
18
|
+
export function getBrowsersDir(): string {
|
|
19
|
+
return path.join(getTabwrightUserDataDir(), 'browsers')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getPlatformKey(): string {
|
|
23
|
+
const platform = os.platform()
|
|
24
|
+
const arch = os.arch()
|
|
25
|
+
if (platform === 'darwin' && arch === 'arm64') {
|
|
26
|
+
return 'mac-arm64'
|
|
27
|
+
}
|
|
28
|
+
if (platform === 'darwin' && arch === 'x64') {
|
|
29
|
+
return 'mac-x64'
|
|
30
|
+
}
|
|
31
|
+
if (platform === 'linux' && arch === 'x64') {
|
|
32
|
+
return 'linux64'
|
|
33
|
+
}
|
|
34
|
+
if (platform === 'win32' && arch === 'x64') {
|
|
35
|
+
return 'win64'
|
|
36
|
+
}
|
|
37
|
+
throw new Error(
|
|
38
|
+
`Unsupported platform for Chrome for Testing download: ${platform}/${arch}. ` +
|
|
39
|
+
`Install Chromium manually and use TABWRIGHT_BROWSER_PATH to point to it.`,
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Find the Chrome binary inside a downloaded Chrome for Testing directory.
|
|
45
|
+
*/
|
|
46
|
+
function findChromeBinaryInDir(dir: string): string | null {
|
|
47
|
+
const platform = os.platform()
|
|
48
|
+
|
|
49
|
+
if (platform === 'darwin') {
|
|
50
|
+
const candidates = [
|
|
51
|
+
path.join(dir, 'Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing'),
|
|
52
|
+
path.join(dir, `chrome-${getPlatformKey()}/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing`),
|
|
53
|
+
]
|
|
54
|
+
for (const candidate of candidates) {
|
|
55
|
+
if (fs.existsSync(candidate)) {
|
|
56
|
+
return candidate
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (platform === 'linux') {
|
|
62
|
+
const candidates = [
|
|
63
|
+
path.join(dir, 'chrome'),
|
|
64
|
+
path.join(dir, 'chrome-linux64/chrome'),
|
|
65
|
+
]
|
|
66
|
+
for (const candidate of candidates) {
|
|
67
|
+
if (fs.existsSync(candidate)) {
|
|
68
|
+
return candidate
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (platform === 'win32') {
|
|
74
|
+
const candidates = [
|
|
75
|
+
path.join(dir, 'chrome.exe'),
|
|
76
|
+
path.join(dir, 'chrome-win64/chrome.exe'),
|
|
77
|
+
]
|
|
78
|
+
for (const candidate of candidates) {
|
|
79
|
+
if (fs.existsSync(candidate)) {
|
|
80
|
+
return candidate
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return null
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Find Chrome installed by `tabwright browser install` in ~/.tabwright/browsers/.
|
|
90
|
+
* Returns the path to the Chrome binary, or null if not found.
|
|
91
|
+
*/
|
|
92
|
+
export function findInstalledChrome(): string | null {
|
|
93
|
+
const browsersDir = getBrowsersDir()
|
|
94
|
+
if (!fs.existsSync(browsersDir)) {
|
|
95
|
+
return null
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const entries = fs.readdirSync(browsersDir, { withFileTypes: true })
|
|
99
|
+
.filter((e) => {
|
|
100
|
+
return e.isDirectory() && e.name.startsWith('chrome-')
|
|
101
|
+
})
|
|
102
|
+
.sort((a, b) => {
|
|
103
|
+
// Sort descending so newest version is first
|
|
104
|
+
return b.name.localeCompare(a.name)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
for (const entry of entries) {
|
|
108
|
+
const dir = path.join(browsersDir, entry.name)
|
|
109
|
+
const binary = findChromeBinaryInDir(dir)
|
|
110
|
+
if (binary) {
|
|
111
|
+
return binary
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return null
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface VersionInfo {
|
|
119
|
+
version: string
|
|
120
|
+
downloadUrl: string
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function fetchDownloadUrl(): Promise<VersionInfo> {
|
|
124
|
+
const response = await fetch(LAST_KNOWN_GOOD_URL, {
|
|
125
|
+
signal: AbortSignal.timeout(30000),
|
|
126
|
+
})
|
|
127
|
+
if (!response.ok) {
|
|
128
|
+
throw new Error(`Failed to fetch Chrome version info: HTTP ${response.status}`)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const data = (await response.json()) as {
|
|
132
|
+
channels: {
|
|
133
|
+
Stable: {
|
|
134
|
+
version: string
|
|
135
|
+
downloads: {
|
|
136
|
+
chrome: Array<{ platform: string; url: string }>
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const channel = data.channels?.Stable
|
|
143
|
+
if (!channel) {
|
|
144
|
+
throw new Error('No Stable channel found in Chrome for Testing version info')
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const platformKey = getPlatformKey()
|
|
148
|
+
const download = channel.downloads?.chrome?.find((d) => {
|
|
149
|
+
return d.platform === platformKey
|
|
150
|
+
})
|
|
151
|
+
if (!download) {
|
|
152
|
+
throw new Error(`No Chrome for Testing download found for platform: ${platformKey}`)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return { version: channel.version, downloadUrl: download.url }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function downloadFile(url: string, destPath: string): Promise<void> {
|
|
159
|
+
// 5-minute timeout per attempt, 3 retries for transient errors
|
|
160
|
+
const maxAttempts = 3
|
|
161
|
+
let lastError: Error | null = null
|
|
162
|
+
|
|
163
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
164
|
+
try {
|
|
165
|
+
await downloadFileAttempt(url, destPath)
|
|
166
|
+
return
|
|
167
|
+
} catch (error) {
|
|
168
|
+
lastError = error instanceof Error ? error : new Error(String(error))
|
|
169
|
+
// Don't retry on 4xx (permanent errors)
|
|
170
|
+
if (lastError.message.includes('HTTP 4')) {
|
|
171
|
+
throw lastError
|
|
172
|
+
}
|
|
173
|
+
if (attempt < maxAttempts) {
|
|
174
|
+
const delay = attempt * 2000
|
|
175
|
+
console.log(` Download attempt ${attempt} failed, retrying in ${delay / 1000}s...`)
|
|
176
|
+
await new Promise((resolve) => {
|
|
177
|
+
return setTimeout(resolve, delay)
|
|
178
|
+
})
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
throw lastError!
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function downloadFileAttempt(url: string, destPath: string): Promise<void> {
|
|
186
|
+
const response = await fetch(url, {
|
|
187
|
+
signal: AbortSignal.timeout(300_000), // 5 minute timeout
|
|
188
|
+
})
|
|
189
|
+
if (!response.ok) {
|
|
190
|
+
throw new Error(`Download failed: HTTP ${response.status} for ${url}`)
|
|
191
|
+
}
|
|
192
|
+
if (!response.body) {
|
|
193
|
+
throw new Error('Download failed: no response body')
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const totalBytes = Number(response.headers.get('content-length') || 0)
|
|
197
|
+
let downloadedBytes = 0
|
|
198
|
+
let lastPct = 0
|
|
199
|
+
|
|
200
|
+
const progressStream = new TransformStream({
|
|
201
|
+
transform(chunk, controller) {
|
|
202
|
+
downloadedBytes += chunk.byteLength
|
|
203
|
+
if (totalBytes > 0) {
|
|
204
|
+
const pct = Math.floor((downloadedBytes / totalBytes) * 100)
|
|
205
|
+
if (pct >= lastPct + 5) {
|
|
206
|
+
lastPct = pct
|
|
207
|
+
const mb = (downloadedBytes / 1_048_576).toFixed(0)
|
|
208
|
+
const totalMb = (totalBytes / 1_048_576).toFixed(0)
|
|
209
|
+
process.stderr.write(`\r ${mb}/${totalMb} MB (${pct}%)`)
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
controller.enqueue(chunk)
|
|
213
|
+
},
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
const readableStream = response.body.pipeThrough(progressStream)
|
|
217
|
+
const nodeReadable = Readable.fromWeb(readableStream as any)
|
|
218
|
+
const writeStream = fs.createWriteStream(destPath)
|
|
219
|
+
await pipeline(nodeReadable, writeStream)
|
|
220
|
+
process.stderr.write('\n')
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function extractZip(zipPath: string, destDir: string): void {
|
|
224
|
+
fs.mkdirSync(destDir, { recursive: true })
|
|
225
|
+
const platform = os.platform()
|
|
226
|
+
|
|
227
|
+
if (platform === 'win32') {
|
|
228
|
+
execFileSync('powershell', [
|
|
229
|
+
'-Command',
|
|
230
|
+
`Expand-Archive -Path '${zipPath}' -DestinationPath '${destDir}' -Force`,
|
|
231
|
+
], { stdio: 'pipe' })
|
|
232
|
+
} else {
|
|
233
|
+
execFileSync('unzip', ['-o', '-q', zipPath, '-d', destDir], { stdio: 'pipe' })
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export async function installChrome(): Promise<{ version: string; binaryPath: string }> {
|
|
238
|
+
console.log('Fetching latest Chrome for Testing version...')
|
|
239
|
+
const { version, downloadUrl } = await fetchDownloadUrl()
|
|
240
|
+
|
|
241
|
+
const browsersDir = getBrowsersDir()
|
|
242
|
+
const destDir = path.join(browsersDir, `chrome-${version}`)
|
|
243
|
+
|
|
244
|
+
// Check if already installed
|
|
245
|
+
const existingBinary = findChromeBinaryInDir(destDir)
|
|
246
|
+
if (existingBinary) {
|
|
247
|
+
console.log(`Chrome ${version} is already installed.`)
|
|
248
|
+
console.log(` Location: ${existingBinary}`)
|
|
249
|
+
return { version, binaryPath: existingBinary }
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
console.log(`Downloading Chrome ${version} for ${getPlatformKey()}...`)
|
|
253
|
+
console.log(` ${downloadUrl}`)
|
|
254
|
+
|
|
255
|
+
const tmpZip = path.join(os.tmpdir(), `tabwright-chrome-${version}.zip`)
|
|
256
|
+
try {
|
|
257
|
+
await downloadFile(downloadUrl, tmpZip)
|
|
258
|
+
console.log('Extracting...')
|
|
259
|
+
extractZip(tmpZip, destDir)
|
|
260
|
+
} finally {
|
|
261
|
+
// Clean up temp zip
|
|
262
|
+
try {
|
|
263
|
+
fs.unlinkSync(tmpZip)
|
|
264
|
+
} catch {
|
|
265
|
+
// ignore cleanup errors
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const binaryPath = findChromeBinaryInDir(destDir)
|
|
270
|
+
if (!binaryPath) {
|
|
271
|
+
// Clean up failed extraction
|
|
272
|
+
fs.rmSync(destDir, { recursive: true, force: true })
|
|
273
|
+
throw new Error('Chrome was downloaded but the binary could not be found in the extracted archive.')
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Ensure binary is executable on Unix
|
|
277
|
+
if (os.platform() !== 'win32') {
|
|
278
|
+
fs.chmodSync(binaryPath, 0o755)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
console.log(`Chrome ${version} installed successfully.`)
|
|
282
|
+
console.log(` Location: ${binaryPath}`)
|
|
283
|
+
return { version, binaryPath }
|
|
284
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { spawn } from 'node:child_process'
|
|
4
|
+
import { EXTENSION_IDS } from './utils.js'
|
|
5
|
+
import { getTabwrightUserDataDir } from './product-paths.js'
|
|
6
|
+
|
|
7
|
+
export type BrowserLaunchOptions = {
|
|
8
|
+
extensionPath: string
|
|
9
|
+
userDataDir: string
|
|
10
|
+
headless: boolean
|
|
11
|
+
noSandbox?: boolean
|
|
12
|
+
url?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getDefaultBrowserUserDataDir(): string {
|
|
16
|
+
return path.join(getTabwrightUserDataDir(), 'browser-profile')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getBrowserLaunchArgs({
|
|
20
|
+
extensionPath,
|
|
21
|
+
userDataDir,
|
|
22
|
+
headless,
|
|
23
|
+
noSandbox = false,
|
|
24
|
+
url = 'about:blank',
|
|
25
|
+
}: BrowserLaunchOptions): string[] {
|
|
26
|
+
const recordingFlags = EXTENSION_IDS.map((extensionId) => {
|
|
27
|
+
return `--allowlisted-extension-id=${extensionId}`
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const args = [
|
|
31
|
+
`--user-data-dir=${path.resolve(userDataDir)}`,
|
|
32
|
+
'--profile-directory=Default',
|
|
33
|
+
'--no-first-run',
|
|
34
|
+
'--no-default-browser-check',
|
|
35
|
+
'--auto-accept-this-tab-capture',
|
|
36
|
+
...recordingFlags,
|
|
37
|
+
`--disable-extensions-except=${path.resolve(extensionPath)}`,
|
|
38
|
+
`--load-extension=${path.resolve(extensionPath)}`,
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
if (headless) {
|
|
42
|
+
args.push('--headless=new')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (noSandbox) {
|
|
46
|
+
args.push('--no-sandbox', '--disable-setuid-sandbox')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
args.push(url)
|
|
50
|
+
return args
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function startBrowserProcess({
|
|
54
|
+
browserPath,
|
|
55
|
+
args,
|
|
56
|
+
userDataDir,
|
|
57
|
+
}: {
|
|
58
|
+
browserPath: string
|
|
59
|
+
args: string[]
|
|
60
|
+
userDataDir: string
|
|
61
|
+
}): { pid: number } {
|
|
62
|
+
fs.mkdirSync(path.resolve(userDataDir), { recursive: true })
|
|
63
|
+
|
|
64
|
+
const browserProcess = spawn(browserPath, args, {
|
|
65
|
+
detached: true,
|
|
66
|
+
stdio: 'ignore',
|
|
67
|
+
})
|
|
68
|
+
browserProcess.unref()
|
|
69
|
+
|
|
70
|
+
if (!browserProcess.pid) {
|
|
71
|
+
throw new Error(`Failed to start browser process for ${browserPath}`)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return { pid: browserProcess.pid }
|
|
75
|
+
}
|