diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1034d64 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,21 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: npm + - run: npm ci + - run: npm run check + - run: npm run build + - run: npm run test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..ae3c770 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,31 @@ +name: Release + +on: + release: + types: [published] + +jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: npm + registry-url: https://registry.npmjs.org + - run: npm ci + - name: Verify version matches release tag + run: | + PKG_VERSION="v$(node -p 'require("./package.json").version')" + if [ "$PKG_VERSION" != "${{ github.event.release.tag_name }}" ]; then + echo "::error::package.json version ($PKG_VERSION) does not match release tag (${{ github.event.release.tag_name }})" + exit 1 + fi + - run: npm run check + - run: npm run build + - run: npm run test + - run: npm publish --access public diff --git a/AGENTS.md b/AGENTS.md index e5b824e..b56beb0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -35,13 +35,13 @@ Each file registers one MCP tool via `registerXxxTool(server, manager)`. All too `NotificationHandler` listens for device events, updates `DeviceStateStore` (in-memory cache), and pushes MCP resource change notifications. ### Config (`src/config.ts`) -CLI args take precedence over env vars. Optional: `--devices`/`ZAPAROO_DEVICES`, `--keys`/`ZAPAROO_KEYS`. Default port is 7497. When no devices are configured, mDNS discovery is enabled automatically. Use `--no-discovery` or `ZAPAROO_NO_DISCOVERY=1` to disable. +CLI args take precedence over env vars. Optional: `--devices`/`ZAPAROO_DEVICES`, `--keys`/`ZAPAROO_KEYS`. Default port is 7497. When no devices are configured, mDNS discovery is enabled automatically. Use `--no-discovery` or `ZAPAROO_NO_DISCOVERY=1` to disable. Tool filtering: `--allowed-tools`/`ZAPAROO_ALLOWED_TOOLS` (comma-separated whitelist) or `--blocked-tools`/`ZAPAROO_BLOCKED_TOOLS` (comma-separated blacklist). Cannot use both simultaneously. ## Adding a new tool 1. Create `src/tools/mytool.ts` with a `registerMyTool(server, manager)` function 2. Define input schema with Zod, use `toolRequest()` from `src/tools/helpers.ts` to call the device -3. Register it in `src/tools/index.ts` inside `registerAllTools()` +3. Add an entry to the `registry` array in `src/tools/index.ts` inside `registerAllTools()` ## Testing @@ -73,3 +73,5 @@ Tests use Vitest and live alongside source files as `*.test.ts`. Test files are - Error responses from tools MUST use the `{ isError: true }` pattern (see `src/tools/helpers.ts`) - Device IDs use `host:port` format - NEVER use CommonJS (`require`/`module.exports`) — this is an ESM-only project +- PR descriptions MUST NOT include test plans — keep them to a summary only +- NEVER amend commits — always create new commits diff --git a/package-lock.json b/package-lock.json index 73e47d1..9d7fd78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "zaparoo-mcp", - "version": "0.1.0", + "version": "1.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "zaparoo-mcp", - "version": "0.1.0", + "version": "1.1.0", "license": "GPL-3.0-or-later", "dependencies": { "@modelcontextprotocol/sdk": "^1.28.0", @@ -24,12 +24,15 @@ "tsup": "^8.5.1", "typescript": "^6.0.2", "vitest": "^4.1.2" + }, + "engines": { + "node": ">=22" } }, "node_modules/@biomejs/biome": { - "version": "2.4.9", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.9.tgz", - "integrity": "sha512-wvZW92FrwitTcacvCBT8xdAbfbxWfDLwjYMmU3djjqQTh7Ni4ZdiWIT/x5VcZ+RQuxiKzIOzi5D+dcyJDFZMsA==", + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.10.tgz", + "integrity": "sha512-xxA3AphFQ1geij4JTHXv4EeSTda1IFn22ye9LdyVPoJU19fNVl0uzfEuhsfQ4Yue/0FaLs2/ccVi4UDiE7R30w==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { @@ -43,20 +46,20 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.4.9", - "@biomejs/cli-darwin-x64": "2.4.9", - "@biomejs/cli-linux-arm64": "2.4.9", - "@biomejs/cli-linux-arm64-musl": "2.4.9", - "@biomejs/cli-linux-x64": "2.4.9", - "@biomejs/cli-linux-x64-musl": "2.4.9", - "@biomejs/cli-win32-arm64": "2.4.9", - "@biomejs/cli-win32-x64": "2.4.9" + "@biomejs/cli-darwin-arm64": "2.4.10", + "@biomejs/cli-darwin-x64": "2.4.10", + "@biomejs/cli-linux-arm64": "2.4.10", + "@biomejs/cli-linux-arm64-musl": "2.4.10", + "@biomejs/cli-linux-x64": "2.4.10", + "@biomejs/cli-linux-x64-musl": "2.4.10", + "@biomejs/cli-win32-arm64": "2.4.10", + "@biomejs/cli-win32-x64": "2.4.10" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.4.9", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.9.tgz", - "integrity": "sha512-d5G8Gf2RpH5pYwiHLPA+UpG3G9TLQu4WM+VK6sfL7K68AmhcEQ9r+nkj/DvR/GYhYox6twsHUtmWWWIKfcfQQA==", + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.10.tgz", + "integrity": "sha512-vuzzI1cWqDVzOMIkYyHbKqp+AkQq4K7k+UCXWpkYcY/HDn1UxdsbsfgtVpa40shem8Kax4TLDLlx8kMAecgqiw==", "cpu": [ "arm64" ], @@ -71,9 +74,9 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.4.9", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.9.tgz", - "integrity": "sha512-LNCLNgqDMG7BLdc3a8aY/dwKPK7+R8/JXJoXjCvZh2gx8KseqBdFDKbhrr7HCWF8SzNhbTaALhTBoh/I6rf9lA==", + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.10.tgz", + "integrity": "sha512-14fzASRo+BPotwp7nWULy2W5xeUyFnTaq1V13Etrrxkrih+ez/2QfgFm5Ehtf5vSjtgx/IJycMMpn5kPd5ZNaA==", "cpu": [ "x64" ], @@ -88,13 +91,16 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.4.9", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.9.tgz", - "integrity": "sha512-4adnkAUi6K4C/emPRgYznMOcLlUqZdXWM6aIui4VP4LraE764g6Q4YguygnAUoxKjKIXIWPteKMgRbN0wsgwcg==", + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.10.tgz", + "integrity": "sha512-7MH1CMW5uuxQ/s7FLST63qF8B3Hgu2HRdZ7tA1X1+mk+St4JOuIrqdhIBnnyqeyWJNI+Bww7Es5QZ0wIc1Cmkw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -105,13 +111,16 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.4.9", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.9.tgz", - "integrity": "sha512-8RCww5xnPn2wpK4L/QDGDOW0dq80uVWfppPxHIUg6mOs9B6gRmqPp32h1Ls3T8GnW8Wo5A8u7vpTwz4fExN+sw==", + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.10.tgz", + "integrity": "sha512-WrJY6UuiSD/Dh+nwK2qOTu8kdMDlLV3dLMmychIghHPAysWFq1/DGC1pVZx8POE3ZkzKR3PUUnVrtZfMfaJjyQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -122,13 +131,16 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "2.4.9", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.9.tgz", - "integrity": "sha512-L10na7POF0Ks/cgLFNF1ZvIe+X4onLkTi5oP9hY+Rh60Q+7fWzKDDCeGyiHUFf1nGIa9dQOOUPGe2MyYg8nMSQ==", + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.10.tgz", + "integrity": "sha512-tZLvEEi2u9Xu1zAqRjTcpIDGVtldigVvzug2fTuPG0ME/g8/mXpRPcNgLB22bGn6FvLJpHHnqLnwliOu8xjYrg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -139,13 +151,16 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.4.9", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.9.tgz", - "integrity": "sha512-5TD+WS9v5vzXKzjetF0hgoaNFHMcpQeBUwKKVi3JbG1e9UCrFuUK3Gt185fyTzvRdwYkJJEMqglRPjmesmVv4A==", + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.10.tgz", + "integrity": "sha512-kDTi3pI6PBN6CiczsWYOyP2zk0IJI08EWEQyDMQWW221rPaaEz6FvjLhnU07KMzLv8q3qSuoB93ua6inSQ55Tw==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -156,9 +171,9 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.4.9", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.9.tgz", - "integrity": "sha512-aDZr0RBC3sMGJOU10BvG7eZIlWLK/i51HRIfScE2lVhfts2dQTreowLiJJd+UYg/tHKxS470IbzpuKmd0MiD6g==", + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.10.tgz", + "integrity": "sha512-umwQU6qPzH+ISTf/eHyJ/QoQnJs3V9Vpjz2OjZXe9MVBZ7prgGafMy7yYeRGnlmDAn87AKTF3Q6weLoMGpeqdQ==", "cpu": [ "arm64" ], @@ -173,9 +188,9 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "2.4.9", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.9.tgz", - "integrity": "sha512-NS4g/2G9SoQ4ktKtz31pvyc/rmgzlcIDCGU/zWbmHJAqx6gcRj2gj5Q/guXhoWTzCUaQZDIqiCQXHS7BcGYc0w==", + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.10.tgz", + "integrity": "sha512-aW/JU5GuyH4uxMrNYpoC2kjaHlyJGLgIa3XkhPEZI0uKhZhJZU8BuEyJmvgzSPQNGozBwWjC972RaNdcJ9KyJg==", "cpu": [ "x64" ], @@ -189,6 +204,43 @@ "node": ">=14.21.3" } }, + "node_modules/@emnapi/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", + "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", + "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.4", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", @@ -632,9 +684,9 @@ } }, "node_modules/@hono/node-server": { - "version": "1.19.11", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.11.tgz", - "integrity": "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==", + "version": "1.19.12", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.12.tgz", + "integrity": "sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw==", "license": "MIT", "engines": { "node": ">=18.14.1" @@ -689,9 +741,9 @@ "license": "MIT" }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.28.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.28.0.tgz", - "integrity": "sha512-gmloF+i+flI8ouQK7MWW4mOwuMh4RePBuPFAEPC6+pdqyWOUMDOixb6qZ69owLJpz6XmyllCouc4t8YWO+E2Nw==", + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", + "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", "license": "MIT", "dependencies": { "@hono/node-server": "^1.19.9", @@ -850,6 +902,9 @@ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -867,6 +922,9 @@ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -884,6 +942,9 @@ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -901,6 +962,9 @@ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -918,6 +982,9 @@ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -935,6 +1002,9 @@ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1020,9 +1090,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.0.tgz", - "integrity": "sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==", + "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==", "cpu": [ "arm" ], @@ -1034,9 +1104,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.0.tgz", - "integrity": "sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==", + "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==", "cpu": [ "arm64" ], @@ -1048,9 +1118,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.0.tgz", - "integrity": "sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==", + "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==", "cpu": [ "arm64" ], @@ -1062,9 +1132,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.0.tgz", - "integrity": "sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==", + "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==", "cpu": [ "x64" ], @@ -1076,9 +1146,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.0.tgz", - "integrity": "sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", "cpu": [ "arm64" ], @@ -1090,9 +1160,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.0.tgz", - "integrity": "sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==", + "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==", "cpu": [ "x64" ], @@ -1104,13 +1174,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.0.tgz", - "integrity": "sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==", + "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==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1118,13 +1191,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.0.tgz", - "integrity": "sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==", + "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==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1132,13 +1208,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.0.tgz", - "integrity": "sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==", + "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==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1146,13 +1225,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.0.tgz", - "integrity": "sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==", + "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==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1160,13 +1242,16 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.0.tgz", - "integrity": "sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==", + "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==", "cpu": [ "loong64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1174,13 +1259,16 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.0.tgz", - "integrity": "sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==", + "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==", "cpu": [ "loong64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1188,13 +1276,16 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.0.tgz", - "integrity": "sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==", + "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==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1202,13 +1293,16 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.0.tgz", - "integrity": "sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==", + "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==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1216,13 +1310,16 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.0.tgz", - "integrity": "sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==", + "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==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1230,13 +1327,16 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.0.tgz", - "integrity": "sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==", + "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==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1244,13 +1344,16 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.0.tgz", - "integrity": "sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==", + "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==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1258,13 +1361,16 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.0.tgz", - "integrity": "sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==", + "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==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1272,13 +1378,16 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.0.tgz", - "integrity": "sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==", + "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==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1286,9 +1395,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.0.tgz", - "integrity": "sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==", + "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==", "cpu": [ "x64" ], @@ -1300,9 +1409,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.0.tgz", - "integrity": "sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", "cpu": [ "arm64" ], @@ -1314,9 +1423,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.0.tgz", - "integrity": "sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==", + "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==", "cpu": [ "arm64" ], @@ -1328,9 +1437,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.0.tgz", - "integrity": "sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==", + "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==", "cpu": [ "ia32" ], @@ -1342,9 +1451,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.0.tgz", - "integrity": "sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==", + "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==", "cpu": [ "x64" ], @@ -1356,9 +1465,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.0.tgz", - "integrity": "sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==", + "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==", "cpu": [ "x64" ], @@ -2106,9 +2215,9 @@ } }, "node_modules/express-rate-limit": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.1.tgz", - "integrity": "sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.2.tgz", + "integrity": "sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==", "license": "MIT", "dependencies": { "ip-address": "10.1.0" @@ -2566,6 +2675,9 @@ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -2587,6 +2699,9 @@ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -2608,6 +2723,9 @@ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -2629,6 +2747,9 @@ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -2923,9 +3044,9 @@ } }, "node_modules/path-to-regexp": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.0.tgz", - "integrity": "sha512-PuseHIvAnz3bjrM2rGJtSgo1zjgxapTLZ7x2pjhzWwlp4SJQgK3f3iZIQwkpEnBaKz6seKBADpM4B4ySkuYypg==", + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.1.tgz", + "integrity": "sha512-fvU78fIjZ+SBM9YwCknCvKOUKkLVqtWDVctl0s7xIqfmfb38t2TT4ZU2gHm+Z8xGwgW+QWEU3oQSAzIbo89Ggw==", "license": "MIT", "funding": { "type": "opencollective", @@ -3182,9 +3303,9 @@ } }, "node_modules/rollup": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz", - "integrity": "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", "dev": true, "license": "MIT", "dependencies": { @@ -3198,31 +3319,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.0", - "@rollup/rollup-android-arm64": "4.60.0", - "@rollup/rollup-darwin-arm64": "4.60.0", - "@rollup/rollup-darwin-x64": "4.60.0", - "@rollup/rollup-freebsd-arm64": "4.60.0", - "@rollup/rollup-freebsd-x64": "4.60.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.0", - "@rollup/rollup-linux-arm-musleabihf": "4.60.0", - "@rollup/rollup-linux-arm64-gnu": "4.60.0", - "@rollup/rollup-linux-arm64-musl": "4.60.0", - "@rollup/rollup-linux-loong64-gnu": "4.60.0", - "@rollup/rollup-linux-loong64-musl": "4.60.0", - "@rollup/rollup-linux-ppc64-gnu": "4.60.0", - "@rollup/rollup-linux-ppc64-musl": "4.60.0", - "@rollup/rollup-linux-riscv64-gnu": "4.60.0", - "@rollup/rollup-linux-riscv64-musl": "4.60.0", - "@rollup/rollup-linux-s390x-gnu": "4.60.0", - "@rollup/rollup-linux-x64-gnu": "4.60.0", - "@rollup/rollup-linux-x64-musl": "4.60.0", - "@rollup/rollup-openbsd-x64": "4.60.0", - "@rollup/rollup-openharmony-arm64": "4.60.0", - "@rollup/rollup-win32-arm64-msvc": "4.60.0", - "@rollup/rollup-win32-ia32-msvc": "4.60.0", - "@rollup/rollup-win32-x64-gnu": "4.60.0", - "@rollup/rollup-win32-x64-msvc": "4.60.0", + "@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", "fsevents": "~2.3.2" } }, diff --git a/package.json b/package.json index 9176cda..4a86d40 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zaparoo-mcp", - "version": "1.0.0", + "version": "1.1.0", "description": "MCP server for Zaparoo Core devices", "type": "module", "bin": { diff --git a/src/config.test.ts b/src/config.test.ts index 0373ed8..1cb8ff6 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -14,6 +14,8 @@ describe('loadConfig', () => { delete process.env.ZAPAROO_DEVICES; delete process.env.ZAPAROO_KEYS; delete process.env.ZAPAROO_NO_DISCOVERY; + delete process.env.ZAPAROO_ALLOWED_TOOLS; + delete process.env.ZAPAROO_BLOCKED_TOOLS; mockParseArgs.mockReturnValue({ values: {}, positionals: [], tokens: undefined }); }); @@ -21,6 +23,8 @@ describe('loadConfig', () => { delete process.env.ZAPAROO_DEVICES; delete process.env.ZAPAROO_KEYS; delete process.env.ZAPAROO_NO_DISCOVERY; + delete process.env.ZAPAROO_ALLOWED_TOOLS; + delete process.env.ZAPAROO_BLOCKED_TOOLS; }); it('enables discovery when no devices configured', () => { @@ -143,6 +147,59 @@ describe('loadConfig', () => { expect(config.devices[0].port).toBe(7497); }); + describe('tool filtering config', () => { + it('parses ZAPAROO_ALLOWED_TOOLS env var', () => { + process.env.ZAPAROO_ALLOWED_TOOLS = 'zaparoo_run,zaparoo_stop'; + const config = loadConfig(); + + expect(config.allowedTools).toEqual(['zaparoo_run', 'zaparoo_stop']); + expect(config.blockedTools).toBeUndefined(); + }); + + it('parses ZAPAROO_BLOCKED_TOOLS env var', () => { + process.env.ZAPAROO_BLOCKED_TOOLS = 'zaparoo_admin,zaparoo_admin_manage'; + const config = loadConfig(); + + expect(config.blockedTools).toEqual(['zaparoo_admin', 'zaparoo_admin_manage']); + expect(config.allowedTools).toBeUndefined(); + }); + + it('CLI --allowed-tools overrides env var', () => { + process.env.ZAPAROO_ALLOWED_TOOLS = 'zaparoo_run'; + mockParseArgs.mockReturnValue({ + values: { 'allowed-tools': 'zaparoo_stop,zaparoo_media' }, + positionals: [], + tokens: undefined, + }); + + const config = loadConfig(); + + expect(config.allowedTools).toEqual(['zaparoo_stop', 'zaparoo_media']); + expect(config.blockedTools).toBeUndefined(); + }); + + it('trims whitespace and ignores empty segments', () => { + process.env.ZAPAROO_ALLOWED_TOOLS = ' zaparoo_run , zaparoo_stop ,,'; + const config = loadConfig(); + + expect(config.allowedTools).toEqual(['zaparoo_run', 'zaparoo_stop']); + }); + + it('throws when both allowed and blocked tools are set', () => { + process.env.ZAPAROO_ALLOWED_TOOLS = 'zaparoo_run'; + process.env.ZAPAROO_BLOCKED_TOOLS = 'zaparoo_admin'; + + expect(() => loadConfig()).toThrow('Cannot use both allowed and blocked tools'); + }); + + it('returns undefined for both when not set', () => { + const config = loadConfig(); + + expect(config.allowedTools).toBeUndefined(); + expect(config.blockedTools).toBeUndefined(); + }); + }); + describe('CLI args precedence', () => { it('CLI --devices overrides ZAPAROO_DEVICES env var', () => { process.env.ZAPAROO_DEVICES = 'envhost:7497'; diff --git a/src/config.ts b/src/config.ts index dce664a..1ec5c41 100644 --- a/src/config.ts +++ b/src/config.ts @@ -12,6 +12,8 @@ export interface DeviceConfig { export interface Config { devices: DeviceConfig[]; discovery: boolean; + allowedTools?: string[]; + blockedTools?: string[]; } function parseDeviceList(raw: string, keys: string): DeviceConfig[] { @@ -52,6 +54,8 @@ export function loadConfig(): Config { devices: { type: 'string' }, keys: { type: 'string' }, 'no-discovery': { type: 'boolean' }, + 'allowed-tools': { type: 'string' }, + 'blocked-tools': { type: 'string' }, }, strict: false, }); @@ -61,17 +65,43 @@ export function loadConfig(): Config { const noDiscovery = (values['no-discovery'] as boolean | undefined) ?? process.env.ZAPAROO_NO_DISCOVERY === '1'; + const allowedToolsRaw = + (values['allowed-tools'] as string | undefined) ?? process.env.ZAPAROO_ALLOWED_TOOLS ?? ''; + const blockedToolsRaw = + (values['blocked-tools'] as string | undefined) ?? process.env.ZAPAROO_BLOCKED_TOOLS ?? ''; + + const allowedTools = allowedToolsRaw + ? allowedToolsRaw + .split(',') + .map((s) => s.trim()) + .filter(Boolean) + : undefined; + const blockedTools = blockedToolsRaw + ? blockedToolsRaw + .split(',') + .map((s) => s.trim()) + .filter(Boolean) + : undefined; + + if (allowedTools && blockedTools) { + throw new Error( + 'Cannot use both allowed and blocked tools. Set only --allowed-tools/ZAPAROO_ALLOWED_TOOLS or --blocked-tools/ZAPAROO_BLOCKED_TOOLS, not both.', + ); + } + if (!devicesRaw) { if (noDiscovery) { throw new Error( 'No devices configured. Use --devices or set ZAPAROO_DEVICES env var.', ); } - return { devices: [], discovery: true }; + return { devices: [], discovery: true, allowedTools, blockedTools }; } return { devices: parseDeviceList(devicesRaw, keysRaw), discovery: false, + allowedTools, + blockedTools, }; } diff --git a/src/index.ts b/src/index.ts index 8958910..ac34e0a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,7 +10,7 @@ async function main(): Promise { const traceBuffer = new TraceBuffer(); const manager = new DeviceManager(config.devices, traceBuffer); - const server = createServer(manager, traceBuffer); + const server = createServer(manager, traceBuffer, config); let discovery: MdnsDiscovery | null = null; diff --git a/src/server.ts b/src/server.ts index b4e8426..7bb5af3 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,4 +1,5 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import type { Config } from './config.js'; import type { DeviceManager } from './connection/manager.js'; import type { TraceBuffer } from './connection/trace.js'; import { NotificationBuffer } from './notifications/buffer.js'; @@ -18,7 +19,11 @@ ZapScript: Before writing or explaining ZapScript commands, read the zaparoo://r Workflows: To launch a game, search with zaparoo_media first, then execute with zaparoo_run. To pause/resume without exiting, use zaparoo_media_control — use zaparoo_stop only to fully exit. To write NFC tags, check readers with zaparoo_readers first, then write with zaparoo_readers_write.`; -export function createServer(manager: DeviceManager, traceBuffer: TraceBuffer): McpServer { +export function createServer( + manager: DeviceManager, + traceBuffer: TraceBuffer, + config: Config, +): McpServer { const server = new McpServer( { name: 'zaparoo-mcp', version: PACKAGE_VERSION }, { @@ -37,7 +42,7 @@ export function createServer(manager: DeviceManager, traceBuffer: TraceBuffer): const notificationHandler = new NotificationHandler(server.server, manager, notificationBuffer); // Register all tools, prompts, and resources - registerAllTools(server, manager, notificationBuffer, traceBuffer); + registerAllTools(server, manager, notificationBuffer, traceBuffer, config); registerAllPrompts(server); registerDeviceStateResources(server, manager, notificationHandler); registerZapScriptReference(server); diff --git a/src/tools/index.test.ts b/src/tools/index.test.ts new file mode 100644 index 0000000..24c265e --- /dev/null +++ b/src/tools/index.test.ts @@ -0,0 +1,42 @@ +import { describe, expect, it } from 'vitest'; +import { filterTools } from './index.js'; + +const ALL_TOOLS = ['zaparoo_run', 'zaparoo_stop', 'zaparoo_media', 'zaparoo_admin']; + +describe('filterTools', () => { + it('returns all tools when neither allowedTools nor blockedTools is set', () => { + const result = filterTools(ALL_TOOLS, {}); + + expect(result).toEqual(new Set(ALL_TOOLS)); + }); + + it('returns only allowed tools when allowedTools is set', () => { + const result = filterTools(ALL_TOOLS, { + allowedTools: ['zaparoo_run', 'zaparoo_stop'], + }); + + expect(result).toEqual(new Set(['zaparoo_run', 'zaparoo_stop'])); + }); + + it('excludes blocked tools when blockedTools is set', () => { + const result = filterTools(ALL_TOOLS, { + blockedTools: ['zaparoo_admin'], + }); + + expect(result).toEqual(new Set(['zaparoo_run', 'zaparoo_stop', 'zaparoo_media'])); + }); + + it('returns empty set when allowedTools is empty', () => { + const result = filterTools(ALL_TOOLS, { allowedTools: [] }); + + expect(result).toEqual(new Set()); + }); + + it('allows unknown names in allowedTools without error', () => { + const result = filterTools(ALL_TOOLS, { + allowedTools: ['zaparoo_run', 'zaparoo_nonexistent'], + }); + + expect(result).toEqual(new Set(['zaparoo_run', 'zaparoo_nonexistent'])); + }); +}); diff --git a/src/tools/index.ts b/src/tools/index.ts index af58a5a..90f5010 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -1,4 +1,5 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import type { Config } from '../config.js'; import type { DeviceManager } from '../connection/manager.js'; import type { TraceBuffer } from '../connection/trace.js'; import type { NotificationBuffer } from '../notifications/buffer.js'; @@ -23,30 +24,75 @@ import { registerStopTool } from './stop.js'; import { registerSystemsTool } from './systems.js'; import { registerTokensTool } from './tokens.js'; +export function filterTools( + allNames: string[], + config: Pick, +): Set { + if (config.allowedTools) { + return new Set(config.allowedTools); + } + if (config.blockedTools) { + const blocked = new Set(config.blockedTools); + return new Set(allNames.filter((name) => !blocked.has(name))); + } + return new Set(allNames); +} + export function registerAllTools( server: McpServer, manager: DeviceManager, notificationBuffer: NotificationBuffer, traceBuffer: TraceBuffer, + config: Config, ): void { - registerDevicesTool(server, manager); - registerRunTool(server, manager); - registerStopTool(server, manager); - registerTokensTool(server, manager); - registerMediaTool(server, manager); - registerMediaControlTool(server, manager); - registerMediaIndexTool(server, manager); - registerSettingsTool(server, manager); - registerSettingsUpdateTool(server, manager); - registerReadersTool(server, manager); - registerReadersWriteTool(server, manager); - registerMappingsTool(server, manager); - registerSystemsTool(server, manager); - registerScreenshotTool(server, manager); - registerAdminTool(server, manager); - registerAdminManageTool(server, manager); - registerInputTool(server, manager); - registerInboxTool(server, manager); - registerNotificationsTool(server, notificationBuffer); - registerLogsTool(server, manager, traceBuffer); + // Names here must match the first argument to server.registerTool() in each tool file. + const registry: Array<{ name: string; register: () => void }> = [ + { name: 'zaparoo_devices', register: () => registerDevicesTool(server, manager) }, + { name: 'zaparoo_run', register: () => registerRunTool(server, manager) }, + { name: 'zaparoo_stop', register: () => registerStopTool(server, manager) }, + { name: 'zaparoo_tokens', register: () => registerTokensTool(server, manager) }, + { name: 'zaparoo_media', register: () => registerMediaTool(server, manager) }, + { name: 'zaparoo_media_control', register: () => registerMediaControlTool(server, manager) }, + { name: 'zaparoo_media_index', register: () => registerMediaIndexTool(server, manager) }, + { name: 'zaparoo_settings', register: () => registerSettingsTool(server, manager) }, + { + name: 'zaparoo_settings_update', + register: () => registerSettingsUpdateTool(server, manager), + }, + { name: 'zaparoo_readers', register: () => registerReadersTool(server, manager) }, + { name: 'zaparoo_readers_write', register: () => registerReadersWriteTool(server, manager) }, + { name: 'zaparoo_mappings', register: () => registerMappingsTool(server, manager) }, + { name: 'zaparoo_systems', register: () => registerSystemsTool(server, manager) }, + { name: 'zaparoo_screenshot', register: () => registerScreenshotTool(server, manager) }, + { name: 'zaparoo_admin', register: () => registerAdminTool(server, manager) }, + { name: 'zaparoo_admin_manage', register: () => registerAdminManageTool(server, manager) }, + { name: 'zaparoo_input', register: () => registerInputTool(server, manager) }, + { name: 'zaparoo_inbox', register: () => registerInboxTool(server, manager) }, + { + name: 'zaparoo_notifications', + register: () => registerNotificationsTool(server, notificationBuffer), + }, + { + name: 'zaparoo_logs', + register: () => registerLogsTool(server, manager, traceBuffer), + }, + ]; + + const allNames = registry.map((t) => t.name); + const enabled = filterTools(allNames, config); + + if (config.allowedTools) { + const knownNames = new Set(allNames); + for (const name of config.allowedTools) { + if (!knownNames.has(name)) { + console.error(`[config] warning: allowed tool "${name}" does not match any known tool`); + } + } + } + + for (const entry of registry) { + if (enabled.has(entry.name)) { + entry.register(); + } + } } diff --git a/src/tools/screenshot.ts b/src/tools/screenshot.ts index cfa2d29..a73eb97 100644 --- a/src/tools/screenshot.ts +++ b/src/tools/screenshot.ts @@ -1,8 +1,9 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; import { z } from 'zod/v3'; import type { DeviceManager } from '../connection/manager.js'; +import type { ScreenshotResponse } from '../types.js'; import { Methods } from '../types.js'; -import { toolRequest } from './helpers.js'; export function registerScreenshotTool(server: McpServer, manager: DeviceManager): void { server.registerTool( @@ -19,8 +20,21 @@ export function registerScreenshotTool(server: McpServer, manager: DeviceManager .describe('Device ID (host:port). Defaults to first available device.'), }), }, - async ({ device }) => { - return toolRequest(manager, device, Methods.Screenshot); + async ({ device }): Promise => { + try { + const dev = manager.getDevice(device); + const result = await dev.request(Methods.Screenshot); + return { + content: [{ type: 'image', data: result.data, mimeType: 'image/png' }], + }; + } catch (err) { + return { + content: [ + { type: 'text', text: `Error: ${err instanceof Error ? err.message : String(err)}` }, + ], + isError: true, + }; + } }, ); }