Spaces:
Paused
Paused
Pavel Feldman commited on
chore: use mcp implementation in Playwright (#992)
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .github/workflows/ci.yml +1 -49
- .github/workflows/copilot-setup-steps.yml +0 -44
- .npmignore +0 -1
- Dockerfile +0 -5
- cli.js +1 -1
- eslint.config.js +0 -230
- examples/generate-test.md +0 -10
- extension/README.md +0 -48
- extension/icons/icon-128.png +0 -0
- extension/icons/icon-16.png +0 -0
- extension/icons/icon-32.png +0 -0
- extension/icons/icon-48.png +0 -0
- extension/manifest.json +0 -35
- extension/package-lock.json +0 -1884
- extension/package.json +0 -35
- extension/playwright.config.ts +0 -31
- extension/src/background.ts +0 -222
- extension/src/relayConnection.ts +0 -178
- extension/src/ui/connect.css +0 -206
- extension/src/ui/connect.html +0 -29
- extension/src/ui/connect.tsx +0 -233
- extension/src/ui/status.html +0 -13
- extension/src/ui/status.tsx +0 -110
- extension/src/ui/tabItem.tsx +0 -67
- extension/src/ui/tsconfig.json +0 -4
- extension/tests/extension.spec.ts +0 -306
- extension/tsconfig.json +0 -22
- extension/tsconfig.ui.json +0 -19
- extension/vite.config.mts +0 -54
- extension/vite.sw.config.mts +0 -31
- index.js +1 -1
- package-lock.json +0 -0
- package.json +9 -37
- playwright.config.ts +0 -4
- src/DEPS.list +0 -5
- src/browser/DEPS.list +0 -5
- src/browser/actions.d.ts +0 -172
- src/browser/browserContextFactory.ts +0 -253
- src/browser/browserServerBackend.ts +0 -88
- src/browser/codegen.ts +0 -53
- src/browser/config.ts +0 -327
- src/browser/context.ts +0 -276
- src/browser/response.ts +0 -201
- src/browser/sessionLog.ts +0 -176
- src/browser/tab.ts +0 -313
- src/browser/tools.ts +0 -60
- src/browser/tools/DEPS.list +0 -3
- src/browser/tools/common.ts +0 -63
- src/browser/tools/console.ts +0 -36
- src/browser/tools/dialogs.ts +0 -55
.github/workflows/ci.yml
CHANGED
|
@@ -18,7 +18,6 @@ jobs:
|
|
| 18 |
cache: 'npm'
|
| 19 |
- name: Install dependencies
|
| 20 |
run: npm ci
|
| 21 |
-
- run: npm run build
|
| 22 |
- name: Run ESLint
|
| 23 |
run: npm run lint
|
| 24 |
- name: Ensure no changes
|
|
@@ -41,14 +40,8 @@ jobs:
|
|
| 41 |
run: npm ci
|
| 42 |
- name: Playwright install
|
| 43 |
run: npx playwright install --with-deps
|
| 44 |
-
- name: Install MS Edge
|
| 45 |
-
# MS Edge is not preinstalled on macOS runners.
|
| 46 |
-
if: ${{ matrix.os == 'macos-latest' }}
|
| 47 |
-
run: npx playwright install msedge
|
| 48 |
-
- name: Build
|
| 49 |
-
run: npm run build
|
| 50 |
- name: Run tests
|
| 51 |
-
run: npm test
|
| 52 |
|
| 53 |
test_docker:
|
| 54 |
runs-on: ubuntu-latest
|
|
@@ -63,8 +56,6 @@ jobs:
|
|
| 63 |
run: npm ci
|
| 64 |
- name: Playwright install
|
| 65 |
run: npx playwright install --with-deps chromium
|
| 66 |
-
- name: Build
|
| 67 |
-
run: npm run build
|
| 68 |
- name: Set up Docker Buildx
|
| 69 |
uses: docker/setup-buildx-action@v3
|
| 70 |
- name: Build and push
|
|
@@ -82,42 +73,3 @@ jobs:
|
|
| 82 |
npm run test -- --project=chromium-docker
|
| 83 |
env:
|
| 84 |
MCP_IN_DOCKER: 1
|
| 85 |
-
|
| 86 |
-
test_extension:
|
| 87 |
-
strategy:
|
| 88 |
-
fail-fast: false
|
| 89 |
-
runs-on: macos-latest
|
| 90 |
-
defaults:
|
| 91 |
-
run:
|
| 92 |
-
working-directory: ./extension
|
| 93 |
-
steps:
|
| 94 |
-
- uses: actions/checkout@v4
|
| 95 |
-
- name: Use Node.js 20
|
| 96 |
-
uses: actions/setup-node@v4
|
| 97 |
-
with:
|
| 98 |
-
node-version: '20' # crypto.randomUUID(); stalls in v18.20.8
|
| 99 |
-
cache: 'npm'
|
| 100 |
-
- name: Install dependencies
|
| 101 |
-
run: npm ci
|
| 102 |
-
- name: Build extension
|
| 103 |
-
run: npm run build
|
| 104 |
-
- name: Upload artifact
|
| 105 |
-
uses: actions/upload-artifact@v4
|
| 106 |
-
with:
|
| 107 |
-
name: extension
|
| 108 |
-
path: ./extension/dist
|
| 109 |
-
retention-days: 7
|
| 110 |
-
- name: Install and build MCP server
|
| 111 |
-
run: |
|
| 112 |
-
cd ..
|
| 113 |
-
npm ci
|
| 114 |
-
npm run build
|
| 115 |
-
npx playwright install chromium
|
| 116 |
-
- name: Run tests
|
| 117 |
-
run: |
|
| 118 |
-
if [[ "$(uname)" == "Linux" ]]; then
|
| 119 |
-
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test
|
| 120 |
-
else
|
| 121 |
-
npm run test
|
| 122 |
-
fi
|
| 123 |
-
shell: bash
|
|
|
|
| 18 |
cache: 'npm'
|
| 19 |
- name: Install dependencies
|
| 20 |
run: npm ci
|
|
|
|
| 21 |
- name: Run ESLint
|
| 22 |
run: npm run lint
|
| 23 |
- name: Ensure no changes
|
|
|
|
| 40 |
run: npm ci
|
| 41 |
- name: Playwright install
|
| 42 |
run: npx playwright install --with-deps
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
- name: Run tests
|
| 44 |
+
run: npm run test
|
| 45 |
|
| 46 |
test_docker:
|
| 47 |
runs-on: ubuntu-latest
|
|
|
|
| 56 |
run: npm ci
|
| 57 |
- name: Playwright install
|
| 58 |
run: npx playwright install --with-deps chromium
|
|
|
|
|
|
|
| 59 |
- name: Set up Docker Buildx
|
| 60 |
uses: docker/setup-buildx-action@v3
|
| 61 |
- name: Build and push
|
|
|
|
| 73 |
npm run test -- --project=chromium-docker
|
| 74 |
env:
|
| 75 |
MCP_IN_DOCKER: 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.github/workflows/copilot-setup-steps.yml
DELETED
|
@@ -1,44 +0,0 @@
|
|
| 1 |
-
name: "Copilot Setup Steps"
|
| 2 |
-
|
| 3 |
-
# Automatically run the setup steps when they are changed to allow for easy validation, and
|
| 4 |
-
# allow manual testing through the repository's "Actions" tab
|
| 5 |
-
on:
|
| 6 |
-
workflow_dispatch:
|
| 7 |
-
push:
|
| 8 |
-
paths:
|
| 9 |
-
- .github/workflows/copilot-setup-steps.yml
|
| 10 |
-
pull_request:
|
| 11 |
-
paths:
|
| 12 |
-
- .github/workflows/copilot-setup-steps.yml
|
| 13 |
-
|
| 14 |
-
jobs:
|
| 15 |
-
# The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot.
|
| 16 |
-
copilot-setup-steps:
|
| 17 |
-
runs-on: ubuntu-latest
|
| 18 |
-
|
| 19 |
-
# Set the permissions to the lowest permissions possible needed for your steps.
|
| 20 |
-
# Copilot will be given its own token for its operations.
|
| 21 |
-
permissions:
|
| 22 |
-
# If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete.
|
| 23 |
-
contents: read
|
| 24 |
-
|
| 25 |
-
# You can define any steps you want, and they will run before the agent starts.
|
| 26 |
-
# If you do not check out your code, Copilot will do this for you.
|
| 27 |
-
steps:
|
| 28 |
-
- name: Checkout code
|
| 29 |
-
uses: actions/checkout@v4
|
| 30 |
-
|
| 31 |
-
- name: Set up Node.js
|
| 32 |
-
uses: actions/setup-node@v4
|
| 33 |
-
with:
|
| 34 |
-
node-version: "18.19"
|
| 35 |
-
cache: "npm"
|
| 36 |
-
|
| 37 |
-
- name: Install JavaScript dependencies
|
| 38 |
-
run: npm ci
|
| 39 |
-
|
| 40 |
-
- name: Playwright install
|
| 41 |
-
run: npx playwright install --with-deps
|
| 42 |
-
|
| 43 |
-
- name: Build
|
| 44 |
-
run: npm run build
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.npmignore
CHANGED
|
@@ -1,7 +1,6 @@
|
|
| 1 |
**/*
|
| 2 |
README.md
|
| 3 |
LICENSE
|
| 4 |
-
!lib/**/*.js
|
| 5 |
!cli.js
|
| 6 |
!index.*
|
| 7 |
!config.d.ts
|
|
|
|
| 1 |
**/*
|
| 2 |
README.md
|
| 3 |
LICENSE
|
|
|
|
| 4 |
!cli.js
|
| 5 |
!index.*
|
| 6 |
!config.d.ts
|
Dockerfile
CHANGED
|
@@ -32,10 +32,6 @@ RUN --mount=type=cache,target=/root/.npm,sharing=locked,id=npm-cache \
|
|
| 32 |
|
| 33 |
# Copy the rest of the app
|
| 34 |
COPY *.json *.js *.ts .
|
| 35 |
-
COPY src src/
|
| 36 |
-
|
| 37 |
-
# Build the app
|
| 38 |
-
RUN npm run build
|
| 39 |
|
| 40 |
# ------------------------------
|
| 41 |
# Browser
|
|
@@ -63,7 +59,6 @@ USER ${USERNAME}
|
|
| 63 |
|
| 64 |
COPY --from=browser --chown=${USERNAME}:${USERNAME} ${PLAYWRIGHT_BROWSERS_PATH} ${PLAYWRIGHT_BROWSERS_PATH}
|
| 65 |
COPY --chown=${USERNAME}:${USERNAME} cli.js package.json ./
|
| 66 |
-
COPY --from=builder --chown=${USERNAME}:${USERNAME} /app/lib /app/lib
|
| 67 |
|
| 68 |
# Run in headless and only with chromium (other browsers need more dependencies not included in this image)
|
| 69 |
ENTRYPOINT ["node", "cli.js", "--headless", "--browser", "chromium", "--no-sandbox"]
|
|
|
|
| 32 |
|
| 33 |
# Copy the rest of the app
|
| 34 |
COPY *.json *.js *.ts .
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
# ------------------------------
|
| 37 |
# Browser
|
|
|
|
| 59 |
|
| 60 |
COPY --from=browser --chown=${USERNAME}:${USERNAME} ${PLAYWRIGHT_BROWSERS_PATH} ${PLAYWRIGHT_BROWSERS_PATH}
|
| 61 |
COPY --chown=${USERNAME}:${USERNAME} cli.js package.json ./
|
|
|
|
| 62 |
|
| 63 |
# Run in headless and only with chromium (other browsers need more dependencies not included in this image)
|
| 64 |
ENTRYPOINT ["node", "cli.js", "--headless", "--browser", "chromium", "--no-sandbox"]
|
cli.js
CHANGED
|
@@ -15,4 +15,4 @@
|
|
| 15 |
* limitations under the License.
|
| 16 |
*/
|
| 17 |
|
| 18 |
-
require('
|
|
|
|
| 15 |
* limitations under the License.
|
| 16 |
*/
|
| 17 |
|
| 18 |
+
require('playwright/lib/mcp/program');
|
eslint.config.js
DELETED
|
@@ -1,230 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
const typescriptEslint = require("@typescript-eslint/eslint-plugin");
|
| 18 |
-
const tsParser = require("@typescript-eslint/parser");
|
| 19 |
-
const notice = require("eslint-plugin-notice");
|
| 20 |
-
const path = require("path");
|
| 21 |
-
const stylistic = require("@stylistic/eslint-plugin");
|
| 22 |
-
const importRules = require("eslint-plugin-import");
|
| 23 |
-
|
| 24 |
-
const plugins = {
|
| 25 |
-
"@stylistic": stylistic,
|
| 26 |
-
"@typescript-eslint": typescriptEslint,
|
| 27 |
-
notice,
|
| 28 |
-
import: importRules,
|
| 29 |
-
};
|
| 30 |
-
|
| 31 |
-
const baseRules = {
|
| 32 |
-
"@typescript-eslint/no-floating-promises": "error",
|
| 33 |
-
"@typescript-eslint/no-unused-vars": [
|
| 34 |
-
2,
|
| 35 |
-
{ args: "none", caughtErrors: "none" },
|
| 36 |
-
],
|
| 37 |
-
|
| 38 |
-
/**
|
| 39 |
-
* Enforced rules
|
| 40 |
-
*/
|
| 41 |
-
// syntax preferences
|
| 42 |
-
"object-curly-spacing": ["error", "always"],
|
| 43 |
-
quotes: [
|
| 44 |
-
2,
|
| 45 |
-
"single",
|
| 46 |
-
{
|
| 47 |
-
avoidEscape: true,
|
| 48 |
-
allowTemplateLiterals: true,
|
| 49 |
-
},
|
| 50 |
-
],
|
| 51 |
-
"jsx-quotes": [2, "prefer-single"],
|
| 52 |
-
"no-extra-semi": 2,
|
| 53 |
-
"@stylistic/semi": [2],
|
| 54 |
-
"comma-style": [2, "last"],
|
| 55 |
-
"wrap-iife": [2, "inside"],
|
| 56 |
-
"spaced-comment": [
|
| 57 |
-
2,
|
| 58 |
-
"always",
|
| 59 |
-
{
|
| 60 |
-
markers: ["*"],
|
| 61 |
-
},
|
| 62 |
-
],
|
| 63 |
-
eqeqeq: [2],
|
| 64 |
-
"accessor-pairs": [
|
| 65 |
-
2,
|
| 66 |
-
{
|
| 67 |
-
getWithoutSet: false,
|
| 68 |
-
setWithoutGet: false,
|
| 69 |
-
},
|
| 70 |
-
],
|
| 71 |
-
"brace-style": [2, "1tbs", { allowSingleLine: true }],
|
| 72 |
-
curly: [2, "multi-or-nest", "consistent"],
|
| 73 |
-
"new-parens": 2,
|
| 74 |
-
"arrow-parens": [2, "as-needed"],
|
| 75 |
-
"prefer-const": 2,
|
| 76 |
-
"quote-props": [2, "consistent"],
|
| 77 |
-
"nonblock-statement-body-position": [2, "below"],
|
| 78 |
-
|
| 79 |
-
// anti-patterns
|
| 80 |
-
"no-var": 2,
|
| 81 |
-
"no-with": 2,
|
| 82 |
-
"no-multi-str": 2,
|
| 83 |
-
"no-caller": 2,
|
| 84 |
-
"no-implied-eval": 2,
|
| 85 |
-
"no-labels": 2,
|
| 86 |
-
"no-new-object": 2,
|
| 87 |
-
"no-octal-escape": 2,
|
| 88 |
-
"no-self-compare": 2,
|
| 89 |
-
"no-shadow-restricted-names": 2,
|
| 90 |
-
"no-cond-assign": 2,
|
| 91 |
-
"no-debugger": 2,
|
| 92 |
-
"no-dupe-keys": 2,
|
| 93 |
-
"no-duplicate-case": 2,
|
| 94 |
-
"no-empty-character-class": 2,
|
| 95 |
-
"no-unreachable": 2,
|
| 96 |
-
"no-unsafe-negation": 2,
|
| 97 |
-
radix: 2,
|
| 98 |
-
"valid-typeof": 2,
|
| 99 |
-
"no-implicit-globals": [2],
|
| 100 |
-
"no-unused-expressions": [
|
| 101 |
-
2,
|
| 102 |
-
{ allowShortCircuit: true, allowTernary: true, allowTaggedTemplates: true },
|
| 103 |
-
],
|
| 104 |
-
"no-proto": 2,
|
| 105 |
-
|
| 106 |
-
// es2015 features
|
| 107 |
-
"require-yield": 2,
|
| 108 |
-
"template-curly-spacing": [2, "never"],
|
| 109 |
-
|
| 110 |
-
// spacing details
|
| 111 |
-
"space-infix-ops": 2,
|
| 112 |
-
"space-in-parens": [2, "never"],
|
| 113 |
-
"array-bracket-spacing": [2, "never"],
|
| 114 |
-
"comma-spacing": [2, { before: false, after: true }],
|
| 115 |
-
"keyword-spacing": [2, "always"],
|
| 116 |
-
"space-before-function-paren": [
|
| 117 |
-
2,
|
| 118 |
-
{
|
| 119 |
-
anonymous: "never",
|
| 120 |
-
named: "never",
|
| 121 |
-
asyncArrow: "always",
|
| 122 |
-
},
|
| 123 |
-
],
|
| 124 |
-
"no-whitespace-before-property": 2,
|
| 125 |
-
"keyword-spacing": [
|
| 126 |
-
2,
|
| 127 |
-
{
|
| 128 |
-
overrides: {
|
| 129 |
-
if: { after: true },
|
| 130 |
-
else: { after: true },
|
| 131 |
-
for: { after: true },
|
| 132 |
-
while: { after: true },
|
| 133 |
-
do: { after: true },
|
| 134 |
-
switch: { after: true },
|
| 135 |
-
return: { after: true },
|
| 136 |
-
},
|
| 137 |
-
},
|
| 138 |
-
],
|
| 139 |
-
"arrow-spacing": [
|
| 140 |
-
2,
|
| 141 |
-
{
|
| 142 |
-
after: true,
|
| 143 |
-
before: true,
|
| 144 |
-
},
|
| 145 |
-
],
|
| 146 |
-
"@stylistic/func-call-spacing": 2,
|
| 147 |
-
"@stylistic/type-annotation-spacing": 2,
|
| 148 |
-
|
| 149 |
-
// file whitespace
|
| 150 |
-
"no-multiple-empty-lines": [2, { max: 2, maxEOF: 0 }],
|
| 151 |
-
"no-mixed-spaces-and-tabs": 2,
|
| 152 |
-
"no-trailing-spaces": 2,
|
| 153 |
-
"linebreak-style": [process.platform === "win32" ? 0 : 2, "unix"],
|
| 154 |
-
indent: [
|
| 155 |
-
2,
|
| 156 |
-
2,
|
| 157 |
-
{ SwitchCase: 1, CallExpression: { arguments: 2 }, MemberExpression: 2 },
|
| 158 |
-
],
|
| 159 |
-
"key-spacing": [
|
| 160 |
-
2,
|
| 161 |
-
{
|
| 162 |
-
beforeColon: false,
|
| 163 |
-
},
|
| 164 |
-
],
|
| 165 |
-
"eol-last": 2,
|
| 166 |
-
|
| 167 |
-
// copyright
|
| 168 |
-
"notice/notice": [
|
| 169 |
-
2,
|
| 170 |
-
{
|
| 171 |
-
mustMatch: "Copyright",
|
| 172 |
-
templateFile: path.join(__dirname, "utils", "copyright.js"),
|
| 173 |
-
},
|
| 174 |
-
],
|
| 175 |
-
|
| 176 |
-
// react
|
| 177 |
-
"react/react-in-jsx-scope": 0,
|
| 178 |
-
"no-console": 2,
|
| 179 |
-
};
|
| 180 |
-
|
| 181 |
-
const languageOptions = {
|
| 182 |
-
parser: tsParser,
|
| 183 |
-
ecmaVersion: 9,
|
| 184 |
-
sourceType: "module",
|
| 185 |
-
parserOptions: {
|
| 186 |
-
project: path.join(__filename, "..", "tsconfig.all.json"),
|
| 187 |
-
}
|
| 188 |
-
};
|
| 189 |
-
|
| 190 |
-
const importOrderRules = {
|
| 191 |
-
"import/order": [
|
| 192 |
-
2,
|
| 193 |
-
{
|
| 194 |
-
groups: [
|
| 195 |
-
"builtin",
|
| 196 |
-
"external",
|
| 197 |
-
"internal",
|
| 198 |
-
["parent", "sibling"],
|
| 199 |
-
"index",
|
| 200 |
-
"type",
|
| 201 |
-
],
|
| 202 |
-
},
|
| 203 |
-
],
|
| 204 |
-
"import/consistent-type-specifier-style": [2, "prefer-top-level"],
|
| 205 |
-
};
|
| 206 |
-
|
| 207 |
-
const noFloatingPromisesRules = {
|
| 208 |
-
"@typescript-eslint/no-floating-promises": "error",
|
| 209 |
-
};
|
| 210 |
-
|
| 211 |
-
const noBooleanCompareRules = {
|
| 212 |
-
"@typescript-eslint/no-unnecessary-boolean-literal-compare": 2,
|
| 213 |
-
};
|
| 214 |
-
|
| 215 |
-
module.exports = [
|
| 216 |
-
{
|
| 217 |
-
ignores: ["**/*.js"],
|
| 218 |
-
},
|
| 219 |
-
{
|
| 220 |
-
files: ["**/*.ts", "**/*.tsx"],
|
| 221 |
-
plugins,
|
| 222 |
-
languageOptions,
|
| 223 |
-
rules: {
|
| 224 |
-
...baseRules,
|
| 225 |
-
...importOrderRules,
|
| 226 |
-
...noFloatingPromisesRules,
|
| 227 |
-
...noBooleanCompareRules,
|
| 228 |
-
},
|
| 229 |
-
},
|
| 230 |
-
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
examples/generate-test.md
DELETED
|
@@ -1,10 +0,0 @@
|
|
| 1 |
-
Use Playwright tools to generate test for scenario:
|
| 2 |
-
|
| 3 |
-
## GitHub PR Checks Navigation Checklist
|
| 4 |
-
|
| 5 |
-
1. Open the [Microsoft Playwright GitHub repository](https://github.com/microsoft/playwright).
|
| 6 |
-
2. Click on the **Pull requests** tab.
|
| 7 |
-
3. Find and open the pull request titled **"chore: make noWaitAfter a default"**.
|
| 8 |
-
4. Switch to the **Checks** tab for that pull request.
|
| 9 |
-
5. Expand the **infra** check suite to view its jobs.
|
| 10 |
-
6. Click on the **docs & lint** job to view its details.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/README.md
DELETED
|
@@ -1,48 +0,0 @@
|
|
| 1 |
-
# Playwright MCP Chrome Extension
|
| 2 |
-
|
| 3 |
-
## Introduction
|
| 4 |
-
|
| 5 |
-
The Playwright MCP Chrome Extension allows you to connect to pages in your existing browser and leverage the state of your default user profile. This means the AI assistant can interact with websites where you're already logged in, using your existing cookies, sessions, and browser state, providing a seamless experience without requiring separate authentication or setup.
|
| 6 |
-
|
| 7 |
-
## Prerequisites
|
| 8 |
-
|
| 9 |
-
- Chrome/Edge/Chromium browser
|
| 10 |
-
|
| 11 |
-
## Installation Steps
|
| 12 |
-
|
| 13 |
-
### Download the Extension
|
| 14 |
-
|
| 15 |
-
Download the latest Chrome extension from GitHub:
|
| 16 |
-
- **Download link**: https://github.com/microsoft/playwright-mcp/releases
|
| 17 |
-
|
| 18 |
-
### Load Chrome Extension
|
| 19 |
-
|
| 20 |
-
1. Open Chrome and navigate to `chrome://extensions/`
|
| 21 |
-
2. Enable "Developer mode" (toggle in the top right corner)
|
| 22 |
-
3. Click "Load unpacked" and select the extension directory
|
| 23 |
-
|
| 24 |
-
### Configure Playwright MCP server
|
| 25 |
-
|
| 26 |
-
Configure Playwright MCP server to connect to the browser using the extension by passing the `--extension` option when running the MCP server:
|
| 27 |
-
|
| 28 |
-
```json
|
| 29 |
-
{
|
| 30 |
-
"mcpServers": {
|
| 31 |
-
"playwright-extension": {
|
| 32 |
-
"command": "npx",
|
| 33 |
-
"args": [
|
| 34 |
-
"@playwright/mcp@latest",
|
| 35 |
-
"--extension"
|
| 36 |
-
]
|
| 37 |
-
}
|
| 38 |
-
}
|
| 39 |
-
}
|
| 40 |
-
```
|
| 41 |
-
|
| 42 |
-
## Usage
|
| 43 |
-
|
| 44 |
-
### Browser Tab Selection
|
| 45 |
-
|
| 46 |
-
When the LLM interacts with the browser for the first time, it will load a page where you can select which browser tab the LLM will connect to. This allows you to control which specific page the AI assistant will interact with during the session.
|
| 47 |
-
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/icons/icon-128.png
DELETED
|
Binary file (6.35 kB)
|
|
|
extension/icons/icon-16.png
DELETED
|
Binary file (571 Bytes)
|
|
|
extension/icons/icon-32.png
DELETED
|
Binary file (1.26 kB)
|
|
|
extension/icons/icon-48.png
DELETED
|
Binary file (2.04 kB)
|
|
|
extension/manifest.json
DELETED
|
@@ -1,35 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"manifest_version": 3,
|
| 3 |
-
"name": "Playwright MCP Bridge",
|
| 4 |
-
"version": "0.0.36",
|
| 5 |
-
"description": "Share browser tabs with Playwright MCP server",
|
| 6 |
-
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9nMS2b0WCohjVHPGb8D9qAdkbIngDqoAjTeSccHJijgcONejge+OJxOQOMLu7b0ovt1c9BiEJa5JcpM+EHFVGL1vluBxK71zmBy1m2f9vZF3HG0LSCp7YRkum9rAIEthDwbkxx6XTvpmAY5rjFa/NON6b9Hlbo+8peUSkoOK7HTwYnnI36asZ9eUTiveIf+DMPLojW2UX33vDWG2UKvMVDewzclb4+uLxAYshY7Mx8we/b44xu+Anb/EBLKjOPk9Yh541xJ5Ozc8EiP/5yxOp9c/lRiYUHaRW+4r0HKZyFt0eZ52ti2iM4Nfk7jRXR7an3JPsUIf5deC/1cVM/+1ZQIDAQAB",
|
| 7 |
-
"permissions": [
|
| 8 |
-
"debugger",
|
| 9 |
-
"activeTab",
|
| 10 |
-
"tabs",
|
| 11 |
-
"storage"
|
| 12 |
-
],
|
| 13 |
-
"host_permissions": [
|
| 14 |
-
"<all_urls>"
|
| 15 |
-
],
|
| 16 |
-
"background": {
|
| 17 |
-
"service_worker": "lib/background.mjs",
|
| 18 |
-
"type": "module"
|
| 19 |
-
},
|
| 20 |
-
"action": {
|
| 21 |
-
"default_title": "Playwright MCP Bridge",
|
| 22 |
-
"default_icon": {
|
| 23 |
-
"16": "icons/icon-16.png",
|
| 24 |
-
"32": "icons/icon-32.png",
|
| 25 |
-
"48": "icons/icon-48.png",
|
| 26 |
-
"128": "icons/icon-128.png"
|
| 27 |
-
}
|
| 28 |
-
},
|
| 29 |
-
"icons": {
|
| 30 |
-
"16": "icons/icon-16.png",
|
| 31 |
-
"32": "icons/icon-32.png",
|
| 32 |
-
"48": "icons/icon-48.png",
|
| 33 |
-
"128": "icons/icon-128.png"
|
| 34 |
-
}
|
| 35 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/package-lock.json
DELETED
|
@@ -1,1884 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"name": "@playwright/mcp-extension",
|
| 3 |
-
"version": "0.0.36",
|
| 4 |
-
"lockfileVersion": 3,
|
| 5 |
-
"requires": true,
|
| 6 |
-
"packages": {
|
| 7 |
-
"": {
|
| 8 |
-
"name": "@playwright/mcp-extension",
|
| 9 |
-
"version": "0.0.36",
|
| 10 |
-
"license": "Apache-2.0",
|
| 11 |
-
"devDependencies": {
|
| 12 |
-
"@types/chrome": "^0.0.315",
|
| 13 |
-
"@types/react": "^18.2.66",
|
| 14 |
-
"@types/react-dom": "^18.2.22",
|
| 15 |
-
"@vitejs/plugin-react": "^4.0.0",
|
| 16 |
-
"react": "^18.2.0",
|
| 17 |
-
"react-dom": "^18.2.0",
|
| 18 |
-
"typescript": "^5.8.2",
|
| 19 |
-
"vite": "^5.0.0",
|
| 20 |
-
"vite-plugin-static-copy": "^3.1.1"
|
| 21 |
-
},
|
| 22 |
-
"engines": {
|
| 23 |
-
"node": ">=18"
|
| 24 |
-
}
|
| 25 |
-
},
|
| 26 |
-
"node_modules/@ampproject/remapping": {
|
| 27 |
-
"version": "2.3.0",
|
| 28 |
-
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
|
| 29 |
-
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
|
| 30 |
-
"dev": true,
|
| 31 |
-
"dependencies": {
|
| 32 |
-
"@jridgewell/gen-mapping": "^0.3.5",
|
| 33 |
-
"@jridgewell/trace-mapping": "^0.3.24"
|
| 34 |
-
},
|
| 35 |
-
"engines": {
|
| 36 |
-
"node": ">=6.0.0"
|
| 37 |
-
}
|
| 38 |
-
},
|
| 39 |
-
"node_modules/@babel/code-frame": {
|
| 40 |
-
"version": "7.27.1",
|
| 41 |
-
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
|
| 42 |
-
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
|
| 43 |
-
"dev": true,
|
| 44 |
-
"dependencies": {
|
| 45 |
-
"@babel/helper-validator-identifier": "^7.27.1",
|
| 46 |
-
"js-tokens": "^4.0.0",
|
| 47 |
-
"picocolors": "^1.1.1"
|
| 48 |
-
},
|
| 49 |
-
"engines": {
|
| 50 |
-
"node": ">=6.9.0"
|
| 51 |
-
}
|
| 52 |
-
},
|
| 53 |
-
"node_modules/@babel/compat-data": {
|
| 54 |
-
"version": "7.28.0",
|
| 55 |
-
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz",
|
| 56 |
-
"integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==",
|
| 57 |
-
"dev": true,
|
| 58 |
-
"engines": {
|
| 59 |
-
"node": ">=6.9.0"
|
| 60 |
-
}
|
| 61 |
-
},
|
| 62 |
-
"node_modules/@babel/core": {
|
| 63 |
-
"version": "7.28.0",
|
| 64 |
-
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz",
|
| 65 |
-
"integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==",
|
| 66 |
-
"dev": true,
|
| 67 |
-
"dependencies": {
|
| 68 |
-
"@ampproject/remapping": "^2.2.0",
|
| 69 |
-
"@babel/code-frame": "^7.27.1",
|
| 70 |
-
"@babel/generator": "^7.28.0",
|
| 71 |
-
"@babel/helper-compilation-targets": "^7.27.2",
|
| 72 |
-
"@babel/helper-module-transforms": "^7.27.3",
|
| 73 |
-
"@babel/helpers": "^7.27.6",
|
| 74 |
-
"@babel/parser": "^7.28.0",
|
| 75 |
-
"@babel/template": "^7.27.2",
|
| 76 |
-
"@babel/traverse": "^7.28.0",
|
| 77 |
-
"@babel/types": "^7.28.0",
|
| 78 |
-
"convert-source-map": "^2.0.0",
|
| 79 |
-
"debug": "^4.1.0",
|
| 80 |
-
"gensync": "^1.0.0-beta.2",
|
| 81 |
-
"json5": "^2.2.3",
|
| 82 |
-
"semver": "^6.3.1"
|
| 83 |
-
},
|
| 84 |
-
"engines": {
|
| 85 |
-
"node": ">=6.9.0"
|
| 86 |
-
},
|
| 87 |
-
"funding": {
|
| 88 |
-
"type": "opencollective",
|
| 89 |
-
"url": "https://opencollective.com/babel"
|
| 90 |
-
}
|
| 91 |
-
},
|
| 92 |
-
"node_modules/@babel/generator": {
|
| 93 |
-
"version": "7.28.0",
|
| 94 |
-
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz",
|
| 95 |
-
"integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==",
|
| 96 |
-
"dev": true,
|
| 97 |
-
"dependencies": {
|
| 98 |
-
"@babel/parser": "^7.28.0",
|
| 99 |
-
"@babel/types": "^7.28.0",
|
| 100 |
-
"@jridgewell/gen-mapping": "^0.3.12",
|
| 101 |
-
"@jridgewell/trace-mapping": "^0.3.28",
|
| 102 |
-
"jsesc": "^3.0.2"
|
| 103 |
-
},
|
| 104 |
-
"engines": {
|
| 105 |
-
"node": ">=6.9.0"
|
| 106 |
-
}
|
| 107 |
-
},
|
| 108 |
-
"node_modules/@babel/helper-compilation-targets": {
|
| 109 |
-
"version": "7.27.2",
|
| 110 |
-
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
|
| 111 |
-
"integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
|
| 112 |
-
"dev": true,
|
| 113 |
-
"dependencies": {
|
| 114 |
-
"@babel/compat-data": "^7.27.2",
|
| 115 |
-
"@babel/helper-validator-option": "^7.27.1",
|
| 116 |
-
"browserslist": "^4.24.0",
|
| 117 |
-
"lru-cache": "^5.1.1",
|
| 118 |
-
"semver": "^6.3.1"
|
| 119 |
-
},
|
| 120 |
-
"engines": {
|
| 121 |
-
"node": ">=6.9.0"
|
| 122 |
-
}
|
| 123 |
-
},
|
| 124 |
-
"node_modules/@babel/helper-globals": {
|
| 125 |
-
"version": "7.28.0",
|
| 126 |
-
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
|
| 127 |
-
"integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
|
| 128 |
-
"dev": true,
|
| 129 |
-
"engines": {
|
| 130 |
-
"node": ">=6.9.0"
|
| 131 |
-
}
|
| 132 |
-
},
|
| 133 |
-
"node_modules/@babel/helper-module-imports": {
|
| 134 |
-
"version": "7.27.1",
|
| 135 |
-
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
|
| 136 |
-
"integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
|
| 137 |
-
"dev": true,
|
| 138 |
-
"dependencies": {
|
| 139 |
-
"@babel/traverse": "^7.27.1",
|
| 140 |
-
"@babel/types": "^7.27.1"
|
| 141 |
-
},
|
| 142 |
-
"engines": {
|
| 143 |
-
"node": ">=6.9.0"
|
| 144 |
-
}
|
| 145 |
-
},
|
| 146 |
-
"node_modules/@babel/helper-module-transforms": {
|
| 147 |
-
"version": "7.27.3",
|
| 148 |
-
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz",
|
| 149 |
-
"integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==",
|
| 150 |
-
"dev": true,
|
| 151 |
-
"dependencies": {
|
| 152 |
-
"@babel/helper-module-imports": "^7.27.1",
|
| 153 |
-
"@babel/helper-validator-identifier": "^7.27.1",
|
| 154 |
-
"@babel/traverse": "^7.27.3"
|
| 155 |
-
},
|
| 156 |
-
"engines": {
|
| 157 |
-
"node": ">=6.9.0"
|
| 158 |
-
},
|
| 159 |
-
"peerDependencies": {
|
| 160 |
-
"@babel/core": "^7.0.0"
|
| 161 |
-
}
|
| 162 |
-
},
|
| 163 |
-
"node_modules/@babel/helper-plugin-utils": {
|
| 164 |
-
"version": "7.27.1",
|
| 165 |
-
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
|
| 166 |
-
"integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
|
| 167 |
-
"dev": true,
|
| 168 |
-
"engines": {
|
| 169 |
-
"node": ">=6.9.0"
|
| 170 |
-
}
|
| 171 |
-
},
|
| 172 |
-
"node_modules/@babel/helper-string-parser": {
|
| 173 |
-
"version": "7.27.1",
|
| 174 |
-
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
| 175 |
-
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
| 176 |
-
"dev": true,
|
| 177 |
-
"engines": {
|
| 178 |
-
"node": ">=6.9.0"
|
| 179 |
-
}
|
| 180 |
-
},
|
| 181 |
-
"node_modules/@babel/helper-validator-identifier": {
|
| 182 |
-
"version": "7.27.1",
|
| 183 |
-
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
|
| 184 |
-
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
|
| 185 |
-
"dev": true,
|
| 186 |
-
"engines": {
|
| 187 |
-
"node": ">=6.9.0"
|
| 188 |
-
}
|
| 189 |
-
},
|
| 190 |
-
"node_modules/@babel/helper-validator-option": {
|
| 191 |
-
"version": "7.27.1",
|
| 192 |
-
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
|
| 193 |
-
"integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
|
| 194 |
-
"dev": true,
|
| 195 |
-
"engines": {
|
| 196 |
-
"node": ">=6.9.0"
|
| 197 |
-
}
|
| 198 |
-
},
|
| 199 |
-
"node_modules/@babel/helpers": {
|
| 200 |
-
"version": "7.28.2",
|
| 201 |
-
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz",
|
| 202 |
-
"integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==",
|
| 203 |
-
"dev": true,
|
| 204 |
-
"dependencies": {
|
| 205 |
-
"@babel/template": "^7.27.2",
|
| 206 |
-
"@babel/types": "^7.28.2"
|
| 207 |
-
},
|
| 208 |
-
"engines": {
|
| 209 |
-
"node": ">=6.9.0"
|
| 210 |
-
}
|
| 211 |
-
},
|
| 212 |
-
"node_modules/@babel/parser": {
|
| 213 |
-
"version": "7.28.0",
|
| 214 |
-
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz",
|
| 215 |
-
"integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
|
| 216 |
-
"dev": true,
|
| 217 |
-
"dependencies": {
|
| 218 |
-
"@babel/types": "^7.28.0"
|
| 219 |
-
},
|
| 220 |
-
"bin": {
|
| 221 |
-
"parser": "bin/babel-parser.js"
|
| 222 |
-
},
|
| 223 |
-
"engines": {
|
| 224 |
-
"node": ">=6.0.0"
|
| 225 |
-
}
|
| 226 |
-
},
|
| 227 |
-
"node_modules/@babel/plugin-transform-react-jsx-self": {
|
| 228 |
-
"version": "7.27.1",
|
| 229 |
-
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
|
| 230 |
-
"integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
|
| 231 |
-
"dev": true,
|
| 232 |
-
"dependencies": {
|
| 233 |
-
"@babel/helper-plugin-utils": "^7.27.1"
|
| 234 |
-
},
|
| 235 |
-
"engines": {
|
| 236 |
-
"node": ">=6.9.0"
|
| 237 |
-
},
|
| 238 |
-
"peerDependencies": {
|
| 239 |
-
"@babel/core": "^7.0.0-0"
|
| 240 |
-
}
|
| 241 |
-
},
|
| 242 |
-
"node_modules/@babel/plugin-transform-react-jsx-source": {
|
| 243 |
-
"version": "7.27.1",
|
| 244 |
-
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
|
| 245 |
-
"integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
|
| 246 |
-
"dev": true,
|
| 247 |
-
"dependencies": {
|
| 248 |
-
"@babel/helper-plugin-utils": "^7.27.1"
|
| 249 |
-
},
|
| 250 |
-
"engines": {
|
| 251 |
-
"node": ">=6.9.0"
|
| 252 |
-
},
|
| 253 |
-
"peerDependencies": {
|
| 254 |
-
"@babel/core": "^7.0.0-0"
|
| 255 |
-
}
|
| 256 |
-
},
|
| 257 |
-
"node_modules/@babel/template": {
|
| 258 |
-
"version": "7.27.2",
|
| 259 |
-
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
|
| 260 |
-
"integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
|
| 261 |
-
"dev": true,
|
| 262 |
-
"dependencies": {
|
| 263 |
-
"@babel/code-frame": "^7.27.1",
|
| 264 |
-
"@babel/parser": "^7.27.2",
|
| 265 |
-
"@babel/types": "^7.27.1"
|
| 266 |
-
},
|
| 267 |
-
"engines": {
|
| 268 |
-
"node": ">=6.9.0"
|
| 269 |
-
}
|
| 270 |
-
},
|
| 271 |
-
"node_modules/@babel/traverse": {
|
| 272 |
-
"version": "7.28.0",
|
| 273 |
-
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz",
|
| 274 |
-
"integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==",
|
| 275 |
-
"dev": true,
|
| 276 |
-
"dependencies": {
|
| 277 |
-
"@babel/code-frame": "^7.27.1",
|
| 278 |
-
"@babel/generator": "^7.28.0",
|
| 279 |
-
"@babel/helper-globals": "^7.28.0",
|
| 280 |
-
"@babel/parser": "^7.28.0",
|
| 281 |
-
"@babel/template": "^7.27.2",
|
| 282 |
-
"@babel/types": "^7.28.0",
|
| 283 |
-
"debug": "^4.3.1"
|
| 284 |
-
},
|
| 285 |
-
"engines": {
|
| 286 |
-
"node": ">=6.9.0"
|
| 287 |
-
}
|
| 288 |
-
},
|
| 289 |
-
"node_modules/@babel/types": {
|
| 290 |
-
"version": "7.28.2",
|
| 291 |
-
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz",
|
| 292 |
-
"integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
|
| 293 |
-
"dev": true,
|
| 294 |
-
"dependencies": {
|
| 295 |
-
"@babel/helper-string-parser": "^7.27.1",
|
| 296 |
-
"@babel/helper-validator-identifier": "^7.27.1"
|
| 297 |
-
},
|
| 298 |
-
"engines": {
|
| 299 |
-
"node": ">=6.9.0"
|
| 300 |
-
}
|
| 301 |
-
},
|
| 302 |
-
"node_modules/@esbuild/aix-ppc64": {
|
| 303 |
-
"version": "0.21.5",
|
| 304 |
-
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
|
| 305 |
-
"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
|
| 306 |
-
"cpu": [
|
| 307 |
-
"ppc64"
|
| 308 |
-
],
|
| 309 |
-
"dev": true,
|
| 310 |
-
"optional": true,
|
| 311 |
-
"os": [
|
| 312 |
-
"aix"
|
| 313 |
-
],
|
| 314 |
-
"engines": {
|
| 315 |
-
"node": ">=12"
|
| 316 |
-
}
|
| 317 |
-
},
|
| 318 |
-
"node_modules/@esbuild/android-arm": {
|
| 319 |
-
"version": "0.21.5",
|
| 320 |
-
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
|
| 321 |
-
"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
|
| 322 |
-
"cpu": [
|
| 323 |
-
"arm"
|
| 324 |
-
],
|
| 325 |
-
"dev": true,
|
| 326 |
-
"optional": true,
|
| 327 |
-
"os": [
|
| 328 |
-
"android"
|
| 329 |
-
],
|
| 330 |
-
"engines": {
|
| 331 |
-
"node": ">=12"
|
| 332 |
-
}
|
| 333 |
-
},
|
| 334 |
-
"node_modules/@esbuild/android-arm64": {
|
| 335 |
-
"version": "0.21.5",
|
| 336 |
-
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
|
| 337 |
-
"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
|
| 338 |
-
"cpu": [
|
| 339 |
-
"arm64"
|
| 340 |
-
],
|
| 341 |
-
"dev": true,
|
| 342 |
-
"optional": true,
|
| 343 |
-
"os": [
|
| 344 |
-
"android"
|
| 345 |
-
],
|
| 346 |
-
"engines": {
|
| 347 |
-
"node": ">=12"
|
| 348 |
-
}
|
| 349 |
-
},
|
| 350 |
-
"node_modules/@esbuild/android-x64": {
|
| 351 |
-
"version": "0.21.5",
|
| 352 |
-
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
|
| 353 |
-
"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
|
| 354 |
-
"cpu": [
|
| 355 |
-
"x64"
|
| 356 |
-
],
|
| 357 |
-
"dev": true,
|
| 358 |
-
"optional": true,
|
| 359 |
-
"os": [
|
| 360 |
-
"android"
|
| 361 |
-
],
|
| 362 |
-
"engines": {
|
| 363 |
-
"node": ">=12"
|
| 364 |
-
}
|
| 365 |
-
},
|
| 366 |
-
"node_modules/@esbuild/darwin-arm64": {
|
| 367 |
-
"version": "0.21.5",
|
| 368 |
-
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
|
| 369 |
-
"integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
|
| 370 |
-
"cpu": [
|
| 371 |
-
"arm64"
|
| 372 |
-
],
|
| 373 |
-
"dev": true,
|
| 374 |
-
"optional": true,
|
| 375 |
-
"os": [
|
| 376 |
-
"darwin"
|
| 377 |
-
],
|
| 378 |
-
"engines": {
|
| 379 |
-
"node": ">=12"
|
| 380 |
-
}
|
| 381 |
-
},
|
| 382 |
-
"node_modules/@esbuild/darwin-x64": {
|
| 383 |
-
"version": "0.21.5",
|
| 384 |
-
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
|
| 385 |
-
"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
|
| 386 |
-
"cpu": [
|
| 387 |
-
"x64"
|
| 388 |
-
],
|
| 389 |
-
"dev": true,
|
| 390 |
-
"optional": true,
|
| 391 |
-
"os": [
|
| 392 |
-
"darwin"
|
| 393 |
-
],
|
| 394 |
-
"engines": {
|
| 395 |
-
"node": ">=12"
|
| 396 |
-
}
|
| 397 |
-
},
|
| 398 |
-
"node_modules/@esbuild/freebsd-arm64": {
|
| 399 |
-
"version": "0.21.5",
|
| 400 |
-
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
|
| 401 |
-
"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
|
| 402 |
-
"cpu": [
|
| 403 |
-
"arm64"
|
| 404 |
-
],
|
| 405 |
-
"dev": true,
|
| 406 |
-
"optional": true,
|
| 407 |
-
"os": [
|
| 408 |
-
"freebsd"
|
| 409 |
-
],
|
| 410 |
-
"engines": {
|
| 411 |
-
"node": ">=12"
|
| 412 |
-
}
|
| 413 |
-
},
|
| 414 |
-
"node_modules/@esbuild/freebsd-x64": {
|
| 415 |
-
"version": "0.21.5",
|
| 416 |
-
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
|
| 417 |
-
"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
|
| 418 |
-
"cpu": [
|
| 419 |
-
"x64"
|
| 420 |
-
],
|
| 421 |
-
"dev": true,
|
| 422 |
-
"optional": true,
|
| 423 |
-
"os": [
|
| 424 |
-
"freebsd"
|
| 425 |
-
],
|
| 426 |
-
"engines": {
|
| 427 |
-
"node": ">=12"
|
| 428 |
-
}
|
| 429 |
-
},
|
| 430 |
-
"node_modules/@esbuild/linux-arm": {
|
| 431 |
-
"version": "0.21.5",
|
| 432 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
|
| 433 |
-
"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
|
| 434 |
-
"cpu": [
|
| 435 |
-
"arm"
|
| 436 |
-
],
|
| 437 |
-
"dev": true,
|
| 438 |
-
"optional": true,
|
| 439 |
-
"os": [
|
| 440 |
-
"linux"
|
| 441 |
-
],
|
| 442 |
-
"engines": {
|
| 443 |
-
"node": ">=12"
|
| 444 |
-
}
|
| 445 |
-
},
|
| 446 |
-
"node_modules/@esbuild/linux-arm64": {
|
| 447 |
-
"version": "0.21.5",
|
| 448 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
|
| 449 |
-
"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
|
| 450 |
-
"cpu": [
|
| 451 |
-
"arm64"
|
| 452 |
-
],
|
| 453 |
-
"dev": true,
|
| 454 |
-
"optional": true,
|
| 455 |
-
"os": [
|
| 456 |
-
"linux"
|
| 457 |
-
],
|
| 458 |
-
"engines": {
|
| 459 |
-
"node": ">=12"
|
| 460 |
-
}
|
| 461 |
-
},
|
| 462 |
-
"node_modules/@esbuild/linux-ia32": {
|
| 463 |
-
"version": "0.21.5",
|
| 464 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
|
| 465 |
-
"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
|
| 466 |
-
"cpu": [
|
| 467 |
-
"ia32"
|
| 468 |
-
],
|
| 469 |
-
"dev": true,
|
| 470 |
-
"optional": true,
|
| 471 |
-
"os": [
|
| 472 |
-
"linux"
|
| 473 |
-
],
|
| 474 |
-
"engines": {
|
| 475 |
-
"node": ">=12"
|
| 476 |
-
}
|
| 477 |
-
},
|
| 478 |
-
"node_modules/@esbuild/linux-loong64": {
|
| 479 |
-
"version": "0.21.5",
|
| 480 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
|
| 481 |
-
"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
|
| 482 |
-
"cpu": [
|
| 483 |
-
"loong64"
|
| 484 |
-
],
|
| 485 |
-
"dev": true,
|
| 486 |
-
"optional": true,
|
| 487 |
-
"os": [
|
| 488 |
-
"linux"
|
| 489 |
-
],
|
| 490 |
-
"engines": {
|
| 491 |
-
"node": ">=12"
|
| 492 |
-
}
|
| 493 |
-
},
|
| 494 |
-
"node_modules/@esbuild/linux-mips64el": {
|
| 495 |
-
"version": "0.21.5",
|
| 496 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
|
| 497 |
-
"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
|
| 498 |
-
"cpu": [
|
| 499 |
-
"mips64el"
|
| 500 |
-
],
|
| 501 |
-
"dev": true,
|
| 502 |
-
"optional": true,
|
| 503 |
-
"os": [
|
| 504 |
-
"linux"
|
| 505 |
-
],
|
| 506 |
-
"engines": {
|
| 507 |
-
"node": ">=12"
|
| 508 |
-
}
|
| 509 |
-
},
|
| 510 |
-
"node_modules/@esbuild/linux-ppc64": {
|
| 511 |
-
"version": "0.21.5",
|
| 512 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
|
| 513 |
-
"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
|
| 514 |
-
"cpu": [
|
| 515 |
-
"ppc64"
|
| 516 |
-
],
|
| 517 |
-
"dev": true,
|
| 518 |
-
"optional": true,
|
| 519 |
-
"os": [
|
| 520 |
-
"linux"
|
| 521 |
-
],
|
| 522 |
-
"engines": {
|
| 523 |
-
"node": ">=12"
|
| 524 |
-
}
|
| 525 |
-
},
|
| 526 |
-
"node_modules/@esbuild/linux-riscv64": {
|
| 527 |
-
"version": "0.21.5",
|
| 528 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
|
| 529 |
-
"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
|
| 530 |
-
"cpu": [
|
| 531 |
-
"riscv64"
|
| 532 |
-
],
|
| 533 |
-
"dev": true,
|
| 534 |
-
"optional": true,
|
| 535 |
-
"os": [
|
| 536 |
-
"linux"
|
| 537 |
-
],
|
| 538 |
-
"engines": {
|
| 539 |
-
"node": ">=12"
|
| 540 |
-
}
|
| 541 |
-
},
|
| 542 |
-
"node_modules/@esbuild/linux-s390x": {
|
| 543 |
-
"version": "0.21.5",
|
| 544 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
|
| 545 |
-
"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
|
| 546 |
-
"cpu": [
|
| 547 |
-
"s390x"
|
| 548 |
-
],
|
| 549 |
-
"dev": true,
|
| 550 |
-
"optional": true,
|
| 551 |
-
"os": [
|
| 552 |
-
"linux"
|
| 553 |
-
],
|
| 554 |
-
"engines": {
|
| 555 |
-
"node": ">=12"
|
| 556 |
-
}
|
| 557 |
-
},
|
| 558 |
-
"node_modules/@esbuild/linux-x64": {
|
| 559 |
-
"version": "0.21.5",
|
| 560 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
|
| 561 |
-
"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
|
| 562 |
-
"cpu": [
|
| 563 |
-
"x64"
|
| 564 |
-
],
|
| 565 |
-
"dev": true,
|
| 566 |
-
"optional": true,
|
| 567 |
-
"os": [
|
| 568 |
-
"linux"
|
| 569 |
-
],
|
| 570 |
-
"engines": {
|
| 571 |
-
"node": ">=12"
|
| 572 |
-
}
|
| 573 |
-
},
|
| 574 |
-
"node_modules/@esbuild/netbsd-x64": {
|
| 575 |
-
"version": "0.21.5",
|
| 576 |
-
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
|
| 577 |
-
"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
|
| 578 |
-
"cpu": [
|
| 579 |
-
"x64"
|
| 580 |
-
],
|
| 581 |
-
"dev": true,
|
| 582 |
-
"optional": true,
|
| 583 |
-
"os": [
|
| 584 |
-
"netbsd"
|
| 585 |
-
],
|
| 586 |
-
"engines": {
|
| 587 |
-
"node": ">=12"
|
| 588 |
-
}
|
| 589 |
-
},
|
| 590 |
-
"node_modules/@esbuild/openbsd-x64": {
|
| 591 |
-
"version": "0.21.5",
|
| 592 |
-
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
|
| 593 |
-
"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
|
| 594 |
-
"cpu": [
|
| 595 |
-
"x64"
|
| 596 |
-
],
|
| 597 |
-
"dev": true,
|
| 598 |
-
"optional": true,
|
| 599 |
-
"os": [
|
| 600 |
-
"openbsd"
|
| 601 |
-
],
|
| 602 |
-
"engines": {
|
| 603 |
-
"node": ">=12"
|
| 604 |
-
}
|
| 605 |
-
},
|
| 606 |
-
"node_modules/@esbuild/sunos-x64": {
|
| 607 |
-
"version": "0.21.5",
|
| 608 |
-
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
|
| 609 |
-
"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
|
| 610 |
-
"cpu": [
|
| 611 |
-
"x64"
|
| 612 |
-
],
|
| 613 |
-
"dev": true,
|
| 614 |
-
"optional": true,
|
| 615 |
-
"os": [
|
| 616 |
-
"sunos"
|
| 617 |
-
],
|
| 618 |
-
"engines": {
|
| 619 |
-
"node": ">=12"
|
| 620 |
-
}
|
| 621 |
-
},
|
| 622 |
-
"node_modules/@esbuild/win32-arm64": {
|
| 623 |
-
"version": "0.21.5",
|
| 624 |
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
|
| 625 |
-
"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
|
| 626 |
-
"cpu": [
|
| 627 |
-
"arm64"
|
| 628 |
-
],
|
| 629 |
-
"dev": true,
|
| 630 |
-
"optional": true,
|
| 631 |
-
"os": [
|
| 632 |
-
"win32"
|
| 633 |
-
],
|
| 634 |
-
"engines": {
|
| 635 |
-
"node": ">=12"
|
| 636 |
-
}
|
| 637 |
-
},
|
| 638 |
-
"node_modules/@esbuild/win32-ia32": {
|
| 639 |
-
"version": "0.21.5",
|
| 640 |
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
|
| 641 |
-
"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
|
| 642 |
-
"cpu": [
|
| 643 |
-
"ia32"
|
| 644 |
-
],
|
| 645 |
-
"dev": true,
|
| 646 |
-
"optional": true,
|
| 647 |
-
"os": [
|
| 648 |
-
"win32"
|
| 649 |
-
],
|
| 650 |
-
"engines": {
|
| 651 |
-
"node": ">=12"
|
| 652 |
-
}
|
| 653 |
-
},
|
| 654 |
-
"node_modules/@esbuild/win32-x64": {
|
| 655 |
-
"version": "0.21.5",
|
| 656 |
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
|
| 657 |
-
"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
|
| 658 |
-
"cpu": [
|
| 659 |
-
"x64"
|
| 660 |
-
],
|
| 661 |
-
"dev": true,
|
| 662 |
-
"optional": true,
|
| 663 |
-
"os": [
|
| 664 |
-
"win32"
|
| 665 |
-
],
|
| 666 |
-
"engines": {
|
| 667 |
-
"node": ">=12"
|
| 668 |
-
}
|
| 669 |
-
},
|
| 670 |
-
"node_modules/@jridgewell/gen-mapping": {
|
| 671 |
-
"version": "0.3.12",
|
| 672 |
-
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz",
|
| 673 |
-
"integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==",
|
| 674 |
-
"dev": true,
|
| 675 |
-
"dependencies": {
|
| 676 |
-
"@jridgewell/sourcemap-codec": "^1.5.0",
|
| 677 |
-
"@jridgewell/trace-mapping": "^0.3.24"
|
| 678 |
-
}
|
| 679 |
-
},
|
| 680 |
-
"node_modules/@jridgewell/resolve-uri": {
|
| 681 |
-
"version": "3.1.2",
|
| 682 |
-
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
| 683 |
-
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
| 684 |
-
"dev": true,
|
| 685 |
-
"engines": {
|
| 686 |
-
"node": ">=6.0.0"
|
| 687 |
-
}
|
| 688 |
-
},
|
| 689 |
-
"node_modules/@jridgewell/sourcemap-codec": {
|
| 690 |
-
"version": "1.5.4",
|
| 691 |
-
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz",
|
| 692 |
-
"integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==",
|
| 693 |
-
"dev": true
|
| 694 |
-
},
|
| 695 |
-
"node_modules/@jridgewell/trace-mapping": {
|
| 696 |
-
"version": "0.3.29",
|
| 697 |
-
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz",
|
| 698 |
-
"integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==",
|
| 699 |
-
"dev": true,
|
| 700 |
-
"dependencies": {
|
| 701 |
-
"@jridgewell/resolve-uri": "^3.1.0",
|
| 702 |
-
"@jridgewell/sourcemap-codec": "^1.4.14"
|
| 703 |
-
}
|
| 704 |
-
},
|
| 705 |
-
"node_modules/@rolldown/pluginutils": {
|
| 706 |
-
"version": "1.0.0-beta.27",
|
| 707 |
-
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
|
| 708 |
-
"integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
|
| 709 |
-
"dev": true
|
| 710 |
-
},
|
| 711 |
-
"node_modules/@rollup/rollup-android-arm-eabi": {
|
| 712 |
-
"version": "4.46.1",
|
| 713 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.1.tgz",
|
| 714 |
-
"integrity": "sha512-oENme6QxtLCqjChRUUo3S6X8hjCXnWmJWnedD7VbGML5GUtaOtAyx+fEEXnBXVf0CBZApMQU0Idwi0FmyxzQhw==",
|
| 715 |
-
"cpu": [
|
| 716 |
-
"arm"
|
| 717 |
-
],
|
| 718 |
-
"dev": true,
|
| 719 |
-
"optional": true,
|
| 720 |
-
"os": [
|
| 721 |
-
"android"
|
| 722 |
-
]
|
| 723 |
-
},
|
| 724 |
-
"node_modules/@rollup/rollup-android-arm64": {
|
| 725 |
-
"version": "4.46.1",
|
| 726 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.1.tgz",
|
| 727 |
-
"integrity": "sha512-OikvNT3qYTl9+4qQ9Bpn6+XHM+ogtFadRLuT2EXiFQMiNkXFLQfNVppi5o28wvYdHL2s3fM0D/MZJ8UkNFZWsw==",
|
| 728 |
-
"cpu": [
|
| 729 |
-
"arm64"
|
| 730 |
-
],
|
| 731 |
-
"dev": true,
|
| 732 |
-
"optional": true,
|
| 733 |
-
"os": [
|
| 734 |
-
"android"
|
| 735 |
-
]
|
| 736 |
-
},
|
| 737 |
-
"node_modules/@rollup/rollup-darwin-arm64": {
|
| 738 |
-
"version": "4.46.1",
|
| 739 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.1.tgz",
|
| 740 |
-
"integrity": "sha512-EFYNNGij2WllnzljQDQnlFTXzSJw87cpAs4TVBAWLdkvic5Uh5tISrIL6NRcxoh/b2EFBG/TK8hgRrGx94zD4A==",
|
| 741 |
-
"cpu": [
|
| 742 |
-
"arm64"
|
| 743 |
-
],
|
| 744 |
-
"dev": true,
|
| 745 |
-
"optional": true,
|
| 746 |
-
"os": [
|
| 747 |
-
"darwin"
|
| 748 |
-
]
|
| 749 |
-
},
|
| 750 |
-
"node_modules/@rollup/rollup-darwin-x64": {
|
| 751 |
-
"version": "4.46.1",
|
| 752 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.1.tgz",
|
| 753 |
-
"integrity": "sha512-ZaNH06O1KeTug9WI2+GRBE5Ujt9kZw4a1+OIwnBHal92I8PxSsl5KpsrPvthRynkhMck4XPdvY0z26Cym/b7oA==",
|
| 754 |
-
"cpu": [
|
| 755 |
-
"x64"
|
| 756 |
-
],
|
| 757 |
-
"dev": true,
|
| 758 |
-
"optional": true,
|
| 759 |
-
"os": [
|
| 760 |
-
"darwin"
|
| 761 |
-
]
|
| 762 |
-
},
|
| 763 |
-
"node_modules/@rollup/rollup-freebsd-arm64": {
|
| 764 |
-
"version": "4.46.1",
|
| 765 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.1.tgz",
|
| 766 |
-
"integrity": "sha512-n4SLVebZP8uUlJ2r04+g2U/xFeiQlw09Me5UFqny8HGbARl503LNH5CqFTb5U5jNxTouhRjai6qPT0CR5c/Iig==",
|
| 767 |
-
"cpu": [
|
| 768 |
-
"arm64"
|
| 769 |
-
],
|
| 770 |
-
"dev": true,
|
| 771 |
-
"optional": true,
|
| 772 |
-
"os": [
|
| 773 |
-
"freebsd"
|
| 774 |
-
]
|
| 775 |
-
},
|
| 776 |
-
"node_modules/@rollup/rollup-freebsd-x64": {
|
| 777 |
-
"version": "4.46.1",
|
| 778 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.1.tgz",
|
| 779 |
-
"integrity": "sha512-8vu9c02F16heTqpvo3yeiu7Vi1REDEC/yES/dIfq3tSXe6mLndiwvYr3AAvd1tMNUqE9yeGYa5w7PRbI5QUV+w==",
|
| 780 |
-
"cpu": [
|
| 781 |
-
"x64"
|
| 782 |
-
],
|
| 783 |
-
"dev": true,
|
| 784 |
-
"optional": true,
|
| 785 |
-
"os": [
|
| 786 |
-
"freebsd"
|
| 787 |
-
]
|
| 788 |
-
},
|
| 789 |
-
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
| 790 |
-
"version": "4.46.1",
|
| 791 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.1.tgz",
|
| 792 |
-
"integrity": "sha512-K4ncpWl7sQuyp6rWiGUvb6Q18ba8mzM0rjWJ5JgYKlIXAau1db7hZnR0ldJvqKWWJDxqzSLwGUhA4jp+KqgDtQ==",
|
| 793 |
-
"cpu": [
|
| 794 |
-
"arm"
|
| 795 |
-
],
|
| 796 |
-
"dev": true,
|
| 797 |
-
"optional": true,
|
| 798 |
-
"os": [
|
| 799 |
-
"linux"
|
| 800 |
-
]
|
| 801 |
-
},
|
| 802 |
-
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
| 803 |
-
"version": "4.46.1",
|
| 804 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.1.tgz",
|
| 805 |
-
"integrity": "sha512-YykPnXsjUjmXE6j6k2QBBGAn1YsJUix7pYaPLK3RVE0bQL2jfdbfykPxfF8AgBlqtYbfEnYHmLXNa6QETjdOjQ==",
|
| 806 |
-
"cpu": [
|
| 807 |
-
"arm"
|
| 808 |
-
],
|
| 809 |
-
"dev": true,
|
| 810 |
-
"optional": true,
|
| 811 |
-
"os": [
|
| 812 |
-
"linux"
|
| 813 |
-
]
|
| 814 |
-
},
|
| 815 |
-
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
| 816 |
-
"version": "4.46.1",
|
| 817 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.1.tgz",
|
| 818 |
-
"integrity": "sha512-kKvqBGbZ8i9pCGW3a1FH3HNIVg49dXXTsChGFsHGXQaVJPLA4f/O+XmTxfklhccxdF5FefUn2hvkoGJH0ScWOA==",
|
| 819 |
-
"cpu": [
|
| 820 |
-
"arm64"
|
| 821 |
-
],
|
| 822 |
-
"dev": true,
|
| 823 |
-
"optional": true,
|
| 824 |
-
"os": [
|
| 825 |
-
"linux"
|
| 826 |
-
]
|
| 827 |
-
},
|
| 828 |
-
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
| 829 |
-
"version": "4.46.1",
|
| 830 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.1.tgz",
|
| 831 |
-
"integrity": "sha512-zzX5nTw1N1plmqC9RGC9vZHFuiM7ZP7oSWQGqpbmfjK7p947D518cVK1/MQudsBdcD84t6k70WNczJOct6+hdg==",
|
| 832 |
-
"cpu": [
|
| 833 |
-
"arm64"
|
| 834 |
-
],
|
| 835 |
-
"dev": true,
|
| 836 |
-
"optional": true,
|
| 837 |
-
"os": [
|
| 838 |
-
"linux"
|
| 839 |
-
]
|
| 840 |
-
},
|
| 841 |
-
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
| 842 |
-
"version": "4.46.1",
|
| 843 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.1.tgz",
|
| 844 |
-
"integrity": "sha512-O8CwgSBo6ewPpktFfSDgB6SJN9XDcPSvuwxfejiddbIC/hn9Tg6Ai0f0eYDf3XvB/+PIWzOQL+7+TZoB8p9Yuw==",
|
| 845 |
-
"cpu": [
|
| 846 |
-
"loong64"
|
| 847 |
-
],
|
| 848 |
-
"dev": true,
|
| 849 |
-
"optional": true,
|
| 850 |
-
"os": [
|
| 851 |
-
"linux"
|
| 852 |
-
]
|
| 853 |
-
},
|
| 854 |
-
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
| 855 |
-
"version": "4.46.1",
|
| 856 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.1.tgz",
|
| 857 |
-
"integrity": "sha512-JnCfFVEKeq6G3h3z8e60kAp8Rd7QVnWCtPm7cxx+5OtP80g/3nmPtfdCXbVl063e3KsRnGSKDHUQMydmzc/wBA==",
|
| 858 |
-
"cpu": [
|
| 859 |
-
"ppc64"
|
| 860 |
-
],
|
| 861 |
-
"dev": true,
|
| 862 |
-
"optional": true,
|
| 863 |
-
"os": [
|
| 864 |
-
"linux"
|
| 865 |
-
]
|
| 866 |
-
},
|
| 867 |
-
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
| 868 |
-
"version": "4.46.1",
|
| 869 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.1.tgz",
|
| 870 |
-
"integrity": "sha512-dVxuDqS237eQXkbYzQQfdf/njgeNw6LZuVyEdUaWwRpKHhsLI+y4H/NJV8xJGU19vnOJCVwaBFgr936FHOnJsQ==",
|
| 871 |
-
"cpu": [
|
| 872 |
-
"riscv64"
|
| 873 |
-
],
|
| 874 |
-
"dev": true,
|
| 875 |
-
"optional": true,
|
| 876 |
-
"os": [
|
| 877 |
-
"linux"
|
| 878 |
-
]
|
| 879 |
-
},
|
| 880 |
-
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
| 881 |
-
"version": "4.46.1",
|
| 882 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.1.tgz",
|
| 883 |
-
"integrity": "sha512-CvvgNl2hrZrTR9jXK1ye0Go0HQRT6ohQdDfWR47/KFKiLd5oN5T14jRdUVGF4tnsN8y9oSfMOqH6RuHh+ck8+w==",
|
| 884 |
-
"cpu": [
|
| 885 |
-
"riscv64"
|
| 886 |
-
],
|
| 887 |
-
"dev": true,
|
| 888 |
-
"optional": true,
|
| 889 |
-
"os": [
|
| 890 |
-
"linux"
|
| 891 |
-
]
|
| 892 |
-
},
|
| 893 |
-
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
| 894 |
-
"version": "4.46.1",
|
| 895 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.1.tgz",
|
| 896 |
-
"integrity": "sha512-x7ANt2VOg2565oGHJ6rIuuAon+A8sfe1IeUx25IKqi49OjSr/K3awoNqr9gCwGEJo9OuXlOn+H2p1VJKx1psxA==",
|
| 897 |
-
"cpu": [
|
| 898 |
-
"s390x"
|
| 899 |
-
],
|
| 900 |
-
"dev": true,
|
| 901 |
-
"optional": true,
|
| 902 |
-
"os": [
|
| 903 |
-
"linux"
|
| 904 |
-
]
|
| 905 |
-
},
|
| 906 |
-
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
| 907 |
-
"version": "4.46.1",
|
| 908 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.1.tgz",
|
| 909 |
-
"integrity": "sha512-9OADZYryz/7E8/qt0vnaHQgmia2Y0wrjSSn1V/uL+zw/i7NUhxbX4cHXdEQ7dnJgzYDS81d8+tf6nbIdRFZQoQ==",
|
| 910 |
-
"cpu": [
|
| 911 |
-
"x64"
|
| 912 |
-
],
|
| 913 |
-
"dev": true,
|
| 914 |
-
"optional": true,
|
| 915 |
-
"os": [
|
| 916 |
-
"linux"
|
| 917 |
-
]
|
| 918 |
-
},
|
| 919 |
-
"node_modules/@rollup/rollup-linux-x64-musl": {
|
| 920 |
-
"version": "4.46.1",
|
| 921 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.1.tgz",
|
| 922 |
-
"integrity": "sha512-NuvSCbXEKY+NGWHyivzbjSVJi68Xfq1VnIvGmsuXs6TCtveeoDRKutI5vf2ntmNnVq64Q4zInet0UDQ+yMB6tA==",
|
| 923 |
-
"cpu": [
|
| 924 |
-
"x64"
|
| 925 |
-
],
|
| 926 |
-
"dev": true,
|
| 927 |
-
"optional": true,
|
| 928 |
-
"os": [
|
| 929 |
-
"linux"
|
| 930 |
-
]
|
| 931 |
-
},
|
| 932 |
-
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
| 933 |
-
"version": "4.46.1",
|
| 934 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.1.tgz",
|
| 935 |
-
"integrity": "sha512-mWz+6FSRb82xuUMMV1X3NGiaPFqbLN9aIueHleTZCc46cJvwTlvIh7reQLk4p97dv0nddyewBhwzryBHH7wtPw==",
|
| 936 |
-
"cpu": [
|
| 937 |
-
"arm64"
|
| 938 |
-
],
|
| 939 |
-
"dev": true,
|
| 940 |
-
"optional": true,
|
| 941 |
-
"os": [
|
| 942 |
-
"win32"
|
| 943 |
-
]
|
| 944 |
-
},
|
| 945 |
-
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
| 946 |
-
"version": "4.46.1",
|
| 947 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.1.tgz",
|
| 948 |
-
"integrity": "sha512-7Thzy9TMXDw9AU4f4vsLNBxh7/VOKuXi73VH3d/kHGr0tZ3x/ewgL9uC7ojUKmH1/zvmZe2tLapYcZllk3SO8Q==",
|
| 949 |
-
"cpu": [
|
| 950 |
-
"ia32"
|
| 951 |
-
],
|
| 952 |
-
"dev": true,
|
| 953 |
-
"optional": true,
|
| 954 |
-
"os": [
|
| 955 |
-
"win32"
|
| 956 |
-
]
|
| 957 |
-
},
|
| 958 |
-
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
| 959 |
-
"version": "4.46.1",
|
| 960 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.1.tgz",
|
| 961 |
-
"integrity": "sha512-7GVB4luhFmGUNXXJhH2jJwZCFB3pIOixv2E3s17GQHBFUOQaISlt7aGcQgqvCaDSxTZJUzlK/QJ1FN8S94MrzQ==",
|
| 962 |
-
"cpu": [
|
| 963 |
-
"x64"
|
| 964 |
-
],
|
| 965 |
-
"dev": true,
|
| 966 |
-
"optional": true,
|
| 967 |
-
"os": [
|
| 968 |
-
"win32"
|
| 969 |
-
]
|
| 970 |
-
},
|
| 971 |
-
"node_modules/@types/babel__core": {
|
| 972 |
-
"version": "7.20.5",
|
| 973 |
-
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
| 974 |
-
"integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
|
| 975 |
-
"dev": true,
|
| 976 |
-
"dependencies": {
|
| 977 |
-
"@babel/parser": "^7.20.7",
|
| 978 |
-
"@babel/types": "^7.20.7",
|
| 979 |
-
"@types/babel__generator": "*",
|
| 980 |
-
"@types/babel__template": "*",
|
| 981 |
-
"@types/babel__traverse": "*"
|
| 982 |
-
}
|
| 983 |
-
},
|
| 984 |
-
"node_modules/@types/babel__generator": {
|
| 985 |
-
"version": "7.27.0",
|
| 986 |
-
"resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
|
| 987 |
-
"integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
|
| 988 |
-
"dev": true,
|
| 989 |
-
"dependencies": {
|
| 990 |
-
"@babel/types": "^7.0.0"
|
| 991 |
-
}
|
| 992 |
-
},
|
| 993 |
-
"node_modules/@types/babel__template": {
|
| 994 |
-
"version": "7.4.4",
|
| 995 |
-
"resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
|
| 996 |
-
"integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
|
| 997 |
-
"dev": true,
|
| 998 |
-
"dependencies": {
|
| 999 |
-
"@babel/parser": "^7.1.0",
|
| 1000 |
-
"@babel/types": "^7.0.0"
|
| 1001 |
-
}
|
| 1002 |
-
},
|
| 1003 |
-
"node_modules/@types/babel__traverse": {
|
| 1004 |
-
"version": "7.20.7",
|
| 1005 |
-
"resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz",
|
| 1006 |
-
"integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==",
|
| 1007 |
-
"dev": true,
|
| 1008 |
-
"dependencies": {
|
| 1009 |
-
"@babel/types": "^7.20.7"
|
| 1010 |
-
}
|
| 1011 |
-
},
|
| 1012 |
-
"node_modules/@types/chrome": {
|
| 1013 |
-
"version": "0.0.315",
|
| 1014 |
-
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.315.tgz",
|
| 1015 |
-
"integrity": "sha512-Oy1dYWkr6BCmgwBtOngLByCHstQ3whltZg7/7lubgIZEYvKobDneqplgc6LKERNRBwckFviV4UU5AZZNUFrJ4A==",
|
| 1016 |
-
"dev": true,
|
| 1017 |
-
"dependencies": {
|
| 1018 |
-
"@types/filesystem": "*",
|
| 1019 |
-
"@types/har-format": "*"
|
| 1020 |
-
}
|
| 1021 |
-
},
|
| 1022 |
-
"node_modules/@types/estree": {
|
| 1023 |
-
"version": "1.0.8",
|
| 1024 |
-
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
| 1025 |
-
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
| 1026 |
-
"dev": true
|
| 1027 |
-
},
|
| 1028 |
-
"node_modules/@types/filesystem": {
|
| 1029 |
-
"version": "0.0.36",
|
| 1030 |
-
"resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.36.tgz",
|
| 1031 |
-
"integrity": "sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==",
|
| 1032 |
-
"dev": true,
|
| 1033 |
-
"dependencies": {
|
| 1034 |
-
"@types/filewriter": "*"
|
| 1035 |
-
}
|
| 1036 |
-
},
|
| 1037 |
-
"node_modules/@types/filewriter": {
|
| 1038 |
-
"version": "0.0.33",
|
| 1039 |
-
"resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.33.tgz",
|
| 1040 |
-
"integrity": "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==",
|
| 1041 |
-
"dev": true
|
| 1042 |
-
},
|
| 1043 |
-
"node_modules/@types/har-format": {
|
| 1044 |
-
"version": "1.2.16",
|
| 1045 |
-
"resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.16.tgz",
|
| 1046 |
-
"integrity": "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==",
|
| 1047 |
-
"dev": true
|
| 1048 |
-
},
|
| 1049 |
-
"node_modules/@types/prop-types": {
|
| 1050 |
-
"version": "15.7.15",
|
| 1051 |
-
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
|
| 1052 |
-
"integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
|
| 1053 |
-
"dev": true
|
| 1054 |
-
},
|
| 1055 |
-
"node_modules/@types/react": {
|
| 1056 |
-
"version": "18.3.23",
|
| 1057 |
-
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.23.tgz",
|
| 1058 |
-
"integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==",
|
| 1059 |
-
"dev": true,
|
| 1060 |
-
"dependencies": {
|
| 1061 |
-
"@types/prop-types": "*",
|
| 1062 |
-
"csstype": "^3.0.2"
|
| 1063 |
-
}
|
| 1064 |
-
},
|
| 1065 |
-
"node_modules/@types/react-dom": {
|
| 1066 |
-
"version": "18.3.7",
|
| 1067 |
-
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
|
| 1068 |
-
"integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
|
| 1069 |
-
"dev": true,
|
| 1070 |
-
"peerDependencies": {
|
| 1071 |
-
"@types/react": "^18.0.0"
|
| 1072 |
-
}
|
| 1073 |
-
},
|
| 1074 |
-
"node_modules/@vitejs/plugin-react": {
|
| 1075 |
-
"version": "4.7.0",
|
| 1076 |
-
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
|
| 1077 |
-
"integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
|
| 1078 |
-
"dev": true,
|
| 1079 |
-
"dependencies": {
|
| 1080 |
-
"@babel/core": "^7.28.0",
|
| 1081 |
-
"@babel/plugin-transform-react-jsx-self": "^7.27.1",
|
| 1082 |
-
"@babel/plugin-transform-react-jsx-source": "^7.27.1",
|
| 1083 |
-
"@rolldown/pluginutils": "1.0.0-beta.27",
|
| 1084 |
-
"@types/babel__core": "^7.20.5",
|
| 1085 |
-
"react-refresh": "^0.17.0"
|
| 1086 |
-
},
|
| 1087 |
-
"engines": {
|
| 1088 |
-
"node": "^14.18.0 || >=16.0.0"
|
| 1089 |
-
},
|
| 1090 |
-
"peerDependencies": {
|
| 1091 |
-
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
| 1092 |
-
}
|
| 1093 |
-
},
|
| 1094 |
-
"node_modules/anymatch": {
|
| 1095 |
-
"version": "3.1.3",
|
| 1096 |
-
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
| 1097 |
-
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
| 1098 |
-
"dev": true,
|
| 1099 |
-
"license": "ISC",
|
| 1100 |
-
"dependencies": {
|
| 1101 |
-
"normalize-path": "^3.0.0",
|
| 1102 |
-
"picomatch": "^2.0.4"
|
| 1103 |
-
},
|
| 1104 |
-
"engines": {
|
| 1105 |
-
"node": ">= 8"
|
| 1106 |
-
}
|
| 1107 |
-
},
|
| 1108 |
-
"node_modules/binary-extensions": {
|
| 1109 |
-
"version": "2.3.0",
|
| 1110 |
-
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
| 1111 |
-
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
|
| 1112 |
-
"dev": true,
|
| 1113 |
-
"license": "MIT",
|
| 1114 |
-
"engines": {
|
| 1115 |
-
"node": ">=8"
|
| 1116 |
-
},
|
| 1117 |
-
"funding": {
|
| 1118 |
-
"url": "https://github.com/sponsors/sindresorhus"
|
| 1119 |
-
}
|
| 1120 |
-
},
|
| 1121 |
-
"node_modules/braces": {
|
| 1122 |
-
"version": "3.0.3",
|
| 1123 |
-
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
| 1124 |
-
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
| 1125 |
-
"dev": true,
|
| 1126 |
-
"license": "MIT",
|
| 1127 |
-
"dependencies": {
|
| 1128 |
-
"fill-range": "^7.1.1"
|
| 1129 |
-
},
|
| 1130 |
-
"engines": {
|
| 1131 |
-
"node": ">=8"
|
| 1132 |
-
}
|
| 1133 |
-
},
|
| 1134 |
-
"node_modules/browserslist": {
|
| 1135 |
-
"version": "4.25.1",
|
| 1136 |
-
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz",
|
| 1137 |
-
"integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==",
|
| 1138 |
-
"dev": true,
|
| 1139 |
-
"funding": [
|
| 1140 |
-
{
|
| 1141 |
-
"type": "opencollective",
|
| 1142 |
-
"url": "https://opencollective.com/browserslist"
|
| 1143 |
-
},
|
| 1144 |
-
{
|
| 1145 |
-
"type": "tidelift",
|
| 1146 |
-
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
| 1147 |
-
},
|
| 1148 |
-
{
|
| 1149 |
-
"type": "github",
|
| 1150 |
-
"url": "https://github.com/sponsors/ai"
|
| 1151 |
-
}
|
| 1152 |
-
],
|
| 1153 |
-
"dependencies": {
|
| 1154 |
-
"caniuse-lite": "^1.0.30001726",
|
| 1155 |
-
"electron-to-chromium": "^1.5.173",
|
| 1156 |
-
"node-releases": "^2.0.19",
|
| 1157 |
-
"update-browserslist-db": "^1.1.3"
|
| 1158 |
-
},
|
| 1159 |
-
"bin": {
|
| 1160 |
-
"browserslist": "cli.js"
|
| 1161 |
-
},
|
| 1162 |
-
"engines": {
|
| 1163 |
-
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
| 1164 |
-
}
|
| 1165 |
-
},
|
| 1166 |
-
"node_modules/caniuse-lite": {
|
| 1167 |
-
"version": "1.0.30001727",
|
| 1168 |
-
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz",
|
| 1169 |
-
"integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==",
|
| 1170 |
-
"dev": true,
|
| 1171 |
-
"funding": [
|
| 1172 |
-
{
|
| 1173 |
-
"type": "opencollective",
|
| 1174 |
-
"url": "https://opencollective.com/browserslist"
|
| 1175 |
-
},
|
| 1176 |
-
{
|
| 1177 |
-
"type": "tidelift",
|
| 1178 |
-
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
|
| 1179 |
-
},
|
| 1180 |
-
{
|
| 1181 |
-
"type": "github",
|
| 1182 |
-
"url": "https://github.com/sponsors/ai"
|
| 1183 |
-
}
|
| 1184 |
-
]
|
| 1185 |
-
},
|
| 1186 |
-
"node_modules/chokidar": {
|
| 1187 |
-
"version": "3.6.0",
|
| 1188 |
-
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
| 1189 |
-
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
| 1190 |
-
"dev": true,
|
| 1191 |
-
"license": "MIT",
|
| 1192 |
-
"dependencies": {
|
| 1193 |
-
"anymatch": "~3.1.2",
|
| 1194 |
-
"braces": "~3.0.2",
|
| 1195 |
-
"glob-parent": "~5.1.2",
|
| 1196 |
-
"is-binary-path": "~2.1.0",
|
| 1197 |
-
"is-glob": "~4.0.1",
|
| 1198 |
-
"normalize-path": "~3.0.0",
|
| 1199 |
-
"readdirp": "~3.6.0"
|
| 1200 |
-
},
|
| 1201 |
-
"engines": {
|
| 1202 |
-
"node": ">= 8.10.0"
|
| 1203 |
-
},
|
| 1204 |
-
"funding": {
|
| 1205 |
-
"url": "https://paulmillr.com/funding/"
|
| 1206 |
-
},
|
| 1207 |
-
"optionalDependencies": {
|
| 1208 |
-
"fsevents": "~2.3.2"
|
| 1209 |
-
}
|
| 1210 |
-
},
|
| 1211 |
-
"node_modules/convert-source-map": {
|
| 1212 |
-
"version": "2.0.0",
|
| 1213 |
-
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
| 1214 |
-
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
|
| 1215 |
-
"dev": true
|
| 1216 |
-
},
|
| 1217 |
-
"node_modules/csstype": {
|
| 1218 |
-
"version": "3.1.3",
|
| 1219 |
-
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
| 1220 |
-
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
| 1221 |
-
"dev": true
|
| 1222 |
-
},
|
| 1223 |
-
"node_modules/debug": {
|
| 1224 |
-
"version": "4.4.1",
|
| 1225 |
-
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
| 1226 |
-
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
| 1227 |
-
"dev": true,
|
| 1228 |
-
"dependencies": {
|
| 1229 |
-
"ms": "^2.1.3"
|
| 1230 |
-
},
|
| 1231 |
-
"engines": {
|
| 1232 |
-
"node": ">=6.0"
|
| 1233 |
-
},
|
| 1234 |
-
"peerDependenciesMeta": {
|
| 1235 |
-
"supports-color": {
|
| 1236 |
-
"optional": true
|
| 1237 |
-
}
|
| 1238 |
-
}
|
| 1239 |
-
},
|
| 1240 |
-
"node_modules/electron-to-chromium": {
|
| 1241 |
-
"version": "1.5.192",
|
| 1242 |
-
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.192.tgz",
|
| 1243 |
-
"integrity": "sha512-rP8Ez0w7UNw/9j5eSXCe10o1g/8B1P5SM90PCCMVkIRQn2R0LEHWz4Eh9RnxkniuDe1W0cTSOB3MLlkTGDcuCg==",
|
| 1244 |
-
"dev": true
|
| 1245 |
-
},
|
| 1246 |
-
"node_modules/esbuild": {
|
| 1247 |
-
"version": "0.21.5",
|
| 1248 |
-
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
|
| 1249 |
-
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
|
| 1250 |
-
"dev": true,
|
| 1251 |
-
"hasInstallScript": true,
|
| 1252 |
-
"bin": {
|
| 1253 |
-
"esbuild": "bin/esbuild"
|
| 1254 |
-
},
|
| 1255 |
-
"engines": {
|
| 1256 |
-
"node": ">=12"
|
| 1257 |
-
},
|
| 1258 |
-
"optionalDependencies": {
|
| 1259 |
-
"@esbuild/aix-ppc64": "0.21.5",
|
| 1260 |
-
"@esbuild/android-arm": "0.21.5",
|
| 1261 |
-
"@esbuild/android-arm64": "0.21.5",
|
| 1262 |
-
"@esbuild/android-x64": "0.21.5",
|
| 1263 |
-
"@esbuild/darwin-arm64": "0.21.5",
|
| 1264 |
-
"@esbuild/darwin-x64": "0.21.5",
|
| 1265 |
-
"@esbuild/freebsd-arm64": "0.21.5",
|
| 1266 |
-
"@esbuild/freebsd-x64": "0.21.5",
|
| 1267 |
-
"@esbuild/linux-arm": "0.21.5",
|
| 1268 |
-
"@esbuild/linux-arm64": "0.21.5",
|
| 1269 |
-
"@esbuild/linux-ia32": "0.21.5",
|
| 1270 |
-
"@esbuild/linux-loong64": "0.21.5",
|
| 1271 |
-
"@esbuild/linux-mips64el": "0.21.5",
|
| 1272 |
-
"@esbuild/linux-ppc64": "0.21.5",
|
| 1273 |
-
"@esbuild/linux-riscv64": "0.21.5",
|
| 1274 |
-
"@esbuild/linux-s390x": "0.21.5",
|
| 1275 |
-
"@esbuild/linux-x64": "0.21.5",
|
| 1276 |
-
"@esbuild/netbsd-x64": "0.21.5",
|
| 1277 |
-
"@esbuild/openbsd-x64": "0.21.5",
|
| 1278 |
-
"@esbuild/sunos-x64": "0.21.5",
|
| 1279 |
-
"@esbuild/win32-arm64": "0.21.5",
|
| 1280 |
-
"@esbuild/win32-ia32": "0.21.5",
|
| 1281 |
-
"@esbuild/win32-x64": "0.21.5"
|
| 1282 |
-
}
|
| 1283 |
-
},
|
| 1284 |
-
"node_modules/escalade": {
|
| 1285 |
-
"version": "3.2.0",
|
| 1286 |
-
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
| 1287 |
-
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
|
| 1288 |
-
"dev": true,
|
| 1289 |
-
"engines": {
|
| 1290 |
-
"node": ">=6"
|
| 1291 |
-
}
|
| 1292 |
-
},
|
| 1293 |
-
"node_modules/fill-range": {
|
| 1294 |
-
"version": "7.1.1",
|
| 1295 |
-
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
| 1296 |
-
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
| 1297 |
-
"dev": true,
|
| 1298 |
-
"license": "MIT",
|
| 1299 |
-
"dependencies": {
|
| 1300 |
-
"to-regex-range": "^5.0.1"
|
| 1301 |
-
},
|
| 1302 |
-
"engines": {
|
| 1303 |
-
"node": ">=8"
|
| 1304 |
-
}
|
| 1305 |
-
},
|
| 1306 |
-
"node_modules/fs-extra": {
|
| 1307 |
-
"version": "11.3.0",
|
| 1308 |
-
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
|
| 1309 |
-
"integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
|
| 1310 |
-
"dev": true,
|
| 1311 |
-
"license": "MIT",
|
| 1312 |
-
"dependencies": {
|
| 1313 |
-
"graceful-fs": "^4.2.0",
|
| 1314 |
-
"jsonfile": "^6.0.1",
|
| 1315 |
-
"universalify": "^2.0.0"
|
| 1316 |
-
},
|
| 1317 |
-
"engines": {
|
| 1318 |
-
"node": ">=14.14"
|
| 1319 |
-
}
|
| 1320 |
-
},
|
| 1321 |
-
"node_modules/fsevents": {
|
| 1322 |
-
"version": "2.3.3",
|
| 1323 |
-
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
| 1324 |
-
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
| 1325 |
-
"dev": true,
|
| 1326 |
-
"hasInstallScript": true,
|
| 1327 |
-
"optional": true,
|
| 1328 |
-
"os": [
|
| 1329 |
-
"darwin"
|
| 1330 |
-
],
|
| 1331 |
-
"engines": {
|
| 1332 |
-
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
| 1333 |
-
}
|
| 1334 |
-
},
|
| 1335 |
-
"node_modules/gensync": {
|
| 1336 |
-
"version": "1.0.0-beta.2",
|
| 1337 |
-
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
| 1338 |
-
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
|
| 1339 |
-
"dev": true,
|
| 1340 |
-
"engines": {
|
| 1341 |
-
"node": ">=6.9.0"
|
| 1342 |
-
}
|
| 1343 |
-
},
|
| 1344 |
-
"node_modules/glob-parent": {
|
| 1345 |
-
"version": "5.1.2",
|
| 1346 |
-
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
| 1347 |
-
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
| 1348 |
-
"dev": true,
|
| 1349 |
-
"license": "ISC",
|
| 1350 |
-
"dependencies": {
|
| 1351 |
-
"is-glob": "^4.0.1"
|
| 1352 |
-
},
|
| 1353 |
-
"engines": {
|
| 1354 |
-
"node": ">= 6"
|
| 1355 |
-
}
|
| 1356 |
-
},
|
| 1357 |
-
"node_modules/graceful-fs": {
|
| 1358 |
-
"version": "4.2.11",
|
| 1359 |
-
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
| 1360 |
-
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
| 1361 |
-
"dev": true,
|
| 1362 |
-
"license": "ISC"
|
| 1363 |
-
},
|
| 1364 |
-
"node_modules/is-binary-path": {
|
| 1365 |
-
"version": "2.1.0",
|
| 1366 |
-
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
| 1367 |
-
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
| 1368 |
-
"dev": true,
|
| 1369 |
-
"license": "MIT",
|
| 1370 |
-
"dependencies": {
|
| 1371 |
-
"binary-extensions": "^2.0.0"
|
| 1372 |
-
},
|
| 1373 |
-
"engines": {
|
| 1374 |
-
"node": ">=8"
|
| 1375 |
-
}
|
| 1376 |
-
},
|
| 1377 |
-
"node_modules/is-extglob": {
|
| 1378 |
-
"version": "2.1.1",
|
| 1379 |
-
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
| 1380 |
-
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
| 1381 |
-
"dev": true,
|
| 1382 |
-
"license": "MIT",
|
| 1383 |
-
"engines": {
|
| 1384 |
-
"node": ">=0.10.0"
|
| 1385 |
-
}
|
| 1386 |
-
},
|
| 1387 |
-
"node_modules/is-glob": {
|
| 1388 |
-
"version": "4.0.3",
|
| 1389 |
-
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
| 1390 |
-
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
| 1391 |
-
"dev": true,
|
| 1392 |
-
"license": "MIT",
|
| 1393 |
-
"dependencies": {
|
| 1394 |
-
"is-extglob": "^2.1.1"
|
| 1395 |
-
},
|
| 1396 |
-
"engines": {
|
| 1397 |
-
"node": ">=0.10.0"
|
| 1398 |
-
}
|
| 1399 |
-
},
|
| 1400 |
-
"node_modules/is-number": {
|
| 1401 |
-
"version": "7.0.0",
|
| 1402 |
-
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
| 1403 |
-
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
| 1404 |
-
"dev": true,
|
| 1405 |
-
"license": "MIT",
|
| 1406 |
-
"engines": {
|
| 1407 |
-
"node": ">=0.12.0"
|
| 1408 |
-
}
|
| 1409 |
-
},
|
| 1410 |
-
"node_modules/js-tokens": {
|
| 1411 |
-
"version": "4.0.0",
|
| 1412 |
-
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
| 1413 |
-
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
| 1414 |
-
"dev": true
|
| 1415 |
-
},
|
| 1416 |
-
"node_modules/jsesc": {
|
| 1417 |
-
"version": "3.1.0",
|
| 1418 |
-
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
|
| 1419 |
-
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
|
| 1420 |
-
"dev": true,
|
| 1421 |
-
"bin": {
|
| 1422 |
-
"jsesc": "bin/jsesc"
|
| 1423 |
-
},
|
| 1424 |
-
"engines": {
|
| 1425 |
-
"node": ">=6"
|
| 1426 |
-
}
|
| 1427 |
-
},
|
| 1428 |
-
"node_modules/json5": {
|
| 1429 |
-
"version": "2.2.3",
|
| 1430 |
-
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
| 1431 |
-
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
| 1432 |
-
"dev": true,
|
| 1433 |
-
"bin": {
|
| 1434 |
-
"json5": "lib/cli.js"
|
| 1435 |
-
},
|
| 1436 |
-
"engines": {
|
| 1437 |
-
"node": ">=6"
|
| 1438 |
-
}
|
| 1439 |
-
},
|
| 1440 |
-
"node_modules/jsonfile": {
|
| 1441 |
-
"version": "6.1.0",
|
| 1442 |
-
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
| 1443 |
-
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
| 1444 |
-
"dev": true,
|
| 1445 |
-
"license": "MIT",
|
| 1446 |
-
"dependencies": {
|
| 1447 |
-
"universalify": "^2.0.0"
|
| 1448 |
-
},
|
| 1449 |
-
"optionalDependencies": {
|
| 1450 |
-
"graceful-fs": "^4.1.6"
|
| 1451 |
-
}
|
| 1452 |
-
},
|
| 1453 |
-
"node_modules/loose-envify": {
|
| 1454 |
-
"version": "1.4.0",
|
| 1455 |
-
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
| 1456 |
-
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
| 1457 |
-
"dev": true,
|
| 1458 |
-
"dependencies": {
|
| 1459 |
-
"js-tokens": "^3.0.0 || ^4.0.0"
|
| 1460 |
-
},
|
| 1461 |
-
"bin": {
|
| 1462 |
-
"loose-envify": "cli.js"
|
| 1463 |
-
}
|
| 1464 |
-
},
|
| 1465 |
-
"node_modules/lru-cache": {
|
| 1466 |
-
"version": "5.1.1",
|
| 1467 |
-
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
| 1468 |
-
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
|
| 1469 |
-
"dev": true,
|
| 1470 |
-
"dependencies": {
|
| 1471 |
-
"yallist": "^3.0.2"
|
| 1472 |
-
}
|
| 1473 |
-
},
|
| 1474 |
-
"node_modules/ms": {
|
| 1475 |
-
"version": "2.1.3",
|
| 1476 |
-
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
| 1477 |
-
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
| 1478 |
-
"dev": true
|
| 1479 |
-
},
|
| 1480 |
-
"node_modules/nanoid": {
|
| 1481 |
-
"version": "3.3.11",
|
| 1482 |
-
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
| 1483 |
-
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
| 1484 |
-
"dev": true,
|
| 1485 |
-
"funding": [
|
| 1486 |
-
{
|
| 1487 |
-
"type": "github",
|
| 1488 |
-
"url": "https://github.com/sponsors/ai"
|
| 1489 |
-
}
|
| 1490 |
-
],
|
| 1491 |
-
"bin": {
|
| 1492 |
-
"nanoid": "bin/nanoid.cjs"
|
| 1493 |
-
},
|
| 1494 |
-
"engines": {
|
| 1495 |
-
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
| 1496 |
-
}
|
| 1497 |
-
},
|
| 1498 |
-
"node_modules/node-releases": {
|
| 1499 |
-
"version": "2.0.19",
|
| 1500 |
-
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
|
| 1501 |
-
"integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
|
| 1502 |
-
"dev": true
|
| 1503 |
-
},
|
| 1504 |
-
"node_modules/normalize-path": {
|
| 1505 |
-
"version": "3.0.0",
|
| 1506 |
-
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
| 1507 |
-
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
| 1508 |
-
"dev": true,
|
| 1509 |
-
"license": "MIT",
|
| 1510 |
-
"engines": {
|
| 1511 |
-
"node": ">=0.10.0"
|
| 1512 |
-
}
|
| 1513 |
-
},
|
| 1514 |
-
"node_modules/p-map": {
|
| 1515 |
-
"version": "7.0.3",
|
| 1516 |
-
"resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz",
|
| 1517 |
-
"integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==",
|
| 1518 |
-
"dev": true,
|
| 1519 |
-
"license": "MIT",
|
| 1520 |
-
"engines": {
|
| 1521 |
-
"node": ">=18"
|
| 1522 |
-
},
|
| 1523 |
-
"funding": {
|
| 1524 |
-
"url": "https://github.com/sponsors/sindresorhus"
|
| 1525 |
-
}
|
| 1526 |
-
},
|
| 1527 |
-
"node_modules/picocolors": {
|
| 1528 |
-
"version": "1.1.1",
|
| 1529 |
-
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
| 1530 |
-
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
| 1531 |
-
"dev": true
|
| 1532 |
-
},
|
| 1533 |
-
"node_modules/picomatch": {
|
| 1534 |
-
"version": "2.3.1",
|
| 1535 |
-
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
| 1536 |
-
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
| 1537 |
-
"dev": true,
|
| 1538 |
-
"license": "MIT",
|
| 1539 |
-
"engines": {
|
| 1540 |
-
"node": ">=8.6"
|
| 1541 |
-
},
|
| 1542 |
-
"funding": {
|
| 1543 |
-
"url": "https://github.com/sponsors/jonschlinkert"
|
| 1544 |
-
}
|
| 1545 |
-
},
|
| 1546 |
-
"node_modules/postcss": {
|
| 1547 |
-
"version": "8.5.6",
|
| 1548 |
-
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
| 1549 |
-
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
| 1550 |
-
"dev": true,
|
| 1551 |
-
"funding": [
|
| 1552 |
-
{
|
| 1553 |
-
"type": "opencollective",
|
| 1554 |
-
"url": "https://opencollective.com/postcss/"
|
| 1555 |
-
},
|
| 1556 |
-
{
|
| 1557 |
-
"type": "tidelift",
|
| 1558 |
-
"url": "https://tidelift.com/funding/github/npm/postcss"
|
| 1559 |
-
},
|
| 1560 |
-
{
|
| 1561 |
-
"type": "github",
|
| 1562 |
-
"url": "https://github.com/sponsors/ai"
|
| 1563 |
-
}
|
| 1564 |
-
],
|
| 1565 |
-
"dependencies": {
|
| 1566 |
-
"nanoid": "^3.3.11",
|
| 1567 |
-
"picocolors": "^1.1.1",
|
| 1568 |
-
"source-map-js": "^1.2.1"
|
| 1569 |
-
},
|
| 1570 |
-
"engines": {
|
| 1571 |
-
"node": "^10 || ^12 || >=14"
|
| 1572 |
-
}
|
| 1573 |
-
},
|
| 1574 |
-
"node_modules/react": {
|
| 1575 |
-
"version": "18.3.1",
|
| 1576 |
-
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
| 1577 |
-
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
| 1578 |
-
"dev": true,
|
| 1579 |
-
"dependencies": {
|
| 1580 |
-
"loose-envify": "^1.1.0"
|
| 1581 |
-
},
|
| 1582 |
-
"engines": {
|
| 1583 |
-
"node": ">=0.10.0"
|
| 1584 |
-
}
|
| 1585 |
-
},
|
| 1586 |
-
"node_modules/react-dom": {
|
| 1587 |
-
"version": "18.3.1",
|
| 1588 |
-
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
| 1589 |
-
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
| 1590 |
-
"dev": true,
|
| 1591 |
-
"dependencies": {
|
| 1592 |
-
"loose-envify": "^1.1.0",
|
| 1593 |
-
"scheduler": "^0.23.2"
|
| 1594 |
-
},
|
| 1595 |
-
"peerDependencies": {
|
| 1596 |
-
"react": "^18.3.1"
|
| 1597 |
-
}
|
| 1598 |
-
},
|
| 1599 |
-
"node_modules/react-refresh": {
|
| 1600 |
-
"version": "0.17.0",
|
| 1601 |
-
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
|
| 1602 |
-
"integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
|
| 1603 |
-
"dev": true,
|
| 1604 |
-
"engines": {
|
| 1605 |
-
"node": ">=0.10.0"
|
| 1606 |
-
}
|
| 1607 |
-
},
|
| 1608 |
-
"node_modules/readdirp": {
|
| 1609 |
-
"version": "3.6.0",
|
| 1610 |
-
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
| 1611 |
-
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
| 1612 |
-
"dev": true,
|
| 1613 |
-
"license": "MIT",
|
| 1614 |
-
"dependencies": {
|
| 1615 |
-
"picomatch": "^2.2.1"
|
| 1616 |
-
},
|
| 1617 |
-
"engines": {
|
| 1618 |
-
"node": ">=8.10.0"
|
| 1619 |
-
}
|
| 1620 |
-
},
|
| 1621 |
-
"node_modules/rollup": {
|
| 1622 |
-
"version": "4.46.1",
|
| 1623 |
-
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.1.tgz",
|
| 1624 |
-
"integrity": "sha512-33xGNBsDJAkzt0PvninskHlWnTIPgDtTwhg0U38CUoNP/7H6wI2Cz6dUeoNPbjdTdsYTGuiFFASuUOWovH0SyQ==",
|
| 1625 |
-
"dev": true,
|
| 1626 |
-
"dependencies": {
|
| 1627 |
-
"@types/estree": "1.0.8"
|
| 1628 |
-
},
|
| 1629 |
-
"bin": {
|
| 1630 |
-
"rollup": "dist/bin/rollup"
|
| 1631 |
-
},
|
| 1632 |
-
"engines": {
|
| 1633 |
-
"node": ">=18.0.0",
|
| 1634 |
-
"npm": ">=8.0.0"
|
| 1635 |
-
},
|
| 1636 |
-
"optionalDependencies": {
|
| 1637 |
-
"@rollup/rollup-android-arm-eabi": "4.46.1",
|
| 1638 |
-
"@rollup/rollup-android-arm64": "4.46.1",
|
| 1639 |
-
"@rollup/rollup-darwin-arm64": "4.46.1",
|
| 1640 |
-
"@rollup/rollup-darwin-x64": "4.46.1",
|
| 1641 |
-
"@rollup/rollup-freebsd-arm64": "4.46.1",
|
| 1642 |
-
"@rollup/rollup-freebsd-x64": "4.46.1",
|
| 1643 |
-
"@rollup/rollup-linux-arm-gnueabihf": "4.46.1",
|
| 1644 |
-
"@rollup/rollup-linux-arm-musleabihf": "4.46.1",
|
| 1645 |
-
"@rollup/rollup-linux-arm64-gnu": "4.46.1",
|
| 1646 |
-
"@rollup/rollup-linux-arm64-musl": "4.46.1",
|
| 1647 |
-
"@rollup/rollup-linux-loongarch64-gnu": "4.46.1",
|
| 1648 |
-
"@rollup/rollup-linux-ppc64-gnu": "4.46.1",
|
| 1649 |
-
"@rollup/rollup-linux-riscv64-gnu": "4.46.1",
|
| 1650 |
-
"@rollup/rollup-linux-riscv64-musl": "4.46.1",
|
| 1651 |
-
"@rollup/rollup-linux-s390x-gnu": "4.46.1",
|
| 1652 |
-
"@rollup/rollup-linux-x64-gnu": "4.46.1",
|
| 1653 |
-
"@rollup/rollup-linux-x64-musl": "4.46.1",
|
| 1654 |
-
"@rollup/rollup-win32-arm64-msvc": "4.46.1",
|
| 1655 |
-
"@rollup/rollup-win32-ia32-msvc": "4.46.1",
|
| 1656 |
-
"@rollup/rollup-win32-x64-msvc": "4.46.1",
|
| 1657 |
-
"fsevents": "~2.3.2"
|
| 1658 |
-
}
|
| 1659 |
-
},
|
| 1660 |
-
"node_modules/scheduler": {
|
| 1661 |
-
"version": "0.23.2",
|
| 1662 |
-
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
|
| 1663 |
-
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
|
| 1664 |
-
"dev": true,
|
| 1665 |
-
"dependencies": {
|
| 1666 |
-
"loose-envify": "^1.1.0"
|
| 1667 |
-
}
|
| 1668 |
-
},
|
| 1669 |
-
"node_modules/semver": {
|
| 1670 |
-
"version": "6.3.1",
|
| 1671 |
-
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
| 1672 |
-
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
| 1673 |
-
"dev": true,
|
| 1674 |
-
"bin": {
|
| 1675 |
-
"semver": "bin/semver.js"
|
| 1676 |
-
}
|
| 1677 |
-
},
|
| 1678 |
-
"node_modules/source-map-js": {
|
| 1679 |
-
"version": "1.2.1",
|
| 1680 |
-
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
| 1681 |
-
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
| 1682 |
-
"dev": true,
|
| 1683 |
-
"engines": {
|
| 1684 |
-
"node": ">=0.10.0"
|
| 1685 |
-
}
|
| 1686 |
-
},
|
| 1687 |
-
"node_modules/tinyglobby": {
|
| 1688 |
-
"version": "0.2.14",
|
| 1689 |
-
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
|
| 1690 |
-
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
|
| 1691 |
-
"dev": true,
|
| 1692 |
-
"license": "MIT",
|
| 1693 |
-
"dependencies": {
|
| 1694 |
-
"fdir": "^6.4.4",
|
| 1695 |
-
"picomatch": "^4.0.2"
|
| 1696 |
-
},
|
| 1697 |
-
"engines": {
|
| 1698 |
-
"node": ">=12.0.0"
|
| 1699 |
-
},
|
| 1700 |
-
"funding": {
|
| 1701 |
-
"url": "https://github.com/sponsors/SuperchupuDev"
|
| 1702 |
-
}
|
| 1703 |
-
},
|
| 1704 |
-
"node_modules/tinyglobby/node_modules/fdir": {
|
| 1705 |
-
"version": "6.4.6",
|
| 1706 |
-
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
|
| 1707 |
-
"integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
|
| 1708 |
-
"dev": true,
|
| 1709 |
-
"license": "MIT",
|
| 1710 |
-
"peerDependencies": {
|
| 1711 |
-
"picomatch": "^3 || ^4"
|
| 1712 |
-
},
|
| 1713 |
-
"peerDependenciesMeta": {
|
| 1714 |
-
"picomatch": {
|
| 1715 |
-
"optional": true
|
| 1716 |
-
}
|
| 1717 |
-
}
|
| 1718 |
-
},
|
| 1719 |
-
"node_modules/tinyglobby/node_modules/picomatch": {
|
| 1720 |
-
"version": "4.0.3",
|
| 1721 |
-
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
| 1722 |
-
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
| 1723 |
-
"dev": true,
|
| 1724 |
-
"license": "MIT",
|
| 1725 |
-
"engines": {
|
| 1726 |
-
"node": ">=12"
|
| 1727 |
-
},
|
| 1728 |
-
"funding": {
|
| 1729 |
-
"url": "https://github.com/sponsors/jonschlinkert"
|
| 1730 |
-
}
|
| 1731 |
-
},
|
| 1732 |
-
"node_modules/to-regex-range": {
|
| 1733 |
-
"version": "5.0.1",
|
| 1734 |
-
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
| 1735 |
-
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
| 1736 |
-
"dev": true,
|
| 1737 |
-
"license": "MIT",
|
| 1738 |
-
"dependencies": {
|
| 1739 |
-
"is-number": "^7.0.0"
|
| 1740 |
-
},
|
| 1741 |
-
"engines": {
|
| 1742 |
-
"node": ">=8.0"
|
| 1743 |
-
}
|
| 1744 |
-
},
|
| 1745 |
-
"node_modules/typescript": {
|
| 1746 |
-
"version": "5.8.3",
|
| 1747 |
-
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
| 1748 |
-
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
| 1749 |
-
"dev": true,
|
| 1750 |
-
"bin": {
|
| 1751 |
-
"tsc": "bin/tsc",
|
| 1752 |
-
"tsserver": "bin/tsserver"
|
| 1753 |
-
},
|
| 1754 |
-
"engines": {
|
| 1755 |
-
"node": ">=14.17"
|
| 1756 |
-
}
|
| 1757 |
-
},
|
| 1758 |
-
"node_modules/universalify": {
|
| 1759 |
-
"version": "2.0.1",
|
| 1760 |
-
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
| 1761 |
-
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
| 1762 |
-
"dev": true,
|
| 1763 |
-
"license": "MIT",
|
| 1764 |
-
"engines": {
|
| 1765 |
-
"node": ">= 10.0.0"
|
| 1766 |
-
}
|
| 1767 |
-
},
|
| 1768 |
-
"node_modules/update-browserslist-db": {
|
| 1769 |
-
"version": "1.1.3",
|
| 1770 |
-
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
|
| 1771 |
-
"integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
|
| 1772 |
-
"dev": true,
|
| 1773 |
-
"funding": [
|
| 1774 |
-
{
|
| 1775 |
-
"type": "opencollective",
|
| 1776 |
-
"url": "https://opencollective.com/browserslist"
|
| 1777 |
-
},
|
| 1778 |
-
{
|
| 1779 |
-
"type": "tidelift",
|
| 1780 |
-
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
| 1781 |
-
},
|
| 1782 |
-
{
|
| 1783 |
-
"type": "github",
|
| 1784 |
-
"url": "https://github.com/sponsors/ai"
|
| 1785 |
-
}
|
| 1786 |
-
],
|
| 1787 |
-
"dependencies": {
|
| 1788 |
-
"escalade": "^3.2.0",
|
| 1789 |
-
"picocolors": "^1.1.1"
|
| 1790 |
-
},
|
| 1791 |
-
"bin": {
|
| 1792 |
-
"update-browserslist-db": "cli.js"
|
| 1793 |
-
},
|
| 1794 |
-
"peerDependencies": {
|
| 1795 |
-
"browserslist": ">= 4.21.0"
|
| 1796 |
-
}
|
| 1797 |
-
},
|
| 1798 |
-
"node_modules/vite": {
|
| 1799 |
-
"version": "5.4.19",
|
| 1800 |
-
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz",
|
| 1801 |
-
"integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==",
|
| 1802 |
-
"dev": true,
|
| 1803 |
-
"dependencies": {
|
| 1804 |
-
"esbuild": "^0.21.3",
|
| 1805 |
-
"postcss": "^8.4.43",
|
| 1806 |
-
"rollup": "^4.20.0"
|
| 1807 |
-
},
|
| 1808 |
-
"bin": {
|
| 1809 |
-
"vite": "bin/vite.js"
|
| 1810 |
-
},
|
| 1811 |
-
"engines": {
|
| 1812 |
-
"node": "^18.0.0 || >=20.0.0"
|
| 1813 |
-
},
|
| 1814 |
-
"funding": {
|
| 1815 |
-
"url": "https://github.com/vitejs/vite?sponsor=1"
|
| 1816 |
-
},
|
| 1817 |
-
"optionalDependencies": {
|
| 1818 |
-
"fsevents": "~2.3.3"
|
| 1819 |
-
},
|
| 1820 |
-
"peerDependencies": {
|
| 1821 |
-
"@types/node": "^18.0.0 || >=20.0.0",
|
| 1822 |
-
"less": "*",
|
| 1823 |
-
"lightningcss": "^1.21.0",
|
| 1824 |
-
"sass": "*",
|
| 1825 |
-
"sass-embedded": "*",
|
| 1826 |
-
"stylus": "*",
|
| 1827 |
-
"sugarss": "*",
|
| 1828 |
-
"terser": "^5.4.0"
|
| 1829 |
-
},
|
| 1830 |
-
"peerDependenciesMeta": {
|
| 1831 |
-
"@types/node": {
|
| 1832 |
-
"optional": true
|
| 1833 |
-
},
|
| 1834 |
-
"less": {
|
| 1835 |
-
"optional": true
|
| 1836 |
-
},
|
| 1837 |
-
"lightningcss": {
|
| 1838 |
-
"optional": true
|
| 1839 |
-
},
|
| 1840 |
-
"sass": {
|
| 1841 |
-
"optional": true
|
| 1842 |
-
},
|
| 1843 |
-
"sass-embedded": {
|
| 1844 |
-
"optional": true
|
| 1845 |
-
},
|
| 1846 |
-
"stylus": {
|
| 1847 |
-
"optional": true
|
| 1848 |
-
},
|
| 1849 |
-
"sugarss": {
|
| 1850 |
-
"optional": true
|
| 1851 |
-
},
|
| 1852 |
-
"terser": {
|
| 1853 |
-
"optional": true
|
| 1854 |
-
}
|
| 1855 |
-
}
|
| 1856 |
-
},
|
| 1857 |
-
"node_modules/vite-plugin-static-copy": {
|
| 1858 |
-
"version": "3.1.1",
|
| 1859 |
-
"resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-3.1.1.tgz",
|
| 1860 |
-
"integrity": "sha512-oR53SkL5cX4KT1t18E/xU50vJDo0N8oaHza4EMk0Fm+2/u6nQivxavOfrDk3udWj+dizRizB/QnBvJOOQrTTAQ==",
|
| 1861 |
-
"dev": true,
|
| 1862 |
-
"license": "MIT",
|
| 1863 |
-
"dependencies": {
|
| 1864 |
-
"chokidar": "^3.6.0",
|
| 1865 |
-
"fs-extra": "^11.3.0",
|
| 1866 |
-
"p-map": "^7.0.3",
|
| 1867 |
-
"picocolors": "^1.1.1",
|
| 1868 |
-
"tinyglobby": "^0.2.14"
|
| 1869 |
-
},
|
| 1870 |
-
"engines": {
|
| 1871 |
-
"node": "^18.0.0 || >=20.0.0"
|
| 1872 |
-
},
|
| 1873 |
-
"peerDependencies": {
|
| 1874 |
-
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0"
|
| 1875 |
-
}
|
| 1876 |
-
},
|
| 1877 |
-
"node_modules/yallist": {
|
| 1878 |
-
"version": "3.1.1",
|
| 1879 |
-
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
| 1880 |
-
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
| 1881 |
-
"dev": true
|
| 1882 |
-
}
|
| 1883 |
-
}
|
| 1884 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/package.json
DELETED
|
@@ -1,35 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"name": "@playwright/mcp-extension",
|
| 3 |
-
"version": "0.0.36",
|
| 4 |
-
"description": "Playwright MCP Browser Extension",
|
| 5 |
-
"private": true,
|
| 6 |
-
"repository": {
|
| 7 |
-
"type": "git",
|
| 8 |
-
"url": "git+https://github.com/microsoft/playwright-mcp.git"
|
| 9 |
-
},
|
| 10 |
-
"homepage": "https://playwright.dev",
|
| 11 |
-
"engines": {
|
| 12 |
-
"node": ">=18"
|
| 13 |
-
},
|
| 14 |
-
"author": {
|
| 15 |
-
"name": "Microsoft Corporation"
|
| 16 |
-
},
|
| 17 |
-
"license": "Apache-2.0",
|
| 18 |
-
"scripts": {
|
| 19 |
-
"build": "tsc --project . && tsc --project tsconfig.ui.json && vite build && vite build --config vite.sw.config.mts",
|
| 20 |
-
"watch": "tsc --watch --project . & tsc --watch --project tsconfig.ui.json & vite build --watch & vite build --watch --config vite.sw.config.mts",
|
| 21 |
-
"test": "playwright test",
|
| 22 |
-
"clean": "rm -rf dist"
|
| 23 |
-
},
|
| 24 |
-
"devDependencies": {
|
| 25 |
-
"@types/chrome": "^0.0.315",
|
| 26 |
-
"@types/react": "^18.2.66",
|
| 27 |
-
"@types/react-dom": "^18.2.22",
|
| 28 |
-
"@vitejs/plugin-react": "^4.0.0",
|
| 29 |
-
"react": "^18.2.0",
|
| 30 |
-
"react-dom": "^18.2.0",
|
| 31 |
-
"typescript": "^5.8.2",
|
| 32 |
-
"vite": "^5.0.0",
|
| 33 |
-
"vite-plugin-static-copy": "^3.1.1"
|
| 34 |
-
}
|
| 35 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/playwright.config.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
import { defineConfig } from '@playwright/test';
|
| 18 |
-
|
| 19 |
-
import type { TestOptions } from '../tests/fixtures';
|
| 20 |
-
|
| 21 |
-
export default defineConfig<TestOptions>({
|
| 22 |
-
testDir: './tests',
|
| 23 |
-
fullyParallel: true,
|
| 24 |
-
forbidOnly: !!process.env.CI,
|
| 25 |
-
retries: process.env.CI ? 2 : 0,
|
| 26 |
-
workers: process.env.CI ? 1 : undefined,
|
| 27 |
-
reporter: 'list',
|
| 28 |
-
projects: [
|
| 29 |
-
{ name: 'chromium', use: { mcpBrowser: 'chromium' } },
|
| 30 |
-
],
|
| 31 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/src/background.ts
DELETED
|
@@ -1,222 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
import { RelayConnection, debugLog } from './relayConnection';
|
| 18 |
-
|
| 19 |
-
type PageMessage = {
|
| 20 |
-
type: 'connectToMCPRelay';
|
| 21 |
-
mcpRelayUrl: string;
|
| 22 |
-
} | {
|
| 23 |
-
type: 'getTabs';
|
| 24 |
-
} | {
|
| 25 |
-
type: 'connectToTab';
|
| 26 |
-
tabId?: number;
|
| 27 |
-
windowId?: number;
|
| 28 |
-
mcpRelayUrl: string;
|
| 29 |
-
} | {
|
| 30 |
-
type: 'getConnectionStatus';
|
| 31 |
-
} | {
|
| 32 |
-
type: 'disconnect';
|
| 33 |
-
};
|
| 34 |
-
|
| 35 |
-
class TabShareExtension {
|
| 36 |
-
private _activeConnection: RelayConnection | undefined;
|
| 37 |
-
private _connectedTabId: number | null = null;
|
| 38 |
-
private _pendingTabSelection = new Map<number, { connection: RelayConnection, timerId?: number }>();
|
| 39 |
-
|
| 40 |
-
constructor() {
|
| 41 |
-
chrome.tabs.onRemoved.addListener(this._onTabRemoved.bind(this));
|
| 42 |
-
chrome.tabs.onUpdated.addListener(this._onTabUpdated.bind(this));
|
| 43 |
-
chrome.tabs.onActivated.addListener(this._onTabActivated.bind(this));
|
| 44 |
-
chrome.runtime.onMessage.addListener(this._onMessage.bind(this));
|
| 45 |
-
chrome.action.onClicked.addListener(this._onActionClicked.bind(this));
|
| 46 |
-
}
|
| 47 |
-
|
| 48 |
-
// Promise-based message handling is not supported in Chrome: https://issues.chromium.org/issues/40753031
|
| 49 |
-
private _onMessage(message: PageMessage, sender: chrome.runtime.MessageSender, sendResponse: (response: any) => void) {
|
| 50 |
-
switch (message.type) {
|
| 51 |
-
case 'connectToMCPRelay':
|
| 52 |
-
this._connectToRelay(sender.tab!.id!, message.mcpRelayUrl).then(
|
| 53 |
-
() => sendResponse({ success: true }),
|
| 54 |
-
(error: any) => sendResponse({ success: false, error: error.message }));
|
| 55 |
-
return true;
|
| 56 |
-
case 'getTabs':
|
| 57 |
-
this._getTabs().then(
|
| 58 |
-
tabs => sendResponse({ success: true, tabs, currentTabId: sender.tab?.id }),
|
| 59 |
-
(error: any) => sendResponse({ success: false, error: error.message }));
|
| 60 |
-
return true;
|
| 61 |
-
case 'connectToTab':
|
| 62 |
-
const tabId = message.tabId || sender.tab?.id!;
|
| 63 |
-
const windowId = message.windowId || sender.tab?.windowId!;
|
| 64 |
-
this._connectTab(sender.tab!.id!, tabId, windowId, message.mcpRelayUrl!).then(
|
| 65 |
-
() => sendResponse({ success: true }),
|
| 66 |
-
(error: any) => sendResponse({ success: false, error: error.message }));
|
| 67 |
-
return true; // Return true to indicate that the response will be sent asynchronously
|
| 68 |
-
case 'getConnectionStatus':
|
| 69 |
-
sendResponse({
|
| 70 |
-
connectedTabId: this._connectedTabId
|
| 71 |
-
});
|
| 72 |
-
return false;
|
| 73 |
-
case 'disconnect':
|
| 74 |
-
this._disconnect().then(
|
| 75 |
-
() => sendResponse({ success: true }),
|
| 76 |
-
(error: any) => sendResponse({ success: false, error: error.message }));
|
| 77 |
-
return true;
|
| 78 |
-
}
|
| 79 |
-
return false;
|
| 80 |
-
}
|
| 81 |
-
|
| 82 |
-
private async _connectToRelay(selectorTabId: number, mcpRelayUrl: string): Promise<void> {
|
| 83 |
-
try {
|
| 84 |
-
debugLog(`Connecting to relay at ${mcpRelayUrl}`);
|
| 85 |
-
const socket = new WebSocket(mcpRelayUrl);
|
| 86 |
-
await new Promise<void>((resolve, reject) => {
|
| 87 |
-
socket.onopen = () => resolve();
|
| 88 |
-
socket.onerror = () => reject(new Error('WebSocket error'));
|
| 89 |
-
setTimeout(() => reject(new Error('Connection timeout')), 5000);
|
| 90 |
-
});
|
| 91 |
-
|
| 92 |
-
const connection = new RelayConnection(socket);
|
| 93 |
-
connection.onclose = () => {
|
| 94 |
-
debugLog('Connection closed');
|
| 95 |
-
this._pendingTabSelection.delete(selectorTabId);
|
| 96 |
-
// TODO: show error in the selector tab?
|
| 97 |
-
};
|
| 98 |
-
this._pendingTabSelection.set(selectorTabId, { connection });
|
| 99 |
-
debugLog(`Connected to MCP relay`);
|
| 100 |
-
} catch (error: any) {
|
| 101 |
-
const message = `Failed to connect to MCP relay: ${error.message}`;
|
| 102 |
-
debugLog(message);
|
| 103 |
-
throw new Error(message);
|
| 104 |
-
}
|
| 105 |
-
}
|
| 106 |
-
|
| 107 |
-
private async _connectTab(selectorTabId: number, tabId: number, windowId: number, mcpRelayUrl: string): Promise<void> {
|
| 108 |
-
try {
|
| 109 |
-
debugLog(`Connecting tab ${tabId} to relay at ${mcpRelayUrl}`);
|
| 110 |
-
try {
|
| 111 |
-
this._activeConnection?.close('Another connection is requested');
|
| 112 |
-
} catch (error: any) {
|
| 113 |
-
debugLog(`Error closing active connection:`, error);
|
| 114 |
-
}
|
| 115 |
-
await this._setConnectedTabId(null);
|
| 116 |
-
|
| 117 |
-
this._activeConnection = this._pendingTabSelection.get(selectorTabId)?.connection;
|
| 118 |
-
if (!this._activeConnection)
|
| 119 |
-
throw new Error('No active MCP relay connection');
|
| 120 |
-
this._pendingTabSelection.delete(selectorTabId);
|
| 121 |
-
|
| 122 |
-
this._activeConnection.setTabId(tabId);
|
| 123 |
-
this._activeConnection.onclose = () => {
|
| 124 |
-
debugLog('MCP connection closed');
|
| 125 |
-
this._activeConnection = undefined;
|
| 126 |
-
void this._setConnectedTabId(null);
|
| 127 |
-
};
|
| 128 |
-
|
| 129 |
-
await Promise.all([
|
| 130 |
-
this._setConnectedTabId(tabId),
|
| 131 |
-
chrome.tabs.update(tabId, { active: true }),
|
| 132 |
-
chrome.windows.update(windowId, { focused: true }),
|
| 133 |
-
]);
|
| 134 |
-
debugLog(`Connected to MCP bridge`);
|
| 135 |
-
} catch (error: any) {
|
| 136 |
-
await this._setConnectedTabId(null);
|
| 137 |
-
debugLog(`Failed to connect tab ${tabId}:`, error.message);
|
| 138 |
-
throw error;
|
| 139 |
-
}
|
| 140 |
-
}
|
| 141 |
-
|
| 142 |
-
private async _setConnectedTabId(tabId: number | null): Promise<void> {
|
| 143 |
-
const oldTabId = this._connectedTabId;
|
| 144 |
-
this._connectedTabId = tabId;
|
| 145 |
-
if (oldTabId && oldTabId !== tabId)
|
| 146 |
-
await this._updateBadge(oldTabId, { text: '' });
|
| 147 |
-
if (tabId)
|
| 148 |
-
await this._updateBadge(tabId, { text: '✓', color: '#4CAF50', title: 'Connected to MCP client' });
|
| 149 |
-
}
|
| 150 |
-
|
| 151 |
-
private async _updateBadge(tabId: number, { text, color, title }: { text: string; color?: string, title?: string }): Promise<void> {
|
| 152 |
-
try {
|
| 153 |
-
await chrome.action.setBadgeText({ tabId, text });
|
| 154 |
-
await chrome.action.setTitle({ tabId, title: title || '' });
|
| 155 |
-
if (color)
|
| 156 |
-
await chrome.action.setBadgeBackgroundColor({ tabId, color });
|
| 157 |
-
} catch (error: any) {
|
| 158 |
-
// Ignore errors as the tab may be closed already.
|
| 159 |
-
}
|
| 160 |
-
}
|
| 161 |
-
|
| 162 |
-
private async _onTabRemoved(tabId: number): Promise<void> {
|
| 163 |
-
const pendingConnection = this._pendingTabSelection.get(tabId)?.connection;
|
| 164 |
-
if (pendingConnection) {
|
| 165 |
-
this._pendingTabSelection.delete(tabId);
|
| 166 |
-
pendingConnection.close('Browser tab closed');
|
| 167 |
-
return;
|
| 168 |
-
}
|
| 169 |
-
if (this._connectedTabId !== tabId)
|
| 170 |
-
return;
|
| 171 |
-
this._activeConnection?.close('Browser tab closed');
|
| 172 |
-
this._activeConnection = undefined;
|
| 173 |
-
this._connectedTabId = null;
|
| 174 |
-
}
|
| 175 |
-
|
| 176 |
-
private _onTabActivated(activeInfo: chrome.tabs.TabActiveInfo) {
|
| 177 |
-
for (const [tabId, pending] of this._pendingTabSelection) {
|
| 178 |
-
if (tabId === activeInfo.tabId) {
|
| 179 |
-
if (pending.timerId) {
|
| 180 |
-
clearTimeout(pending.timerId);
|
| 181 |
-
pending.timerId = undefined;
|
| 182 |
-
}
|
| 183 |
-
continue;
|
| 184 |
-
}
|
| 185 |
-
if (!pending.timerId) {
|
| 186 |
-
pending.timerId = setTimeout(() => {
|
| 187 |
-
const existed = this._pendingTabSelection.delete(tabId);
|
| 188 |
-
if (existed) {
|
| 189 |
-
pending.connection.close('Tab has been inactive for 5 seconds');
|
| 190 |
-
chrome.tabs.sendMessage(tabId, { type: 'connectionTimeout' });
|
| 191 |
-
}
|
| 192 |
-
}, 5000);
|
| 193 |
-
return;
|
| 194 |
-
}
|
| 195 |
-
}
|
| 196 |
-
}
|
| 197 |
-
|
| 198 |
-
private _onTabUpdated(tabId: number, changeInfo: chrome.tabs.TabChangeInfo, tab: chrome.tabs.Tab) {
|
| 199 |
-
if (this._connectedTabId === tabId)
|
| 200 |
-
void this._setConnectedTabId(tabId);
|
| 201 |
-
}
|
| 202 |
-
|
| 203 |
-
private async _getTabs(): Promise<chrome.tabs.Tab[]> {
|
| 204 |
-
const tabs = await chrome.tabs.query({});
|
| 205 |
-
return tabs.filter(tab => tab.url && !['chrome:', 'edge:', 'devtools:'].some(scheme => tab.url!.startsWith(scheme)));
|
| 206 |
-
}
|
| 207 |
-
|
| 208 |
-
private async _onActionClicked(): Promise<void> {
|
| 209 |
-
await chrome.tabs.create({
|
| 210 |
-
url: chrome.runtime.getURL('status.html'),
|
| 211 |
-
active: true
|
| 212 |
-
});
|
| 213 |
-
}
|
| 214 |
-
|
| 215 |
-
private async _disconnect(): Promise<void> {
|
| 216 |
-
this._activeConnection?.close('User disconnected');
|
| 217 |
-
this._activeConnection = undefined;
|
| 218 |
-
await this._setConnectedTabId(null);
|
| 219 |
-
}
|
| 220 |
-
}
|
| 221 |
-
|
| 222 |
-
new TabShareExtension();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/src/relayConnection.ts
DELETED
|
@@ -1,178 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
export function debugLog(...args: unknown[]): void {
|
| 18 |
-
const enabled = true;
|
| 19 |
-
if (enabled) {
|
| 20 |
-
// eslint-disable-next-line no-console
|
| 21 |
-
console.log('[Extension]', ...args);
|
| 22 |
-
}
|
| 23 |
-
}
|
| 24 |
-
|
| 25 |
-
type ProtocolCommand = {
|
| 26 |
-
id: number;
|
| 27 |
-
method: string;
|
| 28 |
-
params?: any;
|
| 29 |
-
};
|
| 30 |
-
|
| 31 |
-
type ProtocolResponse = {
|
| 32 |
-
id?: number;
|
| 33 |
-
method?: string;
|
| 34 |
-
params?: any;
|
| 35 |
-
result?: any;
|
| 36 |
-
error?: string;
|
| 37 |
-
};
|
| 38 |
-
|
| 39 |
-
export class RelayConnection {
|
| 40 |
-
private _debuggee: chrome.debugger.Debuggee;
|
| 41 |
-
private _ws: WebSocket;
|
| 42 |
-
private _eventListener: (source: chrome.debugger.DebuggerSession, method: string, params: any) => void;
|
| 43 |
-
private _detachListener: (source: chrome.debugger.Debuggee, reason: string) => void;
|
| 44 |
-
private _tabPromise: Promise<void>;
|
| 45 |
-
private _tabPromiseResolve!: () => void;
|
| 46 |
-
private _closed = false;
|
| 47 |
-
|
| 48 |
-
onclose?: () => void;
|
| 49 |
-
|
| 50 |
-
constructor(ws: WebSocket) {
|
| 51 |
-
this._debuggee = { };
|
| 52 |
-
this._tabPromise = new Promise(resolve => this._tabPromiseResolve = resolve);
|
| 53 |
-
this._ws = ws;
|
| 54 |
-
this._ws.onmessage = this._onMessage.bind(this);
|
| 55 |
-
this._ws.onclose = () => this._onClose();
|
| 56 |
-
// Store listeners for cleanup
|
| 57 |
-
this._eventListener = this._onDebuggerEvent.bind(this);
|
| 58 |
-
this._detachListener = this._onDebuggerDetach.bind(this);
|
| 59 |
-
chrome.debugger.onEvent.addListener(this._eventListener);
|
| 60 |
-
chrome.debugger.onDetach.addListener(this._detachListener);
|
| 61 |
-
}
|
| 62 |
-
|
| 63 |
-
// Either setTabId or close is called after creating the connection.
|
| 64 |
-
setTabId(tabId: number): void {
|
| 65 |
-
this._debuggee = { tabId };
|
| 66 |
-
this._tabPromiseResolve();
|
| 67 |
-
}
|
| 68 |
-
|
| 69 |
-
close(message: string): void {
|
| 70 |
-
this._ws.close(1000, message);
|
| 71 |
-
// ws.onclose is called asynchronously, so we call it here to avoid forwarding
|
| 72 |
-
// CDP events to the closed connection.
|
| 73 |
-
this._onClose();
|
| 74 |
-
}
|
| 75 |
-
|
| 76 |
-
private _onClose() {
|
| 77 |
-
if (this._closed)
|
| 78 |
-
return;
|
| 79 |
-
this._closed = true;
|
| 80 |
-
chrome.debugger.onEvent.removeListener(this._eventListener);
|
| 81 |
-
chrome.debugger.onDetach.removeListener(this._detachListener);
|
| 82 |
-
chrome.debugger.detach(this._debuggee).catch(() => {});
|
| 83 |
-
this.onclose?.();
|
| 84 |
-
}
|
| 85 |
-
|
| 86 |
-
private _onDebuggerEvent(source: chrome.debugger.DebuggerSession, method: string, params: any): void {
|
| 87 |
-
if (source.tabId !== this._debuggee.tabId)
|
| 88 |
-
return;
|
| 89 |
-
debugLog('Forwarding CDP event:', method, params);
|
| 90 |
-
const sessionId = source.sessionId;
|
| 91 |
-
this._sendMessage({
|
| 92 |
-
method: 'forwardCDPEvent',
|
| 93 |
-
params: {
|
| 94 |
-
sessionId,
|
| 95 |
-
method,
|
| 96 |
-
params,
|
| 97 |
-
},
|
| 98 |
-
});
|
| 99 |
-
}
|
| 100 |
-
|
| 101 |
-
private _onDebuggerDetach(source: chrome.debugger.Debuggee, reason: string): void {
|
| 102 |
-
if (source.tabId !== this._debuggee.tabId)
|
| 103 |
-
return;
|
| 104 |
-
this.close(`Debugger detached: ${reason}`);
|
| 105 |
-
this._debuggee = { };
|
| 106 |
-
}
|
| 107 |
-
|
| 108 |
-
private _onMessage(event: MessageEvent): void {
|
| 109 |
-
this._onMessageAsync(event).catch(e => debugLog('Error handling message:', e));
|
| 110 |
-
}
|
| 111 |
-
|
| 112 |
-
private async _onMessageAsync(event: MessageEvent): Promise<void> {
|
| 113 |
-
let message: ProtocolCommand;
|
| 114 |
-
try {
|
| 115 |
-
message = JSON.parse(event.data);
|
| 116 |
-
} catch (error: any) {
|
| 117 |
-
debugLog('Error parsing message:', error);
|
| 118 |
-
this._sendError(-32700, `Error parsing message: ${error.message}`);
|
| 119 |
-
return;
|
| 120 |
-
}
|
| 121 |
-
|
| 122 |
-
debugLog('Received message:', message);
|
| 123 |
-
|
| 124 |
-
const response: ProtocolResponse = {
|
| 125 |
-
id: message.id,
|
| 126 |
-
};
|
| 127 |
-
try {
|
| 128 |
-
response.result = await this._handleCommand(message);
|
| 129 |
-
} catch (error: any) {
|
| 130 |
-
debugLog('Error handling command:', error);
|
| 131 |
-
response.error = error.message;
|
| 132 |
-
}
|
| 133 |
-
debugLog('Sending response:', response);
|
| 134 |
-
this._sendMessage(response);
|
| 135 |
-
}
|
| 136 |
-
|
| 137 |
-
private async _handleCommand(message: ProtocolCommand): Promise<any> {
|
| 138 |
-
if (message.method === 'attachToTab') {
|
| 139 |
-
await this._tabPromise;
|
| 140 |
-
debugLog('Attaching debugger to tab:', this._debuggee);
|
| 141 |
-
await chrome.debugger.attach(this._debuggee, '1.3');
|
| 142 |
-
const result: any = await chrome.debugger.sendCommand(this._debuggee, 'Target.getTargetInfo');
|
| 143 |
-
return {
|
| 144 |
-
targetInfo: result?.targetInfo,
|
| 145 |
-
};
|
| 146 |
-
}
|
| 147 |
-
if (!this._debuggee.tabId)
|
| 148 |
-
throw new Error('No tab is connected. Please go to the Playwright MCP extension and select the tab you want to connect to.');
|
| 149 |
-
if (message.method === 'forwardCDPCommand') {
|
| 150 |
-
const { sessionId, method, params } = message.params;
|
| 151 |
-
debugLog('CDP command:', method, params);
|
| 152 |
-
const debuggerSession: chrome.debugger.DebuggerSession = {
|
| 153 |
-
...this._debuggee,
|
| 154 |
-
sessionId,
|
| 155 |
-
};
|
| 156 |
-
// Forward CDP command to chrome.debugger
|
| 157 |
-
return await chrome.debugger.sendCommand(
|
| 158 |
-
debuggerSession,
|
| 159 |
-
method,
|
| 160 |
-
params
|
| 161 |
-
);
|
| 162 |
-
}
|
| 163 |
-
}
|
| 164 |
-
|
| 165 |
-
private _sendError(code: number, message: string): void {
|
| 166 |
-
this._sendMessage({
|
| 167 |
-
error: {
|
| 168 |
-
code,
|
| 169 |
-
message,
|
| 170 |
-
},
|
| 171 |
-
});
|
| 172 |
-
}
|
| 173 |
-
|
| 174 |
-
private _sendMessage(message: any): void {
|
| 175 |
-
if (this._ws.readyState === WebSocket.OPEN)
|
| 176 |
-
this._ws.send(JSON.stringify(message));
|
| 177 |
-
}
|
| 178 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/src/ui/connect.css
DELETED
|
@@ -1,206 +0,0 @@
|
|
| 1 |
-
/*
|
| 2 |
-
Copyright (c) Microsoft Corporation.
|
| 3 |
-
|
| 4 |
-
Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
you may not use this file except in compliance with the License.
|
| 6 |
-
You may obtain a copy of the License at
|
| 7 |
-
|
| 8 |
-
http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
|
| 10 |
-
Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
See the License for the specific language governing permissions and
|
| 14 |
-
limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
body {
|
| 18 |
-
margin: 0;
|
| 19 |
-
padding: 0;
|
| 20 |
-
}
|
| 21 |
-
|
| 22 |
-
/* Base styles */
|
| 23 |
-
.app-container {
|
| 24 |
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif;
|
| 25 |
-
background-color: #ffffff;
|
| 26 |
-
color: #1f2328;
|
| 27 |
-
margin: 0;
|
| 28 |
-
padding: 16px;
|
| 29 |
-
min-height: 100vh;
|
| 30 |
-
font-size: 14px;
|
| 31 |
-
}
|
| 32 |
-
|
| 33 |
-
.content-wrapper {
|
| 34 |
-
max-width: 600px;
|
| 35 |
-
margin: 0 auto;
|
| 36 |
-
}
|
| 37 |
-
|
| 38 |
-
/* Status Banner */
|
| 39 |
-
.status-container {
|
| 40 |
-
display: flex;
|
| 41 |
-
align-items: center;
|
| 42 |
-
justify-content: space-between;
|
| 43 |
-
margin-bottom: 16px;
|
| 44 |
-
padding-right: 12px;
|
| 45 |
-
}
|
| 46 |
-
|
| 47 |
-
.status-banner {
|
| 48 |
-
padding: 12px;
|
| 49 |
-
font-size: 14px;
|
| 50 |
-
font-weight: 500;
|
| 51 |
-
display: flex;
|
| 52 |
-
align-items: center;
|
| 53 |
-
gap: 8px;
|
| 54 |
-
flex: 1;
|
| 55 |
-
}
|
| 56 |
-
|
| 57 |
-
.status-banner.connected {
|
| 58 |
-
color: #1f2328;
|
| 59 |
-
}
|
| 60 |
-
|
| 61 |
-
.status-banner.connected::before {
|
| 62 |
-
content: "\2705";
|
| 63 |
-
margin-right: 8px;
|
| 64 |
-
}
|
| 65 |
-
|
| 66 |
-
.status-banner.error {
|
| 67 |
-
color: #1f2328;
|
| 68 |
-
}
|
| 69 |
-
|
| 70 |
-
.status-banner.error::before {
|
| 71 |
-
content: "\274C";
|
| 72 |
-
margin-right: 8px;
|
| 73 |
-
}
|
| 74 |
-
|
| 75 |
-
/* Buttons */
|
| 76 |
-
.button-container {
|
| 77 |
-
margin-bottom: 16px;
|
| 78 |
-
display: flex;
|
| 79 |
-
justify-content: flex-end;
|
| 80 |
-
padding-right: 12px;
|
| 81 |
-
}
|
| 82 |
-
|
| 83 |
-
.button {
|
| 84 |
-
padding: 8px 16px;
|
| 85 |
-
border-radius: 6px;
|
| 86 |
-
border: none;
|
| 87 |
-
font-size: 14px;
|
| 88 |
-
font-weight: 500;
|
| 89 |
-
cursor: pointer;
|
| 90 |
-
display: inline-flex;
|
| 91 |
-
align-items: center;
|
| 92 |
-
justify-content: center;
|
| 93 |
-
text-decoration: none;
|
| 94 |
-
margin-right: 8px;
|
| 95 |
-
min-width: 90px;
|
| 96 |
-
}
|
| 97 |
-
|
| 98 |
-
.button.primary {
|
| 99 |
-
background-color: #f8f9fa;
|
| 100 |
-
color: #3c4043;
|
| 101 |
-
border: 1px solid #dadce0;
|
| 102 |
-
}
|
| 103 |
-
|
| 104 |
-
.button.primary:hover {
|
| 105 |
-
background-color: #f1f3f4;
|
| 106 |
-
border-color: #dadce0;
|
| 107 |
-
box-shadow: 0 1px 2px 0 rgba(60,64,67,.1);
|
| 108 |
-
}
|
| 109 |
-
|
| 110 |
-
.button.default {
|
| 111 |
-
background-color: #f6f8fa;
|
| 112 |
-
color: #24292f;
|
| 113 |
-
}
|
| 114 |
-
|
| 115 |
-
.button.default:hover {
|
| 116 |
-
background-color: #f3f4f6;
|
| 117 |
-
}
|
| 118 |
-
|
| 119 |
-
.button.reject {
|
| 120 |
-
background-color: #da3633;
|
| 121 |
-
color: #ffffff;
|
| 122 |
-
border: 1px solid #da3633;
|
| 123 |
-
}
|
| 124 |
-
|
| 125 |
-
.button.reject:hover {
|
| 126 |
-
background-color: #c73836;
|
| 127 |
-
border-color: #c73836;
|
| 128 |
-
}
|
| 129 |
-
|
| 130 |
-
/* Tab selection */
|
| 131 |
-
.tab-section-title {
|
| 132 |
-
padding-left: 12px;
|
| 133 |
-
font-size: 12px;
|
| 134 |
-
font-weight: 400;
|
| 135 |
-
margin-bottom: 12px;
|
| 136 |
-
color: #656d76;
|
| 137 |
-
}
|
| 138 |
-
|
| 139 |
-
.tab-item {
|
| 140 |
-
display: flex;
|
| 141 |
-
align-items: center;
|
| 142 |
-
padding: 12px;
|
| 143 |
-
margin-bottom: 8px;
|
| 144 |
-
background-color: #ffffff;
|
| 145 |
-
cursor: pointer;
|
| 146 |
-
border-radius: 6px;
|
| 147 |
-
transition: background-color 0.2s ease;
|
| 148 |
-
}
|
| 149 |
-
|
| 150 |
-
.tab-item:hover {
|
| 151 |
-
background-color: #f8f9fa;
|
| 152 |
-
}
|
| 153 |
-
|
| 154 |
-
.tab-item.selected {
|
| 155 |
-
background-color: #f6f8fa;
|
| 156 |
-
}
|
| 157 |
-
|
| 158 |
-
.tab-item.disabled {
|
| 159 |
-
cursor: not-allowed;
|
| 160 |
-
opacity: 0.5;
|
| 161 |
-
}
|
| 162 |
-
|
| 163 |
-
.tab-radio {
|
| 164 |
-
margin-right: 12px;
|
| 165 |
-
flex-shrink: 0;
|
| 166 |
-
}
|
| 167 |
-
|
| 168 |
-
.tab-favicon {
|
| 169 |
-
width: 16px;
|
| 170 |
-
height: 16px;
|
| 171 |
-
margin-right: 8px;
|
| 172 |
-
flex-shrink: 0;
|
| 173 |
-
}
|
| 174 |
-
|
| 175 |
-
.tab-content {
|
| 176 |
-
flex: 1;
|
| 177 |
-
min-width: 0;
|
| 178 |
-
}
|
| 179 |
-
|
| 180 |
-
.tab-title {
|
| 181 |
-
font-weight: 500;
|
| 182 |
-
color: #1f2328;
|
| 183 |
-
margin-bottom: 2px;
|
| 184 |
-
white-space: nowrap;
|
| 185 |
-
overflow: hidden;
|
| 186 |
-
text-overflow: ellipsis;
|
| 187 |
-
}
|
| 188 |
-
|
| 189 |
-
.tab-url {
|
| 190 |
-
font-size: 12px;
|
| 191 |
-
color: #656d76;
|
| 192 |
-
white-space: nowrap;
|
| 193 |
-
overflow: hidden;
|
| 194 |
-
text-overflow: ellipsis;
|
| 195 |
-
}
|
| 196 |
-
|
| 197 |
-
/* Link-style button */
|
| 198 |
-
.link-button {
|
| 199 |
-
background: none;
|
| 200 |
-
border: none;
|
| 201 |
-
color: #0066cc;
|
| 202 |
-
text-decoration: underline;
|
| 203 |
-
cursor: pointer;
|
| 204 |
-
padding: 0;
|
| 205 |
-
font: inherit;
|
| 206 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/src/ui/connect.html
DELETED
|
@@ -1,29 +0,0 @@
|
|
| 1 |
-
<!--
|
| 2 |
-
Copyright (c) Microsoft Corporation.
|
| 3 |
-
|
| 4 |
-
Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
you may not use this file except in compliance with the License.
|
| 6 |
-
You may obtain a copy of the License at
|
| 7 |
-
|
| 8 |
-
http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
|
| 10 |
-
Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
See the License for the specific language governing permissions and
|
| 14 |
-
limitations under the License.
|
| 15 |
-
-->
|
| 16 |
-
<!DOCTYPE html>
|
| 17 |
-
<html>
|
| 18 |
-
<head>
|
| 19 |
-
<title>Playwright MCP extension</title>
|
| 20 |
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 21 |
-
<link rel="icon" type="image/png" sizes="32x32" href="../../icons/icon-32.png">
|
| 22 |
-
<link rel="icon" type="image/png" sizes="16x16" href="../../icons/icon-16.png">
|
| 23 |
-
<link rel="stylesheet" href="connect.css">
|
| 24 |
-
</head>
|
| 25 |
-
<body>
|
| 26 |
-
<div id="root"></div>
|
| 27 |
-
<script type="module" src="connect.tsx"></script>
|
| 28 |
-
</body>
|
| 29 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/src/ui/connect.tsx
DELETED
|
@@ -1,233 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
import React, { useState, useEffect, useCallback } from 'react';
|
| 18 |
-
import { createRoot } from 'react-dom/client';
|
| 19 |
-
import { Button, TabItem } from './tabItem';
|
| 20 |
-
import type { TabInfo } from './tabItem';
|
| 21 |
-
|
| 22 |
-
type Status =
|
| 23 |
-
| { type: 'connecting'; message: string }
|
| 24 |
-
| { type: 'connected'; message: string }
|
| 25 |
-
| { type: 'error'; message: string }
|
| 26 |
-
| { type: 'error'; versionMismatch: { extensionVersion: string; } };
|
| 27 |
-
|
| 28 |
-
const SUPPORTED_PROTOCOL_VERSION = 1;
|
| 29 |
-
|
| 30 |
-
const ConnectApp: React.FC = () => {
|
| 31 |
-
const [tabs, setTabs] = useState<TabInfo[]>([]);
|
| 32 |
-
const [status, setStatus] = useState<Status | null>(null);
|
| 33 |
-
const [showButtons, setShowButtons] = useState(true);
|
| 34 |
-
const [showTabList, setShowTabList] = useState(true);
|
| 35 |
-
const [clientInfo, setClientInfo] = useState('unknown');
|
| 36 |
-
const [mcpRelayUrl, setMcpRelayUrl] = useState('');
|
| 37 |
-
const [newTab, setNewTab] = useState<boolean>(false);
|
| 38 |
-
|
| 39 |
-
useEffect(() => {
|
| 40 |
-
const params = new URLSearchParams(window.location.search);
|
| 41 |
-
const relayUrl = params.get('mcpRelayUrl');
|
| 42 |
-
|
| 43 |
-
if (!relayUrl) {
|
| 44 |
-
setShowButtons(false);
|
| 45 |
-
setStatus({ type: 'error', message: 'Missing mcpRelayUrl parameter in URL.' });
|
| 46 |
-
return;
|
| 47 |
-
}
|
| 48 |
-
|
| 49 |
-
setMcpRelayUrl(relayUrl);
|
| 50 |
-
|
| 51 |
-
try {
|
| 52 |
-
const client = JSON.parse(params.get('client') || '{}');
|
| 53 |
-
const info = `${client.name}/${client.version}`;
|
| 54 |
-
setClientInfo(info);
|
| 55 |
-
setStatus({
|
| 56 |
-
type: 'connecting',
|
| 57 |
-
message: `🎭 Playwright MCP started from "${info}" is trying to connect. Do you want to continue?`
|
| 58 |
-
});
|
| 59 |
-
} catch (e) {
|
| 60 |
-
setStatus({ type: 'error', message: 'Failed to parse client version.' });
|
| 61 |
-
return;
|
| 62 |
-
}
|
| 63 |
-
|
| 64 |
-
const parsedVersion = parseInt(params.get('protocolVersion') ?? '', 10);
|
| 65 |
-
const requiredVersion = isNaN(parsedVersion) ? 1 : parsedVersion;
|
| 66 |
-
if (requiredVersion > SUPPORTED_PROTOCOL_VERSION) {
|
| 67 |
-
const extensionVersion = chrome.runtime.getManifest().version;
|
| 68 |
-
setShowButtons(false);
|
| 69 |
-
setShowTabList(false);
|
| 70 |
-
setStatus({
|
| 71 |
-
type: 'error',
|
| 72 |
-
versionMismatch: {
|
| 73 |
-
extensionVersion,
|
| 74 |
-
}
|
| 75 |
-
});
|
| 76 |
-
return;
|
| 77 |
-
}
|
| 78 |
-
|
| 79 |
-
void connectToMCPRelay(relayUrl);
|
| 80 |
-
|
| 81 |
-
// If this is a browser_navigate command, hide the tab list and show simple allow/reject
|
| 82 |
-
if (params.get('newTab') === 'true') {
|
| 83 |
-
setNewTab(true);
|
| 84 |
-
setShowTabList(false);
|
| 85 |
-
} else {
|
| 86 |
-
void loadTabs();
|
| 87 |
-
}
|
| 88 |
-
}, []);
|
| 89 |
-
|
| 90 |
-
const handleReject = useCallback((message: string) => {
|
| 91 |
-
setShowButtons(false);
|
| 92 |
-
setShowTabList(false);
|
| 93 |
-
setStatus({ type: 'error', message });
|
| 94 |
-
}, []);
|
| 95 |
-
|
| 96 |
-
const connectToMCPRelay = useCallback(async (mcpRelayUrl: string) => {
|
| 97 |
-
|
| 98 |
-
const response = await chrome.runtime.sendMessage({ type: 'connectToMCPRelay', mcpRelayUrl });
|
| 99 |
-
if (!response.success)
|
| 100 |
-
handleReject(response.error);
|
| 101 |
-
}, [handleReject]);
|
| 102 |
-
|
| 103 |
-
const loadTabs = useCallback(async () => {
|
| 104 |
-
const response = await chrome.runtime.sendMessage({ type: 'getTabs' });
|
| 105 |
-
if (response.success)
|
| 106 |
-
setTabs(response.tabs);
|
| 107 |
-
else
|
| 108 |
-
setStatus({ type: 'error', message: 'Failed to load tabs: ' + response.error });
|
| 109 |
-
}, []);
|
| 110 |
-
|
| 111 |
-
const handleConnectToTab = useCallback(async (tab?: TabInfo) => {
|
| 112 |
-
setShowButtons(false);
|
| 113 |
-
setShowTabList(false);
|
| 114 |
-
|
| 115 |
-
try {
|
| 116 |
-
const response = await chrome.runtime.sendMessage({
|
| 117 |
-
type: 'connectToTab',
|
| 118 |
-
mcpRelayUrl,
|
| 119 |
-
tabId: tab?.id,
|
| 120 |
-
windowId: tab?.windowId,
|
| 121 |
-
});
|
| 122 |
-
|
| 123 |
-
if (response?.success) {
|
| 124 |
-
setStatus({ type: 'connected', message: `MCP client "${clientInfo}" connected.` });
|
| 125 |
-
} else {
|
| 126 |
-
setStatus({
|
| 127 |
-
type: 'error',
|
| 128 |
-
message: response?.error || `MCP client "${clientInfo}" failed to connect.`
|
| 129 |
-
});
|
| 130 |
-
}
|
| 131 |
-
} catch (e) {
|
| 132 |
-
setStatus({
|
| 133 |
-
type: 'error',
|
| 134 |
-
message: `MCP client "${clientInfo}" failed to connect: ${e}`
|
| 135 |
-
});
|
| 136 |
-
}
|
| 137 |
-
}, [clientInfo, mcpRelayUrl]);
|
| 138 |
-
|
| 139 |
-
useEffect(() => {
|
| 140 |
-
const listener = (message: any) => {
|
| 141 |
-
if (message.type === 'connectionTimeout')
|
| 142 |
-
handleReject('Connection timed out.');
|
| 143 |
-
};
|
| 144 |
-
chrome.runtime.onMessage.addListener(listener);
|
| 145 |
-
return () => {
|
| 146 |
-
chrome.runtime.onMessage.removeListener(listener);
|
| 147 |
-
};
|
| 148 |
-
}, [handleReject]);
|
| 149 |
-
|
| 150 |
-
return (
|
| 151 |
-
<div className='app-container'>
|
| 152 |
-
<div className='content-wrapper'>
|
| 153 |
-
{status && (
|
| 154 |
-
<div className='status-container'>
|
| 155 |
-
<StatusBanner status={status} />
|
| 156 |
-
{showButtons && (
|
| 157 |
-
<div className='button-container'>
|
| 158 |
-
{newTab ? (
|
| 159 |
-
<>
|
| 160 |
-
<Button variant='primary' onClick={() => handleConnectToTab()}>
|
| 161 |
-
Allow
|
| 162 |
-
</Button>
|
| 163 |
-
<Button variant='reject' onClick={() => handleReject('Connection rejected. This tab can be closed.')}>
|
| 164 |
-
Reject
|
| 165 |
-
</Button>
|
| 166 |
-
</>
|
| 167 |
-
) : (
|
| 168 |
-
<Button variant='reject' onClick={() => handleReject('Connection rejected. This tab can be closed.')}>
|
| 169 |
-
Reject
|
| 170 |
-
</Button>
|
| 171 |
-
)}
|
| 172 |
-
</div>
|
| 173 |
-
)}
|
| 174 |
-
</div>
|
| 175 |
-
)}
|
| 176 |
-
|
| 177 |
-
{showTabList && (
|
| 178 |
-
<div>
|
| 179 |
-
<div className='tab-section-title'>
|
| 180 |
-
Select page to expose to MCP server:
|
| 181 |
-
</div>
|
| 182 |
-
<div>
|
| 183 |
-
{tabs.map(tab => (
|
| 184 |
-
<TabItem
|
| 185 |
-
key={tab.id}
|
| 186 |
-
tab={tab}
|
| 187 |
-
button={
|
| 188 |
-
<Button variant='primary' onClick={() => handleConnectToTab(tab)}>
|
| 189 |
-
Connect
|
| 190 |
-
</Button>
|
| 191 |
-
}
|
| 192 |
-
/>
|
| 193 |
-
))}
|
| 194 |
-
</div>
|
| 195 |
-
</div>
|
| 196 |
-
)}
|
| 197 |
-
</div>
|
| 198 |
-
</div>
|
| 199 |
-
);
|
| 200 |
-
};
|
| 201 |
-
|
| 202 |
-
const VersionMismatchError: React.FC<{ extensionVersion: string }> = ({ extensionVersion }) => {
|
| 203 |
-
const readmeUrl = 'https://github.com/microsoft/playwright-mcp/blob/main/extension/README.md';
|
| 204 |
-
const latestReleaseUrl = 'https://github.com/microsoft/playwright-mcp/releases/latest';
|
| 205 |
-
return (
|
| 206 |
-
<div>
|
| 207 |
-
Playwright MCP version trying to connect requires newer extension version (current version: {extensionVersion}).{' '}
|
| 208 |
-
<a href={latestReleaseUrl}>Click here</a> to download latest version of the extension, then drag and drop it into the Chrome Extensions page.{' '}
|
| 209 |
-
See <a href={readmeUrl} target='_blank' rel='noopener noreferrer'>installation instructions</a> for more details.
|
| 210 |
-
</div>
|
| 211 |
-
);
|
| 212 |
-
};
|
| 213 |
-
|
| 214 |
-
const StatusBanner: React.FC<{ status: Status }> = ({ status }) => {
|
| 215 |
-
return (
|
| 216 |
-
<div className={`status-banner ${status.type}`}>
|
| 217 |
-
{'versionMismatch' in status ? (
|
| 218 |
-
<VersionMismatchError
|
| 219 |
-
extensionVersion={status.versionMismatch.extensionVersion}
|
| 220 |
-
/>
|
| 221 |
-
) : (
|
| 222 |
-
status.message
|
| 223 |
-
)}
|
| 224 |
-
</div>
|
| 225 |
-
);
|
| 226 |
-
};
|
| 227 |
-
|
| 228 |
-
// Initialize the React app
|
| 229 |
-
const container = document.getElementById('root');
|
| 230 |
-
if (container) {
|
| 231 |
-
const root = createRoot(container);
|
| 232 |
-
root.render(<ConnectApp />);
|
| 233 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/src/ui/status.html
DELETED
|
@@ -1,13 +0,0 @@
|
|
| 1 |
-
<!DOCTYPE html>
|
| 2 |
-
<html lang="en">
|
| 3 |
-
<head>
|
| 4 |
-
<meta charset="UTF-8">
|
| 5 |
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
-
<title>Playwright MCP Bridge Status</title>
|
| 7 |
-
<link rel="stylesheet" href="connect.css">
|
| 8 |
-
</head>
|
| 9 |
-
<body>
|
| 10 |
-
<div id="root"></div>
|
| 11 |
-
<script src="status.tsx" type="module"></script>
|
| 12 |
-
</body>
|
| 13 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/src/ui/status.tsx
DELETED
|
@@ -1,110 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
import React, { useState, useEffect } from 'react';
|
| 18 |
-
import { createRoot } from 'react-dom/client';
|
| 19 |
-
import { Button, TabItem } from './tabItem';
|
| 20 |
-
|
| 21 |
-
import type { TabInfo } from './tabItem';
|
| 22 |
-
|
| 23 |
-
interface ConnectionStatus {
|
| 24 |
-
isConnected: boolean;
|
| 25 |
-
connectedTabId: number | null;
|
| 26 |
-
connectedTab?: TabInfo;
|
| 27 |
-
}
|
| 28 |
-
|
| 29 |
-
const StatusApp: React.FC = () => {
|
| 30 |
-
const [status, setStatus] = useState<ConnectionStatus>({
|
| 31 |
-
isConnected: false,
|
| 32 |
-
connectedTabId: null
|
| 33 |
-
});
|
| 34 |
-
|
| 35 |
-
useEffect(() => {
|
| 36 |
-
void loadStatus();
|
| 37 |
-
}, []);
|
| 38 |
-
|
| 39 |
-
const loadStatus = async () => {
|
| 40 |
-
// Get current connection status from background script
|
| 41 |
-
const { connectedTabId } = await chrome.runtime.sendMessage({ type: 'getConnectionStatus' });
|
| 42 |
-
if (connectedTabId) {
|
| 43 |
-
const tab = await chrome.tabs.get(connectedTabId);
|
| 44 |
-
setStatus({
|
| 45 |
-
isConnected: true,
|
| 46 |
-
connectedTabId,
|
| 47 |
-
connectedTab: {
|
| 48 |
-
id: tab.id!,
|
| 49 |
-
windowId: tab.windowId!,
|
| 50 |
-
title: tab.title!,
|
| 51 |
-
url: tab.url!,
|
| 52 |
-
favIconUrl: tab.favIconUrl
|
| 53 |
-
}
|
| 54 |
-
});
|
| 55 |
-
} else {
|
| 56 |
-
setStatus({
|
| 57 |
-
isConnected: false,
|
| 58 |
-
connectedTabId: null
|
| 59 |
-
});
|
| 60 |
-
}
|
| 61 |
-
};
|
| 62 |
-
|
| 63 |
-
const openConnectedTab = async () => {
|
| 64 |
-
if (!status.connectedTabId)
|
| 65 |
-
return;
|
| 66 |
-
await chrome.tabs.update(status.connectedTabId, { active: true });
|
| 67 |
-
window.close();
|
| 68 |
-
};
|
| 69 |
-
|
| 70 |
-
const disconnect = async () => {
|
| 71 |
-
await chrome.runtime.sendMessage({ type: 'disconnect' });
|
| 72 |
-
window.close();
|
| 73 |
-
};
|
| 74 |
-
|
| 75 |
-
return (
|
| 76 |
-
<div className='app-container'>
|
| 77 |
-
<div className='content-wrapper'>
|
| 78 |
-
{status.isConnected && status.connectedTab ? (
|
| 79 |
-
<div>
|
| 80 |
-
<div className='tab-section-title'>
|
| 81 |
-
Page with connected MCP client:
|
| 82 |
-
</div>
|
| 83 |
-
<div>
|
| 84 |
-
<TabItem
|
| 85 |
-
tab={status.connectedTab}
|
| 86 |
-
button={
|
| 87 |
-
<Button variant='primary' onClick={disconnect}>
|
| 88 |
-
Disconnect
|
| 89 |
-
</Button>
|
| 90 |
-
}
|
| 91 |
-
onClick={openConnectedTab}
|
| 92 |
-
/>
|
| 93 |
-
</div>
|
| 94 |
-
</div>
|
| 95 |
-
) : (
|
| 96 |
-
<div className='status-banner'>
|
| 97 |
-
No MCP clients are currently connected.
|
| 98 |
-
</div>
|
| 99 |
-
)}
|
| 100 |
-
</div>
|
| 101 |
-
</div>
|
| 102 |
-
);
|
| 103 |
-
};
|
| 104 |
-
|
| 105 |
-
// Initialize the React app
|
| 106 |
-
const container = document.getElementById('root');
|
| 107 |
-
if (container) {
|
| 108 |
-
const root = createRoot(container);
|
| 109 |
-
root.render(<StatusApp />);
|
| 110 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/src/ui/tabItem.tsx
DELETED
|
@@ -1,67 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
import React from 'react';
|
| 18 |
-
|
| 19 |
-
export interface TabInfo {
|
| 20 |
-
id: number;
|
| 21 |
-
windowId: number;
|
| 22 |
-
title: string;
|
| 23 |
-
url: string;
|
| 24 |
-
favIconUrl?: string;
|
| 25 |
-
}
|
| 26 |
-
|
| 27 |
-
export const Button: React.FC<{ variant: 'primary' | 'default' | 'reject'; onClick: () => void; children: React.ReactNode }> = ({
|
| 28 |
-
variant,
|
| 29 |
-
onClick,
|
| 30 |
-
children
|
| 31 |
-
}) => {
|
| 32 |
-
return (
|
| 33 |
-
<button className={`button ${variant}`} onClick={onClick}>
|
| 34 |
-
{children}
|
| 35 |
-
</button>
|
| 36 |
-
);
|
| 37 |
-
};
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
export interface TabItemProps {
|
| 41 |
-
tab: TabInfo;
|
| 42 |
-
onClick?: () => void;
|
| 43 |
-
button?: React.ReactNode;
|
| 44 |
-
}
|
| 45 |
-
|
| 46 |
-
export const TabItem: React.FC<TabItemProps> = ({
|
| 47 |
-
tab,
|
| 48 |
-
onClick,
|
| 49 |
-
button
|
| 50 |
-
}) => {
|
| 51 |
-
return (
|
| 52 |
-
<div className='tab-item' onClick={onClick} style={onClick ? { cursor: 'pointer' } : undefined}>
|
| 53 |
-
<img
|
| 54 |
-
src={tab.favIconUrl || 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect width="16" height="16" fill="%23f6f8fa"/></svg>'}
|
| 55 |
-
alt=''
|
| 56 |
-
className='tab-favicon'
|
| 57 |
-
/>
|
| 58 |
-
<div className='tab-content'>
|
| 59 |
-
<div className='tab-title'>
|
| 60 |
-
{tab.title || 'Untitled'}
|
| 61 |
-
</div>
|
| 62 |
-
<div className='tab-url'>{tab.url}</div>
|
| 63 |
-
</div>
|
| 64 |
-
{button}
|
| 65 |
-
</div>
|
| 66 |
-
);
|
| 67 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/src/ui/tsconfig.json
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
// Help VSCode to find right tsconfig file.
|
| 2 |
-
{
|
| 3 |
-
"extends": "../../tsconfig.ui.json"
|
| 4 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/tests/extension.spec.ts
DELETED
|
@@ -1,306 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
import fs from 'fs';
|
| 18 |
-
import path from 'path';
|
| 19 |
-
import { chromium } from 'playwright';
|
| 20 |
-
import { test as base, expect } from '../../tests/fixtures';
|
| 21 |
-
|
| 22 |
-
import type { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
| 23 |
-
import type { BrowserContext } from 'playwright';
|
| 24 |
-
import type { StartClient } from '../../tests/fixtures';
|
| 25 |
-
|
| 26 |
-
type BrowserWithExtension = {
|
| 27 |
-
userDataDir: string;
|
| 28 |
-
launch: (mode?: 'disable-extension') => Promise<BrowserContext>;
|
| 29 |
-
};
|
| 30 |
-
|
| 31 |
-
type TestFixtures = {
|
| 32 |
-
browserWithExtension: BrowserWithExtension,
|
| 33 |
-
pathToExtension: string,
|
| 34 |
-
useShortConnectionTimeout: (timeoutMs: number) => void
|
| 35 |
-
overrideProtocolVersion: (version: number) => void
|
| 36 |
-
};
|
| 37 |
-
|
| 38 |
-
const test = base.extend<TestFixtures>({
|
| 39 |
-
pathToExtension: async ({}, use) => {
|
| 40 |
-
await use(path.resolve(__dirname, '../dist'));
|
| 41 |
-
},
|
| 42 |
-
|
| 43 |
-
browserWithExtension: async ({ mcpBrowser, pathToExtension }, use, testInfo) => {
|
| 44 |
-
// The flags no longer work in Chrome since
|
| 45 |
-
// https://chromium.googlesource.com/chromium/src/+/290ed8046692651ce76088914750cb659b65fb17%5E%21/chrome/browser/extensions/extension_service.cc?pli=1#
|
| 46 |
-
test.skip('chromium' !== mcpBrowser, '--load-extension is not supported for official builds of Chromium');
|
| 47 |
-
|
| 48 |
-
let browserContext: BrowserContext | undefined;
|
| 49 |
-
const userDataDir = testInfo.outputPath('extension-user-data-dir');
|
| 50 |
-
await use({
|
| 51 |
-
userDataDir,
|
| 52 |
-
launch: async (mode?: 'disable-extension') => {
|
| 53 |
-
browserContext = await chromium.launchPersistentContext(userDataDir, {
|
| 54 |
-
channel: mcpBrowser,
|
| 55 |
-
// Opening the browser singleton only works in headed.
|
| 56 |
-
headless: false,
|
| 57 |
-
// Automation disables singleton browser process behavior, which is necessary for the extension.
|
| 58 |
-
ignoreDefaultArgs: ['--enable-automation'],
|
| 59 |
-
args: mode === 'disable-extension' ? [] : [
|
| 60 |
-
`--disable-extensions-except=${pathToExtension}`,
|
| 61 |
-
`--load-extension=${pathToExtension}`,
|
| 62 |
-
],
|
| 63 |
-
});
|
| 64 |
-
|
| 65 |
-
// for manifest v3:
|
| 66 |
-
let [serviceWorker] = browserContext.serviceWorkers();
|
| 67 |
-
if (!serviceWorker)
|
| 68 |
-
serviceWorker = await browserContext.waitForEvent('serviceworker');
|
| 69 |
-
|
| 70 |
-
return browserContext;
|
| 71 |
-
}
|
| 72 |
-
});
|
| 73 |
-
await browserContext?.close();
|
| 74 |
-
},
|
| 75 |
-
|
| 76 |
-
useShortConnectionTimeout: async ({}, use) => {
|
| 77 |
-
await use((timeoutMs: number) => {
|
| 78 |
-
process.env.PWMCP_TEST_CONNECTION_TIMEOUT = timeoutMs.toString();
|
| 79 |
-
});
|
| 80 |
-
process.env.PWMCP_TEST_CONNECTION_TIMEOUT = undefined;
|
| 81 |
-
},
|
| 82 |
-
|
| 83 |
-
overrideProtocolVersion: async ({}, use) => {
|
| 84 |
-
await use((version: number) => {
|
| 85 |
-
process.env.PWMCP_TEST_PROTOCOL_VERSION = version.toString();
|
| 86 |
-
});
|
| 87 |
-
process.env.PWMCP_TEST_PROTOCOL_VERSION = undefined;
|
| 88 |
-
}
|
| 89 |
-
});
|
| 90 |
-
|
| 91 |
-
async function startAndCallConnectTool(browserWithExtension: BrowserWithExtension, startClient: StartClient): Promise<Client> {
|
| 92 |
-
const { client } = await startClient({
|
| 93 |
-
args: [`--connect-tool`],
|
| 94 |
-
config: {
|
| 95 |
-
browser: {
|
| 96 |
-
userDataDir: browserWithExtension.userDataDir,
|
| 97 |
-
}
|
| 98 |
-
},
|
| 99 |
-
});
|
| 100 |
-
|
| 101 |
-
expect(await client.callTool({
|
| 102 |
-
name: 'browser_connect',
|
| 103 |
-
arguments: {
|
| 104 |
-
name: 'extension'
|
| 105 |
-
}
|
| 106 |
-
})).toHaveResponse({
|
| 107 |
-
result: 'Successfully changed connection method.',
|
| 108 |
-
});
|
| 109 |
-
|
| 110 |
-
return client;
|
| 111 |
-
}
|
| 112 |
-
|
| 113 |
-
async function startWithExtensionFlag(browserWithExtension: BrowserWithExtension, startClient: StartClient): Promise<Client> {
|
| 114 |
-
const { client } = await startClient({
|
| 115 |
-
args: [`--extension`],
|
| 116 |
-
config: {
|
| 117 |
-
browser: {
|
| 118 |
-
userDataDir: browserWithExtension.userDataDir,
|
| 119 |
-
}
|
| 120 |
-
},
|
| 121 |
-
});
|
| 122 |
-
return client;
|
| 123 |
-
}
|
| 124 |
-
|
| 125 |
-
const testWithOldExtensionVersion = test.extend({
|
| 126 |
-
pathToExtension: async ({}, use, testInfo) => {
|
| 127 |
-
const extensionDir = testInfo.outputPath('extension');
|
| 128 |
-
const oldPath = path.resolve(__dirname, '../dist');
|
| 129 |
-
|
| 130 |
-
await fs.promises.cp(oldPath, extensionDir, { recursive: true });
|
| 131 |
-
const manifestPath = path.join(extensionDir, 'manifest.json');
|
| 132 |
-
const manifest = JSON.parse(await fs.promises.readFile(manifestPath, 'utf8'));
|
| 133 |
-
manifest.version = '0.0.1';
|
| 134 |
-
await fs.promises.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
| 135 |
-
|
| 136 |
-
await use(extensionDir);
|
| 137 |
-
},
|
| 138 |
-
});
|
| 139 |
-
|
| 140 |
-
for (const [mode, startClientMethod] of [
|
| 141 |
-
['connect-tool', startAndCallConnectTool],
|
| 142 |
-
['extension-flag', startWithExtensionFlag],
|
| 143 |
-
] as const) {
|
| 144 |
-
|
| 145 |
-
test(`navigate with extension (${mode})`, async ({ browserWithExtension, startClient, server }) => {
|
| 146 |
-
const browserContext = await browserWithExtension.launch();
|
| 147 |
-
|
| 148 |
-
const client = await startClientMethod(browserWithExtension, startClient);
|
| 149 |
-
|
| 150 |
-
const confirmationPagePromise = browserContext.waitForEvent('page', page => {
|
| 151 |
-
return page.url().startsWith('chrome-extension://jakfalbnbhgkpmoaakfflhflbfpkailf/connect.html');
|
| 152 |
-
});
|
| 153 |
-
|
| 154 |
-
const navigateResponse = client.callTool({
|
| 155 |
-
name: 'browser_navigate',
|
| 156 |
-
arguments: { url: server.HELLO_WORLD },
|
| 157 |
-
});
|
| 158 |
-
|
| 159 |
-
const selectorPage = await confirmationPagePromise;
|
| 160 |
-
// For browser_navigate command, the UI shows Allow/Reject buttons instead of tab selector
|
| 161 |
-
await selectorPage.getByRole('button', { name: 'Allow' }).click();
|
| 162 |
-
|
| 163 |
-
expect(await navigateResponse).toHaveResponse({
|
| 164 |
-
pageState: expect.stringContaining(`- generic [active] [ref=e1]: Hello, world!`),
|
| 165 |
-
});
|
| 166 |
-
});
|
| 167 |
-
|
| 168 |
-
test(`snapshot of an existing page (${mode})`, async ({ browserWithExtension, startClient, server }) => {
|
| 169 |
-
const browserContext = await browserWithExtension.launch();
|
| 170 |
-
|
| 171 |
-
const page = await browserContext.newPage();
|
| 172 |
-
await page.goto(server.HELLO_WORLD);
|
| 173 |
-
|
| 174 |
-
// Another empty page.
|
| 175 |
-
await browserContext.newPage();
|
| 176 |
-
expect(browserContext.pages()).toHaveLength(3);
|
| 177 |
-
|
| 178 |
-
const client = await startClientMethod(browserWithExtension, startClient);
|
| 179 |
-
expect(browserContext.pages()).toHaveLength(3);
|
| 180 |
-
|
| 181 |
-
const confirmationPagePromise = browserContext.waitForEvent('page', page => {
|
| 182 |
-
return page.url().startsWith('chrome-extension://jakfalbnbhgkpmoaakfflhflbfpkailf/connect.html');
|
| 183 |
-
});
|
| 184 |
-
|
| 185 |
-
const navigateResponse = client.callTool({
|
| 186 |
-
name: 'browser_snapshot',
|
| 187 |
-
arguments: { },
|
| 188 |
-
});
|
| 189 |
-
|
| 190 |
-
const selectorPage = await confirmationPagePromise;
|
| 191 |
-
expect(browserContext.pages()).toHaveLength(4);
|
| 192 |
-
|
| 193 |
-
await selectorPage.locator('.tab-item', { hasText: 'Title' }).getByRole('button', { name: 'Connect' }).click();
|
| 194 |
-
|
| 195 |
-
expect(await navigateResponse).toHaveResponse({
|
| 196 |
-
pageState: expect.stringContaining(`- generic [active] [ref=e1]: Hello, world!`),
|
| 197 |
-
});
|
| 198 |
-
|
| 199 |
-
expect(browserContext.pages()).toHaveLength(4);
|
| 200 |
-
});
|
| 201 |
-
|
| 202 |
-
test(`extension not installed timeout (${mode})`, async ({ browserWithExtension, startClient, server, useShortConnectionTimeout }) => {
|
| 203 |
-
useShortConnectionTimeout(100);
|
| 204 |
-
|
| 205 |
-
const browserContext = await browserWithExtension.launch();
|
| 206 |
-
|
| 207 |
-
const client = await startClientMethod(browserWithExtension, startClient);
|
| 208 |
-
|
| 209 |
-
const confirmationPagePromise = browserContext.waitForEvent('page', page => {
|
| 210 |
-
return page.url().startsWith('chrome-extension://jakfalbnbhgkpmoaakfflhflbfpkailf/connect.html');
|
| 211 |
-
});
|
| 212 |
-
|
| 213 |
-
expect(await client.callTool({
|
| 214 |
-
name: 'browser_navigate',
|
| 215 |
-
arguments: { url: server.HELLO_WORLD },
|
| 216 |
-
})).toHaveResponse({
|
| 217 |
-
result: expect.stringContaining('Extension connection timeout. Make sure the "Playwright MCP Bridge" extension is installed.'),
|
| 218 |
-
isError: true,
|
| 219 |
-
});
|
| 220 |
-
|
| 221 |
-
await confirmationPagePromise;
|
| 222 |
-
});
|
| 223 |
-
|
| 224 |
-
testWithOldExtensionVersion(`works with old extension version (${mode})`, async ({ browserWithExtension, startClient, server, useShortConnectionTimeout }) => {
|
| 225 |
-
useShortConnectionTimeout(500);
|
| 226 |
-
|
| 227 |
-
// Prelaunch the browser, so that it is properly closed after the test.
|
| 228 |
-
const browserContext = await browserWithExtension.launch();
|
| 229 |
-
|
| 230 |
-
const client = await startClientMethod(browserWithExtension, startClient);
|
| 231 |
-
|
| 232 |
-
const confirmationPagePromise = browserContext.waitForEvent('page', page => {
|
| 233 |
-
return page.url().startsWith('chrome-extension://jakfalbnbhgkpmoaakfflhflbfpkailf/connect.html');
|
| 234 |
-
});
|
| 235 |
-
|
| 236 |
-
const navigateResponse = client.callTool({
|
| 237 |
-
name: 'browser_navigate',
|
| 238 |
-
arguments: { url: server.HELLO_WORLD },
|
| 239 |
-
});
|
| 240 |
-
|
| 241 |
-
const selectorPage = await confirmationPagePromise;
|
| 242 |
-
// For browser_navigate command, the UI shows Allow/Reject buttons instead of tab selector
|
| 243 |
-
await selectorPage.getByRole('button', { name: 'Allow' }).click();
|
| 244 |
-
|
| 245 |
-
expect(await navigateResponse).toHaveResponse({
|
| 246 |
-
pageState: expect.stringContaining(`- generic [active] [ref=e1]: Hello, world!`),
|
| 247 |
-
});
|
| 248 |
-
});
|
| 249 |
-
|
| 250 |
-
test(`extension needs update (${mode})`, async ({ browserWithExtension, startClient, server, useShortConnectionTimeout, overrideProtocolVersion }) => {
|
| 251 |
-
useShortConnectionTimeout(500);
|
| 252 |
-
overrideProtocolVersion(1000);
|
| 253 |
-
|
| 254 |
-
// Prelaunch the browser, so that it is properly closed after the test.
|
| 255 |
-
const browserContext = await browserWithExtension.launch();
|
| 256 |
-
|
| 257 |
-
const client = await startClientMethod(browserWithExtension, startClient);
|
| 258 |
-
|
| 259 |
-
const confirmationPagePromise = browserContext.waitForEvent('page', page => {
|
| 260 |
-
return page.url().startsWith('chrome-extension://jakfalbnbhgkpmoaakfflhflbfpkailf/connect.html');
|
| 261 |
-
});
|
| 262 |
-
|
| 263 |
-
const navigateResponse = client.callTool({
|
| 264 |
-
name: 'browser_navigate',
|
| 265 |
-
arguments: { url: server.HELLO_WORLD },
|
| 266 |
-
});
|
| 267 |
-
|
| 268 |
-
const confirmationPage = await confirmationPagePromise;
|
| 269 |
-
await expect(confirmationPage.locator('.status-banner')).toContainText(`Playwright MCP version trying to connect requires newer extension version`);
|
| 270 |
-
|
| 271 |
-
expect(await navigateResponse).toHaveResponse({
|
| 272 |
-
result: expect.stringContaining('Extension connection timeout.'),
|
| 273 |
-
isError: true,
|
| 274 |
-
});
|
| 275 |
-
});
|
| 276 |
-
|
| 277 |
-
}
|
| 278 |
-
|
| 279 |
-
test(`custom executablePath`, async ({ startClient, server, useShortConnectionTimeout }) => {
|
| 280 |
-
useShortConnectionTimeout(1000);
|
| 281 |
-
|
| 282 |
-
const executablePath = test.info().outputPath('echo.sh');
|
| 283 |
-
await fs.promises.writeFile(executablePath, '#!/bin/bash\necho "Custom exec args: $@" > "$(dirname "$0")/output.txt"', { mode: 0o755 });
|
| 284 |
-
|
| 285 |
-
const { client } = await startClient({
|
| 286 |
-
args: [`--extension`],
|
| 287 |
-
config: {
|
| 288 |
-
browser: {
|
| 289 |
-
launchOptions: {
|
| 290 |
-
executablePath,
|
| 291 |
-
},
|
| 292 |
-
}
|
| 293 |
-
},
|
| 294 |
-
});
|
| 295 |
-
|
| 296 |
-
const navigateResponse = await client.callTool({
|
| 297 |
-
name: 'browser_navigate',
|
| 298 |
-
arguments: { url: server.HELLO_WORLD },
|
| 299 |
-
timeout: 1000,
|
| 300 |
-
});
|
| 301 |
-
expect(await navigateResponse).toHaveResponse({
|
| 302 |
-
result: expect.stringContaining('Extension connection timeout.'),
|
| 303 |
-
isError: true,
|
| 304 |
-
});
|
| 305 |
-
expect(await fs.promises.readFile(test.info().outputPath('output.txt'), 'utf8')).toContain('Custom exec args: chrome-extension://jakfalbnbhgkpmoaakfflhflbfpkailf/connect.html?');
|
| 306 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/tsconfig.json
DELETED
|
@@ -1,22 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"compilerOptions": {
|
| 3 |
-
"target": "ESNext",
|
| 4 |
-
"esModuleInterop": true,
|
| 5 |
-
"moduleResolution": "node",
|
| 6 |
-
"strict": true,
|
| 7 |
-
"module": "ESNext",
|
| 8 |
-
"rootDir": "src",
|
| 9 |
-
"outDir": "./dist/lib",
|
| 10 |
-
"resolveJsonModule": true,
|
| 11 |
-
"types": ["chrome"],
|
| 12 |
-
"jsx": "react-jsx",
|
| 13 |
-
"jsxImportSource": "react",
|
| 14 |
-
"noEmit": true
|
| 15 |
-
},
|
| 16 |
-
"include": [
|
| 17 |
-
"src",
|
| 18 |
-
],
|
| 19 |
-
"exclude": [
|
| 20 |
-
"src/ui",
|
| 21 |
-
]
|
| 22 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/tsconfig.ui.json
DELETED
|
@@ -1,19 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"compilerOptions": {
|
| 3 |
-
"target": "ESNext",
|
| 4 |
-
"esModuleInterop": true,
|
| 5 |
-
"moduleResolution": "node",
|
| 6 |
-
"strict": true,
|
| 7 |
-
"module": "ESNext",
|
| 8 |
-
"rootDir": "src",
|
| 9 |
-
"outDir": "./lib",
|
| 10 |
-
"resolveJsonModule": true,
|
| 11 |
-
"types": ["chrome"],
|
| 12 |
-
"jsx": "react-jsx",
|
| 13 |
-
"jsxImportSource": "react",
|
| 14 |
-
"noEmit": true,
|
| 15 |
-
},
|
| 16 |
-
"include": [
|
| 17 |
-
"src/ui",
|
| 18 |
-
],
|
| 19 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/vite.config.mts
DELETED
|
@@ -1,54 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
import { resolve } from 'path';
|
| 18 |
-
import { defineConfig } from 'vite';
|
| 19 |
-
import react from '@vitejs/plugin-react';
|
| 20 |
-
import { viteStaticCopy } from 'vite-plugin-static-copy';
|
| 21 |
-
|
| 22 |
-
// https://vitejs.dev/config/
|
| 23 |
-
export default defineConfig({
|
| 24 |
-
plugins: [
|
| 25 |
-
react(),
|
| 26 |
-
viteStaticCopy({
|
| 27 |
-
targets: [
|
| 28 |
-
{
|
| 29 |
-
src: '../../icons/*',
|
| 30 |
-
dest: 'icons'
|
| 31 |
-
},
|
| 32 |
-
{
|
| 33 |
-
src: '../../manifest.json',
|
| 34 |
-
dest: '.'
|
| 35 |
-
}
|
| 36 |
-
]
|
| 37 |
-
})
|
| 38 |
-
],
|
| 39 |
-
root: resolve(__dirname, 'src/ui'),
|
| 40 |
-
build: {
|
| 41 |
-
outDir: resolve(__dirname, 'dist/'),
|
| 42 |
-
emptyOutDir: false,
|
| 43 |
-
minify: false,
|
| 44 |
-
rollupOptions: {
|
| 45 |
-
input: ['src/ui/connect.html', 'src/ui/status.html'],
|
| 46 |
-
output: {
|
| 47 |
-
manualChunks: undefined,
|
| 48 |
-
entryFileNames: 'lib/ui/[name].js',
|
| 49 |
-
chunkFileNames: 'lib/ui/[name].js',
|
| 50 |
-
assetFileNames: 'lib/ui/[name].[ext]'
|
| 51 |
-
}
|
| 52 |
-
}
|
| 53 |
-
}
|
| 54 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extension/vite.sw.config.mts
DELETED
|
@@ -1,31 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
import { resolve } from 'path';
|
| 18 |
-
import { defineConfig } from 'vite';
|
| 19 |
-
|
| 20 |
-
export default defineConfig({
|
| 21 |
-
build: {
|
| 22 |
-
lib: {
|
| 23 |
-
entry: resolve(__dirname, 'src/background.ts'),
|
| 24 |
-
fileName: 'lib/background',
|
| 25 |
-
formats: ['es']
|
| 26 |
-
},
|
| 27 |
-
outDir: 'dist',
|
| 28 |
-
emptyOutDir: false,
|
| 29 |
-
minify: false
|
| 30 |
-
}
|
| 31 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
index.js
CHANGED
|
@@ -15,5 +15,5 @@
|
|
| 15 |
* limitations under the License.
|
| 16 |
*/
|
| 17 |
|
| 18 |
-
const { createConnection } = require('
|
| 19 |
module.exports = { createConnection };
|
|
|
|
| 15 |
* limitations under the License.
|
| 16 |
*/
|
| 17 |
|
| 18 |
+
const { createConnection } = require('playwright/lib/mcp/index');
|
| 19 |
module.exports = { createConnection };
|
package-lock.json
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
package.json
CHANGED
|
@@ -15,18 +15,12 @@
|
|
| 15 |
},
|
| 16 |
"license": "Apache-2.0",
|
| 17 |
"scripts": {
|
| 18 |
-
"
|
| 19 |
-
"
|
| 20 |
-
"lint-fix": "eslint . --fix",
|
| 21 |
-
"check-deps": "node utils/check-deps.js",
|
| 22 |
-
"update-readme": "node utils/update-readme.js",
|
| 23 |
-
"watch": "tsc --watch",
|
| 24 |
"test": "playwright test",
|
| 25 |
"ctest": "playwright test --project=chrome",
|
| 26 |
"ftest": "playwright test --project=firefox",
|
| 27 |
"wtest": "playwright test --project=webkit",
|
| 28 |
-
"run-server": "node lib/browserServer.js",
|
| 29 |
-
"clean": "rm -rf lib",
|
| 30 |
"npm-publish": "npm run clean && npm run build && npm run test && npm publish"
|
| 31 |
},
|
| 32 |
"exports": {
|
|
@@ -37,37 +31,15 @@
|
|
| 37 |
}
|
| 38 |
},
|
| 39 |
"dependencies": {
|
| 40 |
-
"
|
| 41 |
-
"
|
| 42 |
-
"dotenv": "^17.2.0",
|
| 43 |
-
"mime": "^4.0.7",
|
| 44 |
-
"playwright": "1.56.0-alpha-1756505518000",
|
| 45 |
-
"playwright-core": "1.56.0-alpha-1756505518000",
|
| 46 |
-
"ws": "^8.18.1"
|
| 47 |
-
},
|
| 48 |
-
"devDependencies": {
|
| 49 |
-
"@anthropic-ai/sdk": "^0.57.0",
|
| 50 |
-
"@eslint/eslintrc": "^3.2.0",
|
| 51 |
-
"@eslint/js": "^9.19.0",
|
| 52 |
-
"@modelcontextprotocol/sdk": "^1.16.0",
|
| 53 |
-
"@playwright/test": "1.56.0-alpha-1756505518000",
|
| 54 |
-
"@stylistic/eslint-plugin": "^3.0.1",
|
| 55 |
-
"@types/debug": "^4.1.12",
|
| 56 |
-
"@types/node": "^22.13.10",
|
| 57 |
-
"@types/ws": "^8.18.1",
|
| 58 |
-
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
| 59 |
-
"@typescript-eslint/parser": "^8.26.1",
|
| 60 |
-
"@typescript-eslint/utils": "^8.26.1",
|
| 61 |
-
"esbuild": "^0.20.1",
|
| 62 |
-
"eslint": "^9.19.0",
|
| 63 |
-
"eslint-plugin-import": "^2.31.0",
|
| 64 |
-
"eslint-plugin-notice": "^1.0.0",
|
| 65 |
-
"openai": "^5.10.2",
|
| 66 |
-
"typescript": "^5.8.2",
|
| 67 |
-
"zod": "^3.24.1",
|
| 68 |
-
"zod-to-json-schema": "^3.24.4"
|
| 69 |
},
|
| 70 |
"bin": {
|
| 71 |
"mcp-server-playwright": "cli.js"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
}
|
| 73 |
}
|
|
|
|
| 15 |
},
|
| 16 |
"license": "Apache-2.0",
|
| 17 |
"scripts": {
|
| 18 |
+
"lint": "npm run update-readme",
|
| 19 |
+
"update-readme": "node update-readme.js",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
"test": "playwright test",
|
| 21 |
"ctest": "playwright test --project=chrome",
|
| 22 |
"ftest": "playwright test --project=firefox",
|
| 23 |
"wtest": "playwright test --project=webkit",
|
|
|
|
|
|
|
| 24 |
"npm-publish": "npm run clean && npm run build && npm run test && npm publish"
|
| 25 |
},
|
| 26 |
"exports": {
|
|
|
|
| 31 |
}
|
| 32 |
},
|
| 33 |
"dependencies": {
|
| 34 |
+
"playwright": "1.56.0-alpha-1756945786000",
|
| 35 |
+
"zod-to-json-schema": "^3.24.6"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
},
|
| 37 |
"bin": {
|
| 38 |
"mcp-server-playwright": "cli.js"
|
| 39 |
+
},
|
| 40 |
+
"devDependencies": {
|
| 41 |
+
"@modelcontextprotocol/sdk": "^1.17.5",
|
| 42 |
+
"@playwright/test": "1.56.0-alpha-1756945786000",
|
| 43 |
+
"@types/node": "^24.3.0"
|
| 44 |
}
|
| 45 |
}
|
playwright.config.ts
CHANGED
|
@@ -26,7 +26,6 @@ export default defineConfig<TestOptions>({
|
|
| 26 |
reporter: 'list',
|
| 27 |
projects: [
|
| 28 |
{ name: 'chrome' },
|
| 29 |
-
{ name: 'chromium', use: { mcpBrowser: 'chromium' } },
|
| 30 |
...process.env.MCP_IN_DOCKER ? [{
|
| 31 |
name: 'chromium-docker',
|
| 32 |
grep: /browser_navigate|browser_click/,
|
|
@@ -35,8 +34,5 @@ export default defineConfig<TestOptions>({
|
|
| 35 |
mcpMode: 'docker' as const
|
| 36 |
}
|
| 37 |
}] : [],
|
| 38 |
-
{ name: 'firefox', use: { mcpBrowser: 'firefox' } },
|
| 39 |
-
{ name: 'webkit', use: { mcpBrowser: 'webkit' } },
|
| 40 |
-
... process.platform === 'win32' ? [{ name: 'msedge', use: { mcpBrowser: 'msedge' } }] : [],
|
| 41 |
],
|
| 42 |
});
|
|
|
|
| 26 |
reporter: 'list',
|
| 27 |
projects: [
|
| 28 |
{ name: 'chrome' },
|
|
|
|
| 29 |
...process.env.MCP_IN_DOCKER ? [{
|
| 30 |
name: 'chromium-docker',
|
| 31 |
grep: /browser_navigate|browser_click/,
|
|
|
|
| 34 |
mcpMode: 'docker' as const
|
| 35 |
}
|
| 36 |
}] : [],
|
|
|
|
|
|
|
|
|
|
| 37 |
],
|
| 38 |
});
|
src/DEPS.list
DELETED
|
@@ -1,5 +0,0 @@
|
|
| 1 |
-
[program.ts]
|
| 2 |
-
***
|
| 3 |
-
|
| 4 |
-
[index.ts]
|
| 5 |
-
***
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/browser/DEPS.list
DELETED
|
@@ -1,5 +0,0 @@
|
|
| 1 |
-
[*]
|
| 2 |
-
./tools/
|
| 3 |
-
../sdk/
|
| 4 |
-
../log.ts
|
| 5 |
-
../package.ts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/browser/actions.d.ts
DELETED
|
@@ -1,172 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
type Point = { x: number, y: number };
|
| 18 |
-
|
| 19 |
-
export type ActionName =
|
| 20 |
-
'check' |
|
| 21 |
-
'click' |
|
| 22 |
-
'closePage' |
|
| 23 |
-
'fill' |
|
| 24 |
-
'navigate' |
|
| 25 |
-
'openPage' |
|
| 26 |
-
'press' |
|
| 27 |
-
'select' |
|
| 28 |
-
'uncheck' |
|
| 29 |
-
'setInputFiles' |
|
| 30 |
-
'assertText' |
|
| 31 |
-
'assertValue' |
|
| 32 |
-
'assertChecked' |
|
| 33 |
-
'assertVisible' |
|
| 34 |
-
'assertSnapshot';
|
| 35 |
-
|
| 36 |
-
export type ActionBase = {
|
| 37 |
-
name: ActionName,
|
| 38 |
-
signals: Signal[],
|
| 39 |
-
ariaSnapshot?: string,
|
| 40 |
-
};
|
| 41 |
-
|
| 42 |
-
export type ActionWithSelector = ActionBase & {
|
| 43 |
-
selector: string,
|
| 44 |
-
ref?: string,
|
| 45 |
-
};
|
| 46 |
-
|
| 47 |
-
export type ClickAction = ActionWithSelector & {
|
| 48 |
-
name: 'click',
|
| 49 |
-
button: 'left' | 'middle' | 'right',
|
| 50 |
-
modifiers: number,
|
| 51 |
-
clickCount: number,
|
| 52 |
-
position?: Point,
|
| 53 |
-
};
|
| 54 |
-
|
| 55 |
-
export type CheckAction = ActionWithSelector & {
|
| 56 |
-
name: 'check',
|
| 57 |
-
};
|
| 58 |
-
|
| 59 |
-
export type UncheckAction = ActionWithSelector & {
|
| 60 |
-
name: 'uncheck',
|
| 61 |
-
};
|
| 62 |
-
|
| 63 |
-
export type FillAction = ActionWithSelector & {
|
| 64 |
-
name: 'fill',
|
| 65 |
-
text: string,
|
| 66 |
-
};
|
| 67 |
-
|
| 68 |
-
export type NavigateAction = ActionBase & {
|
| 69 |
-
name: 'navigate',
|
| 70 |
-
url: string,
|
| 71 |
-
};
|
| 72 |
-
|
| 73 |
-
export type OpenPageAction = ActionBase & {
|
| 74 |
-
name: 'openPage',
|
| 75 |
-
url: string,
|
| 76 |
-
};
|
| 77 |
-
|
| 78 |
-
export type ClosesPageAction = ActionBase & {
|
| 79 |
-
name: 'closePage',
|
| 80 |
-
};
|
| 81 |
-
|
| 82 |
-
export type PressAction = ActionWithSelector & {
|
| 83 |
-
name: 'press',
|
| 84 |
-
key: string,
|
| 85 |
-
modifiers: number,
|
| 86 |
-
};
|
| 87 |
-
|
| 88 |
-
export type SelectAction = ActionWithSelector & {
|
| 89 |
-
name: 'select',
|
| 90 |
-
options: string[],
|
| 91 |
-
};
|
| 92 |
-
|
| 93 |
-
export type SetInputFilesAction = ActionWithSelector & {
|
| 94 |
-
name: 'setInputFiles',
|
| 95 |
-
files: string[],
|
| 96 |
-
};
|
| 97 |
-
|
| 98 |
-
export type AssertTextAction = ActionWithSelector & {
|
| 99 |
-
name: 'assertText',
|
| 100 |
-
text: string,
|
| 101 |
-
substring: boolean,
|
| 102 |
-
};
|
| 103 |
-
|
| 104 |
-
export type AssertValueAction = ActionWithSelector & {
|
| 105 |
-
name: 'assertValue',
|
| 106 |
-
value: string,
|
| 107 |
-
};
|
| 108 |
-
|
| 109 |
-
export type AssertCheckedAction = ActionWithSelector & {
|
| 110 |
-
name: 'assertChecked',
|
| 111 |
-
checked: boolean,
|
| 112 |
-
};
|
| 113 |
-
|
| 114 |
-
export type AssertVisibleAction = ActionWithSelector & {
|
| 115 |
-
name: 'assertVisible',
|
| 116 |
-
};
|
| 117 |
-
|
| 118 |
-
export type AssertSnapshotAction = ActionWithSelector & {
|
| 119 |
-
name: 'assertSnapshot',
|
| 120 |
-
ariaSnapshot: string,
|
| 121 |
-
};
|
| 122 |
-
|
| 123 |
-
export type Action = ClickAction | CheckAction | ClosesPageAction | OpenPageAction | UncheckAction | FillAction | NavigateAction | PressAction | SelectAction | SetInputFilesAction | AssertTextAction | AssertValueAction | AssertCheckedAction | AssertVisibleAction | AssertSnapshotAction;
|
| 124 |
-
export type AssertAction = AssertCheckedAction | AssertValueAction | AssertTextAction | AssertVisibleAction | AssertSnapshotAction;
|
| 125 |
-
export type PerformOnRecordAction = ClickAction | CheckAction | UncheckAction | PressAction | SelectAction;
|
| 126 |
-
|
| 127 |
-
// Signals.
|
| 128 |
-
|
| 129 |
-
export type BaseSignal = {
|
| 130 |
-
};
|
| 131 |
-
|
| 132 |
-
export type NavigationSignal = BaseSignal & {
|
| 133 |
-
name: 'navigation',
|
| 134 |
-
url: string,
|
| 135 |
-
};
|
| 136 |
-
|
| 137 |
-
export type PopupSignal = BaseSignal & {
|
| 138 |
-
name: 'popup',
|
| 139 |
-
popupAlias: string,
|
| 140 |
-
};
|
| 141 |
-
|
| 142 |
-
export type DownloadSignal = BaseSignal & {
|
| 143 |
-
name: 'download',
|
| 144 |
-
downloadAlias: string,
|
| 145 |
-
};
|
| 146 |
-
|
| 147 |
-
export type DialogSignal = BaseSignal & {
|
| 148 |
-
name: 'dialog',
|
| 149 |
-
dialogAlias: string,
|
| 150 |
-
};
|
| 151 |
-
|
| 152 |
-
export type Signal = NavigationSignal | PopupSignal | DownloadSignal | DialogSignal;
|
| 153 |
-
|
| 154 |
-
export type FrameDescription = {
|
| 155 |
-
pageGuid: string;
|
| 156 |
-
pageAlias: string;
|
| 157 |
-
framePath: string[];
|
| 158 |
-
};
|
| 159 |
-
|
| 160 |
-
export type ActionInContext = {
|
| 161 |
-
frame: FrameDescription;
|
| 162 |
-
description?: string;
|
| 163 |
-
action: Action;
|
| 164 |
-
startTime: number;
|
| 165 |
-
endTime?: number;
|
| 166 |
-
};
|
| 167 |
-
|
| 168 |
-
export type SignalInContext = {
|
| 169 |
-
frame: FrameDescription;
|
| 170 |
-
signal: Signal;
|
| 171 |
-
timestamp: number;
|
| 172 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/browser/browserContextFactory.ts
DELETED
|
@@ -1,253 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
import crypto from 'crypto';
|
| 18 |
-
import fs from 'fs';
|
| 19 |
-
import net from 'net';
|
| 20 |
-
import path from 'path';
|
| 21 |
-
|
| 22 |
-
import * as playwright from 'playwright';
|
| 23 |
-
// @ts-ignore
|
| 24 |
-
import { registryDirectory } from 'playwright-core/lib/server/registry/index';
|
| 25 |
-
// @ts-ignore
|
| 26 |
-
import { startTraceViewerServer } from 'playwright-core/lib/server';
|
| 27 |
-
import { logUnhandledError, testDebug } from '../log';
|
| 28 |
-
import { outputFile } from './config';
|
| 29 |
-
|
| 30 |
-
import type { FullConfig } from './config';
|
| 31 |
-
|
| 32 |
-
export function contextFactory(config: FullConfig): BrowserContextFactory {
|
| 33 |
-
if (config.browser.remoteEndpoint)
|
| 34 |
-
return new RemoteContextFactory(config);
|
| 35 |
-
if (config.browser.cdpEndpoint)
|
| 36 |
-
return new CdpContextFactory(config);
|
| 37 |
-
if (config.browser.isolated)
|
| 38 |
-
return new IsolatedContextFactory(config);
|
| 39 |
-
return new PersistentContextFactory(config);
|
| 40 |
-
}
|
| 41 |
-
|
| 42 |
-
export type ClientInfo = { name?: string, version?: string, rootPath?: string };
|
| 43 |
-
|
| 44 |
-
export interface BrowserContextFactory {
|
| 45 |
-
createContext(clientInfo: ClientInfo, abortSignal: AbortSignal, toolName: string | undefined): Promise<{ browserContext: playwright.BrowserContext, close: () => Promise<void> }>;
|
| 46 |
-
}
|
| 47 |
-
|
| 48 |
-
class BaseContextFactory implements BrowserContextFactory {
|
| 49 |
-
readonly config: FullConfig;
|
| 50 |
-
private _logName: string;
|
| 51 |
-
protected _browserPromise: Promise<playwright.Browser> | undefined;
|
| 52 |
-
|
| 53 |
-
constructor(name: string, config: FullConfig) {
|
| 54 |
-
this._logName = name;
|
| 55 |
-
this.config = config;
|
| 56 |
-
}
|
| 57 |
-
|
| 58 |
-
protected async _obtainBrowser(clientInfo: ClientInfo): Promise<playwright.Browser> {
|
| 59 |
-
if (this._browserPromise)
|
| 60 |
-
return this._browserPromise;
|
| 61 |
-
testDebug(`obtain browser (${this._logName})`);
|
| 62 |
-
this._browserPromise = this._doObtainBrowser(clientInfo);
|
| 63 |
-
void this._browserPromise.then(browser => {
|
| 64 |
-
browser.on('disconnected', () => {
|
| 65 |
-
this._browserPromise = undefined;
|
| 66 |
-
});
|
| 67 |
-
}).catch(() => {
|
| 68 |
-
this._browserPromise = undefined;
|
| 69 |
-
});
|
| 70 |
-
return this._browserPromise;
|
| 71 |
-
}
|
| 72 |
-
|
| 73 |
-
protected async _doObtainBrowser(clientInfo: ClientInfo): Promise<playwright.Browser> {
|
| 74 |
-
throw new Error('Not implemented');
|
| 75 |
-
}
|
| 76 |
-
|
| 77 |
-
async createContext(clientInfo: ClientInfo): Promise<{ browserContext: playwright.BrowserContext, close: () => Promise<void> }> {
|
| 78 |
-
testDebug(`create browser context (${this._logName})`);
|
| 79 |
-
const browser = await this._obtainBrowser(clientInfo);
|
| 80 |
-
const browserContext = await this._doCreateContext(browser);
|
| 81 |
-
return { browserContext, close: () => this._closeBrowserContext(browserContext, browser) };
|
| 82 |
-
}
|
| 83 |
-
|
| 84 |
-
protected async _doCreateContext(browser: playwright.Browser): Promise<playwright.BrowserContext> {
|
| 85 |
-
throw new Error('Not implemented');
|
| 86 |
-
}
|
| 87 |
-
|
| 88 |
-
private async _closeBrowserContext(browserContext: playwright.BrowserContext, browser: playwright.Browser) {
|
| 89 |
-
testDebug(`close browser context (${this._logName})`);
|
| 90 |
-
if (browser.contexts().length === 1)
|
| 91 |
-
this._browserPromise = undefined;
|
| 92 |
-
await browserContext.close().catch(logUnhandledError);
|
| 93 |
-
if (browser.contexts().length === 0) {
|
| 94 |
-
testDebug(`close browser (${this._logName})`);
|
| 95 |
-
await browser.close().catch(logUnhandledError);
|
| 96 |
-
}
|
| 97 |
-
}
|
| 98 |
-
}
|
| 99 |
-
|
| 100 |
-
class IsolatedContextFactory extends BaseContextFactory {
|
| 101 |
-
constructor(config: FullConfig) {
|
| 102 |
-
super('isolated', config);
|
| 103 |
-
}
|
| 104 |
-
|
| 105 |
-
protected override async _doObtainBrowser(clientInfo: ClientInfo): Promise<playwright.Browser> {
|
| 106 |
-
await injectCdpPort(this.config.browser);
|
| 107 |
-
const browserType = playwright[this.config.browser.browserName];
|
| 108 |
-
return browserType.launch({
|
| 109 |
-
tracesDir: await startTraceServer(this.config, clientInfo.rootPath),
|
| 110 |
-
...this.config.browser.launchOptions,
|
| 111 |
-
handleSIGINT: false,
|
| 112 |
-
handleSIGTERM: false,
|
| 113 |
-
}).catch(error => {
|
| 114 |
-
if (error.message.includes('Executable doesn\'t exist'))
|
| 115 |
-
throw new Error(`Browser specified in your config is not installed. Either install it (likely) or change the config.`);
|
| 116 |
-
throw error;
|
| 117 |
-
});
|
| 118 |
-
}
|
| 119 |
-
|
| 120 |
-
protected override async _doCreateContext(browser: playwright.Browser): Promise<playwright.BrowserContext> {
|
| 121 |
-
return browser.newContext(this.config.browser.contextOptions);
|
| 122 |
-
}
|
| 123 |
-
}
|
| 124 |
-
|
| 125 |
-
class CdpContextFactory extends BaseContextFactory {
|
| 126 |
-
constructor(config: FullConfig) {
|
| 127 |
-
super('cdp', config);
|
| 128 |
-
}
|
| 129 |
-
|
| 130 |
-
protected override async _doObtainBrowser(): Promise<playwright.Browser> {
|
| 131 |
-
return playwright.chromium.connectOverCDP(this.config.browser.cdpEndpoint!);
|
| 132 |
-
}
|
| 133 |
-
|
| 134 |
-
protected override async _doCreateContext(browser: playwright.Browser): Promise<playwright.BrowserContext> {
|
| 135 |
-
return this.config.browser.isolated ? await browser.newContext() : browser.contexts()[0];
|
| 136 |
-
}
|
| 137 |
-
}
|
| 138 |
-
|
| 139 |
-
class RemoteContextFactory extends BaseContextFactory {
|
| 140 |
-
constructor(config: FullConfig) {
|
| 141 |
-
super('remote', config);
|
| 142 |
-
}
|
| 143 |
-
|
| 144 |
-
protected override async _doObtainBrowser(): Promise<playwright.Browser> {
|
| 145 |
-
const url = new URL(this.config.browser.remoteEndpoint!);
|
| 146 |
-
url.searchParams.set('browser', this.config.browser.browserName);
|
| 147 |
-
if (this.config.browser.launchOptions)
|
| 148 |
-
url.searchParams.set('launch-options', JSON.stringify(this.config.browser.launchOptions));
|
| 149 |
-
return playwright[this.config.browser.browserName].connect(String(url));
|
| 150 |
-
}
|
| 151 |
-
|
| 152 |
-
protected override async _doCreateContext(browser: playwright.Browser): Promise<playwright.BrowserContext> {
|
| 153 |
-
return browser.newContext();
|
| 154 |
-
}
|
| 155 |
-
}
|
| 156 |
-
|
| 157 |
-
class PersistentContextFactory implements BrowserContextFactory {
|
| 158 |
-
readonly config: FullConfig;
|
| 159 |
-
readonly name = 'persistent';
|
| 160 |
-
readonly description = 'Create a new persistent browser context';
|
| 161 |
-
|
| 162 |
-
private _userDataDirs = new Set<string>();
|
| 163 |
-
|
| 164 |
-
constructor(config: FullConfig) {
|
| 165 |
-
this.config = config;
|
| 166 |
-
}
|
| 167 |
-
|
| 168 |
-
async createContext(clientInfo: ClientInfo): Promise<{ browserContext: playwright.BrowserContext, close: () => Promise<void> }> {
|
| 169 |
-
await injectCdpPort(this.config.browser);
|
| 170 |
-
testDebug('create browser context (persistent)');
|
| 171 |
-
const userDataDir = this.config.browser.userDataDir ?? await this._createUserDataDir(clientInfo.rootPath);
|
| 172 |
-
const tracesDir = await startTraceServer(this.config, clientInfo.rootPath);
|
| 173 |
-
|
| 174 |
-
this._userDataDirs.add(userDataDir);
|
| 175 |
-
testDebug('lock user data dir', userDataDir);
|
| 176 |
-
|
| 177 |
-
const browserType = playwright[this.config.browser.browserName];
|
| 178 |
-
for (let i = 0; i < 5; i++) {
|
| 179 |
-
try {
|
| 180 |
-
const browserContext = await browserType.launchPersistentContext(userDataDir, {
|
| 181 |
-
tracesDir,
|
| 182 |
-
...this.config.browser.launchOptions,
|
| 183 |
-
...this.config.browser.contextOptions,
|
| 184 |
-
handleSIGINT: false,
|
| 185 |
-
handleSIGTERM: false,
|
| 186 |
-
});
|
| 187 |
-
const close = () => this._closeBrowserContext(browserContext, userDataDir);
|
| 188 |
-
return { browserContext, close };
|
| 189 |
-
} catch (error: any) {
|
| 190 |
-
if (error.message.includes('Executable doesn\'t exist'))
|
| 191 |
-
throw new Error(`Browser specified in your config is not installed. Either install it (likely) or change the config.`);
|
| 192 |
-
if (error.message.includes('ProcessSingleton') || error.message.includes('Invalid URL')) {
|
| 193 |
-
// User data directory is already in use, try again.
|
| 194 |
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
| 195 |
-
continue;
|
| 196 |
-
}
|
| 197 |
-
throw error;
|
| 198 |
-
}
|
| 199 |
-
}
|
| 200 |
-
throw new Error(`Browser is already in use for ${userDataDir}, use --isolated to run multiple instances of the same browser`);
|
| 201 |
-
}
|
| 202 |
-
|
| 203 |
-
private async _closeBrowserContext(browserContext: playwright.BrowserContext, userDataDir: string) {
|
| 204 |
-
testDebug('close browser context (persistent)');
|
| 205 |
-
testDebug('release user data dir', userDataDir);
|
| 206 |
-
await browserContext.close().catch(() => {});
|
| 207 |
-
this._userDataDirs.delete(userDataDir);
|
| 208 |
-
testDebug('close browser context complete (persistent)');
|
| 209 |
-
}
|
| 210 |
-
|
| 211 |
-
private async _createUserDataDir(rootPath: string | undefined) {
|
| 212 |
-
const dir = process.env.PWMCP_PROFILES_DIR_FOR_TEST ?? registryDirectory;
|
| 213 |
-
const browserToken = this.config.browser.launchOptions?.channel ?? this.config.browser?.browserName;
|
| 214 |
-
// Hesitant putting hundreds of files into the user's workspace, so using it for hashing instead.
|
| 215 |
-
const rootPathToken = rootPath ? `-${createHash(rootPath)}` : '';
|
| 216 |
-
const result = path.join(dir, `mcp-${browserToken}${rootPathToken}`);
|
| 217 |
-
await fs.promises.mkdir(result, { recursive: true });
|
| 218 |
-
return result;
|
| 219 |
-
}
|
| 220 |
-
}
|
| 221 |
-
|
| 222 |
-
async function injectCdpPort(browserConfig: FullConfig['browser']) {
|
| 223 |
-
if (browserConfig.browserName === 'chromium')
|
| 224 |
-
(browserConfig.launchOptions as any).cdpPort = await findFreePort();
|
| 225 |
-
}
|
| 226 |
-
|
| 227 |
-
async function findFreePort(): Promise<number> {
|
| 228 |
-
return new Promise((resolve, reject) => {
|
| 229 |
-
const server = net.createServer();
|
| 230 |
-
server.listen(0, () => {
|
| 231 |
-
const { port } = server.address() as net.AddressInfo;
|
| 232 |
-
server.close(() => resolve(port));
|
| 233 |
-
});
|
| 234 |
-
server.on('error', reject);
|
| 235 |
-
});
|
| 236 |
-
}
|
| 237 |
-
|
| 238 |
-
async function startTraceServer(config: FullConfig, rootPath: string | undefined): Promise<string | undefined> {
|
| 239 |
-
if (!config.saveTrace)
|
| 240 |
-
return undefined;
|
| 241 |
-
|
| 242 |
-
const tracesDir = await outputFile(config, rootPath, `traces-${Date.now()}`);
|
| 243 |
-
const server = await startTraceViewerServer();
|
| 244 |
-
const urlPrefix = server.urlPrefix('human-readable');
|
| 245 |
-
const url = urlPrefix + '/trace/index.html?trace=' + tracesDir + '/trace.json';
|
| 246 |
-
// eslint-disable-next-line no-console
|
| 247 |
-
console.error('\nTrace viewer listening on ' + url);
|
| 248 |
-
return tracesDir;
|
| 249 |
-
}
|
| 250 |
-
|
| 251 |
-
function createHash(data: string): string {
|
| 252 |
-
return crypto.createHash('sha256').update(data).digest('hex').slice(0, 7);
|
| 253 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/browser/browserServerBackend.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
import { fileURLToPath } from 'url';
|
| 18 |
-
import { FullConfig } from './config';
|
| 19 |
-
import { Context } from './context';
|
| 20 |
-
import { logUnhandledError } from '../log';
|
| 21 |
-
import { Response } from './response';
|
| 22 |
-
import { SessionLog } from './sessionLog';
|
| 23 |
-
import { filteredTools } from './tools';
|
| 24 |
-
import { toMcpTool } from '../sdk/tool';
|
| 25 |
-
|
| 26 |
-
import type { Tool } from './tools/tool';
|
| 27 |
-
import type { BrowserContextFactory } from './browserContextFactory';
|
| 28 |
-
import type * as mcpServer from '../sdk/server';
|
| 29 |
-
import type { ServerBackend } from '../sdk/server';
|
| 30 |
-
|
| 31 |
-
export class BrowserServerBackend implements ServerBackend {
|
| 32 |
-
private _tools: Tool[];
|
| 33 |
-
private _context: Context | undefined;
|
| 34 |
-
private _sessionLog: SessionLog | undefined;
|
| 35 |
-
private _config: FullConfig;
|
| 36 |
-
private _browserContextFactory: BrowserContextFactory;
|
| 37 |
-
|
| 38 |
-
constructor(config: FullConfig, factory: BrowserContextFactory) {
|
| 39 |
-
this._config = config;
|
| 40 |
-
this._browserContextFactory = factory;
|
| 41 |
-
this._tools = filteredTools(config);
|
| 42 |
-
}
|
| 43 |
-
|
| 44 |
-
async initialize(server: mcpServer.Server, clientVersion: mcpServer.ClientVersion, roots: mcpServer.Root[]): Promise<void> {
|
| 45 |
-
let rootPath: string | undefined;
|
| 46 |
-
if (roots.length > 0) {
|
| 47 |
-
const firstRootUri = roots[0]?.uri;
|
| 48 |
-
const url = firstRootUri ? new URL(firstRootUri) : undefined;
|
| 49 |
-
rootPath = url ? fileURLToPath(url) : undefined;
|
| 50 |
-
}
|
| 51 |
-
this._sessionLog = this._config.saveSession ? await SessionLog.create(this._config, rootPath) : undefined;
|
| 52 |
-
this._context = new Context({
|
| 53 |
-
tools: this._tools,
|
| 54 |
-
config: this._config,
|
| 55 |
-
browserContextFactory: this._browserContextFactory,
|
| 56 |
-
sessionLog: this._sessionLog,
|
| 57 |
-
clientInfo: { ...clientVersion, rootPath },
|
| 58 |
-
});
|
| 59 |
-
}
|
| 60 |
-
|
| 61 |
-
async listTools(): Promise<mcpServer.Tool[]> {
|
| 62 |
-
return this._tools.map(tool => toMcpTool(tool.schema));
|
| 63 |
-
}
|
| 64 |
-
|
| 65 |
-
async callTool(name: string, rawArguments: mcpServer.CallToolRequest['params']['arguments']) {
|
| 66 |
-
const tool = this._tools.find(tool => tool.schema.name === name)!;
|
| 67 |
-
if (!tool)
|
| 68 |
-
throw new Error(`Tool "${name}" not found`);
|
| 69 |
-
const parsedArguments = tool.schema.inputSchema.parse(rawArguments || {});
|
| 70 |
-
const context = this._context!;
|
| 71 |
-
const response = new Response(context, name, parsedArguments);
|
| 72 |
-
context.setRunningTool(name);
|
| 73 |
-
try {
|
| 74 |
-
await tool.handle(context, parsedArguments, response);
|
| 75 |
-
await response.finish();
|
| 76 |
-
this._sessionLog?.logResponse(response);
|
| 77 |
-
} catch (error: any) {
|
| 78 |
-
response.addError(String(error));
|
| 79 |
-
} finally {
|
| 80 |
-
context.setRunningTool(undefined);
|
| 81 |
-
}
|
| 82 |
-
return response.serialize();
|
| 83 |
-
}
|
| 84 |
-
|
| 85 |
-
serverClosed() {
|
| 86 |
-
void this._context?.dispose().catch(logUnhandledError);
|
| 87 |
-
}
|
| 88 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/browser/codegen.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
// adapted from:
|
| 18 |
-
// - https://github.com/microsoft/playwright/blob/76ee48dc9d4034536e3ec5b2c7ce8be3b79418a8/packages/playwright-core/src/utils/isomorphic/stringUtils.ts
|
| 19 |
-
// - https://github.com/microsoft/playwright/blob/76ee48dc9d4034536e3ec5b2c7ce8be3b79418a8/packages/playwright-core/src/server/codegen/javascript.ts
|
| 20 |
-
|
| 21 |
-
// NOTE: this function should not be used to escape any selectors.
|
| 22 |
-
export function escapeWithQuotes(text: string, char: string = '\'') {
|
| 23 |
-
const stringified = JSON.stringify(text);
|
| 24 |
-
const escapedText = stringified.substring(1, stringified.length - 1).replace(/\\"/g, '"');
|
| 25 |
-
if (char === '\'')
|
| 26 |
-
return char + escapedText.replace(/[']/g, '\\\'') + char;
|
| 27 |
-
if (char === '"')
|
| 28 |
-
return char + escapedText.replace(/["]/g, '\\"') + char;
|
| 29 |
-
if (char === '`')
|
| 30 |
-
return char + escapedText.replace(/[`]/g, '\\`') + char;
|
| 31 |
-
throw new Error('Invalid escape char');
|
| 32 |
-
}
|
| 33 |
-
|
| 34 |
-
export function quote(text: string) {
|
| 35 |
-
return escapeWithQuotes(text, '\'');
|
| 36 |
-
}
|
| 37 |
-
|
| 38 |
-
export function formatObject(value: any, indent = ' '): string {
|
| 39 |
-
if (typeof value === 'string')
|
| 40 |
-
return quote(value);
|
| 41 |
-
if (Array.isArray(value))
|
| 42 |
-
return `[${value.map(o => formatObject(o)).join(', ')}]`;
|
| 43 |
-
if (typeof value === 'object') {
|
| 44 |
-
const keys = Object.keys(value).filter(key => value[key] !== undefined).sort();
|
| 45 |
-
if (!keys.length)
|
| 46 |
-
return '{}';
|
| 47 |
-
const tokens: string[] = [];
|
| 48 |
-
for (const key of keys)
|
| 49 |
-
tokens.push(`${key}: ${formatObject(value[key])}`);
|
| 50 |
-
return `{\n${indent}${tokens.join(`,\n${indent}`)}\n}`;
|
| 51 |
-
}
|
| 52 |
-
return String(value);
|
| 53 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/browser/config.ts
DELETED
|
@@ -1,327 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
import fs from 'fs';
|
| 18 |
-
import os from 'os';
|
| 19 |
-
import path from 'path';
|
| 20 |
-
import { devices } from 'playwright';
|
| 21 |
-
|
| 22 |
-
import type { Config, ToolCapability } from '../../config';
|
| 23 |
-
import type { BrowserContextOptions, LaunchOptions } from 'playwright';
|
| 24 |
-
|
| 25 |
-
export type CLIOptions = {
|
| 26 |
-
allowedOrigins?: string[];
|
| 27 |
-
blockedOrigins?: string[];
|
| 28 |
-
blockServiceWorkers?: boolean;
|
| 29 |
-
browser?: string;
|
| 30 |
-
caps?: string[];
|
| 31 |
-
cdpEndpoint?: string;
|
| 32 |
-
config?: string;
|
| 33 |
-
device?: string;
|
| 34 |
-
executablePath?: string;
|
| 35 |
-
headless?: boolean;
|
| 36 |
-
host?: string;
|
| 37 |
-
ignoreHttpsErrors?: boolean;
|
| 38 |
-
isolated?: boolean;
|
| 39 |
-
imageResponses?: 'allow' | 'omit';
|
| 40 |
-
sandbox?: boolean;
|
| 41 |
-
outputDir?: string;
|
| 42 |
-
port?: number;
|
| 43 |
-
proxyBypass?: string;
|
| 44 |
-
proxyServer?: string;
|
| 45 |
-
saveSession?: boolean;
|
| 46 |
-
saveTrace?: boolean;
|
| 47 |
-
storageState?: string;
|
| 48 |
-
userAgent?: string;
|
| 49 |
-
userDataDir?: string;
|
| 50 |
-
viewportSize?: string;
|
| 51 |
-
};
|
| 52 |
-
|
| 53 |
-
const defaultConfig: FullConfig = {
|
| 54 |
-
browser: {
|
| 55 |
-
browserName: 'chromium',
|
| 56 |
-
launchOptions: {
|
| 57 |
-
channel: 'chrome',
|
| 58 |
-
headless: os.platform() === 'linux' && !process.env.DISPLAY,
|
| 59 |
-
chromiumSandbox: true,
|
| 60 |
-
},
|
| 61 |
-
contextOptions: {
|
| 62 |
-
viewport: null,
|
| 63 |
-
},
|
| 64 |
-
},
|
| 65 |
-
network: {
|
| 66 |
-
allowedOrigins: undefined,
|
| 67 |
-
blockedOrigins: undefined,
|
| 68 |
-
},
|
| 69 |
-
server: {},
|
| 70 |
-
saveTrace: false,
|
| 71 |
-
};
|
| 72 |
-
|
| 73 |
-
type BrowserUserConfig = NonNullable<Config['browser']>;
|
| 74 |
-
|
| 75 |
-
export type FullConfig = Config & {
|
| 76 |
-
browser: Omit<BrowserUserConfig, 'browserName'> & {
|
| 77 |
-
browserName: 'chromium' | 'firefox' | 'webkit';
|
| 78 |
-
launchOptions: NonNullable<BrowserUserConfig['launchOptions']>;
|
| 79 |
-
contextOptions: NonNullable<BrowserUserConfig['contextOptions']>;
|
| 80 |
-
},
|
| 81 |
-
network: NonNullable<Config['network']>,
|
| 82 |
-
saveTrace: boolean;
|
| 83 |
-
server: NonNullable<Config['server']>,
|
| 84 |
-
};
|
| 85 |
-
|
| 86 |
-
export async function resolveConfig(config: Config): Promise<FullConfig> {
|
| 87 |
-
return mergeConfig(defaultConfig, config);
|
| 88 |
-
}
|
| 89 |
-
|
| 90 |
-
export async function resolveCLIConfig(cliOptions: CLIOptions): Promise<FullConfig> {
|
| 91 |
-
const configInFile = await loadConfig(cliOptions.config);
|
| 92 |
-
const envOverrides = configFromEnv();
|
| 93 |
-
const cliOverrides = configFromCLIOptions(cliOptions);
|
| 94 |
-
let result = defaultConfig;
|
| 95 |
-
result = mergeConfig(result, configInFile);
|
| 96 |
-
result = mergeConfig(result, envOverrides);
|
| 97 |
-
result = mergeConfig(result, cliOverrides);
|
| 98 |
-
return result;
|
| 99 |
-
}
|
| 100 |
-
|
| 101 |
-
export function configFromCLIOptions(cliOptions: CLIOptions): Config {
|
| 102 |
-
let browserName: 'chromium' | 'firefox' | 'webkit' | undefined;
|
| 103 |
-
let channel: string | undefined;
|
| 104 |
-
switch (cliOptions.browser) {
|
| 105 |
-
case 'chrome':
|
| 106 |
-
case 'chrome-beta':
|
| 107 |
-
case 'chrome-canary':
|
| 108 |
-
case 'chrome-dev':
|
| 109 |
-
case 'chromium':
|
| 110 |
-
case 'msedge':
|
| 111 |
-
case 'msedge-beta':
|
| 112 |
-
case 'msedge-canary':
|
| 113 |
-
case 'msedge-dev':
|
| 114 |
-
browserName = 'chromium';
|
| 115 |
-
channel = cliOptions.browser;
|
| 116 |
-
break;
|
| 117 |
-
case 'firefox':
|
| 118 |
-
browserName = 'firefox';
|
| 119 |
-
break;
|
| 120 |
-
case 'webkit':
|
| 121 |
-
browserName = 'webkit';
|
| 122 |
-
break;
|
| 123 |
-
}
|
| 124 |
-
|
| 125 |
-
// Launch options
|
| 126 |
-
const launchOptions: LaunchOptions = {
|
| 127 |
-
channel,
|
| 128 |
-
executablePath: cliOptions.executablePath,
|
| 129 |
-
headless: cliOptions.headless,
|
| 130 |
-
};
|
| 131 |
-
|
| 132 |
-
// --no-sandbox was passed, disable the sandbox
|
| 133 |
-
if (cliOptions.sandbox === false)
|
| 134 |
-
launchOptions.chromiumSandbox = false;
|
| 135 |
-
|
| 136 |
-
if (cliOptions.proxyServer) {
|
| 137 |
-
launchOptions.proxy = {
|
| 138 |
-
server: cliOptions.proxyServer
|
| 139 |
-
};
|
| 140 |
-
if (cliOptions.proxyBypass)
|
| 141 |
-
launchOptions.proxy.bypass = cliOptions.proxyBypass;
|
| 142 |
-
}
|
| 143 |
-
|
| 144 |
-
if (cliOptions.device && cliOptions.cdpEndpoint)
|
| 145 |
-
throw new Error('Device emulation is not supported with cdpEndpoint.');
|
| 146 |
-
|
| 147 |
-
// Context options
|
| 148 |
-
const contextOptions: BrowserContextOptions = cliOptions.device ? devices[cliOptions.device] : {};
|
| 149 |
-
if (cliOptions.storageState)
|
| 150 |
-
contextOptions.storageState = cliOptions.storageState;
|
| 151 |
-
|
| 152 |
-
if (cliOptions.userAgent)
|
| 153 |
-
contextOptions.userAgent = cliOptions.userAgent;
|
| 154 |
-
|
| 155 |
-
if (cliOptions.viewportSize) {
|
| 156 |
-
try {
|
| 157 |
-
const [width, height] = cliOptions.viewportSize.split(',').map(n => +n);
|
| 158 |
-
if (isNaN(width) || isNaN(height))
|
| 159 |
-
throw new Error('bad values');
|
| 160 |
-
contextOptions.viewport = { width, height };
|
| 161 |
-
} catch (e) {
|
| 162 |
-
throw new Error('Invalid viewport size format: use "width,height", for example --viewport-size="800,600"');
|
| 163 |
-
}
|
| 164 |
-
}
|
| 165 |
-
|
| 166 |
-
if (cliOptions.ignoreHttpsErrors)
|
| 167 |
-
contextOptions.ignoreHTTPSErrors = true;
|
| 168 |
-
|
| 169 |
-
if (cliOptions.blockServiceWorkers)
|
| 170 |
-
contextOptions.serviceWorkers = 'block';
|
| 171 |
-
|
| 172 |
-
const result: Config = {
|
| 173 |
-
browser: {
|
| 174 |
-
browserName,
|
| 175 |
-
isolated: cliOptions.isolated,
|
| 176 |
-
userDataDir: cliOptions.userDataDir,
|
| 177 |
-
launchOptions,
|
| 178 |
-
contextOptions,
|
| 179 |
-
cdpEndpoint: cliOptions.cdpEndpoint,
|
| 180 |
-
},
|
| 181 |
-
server: {
|
| 182 |
-
port: cliOptions.port,
|
| 183 |
-
host: cliOptions.host,
|
| 184 |
-
},
|
| 185 |
-
capabilities: cliOptions.caps as ToolCapability[],
|
| 186 |
-
network: {
|
| 187 |
-
allowedOrigins: cliOptions.allowedOrigins,
|
| 188 |
-
blockedOrigins: cliOptions.blockedOrigins,
|
| 189 |
-
},
|
| 190 |
-
saveSession: cliOptions.saveSession,
|
| 191 |
-
saveTrace: cliOptions.saveTrace,
|
| 192 |
-
outputDir: cliOptions.outputDir,
|
| 193 |
-
imageResponses: cliOptions.imageResponses,
|
| 194 |
-
};
|
| 195 |
-
|
| 196 |
-
return result;
|
| 197 |
-
}
|
| 198 |
-
|
| 199 |
-
function configFromEnv(): Config {
|
| 200 |
-
const options: CLIOptions = {};
|
| 201 |
-
options.allowedOrigins = semicolonSeparatedList(process.env.PLAYWRIGHT_MCP_ALLOWED_ORIGINS);
|
| 202 |
-
options.blockedOrigins = semicolonSeparatedList(process.env.PLAYWRIGHT_MCP_BLOCKED_ORIGINS);
|
| 203 |
-
options.blockServiceWorkers = envToBoolean(process.env.PLAYWRIGHT_MCP_BLOCK_SERVICE_WORKERS);
|
| 204 |
-
options.browser = envToString(process.env.PLAYWRIGHT_MCP_BROWSER);
|
| 205 |
-
options.caps = commaSeparatedList(process.env.PLAYWRIGHT_MCP_CAPS);
|
| 206 |
-
options.cdpEndpoint = envToString(process.env.PLAYWRIGHT_MCP_CDP_ENDPOINT);
|
| 207 |
-
options.config = envToString(process.env.PLAYWRIGHT_MCP_CONFIG);
|
| 208 |
-
options.device = envToString(process.env.PLAYWRIGHT_MCP_DEVICE);
|
| 209 |
-
options.executablePath = envToString(process.env.PLAYWRIGHT_MCP_EXECUTABLE_PATH);
|
| 210 |
-
options.headless = envToBoolean(process.env.PLAYWRIGHT_MCP_HEADLESS);
|
| 211 |
-
options.host = envToString(process.env.PLAYWRIGHT_MCP_HOST);
|
| 212 |
-
options.ignoreHttpsErrors = envToBoolean(process.env.PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS);
|
| 213 |
-
options.isolated = envToBoolean(process.env.PLAYWRIGHT_MCP_ISOLATED);
|
| 214 |
-
if (process.env.PLAYWRIGHT_MCP_IMAGE_RESPONSES === 'omit')
|
| 215 |
-
options.imageResponses = 'omit';
|
| 216 |
-
options.sandbox = envToBoolean(process.env.PLAYWRIGHT_MCP_SANDBOX);
|
| 217 |
-
options.outputDir = envToString(process.env.PLAYWRIGHT_MCP_OUTPUT_DIR);
|
| 218 |
-
options.port = envToNumber(process.env.PLAYWRIGHT_MCP_PORT);
|
| 219 |
-
options.proxyBypass = envToString(process.env.PLAYWRIGHT_MCP_PROXY_BYPASS);
|
| 220 |
-
options.proxyServer = envToString(process.env.PLAYWRIGHT_MCP_PROXY_SERVER);
|
| 221 |
-
options.saveTrace = envToBoolean(process.env.PLAYWRIGHT_MCP_SAVE_TRACE);
|
| 222 |
-
options.storageState = envToString(process.env.PLAYWRIGHT_MCP_STORAGE_STATE);
|
| 223 |
-
options.userAgent = envToString(process.env.PLAYWRIGHT_MCP_USER_AGENT);
|
| 224 |
-
options.userDataDir = envToString(process.env.PLAYWRIGHT_MCP_USER_DATA_DIR);
|
| 225 |
-
options.viewportSize = envToString(process.env.PLAYWRIGHT_MCP_VIEWPORT_SIZE);
|
| 226 |
-
return configFromCLIOptions(options);
|
| 227 |
-
}
|
| 228 |
-
|
| 229 |
-
async function loadConfig(configFile: string | undefined): Promise<Config> {
|
| 230 |
-
if (!configFile)
|
| 231 |
-
return {};
|
| 232 |
-
|
| 233 |
-
try {
|
| 234 |
-
return JSON.parse(await fs.promises.readFile(configFile, 'utf8'));
|
| 235 |
-
} catch (error) {
|
| 236 |
-
throw new Error(`Failed to load config file: ${configFile}, ${error}`);
|
| 237 |
-
}
|
| 238 |
-
}
|
| 239 |
-
|
| 240 |
-
export async function outputFile(config: FullConfig, rootPath: string | undefined, name: string): Promise<string> {
|
| 241 |
-
const outputDir = config.outputDir
|
| 242 |
-
?? (rootPath ? path.join(rootPath, '.playwright-mcp') : undefined)
|
| 243 |
-
?? path.join(os.tmpdir(), 'playwright-mcp-output', sanitizeForFilePath(new Date().toISOString()));
|
| 244 |
-
|
| 245 |
-
await fs.promises.mkdir(outputDir, { recursive: true });
|
| 246 |
-
const fileName = sanitizeForFilePath(name);
|
| 247 |
-
return path.join(outputDir, fileName);
|
| 248 |
-
}
|
| 249 |
-
|
| 250 |
-
function pickDefined<T extends object>(obj: T | undefined): Partial<T> {
|
| 251 |
-
return Object.fromEntries(
|
| 252 |
-
Object.entries(obj ?? {}).filter(([_, v]) => v !== undefined)
|
| 253 |
-
) as Partial<T>;
|
| 254 |
-
}
|
| 255 |
-
|
| 256 |
-
function mergeConfig(base: FullConfig, overrides: Config): FullConfig {
|
| 257 |
-
const browser: FullConfig['browser'] = {
|
| 258 |
-
...pickDefined(base.browser),
|
| 259 |
-
...pickDefined(overrides.browser),
|
| 260 |
-
browserName: overrides.browser?.browserName ?? base.browser?.browserName ?? 'chromium',
|
| 261 |
-
isolated: overrides.browser?.isolated ?? base.browser?.isolated ?? false,
|
| 262 |
-
launchOptions: {
|
| 263 |
-
...pickDefined(base.browser?.launchOptions),
|
| 264 |
-
...pickDefined(overrides.browser?.launchOptions),
|
| 265 |
-
...{ assistantMode: true },
|
| 266 |
-
},
|
| 267 |
-
contextOptions: {
|
| 268 |
-
...pickDefined(base.browser?.contextOptions),
|
| 269 |
-
...pickDefined(overrides.browser?.contextOptions),
|
| 270 |
-
},
|
| 271 |
-
};
|
| 272 |
-
|
| 273 |
-
if (browser.browserName !== 'chromium' && browser.launchOptions)
|
| 274 |
-
delete browser.launchOptions.channel;
|
| 275 |
-
|
| 276 |
-
return {
|
| 277 |
-
...pickDefined(base),
|
| 278 |
-
...pickDefined(overrides),
|
| 279 |
-
browser,
|
| 280 |
-
network: {
|
| 281 |
-
...pickDefined(base.network),
|
| 282 |
-
...pickDefined(overrides.network),
|
| 283 |
-
},
|
| 284 |
-
server: {
|
| 285 |
-
...pickDefined(base.server),
|
| 286 |
-
...pickDefined(overrides.server),
|
| 287 |
-
},
|
| 288 |
-
} as FullConfig;
|
| 289 |
-
}
|
| 290 |
-
|
| 291 |
-
export function semicolonSeparatedList(value: string | undefined): string[] | undefined {
|
| 292 |
-
if (!value)
|
| 293 |
-
return undefined;
|
| 294 |
-
return value.split(';').map(v => v.trim());
|
| 295 |
-
}
|
| 296 |
-
|
| 297 |
-
export function commaSeparatedList(value: string | undefined): string[] | undefined {
|
| 298 |
-
if (!value)
|
| 299 |
-
return undefined;
|
| 300 |
-
return value.split(',').map(v => v.trim());
|
| 301 |
-
}
|
| 302 |
-
|
| 303 |
-
function envToNumber(value: string | undefined): number | undefined {
|
| 304 |
-
if (!value)
|
| 305 |
-
return undefined;
|
| 306 |
-
return +value;
|
| 307 |
-
}
|
| 308 |
-
|
| 309 |
-
function envToBoolean(value: string | undefined): boolean | undefined {
|
| 310 |
-
if (value === 'true' || value === '1')
|
| 311 |
-
return true;
|
| 312 |
-
if (value === 'false' || value === '0')
|
| 313 |
-
return false;
|
| 314 |
-
return undefined;
|
| 315 |
-
}
|
| 316 |
-
|
| 317 |
-
function envToString(value: string | undefined): string | undefined {
|
| 318 |
-
return value ? value.trim() : undefined;
|
| 319 |
-
}
|
| 320 |
-
|
| 321 |
-
function sanitizeForFilePath(s: string) {
|
| 322 |
-
const sanitize = (s: string) => s.replace(/[\x00-\x2C\x2E-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+/g, '-');
|
| 323 |
-
const separator = s.lastIndexOf('.');
|
| 324 |
-
if (separator === -1)
|
| 325 |
-
return sanitize(s);
|
| 326 |
-
return sanitize(s.substring(0, separator)) + '.' + sanitize(s.substring(separator + 1));
|
| 327 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/browser/context.ts
DELETED
|
@@ -1,276 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
import debug from 'debug';
|
| 18 |
-
import * as playwright from 'playwright';
|
| 19 |
-
|
| 20 |
-
import { logUnhandledError } from '../log';
|
| 21 |
-
import { Tab } from './tab';
|
| 22 |
-
import { outputFile } from './config';
|
| 23 |
-
|
| 24 |
-
import type { FullConfig } from './config';
|
| 25 |
-
import type { Tool } from './tools/tool';
|
| 26 |
-
import type { BrowserContextFactory, ClientInfo } from './browserContextFactory';
|
| 27 |
-
import type * as actions from './actions';
|
| 28 |
-
import type { SessionLog } from './sessionLog';
|
| 29 |
-
|
| 30 |
-
const testDebug = debug('pw:mcp:test');
|
| 31 |
-
|
| 32 |
-
type ContextOptions = {
|
| 33 |
-
tools: Tool[];
|
| 34 |
-
config: FullConfig;
|
| 35 |
-
browserContextFactory: BrowserContextFactory;
|
| 36 |
-
sessionLog: SessionLog | undefined;
|
| 37 |
-
clientInfo: ClientInfo;
|
| 38 |
-
};
|
| 39 |
-
|
| 40 |
-
export class Context {
|
| 41 |
-
readonly tools: Tool[];
|
| 42 |
-
readonly config: FullConfig;
|
| 43 |
-
readonly sessionLog: SessionLog | undefined;
|
| 44 |
-
readonly options: ContextOptions;
|
| 45 |
-
private _browserContextPromise: Promise<{ browserContext: playwright.BrowserContext, close: () => Promise<void> }> | undefined;
|
| 46 |
-
private _browserContextFactory: BrowserContextFactory;
|
| 47 |
-
private _tabs: Tab[] = [];
|
| 48 |
-
private _currentTab: Tab | undefined;
|
| 49 |
-
private _clientInfo: ClientInfo;
|
| 50 |
-
|
| 51 |
-
private static _allContexts: Set<Context> = new Set();
|
| 52 |
-
private _closeBrowserContextPromise: Promise<void> | undefined;
|
| 53 |
-
private _runningToolName: string | undefined;
|
| 54 |
-
private _abortController = new AbortController();
|
| 55 |
-
|
| 56 |
-
constructor(options: ContextOptions) {
|
| 57 |
-
this.tools = options.tools;
|
| 58 |
-
this.config = options.config;
|
| 59 |
-
this.sessionLog = options.sessionLog;
|
| 60 |
-
this.options = options;
|
| 61 |
-
this._browserContextFactory = options.browserContextFactory;
|
| 62 |
-
this._clientInfo = options.clientInfo;
|
| 63 |
-
testDebug('create context');
|
| 64 |
-
Context._allContexts.add(this);
|
| 65 |
-
}
|
| 66 |
-
|
| 67 |
-
static async disposeAll() {
|
| 68 |
-
await Promise.all([...Context._allContexts].map(context => context.dispose()));
|
| 69 |
-
}
|
| 70 |
-
|
| 71 |
-
tabs(): Tab[] {
|
| 72 |
-
return this._tabs;
|
| 73 |
-
}
|
| 74 |
-
|
| 75 |
-
currentTab(): Tab | undefined {
|
| 76 |
-
return this._currentTab;
|
| 77 |
-
}
|
| 78 |
-
|
| 79 |
-
currentTabOrDie(): Tab {
|
| 80 |
-
if (!this._currentTab)
|
| 81 |
-
throw new Error('No open pages available. Use the "browser_navigate" tool to navigate to a page first.');
|
| 82 |
-
return this._currentTab;
|
| 83 |
-
}
|
| 84 |
-
|
| 85 |
-
async newTab(): Promise<Tab> {
|
| 86 |
-
const { browserContext } = await this._ensureBrowserContext();
|
| 87 |
-
const page = await browserContext.newPage();
|
| 88 |
-
this._currentTab = this._tabs.find(t => t.page === page)!;
|
| 89 |
-
return this._currentTab;
|
| 90 |
-
}
|
| 91 |
-
|
| 92 |
-
async selectTab(index: number) {
|
| 93 |
-
const tab = this._tabs[index];
|
| 94 |
-
if (!tab)
|
| 95 |
-
throw new Error(`Tab ${index} not found`);
|
| 96 |
-
await tab.page.bringToFront();
|
| 97 |
-
this._currentTab = tab;
|
| 98 |
-
return tab;
|
| 99 |
-
}
|
| 100 |
-
|
| 101 |
-
async ensureTab(): Promise<Tab> {
|
| 102 |
-
const { browserContext } = await this._ensureBrowserContext();
|
| 103 |
-
if (!this._currentTab)
|
| 104 |
-
await browserContext.newPage();
|
| 105 |
-
return this._currentTab!;
|
| 106 |
-
}
|
| 107 |
-
|
| 108 |
-
async closeTab(index: number | undefined): Promise<string> {
|
| 109 |
-
const tab = index === undefined ? this._currentTab : this._tabs[index];
|
| 110 |
-
if (!tab)
|
| 111 |
-
throw new Error(`Tab ${index} not found`);
|
| 112 |
-
const url = tab.page.url();
|
| 113 |
-
await tab.page.close();
|
| 114 |
-
return url;
|
| 115 |
-
}
|
| 116 |
-
|
| 117 |
-
async outputFile(name: string): Promise<string> {
|
| 118 |
-
return outputFile(this.config, this._clientInfo.rootPath, name);
|
| 119 |
-
}
|
| 120 |
-
|
| 121 |
-
private _onPageCreated(page: playwright.Page) {
|
| 122 |
-
const tab = new Tab(this, page, tab => this._onPageClosed(tab));
|
| 123 |
-
this._tabs.push(tab);
|
| 124 |
-
if (!this._currentTab)
|
| 125 |
-
this._currentTab = tab;
|
| 126 |
-
}
|
| 127 |
-
|
| 128 |
-
private _onPageClosed(tab: Tab) {
|
| 129 |
-
const index = this._tabs.indexOf(tab);
|
| 130 |
-
if (index === -1)
|
| 131 |
-
return;
|
| 132 |
-
this._tabs.splice(index, 1);
|
| 133 |
-
|
| 134 |
-
if (this._currentTab === tab)
|
| 135 |
-
this._currentTab = this._tabs[Math.min(index, this._tabs.length - 1)];
|
| 136 |
-
if (!this._tabs.length)
|
| 137 |
-
void this.closeBrowserContext();
|
| 138 |
-
}
|
| 139 |
-
|
| 140 |
-
async closeBrowserContext() {
|
| 141 |
-
if (!this._closeBrowserContextPromise)
|
| 142 |
-
this._closeBrowserContextPromise = this._closeBrowserContextImpl().catch(logUnhandledError);
|
| 143 |
-
await this._closeBrowserContextPromise;
|
| 144 |
-
this._closeBrowserContextPromise = undefined;
|
| 145 |
-
}
|
| 146 |
-
|
| 147 |
-
isRunningTool() {
|
| 148 |
-
return this._runningToolName !== undefined;
|
| 149 |
-
}
|
| 150 |
-
|
| 151 |
-
setRunningTool(name: string | undefined) {
|
| 152 |
-
this._runningToolName = name;
|
| 153 |
-
}
|
| 154 |
-
|
| 155 |
-
private async _closeBrowserContextImpl() {
|
| 156 |
-
if (!this._browserContextPromise)
|
| 157 |
-
return;
|
| 158 |
-
|
| 159 |
-
testDebug('close context');
|
| 160 |
-
|
| 161 |
-
const promise = this._browserContextPromise;
|
| 162 |
-
this._browserContextPromise = undefined;
|
| 163 |
-
|
| 164 |
-
await promise.then(async ({ browserContext, close }) => {
|
| 165 |
-
if (this.config.saveTrace)
|
| 166 |
-
await browserContext.tracing.stop();
|
| 167 |
-
await close();
|
| 168 |
-
});
|
| 169 |
-
}
|
| 170 |
-
|
| 171 |
-
async dispose() {
|
| 172 |
-
this._abortController.abort('MCP context disposed');
|
| 173 |
-
await this.closeBrowserContext();
|
| 174 |
-
Context._allContexts.delete(this);
|
| 175 |
-
}
|
| 176 |
-
|
| 177 |
-
private async _setupRequestInterception(context: playwright.BrowserContext) {
|
| 178 |
-
if (this.config.network?.allowedOrigins?.length) {
|
| 179 |
-
await context.route('**', route => route.abort('blockedbyclient'));
|
| 180 |
-
|
| 181 |
-
for (const origin of this.config.network.allowedOrigins)
|
| 182 |
-
await context.route(`*://${origin}/**`, route => route.continue());
|
| 183 |
-
}
|
| 184 |
-
|
| 185 |
-
if (this.config.network?.blockedOrigins?.length) {
|
| 186 |
-
for (const origin of this.config.network.blockedOrigins)
|
| 187 |
-
await context.route(`*://${origin}/**`, route => route.abort('blockedbyclient'));
|
| 188 |
-
}
|
| 189 |
-
}
|
| 190 |
-
|
| 191 |
-
private _ensureBrowserContext() {
|
| 192 |
-
if (!this._browserContextPromise) {
|
| 193 |
-
this._browserContextPromise = this._setupBrowserContext();
|
| 194 |
-
this._browserContextPromise.catch(() => {
|
| 195 |
-
this._browserContextPromise = undefined;
|
| 196 |
-
});
|
| 197 |
-
}
|
| 198 |
-
return this._browserContextPromise;
|
| 199 |
-
}
|
| 200 |
-
|
| 201 |
-
private async _setupBrowserContext(): Promise<{ browserContext: playwright.BrowserContext, close: () => Promise<void> }> {
|
| 202 |
-
if (this._closeBrowserContextPromise)
|
| 203 |
-
throw new Error('Another browser context is being closed.');
|
| 204 |
-
// TODO: move to the browser context factory to make it based on isolation mode.
|
| 205 |
-
const result = await this._browserContextFactory.createContext(this._clientInfo, this._abortController.signal, this._runningToolName);
|
| 206 |
-
const { browserContext } = result;
|
| 207 |
-
await this._setupRequestInterception(browserContext);
|
| 208 |
-
if (this.sessionLog)
|
| 209 |
-
await InputRecorder.create(this, browserContext);
|
| 210 |
-
for (const page of browserContext.pages())
|
| 211 |
-
this._onPageCreated(page);
|
| 212 |
-
browserContext.on('page', page => this._onPageCreated(page));
|
| 213 |
-
if (this.config.saveTrace) {
|
| 214 |
-
await browserContext.tracing.start({
|
| 215 |
-
name: 'trace',
|
| 216 |
-
screenshots: false,
|
| 217 |
-
snapshots: true,
|
| 218 |
-
sources: false,
|
| 219 |
-
});
|
| 220 |
-
}
|
| 221 |
-
return result;
|
| 222 |
-
}
|
| 223 |
-
}
|
| 224 |
-
|
| 225 |
-
export class InputRecorder {
|
| 226 |
-
private _context: Context;
|
| 227 |
-
private _browserContext: playwright.BrowserContext;
|
| 228 |
-
|
| 229 |
-
private constructor(context: Context, browserContext: playwright.BrowserContext) {
|
| 230 |
-
this._context = context;
|
| 231 |
-
this._browserContext = browserContext;
|
| 232 |
-
}
|
| 233 |
-
|
| 234 |
-
static async create(context: Context, browserContext: playwright.BrowserContext) {
|
| 235 |
-
const recorder = new InputRecorder(context, browserContext);
|
| 236 |
-
await recorder._initialize();
|
| 237 |
-
return recorder;
|
| 238 |
-
}
|
| 239 |
-
|
| 240 |
-
private async _initialize() {
|
| 241 |
-
const sessionLog = this._context.sessionLog!;
|
| 242 |
-
await (this._browserContext as any)._enableRecorder({
|
| 243 |
-
mode: 'recording',
|
| 244 |
-
recorderMode: 'api',
|
| 245 |
-
}, {
|
| 246 |
-
actionAdded: (page: playwright.Page, data: actions.ActionInContext, code: string) => {
|
| 247 |
-
if (this._context.isRunningTool())
|
| 248 |
-
return;
|
| 249 |
-
const tab = Tab.forPage(page);
|
| 250 |
-
if (tab)
|
| 251 |
-
sessionLog.logUserAction(data.action, tab, code, false);
|
| 252 |
-
},
|
| 253 |
-
actionUpdated: (page: playwright.Page, data: actions.ActionInContext, code: string) => {
|
| 254 |
-
if (this._context.isRunningTool())
|
| 255 |
-
return;
|
| 256 |
-
const tab = Tab.forPage(page);
|
| 257 |
-
if (tab)
|
| 258 |
-
sessionLog.logUserAction(data.action, tab, code, true);
|
| 259 |
-
},
|
| 260 |
-
signalAdded: (page: playwright.Page, data: actions.SignalInContext) => {
|
| 261 |
-
if (this._context.isRunningTool())
|
| 262 |
-
return;
|
| 263 |
-
if (data.signal.name !== 'navigation')
|
| 264 |
-
return;
|
| 265 |
-
const tab = Tab.forPage(page);
|
| 266 |
-
const navigateAction: actions.Action = {
|
| 267 |
-
name: 'navigate',
|
| 268 |
-
url: data.signal.url,
|
| 269 |
-
signals: [],
|
| 270 |
-
};
|
| 271 |
-
if (tab)
|
| 272 |
-
sessionLog.logUserAction(navigateAction, tab, `await page.goto('${data.signal.url}');`, false);
|
| 273 |
-
},
|
| 274 |
-
});
|
| 275 |
-
}
|
| 276 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/browser/response.ts
DELETED
|
@@ -1,201 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
import { renderModalStates } from './tab';
|
| 18 |
-
|
| 19 |
-
import type { Tab, TabSnapshot } from './tab';
|
| 20 |
-
import type { ImageContent, TextContent } from '@modelcontextprotocol/sdk/types.js';
|
| 21 |
-
import type { Context } from './context';
|
| 22 |
-
|
| 23 |
-
export class Response {
|
| 24 |
-
private _result: string[] = [];
|
| 25 |
-
private _code: string[] = [];
|
| 26 |
-
private _images: { contentType: string, data: Buffer }[] = [];
|
| 27 |
-
private _context: Context;
|
| 28 |
-
private _includeSnapshot = false;
|
| 29 |
-
private _includeTabs = false;
|
| 30 |
-
private _tabSnapshot: TabSnapshot | undefined;
|
| 31 |
-
|
| 32 |
-
readonly toolName: string;
|
| 33 |
-
readonly toolArgs: Record<string, any>;
|
| 34 |
-
private _isError: boolean | undefined;
|
| 35 |
-
|
| 36 |
-
constructor(context: Context, toolName: string, toolArgs: Record<string, any>) {
|
| 37 |
-
this._context = context;
|
| 38 |
-
this.toolName = toolName;
|
| 39 |
-
this.toolArgs = toolArgs;
|
| 40 |
-
}
|
| 41 |
-
|
| 42 |
-
addResult(result: string) {
|
| 43 |
-
this._result.push(result);
|
| 44 |
-
}
|
| 45 |
-
|
| 46 |
-
addError(error: string) {
|
| 47 |
-
this._result.push(error);
|
| 48 |
-
this._isError = true;
|
| 49 |
-
}
|
| 50 |
-
|
| 51 |
-
isError() {
|
| 52 |
-
return this._isError;
|
| 53 |
-
}
|
| 54 |
-
|
| 55 |
-
result() {
|
| 56 |
-
return this._result.join('\n');
|
| 57 |
-
}
|
| 58 |
-
|
| 59 |
-
addCode(code: string) {
|
| 60 |
-
this._code.push(code);
|
| 61 |
-
}
|
| 62 |
-
|
| 63 |
-
code() {
|
| 64 |
-
return this._code.join('\n');
|
| 65 |
-
}
|
| 66 |
-
|
| 67 |
-
addImage(image: { contentType: string, data: Buffer }) {
|
| 68 |
-
this._images.push(image);
|
| 69 |
-
}
|
| 70 |
-
|
| 71 |
-
images() {
|
| 72 |
-
return this._images;
|
| 73 |
-
}
|
| 74 |
-
|
| 75 |
-
setIncludeSnapshot() {
|
| 76 |
-
this._includeSnapshot = true;
|
| 77 |
-
}
|
| 78 |
-
|
| 79 |
-
setIncludeTabs() {
|
| 80 |
-
this._includeTabs = true;
|
| 81 |
-
}
|
| 82 |
-
|
| 83 |
-
async finish() {
|
| 84 |
-
// All the async snapshotting post-action is happening here.
|
| 85 |
-
// Everything below should race against modal states.
|
| 86 |
-
if (this._includeSnapshot && this._context.currentTab())
|
| 87 |
-
this._tabSnapshot = await this._context.currentTabOrDie().captureSnapshot();
|
| 88 |
-
for (const tab of this._context.tabs())
|
| 89 |
-
await tab.updateTitle();
|
| 90 |
-
}
|
| 91 |
-
|
| 92 |
-
tabSnapshot(): TabSnapshot | undefined {
|
| 93 |
-
return this._tabSnapshot;
|
| 94 |
-
}
|
| 95 |
-
|
| 96 |
-
serialize(): { content: (TextContent | ImageContent)[], isError?: boolean } {
|
| 97 |
-
const response: string[] = [];
|
| 98 |
-
|
| 99 |
-
// Start with command result.
|
| 100 |
-
if (this._result.length) {
|
| 101 |
-
response.push('### Result');
|
| 102 |
-
response.push(this._result.join('\n'));
|
| 103 |
-
response.push('');
|
| 104 |
-
}
|
| 105 |
-
|
| 106 |
-
// Add code if it exists.
|
| 107 |
-
if (this._code.length) {
|
| 108 |
-
response.push(`### Ran Playwright code
|
| 109 |
-
\`\`\`js
|
| 110 |
-
${this._code.join('\n')}
|
| 111 |
-
\`\`\``);
|
| 112 |
-
response.push('');
|
| 113 |
-
}
|
| 114 |
-
|
| 115 |
-
// List browser tabs.
|
| 116 |
-
if (this._includeSnapshot || this._includeTabs)
|
| 117 |
-
response.push(...renderTabsMarkdown(this._context.tabs(), this._includeTabs));
|
| 118 |
-
|
| 119 |
-
// Add snapshot if provided.
|
| 120 |
-
if (this._tabSnapshot?.modalStates.length) {
|
| 121 |
-
response.push(...renderModalStates(this._context, this._tabSnapshot.modalStates));
|
| 122 |
-
response.push('');
|
| 123 |
-
} else if (this._tabSnapshot) {
|
| 124 |
-
response.push(renderTabSnapshot(this._tabSnapshot));
|
| 125 |
-
response.push('');
|
| 126 |
-
}
|
| 127 |
-
|
| 128 |
-
// Main response part
|
| 129 |
-
const content: (TextContent | ImageContent)[] = [
|
| 130 |
-
{ type: 'text', text: response.join('\n') },
|
| 131 |
-
];
|
| 132 |
-
|
| 133 |
-
// Image attachments.
|
| 134 |
-
if (this._context.config.imageResponses !== 'omit') {
|
| 135 |
-
for (const image of this._images)
|
| 136 |
-
content.push({ type: 'image', data: image.data.toString('base64'), mimeType: image.contentType });
|
| 137 |
-
}
|
| 138 |
-
|
| 139 |
-
return { content, isError: this._isError };
|
| 140 |
-
}
|
| 141 |
-
}
|
| 142 |
-
|
| 143 |
-
function renderTabSnapshot(tabSnapshot: TabSnapshot): string {
|
| 144 |
-
const lines: string[] = [];
|
| 145 |
-
|
| 146 |
-
if (tabSnapshot.consoleMessages.length) {
|
| 147 |
-
lines.push(`### New console messages`);
|
| 148 |
-
for (const message of tabSnapshot.consoleMessages)
|
| 149 |
-
lines.push(`- ${trim(message.toString(), 100)}`);
|
| 150 |
-
lines.push('');
|
| 151 |
-
}
|
| 152 |
-
|
| 153 |
-
if (tabSnapshot.downloads.length) {
|
| 154 |
-
lines.push(`### Downloads`);
|
| 155 |
-
for (const entry of tabSnapshot.downloads) {
|
| 156 |
-
if (entry.finished)
|
| 157 |
-
lines.push(`- Downloaded file ${entry.download.suggestedFilename()} to ${entry.outputFile}`);
|
| 158 |
-
else
|
| 159 |
-
lines.push(`- Downloading file ${entry.download.suggestedFilename()} ...`);
|
| 160 |
-
}
|
| 161 |
-
lines.push('');
|
| 162 |
-
}
|
| 163 |
-
|
| 164 |
-
lines.push(`### Page state`);
|
| 165 |
-
lines.push(`- Page URL: ${tabSnapshot.url}`);
|
| 166 |
-
lines.push(`- Page Title: ${tabSnapshot.title}`);
|
| 167 |
-
lines.push(`- Page Snapshot:`);
|
| 168 |
-
lines.push('```yaml');
|
| 169 |
-
lines.push(tabSnapshot.ariaSnapshot);
|
| 170 |
-
lines.push('```');
|
| 171 |
-
|
| 172 |
-
return lines.join('\n');
|
| 173 |
-
}
|
| 174 |
-
|
| 175 |
-
function renderTabsMarkdown(tabs: Tab[], force: boolean = false): string[] {
|
| 176 |
-
if (tabs.length === 1 && !force)
|
| 177 |
-
return [];
|
| 178 |
-
|
| 179 |
-
if (!tabs.length) {
|
| 180 |
-
return [
|
| 181 |
-
'### Open tabs',
|
| 182 |
-
'No open tabs. Use the "browser_navigate" tool to navigate to a page first.',
|
| 183 |
-
'',
|
| 184 |
-
];
|
| 185 |
-
}
|
| 186 |
-
|
| 187 |
-
const lines: string[] = ['### Open tabs'];
|
| 188 |
-
for (let i = 0; i < tabs.length; i++) {
|
| 189 |
-
const tab = tabs[i];
|
| 190 |
-
const current = tab.isCurrentTab() ? ' (current)' : '';
|
| 191 |
-
lines.push(`- ${i}:${current} [${tab.lastTitle()}] (${tab.page.url()})`);
|
| 192 |
-
}
|
| 193 |
-
lines.push('');
|
| 194 |
-
return lines;
|
| 195 |
-
}
|
| 196 |
-
|
| 197 |
-
function trim(text: string, maxLength: number) {
|
| 198 |
-
if (text.length <= maxLength)
|
| 199 |
-
return text;
|
| 200 |
-
return text.slice(0, maxLength) + '...';
|
| 201 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/browser/sessionLog.ts
DELETED
|
@@ -1,176 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
import fs from 'fs';
|
| 18 |
-
import path from 'path';
|
| 19 |
-
|
| 20 |
-
import { Response } from './response';
|
| 21 |
-
import { logUnhandledError } from '../log';
|
| 22 |
-
import { outputFile } from './config';
|
| 23 |
-
|
| 24 |
-
import type { FullConfig } from './config';
|
| 25 |
-
import type * as actions from './actions';
|
| 26 |
-
import type { Tab, TabSnapshot } from './tab';
|
| 27 |
-
|
| 28 |
-
type LogEntry = {
|
| 29 |
-
timestamp: number;
|
| 30 |
-
toolCall?: {
|
| 31 |
-
toolName: string;
|
| 32 |
-
toolArgs: Record<string, any>;
|
| 33 |
-
result: string;
|
| 34 |
-
isError?: boolean;
|
| 35 |
-
};
|
| 36 |
-
userAction?: actions.Action;
|
| 37 |
-
code: string;
|
| 38 |
-
tabSnapshot?: TabSnapshot;
|
| 39 |
-
};
|
| 40 |
-
|
| 41 |
-
export class SessionLog {
|
| 42 |
-
private _folder: string;
|
| 43 |
-
private _file: string;
|
| 44 |
-
private _ordinal = 0;
|
| 45 |
-
private _pendingEntries: LogEntry[] = [];
|
| 46 |
-
private _sessionFileQueue = Promise.resolve();
|
| 47 |
-
private _flushEntriesTimeout: NodeJS.Timeout | undefined;
|
| 48 |
-
|
| 49 |
-
constructor(sessionFolder: string) {
|
| 50 |
-
this._folder = sessionFolder;
|
| 51 |
-
this._file = path.join(this._folder, 'session.md');
|
| 52 |
-
}
|
| 53 |
-
|
| 54 |
-
static async create(config: FullConfig, rootPath: string | undefined): Promise<SessionLog> {
|
| 55 |
-
const sessionFolder = await outputFile(config, rootPath, `session-${Date.now()}`);
|
| 56 |
-
await fs.promises.mkdir(sessionFolder, { recursive: true });
|
| 57 |
-
// eslint-disable-next-line no-console
|
| 58 |
-
console.error(`Session: ${sessionFolder}`);
|
| 59 |
-
return new SessionLog(sessionFolder);
|
| 60 |
-
}
|
| 61 |
-
|
| 62 |
-
logResponse(response: Response) {
|
| 63 |
-
const entry: LogEntry = {
|
| 64 |
-
timestamp: performance.now(),
|
| 65 |
-
toolCall: {
|
| 66 |
-
toolName: response.toolName,
|
| 67 |
-
toolArgs: response.toolArgs,
|
| 68 |
-
result: response.result(),
|
| 69 |
-
isError: response.isError(),
|
| 70 |
-
},
|
| 71 |
-
code: response.code(),
|
| 72 |
-
tabSnapshot: response.tabSnapshot(),
|
| 73 |
-
};
|
| 74 |
-
this._appendEntry(entry);
|
| 75 |
-
}
|
| 76 |
-
|
| 77 |
-
logUserAction(action: actions.Action, tab: Tab, code: string, isUpdate: boolean) {
|
| 78 |
-
code = code.trim();
|
| 79 |
-
if (isUpdate) {
|
| 80 |
-
const lastEntry = this._pendingEntries[this._pendingEntries.length - 1];
|
| 81 |
-
if (lastEntry.userAction?.name === action.name) {
|
| 82 |
-
lastEntry.userAction = action;
|
| 83 |
-
lastEntry.code = code;
|
| 84 |
-
return;
|
| 85 |
-
}
|
| 86 |
-
}
|
| 87 |
-
if (action.name === 'navigate') {
|
| 88 |
-
// Already logged at this location.
|
| 89 |
-
const lastEntry = this._pendingEntries[this._pendingEntries.length - 1];
|
| 90 |
-
if (lastEntry?.tabSnapshot?.url === action.url)
|
| 91 |
-
return;
|
| 92 |
-
}
|
| 93 |
-
const entry: LogEntry = {
|
| 94 |
-
timestamp: performance.now(),
|
| 95 |
-
userAction: action,
|
| 96 |
-
code,
|
| 97 |
-
tabSnapshot: {
|
| 98 |
-
url: tab.page.url(),
|
| 99 |
-
title: '',
|
| 100 |
-
ariaSnapshot: action.ariaSnapshot || '',
|
| 101 |
-
modalStates: [],
|
| 102 |
-
consoleMessages: [],
|
| 103 |
-
downloads: [],
|
| 104 |
-
},
|
| 105 |
-
};
|
| 106 |
-
this._appendEntry(entry);
|
| 107 |
-
}
|
| 108 |
-
|
| 109 |
-
private _appendEntry(entry: LogEntry) {
|
| 110 |
-
this._pendingEntries.push(entry);
|
| 111 |
-
if (this._flushEntriesTimeout)
|
| 112 |
-
clearTimeout(this._flushEntriesTimeout);
|
| 113 |
-
this._flushEntriesTimeout = setTimeout(() => this._flushEntries(), 1000);
|
| 114 |
-
}
|
| 115 |
-
|
| 116 |
-
private async _flushEntries() {
|
| 117 |
-
clearTimeout(this._flushEntriesTimeout);
|
| 118 |
-
const entries = this._pendingEntries;
|
| 119 |
-
this._pendingEntries = [];
|
| 120 |
-
const lines: string[] = [''];
|
| 121 |
-
|
| 122 |
-
for (const entry of entries) {
|
| 123 |
-
const ordinal = (++this._ordinal).toString().padStart(3, '0');
|
| 124 |
-
if (entry.toolCall) {
|
| 125 |
-
lines.push(
|
| 126 |
-
`### Tool call: ${entry.toolCall.toolName}`,
|
| 127 |
-
`- Args`,
|
| 128 |
-
'```json',
|
| 129 |
-
JSON.stringify(entry.toolCall.toolArgs, null, 2),
|
| 130 |
-
'```',
|
| 131 |
-
);
|
| 132 |
-
if (entry.toolCall.result) {
|
| 133 |
-
lines.push(
|
| 134 |
-
entry.toolCall.isError ? `- Error` : `- Result`,
|
| 135 |
-
'```',
|
| 136 |
-
entry.toolCall.result,
|
| 137 |
-
'```',
|
| 138 |
-
);
|
| 139 |
-
}
|
| 140 |
-
}
|
| 141 |
-
|
| 142 |
-
if (entry.userAction) {
|
| 143 |
-
const actionData = { ...entry.userAction } as any;
|
| 144 |
-
delete actionData.ariaSnapshot;
|
| 145 |
-
delete actionData.selector;
|
| 146 |
-
delete actionData.signals;
|
| 147 |
-
|
| 148 |
-
lines.push(
|
| 149 |
-
`### User action: ${entry.userAction.name}`,
|
| 150 |
-
`- Args`,
|
| 151 |
-
'```json',
|
| 152 |
-
JSON.stringify(actionData, null, 2),
|
| 153 |
-
'```',
|
| 154 |
-
);
|
| 155 |
-
}
|
| 156 |
-
|
| 157 |
-
if (entry.code) {
|
| 158 |
-
lines.push(
|
| 159 |
-
`- Code`,
|
| 160 |
-
'```js',
|
| 161 |
-
entry.code,
|
| 162 |
-
'```');
|
| 163 |
-
}
|
| 164 |
-
|
| 165 |
-
if (entry.tabSnapshot) {
|
| 166 |
-
const fileName = `${ordinal}.snapshot.yml`;
|
| 167 |
-
fs.promises.writeFile(path.join(this._folder, fileName), entry.tabSnapshot.ariaSnapshot).catch(logUnhandledError);
|
| 168 |
-
lines.push(`- Snapshot: ${fileName}`);
|
| 169 |
-
}
|
| 170 |
-
|
| 171 |
-
lines.push('', '');
|
| 172 |
-
}
|
| 173 |
-
|
| 174 |
-
this._sessionFileQueue = this._sessionFileQueue.then(() => fs.promises.appendFile(this._file, lines.join('\n')));
|
| 175 |
-
}
|
| 176 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/browser/tab.ts
DELETED
|
@@ -1,313 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
import { EventEmitter } from 'events';
|
| 18 |
-
import * as playwright from 'playwright';
|
| 19 |
-
import { callOnPageNoTrace, waitForCompletion } from './tools/utils';
|
| 20 |
-
import { logUnhandledError } from '../log';
|
| 21 |
-
import { ManualPromise } from '../sdk/manualPromise';
|
| 22 |
-
import { ModalState } from './tools/tool';
|
| 23 |
-
|
| 24 |
-
import type { Context } from './context';
|
| 25 |
-
|
| 26 |
-
type PageEx = playwright.Page & {
|
| 27 |
-
_snapshotForAI: () => Promise<string>;
|
| 28 |
-
};
|
| 29 |
-
|
| 30 |
-
export const TabEvents = {
|
| 31 |
-
modalState: 'modalState'
|
| 32 |
-
};
|
| 33 |
-
|
| 34 |
-
export type TabEventsInterface = {
|
| 35 |
-
[TabEvents.modalState]: [modalState: ModalState];
|
| 36 |
-
};
|
| 37 |
-
|
| 38 |
-
export type TabSnapshot = {
|
| 39 |
-
url: string;
|
| 40 |
-
title: string;
|
| 41 |
-
ariaSnapshot: string;
|
| 42 |
-
modalStates: ModalState[];
|
| 43 |
-
consoleMessages: ConsoleMessage[];
|
| 44 |
-
downloads: { download: playwright.Download, finished: boolean, outputFile: string }[];
|
| 45 |
-
};
|
| 46 |
-
|
| 47 |
-
export class Tab extends EventEmitter<TabEventsInterface> {
|
| 48 |
-
readonly context: Context;
|
| 49 |
-
readonly page: playwright.Page;
|
| 50 |
-
private _lastTitle = 'about:blank';
|
| 51 |
-
private _consoleMessages: ConsoleMessage[] = [];
|
| 52 |
-
private _recentConsoleMessages: ConsoleMessage[] = [];
|
| 53 |
-
private _requests: Map<playwright.Request, playwright.Response | null> = new Map();
|
| 54 |
-
private _onPageClose: (tab: Tab) => void;
|
| 55 |
-
private _modalStates: ModalState[] = [];
|
| 56 |
-
private _downloads: { download: playwright.Download, finished: boolean, outputFile: string }[] = [];
|
| 57 |
-
|
| 58 |
-
constructor(context: Context, page: playwright.Page, onPageClose: (tab: Tab) => void) {
|
| 59 |
-
super();
|
| 60 |
-
this.context = context;
|
| 61 |
-
this.page = page;
|
| 62 |
-
this._onPageClose = onPageClose;
|
| 63 |
-
page.on('console', event => this._handleConsoleMessage(messageToConsoleMessage(event)));
|
| 64 |
-
page.on('pageerror', error => this._handleConsoleMessage(pageErrorToConsoleMessage(error)));
|
| 65 |
-
page.on('request', request => this._requests.set(request, null));
|
| 66 |
-
page.on('response', response => this._requests.set(response.request(), response));
|
| 67 |
-
page.on('close', () => this._onClose());
|
| 68 |
-
page.on('filechooser', chooser => {
|
| 69 |
-
this.setModalState({
|
| 70 |
-
type: 'fileChooser',
|
| 71 |
-
description: 'File chooser',
|
| 72 |
-
fileChooser: chooser,
|
| 73 |
-
});
|
| 74 |
-
});
|
| 75 |
-
page.on('dialog', dialog => this._dialogShown(dialog));
|
| 76 |
-
page.on('download', download => {
|
| 77 |
-
void this._downloadStarted(download);
|
| 78 |
-
});
|
| 79 |
-
page.setDefaultNavigationTimeout(60000);
|
| 80 |
-
page.setDefaultTimeout(5000);
|
| 81 |
-
(page as any)[tabSymbol] = this;
|
| 82 |
-
}
|
| 83 |
-
|
| 84 |
-
static forPage(page: playwright.Page): Tab | undefined {
|
| 85 |
-
return (page as any)[tabSymbol];
|
| 86 |
-
}
|
| 87 |
-
|
| 88 |
-
modalStates(): ModalState[] {
|
| 89 |
-
return this._modalStates;
|
| 90 |
-
}
|
| 91 |
-
|
| 92 |
-
setModalState(modalState: ModalState) {
|
| 93 |
-
this._modalStates.push(modalState);
|
| 94 |
-
this.emit(TabEvents.modalState, modalState);
|
| 95 |
-
}
|
| 96 |
-
|
| 97 |
-
clearModalState(modalState: ModalState) {
|
| 98 |
-
this._modalStates = this._modalStates.filter(state => state !== modalState);
|
| 99 |
-
}
|
| 100 |
-
|
| 101 |
-
modalStatesMarkdown(): string[] {
|
| 102 |
-
return renderModalStates(this.context, this.modalStates());
|
| 103 |
-
}
|
| 104 |
-
|
| 105 |
-
private _dialogShown(dialog: playwright.Dialog) {
|
| 106 |
-
this.setModalState({
|
| 107 |
-
type: 'dialog',
|
| 108 |
-
description: `"${dialog.type()}" dialog with message "${dialog.message()}"`,
|
| 109 |
-
dialog,
|
| 110 |
-
});
|
| 111 |
-
}
|
| 112 |
-
|
| 113 |
-
private async _downloadStarted(download: playwright.Download) {
|
| 114 |
-
const entry = {
|
| 115 |
-
download,
|
| 116 |
-
finished: false,
|
| 117 |
-
outputFile: await this.context.outputFile(download.suggestedFilename())
|
| 118 |
-
};
|
| 119 |
-
this._downloads.push(entry);
|
| 120 |
-
await download.saveAs(entry.outputFile);
|
| 121 |
-
entry.finished = true;
|
| 122 |
-
}
|
| 123 |
-
|
| 124 |
-
private _clearCollectedArtifacts() {
|
| 125 |
-
this._consoleMessages.length = 0;
|
| 126 |
-
this._recentConsoleMessages.length = 0;
|
| 127 |
-
this._requests.clear();
|
| 128 |
-
}
|
| 129 |
-
|
| 130 |
-
private _handleConsoleMessage(message: ConsoleMessage) {
|
| 131 |
-
this._consoleMessages.push(message);
|
| 132 |
-
this._recentConsoleMessages.push(message);
|
| 133 |
-
}
|
| 134 |
-
|
| 135 |
-
private _onClose() {
|
| 136 |
-
this._clearCollectedArtifacts();
|
| 137 |
-
this._onPageClose(this);
|
| 138 |
-
}
|
| 139 |
-
|
| 140 |
-
async updateTitle() {
|
| 141 |
-
await this._raceAgainstModalStates(async () => {
|
| 142 |
-
this._lastTitle = await callOnPageNoTrace(this.page, page => page.title());
|
| 143 |
-
});
|
| 144 |
-
}
|
| 145 |
-
|
| 146 |
-
lastTitle(): string {
|
| 147 |
-
return this._lastTitle;
|
| 148 |
-
}
|
| 149 |
-
|
| 150 |
-
isCurrentTab(): boolean {
|
| 151 |
-
return this === this.context.currentTab();
|
| 152 |
-
}
|
| 153 |
-
|
| 154 |
-
async waitForLoadState(state: 'load', options?: { timeout?: number }): Promise<void> {
|
| 155 |
-
await callOnPageNoTrace(this.page, page => page.waitForLoadState(state, options).catch(logUnhandledError));
|
| 156 |
-
}
|
| 157 |
-
|
| 158 |
-
async navigate(url: string) {
|
| 159 |
-
this._clearCollectedArtifacts();
|
| 160 |
-
|
| 161 |
-
const downloadEvent = callOnPageNoTrace(this.page, page => page.waitForEvent('download').catch(logUnhandledError));
|
| 162 |
-
try {
|
| 163 |
-
await this.page.goto(url, { waitUntil: 'domcontentloaded' });
|
| 164 |
-
} catch (_e: unknown) {
|
| 165 |
-
const e = _e as Error;
|
| 166 |
-
const mightBeDownload =
|
| 167 |
-
e.message.includes('net::ERR_ABORTED') // chromium
|
| 168 |
-
|| e.message.includes('Download is starting'); // firefox + webkit
|
| 169 |
-
if (!mightBeDownload)
|
| 170 |
-
throw e;
|
| 171 |
-
// on chromium, the download event is fired *after* page.goto rejects, so we wait a lil bit
|
| 172 |
-
const download = await Promise.race([
|
| 173 |
-
downloadEvent,
|
| 174 |
-
new Promise(resolve => setTimeout(resolve, 3000)),
|
| 175 |
-
]);
|
| 176 |
-
if (!download)
|
| 177 |
-
throw e;
|
| 178 |
-
// Make sure other "download" listeners are notified first.
|
| 179 |
-
await new Promise(resolve => setTimeout(resolve, 500));
|
| 180 |
-
return;
|
| 181 |
-
}
|
| 182 |
-
|
| 183 |
-
// Cap load event to 5 seconds, the page is operational at this point.
|
| 184 |
-
await this.waitForLoadState('load', { timeout: 5000 });
|
| 185 |
-
}
|
| 186 |
-
|
| 187 |
-
consoleMessages(): ConsoleMessage[] {
|
| 188 |
-
return this._consoleMessages;
|
| 189 |
-
}
|
| 190 |
-
|
| 191 |
-
requests(): Map<playwright.Request, playwright.Response | null> {
|
| 192 |
-
return this._requests;
|
| 193 |
-
}
|
| 194 |
-
|
| 195 |
-
async captureSnapshot(): Promise<TabSnapshot> {
|
| 196 |
-
let tabSnapshot: TabSnapshot | undefined;
|
| 197 |
-
const modalStates = await this._raceAgainstModalStates(async () => {
|
| 198 |
-
const snapshot = await (this.page as PageEx)._snapshotForAI();
|
| 199 |
-
tabSnapshot = {
|
| 200 |
-
url: this.page.url(),
|
| 201 |
-
title: await this.page.title(),
|
| 202 |
-
ariaSnapshot: snapshot,
|
| 203 |
-
modalStates: [],
|
| 204 |
-
consoleMessages: [],
|
| 205 |
-
downloads: this._downloads,
|
| 206 |
-
};
|
| 207 |
-
});
|
| 208 |
-
if (tabSnapshot) {
|
| 209 |
-
// Assign console message late so that we did not lose any to modal state.
|
| 210 |
-
tabSnapshot.consoleMessages = this._recentConsoleMessages;
|
| 211 |
-
this._recentConsoleMessages = [];
|
| 212 |
-
}
|
| 213 |
-
return tabSnapshot ?? {
|
| 214 |
-
url: this.page.url(),
|
| 215 |
-
title: '',
|
| 216 |
-
ariaSnapshot: '',
|
| 217 |
-
modalStates,
|
| 218 |
-
consoleMessages: [],
|
| 219 |
-
downloads: [],
|
| 220 |
-
};
|
| 221 |
-
}
|
| 222 |
-
|
| 223 |
-
private _javaScriptBlocked(): boolean {
|
| 224 |
-
return this._modalStates.some(state => state.type === 'dialog');
|
| 225 |
-
}
|
| 226 |
-
|
| 227 |
-
private async _raceAgainstModalStates(action: () => Promise<void>): Promise<ModalState[]> {
|
| 228 |
-
if (this.modalStates().length)
|
| 229 |
-
return this.modalStates();
|
| 230 |
-
|
| 231 |
-
const promise = new ManualPromise<ModalState[]>();
|
| 232 |
-
const listener = (modalState: ModalState) => promise.resolve([modalState]);
|
| 233 |
-
this.once(TabEvents.modalState, listener);
|
| 234 |
-
|
| 235 |
-
return await Promise.race([
|
| 236 |
-
action().then(() => {
|
| 237 |
-
this.off(TabEvents.modalState, listener);
|
| 238 |
-
return [];
|
| 239 |
-
}),
|
| 240 |
-
promise,
|
| 241 |
-
]);
|
| 242 |
-
}
|
| 243 |
-
|
| 244 |
-
async waitForCompletion(callback: () => Promise<void>) {
|
| 245 |
-
await this._raceAgainstModalStates(() => waitForCompletion(this, callback));
|
| 246 |
-
}
|
| 247 |
-
|
| 248 |
-
async refLocator(params: { element: string, ref: string }): Promise<playwright.Locator> {
|
| 249 |
-
return (await this.refLocators([params]))[0];
|
| 250 |
-
}
|
| 251 |
-
|
| 252 |
-
async refLocators(params: { element: string, ref: string }[]): Promise<playwright.Locator[]> {
|
| 253 |
-
const snapshot = await (this.page as PageEx)._snapshotForAI();
|
| 254 |
-
return params.map(param => {
|
| 255 |
-
if (!snapshot.includes(`[ref=${param.ref}]`))
|
| 256 |
-
throw new Error(`Ref ${param.ref} not found in the current page snapshot. Try capturing new snapshot.`);
|
| 257 |
-
return this.page.locator(`aria-ref=${param.ref}`).describe(param.element);
|
| 258 |
-
});
|
| 259 |
-
}
|
| 260 |
-
|
| 261 |
-
async waitForTimeout(time: number) {
|
| 262 |
-
if (this._javaScriptBlocked()) {
|
| 263 |
-
await new Promise(f => setTimeout(f, time));
|
| 264 |
-
return;
|
| 265 |
-
}
|
| 266 |
-
|
| 267 |
-
await callOnPageNoTrace(this.page, page => {
|
| 268 |
-
return page.evaluate(() => new Promise(f => setTimeout(f, 1000)));
|
| 269 |
-
});
|
| 270 |
-
}
|
| 271 |
-
}
|
| 272 |
-
|
| 273 |
-
export type ConsoleMessage = {
|
| 274 |
-
type: ReturnType<playwright.ConsoleMessage['type']> | undefined;
|
| 275 |
-
text: string;
|
| 276 |
-
toString(): string;
|
| 277 |
-
};
|
| 278 |
-
|
| 279 |
-
function messageToConsoleMessage(message: playwright.ConsoleMessage): ConsoleMessage {
|
| 280 |
-
return {
|
| 281 |
-
type: message.type(),
|
| 282 |
-
text: message.text(),
|
| 283 |
-
toString: () => `[${message.type().toUpperCase()}] ${message.text()} @ ${message.location().url}:${message.location().lineNumber}`,
|
| 284 |
-
};
|
| 285 |
-
}
|
| 286 |
-
|
| 287 |
-
function pageErrorToConsoleMessage(errorOrValue: Error | any): ConsoleMessage {
|
| 288 |
-
if (errorOrValue instanceof Error) {
|
| 289 |
-
return {
|
| 290 |
-
type: undefined,
|
| 291 |
-
text: errorOrValue.message,
|
| 292 |
-
toString: () => errorOrValue.stack || errorOrValue.message,
|
| 293 |
-
};
|
| 294 |
-
}
|
| 295 |
-
return {
|
| 296 |
-
type: undefined,
|
| 297 |
-
text: String(errorOrValue),
|
| 298 |
-
toString: () => String(errorOrValue),
|
| 299 |
-
};
|
| 300 |
-
}
|
| 301 |
-
|
| 302 |
-
export function renderModalStates(context: Context, modalStates: ModalState[]): string[] {
|
| 303 |
-
const result: string[] = ['### Modal state'];
|
| 304 |
-
if (modalStates.length === 0)
|
| 305 |
-
result.push('- There is no modal state present');
|
| 306 |
-
for (const state of modalStates) {
|
| 307 |
-
const tool = context.tools.filter(tool => 'clearsModalState' in tool).find(tool => tool.clearsModalState === state.type);
|
| 308 |
-
result.push(`- [${state.description}]: can be handled by the "${tool?.schema.name}" tool`);
|
| 309 |
-
}
|
| 310 |
-
return result;
|
| 311 |
-
}
|
| 312 |
-
|
| 313 |
-
const tabSymbol = Symbol('tabSymbol');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/browser/tools.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
import common from './tools/common';
|
| 18 |
-
import console from './tools/console';
|
| 19 |
-
import dialogs from './tools/dialogs';
|
| 20 |
-
import evaluate from './tools/evaluate';
|
| 21 |
-
import files from './tools/files';
|
| 22 |
-
import form from './tools/form';
|
| 23 |
-
import install from './tools/install';
|
| 24 |
-
import keyboard from './tools/keyboard';
|
| 25 |
-
import mouse from './tools/mouse';
|
| 26 |
-
import navigate from './tools/navigate';
|
| 27 |
-
import network from './tools/network';
|
| 28 |
-
import pdf from './tools/pdf';
|
| 29 |
-
import snapshot from './tools/snapshot';
|
| 30 |
-
import tabs from './tools/tabs';
|
| 31 |
-
import screenshot from './tools/screenshot';
|
| 32 |
-
import wait from './tools/wait';
|
| 33 |
-
import verify from './tools/verify';
|
| 34 |
-
|
| 35 |
-
import type { Tool } from './tools/tool';
|
| 36 |
-
import type { FullConfig } from './config';
|
| 37 |
-
|
| 38 |
-
export const allTools: Tool<any>[] = [
|
| 39 |
-
...common,
|
| 40 |
-
...console,
|
| 41 |
-
...dialogs,
|
| 42 |
-
...evaluate,
|
| 43 |
-
...files,
|
| 44 |
-
...form,
|
| 45 |
-
...install,
|
| 46 |
-
...keyboard,
|
| 47 |
-
...navigate,
|
| 48 |
-
...network,
|
| 49 |
-
...mouse,
|
| 50 |
-
...pdf,
|
| 51 |
-
...screenshot,
|
| 52 |
-
...snapshot,
|
| 53 |
-
...tabs,
|
| 54 |
-
...wait,
|
| 55 |
-
...verify,
|
| 56 |
-
];
|
| 57 |
-
|
| 58 |
-
export function filteredTools(config: FullConfig) {
|
| 59 |
-
return allTools.filter(tool => tool.capability.startsWith('core') || config.capabilities?.includes(tool.capability));
|
| 60 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/browser/tools/DEPS.list
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
[*]
|
| 2 |
-
../
|
| 3 |
-
../../sdk/
|
|
|
|
|
|
|
|
|
|
|
|
src/browser/tools/common.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
import { z } from '../../sdk/bundle';
|
| 18 |
-
import { defineTabTool, defineTool } from './tool';
|
| 19 |
-
|
| 20 |
-
const close = defineTool({
|
| 21 |
-
capability: 'core',
|
| 22 |
-
|
| 23 |
-
schema: {
|
| 24 |
-
name: 'browser_close',
|
| 25 |
-
title: 'Close browser',
|
| 26 |
-
description: 'Close the page',
|
| 27 |
-
inputSchema: z.object({}),
|
| 28 |
-
type: 'readOnly',
|
| 29 |
-
},
|
| 30 |
-
|
| 31 |
-
handle: async (context, params, response) => {
|
| 32 |
-
await context.closeBrowserContext();
|
| 33 |
-
response.setIncludeTabs();
|
| 34 |
-
response.addCode(`await page.close()`);
|
| 35 |
-
},
|
| 36 |
-
});
|
| 37 |
-
|
| 38 |
-
const resize = defineTabTool({
|
| 39 |
-
capability: 'core',
|
| 40 |
-
schema: {
|
| 41 |
-
name: 'browser_resize',
|
| 42 |
-
title: 'Resize browser window',
|
| 43 |
-
description: 'Resize the browser window',
|
| 44 |
-
inputSchema: z.object({
|
| 45 |
-
width: z.number().describe('Width of the browser window'),
|
| 46 |
-
height: z.number().describe('Height of the browser window'),
|
| 47 |
-
}),
|
| 48 |
-
type: 'readOnly',
|
| 49 |
-
},
|
| 50 |
-
|
| 51 |
-
handle: async (tab, params, response) => {
|
| 52 |
-
response.addCode(`await page.setViewportSize({ width: ${params.width}, height: ${params.height} });`);
|
| 53 |
-
|
| 54 |
-
await tab.waitForCompletion(async () => {
|
| 55 |
-
await tab.page.setViewportSize({ width: params.width, height: params.height });
|
| 56 |
-
});
|
| 57 |
-
},
|
| 58 |
-
});
|
| 59 |
-
|
| 60 |
-
export default [
|
| 61 |
-
close,
|
| 62 |
-
resize
|
| 63 |
-
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/browser/tools/console.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
import { z } from '../../sdk/bundle';
|
| 18 |
-
import { defineTabTool } from './tool';
|
| 19 |
-
|
| 20 |
-
const console = defineTabTool({
|
| 21 |
-
capability: 'core',
|
| 22 |
-
schema: {
|
| 23 |
-
name: 'browser_console_messages',
|
| 24 |
-
title: 'Get console messages',
|
| 25 |
-
description: 'Returns all console messages',
|
| 26 |
-
inputSchema: z.object({}),
|
| 27 |
-
type: 'readOnly',
|
| 28 |
-
},
|
| 29 |
-
handle: async (tab, params, response) => {
|
| 30 |
-
tab.consoleMessages().map(message => response.addResult(message.toString()));
|
| 31 |
-
},
|
| 32 |
-
});
|
| 33 |
-
|
| 34 |
-
export default [
|
| 35 |
-
console,
|
| 36 |
-
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/browser/tools/dialogs.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Copyright (c) Microsoft Corporation.
|
| 3 |
-
*
|
| 4 |
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
* you may not use this file except in compliance with the License.
|
| 6 |
-
* You may obtain a copy of the License at
|
| 7 |
-
*
|
| 8 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
*
|
| 10 |
-
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
* See the License for the specific language governing permissions and
|
| 14 |
-
* limitations under the License.
|
| 15 |
-
*/
|
| 16 |
-
|
| 17 |
-
import { z } from '../../sdk/bundle';
|
| 18 |
-
import { defineTabTool } from './tool';
|
| 19 |
-
|
| 20 |
-
const handleDialog = defineTabTool({
|
| 21 |
-
capability: 'core',
|
| 22 |
-
|
| 23 |
-
schema: {
|
| 24 |
-
name: 'browser_handle_dialog',
|
| 25 |
-
title: 'Handle a dialog',
|
| 26 |
-
description: 'Handle a dialog',
|
| 27 |
-
inputSchema: z.object({
|
| 28 |
-
accept: z.boolean().describe('Whether to accept the dialog.'),
|
| 29 |
-
promptText: z.string().optional().describe('The text of the prompt in case of a prompt dialog.'),
|
| 30 |
-
}),
|
| 31 |
-
type: 'destructive',
|
| 32 |
-
},
|
| 33 |
-
|
| 34 |
-
handle: async (tab, params, response) => {
|
| 35 |
-
response.setIncludeSnapshot();
|
| 36 |
-
|
| 37 |
-
const dialogState = tab.modalStates().find(state => state.type === 'dialog');
|
| 38 |
-
if (!dialogState)
|
| 39 |
-
throw new Error('No dialog visible');
|
| 40 |
-
|
| 41 |
-
tab.clearModalState(dialogState);
|
| 42 |
-
await tab.waitForCompletion(async () => {
|
| 43 |
-
if (params.accept)
|
| 44 |
-
await dialogState.dialog.accept(params.promptText);
|
| 45 |
-
else
|
| 46 |
-
await dialogState.dialog.dismiss();
|
| 47 |
-
});
|
| 48 |
-
},
|
| 49 |
-
|
| 50 |
-
clearsModalState: 'dialog',
|
| 51 |
-
});
|
| 52 |
-
|
| 53 |
-
export default [
|
| 54 |
-
handleDialog,
|
| 55 |
-
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|