diff --git a/.github/plugin/plugin.json b/.github/plugin/plugin.json deleted file mode 100644 index 51464ff4a..000000000 --- a/.github/plugin/plugin.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "chrome-devtools-mcp", - "version": "0.20.3", - "description": "Reliable automation, in-depth debugging, and performance analysis in Chrome using Chrome DevTools and Puppeteer", - "mcpServers": { - "chrome-devtools": { - "command": "npx", - "args": ["chrome-devtools-mcp@latest"] - } - } -} diff --git a/.npmrc b/.npmrc deleted file mode 100644 index cafe685a1..000000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -package-lock=true diff --git a/.prettierignore b/.prettierignore index c357cfaf0..0a39840d3 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,6 +1,3 @@ # Prettier-only ignores. CHANGELOG.md -src/third_party/lighthouse-devtools-mcp-bundle.js - -# Release-please formatting brakes CI checks -.claude-plugin/plugin.json +src/third_party/lighthouse-devtools-mcp-bundle.js \ No newline at end of file diff --git a/README.md b/README.md index 577cf7bb1..ed6167d30 100644 --- a/README.md +++ b/README.md @@ -51,12 +51,7 @@ Google handles this data in accordance with the [Google Privacy Policy](https:// Google's collection of usage statistics for Chrome DevTools MCP is independent from the Chrome browser's usage statistics. Opting out of Chrome metrics does not automatically opt you out of this tool, and vice-versa. -Collection is disabled if `CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS` or `CI` env variables are set. - -## Update checks - -By default, the server periodically checks the npm registry for updates and logs a notification when a newer version is available. -You can disable these update checks by setting the `CHROME_DEVTOOLS_MCP_NO_UPDATE_CHECKS` environment variable. +Collection is disabled if CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS or CI env variables are set. ## Requirements @@ -240,22 +235,6 @@ Configure the following fields and press `CTRL+S` to save the configuration:
Copilot / VS Code -**Install as a Plugin (Recommended)** - -The easiest way to get up and running is to install `chrome-devtools-mcp` as an agent plugin. -This bundles the **MCP server** and all **skills** together, so your agent gets both the tools -and the expert guidance it needs to use them effectively. - -1. Open the **Command Palette** (`Cmd+Shift+P` on macOS or `Ctrl+Shift+P` on Windows/Linux). -2. Search for and run the **Chat: Install Plugin From Source** command. -3. Paste in our repository URL: `https://github.com/ChromeDevTools/chrome-devtools-mcp` - -That's it! Your agent is now supercharged with Chrome DevTools capabilities. - ---- - -**Install as an MCP Server (MCP only)** - **Click the button to install:** [Install in VS Code](https://vscode.dev/redirect/mcp/install?name=io.github.ChromeDevTools%2Fchrome-devtools-mcp&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22chrome-devtools-mcp%22%5D%2C%22env%22%3A%7B%7D%7D) @@ -264,7 +243,8 @@ That's it! Your agent is now supercharged with Chrome DevTools capabilities. **Or install manually:** -Follow the VS Code [MCP configuration guide](https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server) using the standard config from above, or use the CLI: +Follow the MCP install guide, +with the standard config from above. You can also install the Chrome DevTools MCP server using the VS Code CLI: For macOS and Linux: @@ -560,10 +540,6 @@ The Chrome DevTools MCP server supports the following configuration option: If enabled, ignores errors relative to self-signed and expired certificates. Use with caution. - **Type:** boolean -- **`--experimentalVision`/ `--experimental-vision`** - Whether to enable coordinate-based tools such as click_at(x,y). Usually requires a computer-use model able to produce accurate coordinates by looking at screenshots. - - **Type:** boolean - - **`--experimentalScreencast`/ `--experimental-screencast`** Exposes experimental screencast tools (requires ffmpeg). Install ffmpeg https://www.ffmpeg.org/download.html and ensure it is available in the MCP server PATH. - **Type:** boolean @@ -597,7 +573,7 @@ The Chrome DevTools MCP server supports the following configuration option: - **Default:** `true` - **`--usageStatistics`/ `--usage-statistics`** - Set to false to opt-out of usage statistics collection. Google collects usage data to improve the tool, handled under the Google Privacy Policy (https://policies.google.com/privacy). This is independent from Chrome browser metrics. Disabled if `CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS` or `CI` env variables are set. + Set to false to opt-out of usage statistics collection. Google collects usage data to improve the tool, handled under the Google Privacy Policy (https://policies.google.com/privacy). This is independent from Chrome browser metrics. Disabled if CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS or CI env variables are set. - **Type:** boolean - **Default:** `true` diff --git a/package-lock.json b/package-lock.json index 22e629cb8..e3e6fd45f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,12 +37,12 @@ "lighthouse": "13.0.3", "prettier": "^3.6.2", "puppeteer": "24.40.0", - "rollup": "4.60.1", + "rollup": "4.59.1", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", "sinon": "^21.0.0", "tiktoken": "^1.0.22", - "typescript": "^6.0.2", + "typescript": "^5.9.2", "typescript-eslint": "^8.43.0", "yargs": "18.0.0" }, @@ -381,9 +381,9 @@ } }, "node_modules/@google/genai": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.48.0.tgz", - "integrity": "sha512-plonYK4ML2PrxsRD9SeqmFt76eREWkQdPCglOA6aYDzL1AAbE+7PUnT54SvpWGfws13L0AZEqGSpL7+1IPnTxQ==", + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.46.0.tgz", + "integrity": "sha512-ewPMN5JkKfgU5/kdco9ZhXBHDPhVqZpMQqIFQhwsHLf8kyZfx1cNpw1pHo1eV6PGEW7EhIBFi3aYZraFndAXqg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1536,9 +1536,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", - "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.1.tgz", + "integrity": "sha512-xB0b51TB7IfDEzAojXahmr+gfA00uYVInJGgNNkeQG6RPnCPGr7udsylFLTubuIUSRE6FkcI1NElyRt83PP5oQ==", "cpu": [ "arm" ], @@ -1550,9 +1550,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", - "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.1.tgz", + "integrity": "sha512-XOjPId0qwSDKHaIsdzHJtKCxX0+nH8MhBwvrNsT7tVyKmdTx1jJ4XzN5RZXCdTzMpufLb+B8llTC0D8uCrLhcw==", "cpu": [ "arm64" ], @@ -1564,9 +1564,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", - "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.1.tgz", + "integrity": "sha512-vQuRd28p0gQpPrS6kppd8IrWmFo42U8Pz1XLRjSZXq5zCqyMDYFABT7/sywL11mO1EL10Qhh7MVPEwkG8GiBeg==", "cpu": [ "arm64" ], @@ -1578,9 +1578,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", - "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.1.tgz", + "integrity": "sha512-x6VG6U29+Ivlnajrg1IHdzXeAwSoEHBFVO+CtC9Brugx6de712CUJobRUxsIA0KYrQvCmzNrMPFTT1A4CCqNTg==", "cpu": [ "x64" ], @@ -1592,9 +1592,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", - "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.1.tgz", + "integrity": "sha512-Sgi0Uo6t1YCHJMNO3Y8+bm+SvOanUGkoZKn/VJPwYUe2kp31X5KnXmzKd/NjW8iA3gFcfNZ64zh14uOGrIllCQ==", "cpu": [ "arm64" ], @@ -1606,9 +1606,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", - "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.1.tgz", + "integrity": "sha512-AM4xnwEZwukdhk7laMWfzWu9JGSVnJd+Fowt6Fd7QW1nrf3h0Hp7Qx5881M4aqrUlKBCybOxz0jofvIIfl7C5g==", "cpu": [ "x64" ], @@ -1620,9 +1620,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", - "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.1.tgz", + "integrity": "sha512-KUizqxpwaR2AZdAUsMWfL/C94pUu7TKpoPd88c8yFVixJ+l9hejkrwoK5Zj3wiNh65UeyryKnJyxL1b7yNqFQA==", "cpu": [ "arm" ], @@ -1634,9 +1634,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", - "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.1.tgz", + "integrity": "sha512-MZoQ/am77ckJtZGFAtPucgUuJWiop3m2R3lw7tC0QCcbfl4DRhQUBUkHWCkcrT3pqy5Mzv5QQgY6Dmlba6iTWg==", "cpu": [ "arm" ], @@ -1648,9 +1648,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", - "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.1.tgz", + "integrity": "sha512-Sez95TP6xGjkWB1608EfhCX1gdGrO5wzyN99VqzRtC17x/1bhw5VU1V0GfKUwbW/Xr1J8mSasoFoJa6Y7aGGSA==", "cpu": [ "arm64" ], @@ -1662,9 +1662,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", - "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.1.tgz", + "integrity": "sha512-9Cs2Seq98LWNOJzR89EGTZoiP8EkZ9UbQhBlDgfAkM6asVna1xJ04W2CLYWDN/RpUgOjtQvcv8wQVi1t5oQazA==", "cpu": [ "arm64" ], @@ -1676,9 +1676,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", - "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.1.tgz", + "integrity": "sha512-n9yqttftgFy7IrNEnHy1bOp6B4OSe8mJDiPkT7EqlM9FnKOwUMnCK62ixW0Kd9Clw0/wgvh8+SqaDXMFvw3KqQ==", "cpu": [ "loong64" ], @@ -1690,9 +1690,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", - "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.1.tgz", + "integrity": "sha512-SfpNXDzVTqs/riak4xXcLpq5gIQWsqGWMhN1AGRQKB4qGSs4r0sEs3ervXPcE1O9RsQ5bm8Muz6zmQpQnPss1g==", "cpu": [ "loong64" ], @@ -1704,9 +1704,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", - "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.1.tgz", + "integrity": "sha512-LjaChED0wQnjKZU+tsmGbN+9nN1XhaWUkAlSbTdhpEseCS4a15f/Q8xC2BN4GDKRzhhLZpYtJBZr2NZhR0jvNw==", "cpu": [ "ppc64" ], @@ -1718,9 +1718,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", - "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.1.tgz", + "integrity": "sha512-ojW7iTJSIs4pwB2xV6QXGwNyDctvXOivYllttuPbXguuKDX5vwpqYJsHc6D2LZzjDGHML414Tuj3LvVPe1CT1A==", "cpu": [ "ppc64" ], @@ -1732,9 +1732,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", - "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.1.tgz", + "integrity": "sha512-FP+Q6WTcxxvsr0wQczhSE+tOZvFPV8A/mUE6mhZYFW9/eea/y/XqAgRoLLMuE9Cz0hfX5bi7p116IWoB+P237A==", "cpu": [ "riscv64" ], @@ -1746,9 +1746,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", - "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.1.tgz", + "integrity": "sha512-L1uD9b/Ig8Z+rn1KttCJjwhN1FgjRMBKsPaBsDKkfUl7GfFq71pU4vWCnpOsGljycFEbkHWARZLf4lMYg3WOLw==", "cpu": [ "riscv64" ], @@ -1760,9 +1760,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", - "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.1.tgz", + "integrity": "sha512-EZc9NGTk/oSUzzOD4nYY4gIjteo2M3CiozX6t1IXGCOdgxJTlVu/7EdPeiqeHPSIrxkLhavqpBAUCfvC6vBOug==", "cpu": [ "s390x" ], @@ -1774,9 +1774,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", - "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.1.tgz", + "integrity": "sha512-NQ9KyU1Anuy59L8+HHOKM++CoUxrQWrZWXRik4BJFm+7i5NP6q/SW43xIBr80zzt+PDBJ7LeNmloQGfa0JGk0w==", "cpu": [ "x64" ], @@ -1788,9 +1788,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", - "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.1.tgz", + "integrity": "sha512-GZkLk2t6naywsveSFBsEb0PLU+JC9ggVjbndsbG20VPhar6D1gkMfCx4NfP9owpovBXTN+eRdqGSkDGIxPHhmQ==", "cpu": [ "x64" ], @@ -1802,9 +1802,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", - "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.1.tgz", + "integrity": "sha512-1hjG9Jpl2KDOetr64iQd8AZAEjkDUUK5RbDkYWsViYLC1op1oNzdjMJeFiofcGhqbNTaY2kfgqowE7DILifsrA==", "cpu": [ "x64" ], @@ -1816,9 +1816,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", - "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.1.tgz", + "integrity": "sha512-ARoKfflk0SiiYm3r1fmF73K/yB+PThmOwfWCk1sr7x/k9dc3uGLWuEE9if+Pw21el8MSpp3TMnG5vLNsJ/MMGQ==", "cpu": [ "arm64" ], @@ -1830,9 +1830,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", - "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.1.tgz", + "integrity": "sha512-oOST61G6VM45Mz2vdzWMr1s2slI7y9LqxEV5fCoWi2MDONmMvgsJVHSXxce/I2xOSZPTZ47nDPOl1tkwKWSHcw==", "cpu": [ "arm64" ], @@ -1844,9 +1844,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", - "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.1.tgz", + "integrity": "sha512-x5WgLi5dWpRz7WclKBGEF15LcWTh0ewrHM6Cq4A+WUbkysUMZNeqt05bwPonOQ3ihPS/WMhAZV5zB1DfnI4Sxg==", "cpu": [ "ia32" ], @@ -1858,9 +1858,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", - "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.1.tgz", + "integrity": "sha512-wS+zHAJRVP5zOL0e+a3V3E/NTEwM2HEvvNKoDy5Xcfs0o8lljxn+EAFPkUsxihBdmDq1JWzXmmB9cbssCPdxxw==", "cpu": [ "x64" ], @@ -1872,9 +1872,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", - "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.1.tgz", + "integrity": "sha512-rhHyrMeLpErT/C7BxcEsU4COHQUzHyrPYW5tOZUeUhziNtRuYxmDWvqQqzpuUt8xpOgmbKa1btGXfnA/ANVO+g==", "cpu": [ "x64" ], @@ -2257,20 +2257,20 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz", - "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.1.tgz", + "integrity": "sha512-Gn3aqnvNl4NGc6x3/Bqk1AOn0thyTU9bqDRhiRnUWezgvr2OnhYCWCgC8zXXRVqBsIL1pSDt7T9nJUe0oM0kDQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.58.0", - "@typescript-eslint/type-utils": "8.58.0", - "@typescript-eslint/utils": "8.58.0", - "@typescript-eslint/visitor-keys": "8.58.0", + "@typescript-eslint/scope-manager": "8.57.1", + "@typescript-eslint/type-utils": "8.57.1", + "@typescript-eslint/utils": "8.57.1", + "@typescript-eslint/visitor-keys": "8.57.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.5.0" + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2282,20 +2282,20 @@ "peerDependencies": { "@typescript-eslint/parser": "^8.43.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz", - "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.1.tgz", + "integrity": "sha512-k4eNDan0EIMTT/dUKc/g+rsJ6wcHYhNPdY19VoX/EOtaAG8DLtKCykhrUnuHPYvinn5jhAPgD2Qw9hXBwrahsw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.58.0", - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/typescript-estree": "8.58.0", - "@typescript-eslint/visitor-keys": "8.58.0", + "@typescript-eslint/scope-manager": "8.57.1", + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/typescript-estree": "8.57.1", + "@typescript-eslint/visitor-keys": "8.57.1", "debug": "^4.4.3" }, "engines": { @@ -2307,18 +2307,18 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz", - "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.1.tgz", + "integrity": "sha512-vx1F37BRO1OftsYlmG9xay1TqnjNVlqALymwWVuYTdo18XuKxtBpCj1QlzNIEHlvlB27osvXFWptYiEWsVdYsg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.58.0", - "@typescript-eslint/types": "^8.58.0", + "@typescript-eslint/tsconfig-utils": "^8.57.1", + "@typescript-eslint/types": "^8.57.1", "debug": "^4.4.3" }, "engines": { @@ -2329,18 +2329,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz", - "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.1.tgz", + "integrity": "sha512-hs/QcpCwlwT2L5S+3fT6gp0PabyGk4Q0Rv2doJXA0435/OpnSR3VRgvrp8Xdoc3UAYSg9cyUjTeFXZEPg/3OKg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/visitor-keys": "8.58.0" + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/visitor-keys": "8.57.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2351,9 +2351,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz", - "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.1.tgz", + "integrity": "sha512-0lgOZB8cl19fHO4eI46YUx2EceQqhgkPSuCGLlGi79L2jwYY1cxeYc1Nae8Aw1xjgW3PKVDLlr3YJ6Bxx8HkWg==", "dev": true, "license": "MIT", "engines": { @@ -2364,21 +2364,21 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz", - "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.1.tgz", + "integrity": "sha512-+Bwwm0ScukFdyoJsh2u6pp4S9ktegF98pYUU0hkphOOqdMB+1sNQhIz8y5E9+4pOioZijrkfNO/HUJVAFFfPKA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/typescript-estree": "8.58.0", - "@typescript-eslint/utils": "8.58.0", + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/typescript-estree": "8.57.1", + "@typescript-eslint/utils": "8.57.1", "debug": "^4.4.3", - "ts-api-utils": "^2.5.0" + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2389,13 +2389,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz", - "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.1.tgz", + "integrity": "sha512-S29BOBPJSFUiblEl6RzPPjJt6w25A6XsBqRVDt53tA/tlL8q7ceQNZHTjPeONt/3S7KRI4quk+yP9jK2WjBiPQ==", "dev": true, "license": "MIT", "engines": { @@ -2407,21 +2407,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz", - "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.1.tgz", + "integrity": "sha512-ybe2hS9G6pXpqGtPli9Gx9quNV0TWLOmh58ADlmZe9DguLq0tiAKVjirSbtM1szG6+QH6rVXyU6GTLQbWnMY+g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.58.0", - "@typescript-eslint/tsconfig-utils": "8.58.0", - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/visitor-keys": "8.58.0", + "@typescript-eslint/project-service": "8.57.1", + "@typescript-eslint/tsconfig-utils": "8.57.1", + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/visitor-keys": "8.57.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2431,7 +2431,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { @@ -2458,13 +2458,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.5" + "brace-expansion": "^5.0.2" }, "engines": { "node": "18 || 20 || >=22" @@ -2474,16 +2474,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz", - "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.1.tgz", + "integrity": "sha512-XUNSJ/lEVFttPMMoDVA2r2bwrl8/oPx8cURtczkSEswY5T3AeLmCy+EKWQNdL4u0MmAHOjcWrqJp2cdvgjn8dQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.58.0", - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/typescript-estree": "8.58.0" + "@typescript-eslint/scope-manager": "8.57.1", + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/typescript-estree": "8.57.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2494,17 +2494,17 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz", - "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.1.tgz", + "integrity": "sha512-YWnmJkXbofiz9KbnbbwuA2rpGkFPLbAIetcCNO6mJ8gdhdZ/v7WDXsoGFAJuM6ikUFKTlSQnjWnVO4ux+UzS6A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/types": "8.57.1", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -7715,9 +7715,9 @@ } }, "node_modules/rollup": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", - "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "version": "4.59.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.1.tgz", + "integrity": "sha512-iZKH8BeoCwTCBTZBZWQQMreekd4mdomwdjIQ40GC1oZm6o+8PnNMIxFOiCsGMWeS8iDJ7KZcl7KwmKk/0HOQpA==", "dev": true, "license": "MIT", "dependencies": { @@ -7731,31 +7731,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.1", - "@rollup/rollup-android-arm64": "4.60.1", - "@rollup/rollup-darwin-arm64": "4.60.1", - "@rollup/rollup-darwin-x64": "4.60.1", - "@rollup/rollup-freebsd-arm64": "4.60.1", - "@rollup/rollup-freebsd-x64": "4.60.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", - "@rollup/rollup-linux-arm-musleabihf": "4.60.1", - "@rollup/rollup-linux-arm64-gnu": "4.60.1", - "@rollup/rollup-linux-arm64-musl": "4.60.1", - "@rollup/rollup-linux-loong64-gnu": "4.60.1", - "@rollup/rollup-linux-loong64-musl": "4.60.1", - "@rollup/rollup-linux-ppc64-gnu": "4.60.1", - "@rollup/rollup-linux-ppc64-musl": "4.60.1", - "@rollup/rollup-linux-riscv64-gnu": "4.60.1", - "@rollup/rollup-linux-riscv64-musl": "4.60.1", - "@rollup/rollup-linux-s390x-gnu": "4.60.1", - "@rollup/rollup-linux-x64-gnu": "4.60.1", - "@rollup/rollup-linux-x64-musl": "4.60.1", - "@rollup/rollup-openbsd-x64": "4.60.1", - "@rollup/rollup-openharmony-arm64": "4.60.1", - "@rollup/rollup-win32-arm64-msvc": "4.60.1", - "@rollup/rollup-win32-ia32-msvc": "4.60.1", - "@rollup/rollup-win32-x64-gnu": "4.60.1", - "@rollup/rollup-win32-x64-msvc": "4.60.1", + "@rollup/rollup-android-arm-eabi": "4.59.1", + "@rollup/rollup-android-arm64": "4.59.1", + "@rollup/rollup-darwin-arm64": "4.59.1", + "@rollup/rollup-darwin-x64": "4.59.1", + "@rollup/rollup-freebsd-arm64": "4.59.1", + "@rollup/rollup-freebsd-x64": "4.59.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.1", + "@rollup/rollup-linux-arm-musleabihf": "4.59.1", + "@rollup/rollup-linux-arm64-gnu": "4.59.1", + "@rollup/rollup-linux-arm64-musl": "4.59.1", + "@rollup/rollup-linux-loong64-gnu": "4.59.1", + "@rollup/rollup-linux-loong64-musl": "4.59.1", + "@rollup/rollup-linux-ppc64-gnu": "4.59.1", + "@rollup/rollup-linux-ppc64-musl": "4.59.1", + "@rollup/rollup-linux-riscv64-gnu": "4.59.1", + "@rollup/rollup-linux-riscv64-musl": "4.59.1", + "@rollup/rollup-linux-s390x-gnu": "4.59.1", + "@rollup/rollup-linux-x64-gnu": "4.59.1", + "@rollup/rollup-linux-x64-musl": "4.59.1", + "@rollup/rollup-openbsd-x64": "4.59.1", + "@rollup/rollup-openharmony-arm64": "4.59.1", + "@rollup/rollup-win32-arm64-msvc": "4.59.1", + "@rollup/rollup-win32-ia32-msvc": "4.59.1", + "@rollup/rollup-win32-x64-gnu": "4.59.1", + "@rollup/rollup-win32-x64-msvc": "4.59.1", "fsevents": "~2.3.2" } }, @@ -8702,9 +8702,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", - "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", "dev": true, "license": "MIT", "engines": { @@ -8871,9 +8871,9 @@ "license": "MIT" }, "node_modules/typescript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", - "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -8885,16 +8885,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz", - "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.1.tgz", + "integrity": "sha512-fLvZWf+cAGw3tqMCYzGIU6yR8K+Y9NT2z23RwOjlNFF2HwSB3KhdEFI5lSBv8tNmFkkBShSjsCjzx1vahZfISA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.58.0", - "@typescript-eslint/parser": "8.58.0", - "@typescript-eslint/typescript-estree": "8.58.0", - "@typescript-eslint/utils": "8.58.0" + "@typescript-eslint/eslint-plugin": "8.57.1", + "@typescript-eslint/parser": "8.57.1", + "@typescript-eslint/typescript-estree": "8.57.1", + "@typescript-eslint/utils": "8.57.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -8905,7 +8905,7 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/unbox-primitive": { diff --git a/package.json b/package.json index 731b2fb6a..29fb93d8e 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "typecheck": "tsc --noEmit", "format": "eslint --cache --fix . && prettier --write --cache .", "check-format": "eslint --cache . && prettier --check --cache .;", - "gen": "npm run build && npm run docs:generate && npm run cli:generate && npm run update-tool-call-metrics && npm run format", + "gen": "npm run build && npm run docs:generate && npm run cli:generate && npm run format", "docs:generate": "node --experimental-strip-types scripts/generate-docs.ts", "start": "npm run build && node build/src/index.js", "start-debug": "DEBUG=mcp:* DEBUG_COLORS=false npm run build && node build/src/index.js", @@ -27,7 +27,6 @@ "prepare": "node --experimental-strip-types scripts/prepare.ts", "verify-server-json-version": "node --experimental-strip-types scripts/verify-server-json-version.ts", "update-lighthouse": "node --experimental-strip-types scripts/update-lighthouse.ts", - "update-tool-call-metrics": "node --experimental-strip-types scripts/update_tool_call_metrics.ts", "verify-npm-package": "node scripts/verify-npm-package.mjs", "eval": "npm run build && node --experimental-strip-types scripts/eval_gemini.ts", "count-tokens": "node --experimental-strip-types scripts/count_tokens.ts" @@ -70,12 +69,12 @@ "lighthouse": "13.0.3", "prettier": "^3.6.2", "puppeteer": "24.40.0", - "rollup": "4.60.1", + "rollup": "4.59.1", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", "sinon": "^21.0.0", "tiktoken": "^1.0.22", - "typescript": "^6.0.2", + "typescript": "^5.9.2", "typescript-eslint": "^8.43.0", "yargs": "18.0.0" }, diff --git a/release-please-config.json b/release-please-config.json index c45a9ffc2..b09f46882 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -39,11 +39,6 @@ "type": "json", "path": ".claude-plugin/plugin.json", "jsonpath": "version" - }, - { - "type": "json", - "path": ".github/plugin/plugin.json", - "jsonpath": "version" } ] } diff --git a/scripts/update_tool_call_metrics.ts b/scripts/update_tool_call_metrics.ts deleted file mode 100644 index f28e2688d..000000000 --- a/scripts/update_tool_call_metrics.ts +++ /dev/null @@ -1,65 +0,0 @@ -/** - * @license - * Copyright 2026 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as fs from 'node:fs'; -import * as path from 'node:path'; - -import type {ParsedArguments} from '../build/src/bin/chrome-devtools-mcp-cli-options.js'; -import { - applyToExistingMetrics, - generateToolMetrics, - type ToolMetric, -} from '../build/src/telemetry/toolMetricsUtils.js'; -import type {ToolDefinition} from '../build/src/tools/ToolDefinition.js'; -import {createTools} from '../build/src/tools/tools.js'; - -export function HaveUniqueNames(tools: ToolDefinition[]): boolean { - const toolNames = tools.map(tool => tool.name); - const toolNamesSet = new Set(toolNames); - return toolNamesSet.size === toolNames.length; -} - -function writeToolCallMetricsConfig() { - const outputPath = path.resolve('src/telemetry/tool_call_metrics.json'); - - const dir = path.dirname(outputPath); - if (!fs.existsSync(dir)) { - throw new Error(`Error: Directory ${dir} does not exist.`); - } - - const fullTools = createTools({slim: false} as ParsedArguments); - const slimTools = createTools({slim: true} as ParsedArguments); - - const allTools = [...fullTools, ...slimTools]; - - if (!HaveUniqueNames(allTools)) { - throw new Error('Error: Duplicate tool names found.'); - } - - let existingMetrics: ToolMetric[] = []; - if (fs.existsSync(outputPath)) { - try { - existingMetrics = JSON.parse( - fs.readFileSync(outputPath, 'utf8'), - ) as ToolMetric[]; - } catch { - console.warn( - `Warning: Failed to parse existing metrics from ${outputPath}. Starting fresh.`, - ); - } - } - - const newMetrics = generateToolMetrics(allTools); - const mergedMetrics = applyToExistingMetrics(existingMetrics, newMetrics); - - fs.writeFileSync(outputPath, JSON.stringify(mergedMetrics, null, 2) + '\n'); - - console.log( - `Successfully wrote ${mergedMetrics.length} total tool metrics (including deprecated ones) to ${outputPath}`, - ); -} - -writeToolCallMetricsConfig(); diff --git a/src/McpPage.ts b/src/McpPage.ts index 1e311bc62..dc4c53785 100644 --- a/src/McpPage.ts +++ b/src/McpPage.ts @@ -112,7 +112,7 @@ export class McpPage implements ContextPage { waitForEventsAfterAction( action: () => Promise, - options?: {timeout?: number}, + options?: {timeout?: number; dialog?: 'accept' | 'dismiss'}, ): Promise { const helper = this.createWaitForHelper( this.cpuThrottlingRate, diff --git a/src/McpResponse.ts b/src/McpResponse.ts index 77898b5df..ed20e4c4f 100644 --- a/src/McpResponse.ts +++ b/src/McpResponse.ts @@ -19,7 +19,6 @@ import type { Page, ResourceType, TextContent, - JSONSchema7Definition, } from './third_party/index.js'; import type {ToolGroup, ToolDefinition} from './tools/inPage.js'; import {handleDialog} from './tools/pages.js'; @@ -42,57 +41,6 @@ interface TraceInsightData { insightName: InsightName; } -export function replaceHtmlElementsWithUids(schema: JSONSchema7Definition) { - if (typeof schema === 'boolean') { - return; - } - - let isHtmlElement = false; - for (const [key, value] of Object.entries(schema)) { - if (key === 'x-mcp-type' && value === 'HTMLElement') { - isHtmlElement = true; - break; - } - } - - if (isHtmlElement) { - schema.properties = {uid: {type: 'string'}}; - schema.required = ['uid']; - } - - if (schema.properties) { - for (const key of Object.keys(schema.properties)) { - replaceHtmlElementsWithUids(schema.properties[key]); - } - } - - if (schema.items) { - if (Array.isArray(schema.items)) { - for (const item of schema.items) { - replaceHtmlElementsWithUids(item); - } - } else { - replaceHtmlElementsWithUids(schema.items); - } - } - - if (schema.anyOf) { - for (const s of schema.anyOf) { - replaceHtmlElementsWithUids(s); - } - } - if (schema.allOf) { - for (const s of schema.allOf) { - replaceHtmlElementsWithUids(s); - } - } - if (schema.oneOf) { - for (const s of schema.oneOf) { - replaceHtmlElementsWithUids(s); - } - } -} - async function getToolGroup( page: McpPage, ): Promise | undefined> { @@ -143,10 +91,6 @@ async function getToolGroup( }, 0); }); }); - - for (const tool of toolGroup?.tools ?? []) { - replaceHtmlElementsWithUids(tool.inputSchema); - } return toolGroup; } diff --git a/src/WaitForHelper.ts b/src/WaitForHelper.ts index 2dd0f48c1..e43e47961 100644 --- a/src/WaitForHelper.ts +++ b/src/WaitForHelper.ts @@ -5,7 +5,7 @@ */ import {logger} from './logger.js'; -import type {Page, Protocol, CdpPage} from './third_party/index.js'; +import type {Page, Protocol, CdpPage, Dialog} from './third_party/index.js'; import type {PredefinedNetworkConditions} from './third_party/index.js'; export class WaitForHelper { @@ -126,8 +126,24 @@ export class WaitForHelper { async waitForEventsAfterAction( action: () => Promise, - options?: {timeout?: number}, + options?: {timeout?: number; dialog?: 'accept' | 'dismiss'}, ): Promise { + if (options?.dialog) { + const dialogHandler = (dialog: Dialog) => { + if (options.dialog === 'dismiss') { + dialog.dismiss().catch((e: unknown) => logger(e)); + } else { + dialog.accept().catch((e: unknown) => logger(e)); + } + }; + // @ts-expect-error The Dialog type from CdpPage and Page are incompatible due to private properties. + this.#page.on('dialog', dialogHandler); + this.#abortController.signal.addEventListener('abort', () => { + // @ts-expect-error The Dialog type from CdpPage and Page are incompatible due to private properties. + this.#page.off('dialog', dialogHandler); + }); + } + const navigationFinished = this.waitForNavigationStarted() .then(navigationStated => { if (navigationStated) { diff --git a/src/bin/check-latest-version.ts b/src/bin/check-latest-version.ts deleted file mode 100644 index eb45674df..000000000 --- a/src/bin/check-latest-version.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @license - * Copyright 2026 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import fs from 'node:fs/promises'; -import path from 'node:path'; -import process from 'node:process'; - -const cachePath = process.argv[2]; - -if (cachePath) { - try { - const response = await fetch( - 'https://registry.npmjs.org/chrome-devtools-mcp/latest', - ); - const data = response.ok ? await response.json() : null; - - if ( - data && - typeof data === 'object' && - 'version' in data && - typeof data.version === 'string' - ) { - await fs.mkdir(path.dirname(cachePath), {recursive: true}); - await fs.writeFile(cachePath, JSON.stringify({version: data.version})); - } - } catch { - // Ignore errors. - } -} diff --git a/src/bin/chrome-devtools-mcp-cli-options.ts b/src/bin/chrome-devtools-mcp-cli-options.ts index 4b8976f22..80046b115 100644 --- a/src/bin/chrome-devtools-mcp-cli-options.ts +++ b/src/bin/chrome-devtools-mcp-cli-options.ts @@ -160,9 +160,8 @@ export const cliOptions = { }, experimentalVision: { type: 'boolean', - describe: - 'Whether to enable coordinate-based tools such as click_at(x,y). Usually requires a computer-use model able to produce accurate coordinates by looking at screenshots.', - hidden: false, + describe: 'Whether to enable vision tools', + hidden: true, }, experimentalStructuredContent: { type: 'boolean', @@ -233,7 +232,7 @@ export const cliOptions = { type: 'boolean', default: true, describe: - 'Set to false to opt-out of usage statistics collection. Google collects usage data to improve the tool, handled under the Google Privacy Policy (https://policies.google.com/privacy). This is independent from Chrome browser metrics. Disabled if `CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS` or `CI` env variables are set.', + 'Set to false to opt-out of usage statistics collection. Google collects usage data to improve the tool, handled under the Google Privacy Policy (https://policies.google.com/privacy). This is independent from Chrome browser metrics. Disabled if CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS or CI env variables are set.', }, clearcutEndpoint: { type: 'string', diff --git a/src/bin/chrome-devtools-mcp-main.ts b/src/bin/chrome-devtools-mcp-main.ts index 46100ed94..bfb6bb38e 100644 --- a/src/bin/chrome-devtools-mcp-main.ts +++ b/src/bin/chrome-devtools-mcp-main.ts @@ -12,15 +12,10 @@ import {createMcpServer, logDisclaimers} from '../index.js'; import {logger, saveLogsToFile} from '../logger.js'; import {computeFlagUsage} from '../telemetry/flagUtils.js'; import {StdioServerTransport} from '../third_party/index.js'; -import {checkForUpdates} from '../utils/check-for-updates.js'; import {VERSION} from '../version.js'; import {cliOptions, parseArguments} from './chrome-devtools-mcp-cli-options.js'; -await checkForUpdates( - 'Run `npm install chrome-devtools-mcp@latest` to update.', -); - export const args = parseArguments(VERSION); const logFile = args.logFile ? saveLogsToFile(args.logFile) : undefined; diff --git a/src/bin/chrome-devtools.ts b/src/bin/chrome-devtools.ts index e3aab5f45..f417c8373 100644 --- a/src/bin/chrome-devtools.ts +++ b/src/bin/chrome-devtools.ts @@ -21,16 +21,11 @@ import { import {isDaemonRunning, serializeArgs} from '../daemon/utils.js'; import {logDisclaimers} from '../index.js'; import {hideBin, yargs, type CallToolResult} from '../third_party/index.js'; -import {checkForUpdates} from '../utils/check-for-updates.js'; import {VERSION} from '../version.js'; import {commands} from './chrome-devtools-cli-options.js'; import {cliOptions, parseArguments} from './chrome-devtools-mcp-cli-options.js'; -await checkForUpdates( - 'Run `npm install -g chrome-devtools-mcp@latest` and `chrome-devtools start` to update and restart the daemon.', -); - async function start(args: string[]) { const combinedArgs = [...args, ...defaultArgs]; await startDaemon(combinedArgs); diff --git a/src/daemon/daemon.ts b/src/daemon/daemon.ts index 18f6ebad0..cde623745 100644 --- a/src/daemon/daemon.ts +++ b/src/daemon/daemon.ts @@ -55,6 +55,10 @@ async function setupMCPClient() { console.log('Setting up MCP client connection...'); // Create stdio transport for chrome-devtools-mcp + // Workaround for https://github.com/modelcontextprotocol/typescript-sdk/blob/v1.x/src/client/stdio.ts#L128 + // which causes the console window to show on Windows. + // @ts-expect-error no types for type. + process.type = 'mcp-client'; mcpTransport = new StdioClientTransport({ command: process.execPath, args: [INDEX_SCRIPT_PATH, ...mcpServerArgs], diff --git a/src/telemetry/ClearcutLogger.ts b/src/telemetry/ClearcutLogger.ts index 82f766cd4..a53345746 100644 --- a/src/telemetry/ClearcutLogger.ts +++ b/src/telemetry/ClearcutLogger.ts @@ -21,7 +21,7 @@ import { import {WatchdogClient} from './WatchdogClient.js'; const MS_PER_DAY = 24 * 60 * 60 * 1000; -export const PARAM_BLOCKLIST = new Set(['uid', 'reqid', 'msgid']); +const PARAM_BLOCKLIST = new Set(['uid']); const SUPPORTED_ZOD_TYPES = [ 'ZodString', @@ -36,7 +36,7 @@ function isZodType(type: string): type is ZodType { return SUPPORTED_ZOD_TYPES.includes(type as ZodType); } -export function getZodType(zodType: zod.ZodTypeAny): ZodType { +function getZodType(zodType: zod.ZodTypeAny): ZodType { const def = zodType._def; const typeName = def.typeName; @@ -59,7 +59,7 @@ export function getZodType(zodType: zod.ZodTypeAny): ZodType { type LoggedToolCallArgValue = string | number | boolean; -export function transformArgName(zodType: ZodType, name: string): string { +function transformName(zodType: ZodType, name: string): string { if (zodType === 'ZodString') { return `${name}_length`; } else if (zodType === 'ZodArray') { @@ -69,22 +69,6 @@ export function transformArgName(zodType: ZodType, name: string): string { } } -export function transformArgType(zodType: ZodType): string { - if (zodType === 'ZodString' || zodType === 'ZodArray') { - return 'number'; - } - switch (zodType) { - case 'ZodNumber': - return 'number'; - case 'ZodBoolean': - return 'boolean'; - case 'ZodEnum': - return 'enum'; - default: - throw new Error(`Unsupported zod type for tool parameter: ${zodType}`); - } -} - function transformValue( zodType: ZodType, value: unknown, @@ -133,7 +117,7 @@ export function sanitizeParams( `parameter ${name} has type ${zodType} but value ${value} is not of equivalent type`, ); } - const transformedName = transformArgName(zodType, name); + const transformedName = transformName(zodType, name); const transformedValue = transformValue(zodType, value); transformed[transformedName] = transformedValue; } diff --git a/src/telemetry/toolMetricsUtils.ts b/src/telemetry/toolMetricsUtils.ts deleted file mode 100644 index 53783645d..000000000 --- a/src/telemetry/toolMetricsUtils.ts +++ /dev/null @@ -1,126 +0,0 @@ -/** - * @license - * Copyright 2026 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import type {ToolDefinition} from '../tools/ToolDefinition.js'; - -import { - transformArgName, - transformArgType, - getZodType, - PARAM_BLOCKLIST, -} from './ClearcutLogger.js'; - -/** - * Validates that all values in an enum are of the homogeneous primitive type. - * Returns the primitive type string. Throws an error if heterogeneous. - */ -export function validateEnumHomogeneity(values: unknown[]): string { - const firstType = typeof values[0]; - for (const val of values) { - if (typeof val !== firstType) { - throw new Error('Heterogeneous enum types found'); - } - } - return firstType; -} - -export interface ArgMetric { - name: string; - argType: string; - isDeprecated?: boolean; -} - -export interface ToolMetric { - name: string; - args: ArgMetric[]; - isDeprecated?: boolean; -} - -export function applyToExistingMetrics( - existing: ToolMetric[], - update: ToolMetric[], -): ToolMetric[] { - const updated = applyToExisting(existing, update); - const existingByName = new Map(existing.map(tool => [tool.name, tool])); - const updatedByName = new Map(update.map(tool => [tool.name, tool])); - - return updated.map(tool => { - const existingTool = existingByName.get(tool.name); - const updatedTool = updatedByName.get(tool.name); - // If the tool still exists in the update, we will update the args. - if (existingTool && updatedTool) { - const updatedArgs = applyToExisting( - existingTool.args, - updatedTool.args, - ); - return {...tool, args: updatedArgs}; - } - return tool; - }); -} - -function applyToExisting( - existing: T[], - update: T[], -): T[] { - const existingNames = new Set(existing.map(item => item.name)); - const updatedNames = new Set(update.map(item => item.name)); - - const result: T[] = []; - // Keep the original ordering. - for (const entry of existing) { - const toAdd = {...entry}; - if (!updatedNames.has(entry.name)) { - toAdd.isDeprecated = true; - } - result.push(toAdd); - } - // New entries must be added to the very back of the list. - for (const entry of update) { - if (!existingNames.has(entry.name)) { - result.push({...entry}); - } - } - return result; -} - -/** - * Generates tool metrics from tool definitions. - */ -export function generateToolMetrics(tools: ToolDefinition[]): ToolMetric[] { - return tools.map(tool => { - const args: ArgMetric[] = []; - - for (const [name, schema] of Object.entries(tool.schema)) { - if (PARAM_BLOCKLIST.has(name)) { - continue; - } - const zodType = getZodType(schema); - const transformedName = transformArgName(zodType, name); - let argType = transformArgType(zodType); - - if (argType === 'enum') { - let values; - if (schema._def.values?.length > 0) { - values = schema._def.values; - } else { - values = schema._def.innerType._def.values; - } - argType = validateEnumHomogeneity(values); - } - - args.push({ - name: transformedName, - argType, - }); - } - - return { - name: tool.name, - args, - }; - }); -} diff --git a/src/telemetry/tool_call_metrics.json b/src/telemetry/tool_call_metrics.json deleted file mode 100644 index 496045ff6..000000000 --- a/src/telemetry/tool_call_metrics.json +++ /dev/null @@ -1,543 +0,0 @@ -[ - { - "name": "click", - "args": [ - { - "name": "dblClick", - "argType": "boolean" - }, - { - "name": "includeSnapshot", - "argType": "boolean" - } - ] - }, - { - "name": "click_at", - "args": [ - { - "name": "x", - "argType": "number" - }, - { - "name": "y", - "argType": "number" - }, - { - "name": "dblClick", - "argType": "boolean" - }, - { - "name": "includeSnapshot", - "argType": "boolean" - } - ] - }, - { - "name": "close_page", - "args": [ - { - "name": "pageId", - "argType": "number" - } - ] - }, - { - "name": "drag", - "args": [ - { - "name": "from_uid_length", - "argType": "number" - }, - { - "name": "to_uid_length", - "argType": "number" - }, - { - "name": "includeSnapshot", - "argType": "boolean" - } - ] - }, - { - "name": "emulate", - "args": [ - { - "name": "networkConditions", - "argType": "string" - }, - { - "name": "cpuThrottlingRate", - "argType": "number" - }, - { - "name": "geolocation_length", - "argType": "number" - }, - { - "name": "userAgent_length", - "argType": "number" - }, - { - "name": "colorScheme", - "argType": "string" - }, - { - "name": "viewport_length", - "argType": "number" - } - ] - }, - { - "name": "evaluate", - "args": [ - { - "name": "script_length", - "argType": "number" - } - ] - }, - { - "name": "evaluate_script", - "args": [ - { - "name": "function_length", - "argType": "number" - }, - { - "name": "args_count", - "argType": "number" - } - ] - }, - { - "name": "execute_in_page_tool", - "args": [ - { - "name": "toolName_length", - "argType": "number" - }, - { - "name": "params_length", - "argType": "number" - } - ] - }, - { - "name": "fill", - "args": [ - { - "name": "value_length", - "argType": "number" - }, - { - "name": "includeSnapshot", - "argType": "boolean" - } - ] - }, - { - "name": "fill_form", - "args": [ - { - "name": "elements_count", - "argType": "number" - }, - { - "name": "includeSnapshot", - "argType": "boolean" - } - ] - }, - { - "name": "get_console_message", - "args": [] - }, - { - "name": "get_network_request", - "args": [ - { - "name": "requestFilePath_length", - "argType": "number" - }, - { - "name": "responseFilePath_length", - "argType": "number" - } - ] - }, - { - "name": "get_tab_id", - "args": [ - { - "name": "pageId", - "argType": "number" - } - ] - }, - { - "name": "handle_dialog", - "args": [ - { - "name": "action", - "argType": "string" - }, - { - "name": "promptText_length", - "argType": "number" - } - ] - }, - { - "name": "hover", - "args": [ - { - "name": "includeSnapshot", - "argType": "boolean" - } - ] - }, - { - "name": "install_extension", - "args": [ - { - "name": "path_length", - "argType": "number" - } - ] - }, - { - "name": "lighthouse_audit", - "args": [ - { - "name": "mode", - "argType": "string" - }, - { - "name": "device", - "argType": "string" - }, - { - "name": "outputDirPath_length", - "argType": "number" - } - ] - }, - { - "name": "list_console_messages", - "args": [ - { - "name": "pageSize", - "argType": "number" - }, - { - "name": "pageIdx", - "argType": "number" - }, - { - "name": "types_count", - "argType": "number" - }, - { - "name": "includePreservedMessages", - "argType": "boolean" - } - ] - }, - { - "name": "list_extensions", - "args": [] - }, - { - "name": "list_in_page_tools", - "args": [] - }, - { - "name": "list_network_requests", - "args": [ - { - "name": "pageSize", - "argType": "number" - }, - { - "name": "pageIdx", - "argType": "number" - }, - { - "name": "resourceTypes_count", - "argType": "number" - }, - { - "name": "includePreservedRequests", - "argType": "boolean" - } - ] - }, - { - "name": "list_pages", - "args": [] - }, - { - "name": "navigate", - "args": [ - { - "name": "url_length", - "argType": "number" - } - ] - }, - { - "name": "navigate_page", - "args": [ - { - "name": "type", - "argType": "string" - }, - { - "name": "url_length", - "argType": "number" - }, - { - "name": "ignoreCache", - "argType": "boolean" - }, - { - "name": "handleBeforeUnload", - "argType": "string" - }, - { - "name": "initScript_length", - "argType": "number" - }, - { - "name": "timeout", - "argType": "number" - } - ] - }, - { - "name": "new_page", - "args": [ - { - "name": "url_length", - "argType": "number" - }, - { - "name": "background", - "argType": "boolean" - }, - { - "name": "isolatedContext_length", - "argType": "number" - }, - { - "name": "timeout", - "argType": "number" - } - ] - }, - { - "name": "performance_analyze_insight", - "args": [ - { - "name": "insightSetId_length", - "argType": "number" - }, - { - "name": "insightName_length", - "argType": "number" - } - ] - }, - { - "name": "performance_start_trace", - "args": [ - { - "name": "reload", - "argType": "boolean" - }, - { - "name": "autoStop", - "argType": "boolean" - }, - { - "name": "filePath_length", - "argType": "number" - } - ] - }, - { - "name": "performance_stop_trace", - "args": [ - { - "name": "filePath_length", - "argType": "number" - } - ] - }, - { - "name": "press_key", - "args": [ - { - "name": "key_length", - "argType": "number" - }, - { - "name": "includeSnapshot", - "argType": "boolean" - } - ] - }, - { - "name": "reload_extension", - "args": [ - { - "name": "id_length", - "argType": "number" - } - ] - }, - { - "name": "resize_page", - "args": [ - { - "name": "width", - "argType": "number" - }, - { - "name": "height", - "argType": "number" - } - ] - }, - { - "name": "screencast_start", - "args": [ - { - "name": "path_length", - "argType": "number" - } - ] - }, - { - "name": "screencast_stop", - "args": [] - }, - { - "name": "screenshot", - "args": [] - }, - { - "name": "select_page", - "args": [ - { - "name": "pageId", - "argType": "number" - }, - { - "name": "bringToFront", - "argType": "boolean" - } - ] - }, - { - "name": "take_memory_snapshot", - "args": [ - { - "name": "filePath_length", - "argType": "number" - } - ] - }, - { - "name": "take_screenshot", - "args": [ - { - "name": "format", - "argType": "string" - }, - { - "name": "quality", - "argType": "number" - }, - { - "name": "fullPage", - "argType": "boolean" - }, - { - "name": "filePath_length", - "argType": "number" - } - ] - }, - { - "name": "take_snapshot", - "args": [ - { - "name": "verbose", - "argType": "boolean" - }, - { - "name": "filePath_length", - "argType": "number" - } - ] - }, - { - "name": "trigger_extension_action", - "args": [ - { - "name": "id_length", - "argType": "number" - } - ] - }, - { - "name": "type_text", - "args": [ - { - "name": "text_length", - "argType": "number" - }, - { - "name": "submitKey_length", - "argType": "number" - } - ] - }, - { - "name": "uninstall_extension", - "args": [ - { - "name": "id_length", - "argType": "number" - } - ] - }, - { - "name": "upload_file", - "args": [ - { - "name": "filePath_length", - "argType": "number" - }, - { - "name": "includeSnapshot", - "argType": "boolean" - } - ] - }, - { - "name": "wait_for", - "args": [ - { - "name": "text_count", - "argType": "number" - }, - { - "name": "timeout", - "argType": "number" - } - ] - } -] diff --git a/src/third_party/index.ts b/src/third_party/index.ts index 09b0d02ce..96f5295c0 100644 --- a/src/third_party/index.ts +++ b/src/third_party/index.ts @@ -41,7 +41,7 @@ export {default as puppeteer} from 'puppeteer-core'; export type * from 'puppeteer-core'; export {PipeTransport} from 'puppeteer-core/internal/node/PipeTransport.js'; export type {CdpPage} from 'puppeteer-core/internal/cdp/Page.js'; -export type {JSONSchema7, JSONSchema7Definition} from 'json-schema'; +export type {JSONSchema7} from 'json-schema'; export { resolveDefaultUserDataDir, detectBrowserPlatform, diff --git a/src/tools/ToolDefinition.ts b/src/tools/ToolDefinition.ts index 8001e18c7..b6d44f6fa 100644 --- a/src/tools/ToolDefinition.ts +++ b/src/tools/ToolDefinition.ts @@ -210,7 +210,7 @@ export type ContextPage = Readonly<{ clearDialog(): void; waitForEventsAfterAction( action: () => Promise, - options?: {timeout?: number}, + options?: {timeout?: number; dialog?: 'accept' | 'dismiss'}, ): Promise; getInPageTools(): ToolGroup | undefined; }>; diff --git a/src/tools/inPage.ts b/src/tools/inPage.ts index 0cb1b6ba7..5b0031f62 100644 --- a/src/tools/inPage.ts +++ b/src/tools/inPage.ts @@ -4,12 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { - zod, - ajv, - type JSONSchema7, - type ElementHandle, -} from '../third_party/index.js'; +import {zod, ajv, type JSONSchema7} from '../third_party/index.js'; import {ToolCategory} from './categories.js'; import {definePageTool} from './ToolDefinition.js'; @@ -92,22 +87,6 @@ export const executeInPageTool = definePageTool({ } } - // Creates array of ElementHandles from the UIDs in the params. - // We do not replace the uids with the ElementsHandles yet, because - // the `evaluate` function only turns them into DOM elements if they - // are passed as non-nested arguments. - const handles: ElementHandle[] = []; - for (const value of Object.values(params)) { - if ( - value instanceof Object && - 'uid' in value && - typeof value.uid === 'string' && - Object.keys(value).length === 1 - ) { - handles.push(await request.page.getElementByUid(value.uid)); - } - } - const toolGroup = request.page.getInPageTools(); const tool = toolGroup?.tools.find(t => t.name === toolName); if (!tool) { @@ -123,19 +102,7 @@ export const executeInPageTool = definePageTool({ } const result = await request.page.pptrPage.evaluate( - async (name, args, ...elements) => { - // Replace the UIDs with DOM elements. - for (const [key, value] of Object.entries(args)) { - if ( - value instanceof Object && - 'uid' in value && - typeof value.uid === 'string' && - Object.keys(value).length === 1 - ) { - args[key] = elements.shift(); - } - } - + async (name, args) => { if (!window.__dtmcp?.executeTool) { throw new Error('No tools found on the page'); } @@ -147,7 +114,6 @@ export const executeInPageTool = definePageTool({ }, toolName, params, - ...handles, ); response.appendResponseLine(JSON.stringify(result, null, 2)); }, diff --git a/src/tools/script.ts b/src/tools/script.ts index 725628bcd..fff3383e8 100644 --- a/src/tools/script.ts +++ b/src/tools/script.ts @@ -77,11 +77,12 @@ Example with arguments: \`(el) => { } const worker = await getWebWorker(context, serviceWorkerId); - await context - .getSelectedMcpPage() - .waitForEventsAfterAction(async () => { + await context.getSelectedMcpPage().waitForEventsAfterAction( + async () => { await performEvaluation(worker, fnString, [], response); - }); + }, + {dialog: 'accept'}, + ); return; } @@ -101,9 +102,12 @@ Example with arguments: \`(el) => { const evaluatable = await getPageOrFrame(page, frames); - await mcpPage.waitForEventsAfterAction(async () => { - await performEvaluation(evaluatable, fnString, args, response); - }); + await mcpPage.waitForEventsAfterAction( + async () => { + await performEvaluation(evaluatable, fnString, args, response); + }, + {dialog: 'accept'}, + ); } finally { void Promise.allSettled(args.map(arg => arg.dispose())); } diff --git a/src/utils/check-for-updates.ts b/src/utils/check-for-updates.ts deleted file mode 100644 index 36c5b13b8..000000000 --- a/src/utils/check-for-updates.ts +++ /dev/null @@ -1,96 +0,0 @@ -/** - * @license - * Copyright 2026 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import child_process from 'node:child_process'; -import fs from 'node:fs/promises'; -import os from 'node:os'; -import path from 'node:path'; -import process from 'node:process'; - -import {VERSION} from '../version.js'; - -/** - * Notifies the user if an update is available. - * @param message The message to display in the update notification. - */ -let isChecking = false; - -/** @internal Reset flag for tests only. */ -export function resetUpdateCheckFlagForTesting() { - isChecking = false; -} - -export async function checkForUpdates(message: string) { - if (isChecking || process.env['CHROME_DEVTOOLS_MCP_NO_UPDATE_CHECKS']) { - return; - } - isChecking = true; - - const cachePath = path.join( - os.homedir(), - '.cache', - 'chrome-devtools-mcp', - 'latest.json', - ); - - let cachedVersion: string | undefined; - let stats: {mtimeMs: number} | undefined; - try { - stats = await fs.stat(cachePath); - const data = await fs.readFile(cachePath, 'utf8'); - cachedVersion = JSON.parse(data).version; - } catch { - // Ignore errors reading cache. - } - - if (cachedVersion && cachedVersion !== VERSION) { - console.warn( - `\nUpdate available: ${VERSION} -> ${cachedVersion}\n${message}\n`, - ); - } - - const now = Date.now(); - if (stats && now - stats.mtimeMs < 24 * 60 * 60 * 1000) { - return; - } - - // Update mtime immediately to prevent multiple subprocesses. - try { - const parentDir = path.dirname(cachePath); - await fs.mkdir(parentDir, {recursive: true}); - const nowTime = new Date(); - if (stats) { - await fs.utimes(cachePath, nowTime, nowTime); - } else { - await fs.writeFile(cachePath, JSON.stringify({version: VERSION})); - } - } catch { - // Ignore errors. - } - - // In a separate process, check the latest available version number - // and update the local snapshot accordingly. - const scriptPath = path.join( - import.meta.dirname, - '..', - 'bin', - 'check-latest-version.js', - ); - - try { - const child = child_process.spawn( - process.execPath, - [scriptPath, cachePath], - { - detached: true, - stdio: 'ignore', - }, - ); - child.unref(); - } catch { - // Fail silently in case of any errors. - } -} diff --git a/tests/McpContext.test.ts b/tests/McpContext.test.ts index 31a6c88b3..d84b506cb 100644 --- a/tests/McpContext.test.ts +++ b/tests/McpContext.test.ts @@ -89,6 +89,10 @@ describe('McpContext', () => { await withMcpContext( async (_response, context) => { const page = await context.newPage(); + // TODO: we do not know when the CLI flag to auto open DevTools will run + // so we need this until + // https://github.com/puppeteer/puppeteer/issues/14368 is there. + await new Promise(resolve => setTimeout(resolve, 5000)); await context.createPagesSnapshot(); assert.ok(context.getDevToolsPage(page.pptrPage)); }, diff --git a/tests/McpResponse.test.ts b/tests/McpResponse.test.ts index 915377197..66be61e84 100644 --- a/tests/McpResponse.test.ts +++ b/tests/McpResponse.test.ts @@ -15,8 +15,6 @@ import sinon from 'sinon'; import type {ParsedArguments} from '../src/bin/chrome-devtools-mcp-cli-options.js'; import type {McpContext} from '../src/McpContext.js'; import type {McpResponse} from '../src/McpResponse.js'; -import {replaceHtmlElementsWithUids} from '../src/McpResponse.js'; -import type {JSONSchema7Definition} from '../src/third_party/index.js'; import { closePage, listPages, @@ -1207,251 +1205,3 @@ describe('inPage tools', () => { }, 'new_page'); }); }); - -describe('replaceHtmlElementsWithUids', () => { - it('does nothing for boolean schemas', () => { - const schemaTrue: JSONSchema7Definition = true; - const schemaFalse: JSONSchema7Definition = false; - - replaceHtmlElementsWithUids(schemaTrue); - replaceHtmlElementsWithUids(schemaFalse); - - assert.strictEqual(schemaTrue, true); - assert.strictEqual(schemaFalse, false); - }); - - it('replaces HTMLElement type with uid string', () => { - const schema: JSONSchema7Definition = { - type: 'object', - properties: { - foo: {type: 'string'}, - bar: {type: 'number'}, - }, - required: ['foo'], - }; - Object.assign(schema, {'x-mcp-type': 'HTMLElement'}); - - replaceHtmlElementsWithUids(schema); - - if (typeof schema === 'object') { - assert.deepStrictEqual(schema.properties, { - uid: {type: 'string'}, - }); - assert.deepStrictEqual(schema.required, ['uid']); - } else { - assert.fail('Schema should be an object'); - } - }); - - it('does not replace if x-mcp-type is not HTMLElement', () => { - const schema: JSONSchema7Definition = { - type: 'object', - properties: { - foo: {type: 'string'}, - }, - }; - Object.assign(schema, {'x-mcp-type': 'OtherType'}); - - replaceHtmlElementsWithUids(schema); - - if (typeof schema === 'object') { - assert.deepStrictEqual(schema.properties, { - foo: {type: 'string'}, - }); - assert.strictEqual(schema.required, undefined); - } else { - assert.fail('Schema should be an object'); - } - }); - - it('recurses into nested properties', () => { - const schema: JSONSchema7Definition = { - type: 'object', - properties: { - element: { - type: 'object', - properties: { - foo: {type: 'string'}, - }, - }, - other: { - type: 'string', - }, - }, - }; - if (typeof schema === 'object' && schema.properties) { - Object.assign(schema.properties.element, {'x-mcp-type': 'HTMLElement'}); - } - - replaceHtmlElementsWithUids(schema); - - if ( - typeof schema === 'object' && - schema.properties && - typeof schema.properties.element === 'object' - ) { - const elementSchema = schema.properties.element; - assert.deepStrictEqual(elementSchema.properties, { - uid: {type: 'string'}, - }); - assert.deepStrictEqual(elementSchema.required, ['uid']); - } else { - assert.fail('Unexpected schema structure'); - } - }); - - it('recurses into array items (single schema object)', () => { - const schema: JSONSchema7Definition = { - type: 'array', - items: { - type: 'object', - }, - }; - if (typeof schema === 'object' && typeof schema.items === 'object') { - Object.assign(schema.items, {'x-mcp-type': 'HTMLElement'}); - } - - replaceHtmlElementsWithUids(schema); - - if (typeof schema === 'object' && typeof schema.items === 'object') { - const itemsSchema = schema.items; - if (!Array.isArray(itemsSchema)) { - assert.deepStrictEqual(itemsSchema.properties, { - uid: {type: 'string'}, - }); - assert.deepStrictEqual(itemsSchema.required, ['uid']); - } else { - assert.fail('items should not be an array in this test case'); - } - } else { - assert.fail('Unexpected schema structure'); - } - }); - - it('recurses into array items (array of schemas)', () => { - const schema: JSONSchema7Definition = { - type: 'array', - items: [ - { - type: 'object', - }, - { - type: 'string', - }, - ], - }; - if (typeof schema === 'object' && Array.isArray(schema.items)) { - Object.assign(schema.items[0], {'x-mcp-type': 'HTMLElement'}); - } - - replaceHtmlElementsWithUids(schema); - - if (typeof schema === 'object' && Array.isArray(schema.items)) { - const firstItem = schema.items[0]; - if (typeof firstItem === 'object') { - assert.deepStrictEqual(firstItem.properties, { - uid: {type: 'string'}, - }); - assert.deepStrictEqual(firstItem.required, ['uid']); - } else { - assert.fail('First item should be an object'); - } - - const secondItem = schema.items[1]; - if (typeof secondItem === 'object') { - assert.strictEqual(secondItem.properties, undefined); - } else { - assert.fail('Second item should be an object'); - } - } else { - assert.fail('Unexpected schema structure'); - } - }); - - it('recurses into anyOf', () => { - const schema: JSONSchema7Definition = { - anyOf: [ - { - type: 'object', - }, - { - type: 'string', - }, - ], - }; - if (typeof schema === 'object' && Array.isArray(schema.anyOf)) { - Object.assign(schema.anyOf[0], {'x-mcp-type': 'HTMLElement'}); - } - - replaceHtmlElementsWithUids(schema); - - if (typeof schema === 'object' && Array.isArray(schema.anyOf)) { - const firstItem = schema.anyOf[0]; - if (typeof firstItem === 'object') { - assert.deepStrictEqual(firstItem.properties, { - uid: {type: 'string'}, - }); - } else { - assert.fail('First item should be an object'); - } - } else { - assert.fail('Unexpected schema structure'); - } - }); - - it('recurses into allOf', () => { - const schema: JSONSchema7Definition = { - allOf: [ - { - type: 'object', - }, - ], - }; - if (typeof schema === 'object' && Array.isArray(schema.allOf)) { - Object.assign(schema.allOf[0], {'x-mcp-type': 'HTMLElement'}); - } - - replaceHtmlElementsWithUids(schema); - - if (typeof schema === 'object' && Array.isArray(schema.allOf)) { - const firstItem = schema.allOf[0]; - if (typeof firstItem === 'object') { - assert.deepStrictEqual(firstItem.properties, { - uid: {type: 'string'}, - }); - } else { - assert.fail('First item should be an object'); - } - } else { - assert.fail('Unexpected schema structure'); - } - }); - - it('recurses into oneOf', () => { - const schema: JSONSchema7Definition = { - oneOf: [ - { - type: 'object', - }, - ], - }; - if (typeof schema === 'object' && Array.isArray(schema.oneOf)) { - Object.assign(schema.oneOf[0], {'x-mcp-type': 'HTMLElement'}); - } - - replaceHtmlElementsWithUids(schema); - - if (typeof schema === 'object' && Array.isArray(schema.oneOf)) { - const firstItem = schema.oneOf[0]; - if (typeof firstItem === 'object') { - assert.deepStrictEqual(firstItem.properties, { - uid: {type: 'string'}, - }); - } else { - assert.fail('First item should be an object'); - } - } else { - assert.fail('Unexpected schema structure'); - } - }); -}); diff --git a/tests/check-for-updates.test.ts b/tests/check-for-updates.test.ts deleted file mode 100644 index 82413c493..000000000 --- a/tests/check-for-updates.test.ts +++ /dev/null @@ -1,148 +0,0 @@ -/** - * @license - * Copyright 2026 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import assert from 'node:assert'; -import child_process from 'node:child_process'; -import type {Stats} from 'node:fs'; -import fs from 'node:fs/promises'; -import os from 'node:os'; -import {afterEach, beforeEach, describe, it} from 'node:test'; - -import sinon from 'sinon'; - -import { - checkForUpdates, - resetUpdateCheckFlagForTesting, -} from '../src/utils/check-for-updates.js'; -import {VERSION} from '../src/version.js'; - -describe('checkForUpdates', () => { - beforeEach(() => { - sinon.stub(fs, 'mkdir').resolves(); - sinon.stub(fs, 'utimes').resolves(); - sinon.stub(fs, 'writeFile').resolves(); - }); - - afterEach(() => { - sinon.restore(); - resetUpdateCheckFlagForTesting(); - }); - - it('does nothing if CHROME_DEVTOOLS_MCP_NO_UPDATE_CHECKS is set', async () => { - process.env['CHROME_DEVTOOLS_MCP_NO_UPDATE_CHECKS'] = 'true'; - - const warnStub = sinon.stub(console, 'warn'); - const spawnStub = sinon.stub(child_process, 'spawn'); - const readFileStub = sinon.stub(fs, 'readFile'); - const statStub = sinon.stub(fs, 'stat'); - - await checkForUpdates('Run `npm update` to update.'); - - assert.ok(warnStub.notCalled); - assert.ok(spawnStub.notCalled); - assert.ok(readFileStub.notCalled); - assert.ok(statStub.notCalled); - - delete process.env['CHROME_DEVTOOLS_MCP_NO_UPDATE_CHECKS']; - }); - - it('notifies if cache exists and version is different', async () => { - sinon.stub(os, 'homedir').returns('/home/user'); - sinon.stub(fs, 'stat').resolves({mtimeMs: Date.now()} as unknown as Stats); - sinon.stub(fs, 'readFile').callsFake(async filePath => { - if (filePath.toString().includes('latest.json')) { - return JSON.stringify({ - version: '99.9.9', - }); - } - throw new Error(`File not found: ${filePath}`); - }); - const warnStub = sinon.stub(console, 'warn'); - const spawnStub = sinon.stub(child_process, 'spawn'); - - await checkForUpdates('Run `npm update` to update.'); - - assert.ok( - warnStub.calledWith( - sinon.match('Update available: ' + VERSION + ' -> 99.9.9'), - ), - ); - assert.ok(spawnStub.notCalled); - }); - - it('does not spawn fetch process if cache is fresh', async () => { - sinon.stub(os, 'homedir').returns('/home/user'); - sinon.stub(fs, 'stat').resolves({mtimeMs: Date.now()} as unknown as Stats); - sinon.stub(fs, 'readFile').callsFake(async filePath => { - if (filePath.toString().includes('latest.json')) { - return JSON.stringify({ - version: VERSION, - }); - } - throw new Error(`File not found: ${filePath}`); - }); - const spawnStub = sinon.stub(child_process, 'spawn'); - - await checkForUpdates('Run `npm update` to update.'); - - assert.ok(spawnStub.notCalled); - }); - - it('spawns detached process if cache is stale', async () => { - sinon.stub(os, 'homedir').returns('/home/user'); - sinon.stub(fs, 'stat').resolves({ - mtimeMs: Date.now() - 25 * 60 * 60 * 1000, // 25 hours ago - } as unknown as Stats); - sinon.stub(fs, 'readFile').callsFake(async filePath => { - if (filePath.toString().includes('latest.json')) { - return JSON.stringify({ - version: VERSION, - }); - } - throw new Error(`File not found: ${filePath}`); - }); - - const unrefSpy = sinon.spy(); - const spawnStub = sinon.stub(child_process, 'spawn').returns({ - unref: unrefSpy, - } as unknown as child_process.ChildProcess); - - await checkForUpdates('Run `npm update` to update.'); - - assert.ok(spawnStub.calledOnce); - assert.strictEqual(spawnStub.firstCall.args[0], process.execPath); - assert.ok( - spawnStub.firstCall.args[1][0]?.includes('check-latest-version.js'), - ); - assert.ok(spawnStub.firstCall.args[1][1]?.includes('latest.json')); - assert.strictEqual(spawnStub.firstCall.args[2]?.detached, true); - assert.ok(unrefSpy.calledOnce); - }); - - it('spawns detached process if cache is missing', async () => { - sinon.stub(os, 'homedir').returns('/home/user'); - sinon.stub(fs, 'stat').rejects(new Error('File not found')); - sinon.stub(fs, 'readFile').callsFake(async filePath => { - throw new Error(`File not found: ${filePath}`); - }); - - const unrefSpy = sinon.spy(); - const spawnStub = sinon.stub(child_process, 'spawn').returns({ - unref: unrefSpy, - } as unknown as child_process.ChildProcess); - - await checkForUpdates('Run `npm update` to update.'); - - assert.ok(spawnStub.calledOnce); - assert.strictEqual(spawnStub.firstCall.args[0], process.execPath); - assert.ok( - spawnStub.firstCall.args[1][0]?.includes('check-latest-version.js'), - ); - assert.ok(spawnStub.firstCall.args[1][1]?.includes('latest.json')); - assert.strictEqual(spawnStub.firstCall.args[2]?.detached, true); - assert.ok(unrefSpy.calledOnce); - }); -}); diff --git a/tests/telemetry/toolMetricsUtils.test.ts b/tests/telemetry/toolMetricsUtils.test.ts deleted file mode 100644 index 0c369aaea..000000000 --- a/tests/telemetry/toolMetricsUtils.test.ts +++ /dev/null @@ -1,263 +0,0 @@ -/** - * @license - * Copyright 2026 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import assert from 'node:assert'; -import {describe, it} from 'node:test'; - -import { - applyToExistingMetrics, - generateToolMetrics, - validateEnumHomogeneity, -} from '../../src/telemetry/toolMetricsUtils.js'; -import {zod} from '../../src/third_party/index.js'; -import {ToolCategory} from '../../src/tools/categories.js'; -import type {ToolDefinition} from '../../src/tools/ToolDefinition.js'; - -describe('toolMetricsUtils', () => { - describe('validateEnumHomogeneity', () => { - it('should return the primitive type of a homogeneous enum', () => { - const result = validateEnumHomogeneity(['a', 'b', 'c']); - assert.strictEqual(result, 'string'); - - const result2 = validateEnumHomogeneity([1, 2, 3]); - assert.strictEqual(result2, 'number'); - }); - - it('should throw for heterogeneous enum types', () => { - assert.throws(() => { - validateEnumHomogeneity(['a', 1, 'c']); - }, /Heterogeneous enum types found/); - }); - }); - - describe('generateToolMetrics', () => { - it('should map tools correctly and apply transformations', () => { - const mockTool: ToolDefinition = { - name: 'test_tool', - description: 'test description', - annotations: { - category: ToolCategory.INPUT, - readOnlyHint: true, - }, - schema: { - argStr: zod.string(), - uid: zod.string(), // Should be blocked - }, - handler: async () => { - // no-op - }, - }; - - const metrics = generateToolMetrics([mockTool]); - assert.strictEqual(metrics.length, 1); - assert.strictEqual(metrics[0].name, 'test_tool'); - assert.strictEqual(metrics[0].args.length, 1); // uid is blocked - assert.strictEqual(metrics[0].args[0].name, 'argStr_length'); - assert.strictEqual(metrics[0].args[0].argType, 'number'); - }); - - it('should handle enums correctly', () => { - const mockTool: ToolDefinition = { - name: 'enum_tool', - description: 'test description', - annotations: { - category: ToolCategory.INPUT, - readOnlyHint: true, - }, - schema: { - argEnum: zod.enum(['foo', 'bar']), - }, - handler: async () => { - // no-op - }, - }; - - const metrics = generateToolMetrics([mockTool]); - assert.strictEqual(metrics.length, 1); - assert.strictEqual(metrics[0].args[0].name, 'argEnum'); - assert.strictEqual(metrics[0].args[0].argType, 'string'); - }); - }); - - describe('applyToExistingMetrics', () => { - it('should return the same metrics if existing and update are the same', () => { - const existing = [{name: 'foo', args: []}]; - const update = [{name: 'foo', args: []}]; - const result = applyToExistingMetrics(existing, update); - const expected = [{name: 'foo', args: []}]; - assert.deepStrictEqual(result, expected); - }); - - it('should append new entries to the end of the array', () => { - const existing = [{name: 'foo', args: []}]; - const update = [ - {name: 'foo', args: []}, - {name: 'bar', args: []}, - ]; - const result = applyToExistingMetrics(existing, update); - const expected = [ - {name: 'foo', args: []}, - {name: 'bar', args: []}, - ]; - assert.deepStrictEqual(result, expected); - }); - - it('should mark missing entries as deprecated and preserve their order', () => { - const existing = [ - {name: 'foo', args: []}, - {name: 'bar', args: []}, - ]; - const update = [{name: 'foo', args: []}]; - const result = applyToExistingMetrics(existing, update); - const expected = [ - {name: 'foo', args: []}, - {name: 'bar', args: [], isDeprecated: true}, - ]; - assert.deepStrictEqual(result, expected); - }); - - it('should handle adding new entries and deprecating old ones simultaneously', () => { - const existing = [ - {name: 'foo', args: []}, - {name: 'bar', args: []}, - ]; - const update = [ - {name: 'bar', args: []}, - {name: 'baz', args: []}, - ]; - const result = applyToExistingMetrics(existing, update); - const expected = [ - {name: 'foo', args: [], isDeprecated: true}, - {name: 'bar', args: []}, - {name: 'baz', args: []}, - ]; - assert.deepStrictEqual(result, expected); - }); - - it('should append new arguments to the back', () => { - const existing = [ - {name: 'foo', args: [{name: 'arg_a', argType: 'string'}]}, - ]; - const update = [ - { - name: 'foo', - args: [ - {name: 'arg_a', argType: 'string'}, - {name: 'arg_b', argType: 'string'}, - ], - }, - ]; - const result = applyToExistingMetrics(existing, update); - const expected = [ - { - name: 'foo', - args: [ - {name: 'arg_a', argType: 'string'}, - {name: 'arg_b', argType: 'string'}, - ], - }, - ]; - assert.deepStrictEqual(result, expected); - }); - - it('should mark removed arguments as deprecated', () => { - const existing = [ - { - name: 'foo', - args: [ - {name: 'arg_a', argType: 'string'}, - {name: 'arg_b', argType: 'string'}, - ], - }, - ]; - const update = [ - {name: 'foo', args: [{name: 'arg_a', argType: 'string'}]}, - ]; - const result = applyToExistingMetrics(existing, update); - const expected = [ - { - name: 'foo', - args: [ - {name: 'arg_a', argType: 'string'}, - {name: 'arg_b', argType: 'string', isDeprecated: true}, - ], - }, - ]; - assert.deepStrictEqual(result, expected); - }); - - it('should not change args if they are the same', () => { - const existing = [ - {name: 'foo', args: [{name: 'arg_a', argType: 'string'}]}, - ]; - const update = [ - {name: 'foo', args: [{name: 'arg_a', argType: 'string'}]}, - ]; - const result = applyToExistingMetrics(existing, update); - const expected = [ - {name: 'foo', args: [{name: 'arg_a', argType: 'string'}]}, - ]; - assert.deepStrictEqual(result, expected); - }); - - it('should handle adding and removing arguments simultaneously', () => { - const existing = [ - { - name: 'foo', - args: [ - {name: 'arg_a', argType: 'string'}, - {name: 'arg_b', argType: 'string'}, - ], - }, - ]; - const update = [ - { - name: 'foo', - args: [ - {name: 'arg_b', argType: 'string'}, - {name: 'arg_c', argType: 'string'}, - ], - }, - ]; - const result = applyToExistingMetrics(existing, update); - const expected = [ - { - name: 'foo', - args: [ - {name: 'arg_a', argType: 'string', isDeprecated: true}, - {name: 'arg_b', argType: 'string'}, - {name: 'arg_c', argType: 'string'}, - ], - }, - ]; - assert.deepStrictEqual(result, expected); - }); - - it('should handle tool and argument changes simultaneously', () => { - const existing = [ - {name: 'foo', args: [{name: 'arg_a', argType: 'string'}]}, - {name: 'bar', args: []}, - ]; - const update = [ - {name: 'foo', args: [{name: 'arg_b', argType: 'string'}]}, - {name: 'baz', args: []}, - ]; - const result = applyToExistingMetrics(existing, update); - const expected = [ - { - name: 'foo', - args: [ - {name: 'arg_a', argType: 'string', isDeprecated: true}, - {name: 'arg_b', argType: 'string'}, - ], - }, - {name: 'bar', args: [], isDeprecated: true}, - {name: 'baz', args: []}, - ]; - assert.deepStrictEqual(result, expected); - }); - }); -}); diff --git a/tests/tools/inPage.test.ts b/tests/tools/inPage.test.ts index d87bbea83..622da9034 100644 --- a/tests/tools/inPage.test.ts +++ b/tests/tools/inPage.test.ts @@ -347,98 +347,5 @@ describe('inPage', () => { {categoryInPageTools: true} as ParsedArguments, ); }); - - it('replaces uid with element handle in params', async () => { - await withMcpContext(async (response, context) => { - const page = await context.newPage(); - response.setPage(page); - - page.inPageTools = { - name: 'test-group', - description: 'test description', - tools: [ - { - name: 'test-tool', - description: 'test tool description', - inputSchema: { - type: 'object', - properties: { - element: {type: 'object'}, - }, - required: ['element'], - }, - }, - ], - }; - - await page.pptrPage.evaluate(() => { - window.__dtmcp = { - executeTool: async ( - _name: string, - args: Record, - ) => { - const el = args.element; - if (el instanceof HTMLElement) { - return { - isElement: true, - tagName: el.tagName, - id: el.id, - }; - } - return { - isElement: false, - tagName: '', - id: '', - }; - }, - }; - }); - - await page.pptrPage.evaluate(() => { - const div = document.createElement('div'); - div.id = 'test-id'; - document.body.appendChild(div); - }); - - const handle = await page.pptrPage.$('#test-id'); - if (!handle) { - throw new Error('Handle not found'); - } - - page.getElementByUid = async (uid: string) => { - if (uid === 'some-uid') { - return handle; - } - throw new Error('Not found'); - }; - - await executeInPageTool.handler( - { - params: { - toolName: 'test-tool', - params: JSON.stringify({element: {uid: 'some-uid'}}), - }, - page: page, - }, - response, - context, - ); - - assert.strictEqual( - response.responseLines[0], - JSON.stringify( - { - result: { - isElement: true, - tagName: 'DIV', - id: 'test-id', - }, - }, - null, - 2, - ), - ); - }); - }); }); }); diff --git a/tests/tools/script.test.ts b/tests/tools/script.test.ts index 772ebc076..85deffb29 100644 --- a/tests/tools/script.test.ts +++ b/tests/tools/script.test.ts @@ -98,6 +98,30 @@ describe('script', () => { }); }); + it('work for scripts that trigger dialogs', async () => { + await withMcpContext(async (response, context) => { + const page = context.getSelectedPptrPage(); + + await page.setContent(html``); + + await evaluateScript().handler( + { + params: { + function: String(async () => { + setTimeout(() => alert('hello'), 10); + await new Promise(r => setTimeout(r, 20)); + return 'Works'; + }), + }, + }, + response, + context, + ); + const lineEvaluation = response.responseLines.at(2)!; + assert.strictEqual(JSON.parse(lineEvaluation), 'Works'); + }); + }); + it('work for async functions', async () => { await withMcpContext(async (response, context) => { const page = context.getSelectedPptrPage(); diff --git a/tsconfig.json b/tsconfig.json index c6597abe7..3d09e3f58 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,13 +5,14 @@ "ES2023", "DOM", "ES2024.Promise", - "ES2025.Iterator", - "ES2025.Collection" + "ESNext.Iterator", + "ESNext.Collection" ], - "types": ["node", "filesystem"], + "module": "esnext", "moduleResolution": "bundler", "outDir": "./build", "rootDir": ".", + "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true,