From 7d09ec112e271ca83a7f9126de1cf266947f3726 Mon Sep 17 00:00:00 2001 From: Paleo Date: Fri, 22 May 2026 07:07:21 +0200 Subject: [PATCH] feat: add openclaw qa toolkit --- .changeset/README.md | 8 + .changeset/config.json | 11 + AGENTS.md | 25 + README.md | 4 - autonomous-agent.md | 4 + package-lock.json | 8616 +++++++++++++++-- package.json | 12 +- packages/docmap/CHANGELOG.md | 7 + packages/docmap/README.md | 2 +- packages/docmap/package.json | 9 +- packages/docmap/tsconfig.build.json | 2 +- .../openclaw-channel-mock-core/CHANGELOG.md | 13 + packages/openclaw-channel-mock-core/LICENSE | 24 + packages/openclaw-channel-mock-core/NOTICE.md | 30 + packages/openclaw-channel-mock-core/README.md | 14 + .../openclaw-channel-mock-core/package.json | 53 + .../src/accounts.ts | 106 + .../src/bus-client.ts | 261 + .../src/bus-handler.ts | 176 + .../src/bus-queries.ts | 190 + .../src/bus-state.ts | 248 + .../src/bus-waiters.ts | 135 + .../src/channel-setup-plugin.ts | 42 + .../src/config-schema.ts | 50 + .../openclaw-channel-mock-core/src/gateway.ts | 57 + .../openclaw-channel-mock-core/src/inbound.ts | 329 + .../openclaw-channel-mock-core/src/index.ts | 38 + .../src/outbound.ts | 34 + .../src/plugin-actions.ts | 321 + .../openclaw-channel-mock-core/src/plugin.ts | 196 + .../src/protocol.ts | 272 + .../src/runtime-api.ts | 26 + .../openclaw-channel-mock-core/src/runtime.ts | 9 + .../openclaw-channel-mock-core/src/setup.ts | 35 + .../openclaw-channel-mock-core/src/status.ts | 25 + .../openclaw-channel-mock-core/src/types.ts | 52 + .../test/bus.test.ts | 85 + .../tsconfig.build.json | 14 + .../openclaw-channel-mock-core/tsconfig.json | 8 + packages/openclaw-discord-mock/CHANGELOG.md | 20 + packages/openclaw-discord-mock/LICENSE | 24 + packages/openclaw-discord-mock/NOTICE.md | 16 + packages/openclaw-discord-mock/README.md | 11 + .../openclaw.plugin.json | 6 + packages/openclaw-discord-mock/package.json | 76 + .../src/channel-plugin-api.ts | 1 + .../src/channel-setup-plugin.ts | 6 + packages/openclaw-discord-mock/src/index.ts | 16 + packages/openclaw-discord-mock/src/plugin.ts | 10 + packages/openclaw-discord-mock/src/runtime.ts | 5 + .../openclaw-discord-mock/src/setup-entry.ts | 13 + .../test/plugin-actions.test.ts | 171 + .../openclaw-discord-mock/tsconfig.build.json | 14 + packages/openclaw-discord-mock/tsconfig.json | 8 + packages/openclaw-qa-runner/CHANGELOG.md | 24 + packages/openclaw-qa-runner/Dockerfile | 59 + packages/openclaw-qa-runner/LICENSE | 21 + packages/openclaw-qa-runner/README.md | 107 + packages/openclaw-qa-runner/bin/cli.mjs | 7 + packages/openclaw-qa-runner/bin/qa.mjs | 88 + .../openclaw-qa-runner/docker-compose.yml | 67 + packages/openclaw-qa-runner/package.json | 61 + packages/openclaw-qa-runner/src/bus.ts | 23 + packages/openclaw-qa-runner/src/cli.ts | 53 + packages/openclaw-qa-runner/src/context.ts | 317 + packages/openclaw-qa-runner/src/cost.ts | 26 + .../openclaw-qa-runner/src/gateway-log.ts | 183 + packages/openclaw-qa-runner/src/index.ts | 23 + packages/openclaw-qa-runner/src/judge.ts | 94 + .../openclaw-qa-runner/src/mock-cli-server.ts | 236 + .../openclaw-qa-runner/src/mock-cli-shim.ts | 100 + packages/openclaw-qa-runner/src/report.ts | 206 + .../openclaw-qa-runner/src/runner-args.ts | 74 + packages/openclaw-qa-runner/src/runner.ts | 519 + .../templates/.env.local.example | 5 + .../templates/docker-compose.yml | 11 + .../templates/openclaw.json | 41 + .../openclaw-qa-runner/tsconfig.build.json | 14 + packages/openclaw-qa-runner/tsconfig.json | 8 + packages/openclaw-slack-mock/CHANGELOG.md | 20 + packages/openclaw-slack-mock/LICENSE | 24 + packages/openclaw-slack-mock/NOTICE.md | 16 + packages/openclaw-slack-mock/README.md | 11 + .../openclaw-slack-mock/openclaw.plugin.json | 6 + packages/openclaw-slack-mock/package.json | 76 + .../src/channel-plugin-api.ts | 1 + .../src/channel-setup-plugin.ts | 6 + packages/openclaw-slack-mock/src/index.ts | 16 + packages/openclaw-slack-mock/src/plugin.ts | 10 + packages/openclaw-slack-mock/src/runtime.ts | 5 + .../openclaw-slack-mock/src/setup-entry.ts | 13 + .../openclaw-slack-mock/test/inbound.test.ts | 115 + .../test/plugin-actions.test.ts | 67 + .../openclaw-slack-mock/tsconfig.build.json | 14 + packages/openclaw-slack-mock/tsconfig.json | 8 + packages/worktree-env/CHANGELOG.md | 7 + packages/worktree-env/package.json | 9 +- packages/worktree-env/tsconfig.build.json | 2 +- 98 files changed, 13881 insertions(+), 852 deletions(-) create mode 100644 .changeset/README.md create mode 100644 .changeset/config.json create mode 100644 packages/docmap/CHANGELOG.md create mode 100644 packages/openclaw-channel-mock-core/CHANGELOG.md create mode 100644 packages/openclaw-channel-mock-core/LICENSE create mode 100644 packages/openclaw-channel-mock-core/NOTICE.md create mode 100644 packages/openclaw-channel-mock-core/README.md create mode 100644 packages/openclaw-channel-mock-core/package.json create mode 100644 packages/openclaw-channel-mock-core/src/accounts.ts create mode 100644 packages/openclaw-channel-mock-core/src/bus-client.ts create mode 100644 packages/openclaw-channel-mock-core/src/bus-handler.ts create mode 100644 packages/openclaw-channel-mock-core/src/bus-queries.ts create mode 100644 packages/openclaw-channel-mock-core/src/bus-state.ts create mode 100644 packages/openclaw-channel-mock-core/src/bus-waiters.ts create mode 100644 packages/openclaw-channel-mock-core/src/channel-setup-plugin.ts create mode 100644 packages/openclaw-channel-mock-core/src/config-schema.ts create mode 100644 packages/openclaw-channel-mock-core/src/gateway.ts create mode 100644 packages/openclaw-channel-mock-core/src/inbound.ts create mode 100644 packages/openclaw-channel-mock-core/src/index.ts create mode 100644 packages/openclaw-channel-mock-core/src/outbound.ts create mode 100644 packages/openclaw-channel-mock-core/src/plugin-actions.ts create mode 100644 packages/openclaw-channel-mock-core/src/plugin.ts create mode 100644 packages/openclaw-channel-mock-core/src/protocol.ts create mode 100644 packages/openclaw-channel-mock-core/src/runtime-api.ts create mode 100644 packages/openclaw-channel-mock-core/src/runtime.ts create mode 100644 packages/openclaw-channel-mock-core/src/setup.ts create mode 100644 packages/openclaw-channel-mock-core/src/status.ts create mode 100644 packages/openclaw-channel-mock-core/src/types.ts create mode 100644 packages/openclaw-channel-mock-core/test/bus.test.ts create mode 100644 packages/openclaw-channel-mock-core/tsconfig.build.json create mode 100644 packages/openclaw-channel-mock-core/tsconfig.json create mode 100644 packages/openclaw-discord-mock/CHANGELOG.md create mode 100644 packages/openclaw-discord-mock/LICENSE create mode 100644 packages/openclaw-discord-mock/NOTICE.md create mode 100644 packages/openclaw-discord-mock/README.md create mode 100644 packages/openclaw-discord-mock/openclaw.plugin.json create mode 100644 packages/openclaw-discord-mock/package.json create mode 100644 packages/openclaw-discord-mock/src/channel-plugin-api.ts create mode 100644 packages/openclaw-discord-mock/src/channel-setup-plugin.ts create mode 100644 packages/openclaw-discord-mock/src/index.ts create mode 100644 packages/openclaw-discord-mock/src/plugin.ts create mode 100644 packages/openclaw-discord-mock/src/runtime.ts create mode 100644 packages/openclaw-discord-mock/src/setup-entry.ts create mode 100644 packages/openclaw-discord-mock/test/plugin-actions.test.ts create mode 100644 packages/openclaw-discord-mock/tsconfig.build.json create mode 100644 packages/openclaw-discord-mock/tsconfig.json create mode 100644 packages/openclaw-qa-runner/CHANGELOG.md create mode 100644 packages/openclaw-qa-runner/Dockerfile create mode 100644 packages/openclaw-qa-runner/LICENSE create mode 100644 packages/openclaw-qa-runner/README.md create mode 100755 packages/openclaw-qa-runner/bin/cli.mjs create mode 100755 packages/openclaw-qa-runner/bin/qa.mjs create mode 100644 packages/openclaw-qa-runner/docker-compose.yml create mode 100644 packages/openclaw-qa-runner/package.json create mode 100644 packages/openclaw-qa-runner/src/bus.ts create mode 100644 packages/openclaw-qa-runner/src/cli.ts create mode 100644 packages/openclaw-qa-runner/src/context.ts create mode 100644 packages/openclaw-qa-runner/src/cost.ts create mode 100644 packages/openclaw-qa-runner/src/gateway-log.ts create mode 100644 packages/openclaw-qa-runner/src/index.ts create mode 100644 packages/openclaw-qa-runner/src/judge.ts create mode 100644 packages/openclaw-qa-runner/src/mock-cli-server.ts create mode 100644 packages/openclaw-qa-runner/src/mock-cli-shim.ts create mode 100644 packages/openclaw-qa-runner/src/report.ts create mode 100644 packages/openclaw-qa-runner/src/runner-args.ts create mode 100644 packages/openclaw-qa-runner/src/runner.ts create mode 100644 packages/openclaw-qa-runner/templates/.env.local.example create mode 100644 packages/openclaw-qa-runner/templates/docker-compose.yml create mode 100644 packages/openclaw-qa-runner/templates/openclaw.json create mode 100644 packages/openclaw-qa-runner/tsconfig.build.json create mode 100644 packages/openclaw-qa-runner/tsconfig.json create mode 100644 packages/openclaw-slack-mock/CHANGELOG.md create mode 100644 packages/openclaw-slack-mock/LICENSE create mode 100644 packages/openclaw-slack-mock/NOTICE.md create mode 100644 packages/openclaw-slack-mock/README.md create mode 100644 packages/openclaw-slack-mock/openclaw.plugin.json create mode 100644 packages/openclaw-slack-mock/package.json create mode 100644 packages/openclaw-slack-mock/src/channel-plugin-api.ts create mode 100644 packages/openclaw-slack-mock/src/channel-setup-plugin.ts create mode 100644 packages/openclaw-slack-mock/src/index.ts create mode 100644 packages/openclaw-slack-mock/src/plugin.ts create mode 100644 packages/openclaw-slack-mock/src/runtime.ts create mode 100644 packages/openclaw-slack-mock/src/setup-entry.ts create mode 100644 packages/openclaw-slack-mock/test/inbound.test.ts create mode 100644 packages/openclaw-slack-mock/test/plugin-actions.test.ts create mode 100644 packages/openclaw-slack-mock/tsconfig.build.json create mode 100644 packages/openclaw-slack-mock/tsconfig.json create mode 100644 packages/worktree-env/CHANGELOG.md diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 0000000..654c6d4 --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,8 @@ +# Changesets + +Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works +with multi-package repos, or single-package repos to help you version and publish your code. You can +find the full documentation for it [in our repository](https://github.com/changesets/changesets). + +We have a quick list of common questions to get you started engaging with this project in +[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md). diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 0000000..ec98e35 --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@3.1.4/schema.json", + "changelog": "@changesets/cli/changelog", + "commit": false, + "fixed": [], + "linked": [], + "access": "public", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "ignore": [] +} diff --git a/AGENTS.md b/AGENTS.md index 3feb244..591890d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,6 +4,22 @@ **Writing Markdown**: Do not wrap text to 80 chars; let it run freely. +## Tooling + +**Package manager**: npm workspaces (root `package.json` declares `"workspaces": ["packages/*"]`). + +**Runtime**: Node (ESM only, `"type": "module"`). + +**Language**: TypeScript with `strict: true`, `module: NodeNext`. Each package has a `tsconfig.build.json` (emits `dist/`) and a `tsconfig.json` (`noEmit`, includes `src` + `test`). + +**Linter / formatter**: Biome (`biome.json` at root). `npm run lint` / `npm run lint:fix`. + +**Test runner**: Vitest (`vitest run`). Per-package: `npm test --workspace `. All packages: `npm test`. + +**Releases**: Changesets (`.changeset/`). Author a changeset with `npm run changeset`; publish via `npm run changeset:publish`. Base branch: `main`. Default access: `public`. + +**Workspace scripts** (root): `build`, `test`, `clear`, `lint`, `lint:fix` — all fan out to packages via `npm run --workspaces --if-present`. + ## AlignFirst - Ticket ID, Commit Message, Branch Name _Ticket ID_: Format is numeric. Use the ticket ID if explicitly provided. Otherwise, deduce it from the current branch name (no confirmation needed). If the branch name is unavailable, get it via `git branch --show-current`. Only ask the user as a last resort. @@ -13,3 +29,12 @@ Commit message convention: we use conventional commit, e.g., `feat: [#123] add n Branch naming convention: `/` (with type from conventional commit, e.g., `feat/123`, `fix/123`, `refactor/123`, `chore/123`). Add `docs/code-style.md` and `docs/code-quality.md` to every plan. + +## Packages + +- `@paleo/docmap` — lightweight documentation system for AI agents and humans +- `@paleo/openclaw-channel-mock-core` — shared library for synthetic OpenClaw channel plugins (bus, actions, factories) +- `@paleo/openclaw-slack-mock` — Slack-shaped channel plugin for QA scenarios +- `@paleo/openclaw-discord-mock` — Discord-shaped channel plugin for QA scenarios +- `@paleo/openclaw-qa-runner` — Dockerised regression-test harness (bus, scenario driver, judge, Compose stack) +- `@paleo/worktree-env` — worktree-based concurrent local environment kernel diff --git a/README.md b/README.md index 92da895..0cc803a 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,3 @@ Collaborative spec/plan/AAD/review protocols. See [alignfirst-skills.md](alignfi ## Autonomous agent (experimental) We're currently working on building an AI developer with _OpenClaw_. See [autonomous-agent.md](autonomous-agent.md). - ----- - -**License:** CC0 1.0 Universal. diff --git a/autonomous-agent.md b/autonomous-agent.md index 71e75ec..df54c29 100644 --- a/autonomous-agent.md +++ b/autonomous-agent.md @@ -13,6 +13,10 @@ export ALIGNFIRST_AGENT_LOG_DIR=path/to/directory # Write input/output logs export ALIGNFIRST_AGENT_SKIP_PERMISSIONS=1 # Use --dangerously-skip-permissions instead of --permission-mode auto ``` +## OpenClaw QA toolkit + +`@paleo/openclaw-qa-runner` and three companion channel packages — a Dockerised regression-test harness that drives OpenClaw through synthetic Discord and Slack channels. See [packages/openclaw-qa-runner/README.md](packages/openclaw-qa-runner/README.md). + ### OpenClaw Playbook (experimental) The `alignfirst-coaching` skill ships a reference that teaches OpenClaw how to handle branches, worktrees, commits, PRs/MRs etc. on the project side, and organize the chat with the user through Discord/Slack threads on the user side. Still in development. diff --git a/package-lock.json b/package-lock.json index 9b0aebc..76264b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,479 @@ "packages/*" ], "devDependencies": { - "@biomejs/biome": "~2.4.15" + "@biomejs/biome": "~2.4.15", + "@changesets/cli": "~2.31.0" + } + }, + "node_modules/@agentclientprotocol/sdk": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-0.21.1.tgz", + "integrity": "sha512-ZTLH+o9QxcZDLX/9ww+W7C2iExnXFM+vD/uGFVSlR61Kzj9FaxUqBC6Rv/kwgA7qVWYUEI9c5ZNqCuO9PM4rKg==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.97.1", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.97.1.tgz", + "integrity": "sha512-wOf7AUeJPitcVpvKO4UMu63mWH5SaVipkGd7OOQJt/G6VYGlV8D2Gp9dLxOrttDJh/9gqPqdaBwDGcBevumeAg==", + "license": "MIT", + "dependencies": { + "json-schema-to-ts": "^3.1.1", + "standardwebhooks": "^1.0.0" + }, + "bin": { + "anthropic-ai-sdk": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-bedrock-runtime": { + "version": "3.1049.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.1049.0.tgz", + "integrity": "sha512-YM8b2baoRY8ul47b4amQW2VlUthLmM8DnqdlGO20LJmmmRpjnT91SaQJai3OMehA6uE0Gig88VyDCT1vEACSww==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.974.12", + "@aws-sdk/credential-provider-node": "^3.972.43", + "@aws-sdk/eventstream-handler-node": "^3.972.16", + "@aws-sdk/middleware-eventstream": "^3.972.12", + "@aws-sdk/middleware-websocket": "^3.972.20", + "@aws-sdk/token-providers": "3.1049.0", + "@aws-sdk/types": "^3.973.8", + "@smithy/core": "^3.24.2", + "@smithy/fetch-http-handler": "^5.4.2", + "@smithy/node-http-handler": "^4.7.2", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.974.12", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.12.tgz", + "integrity": "sha512-qrqgioqYFjwR6LatVNS1L2Vk++EwRIxqSQXPKNv5Ofux2D8UNgqMQ1znnMyEImXquVPTtbf71fc128pvmU6y9A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@aws-sdk/xml-builder": "^3.972.24", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/core": "^3.24.2", + "@smithy/signature-v4": "^5.4.2", + "@smithy/types": "^4.14.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.38", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.38.tgz", + "integrity": "sha512-m3WjZEgPtioMhPmwqUt+DhlTJ2i9ufR6DhfkyXojb9puEvfR+ur2U5shavu5/Cc9WHHsDCvALi6UFHgcqjhQ5w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.12", + "@aws-sdk/types": "^3.973.8", + "@smithy/core": "^3.24.2", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.40", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.40.tgz", + "integrity": "sha512-D78L/m2Dr6cJnnSvWoAudPhQmCwmJ7j6APXsPYmFpPaKfQTfCSu0rdm8j14Np+VmXF9z8Aj8HE3xFpsrwtfgeg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.12", + "@aws-sdk/types": "^3.973.8", + "@smithy/core": "^3.24.2", + "@smithy/fetch-http-handler": "^5.4.2", + "@smithy/node-http-handler": "^4.7.2", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.972.42", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.42.tgz", + "integrity": "sha512-Mu5ESvFXeinafVM8jTIvRqcvK2Ehj4kz3auT39yUcHwu1Vfxo6xRlmUafdKLW4tusjAJukQwK09sCSMgOm7OKg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.12", + "@aws-sdk/credential-provider-env": "^3.972.38", + "@aws-sdk/credential-provider-http": "^3.972.40", + "@aws-sdk/credential-provider-login": "^3.972.42", + "@aws-sdk/credential-provider-process": "^3.972.38", + "@aws-sdk/credential-provider-sso": "^3.972.42", + "@aws-sdk/credential-provider-web-identity": "^3.972.42", + "@aws-sdk/nested-clients": "^3.997.10", + "@aws-sdk/types": "^3.973.8", + "@smithy/core": "^3.24.2", + "@smithy/credential-provider-imds": "^4.3.2", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.42", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.42.tgz", + "integrity": "sha512-O6WkZga3kf0yqyJYd1dbeJqVhEgJx/x1UaLgtbR+XuL/YP+K5y6QTxQKL7ka9z3jnQASESKGAPnRyt4D5hQrxA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.12", + "@aws-sdk/nested-clients": "^3.997.10", + "@aws-sdk/types": "^3.973.8", + "@smithy/core": "^3.24.2", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.43", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.43.tgz", + "integrity": "sha512-D/DJmbrWRP5BXEO3FH+ar4el+2n6OlGofiud7dQun2jES+AQEJjczenp1jBb4MBN7CpGpS8nsWGQLtuzc9tQbA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.38", + "@aws-sdk/credential-provider-http": "^3.972.40", + "@aws-sdk/credential-provider-ini": "^3.972.42", + "@aws-sdk/credential-provider-process": "^3.972.38", + "@aws-sdk/credential-provider-sso": "^3.972.42", + "@aws-sdk/credential-provider-web-identity": "^3.972.42", + "@aws-sdk/types": "^3.973.8", + "@smithy/core": "^3.24.2", + "@smithy/credential-provider-imds": "^4.3.2", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.38", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.38.tgz", + "integrity": "sha512-EnbYVajGgbkb24s0K1eo4VNAPV5mHIET7LSvirTaFCwkfrfaOJxtSE+wY/tJdKDS21cEYkZs2ruCaAm+W4iblg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.12", + "@aws-sdk/types": "^3.973.8", + "@smithy/core": "^3.24.2", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.972.42", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.42.tgz", + "integrity": "sha512-RVV/9NbFwI8ZHEH5dn39lGyFmSbSVj1+orZdr6QsOe1mW9DCglmlen0cFaNZmCcqkqc7erNRHNBduxbeZuHAnw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.12", + "@aws-sdk/nested-clients": "^3.997.10", + "@aws-sdk/token-providers": "3.1049.0", + "@aws-sdk/types": "^3.973.8", + "@smithy/core": "^3.24.2", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.42", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.42.tgz", + "integrity": "sha512-/67fXX0ddllD4u2Nujc5PvT4byHgpMUfz6+RxIKi/0nFIckeorm7JvXgzBuDyVKw0s58EbofmETDWUf9vTEuHQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.12", + "@aws-sdk/nested-clients": "^3.997.10", + "@aws-sdk/types": "^3.973.8", + "@smithy/core": "^3.24.2", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-handler-node": { + "version": "3.972.16", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.16.tgz", + "integrity": "sha512-yedpPgKftqjU5SlPFHfqWpOw6xSCRieWRG1euWOlXn4WJxt2VX92VprCa2PpSOXjVCAeK6dTjW9eJRXVig9yGA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@smithy/core": "^3.24.2", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-eventstream": { + "version": "3.972.12", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.12.tgz", + "integrity": "sha512-tHTHHCHNrq6XklQvlzHBDJG4Iuhh7NVPRdtmvP+nHFA+5sxPlIDzlAHHgfoYHGvT3NXP1yVP/L5c3opUn6T3Qg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@smithy/core": "^3.24.2", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-websocket": { + "version": "3.972.20", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.20.tgz", + "integrity": "sha512-LM6P0i+Lu6pi25oNw2nqxjRxiEOtLgPB7xIvHfa+FxHTRLg8wcgqu3qg2COl4QaT7Es2yCxYdeRLVYazKAwL8g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.12", + "@aws-sdk/types": "^3.973.8", + "@smithy/core": "^3.24.2", + "@smithy/fetch-http-handler": "^5.4.2", + "@smithy/signature-v4": "^5.4.2", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.997.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.10.tgz", + "integrity": "sha512-FtQ/Bt327peZJuyo4WZSOLVUTw9ujRxntepiC7L65FxA2P82Xlq0g14T22BuqBUeMjDoxa9nvwiMHjLIfP3eUg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.974.12", + "@aws-sdk/signature-v4-multi-region": "^3.996.27", + "@aws-sdk/types": "^3.973.8", + "@smithy/core": "^3.24.2", + "@smithy/fetch-http-handler": "^5.4.2", + "@smithy/node-http-handler": "^4.7.2", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.996.27", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.27.tgz", + "integrity": "sha512-0Phbz4t6HI3D3skxvG2uI+VWU034/nSIw1T8d+FPzzQG9EQTrw94o9mOKO2Gv3n3Oc8P7JD7RAUxkoneLWv5Eg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@smithy/core": "^3.24.2", + "@smithy/signature-v4": "^5.4.2", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.1049.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1049.0.tgz", + "integrity": "sha512-r7+d0lQMTHKypkmaF5jRTBYLYHCUHzt3gaVoN9SidLhQeWhCmHk3AKrboDTpPF5b7Pt7vKu3+oeMjznM2Eu1ow==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.12", + "@aws-sdk/nested-clients": "^3.997.10", + "@aws-sdk/types": "^3.973.8", + "@smithy/core": "^3.24.2", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.973.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.8.tgz", + "integrity": "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.965.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.5.tgz", + "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.972.24", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.24.tgz", + "integrity": "sha512-V8z5YcDPfsvzrBlj0xR1vhRtocblhYbqdreCJB/voGd4Sr5zjNAeWxexbnqVtskTJe0vFb5KMqbSL++ePl+zRw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@nodable/entities": "2.1.0", + "@smithy/types": "^4.14.1", + "fast-xml-parser": "5.7.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.4.tgz", + "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, "node_modules/@biomejs/biome": { @@ -187,1235 +659,7328 @@ "node": ">=14.21.3" } }, - "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "node_modules/@borewit/text-codec": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", + "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", "dev": true, "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "node_modules/@changesets/apply-release-plan": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-7.1.1.tgz", + "integrity": "sha512-9qPCm/rLx/xoOFXIHGB229+4GOL76S4MC+7tyOuTsR6+1jYlfFDQORdvwR5hDA6y4FL2BPt3qpbcQIS+dW85LA==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "tslib": "^2.4.0" + "@changesets/config": "^3.1.4", + "@changesets/get-version-range-type": "^0.4.0", + "@changesets/git": "^3.0.4", + "@changesets/should-skip-package": "^0.1.2", + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3", + "detect-indent": "^6.0.0", + "fs-extra": "^7.0.1", + "lodash.startcase": "^4.4.0", + "outdent": "^0.5.0", + "prettier": "^2.7.1", + "resolve-from": "^5.0.0", + "semver": "^7.5.3" } }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "node_modules/@changesets/assemble-release-plan": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-6.0.10.tgz", + "integrity": "sha512-rSDcqdJ9KbVyjpBIuCidhvZNIiVt1XaIYp73ycVQRIA5n/j6wQaEk0ChRLMUQ1vkxZe51PTQ9OIhbg6HQMW45A==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "tslib": "^2.4.0" + "@changesets/errors": "^0.2.0", + "@changesets/get-dependents-graph": "^2.1.4", + "@changesets/should-skip-package": "^0.1.2", + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3", + "semver": "^7.5.3" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "node_modules/@changesets/changelog-git": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@changesets/changelog-git/-/changelog-git-0.2.1.tgz", + "integrity": "sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@changesets/types": "^6.1.0" + } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "node_modules/@changesets/cli": { + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/@changesets/cli/-/cli-2.31.0.tgz", + "integrity": "sha512-AhI4enNTgHu2IZr6K4WZyf0EPch4XVMn1yOMFmCD9gsfBGqMYaHXls5HyDv6/CL5axVQABz68eG30eCtbr2wFg==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.1" + "@changesets/apply-release-plan": "^7.1.1", + "@changesets/assemble-release-plan": "^6.0.10", + "@changesets/changelog-git": "^0.2.1", + "@changesets/config": "^3.1.4", + "@changesets/errors": "^0.2.0", + "@changesets/get-dependents-graph": "^2.1.4", + "@changesets/get-release-plan": "^4.0.16", + "@changesets/git": "^3.0.4", + "@changesets/logger": "^0.1.1", + "@changesets/pre": "^2.0.2", + "@changesets/read": "^0.6.7", + "@changesets/should-skip-package": "^0.1.2", + "@changesets/types": "^6.1.0", + "@changesets/write": "^0.4.0", + "@inquirer/external-editor": "^1.0.2", + "@manypkg/get-packages": "^1.1.3", + "ansi-colors": "^4.1.3", + "enquirer": "^2.4.1", + "fs-extra": "^7.0.1", + "mri": "^1.2.0", + "package-manager-detector": "^0.2.0", + "picocolors": "^1.1.0", + "resolve-from": "^5.0.0", + "semver": "^7.5.3", + "spawndamnit": "^3.0.1", + "term-size": "^2.1.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" + "bin": { + "changeset": "bin.js" } }, - "node_modules/@oxc-project/types": { - "version": "0.130.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.130.0.tgz", - "integrity": "sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==", + "node_modules/@changesets/config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@changesets/config/-/config-3.1.4.tgz", + "integrity": "sha512-pf0bvD/v6WI2cRlZ6hzpjtZdSlXDXMAJ+Iz7xfFzV4ZxJ8OGGAON+1qYc99ZPrijnt4xp3VGG7eNvAOGS24V1Q==", "dev": true, "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" + "dependencies": { + "@changesets/errors": "^0.2.0", + "@changesets/get-dependents-graph": "^2.1.4", + "@changesets/logger": "^0.1.1", + "@changesets/should-skip-package": "^0.1.2", + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3", + "fs-extra": "^7.0.1", + "micromatch": "^4.0.8" } }, - "node_modules/@paleo/docmap": { - "resolved": "packages/docmap", - "link": true - }, - "node_modules/@paleo/worktree-env": { - "resolved": "packages/worktree-env", - "link": true - }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.1.tgz", - "integrity": "sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg==", - "cpu": [ - "arm64" - ], + "node_modules/@changesets/errors": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@changesets/errors/-/errors-0.2.0.tgz", + "integrity": "sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==", "dev": true, "license": "MIT", - "optional": true, - "os": [ + "dependencies": { + "extendable-error": "^0.1.5" + } + }, + "node_modules/@changesets/get-dependents-graph": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@changesets/get-dependents-graph/-/get-dependents-graph-2.1.4.tgz", + "integrity": "sha512-ZsS00x6WvmHq3sQv8oCMwL0f/z3wbXCVuSVTJwCnnmbC/iBdNJGFx1EcbMG4PC6sXRyH69liM4A2WKXzn/kRPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3", + "picocolors": "^1.1.0", + "semver": "^7.5.3" + } + }, + "node_modules/@changesets/get-release-plan": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-4.0.16.tgz", + "integrity": "sha512-2K5Om6CrMPm45rtvckfzWo7e9jOVCKLCnXia5eUPaURH7/LWzri7pK1TycdzAuAtehLkW7VPbWLCSExTHmiI6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/assemble-release-plan": "^6.0.10", + "@changesets/config": "^3.1.4", + "@changesets/pre": "^2.0.2", + "@changesets/read": "^0.6.7", + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3" + } + }, + "node_modules/@changesets/get-version-range-type": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@changesets/get-version-range-type/-/get-version-range-type-0.4.0.tgz", + "integrity": "sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@changesets/git": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@changesets/git/-/git-3.0.4.tgz", + "integrity": "sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/errors": "^0.2.0", + "@manypkg/get-packages": "^1.1.3", + "is-subdir": "^1.1.1", + "micromatch": "^4.0.8", + "spawndamnit": "^3.0.1" + } + }, + "node_modules/@changesets/logger": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@changesets/logger/-/logger-0.1.1.tgz", + "integrity": "sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.1.0" + } + }, + "node_modules/@changesets/parse": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@changesets/parse/-/parse-0.4.3.tgz", + "integrity": "sha512-ZDmNc53+dXdWEv7fqIUSgRQOLYoUom5Z40gmLgmATmYR9NbL6FJJHwakcCpzaeCy+1D0m0n7mT4jj2B/MQPl7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^6.1.0", + "js-yaml": "^4.1.1" + } + }, + "node_modules/@changesets/pre": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@changesets/pre/-/pre-2.0.2.tgz", + "integrity": "sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/errors": "^0.2.0", + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3", + "fs-extra": "^7.0.1" + } + }, + "node_modules/@changesets/read": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/@changesets/read/-/read-0.6.7.tgz", + "integrity": "sha512-D1G4AUYGrBEk8vj8MGwf75k9GpN6XL3wg8i42P2jZZwFLXnlr2Pn7r9yuQNbaMCarP7ZQWNJbV6XLeysAIMhTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/git": "^3.0.4", + "@changesets/logger": "^0.1.1", + "@changesets/parse": "^0.4.3", + "@changesets/types": "^6.1.0", + "fs-extra": "^7.0.1", + "p-filter": "^2.1.0", + "picocolors": "^1.1.0" + } + }, + "node_modules/@changesets/should-skip-package": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@changesets/should-skip-package/-/should-skip-package-0.1.2.tgz", + "integrity": "sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3" + } + }, + "node_modules/@changesets/types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-6.1.0.tgz", + "integrity": "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@changesets/write": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@changesets/write/-/write-0.4.0.tgz", + "integrity": "sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^6.1.0", + "fs-extra": "^7.0.1", + "human-id": "^4.1.1", + "prettier": "^2.7.1" + } + }, + "node_modules/@clack/core": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.3.1.tgz", + "integrity": "sha512-fT1qHVGAag4IEkrupZ6lRRbNCs1vS9P01KB/sG8zKgvUztbYtFBtQpjSITNwooDZ83tpsPzP0mRNs1/KVszCRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@clack/prompts": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.4.0.tgz", + "integrity": "sha512-S0My7XPGIgpRWMDG8uRqalbgT+a6FmCUdOW+HaIOVVpUPHOb7RrpvjTjiODadKp06fsrVDJZlIzc6yCTp4AnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@clack/core": "1.3.1", + "fast-string-width": "^3.0.2", + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@earendil-works/pi-agent-core": { + "version": "0.75.1", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.75.1.tgz", + "integrity": "sha512-JVpX/Zle/enBzEM6he9sE0ASMo8Yhm8q7nOuPQjR/BXhkTBUevrNz7wtTV8VFvgjyhsXzbAsNCP5A4LiCcDx/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@earendil-works/pi-ai": "^0.75.1", + "ignore": "^7.0.5", + "typebox": "^1.1.24", + "yaml": "^2.8.2" + }, + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/@earendil-works/pi-ai": { + "version": "0.75.1", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.75.1.tgz", + "integrity": "sha512-/bhCWS2R+qHLBDnN+d1t1QRUxtZk7sZpMcrlexPq3W++3bJ0Df0GjhM2FToTubhoCsjOBdBOuRYcV8FNPfRUVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@anthropic-ai/sdk": "^0.91.1", + "@aws-sdk/client-bedrock-runtime": "^3.1030.0", + "@google/genai": "^1.40.0", + "@mistralai/mistralai": "^2.2.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "openai": "6.26.0", + "partial-json": "^0.1.7", + "typebox": "^1.1.24" + }, + "bin": { + "pi-ai": "dist/cli.js" + }, + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/@earendil-works/pi-ai/node_modules/@anthropic-ai/sdk": { + "version": "0.91.1", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.91.1.tgz", + "integrity": "sha512-LAmu761tSN9r66ixvmciswUj/ZC+1Q4iAfpedTfSVLeswRwnY3n2Nb6Tsk+cLPP28aLOPWeMgIuTuCcMC6W/iw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-schema-to-ts": "^3.1.1" + }, + "bin": { + "anthropic-ai-sdk": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@earendil-works/pi-ai/node_modules/@google/genai": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.52.0.tgz", + "integrity": "sha512-gwSvbpiN/17O9TbsqSsE/OzZcpv5Fo4RQjdngGgogtuB9RsyJ8ZHhX5KjHj1bp5N9snN2eK8LDGXSaWW2hof8Q==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^10.3.0", + "p-retry": "^4.6.2", + "protobufjs": "^7.5.4", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.25.2" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } + } + }, + "node_modules/@earendil-works/pi-ai/node_modules/openai": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.26.0.tgz", + "integrity": "sha512-zd23dbWTjiJ6sSAX6s0HrCZi41JwTA1bQVs0wLQPZ2/5o2gxOJA5wh7yOAUgwYybfhDXyhwlpeQf7Mlgx8EOCA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/@earendil-works/pi-coding-agent": { + "version": "0.75.1", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-coding-agent/-/pi-coding-agent-0.75.1.tgz", + "integrity": "sha512-QMbmv8lFQ8P98kpuMc/z1ATTq7t0lQ+Bo3GLiOKQ/HonO34n4E1+395FCqlmG8zJEhiMp4yqVTzlj7BALQMlqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@earendil-works/pi-agent-core": "^0.75.1", + "@earendil-works/pi-ai": "^0.75.1", + "@earendil-works/pi-tui": "^0.75.1", + "@silvia-odwyer/photon-node": "^0.3.4", + "chalk": "^5.5.0", + "diff": "^8.0.2", + "glob": "^13.0.1", + "highlight.js": "^10.7.3", + "hosted-git-info": "^9.0.2", + "ignore": "^7.0.5", + "jiti": "^2.7.0", + "minimatch": "^10.2.3", + "proper-lockfile": "^4.1.2", + "typebox": "^1.1.24", + "undici": "^8.3.0", + "yaml": "^2.8.2" + }, + "bin": { + "pi": "dist/cli.js" + }, + "engines": { + "node": ">=22.19.0" + }, + "optionalDependencies": { + "@mariozechner/clipboard": "^0.3.6" + } + }, + "node_modules/@earendil-works/pi-tui": { + "version": "0.75.1", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.75.1.tgz", + "integrity": "sha512-IFDSvCXcXMoIxFKxdhqc7ybX8p86KpdxoTUTYEq3FHilMFkBqlXqZD0jZBitqxStBjjMkAlhjS1bKS0IOXSpsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.0", + "marked": "^15.0.12" + }, + "engines": { + "node": ">=22.19.0" + }, + "optionalDependencies": { + "koffi": "^2.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@google/genai": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-2.3.0.tgz", + "integrity": "sha512-rXDhXUBj31gZafcwQFbXvt8jMrMxZoK7ECjQpk88UfA/OkZls3PtZDprT9lM3jjqRtwRjQoNLoPoNq6MlV8qLw==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^10.3.0", + "p-retry": "^4.6.2", + "protobufjs": "^7.5.4", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.25.2" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } + } + }, + "node_modules/@grammyjs/runner": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@grammyjs/runner/-/runner-2.0.3.tgz", + "integrity": "sha512-nckmTs1dPWfVQteK9cxqxzE+0m1VRvluLWB8UgFzsjg62w3qthPJt0TYtJBEdG7OedvfQq4vnFAyE6iaMkR42A==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0" + }, + "engines": { + "node": ">=12.20.0 || >=14.13.1" + }, + "peerDependencies": { + "grammy": "^1.13.1" + } + }, + "node_modules/@grammyjs/transformer-throttler": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@grammyjs/transformer-throttler/-/transformer-throttler-1.2.1.tgz", + "integrity": "sha512-CpWB0F3rJdUiKsq7826QhQsxbZi4wqfz1ccKX+fr+AOC+o8K7ZvS+wqX0suSu1QCsyUq2MDpNiKhyL2ZOJUS4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "bottleneck": "^2.0.0" + }, + "engines": { + "node": "^12.20.0 || >=14.13.1" + }, + "peerDependencies": { + "grammy": "^1.0.0" + } + }, + "node_modules/@grammyjs/types": { + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/@grammyjs/types/-/types-3.26.0.tgz", + "integrity": "sha512-jlnyfxfev/2o68HlvAGRocAXgdPPX5QabG7jZlbqC2r9DZyWBfzTlg+nu3O3Fy4EhgLWu28hZ/8wr7DsNamP9A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@homebridge/ciao": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/@homebridge/ciao/-/ciao-1.3.8.tgz", + "integrity": "sha512-lNhpCsZVbdbjz2trFjQdzQ3cUIMZQMIMksi7wd3ntTIYgdaGLqT1Ms97DfVIJYHzRuduf56ISvgU8RRLTpK/ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "fast-deep-equal": "^3.1.3", + "source-map-support": "^0.5.21", + "tslib": "^2.8.1" + }, + "bin": { + "ciao-bcs": "lib/bonjour-conformance-testing.js" + } + }, + "node_modules/@hono/node-server": { + "version": "1.19.14", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz", + "integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@lydell/node-pty": { + "version": "1.2.0-beta.12", + "resolved": "https://registry.npmjs.org/@lydell/node-pty/-/node-pty-1.2.0-beta.12.tgz", + "integrity": "sha512-qIK890UwPupoj07osVvgOIa++1mxeHbcGry4PKRHhNVNs81V2SCG34eJr46GybiOmBtc8Sj5PB1/GGM5PL549g==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "@lydell/node-pty-darwin-arm64": "1.2.0-beta.12", + "@lydell/node-pty-darwin-x64": "1.2.0-beta.12", + "@lydell/node-pty-linux-arm64": "1.2.0-beta.12", + "@lydell/node-pty-linux-x64": "1.2.0-beta.12", + "@lydell/node-pty-win32-arm64": "1.2.0-beta.12", + "@lydell/node-pty-win32-x64": "1.2.0-beta.12" + } + }, + "node_modules/@lydell/node-pty-darwin-arm64": { + "version": "1.2.0-beta.12", + "resolved": "https://registry.npmjs.org/@lydell/node-pty-darwin-arm64/-/node-pty-darwin-arm64-1.2.0-beta.12.tgz", + "integrity": "sha512-tqaifcY9Cr41SblO1+FLzh8oxxtkNhuW9Dhl22lKme9BreYvKvxEZcdPIXTuqkJc5tagOEC4QHShKmJjLyLXLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lydell/node-pty-darwin-x64": { + "version": "1.2.0-beta.12", + "resolved": "https://registry.npmjs.org/@lydell/node-pty-darwin-x64/-/node-pty-darwin-x64-1.2.0-beta.12.tgz", + "integrity": "sha512-4LrS5pCJwqHKDVf1zS2gyNV0m4hKAXch+XZNhbZ6LY8uwVL8BhchzQBO40Os5anuRxRCWzHpw4Sp64Ie8q7E4Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lydell/node-pty-linux-arm64": { + "version": "1.2.0-beta.12", + "resolved": "https://registry.npmjs.org/@lydell/node-pty-linux-arm64/-/node-pty-linux-arm64-1.2.0-beta.12.tgz", + "integrity": "sha512-Sx+A71x5BDGHt9ansfrtGxwq2VFVDWvJUAdlUL0Hv0qeiJUfts+hgopx+CgT4PSwahKjdEgtu0+FAfY9rICKRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lydell/node-pty-linux-x64": { + "version": "1.2.0-beta.12", + "resolved": "https://registry.npmjs.org/@lydell/node-pty-linux-x64/-/node-pty-linux-x64-1.2.0-beta.12.tgz", + "integrity": "sha512-bJzs94njofYhGg/UDqW1nj0dtvvu+2OvxMY+RlLS1T17VgcktKoIR6PuenTwE5HJ/D6StCPADmXcT0nNsCKmIQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lydell/node-pty-win32-arm64": { + "version": "1.2.0-beta.12", + "resolved": "https://registry.npmjs.org/@lydell/node-pty-win32-arm64/-/node-pty-win32-arm64-1.2.0-beta.12.tgz", + "integrity": "sha512-p7POgjVEiFaBC3/y+AKuV1FzePCsJ6HmZDv2XK+jBZSfwP8+uBAw181ZiKYN1YuRa/XpmBGaWezcI8hZkbW++g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@lydell/node-pty-win32-x64": { + "version": "1.2.0-beta.12", + "resolved": "https://registry.npmjs.org/@lydell/node-pty-win32-x64/-/node-pty-win32-x64-1.2.0-beta.12.tgz", + "integrity": "sha512-IDFa00g7qUDGUYgByrUBJtC+mOjYVt/8KYyWivCg5JjGOHbBUACUQZLl0jTWmnr+tld/UyTpX90a2PY6oTVtRw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@manypkg/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@manypkg/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.5.5", + "@types/node": "^12.7.1", + "find-up": "^4.1.0", + "fs-extra": "^8.1.0" + } + }, + "node_modules/@manypkg/find-root/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@manypkg/find-root/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@manypkg/get-packages": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@manypkg/get-packages/-/get-packages-1.1.3.tgz", + "integrity": "sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.5.5", + "@changesets/types": "^4.0.1", + "@manypkg/find-root": "^1.1.0", + "fs-extra": "^8.1.0", + "globby": "^11.0.0", + "read-yaml-file": "^1.1.0" + } + }, + "node_modules/@manypkg/get-packages/node_modules/@changesets/types": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-4.1.0.tgz", + "integrity": "sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@manypkg/get-packages/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@mariozechner/clipboard": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard/-/clipboard-0.3.6.tgz", + "integrity": "sha512-MXdtr+6+ntlIVHdrZYuZNQydu6o8yZswFJ2Ln81j2O/Y9B/LDHvEaIm95xWNPkjGTWriSOeLnQJRFs6dYb60bg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@mariozechner/clipboard-darwin-arm64": "0.3.6", + "@mariozechner/clipboard-darwin-universal": "0.3.6", + "@mariozechner/clipboard-darwin-x64": "0.3.6", + "@mariozechner/clipboard-linux-arm64-gnu": "0.3.6", + "@mariozechner/clipboard-linux-arm64-musl": "0.3.6", + "@mariozechner/clipboard-linux-riscv64-gnu": "0.3.6", + "@mariozechner/clipboard-linux-x64-gnu": "0.3.6", + "@mariozechner/clipboard-linux-x64-musl": "0.3.6", + "@mariozechner/clipboard-win32-arm64-msvc": "0.3.6", + "@mariozechner/clipboard-win32-x64-msvc": "0.3.6" + } + }, + "node_modules/@mariozechner/clipboard-darwin-arm64": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-arm64/-/clipboard-darwin-arm64-0.3.6.tgz", + "integrity": "sha512-HjaisYCAbHi/1+N1yDAQHc8ZXGffufIUT5NSOSVR3f3AuMDusxTtnbK8tZ7JFDkShua1oNGZoNwQHsc8MPtE0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-darwin-universal": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-universal/-/clipboard-darwin-universal-0.3.6.tgz", + "integrity": "sha512-8BWtPjOtJOJoykml3w0fx0zRrfWP31mXrJwfoA7xzNprkZw1uolCNfgmjDiVBseoKjp16EGITz7bN+61qn8dWA==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-darwin-x64": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-x64/-/clipboard-darwin-x64-0.3.6.tgz", + "integrity": "sha512-p9syiZD1kU4I+1ya7f7g+zD1GiUvR8fdlRlNmgsZNWlyjtc8rlV2EjTLd/35x1LsdBq020GVvtzp0ZmPgBI09Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-arm64-gnu": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-arm64-gnu/-/clipboard-linux-arm64-gnu-0.3.6.tgz", + "integrity": "sha512-5JFf5rGofrm+V29HNF+wLthXphHdQpMbKDUYJ5tML6/Z5DLlLOV/9Ak4kDPtYyZ+Dzf+kAusE0VsFg4+tfP1IA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-arm64-musl": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-arm64-musl/-/clipboard-linux-arm64-musl-0.3.6.tgz", + "integrity": "sha512-JlVjxxw0GbGC0djXYWRIqyteO3J1KZ/QG3udlEFaOD5TLOM1FnmXXAPDQBqr+aBVr720ef9K00dirYnJ0LDCtw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-riscv64-gnu": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-riscv64-gnu/-/clipboard-linux-riscv64-gnu-0.3.6.tgz", + "integrity": "sha512-4t8BUi5zZ+L77otFQVnVSlaTyAX4TVk9EqQm4syMrEQp96trFEHEwwNHcNEBGzYv5+K7mxay50TthYkz47OWzQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-x64-gnu": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-gnu/-/clipboard-linux-x64-gnu-0.3.6.tgz", + "integrity": "sha512-trtPwcNLW37irwQCJLtCxLw757jjJZk3TSnY/MU9bhtWtA3K9b/eLW0e4RGhUXDoFRds9opNWWaUDuFLa8dm0w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-linux-x64-musl": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-musl/-/clipboard-linux-x64-musl-0.3.6.tgz", + "integrity": "sha512-WfnzIvOCCWQiN0MmltCEo6cLceUDbYe+I7xyFZjaps5A+2Op/M2CY7Rey+C4ucQhrvmpoHmTSFgY9ODWk7snoA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-win32-arm64-msvc": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-win32-arm64-msvc/-/clipboard-win32-arm64-msvc-0.3.6.tgz", + "integrity": "sha512-+8+1aHYsBPUjmW3otmWlg+Hijt0iJvoBBs5e0mxFeUd4gDaKMB8Bn6x7c6KVtscg7E5j5NFXnwQqNSIAO4p8zQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mariozechner/clipboard-win32-x64-msvc": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-win32-x64-msvc/-/clipboard-win32-x64-msvc-0.3.6.tgz", + "integrity": "sha512-S4xfPmERC8ZkiLHe3vekZCjdDwNEETCuvCgQK2kP6/TnvmUkq1y2Pk+DjM4t8uh9KMX9bH4zs5ePcKa8GTXmfg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mistralai/mistralai": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-2.2.1.tgz", + "integrity": "sha512-uKU8CZmL2RzYKmplsU01hii4p3pe4HqJefpWNRWXm1Tcm0Sm4xXfwSLIy4k7ZCPlbETCGcp69E7hZs+WOJ5itQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "ws": "^8.18.0", + "zod": "^3.25.0 || ^4.0.0", + "zod-to-json-schema": "^3.25.0" + } + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", + "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@mozilla/readability": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@mozilla/readability/-/readability-0.6.0.tgz", + "integrity": "sha512-juG5VWh4qAivzTAeMzvY9xs9HY5rAcr2E4I7tiSSCokRFi7XIZCAu92ZkSTsIj1OPceCifL3cpfteP3pDT9/QQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@napi-rs/canvas": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.100.tgz", + "integrity": "sha512-xglYA6q3XO5P3BNJYxVZ1IV7DLVjp1Py6nwag88YntrS+3vKHyYcMqXVS4ZztJmwz2uGvz1FWhI/4LgbR5uQDA==", + "dev": true, + "license": "MIT", + "optional": true, + "workspaces": [ + "e2e/*" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/canvas-android-arm64": "0.1.100", + "@napi-rs/canvas-darwin-arm64": "0.1.100", + "@napi-rs/canvas-darwin-x64": "0.1.100", + "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.100", + "@napi-rs/canvas-linux-arm64-gnu": "0.1.100", + "@napi-rs/canvas-linux-arm64-musl": "0.1.100", + "@napi-rs/canvas-linux-riscv64-gnu": "0.1.100", + "@napi-rs/canvas-linux-x64-gnu": "0.1.100", + "@napi-rs/canvas-linux-x64-musl": "0.1.100", + "@napi-rs/canvas-win32-arm64-msvc": "0.1.100", + "@napi-rs/canvas-win32-x64-msvc": "0.1.100" + } + }, + "node_modules/@napi-rs/canvas-android-arm64": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.100.tgz", + "integrity": "sha512-hjhCKhntPv9+t4ckHymdx0phYNcVW+GKQR6Lzw2zE+pOVjOplSmtx9nNNknTjbEDLcuLZqA1y8ufKg1XfgftzQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-darwin-arm64": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.100.tgz", + "integrity": "sha512-2PcswRaC7Ly645DGt88///zuFDhJxJYdKAs1uU3mfk1atYkXufgcgLfBpk6Tm12nCQBaNt1wpybuPZ4qOhTo8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-darwin-x64": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.100.tgz", + "integrity": "sha512-ePNZtj7pNIva/siZMg+HmbeozkIjqUIYdoymH8HaA3qK7LfzFN4WMBM8G6HQ9ZC+H3+Dnn5pqtiXpgLykaPOhw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.100.tgz", + "integrity": "sha512-d5cDB48oWFGU8/XPhUOFAlySgb/VAu7D+s8fi55K1Pcfg8aPplHWqMgibhVLU8ky7Pyg/fuiVLz4Nf3JrSTuUA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-gnu": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.100.tgz", + "integrity": "sha512-rDxgxRu69RvDlX/bh9o22DxLsGr8EqsNgotL9+RwQE1S0b0cqeatqsw6aW45mukm0B42DIAaAacKaYQ8cqS1nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-musl": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.100.tgz", + "integrity": "sha512-K3mDW66N+xT2/V439u1alFANiBUjdEx2gLiNYnCmUsva5jZMxWTjafBYwTzYK+EMFMHrUoabuU+T1BIP5CgbYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.100.tgz", + "integrity": "sha512-mooqUBTIsccZpnoQC4NgrC1v6C1vof39etLNMnBwCY+p0gajWJvAHLGQ6g/gGyS5YrpDW+GefSN4+Cvcr08UWw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-gnu": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.100.tgz", + "integrity": "sha512-1eCvkDCazm7FFhsT7DfGOdSaHgZVK3bt/dSBl5EWHOWmnz+I7j8tPseJqqD81NF+MH21jKUK4wQSDjN0mdhnTg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-musl": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.100.tgz", + "integrity": "sha512-20arT6lnI19S68qNlii73TSEDbECNgzMz2EpldC1V3mZFuRkeujXkcebRk0LRJe9SEUAooYiLokfMViY8IX7yA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-win32-arm64-msvc": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.100.tgz", + "integrity": "sha512-DZFFT1wIAg37LJw37yhMRFfjATd3vTQzjZ1Yki8u2vhO6Hi5VE6BVaGQ1aaDu7xb4iMErz+9EOwjpS7xcxFeBw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-win32-x64-msvc": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.100.tgz", + "integrity": "sha512-MyT1j3mHC2+Lu4pBi9mKyMJhtP6U7k7EldY7sj/uS5gJA65gTXt8MefJQXLJo5d/vZbuWmfxzkEUNc/urV3pHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@nodable/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@openclaw/fs-safe": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@openclaw/fs-safe/-/fs-safe-0.2.4.tgz", + "integrity": "sha512-Fo3WTQhxu0asD/rZqIKBqhX6fuZfjyHxSW5yTKfcRx+D9BRAcz0AGoVh+3ur/4XRvZkvsh3Ud8XTw006yRYLgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.11" + }, + "optionalDependencies": { + "jszip": "^3.10.1", + "tar": "7.5.13" + } + }, + "node_modules/@openclaw/fs-safe/node_modules/tar": { + "version": "7.5.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.13.tgz", + "integrity": "sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==", + "dev": true, + "license": "BlueOak-1.0.0", + "optional": true, + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@openclaw/proxyline": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@openclaw/proxyline/-/proxyline-0.3.3.tgz", + "integrity": "sha512-sftHnW69NHQqLjCxBTvQ8f/eQl+peZ5pHCBQtuTWBbeuYRHZ0/GXVTmw/O/YKsShMbqPWhJB0UYtPPdvCUSS8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.19.0" + }, + "peerDependencies": { + "undici": ">=8.3.0 <9" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.130.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.130.0.tgz", + "integrity": "sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@paleo/docmap": { + "resolved": "packages/docmap", + "link": true + }, + "node_modules/@paleo/openclaw-channel-mock-core": { + "resolved": "packages/openclaw-channel-mock-core", + "link": true + }, + "node_modules/@paleo/openclaw-discord-mock": { + "resolved": "packages/openclaw-discord-mock", + "link": true + }, + "node_modules/@paleo/openclaw-qa-runner": { + "resolved": "packages/openclaw-qa-runner", + "link": true + }, + "node_modules/@paleo/openclaw-slack-mock": { + "resolved": "packages/openclaw-slack-mock", + "link": true + }, + "node_modules/@paleo/worktree-env": { + "resolved": "packages/worktree-env", + "link": true + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.2.tgz", + "integrity": "sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", + "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.1.tgz", + "integrity": "sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.1.tgz", + "integrity": "sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.1.tgz", + "integrity": "sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.1.tgz", + "integrity": "sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.1.tgz", + "integrity": "sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.1.tgz", + "integrity": "sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.1.tgz", + "integrity": "sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.1.tgz", + "integrity": "sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.1.tgz", + "integrity": "sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.1.tgz", + "integrity": "sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.1.tgz", + "integrity": "sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.1.tgz", + "integrity": "sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.1.tgz", + "integrity": "sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.1.tgz", + "integrity": "sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.1.tgz", + "integrity": "sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@silvia-odwyer/photon-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@silvia-odwyer/photon-node/-/photon-node-0.3.4.tgz", + "integrity": "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@smithy/core": { + "version": "3.24.3", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.3.tgz", + "integrity": "sha512-Ep/7tPamGY8mgESE3LyLKtxJyy6U52WWAqr/3wial47Sj4u3PiIF73AOGI27UyLy9duTkhZbgzodOfLV4TduZg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.3.3.tgz", + "integrity": "sha512-I2Bti0DKFo2IJyN28ijCsx51BAumEYR4/1yZ1FXyBygy9MqbnMqCev4JPth/MbpRfBSRAX35hITSnAdJRo1u5w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.4.3.tgz", + "integrity": "sha512-F+DRf8IJazRJgYog2A/yJK7eYVc0rqTlRzO+5ZxjJd4WkZoKz0IJRncf7G6t1pdVT3kryJcwuTFhN1c5m6N47A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.7.3.tgz", + "integrity": "sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.4.3.tgz", + "integrity": "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.2.tgz", + "integrity": "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@stablelib/base64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==", + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tokenizer/inflate": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "token-types": "^6.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.12.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.4.tgz", + "integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/expect": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.6.tgz", + "integrity": "sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.6", + "@vitest/utils": "4.1.6", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.6.tgz", + "integrity": "sha512-MCFc63czMjEInOlcY2cpQCvCN+KgbAn+60xu9cMgP4sKaLC5JNAKw7JH8QdAnoAC88hW1IiSNZ+GgVXlN1UcMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.6", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.6.tgz", + "integrity": "sha512-h5SxD/IzNhZYnrSZRsUZQIC+vD0GY8cUvq0iwsmkFKixRCKLLWqCXa/FIQ4S1R+sI+PGoojkHsdNrbZiM9Qpgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.6.tgz", + "integrity": "sha512-nOPCmn2+yD0ZNmKdsXGv/UxMMWbMuKeD6GyYncNwdkYDxpQvrPSKYj2rWuDjC2Y4b6w6hjip5dBKFzEUuZe3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.6", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.6.tgz", + "integrity": "sha512-YhsdE6xAVfTDmzjxL2ZDUvjj+ZsgyOKe+TdQzqkD72wIOmHka8NuGQ6NpTNZv9D2Z63fbwWKJPeVpEw4EQgYxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.6", + "@vitest/utils": "4.1.6", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.6.tgz", + "integrity": "sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.6.tgz", + "integrity": "sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.6", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/better-path-resolve": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/better-path-resolve/-/better-path-resolve-1.0.0.tgz", + "integrity": "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-windows": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/bn.js": { + "version": "4.12.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/bowser": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/croner": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/croner/-/croner-10.0.1.tgz", + "integrity": "sha512-ixNtAJndqh173VQ4KodSdJEI6nuioBWI0V1ITNKhZZsO0pEMoDxz539T4FTTbSZ/xIOSuDnzxLVRqBVSvPNE2g==", + "dev": true, + "funding": [ + { + "type": "other", + "url": "https://paypal.me/hexagonpp" + }, + { + "type": "github", + "url": "https://github.com/sponsors/hexagon" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true, + "license": "MIT" + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.8.tgz", + "integrity": "sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz", + "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^10.2.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/express/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/extendable-error": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/extendable-error/-/extendable-error-0.1.7.tgz", + "integrity": "sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-sha256": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==", + "license": "Unlicense" + }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, + "node_modules/fast-xml-builder": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz", + "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.5.0", + "xml-naming": "^0.1.0" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.3.tgz", + "integrity": "sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "@nodable/entities": "^2.1.0", + "fast-xml-builder": "^1.1.7", + "path-expression-matcher": "^1.5.0", + "strnum": "^2.2.3" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/fetch-blob/node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/file-type": { + "version": "22.0.1", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-22.0.1.tgz", + "integrity": "sha512-ww5Mhre0EE+jmBvOXTmXAbEMuZE7uX4a3+oRCQFNj8w++g3ev913N6tXQz0XTXbueQ5TWQfm6BdaViEHHn8bhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.5", + "token-types": "^6.1.2", + "uint8array-extras": "^1.5.0" + }, + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gaxios": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.4.tgz", + "integrity": "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gaxios/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/gcp-metadata": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", + "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/google-auth-library": { + "version": "10.6.2", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.2.tgz", + "integrity": "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.1.4", + "gcp-metadata": "8.1.2", + "google-logging-utils": "1.1.3", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/google-logging-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/grammy": { + "version": "1.42.0", + "resolved": "https://registry.npmjs.org/grammy/-/grammy-1.42.0.tgz", + "integrity": "sha512-1AdCge+AkjSdp2FwfICSFnVbl8Mq3KVHJDy+DgTI9+D6keJ0zWALPRKas5jv/8psiCzL4N2cEOcGW7O45Kn39g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@grammyjs/types": "3.26.0", + "abort-controller": "^3.0.0", + "debug": "^4.4.3", + "node-fetch": "^2.7.0" + }, + "engines": { + "node": "^12.20.0 || >=14.13.1" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/hono": { + "version": "4.12.21", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.21.tgz", + "integrity": "sha512-uV63apnb0kyPtAUwoWgaGh9HyIFcv8lgmzPZSiTBQAFOFGIzka5EZ1dZocmGnn0XdX0+XTqJ6Tqv7selMuGLRQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/hosted-git-info": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.3.tgz", + "integrity": "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/htmlparser2": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http_ece": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http_ece/-/http_ece-1.2.0.tgz", + "integrity": "sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-id": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/human-id/-/human-id-4.1.3.tgz", + "integrity": "sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==", + "dev": true, + "license": "MIT", + "bin": { + "human-id": "dist/cli.js" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.4.0.tgz", + "integrity": "sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-subdir": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-subdir/-/is-subdir-1.2.0.tgz", + "integrity": "sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "better-path-resolve": "1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/jose": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", + "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-schema-to-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", + "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "ts-algebra": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/koffi": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/koffi/-/koffi-2.16.2.tgz", + "integrity": "sha512-owU0MRwv6xkrVqCd+33uw6BaYppkTRXbO/rVdJNI2dvZG0gzyRhYwW25eWtc5pauwK8TGh3AbkFONSezdykfSA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "funding": { + "url": "https://liberapay.com/Koromix" + } + }, + "node_modules/kysely": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.29.1.tgz", + "integrity": "sha512-mOW4e+UMfrV1u/+a4uXO72mkwEJCIL4Tb/OQ8wU8jY5spUHxLKFfC1AnfNhfSoHubnIRly3u/xgnMdD0Vzq2RQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ "android" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/linkedom": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/linkedom/-/linkedom-0.18.12.tgz", + "integrity": "sha512-jalJsOwIKuQJSeTvsgzPe9iJzyfVaEJiEXl+25EkKevsULHvMJzpNqwvj1jOESWdmgKDiXObyjOYwlUqG7wo1Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "css-select": "^5.1.0", + "cssom": "^0.5.0", + "html-escaper": "^3.0.3", + "htmlparser2": "^10.0.0", + "uhyphen": "^0.2.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "canvas": ">= 2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "dev": true, + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/lru-cache": { + "version": "11.3.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.6.tgz", + "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/markdown-it": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/marked": { + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", + "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-addon-api": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.7.0.tgz", + "integrity": "sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-edge-tts": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/node-edge-tts/-/node-edge-tts-1.2.10.tgz", + "integrity": "sha512-bV2i4XU54D45+US0Zm1HcJRkifuB3W438dWyuJEHLQdKxnuqlI1kim2MOvR6Q3XUQZvfF9PoDyR1Rt7aeXhPdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "https-proxy-agent": "^7.0.1", + "ws": "^8.13.0", + "yargs": "^17.7.2" + }, + "bin": { + "node-edge-tts": "bin.js" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "dev": true, + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/openai": { + "version": "6.38.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.38.0.tgz", + "integrity": "sha512-AoMplt2UalrpgUDMh3L09QWjNRlgJPipclQvA6sYAaeF6nHNBMgmikAZGmcYLn8on4d9sQY9Q8bOLfrBS7Lc8g==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/openclaw": { + "version": "2026.5.18", + "resolved": "https://registry.npmjs.org/openclaw/-/openclaw-2026.5.18.tgz", + "integrity": "sha512-a9p2jdD0SEFUIxyCeOsf8gcO7fdo3vn1zGSYi04gA5mE+J1gHCSJTmk+R+hDPg6XOgHLXD+S2PrKi/74qTGPKw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@agentclientprotocol/sdk": "0.21.1", + "@clack/core": "1.3.1", + "@clack/prompts": "1.4.0", + "@earendil-works/pi-agent-core": "0.75.1", + "@earendil-works/pi-ai": "0.75.1", + "@earendil-works/pi-coding-agent": "0.75.1", + "@earendil-works/pi-tui": "0.75.1", + "@google/genai": "2.3.0", + "@grammyjs/runner": "2.0.3", + "@grammyjs/transformer-throttler": "1.2.1", + "@homebridge/ciao": "1.3.8", + "@lydell/node-pty": "1.2.0-beta.12", + "@modelcontextprotocol/sdk": "1.29.0", + "@mozilla/readability": "0.6.0", + "@openclaw/fs-safe": "0.2.4", + "@openclaw/proxyline": "0.3.3", + "ajv": "8.20.0", + "chalk": "5.6.2", + "chokidar": "5.0.0", + "commander": "14.0.3", + "croner": "10.0.1", + "dotenv": "17.4.2", + "express": "5.2.1", + "file-type": "22.0.1", + "grammy": "1.42.0", + "ipaddr.js": "2.4.0", + "jiti": "2.7.0", + "json5": "2.2.3", + "jszip": "3.10.1", + "kysely": "0.29.1", + "linkedom": "0.18.12", + "markdown-it": "14.1.1", + "node-edge-tts": "1.2.10", + "openai": "6.38.0", + "pdfjs-dist": "5.7.284", + "playwright-core": "1.60.0", + "qrcode": "1.5.4", + "quickjs-wasi": "2.2.0", + "tar": "7.5.15", + "tokenjuice": "0.7.1", + "tree-sitter-bash": "0.25.1", + "tslog": "4.10.2", + "typebox": "1.1.38", + "typescript": "6.0.3", + "undici": "8.3.0", + "web-push": "3.6.7", + "web-tree-sitter": "0.26.8", + "ws": "8.20.1", + "yaml": "2.9.0", + "zod": "4.4.3" + }, + "bin": { + "openclaw": "openclaw.mjs" + }, + "engines": { + "node": ">=22.19.0" + }, + "optionalDependencies": { + "sharp": "0.34.5", + "sqlite-vec": "0.1.9" + } + }, + "node_modules/outdent": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/outdent/-/outdent-0.5.0.tgz", + "integrity": "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/p-filter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", + "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-map": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.1.tgz", - "integrity": "sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==", - "cpu": [ - "arm64" - ], + "node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=6" } }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.1.tgz", - "integrity": "sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==", - "cpu": [ - "x64" - ], + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=8" } }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.1.tgz", - "integrity": "sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==", - "cpu": [ - "x64" - ], + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=6" } }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "node_modules/package-json-from-dist": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.1.tgz", - "integrity": "sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==", - "cpu": [ - "arm" - ], + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/package-manager-detector": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.11.tgz", + "integrity": "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "quansync": "^0.2.7" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">= 0.8" } }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.1.tgz", - "integrity": "sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==", - "cpu": [ - "arm64" - ], + "node_modules/partial-json": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", + "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=8" } }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.1.tgz", - "integrity": "sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==", - "cpu": [ - "arm64" - ], + "node_modules/path-expression-matcher": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", + "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", "dev": true, - "libc": [ - "musl" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } ], "license": "MIT", - "optional": true, - "os": [ - "linux" + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pdfjs-dist": { + "version": "5.7.284", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.7.284.tgz", + "integrity": "sha512-h4EdYQczmGhbOlqc3PPZwxevn7ApdWPbovAuWXOB/DjIyigSnwfy2oze7c6mRcSr9XgLp3eN3EeL4DyySTPMFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=22.13.0 || >=24" + }, + "optionalDependencies": { + "@napi-rs/canvas": "^0.1.100" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/playwright-core": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz", + "integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/postcss": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proper-lockfile/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/protobufjs": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.0.tgz", + "integrity": "sha512-LtESOsMPTZgyYtwxhvdgdjGL0HmXEaRA/hVD6sol4zA60hVXXXP/SGmxnqDbgGE8gy7pYex7cym+5vYPcmaXBQ==", + "dev": true, + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" } }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.1.tgz", - "integrity": "sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==", - "cpu": [ - "ppc64" - ], + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">= 0.10" } }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.1.tgz", - "integrity": "sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==", - "cpu": [ - "s390x" - ], + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">= 0.10" } }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.1.tgz", - "integrity": "sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==", - "cpu": [ - "x64" - ], + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=6" } }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.1.tgz", - "integrity": "sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==", - "cpu": [ - "x64" - ], + "node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", "dev": true, - "libc": [ - "musl" - ], "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=10.13.0" } }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.1.tgz", - "integrity": "sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==", - "cpu": [ - "arm64" - ], + "node_modules/qrcode/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/qrcode/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=8" } }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.1.tgz", - "integrity": "sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==", - "cpu": [ - "wasm32" - ], + "node_modules/qrcode/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/qrcode/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=8" } }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.1.tgz", - "integrity": "sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==", - "cpu": [ - "arm64" - ], + "node_modules/qrcode/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=6" } }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.1.tgz", - "integrity": "sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==", - "cpu": [ - "x64" - ], + "node_modules/qs": { + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", - "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], "license": "MIT" }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT" }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "node_modules/quickjs-wasi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/quickjs-wasi/-/quickjs-wasi-2.2.0.tgz", + "integrity": "sha512-zQxXmQMrEoD3S+jQdYsloq4qAuaxKFHZj6hHqOYGwB2iQZH+q9e/lf5zQPXCKOk0WJuAjzRFbO4KwHIp2D05Iw==", + "dev": true, + "license": "MIT" + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "dev": true, "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "engines": { + "node": ">= 0.6" } }, - "node_modules/@types/chai": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", - "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "dev": true, "license": "MIT", "dependencies": { - "@types/deep-eql": "*", - "assertion-error": "^2.0.1" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" } }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "node_modules/read-yaml-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-yaml-file/-/read-yaml-file-1.1.0.tgz", + "integrity": "sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.5", + "js-yaml": "^3.6.1", + "pify": "^4.0.1", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "node_modules/read-yaml-file/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } }, - "node_modules/@types/node": { - "version": "24.12.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.3.tgz", - "integrity": "sha512-8oljBDGun9cIsZRJR6fkihn0TSXJI0UDOOhncYaERq6M0JMDoPLxyscwruJcb4GKS6dvK/d8xebYBg27h/duaQ==", + "node_modules/read-yaml-file/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.16.0" + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@vitest/expect": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.6.tgz", - "integrity": "sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg==", + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "license": "MIT", "dependencies": { - "@standard-schema/spec": "^1.1.0", - "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.6", - "@vitest/utils": "4.1.6", - "chai": "^6.2.2", - "tinyrainbow": "^3.1.0" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" }, "funding": { - "url": "https://opencollective.com/vitest" + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true, + "license": "ISC" + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" } }, - "node_modules/@vitest/mocker": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.6.tgz", - "integrity": "sha512-MCFc63czMjEInOlcY2cpQCvCN+KgbAn+60xu9cMgP4sKaLC5JNAKw7JH8QdAnoAC88hW1IiSNZ+GgVXlN1UcMQ==", + "node_modules/rimraf": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", + "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "@vitest/spy": "4.1.6", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.21" + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" }, - "funding": { - "url": "https://opencollective.com/vitest" + "bin": { + "rimraf": "dist/esm/bin.mjs" }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + "engines": { + "node": "20 || >=22" }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@vitest/pretty-format": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.6.tgz", - "integrity": "sha512-h5SxD/IzNhZYnrSZRsUZQIC+vD0GY8cUvq0iwsmkFKixRCKLLWqCXa/FIQ4S1R+sI+PGoojkHsdNrbZiM9Qpgw==", + "node_modules/rolldown": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.1.tgz", + "integrity": "sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^3.1.0" + "@oxc-project/types": "=0.130.0", + "@rolldown/pluginutils": "^1.0.0" }, - "funding": { - "url": "https://opencollective.com/vitest" + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.1", + "@rolldown/binding-darwin-arm64": "1.0.1", + "@rolldown/binding-darwin-x64": "1.0.1", + "@rolldown/binding-freebsd-x64": "1.0.1", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.1", + "@rolldown/binding-linux-arm64-gnu": "1.0.1", + "@rolldown/binding-linux-arm64-musl": "1.0.1", + "@rolldown/binding-linux-ppc64-gnu": "1.0.1", + "@rolldown/binding-linux-s390x-gnu": "1.0.1", + "@rolldown/binding-linux-x64-gnu": "1.0.1", + "@rolldown/binding-linux-x64-musl": "1.0.1", + "@rolldown/binding-openharmony-arm64": "1.0.1", + "@rolldown/binding-wasm32-wasi": "1.0.1", + "@rolldown/binding-win32-arm64-msvc": "1.0.1", + "@rolldown/binding-win32-x64-msvc": "1.0.1" } }, - "node_modules/@vitest/runner": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.6.tgz", - "integrity": "sha512-nOPCmn2+yD0ZNmKdsXGv/UxMMWbMuKeD6GyYncNwdkYDxpQvrPSKYj2rWuDjC2Y4b6w6hjip5dBKFzEUuZe3vA==", + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.6", - "pathe": "^2.0.3" + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">= 18" } }, - "node_modules/@vitest/snapshot": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.6.tgz", - "integrity": "sha512-YhsdE6xAVfTDmzjxL2ZDUvjj+ZsgyOKe+TdQzqkD72wIOmHka8NuGQ6NpTNZv9D2Z63fbwWKJPeVpEw4EQgYxw==", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.6", - "@vitest/utils": "4.1.6", - "magic-string": "^0.30.21", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "queue-microtask": "^1.2.2" } }, - "node_modules/@vitest/spy": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.6.tgz", - "integrity": "sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg==", + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, - "license": "MIT", - "funding": { - "url": "https://opencollective.com/vitest" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, - "node_modules/@vitest/utils": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.6.tgz", - "integrity": "sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ==", + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "4.1.6", - "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.1.0" + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">=10" } }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "dev": true, "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, "engines": { - "node": ">=12" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "node_modules/send/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "dev": true, "license": "MIT", "engines": { - "node": "18 || 20 || >=22" + "node": ">= 0.6" } }, - "node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "node_modules/send/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^4.0.2" + "mime-db": "^1.54.0" }, "engines": { - "node": "18 || 20 || >=22" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/chai": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", - "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "dev": true, "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, "engines": { - "node": ">=18" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/convert-source-map": { + "node_modules/set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC" + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", "dev": true, "license": "MIT" }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", "dev": true, + "hasInstallScript": true, "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, "engines": { - "node": ">=8" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" } }, - "node_modules/es-module-lexer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", - "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "^1.0.0" + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/expect-type": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "engines": { - "node": ">=12.0.0" + "node": ">=8" } }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, "license": "MIT", - "engines": { - "node": ">=12.0.0" + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" }, - "peerDependencies": { - "picomatch": "^3 || ^4" + "engines": { + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" }, "engines": { - "node": "18 || 20 || >=22" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, - "license": "MPL-2.0", + "license": "MIT", "dependencies": { - "detect-libc": "^2.0.3" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { - "node": ">= 12.0.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": ">=8" } }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], + "license": "BSD-3-Clause", "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": ">=0.10.0" } }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], + "license": "BSD-3-Clause", "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": ">=0.10.0" } }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], + "node_modules/spawndamnit": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spawndamnit/-/spawndamnit-3.0.1.tgz", + "integrity": "sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "cross-spawn": "^7.0.5", + "signal-exit": "^4.0.1" + } + }, + "node_modules/spawndamnit/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", "engines": { - "node": ">= 12.0.0" + "node": ">=14" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true, - "libc": [ - "glibc" - ], - "license": "MPL-2.0", + "license": "BSD-3-Clause" + }, + "node_modules/sqlite-vec": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/sqlite-vec/-/sqlite-vec-0.1.9.tgz", + "integrity": "sha512-L7XJWRIBNvR9O5+vh1FQ+IGkh/3D2AzVksW5gdtk28m78Hy8skFD0pqReKH1Yp0/BUKRGcffgKvyO/EON5JXpA==", + "dev": true, + "license": "MIT OR Apache", "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "optionalDependencies": { + "sqlite-vec-darwin-arm64": "0.1.9", + "sqlite-vec-darwin-x64": "0.1.9", + "sqlite-vec-linux-arm64": "0.1.9", + "sqlite-vec-linux-x64": "0.1.9", + "sqlite-vec-windows-x64": "0.1.9" } }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "node_modules/sqlite-vec-darwin-arm64": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/sqlite-vec-darwin-arm64/-/sqlite-vec-darwin-arm64-0.1.9.tgz", + "integrity": "sha512-jSsZpE42OfBkGL/ItyJTVCUwl6o6Ka3U5rc4j+UBDIQzC1ulSSKMEhQLthsOnF/MdAf1MuAkYhkdKmmcjaIZQg==", "cpu": [ "arm64" ], "dev": true, - "libc": [ - "musl" - ], - "license": "MPL-2.0", + "license": "MIT OR Apache", "optional": true, "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + "darwin" + ] }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "node_modules/sqlite-vec-darwin-x64": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/sqlite-vec-darwin-x64/-/sqlite-vec-darwin-x64-0.1.9.tgz", + "integrity": "sha512-KDlVyqQT7pnOhU1ymB9gs7dMbSoVmKHitT+k1/xkjarcX8bBqPxWrGlK/R+C5WmWkfvWwyq5FfXfiBYCBs6PlA==", "cpu": [ "x64" ], "dev": true, - "libc": [ - "glibc" - ], - "license": "MPL-2.0", + "license": "MIT OR Apache", "optional": true, "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + "darwin" + ] }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "node_modules/sqlite-vec-linux-arm64": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/sqlite-vec-linux-arm64/-/sqlite-vec-linux-arm64-0.1.9.tgz", + "integrity": "sha512-5wXVJ9c9kR4CHm/wVqXb/R+XUHTdpZ4nWbPHlS+gc9qQFVHs92Km4bPnCKX4rtcPMzvNis+SIzMJR1SCEwpuUw==", "cpu": [ - "x64" + "arm64" ], "dev": true, - "libc": [ - "musl" - ], - "license": "MPL-2.0", + "license": "MIT OR Apache", "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + ] }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "node_modules/sqlite-vec-linux-x64": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/sqlite-vec-linux-x64/-/sqlite-vec-linux-x64-0.1.9.tgz", + "integrity": "sha512-w3tCH8xK2finW8fQJ/m8uqKodXUZ9KAuAar2UIhz4BHILfpE0WM/MTGCRfa7RjYbrYim5Luk3guvMOGI7T7JQA==", "cpu": [ - "arm64" + "x64" ], "dev": true, - "license": "MPL-2.0", + "license": "MIT OR Apache", "optional": true, "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + "linux" + ] }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "node_modules/sqlite-vec-windows-x64": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/sqlite-vec-windows-x64/-/sqlite-vec-windows-x64-0.1.9.tgz", + "integrity": "sha512-y3gEIyy/17bq2QFPQOWLE68TYWcRZkBQVA2XLrTPHNTOp55xJi/BBBmOm40tVMDMjtP+Elpk6UBUXdaq+46b0Q==", "cpu": [ "x64" ], "dev": true, - "license": "MPL-2.0", + "license": "MIT OR Apache", "optional": true, "os": [ "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + ] + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/standardwebhooks": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz", + "integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==", + "license": "MIT", + "dependencies": { + "@stablelib/base64": "^1.0.0", + "fast-sha256": "^1.3.0" } }, - "node_modules/lru-cache": { - "version": "11.3.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.6.tgz", - "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==", + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "engines": { - "node": "20 || >=22" + "node": ">= 0.8" } }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "node_modules/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" + "safe-buffer": "~5.1.0" } }, - "node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", "dependencies": { - "brace-expansion": "^5.0.5" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8" } }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=8" } }, - "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": ">=4" } }, - "node_modules/obug": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", - "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "node_modules/strnum": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.3.0.tgz", + "integrity": "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==", "dev": true, "funding": [ - "https://github.com/sponsors/sxzz", - "https://opencollective.com/debug" + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } ], "license": "MIT" }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "node_modules/strtok3": { + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", + "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", "dev": true, - "license": "BlueOak-1.0.0" + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } }, - "node_modules/path-scurry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "node_modules/tar": { + "version": "7.5.15", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.15.tgz", + "integrity": "sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" }, "engines": { - "node": "18 || 20 || >=22" + "node": ">=18" + } + }, + "node_modules/term-size": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, "license": "MIT" }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "node_modules/tinyexec": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", "dev": true, - "license": "ISC" + "license": "MIT", + "engines": { + "node": ">=18" + } }, - "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "dev": true, "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, "engines": { - "node": ">=12" + "node": ">=12.0.0" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/postcss": { - "version": "8.5.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", - "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">=14.0.0" } }, - "node_modules/rimraf": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", - "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "glob": "^13.0.3", - "package-json-from-dist": "^1.0.1" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" + "is-number": "^7.0.0" }, "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8.0" } }, - "node_modules/rolldown": { + "node_modules/toidentifier": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.1.tgz", - "integrity": "sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/token-types": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", + "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.130.0", - "@rolldown/pluginutils": "^1.0.0" - }, - "bin": { - "rolldown": "bin/cli.mjs" + "@borewit/text-codec": "^0.2.1", + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=14.16" }, - "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.1", - "@rolldown/binding-darwin-arm64": "1.0.1", - "@rolldown/binding-darwin-x64": "1.0.1", - "@rolldown/binding-freebsd-x64": "1.0.1", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.1", - "@rolldown/binding-linux-arm64-gnu": "1.0.1", - "@rolldown/binding-linux-arm64-musl": "1.0.1", - "@rolldown/binding-linux-ppc64-gnu": "1.0.1", - "@rolldown/binding-linux-s390x-gnu": "1.0.1", - "@rolldown/binding-linux-x64-gnu": "1.0.1", - "@rolldown/binding-linux-x64-musl": "1.0.1", - "@rolldown/binding-openharmony-arm64": "1.0.1", - "@rolldown/binding-wasm32-wasi": "1.0.1", - "@rolldown/binding-win32-arm64-msvc": "1.0.1", - "@rolldown/binding-win32-x64-msvc": "1.0.1" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "node_modules/tokenjuice": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/tokenjuice/-/tokenjuice-0.7.1.tgz", + "integrity": "sha512-eO048hm9UcGHASjYkIWEij8QN68amGp+S1nJyo685qB1/ol+VGEYjPglcVPvCbJbZyFHvI+BBAMvOfnqYCtpsQ==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", + "bin": { + "tokenjuice": "dist/cli/main.js" + }, "engines": { - "node": ">=0.10.0" + "node": ">=20" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/vincentkoc" } }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true, "license": "MIT" }, - "node_modules/std-env": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", - "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "node_modules/tree-sitter-bash": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/tree-sitter-bash/-/tree-sitter-bash-0.25.1.tgz", + "integrity": "sha512-7hMytuYIMoXOq24yRulgIxthE9YmggZIOHCyPTTuJcu6EU54tYD+4G39cUb28kxC6jMf/AbPfWGLQtgPTdh3xw==", "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.2.1", + "node-gyp-build": "^4.8.2" + }, + "peerDependencies": { + "tree-sitter": "^0.25.0" + }, + "peerDependenciesMeta": { + "tree-sitter": { + "optional": true + } + } + }, + "node_modules/ts-algebra": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", + "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", "license": "MIT" }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, - "license": "MIT" + "license": "0BSD" }, - "node_modules/tinyexec": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", - "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", + "node_modules/tslog": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/tslog/-/tslog-4.10.2.tgz", + "integrity": "sha512-XuELoRpMR+sq8fuWwX7P0bcj+PRNiicOKDEb3fGNURhxWVyykCi9BNq7c4uVz7h7P0sj8qgBsr5SWS6yBClq3g==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/fullstack-build/tslog?sponsor=1" } }, - "node_modules/tinyglobby": { - "version": "0.2.16", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" }, "engines": { - "node": ">=12.0.0" + "node": ">= 18" }, "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/tinyrainbow": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", - "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "node_modules/type-is/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "dev": true, - "license": "0BSD", - "optional": true + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/typebox": { + "version": "1.1.38", + "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.1.38.tgz", + "integrity": "sha512-pZ0aQPmMmXoUvSbeuWf/Hzsc+avNw/Zd6VeE8CFgkVGWyuHPJvqeJJDeJqLve+K70LvjYIoleGcoJHPT17cWoA==", + "license": "MIT" }, "node_modules/typescript": { "version": "6.0.3", @@ -1431,6 +7996,43 @@ "node": ">=14.17" } }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/uhyphen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/uhyphen/-/uhyphen-0.2.0.tgz", + "integrity": "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==", + "dev": true, + "license": "ISC" + }, + "node_modules/uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/undici": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.3.0.tgz", + "integrity": "sha512-TkUDgb6tl7KOGZ+7e8E3d2FYgUQgF6z5YypqjWmixVQSQERFcVrVg0ySADm2LVLRh5ljAaHTCR5Fmz3Q34rB7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.19.0" + } + }, "node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", @@ -1438,6 +8040,43 @@ "dev": true, "license": "MIT" }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/vite": { "version": "8.0.13", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.13.tgz", @@ -1606,6 +8245,74 @@ } } }, + "node_modules/web-push": { + "version": "3.6.7", + "resolved": "https://registry.npmjs.org/web-push/-/web-push-3.6.7.tgz", + "integrity": "sha512-OpiIUe8cuGjrj3mMBFWY+e4MMIkW3SVT+7vEIjvD9kejGUypv8GPDf84JdPWskK8zMRIJ6xYGm+Kxr8YkPyA0A==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "asn1.js": "^5.3.0", + "http_ece": "1.2.0", + "https-proxy-agent": "^7.0.0", + "jws": "^4.0.0", + "minimist": "^1.2.5" + }, + "bin": { + "web-push": "src/cli.js" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/web-tree-sitter": { + "version": "0.26.8", + "resolved": "https://registry.npmjs.org/web-tree-sitter/-/web-tree-sitter-0.26.8.tgz", + "integrity": "sha512-4sUwi7ZyOrIk5KLgYLkc2A/F0LFMQnBhfb+2Cdl7ik4ePJ6JD+fk4ofI2sA5eGawBKBaK4Vntt7Ww5KcEsay4A==", + "dev": true, + "license": "MIT" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true, + "license": "ISC" + }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", @@ -1623,29 +8330,266 @@ "node": ">=8" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz", + "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-naming": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", + "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "devOptional": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + }, "packages/docmap": { "name": "@paleo/docmap", - "version": "0.4.1", + "version": "0.4.2", "license": "CC0-1.0", "bin": { "docmap": "bin/docmap.mjs" }, "devDependencies": { - "@types/node": "~24.12.3", + "@types/node": "~24.12.4", + "rimraf": "~6.1.3", + "typescript": "~6.0.3", + "vitest": "~4.1.6" + }, + "engines": { + "node": ">=22.11.0" + } + }, + "packages/openclaw-channel-mock-core": { + "name": "@paleo/openclaw-channel-mock-core", + "version": "0.2.1", + "license": "MIT", + "dependencies": { + "typebox": "1.1.38" + }, + "devDependencies": { + "@types/node": "~24.12.4", + "openclaw": "2026.5.18", + "rimraf": "~6.1.3", + "typescript": "~6.0.3", + "vitest": "~4.1.6" + }, + "engines": { + "node": ">=22.11.0" + }, + "peerDependencies": { + "openclaw": "*" + } + }, + "packages/openclaw-discord-mock": { + "name": "@paleo/openclaw-discord-mock", + "version": "0.2.1", + "license": "MIT", + "dependencies": { + "@paleo/openclaw-channel-mock-core": "0.2.1" + }, + "devDependencies": { + "@types/node": "~24.12.4", + "openclaw": "2026.5.18", + "rimraf": "~6.1.3", + "typescript": "~6.0.3", + "vitest": "~4.1.6" + }, + "engines": { + "node": ">=22.11.0" + }, + "peerDependencies": { + "openclaw": "*" + } + }, + "packages/openclaw-qa-runner": { + "name": "@paleo/openclaw-qa-runner", + "version": "0.2.1", + "license": "MIT", + "dependencies": { + "@anthropic-ai/sdk": "~0.97.1", + "@paleo/openclaw-channel-mock-core": "0.2.1", + "@paleo/openclaw-discord-mock": "0.2.1", + "@paleo/openclaw-slack-mock": "0.2.1" + }, + "bin": { + "openclaw-qa-runner": "bin/cli.mjs" + }, + "devDependencies": { + "@types/node": "~24.12.4", + "openclaw": "2026.5.18", + "rimraf": "~6.1.3", + "typescript": "~6.0.3" + }, + "engines": { + "node": ">=22.11.0" + }, + "peerDependencies": { + "openclaw": "*" + } + }, + "packages/openclaw-slack-mock": { + "name": "@paleo/openclaw-slack-mock", + "version": "0.2.1", + "license": "MIT", + "dependencies": { + "@paleo/openclaw-channel-mock-core": "0.2.1" + }, + "devDependencies": { + "@types/node": "~24.12.4", + "openclaw": "2026.5.18", "rimraf": "~6.1.3", "typescript": "~6.0.3", "vitest": "~4.1.6" }, "engines": { "node": ">=22.11.0" + }, + "peerDependencies": { + "openclaw": "*" } }, "packages/worktree-env": { "name": "@paleo/worktree-env", - "version": "0.10.1", + "version": "0.10.2", "license": "CC0-1.0", "devDependencies": { - "@types/node": "~24.12.3", + "@types/node": "~24.12.4", "rimraf": "~6.1.3", "typescript": "~6.0.3", "vitest": "~4.1.6" diff --git a/package.json b/package.json index 62c729f..fc27053 100644 --- a/package.json +++ b/package.json @@ -2,15 +2,21 @@ "name": "alignfirst-monorepo", "private": true, "type": "module", - "workspaces": ["packages/*"], + "workspaces": [ + "packages/*" + ], "scripts": { "lint": "biome check", "lint:fix": "biome check --write", "build": "npm run build --workspaces --if-present", "clear": "npm run clear --workspaces --if-present", - "test": "npm run test --workspaces --if-present" + "test": "npm run test --workspaces --if-present", + "changeset": "changeset", + "changeset:bump": "changeset version", + "changeset:publish": "npm i && changeset publish" }, "devDependencies": { - "@biomejs/biome": "~2.4.15" + "@biomejs/biome": "~2.4.15", + "@changesets/cli": "~2.31.0" } } diff --git a/packages/docmap/CHANGELOG.md b/packages/docmap/CHANGELOG.md new file mode 100644 index 0000000..ac1fa6d --- /dev/null +++ b/packages/docmap/CHANGELOG.md @@ -0,0 +1,7 @@ +# @paleo/docmap + +## 0.4.2 + +### Patch Changes + +- First version in changelog diff --git a/packages/docmap/README.md b/packages/docmap/README.md index 9952e13..ca30a3f 100644 --- a/packages/docmap/README.md +++ b/packages/docmap/README.md @@ -2,7 +2,7 @@ A lightweight documentation system for AI agents and humans. Keep project docs in a `docs/` folder with YAML frontmatter, browse and read them from the terminal. -Docmap is both a **npm package** (this CLI — lists, reads, and validates docs) and an **agent skill** `docmap` (conventions and workflows that teach AI agents how to write, organize, and migrate documentation). You need both: the package provides the tooling, the skill provides the knowledge. +Docmap is both a **npm package** (this CLI; it lists, reads, and validates docs) and an **agent skill** `docmap` (conventions and workflows that teach AI agents how to write, organize, and migrate documentation). You need both: the package provides the tooling, the skill provides the knowledge. _Inspired by the [OpenClaw](https://github.com/openclaw/openclaw/) docs system, which uses [Mintlify](https://www.mintlify.com/). This project doesn't depend on Mintlify._ diff --git a/packages/docmap/package.json b/packages/docmap/package.json index 470e115..6fdf8e6 100644 --- a/packages/docmap/package.json +++ b/packages/docmap/package.json @@ -1,6 +1,6 @@ { "name": "@paleo/docmap", - "version": "0.4.1", + "version": "0.4.2", "license": "CC0-1.0", "description": "A lightweight documentation system for AI agents and humans.", "keywords": [ @@ -27,16 +27,19 @@ "bin", "dist" ], + "publishConfig": { + "access": "public" + }, "scripts": { "build": "tsc -p tsconfig.build.json", "clear": "rimraf dist/*", "lint": "biome check", "test": "vitest run", "docmap": "node bin/docmap.mjs", - "prepublishOnly": "npm i && npm run clear && npm run build && npm run lint && npm run test" + "prepublishOnly": "npm run clear && npm run build && npm run lint && npm run test" }, "devDependencies": { - "@types/node": "~24.12.3", + "@types/node": "~24.12.4", "rimraf": "~6.1.3", "typescript": "~6.0.3", "vitest": "~4.1.6" diff --git a/packages/docmap/tsconfig.build.json b/packages/docmap/tsconfig.build.json index fef656c..f1e54d4 100644 --- a/packages/docmap/tsconfig.build.json +++ b/packages/docmap/tsconfig.build.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "ES2022", + "target": "ES2023", "module": "NodeNext", "moduleResolution": "NodeNext", "outDir": "dist", diff --git a/packages/openclaw-channel-mock-core/CHANGELOG.md b/packages/openclaw-channel-mock-core/CHANGELOG.md new file mode 100644 index 0000000..9b7823c --- /dev/null +++ b/packages/openclaw-channel-mock-core/CHANGELOG.md @@ -0,0 +1,13 @@ +# @paleo/openclaw-channel-mock-core + +## 0.2.1 + +### Patch Changes + +- Hardened openclaw qa toolkit + +## 0.2.0 + +### Minor Changes + +- Initial version diff --git a/packages/openclaw-channel-mock-core/LICENSE b/packages/openclaw-channel-mock-core/LICENSE new file mode 100644 index 0000000..91b9589 --- /dev/null +++ b/packages/openclaw-channel-mock-core/LICENSE @@ -0,0 +1,24 @@ +MIT License + +Copyright (c) 2026 Paleo + +Portions adapted from OpenClaw (https://github.com/steipete/openclaw), +Copyright (c) 2025 Peter Steinberger. Licensed under the MIT License. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/openclaw-channel-mock-core/NOTICE.md b/packages/openclaw-channel-mock-core/NOTICE.md new file mode 100644 index 0000000..ea1059a --- /dev/null +++ b/packages/openclaw-channel-mock-core/NOTICE.md @@ -0,0 +1,30 @@ +# NOTICE — upstream attribution + +This package adapts code from the upstream [OpenClaw](https://github.com/steipete/openclaw) `extensions/qa-channel/` extension (MIT, Copyright 2025 Peter Steinberger). + +## Adapted from `openclaw/extensions/qa-channel/` + +- `api.ts` → re-exported via `src/index.ts` +- `channel-plugin-api.ts` → `src/plugin.ts` +- `runtime-api.ts` → `src/runtime-api.ts` +- `setup-entry.ts` → upstream pattern reused by sibling wrapper packages +- `setup-plugin-api.ts` → `src/channel-setup-plugin.ts` + +## Adapted from `openclaw/extensions/qa-channel/src/` + +- `accounts.ts` → `src/accounts.ts` +- `bus-client.ts` → `src/bus-client.ts` (split into `bus-handler.ts`, `bus-queries.ts`, `bus-state.ts`, `bus-waiters.ts`) +- `channel-actions.ts` → `src/plugin-actions.ts` +- `channel.setup.ts` → `src/setup.ts` +- `channel.ts` → influenced `src/plugin.ts` +- `config-schema.ts` → `src/config-schema.ts` +- `gateway.ts` → `src/gateway.ts` +- `inbound.ts` → `src/inbound.ts` +- `outbound.ts` → `src/outbound.ts` +- `protocol.ts` → `src/protocol.ts` +- `runtime.ts` → `src/runtime.ts` +- `setup.ts` → `src/setup.ts` +- `status.ts` → `src/status.ts` +- `types.ts` → `src/types.ts` + +See [`LICENSE`](LICENSE) for the combined MIT license text (Paleo + upstream OpenClaw attribution). diff --git a/packages/openclaw-channel-mock-core/README.md b/packages/openclaw-channel-mock-core/README.md new file mode 100644 index 0000000..414c660 --- /dev/null +++ b/packages/openclaw-channel-mock-core/README.md @@ -0,0 +1,14 @@ +# @paleo/openclaw-channel-mock-core + +Shared library powering the synthetic OpenClaw channel plugins used in QA harnesses. Provides the bus client, action handlers, account / inbound / outbound primitives, plugin and setup factories, and the typebox-based config schema. + +Not meant to be consumed directly. Use the surface wrappers: + +- [`@paleo/openclaw-discord-mock`](../openclaw-discord-mock/) — `surface: "discord"`, full action surface, `autoThread: false`. +- [`@paleo/openclaw-slack-mock`](../openclaw-slack-mock/) — `surface: "slack"`, restricted action surface, `autoThread: true`. + +Both wrappers register as OpenClaw channels and talk to a single bus (`http://bus:43123` by default) provisioned by [`@paleo/openclaw-qa-runner`](../openclaw-qa-runner/). + +## Attribution + +Adapted from upstream OpenClaw's `extensions/qa-channel/`. See [`NOTICE.md`](NOTICE.md). diff --git a/packages/openclaw-channel-mock-core/package.json b/packages/openclaw-channel-mock-core/package.json new file mode 100644 index 0000000..ad1934a --- /dev/null +++ b/packages/openclaw-channel-mock-core/package.json @@ -0,0 +1,53 @@ +{ + "name": "@paleo/openclaw-channel-mock-core", + "version": "0.2.1", + "description": "Shared library for synthetic OpenClaw channel plugins used in QA harnesses (bus client, action handlers, factories).", + "keywords": [ + "openclaw", + "qa", + "testing", + "channel", + "mock" + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/paleo/alignfirst.git", + "directory": "packages/openclaw-channel-mock-core" + }, + "engines": { + "node": ">=22.11.0" + }, + "packageManager": "npm@11.11.0", + "type": "module", + "main": "./dist/index.js", + "exports": "./dist/index.js", + "files": [ + "dist/", + "LICENSE", + "NOTICE.md" + ], + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsc -p tsconfig.build.json", + "clear": "rimraf dist/*", + "lint": "biome check", + "test": "vitest run", + "prepublishOnly": "npm run clear && npm run build && npm run lint && npm run test" + }, + "peerDependencies": { + "openclaw": "*" + }, + "dependencies": { + "typebox": "1.1.38" + }, + "devDependencies": { + "@types/node": "~24.12.4", + "openclaw": "2026.5.18", + "rimraf": "~6.1.3", + "typescript": "~6.0.3", + "vitest": "~4.1.6" + } +} diff --git a/packages/openclaw-channel-mock-core/src/accounts.ts b/packages/openclaw-channel-mock-core/src/accounts.ts new file mode 100644 index 0000000..13682be --- /dev/null +++ b/packages/openclaw-channel-mock-core/src/accounts.ts @@ -0,0 +1,106 @@ +import { createAccountListHelpers } from "openclaw/plugin-sdk/account-helpers"; +import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id"; +import { resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution-runtime"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime"; +import type { ChannelMockAccountConfig, CoreConfig, ResolvedChannelMockAccount } from "./types.js"; + +const DEFAULT_POLL_TIMEOUT_MS = 1_000; + +export type ChannelMockAccountHelpers = { + listAccountIds: (cfg: CoreConfig) => string[]; + resolveDefaultAccountId: (cfg: CoreConfig) => string; + resolveAccount: (params: { + cfg: CoreConfig; + accountId?: string | null; + }) => ResolvedChannelMockAccount; + listEnabledAccounts: (cfg: CoreConfig) => ResolvedChannelMockAccount[]; +}; + +export function createChannelMockAccountHelpers(params: { + channelId: string; +}): ChannelMockAccountHelpers { + const { channelId } = params; + const sdkHelpers = createAccountListHelpers(channelId, { + normalizeAccountId, + implicitDefaultAccount: { + channelKeys: ["baseUrl"], + }, + }); + + // For channel-mock the bus is the only endpoint; we collapse the "account" axis + // onto the channel id so per-channel state stays partitioned on the shared bus. + // When the config has no explicit `accounts` map, the SDK would synthesise a + // single implicit account named "default" — we remap it to the channel id. + function hasExplicitAccounts(cfg: CoreConfig): boolean { + const accounts = cfg.channels?.[channelId]?.accounts; + return Boolean(accounts && Object.keys(accounts).length > 0); + } + + function normalizeForChannel(raw: string | null | undefined): string { + const normalized = normalizeAccountId(raw); + return normalized === DEFAULT_ACCOUNT_ID ? channelId : normalized; + } + + function listAccountIds(cfg: CoreConfig): string[] { + if (hasExplicitAccounts(cfg)) { + return sdkHelpers.listAccountIds(cfg); + } + return [channelId]; + } + + function resolveDefaultAccountId(cfg: CoreConfig): string { + if (hasExplicitAccounts(cfg)) { + return sdkHelpers.resolveDefaultAccountId(cfg); + } + return channelId; + } + + function resolveMerged(cfg: CoreConfig, accountId: string): ChannelMockAccountConfig { + return resolveMergedAccountConfig({ + channelConfig: cfg.channels?.[channelId] as ChannelMockAccountConfig | undefined, + accounts: cfg.channels?.[channelId]?.accounts, + accountId, + omitKeys: ["defaultAccount"], + normalizeAccountId, + }); + } + + function resolveAccount(params: { + cfg: CoreConfig; + accountId?: string | null; + }): ResolvedChannelMockAccount { + const accountId = hasExplicitAccounts(params.cfg) + ? normalizeAccountId(params.accountId) + : normalizeForChannel(params.accountId); + const merged = resolveMerged(params.cfg, accountId); + const baseEnabled = params.cfg.channels?.[channelId]?.enabled !== false; + const enabled = baseEnabled && merged.enabled !== false; + const baseUrl = merged.baseUrl?.trim() ?? ""; + const botUserId = merged.botUserId?.trim() || "openclaw"; + const botDisplayName = merged.botDisplayName?.trim() || "OpenClaw QA"; + return { + accountId, + enabled, + configured: Boolean(baseUrl), + name: normalizeOptionalString(merged.name), + baseUrl, + botUserId, + botDisplayName, + pollTimeoutMs: merged.pollTimeoutMs ?? DEFAULT_POLL_TIMEOUT_MS, + config: { + ...merged, + allowFrom: merged.allowFrom ?? ["*"], + }, + }; + } + + function listEnabledAccounts(cfg: CoreConfig): ResolvedChannelMockAccount[] { + return listAccountIds(cfg) + .map((accountId) => resolveAccount({ cfg, accountId })) + .filter((account) => account.enabled); + } + + return { listAccountIds, resolveDefaultAccountId, resolveAccount, listEnabledAccounts }; +} + +export { DEFAULT_ACCOUNT_ID }; diff --git a/packages/openclaw-channel-mock-core/src/bus-client.ts b/packages/openclaw-channel-mock-core/src/bus-client.ts new file mode 100644 index 0000000..d1b1a63 --- /dev/null +++ b/packages/openclaw-channel-mock-core/src/bus-client.ts @@ -0,0 +1,261 @@ +import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime"; +import type { + QaBusInboundMessageInput, + QaBusMessage, + QaBusPollResult, + QaBusSearchMessagesInput, + QaBusStateSnapshot, + QaBusThread, + QaBusToolCall, +} from "./protocol.js"; + +export type { + QaBusAttachment, + QaBusConversation, + QaBusConversationKind, + QaBusCreateThreadInput, + QaBusDeleteMessageInput, + QaBusEditMessageInput, + QaBusEvent, + QaBusInboundMessageInput, + QaBusMessage, + QaBusOutboundMessageInput, + QaBusPollInput, + QaBusPollResult, + QaBusReactToMessageInput, + QaBusReadMessageInput, + QaBusSearchMessagesInput, + QaBusStateSnapshot, + QaBusThread, + QaBusToolCall, + QaBusWaitForInput, +} from "./protocol.js"; + +type JsonResult = Promise; + +function buildQaBusUrl(baseUrl: string, path: string): URL { + const normalizedBaseUrl = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`; + return new URL(path.replace(/^\/+/, ""), normalizedBaseUrl); +} + +async function postJson( + baseUrl: string, + path: string, + body: unknown, + signal?: AbortSignal, +): JsonResult { + const url = buildQaBusUrl(baseUrl, path).toString(); + const { response, release } = await fetchWithSsrFGuard({ + url, + init: { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify(body), + }, + signal, + policy: { allowPrivateNetwork: true }, + auditContext: "channel-mock.bus-post", + }); + try { + const text = await response.text(); + let parsed: T | { error?: string }; + try { + parsed = text ? (JSON.parse(text) as T | { error?: string }) : ({} as T); + } catch (error) { + throw error instanceof Error ? error : new Error(String(error)); + } + if (!response.ok) { + const error = + typeof parsed === "object" && parsed && "error" in parsed ? parsed.error : undefined; + throw new Error(error || `qa-bus request failed: ${response.status}`); + } + return parsed as T; + } finally { + await release(); + } +} + +export function normalizeQaTarget(raw: string): string | undefined { + const trimmed = raw.trim(); + if (!trimmed) { + return undefined; + } + return trimmed; +} + +export function parseQaTarget(raw: string): { + chatType: "direct" | "channel" | "group"; + conversationId: string; + threadId?: string; +} { + const normalized = normalizeQaTarget(raw); + if (!normalized) { + throw new Error("channel-mock target is required"); + } + if (normalized.startsWith("thread:")) { + const rest = normalized.slice("thread:".length); + const slashIndex = rest.indexOf("/"); + if (slashIndex <= 0 || slashIndex === rest.length - 1) { + throw new Error(`invalid channel-mock thread target: ${normalized}`); + } + return { + chatType: "channel", + conversationId: rest.slice(0, slashIndex), + threadId: rest.slice(slashIndex + 1), + }; + } + if (normalized.startsWith("channel:")) { + return { + chatType: "channel", + conversationId: normalized.slice("channel:".length), + }; + } + if (normalized.startsWith("group:")) { + return { + chatType: "group", + conversationId: normalized.slice("group:".length), + }; + } + if (normalized.startsWith("dm:")) { + return { + chatType: "direct", + conversationId: normalized.slice("dm:".length), + }; + } + return { + chatType: "direct", + conversationId: normalized, + }; +} + +export function buildQaTarget(params: { + chatType: "direct" | "channel" | "group"; + conversationId: string; + threadId?: string | null; +}) { + if (params.threadId) { + return `thread:${params.conversationId}/${params.threadId}`; + } + return `${params.chatType === "direct" ? "dm" : params.chatType}:${params.conversationId}`; +} + +export async function pollQaBus(params: { + baseUrl: string; + accountId: string; + cursor: number; + timeoutMs: number; + signal?: AbortSignal; +}): Promise { + return await postJson( + params.baseUrl, + "/v1/poll", + { + accountId: params.accountId, + cursor: params.cursor, + timeoutMs: params.timeoutMs, + }, + params.signal, + ); +} + +export async function sendQaBusMessage(params: { + baseUrl: string; + accountId: string; + to: string; + text: string; + senderId?: string; + senderName?: string; + threadId?: string; + replyToId?: string; + attachments?: import("./protocol.js").QaBusAttachment[]; + toolCalls?: QaBusToolCall[]; +}) { + return await postJson<{ message: QaBusMessage }>(params.baseUrl, "/v1/outbound/message", params); +} + +export async function createQaBusThread(params: { + baseUrl: string; + accountId: string; + conversationId: string; + title: string; + createdBy?: string; +}) { + return await postJson<{ thread: QaBusThread }>( + params.baseUrl, + "/v1/actions/thread-create", + params, + ); +} + +export async function reactToQaBusMessage(params: { + baseUrl: string; + accountId: string; + messageId: string; + emoji: string; + senderId?: string; +}) { + return await postJson<{ message: QaBusMessage }>(params.baseUrl, "/v1/actions/react", params); +} + +export async function editQaBusMessage(params: { + baseUrl: string; + accountId: string; + messageId: string; + text: string; +}) { + return await postJson<{ message: QaBusMessage }>(params.baseUrl, "/v1/actions/edit", params); +} + +export async function deleteQaBusMessage(params: { + baseUrl: string; + accountId: string; + messageId: string; +}) { + return await postJson<{ message: QaBusMessage }>(params.baseUrl, "/v1/actions/delete", params); +} + +export async function readQaBusMessage(params: { + baseUrl: string; + accountId: string; + messageId: string; +}) { + return await postJson<{ message: QaBusMessage }>(params.baseUrl, "/v1/actions/read", params); +} + +export async function searchQaBusMessages(params: { + baseUrl: string; + input: QaBusSearchMessagesInput; +}) { + return await postJson<{ messages: QaBusMessage[] }>( + params.baseUrl, + "/v1/actions/search", + params.input, + ); +} + +export async function injectQaBusInboundMessage(params: { + baseUrl: string; + input: QaBusInboundMessageInput; +}) { + return await postJson<{ message: QaBusMessage }>( + params.baseUrl, + "/v1/inbound/message", + params.input, + ); +} + +export async function getQaBusState(baseUrl: string): Promise { + const { response, release } = await fetchWithSsrFGuard({ + url: buildQaBusUrl(baseUrl, "/v1/state").toString(), + policy: { allowPrivateNetwork: true }, + auditContext: "channel-mock.bus-state", + }); + try { + if (!response.ok) { + throw new Error(`qa-bus request failed: ${response.status}`); + } + return (await response.json()) as QaBusStateSnapshot; + } finally { + await release(); + } +} diff --git a/packages/openclaw-channel-mock-core/src/bus-handler.ts b/packages/openclaw-channel-mock-core/src/bus-handler.ts new file mode 100644 index 0000000..6c2e8c7 --- /dev/null +++ b/packages/openclaw-channel-mock-core/src/bus-handler.ts @@ -0,0 +1,176 @@ +import type { IncomingMessage, ServerResponse } from "node:http"; +import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; +import { + isRequestBodyLimitError, + readRequestBodyWithLimit, + requestBodyErrorToText, +} from "openclaw/plugin-sdk/webhook-ingress"; +import { normalizeAccountId, resolveQaBusPollStartCursor } from "./bus-queries.js"; +import { createQaBusState, type QaBusState } from "./bus-state.js"; +import type { + QaBusCreateThreadInput, + QaBusDeleteMessageInput, + QaBusEditMessageInput, + QaBusInboundMessageInput, + QaBusOutboundMessageInput, + QaBusPollInput, + QaBusReactToMessageInput, + QaBusReadMessageInput, + QaBusSearchMessagesInput, + QaBusWaitForInput, +} from "./protocol.js"; + +const QA_HTTP_JSON_MAX_BODY_BYTES = 1024 * 1024; +const QA_HTTP_JSON_BODY_TIMEOUT_MS = 5_000; + +export async function readQaJsonBody(req: IncomingMessage): Promise { + const text = ( + await readRequestBodyWithLimit(req, { + maxBytes: QA_HTTP_JSON_MAX_BODY_BYTES, + timeoutMs: QA_HTTP_JSON_BODY_TIMEOUT_MS, + }) + ).trim(); + return text ? (JSON.parse(text) as unknown) : {}; +} + +export function writeJson(res: ServerResponse, statusCode: number, body: unknown) { + const payload = JSON.stringify(body); + res.writeHead(statusCode, { + "content-type": "application/json; charset=utf-8", + "content-length": Buffer.byteLength(payload), + }); + res.end(payload); +} + +export function writeError(res: ServerResponse, statusCode: number, error: unknown) { + writeJson(res, statusCode, { error: formatErrorMessage(error) }); +} + +export function writeQaRequestBodyLimitError(res: ServerResponse, error: unknown): boolean { + if (!isRequestBodyLimitError(error)) { + return false; + } + writeError(res, error.statusCode, requestBodyErrorToText(error.code)); + return true; +} + +export async function handleQaBusRequest(params: { + req: IncomingMessage; + res: ServerResponse; + state: QaBusState; +}): Promise { + const method = params.req.method ?? "GET"; + const url = new URL(params.req.url ?? "/", "http://127.0.0.1"); + + if (method === "GET" && url.pathname === "/health") { + writeJson(params.res, 200, { ok: true }); + return true; + } + + if (method === "GET" && url.pathname === "/v1/state") { + writeJson(params.res, 200, params.state.getSnapshot()); + return true; + } + + if (!url.pathname.startsWith("/v1/")) { + return false; + } + + if (method !== "POST") { + writeError(params.res, 405, "method not allowed"); + return true; + } + + try { + const body = (await readQaJsonBody(params.req)) as Record; + switch (url.pathname) { + case "/v1/inbound/message": + writeJson(params.res, 200, { + message: params.state.addInboundMessage(body as unknown as QaBusInboundMessageInput), + }); + return true; + case "/v1/outbound/message": + writeJson(params.res, 200, { + message: params.state.addOutboundMessage(body as unknown as QaBusOutboundMessageInput), + }); + return true; + case "/v1/actions/thread-create": + writeJson(params.res, 200, { + thread: params.state.createThread(body as unknown as QaBusCreateThreadInput), + }); + return true; + case "/v1/actions/react": + writeJson(params.res, 200, { + message: params.state.reactToMessage(body as unknown as QaBusReactToMessageInput), + }); + return true; + case "/v1/actions/edit": + writeJson(params.res, 200, { + message: params.state.editMessage(body as unknown as QaBusEditMessageInput), + }); + return true; + case "/v1/actions/delete": + writeJson(params.res, 200, { + message: params.state.deleteMessage(body as unknown as QaBusDeleteMessageInput), + }); + return true; + case "/v1/actions/read": + writeJson(params.res, 200, { + message: params.state.readMessage(body as unknown as QaBusReadMessageInput), + }); + return true; + case "/v1/actions/search": + writeJson(params.res, 200, { + messages: params.state.searchMessages(body as unknown as QaBusSearchMessagesInput), + }); + return true; + case "/v1/poll": { + const input = body as unknown as QaBusPollInput; + const timeoutMs = Math.max(0, Math.min(input.timeoutMs ?? 0, 30_000)); + const accountId = normalizeAccountId(input.accountId); + const initial = params.state.poll(input); + const effectiveStartCursor = resolveQaBusPollStartCursor({ + currentCursor: initial.cursor, + requestedCursor: input.cursor, + }); + if (initial.events.length > 0 || timeoutMs === 0) { + writeJson(params.res, 200, initial); + return true; + } + try { + await params.state.waitForCursorAdvance(effectiveStartCursor, timeoutMs, (snapshot) => { + return snapshot.events.some( + (event) => event.accountId === accountId && event.cursor > effectiveStartCursor, + ); + }); + } catch { + // timeout ok for long-poll + } + writeJson(params.res, 200, params.state.poll(input)); + return true; + } + case "/v1/wait": + writeJson(params.res, 200, { + match: await params.state.waitFor(body as unknown as QaBusWaitForInput), + }); + return true; + default: + writeError(params.res, 404, "not found"); + return true; + } + } catch (error) { + if (writeQaRequestBodyLimitError(params.res, error)) { + return true; + } + writeError(params.res, 400, error); + return true; + } +} + +export type QaBusHandler = (req: IncomingMessage, res: ServerResponse) => Promise; + +export function createBus(): { state: QaBusState; handler: QaBusHandler } { + const state = createQaBusState(); + const handler: QaBusHandler = (req, res) => handleQaBusRequest({ req, res, state }); + return { state, handler }; +} diff --git a/packages/openclaw-channel-mock-core/src/bus-queries.ts b/packages/openclaw-channel-mock-core/src/bus-queries.ts new file mode 100644 index 0000000..31dc592 --- /dev/null +++ b/packages/openclaw-channel-mock-core/src/bus-queries.ts @@ -0,0 +1,190 @@ +import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime"; +import type { + QaBusAttachment, + QaBusConversation, + QaBusEvent, + QaBusMessage, + QaBusPollInput, + QaBusPollResult, + QaBusReadMessageInput, + QaBusSearchMessagesInput, + QaBusStateSnapshot, + QaBusThread, + QaBusToolCall, +} from "./protocol.js"; + +export const DEFAULT_ACCOUNT_ID = "default"; + +export function normalizeAccountId(raw?: string): string { + const trimmed = raw?.trim(); + return trimmed || DEFAULT_ACCOUNT_ID; +} + +export function normalizeConversationFromTarget(target: string): { + conversation: QaBusConversation; + threadId?: string; +} { + const trimmed = target.trim(); + if (trimmed.startsWith("thread:")) { + const rest = trimmed.slice("thread:".length); + const slash = rest.indexOf("/"); + if (slash > 0) { + return { + conversation: { id: rest.slice(0, slash), kind: "channel" }, + threadId: rest.slice(slash + 1), + }; + } + } + if (trimmed.startsWith("channel:")) { + return { + conversation: { id: trimmed.slice("channel:".length), kind: "channel" }, + }; + } + if (trimmed.startsWith("group:")) { + return { + conversation: { id: trimmed.slice("group:".length), kind: "group" }, + }; + } + if (trimmed.startsWith("dm:")) { + return { + conversation: { id: trimmed.slice("dm:".length), kind: "direct" }, + }; + } + return { + conversation: { id: trimmed, kind: "direct" }, + }; +} + +export function cloneMessage(message: QaBusMessage): QaBusMessage { + return { + ...message, + conversation: { ...message.conversation }, + attachments: (message.attachments ?? []).map((attachment) => cloneAttachment(attachment)), + toolCalls: message.toolCalls?.map((toolCall) => cloneToolCall(toolCall)), + reactions: message.reactions.map((reaction) => ({ ...reaction })), + }; +} + +function cloneAttachment(attachment: QaBusAttachment): QaBusAttachment { + return { ...attachment }; +} + +function cloneToolCall(toolCall: QaBusToolCall): QaBusToolCall { + return { + name: toolCall.name, + ...(toolCall.arguments ? { arguments: structuredClone(toolCall.arguments) } : {}), + }; +} + +export function cloneEvent(event: QaBusEvent): QaBusEvent { + switch (event.kind) { + case "inbound-message": + case "outbound-message": + case "message-edited": + case "message-deleted": + case "reaction-added": + return { ...event, message: cloneMessage(event.message) }; + case "thread-created": + return { ...event, thread: { ...event.thread } }; + } + throw new Error("Unsupported QA bus event kind"); +} + +export function buildQaBusSnapshot(params: { + cursor: number; + conversations: Map; + threads: Map; + messages: Map; + events: QaBusEvent[]; +}): QaBusStateSnapshot { + return { + cursor: params.cursor, + conversations: Array.from(params.conversations.values()).map((conversation) => + Object.assign({}, conversation), + ), + threads: Array.from(params.threads.values()).map((thread) => Object.assign({}, thread)), + messages: Array.from(params.messages.values()).map((message) => cloneMessage(message)), + events: params.events.map((event) => cloneEvent(event)), + }; +} + +export function readQaBusMessage(params: { + messages: Map; + input: QaBusReadMessageInput; +}) { + const message = params.messages.get(params.input.messageId); + if (!message) { + throw new Error(`qa-bus message not found: ${params.input.messageId}`); + } + return cloneMessage(message); +} + +export function searchQaBusMessages(params: { + messages: Map; + input: QaBusSearchMessagesInput; +}) { + const accountId = normalizeAccountId(params.input.accountId); + const limit = Math.max(1, Math.min(params.input.limit ?? 20, 100)); + const query = normalizeOptionalLowercaseString(params.input.query); + return Array.from(params.messages.values()) + .filter((message) => message.accountId === accountId) + .filter((message) => + params.input.conversationId ? message.conversation.id === params.input.conversationId : true, + ) + .filter((message) => + params.input.threadId ? message.threadId === params.input.threadId : true, + ) + .filter((message) => { + if (!query) { + return true; + } + const attachmentHaystack = message.attachments ?? []; + const searchableAttachmentText = attachmentHaystack + .flatMap((attachment) => [ + attachment.fileName, + attachment.altText, + attachment.transcript, + attachment.mimeType, + ]) + .filter((value): value is string => Boolean(value)) + .join(" ") + .toLowerCase(); + const messageText = normalizeOptionalLowercaseString(message.text) ?? ""; + const searchableToolText = (message.toolCalls ?? []) + .map((toolCall) => toolCall.name) + .join(" ") + .toLowerCase(); + return `${messageText} ${searchableAttachmentText} ${searchableToolText}`.includes(query); + }) + .slice(-limit) + .map((message) => cloneMessage(message)); +} + +export function resolveQaBusPollStartCursor(params: { + currentCursor: number; + requestedCursor?: number; +}): number { + const requestedCursor = params.requestedCursor ?? 0; + return params.currentCursor < requestedCursor ? 0 : requestedCursor; +} + +export function pollQaBusEvents(params: { + events: QaBusEvent[]; + cursor: number; + input?: QaBusPollInput; +}): QaBusPollResult { + const accountId = normalizeAccountId(params.input?.accountId); + const effectiveStartCursor = resolveQaBusPollStartCursor({ + currentCursor: params.cursor, + requestedCursor: params.input?.cursor, + }); + const limit = Math.max(1, Math.min(params.input?.limit ?? 100, 500)); + const matches = params.events + .filter((event) => event.accountId === accountId && event.cursor > effectiveStartCursor) + .slice(0, limit) + .map((event) => cloneEvent(event)); + return { + cursor: params.cursor, + events: matches, + }; +} diff --git a/packages/openclaw-channel-mock-core/src/bus-state.ts b/packages/openclaw-channel-mock-core/src/bus-state.ts new file mode 100644 index 0000000..a052e58 --- /dev/null +++ b/packages/openclaw-channel-mock-core/src/bus-state.ts @@ -0,0 +1,248 @@ +import { randomUUID } from "node:crypto"; +import { sanitizeQaBusToolCalls } from "./protocol.js"; +import { + buildQaBusSnapshot, + cloneMessage, + normalizeAccountId, + normalizeConversationFromTarget, + pollQaBusEvents, + readQaBusMessage, + searchQaBusMessages, +} from "./bus-queries.js"; +import { createQaBusWaiterStore } from "./bus-waiters.js"; +import type { + QaBusAttachment, + QaBusConversation, + QaBusCreateThreadInput, + QaBusDeleteMessageInput, + QaBusEditMessageInput, + QaBusEvent, + QaBusInboundMessageInput, + QaBusMessage, + QaBusOutboundMessageInput, + QaBusPollInput, + QaBusReadMessageInput, + QaBusReactToMessageInput, + QaBusSearchMessagesInput, + QaBusStateSnapshot, + QaBusThread, + QaBusToolCall, + QaBusWaitForInput, +} from "./protocol.js"; + +const DEFAULT_BOT_ID = "openclaw"; +const DEFAULT_BOT_NAME = "OpenClaw QA"; + +type QaBusEventSeed = + | { kind: "inbound-message"; accountId: string; message: QaBusMessage } + | { kind: "outbound-message"; accountId: string; message: QaBusMessage } + | { kind: "thread-created"; accountId: string; thread: QaBusThread } + | { kind: "message-edited"; accountId: string; message: QaBusMessage } + | { kind: "message-deleted"; accountId: string; message: QaBusMessage } + | { + kind: "reaction-added"; + accountId: string; + message: QaBusMessage; + emoji: string; + senderId: string; + }; + +export function createQaBusState() { + const conversations = new Map(); + const threads = new Map(); + const messages = new Map(); + const events: QaBusEvent[] = []; + let cursor = 0; + const waiters = createQaBusWaiterStore(() => + buildQaBusSnapshot({ cursor, conversations, threads, messages, events }), + ); + + const pushEvent = (event: QaBusEventSeed | ((cursor: number) => QaBusEventSeed)): QaBusEvent => { + cursor += 1; + const next = typeof event === "function" ? event(cursor) : event; + const finalized = { cursor, ...next } as QaBusEvent; + events.push(finalized); + waiters.settle(); + return finalized; + }; + + const ensureConversation = (conversation: QaBusConversation): QaBusConversation => { + const existing = conversations.get(conversation.id); + if (existing) { + if (!existing.title && conversation.title) { + existing.title = conversation.title; + } + return existing; + } + const created = { ...conversation }; + conversations.set(created.id, created); + return created; + }; + + const createMessage = (params: { + direction: QaBusMessage["direction"]; + accountId: string; + conversation: QaBusConversation; + senderId: string; + senderName?: string; + text: string; + timestamp?: number; + threadId?: string; + threadTitle?: string; + replyToId?: string; + attachments?: QaBusAttachment[]; + toolCalls?: QaBusToolCall[]; + }): QaBusMessage => { + const conversation = ensureConversation(params.conversation); + const toolCalls = sanitizeQaBusToolCalls(params.toolCalls); + const message: QaBusMessage = { + id: randomUUID(), + accountId: params.accountId, + direction: params.direction, + conversation, + senderId: params.senderId, + senderName: params.senderName, + text: params.text, + timestamp: params.timestamp ?? Date.now(), + threadId: params.threadId, + threadTitle: params.threadTitle, + replyToId: params.replyToId, + attachments: params.attachments?.map((attachment) => ({ ...attachment })) ?? [], + ...(toolCalls ? { toolCalls } : {}), + reactions: [], + }; + messages.set(message.id, message); + return message; + }; + + return { + reset() { + conversations.clear(); + threads.clear(); + messages.clear(); + events.length = 0; + // Keep the cursor monotonic across resets so long-poll clients do not + // miss fresh events after the bus is cleared mid-session. + waiters.reset(); + }, + getSnapshot() { + return buildQaBusSnapshot({ cursor, conversations, threads, messages, events }); + }, + addInboundMessage(input: QaBusInboundMessageInput) { + const accountId = normalizeAccountId(input.accountId); + const message = createMessage({ + direction: "inbound", + accountId, + conversation: input.conversation, + senderId: input.senderId, + senderName: input.senderName, + text: input.text, + timestamp: input.timestamp, + threadId: input.threadId, + threadTitle: input.threadTitle, + replyToId: input.replyToId, + attachments: input.attachments, + toolCalls: input.toolCalls, + }); + pushEvent({ kind: "inbound-message", accountId, message: cloneMessage(message) }); + return cloneMessage(message); + }, + addOutboundMessage(input: QaBusOutboundMessageInput) { + const accountId = normalizeAccountId(input.accountId); + const { conversation, threadId } = normalizeConversationFromTarget(input.to); + const message = createMessage({ + direction: "outbound", + accountId, + conversation, + senderId: input.senderId?.trim() || DEFAULT_BOT_ID, + senderName: input.senderName?.trim() || DEFAULT_BOT_NAME, + text: input.text, + timestamp: input.timestamp, + threadId: input.threadId ?? threadId, + replyToId: input.replyToId, + attachments: input.attachments, + toolCalls: input.toolCalls, + }); + pushEvent({ kind: "outbound-message", accountId, message: cloneMessage(message) }); + return cloneMessage(message); + }, + createThread(input: QaBusCreateThreadInput) { + const accountId = normalizeAccountId(input.accountId); + const thread: QaBusThread = { + id: `thread-${randomUUID()}`, + accountId, + conversationId: input.conversationId, + title: input.title, + createdAt: input.timestamp ?? Date.now(), + createdBy: input.createdBy?.trim() || DEFAULT_BOT_ID, + }; + threads.set(thread.id, thread); + ensureConversation({ id: input.conversationId, kind: "channel" }); + pushEvent({ kind: "thread-created", accountId, thread: { ...thread } }); + return { ...thread }; + }, + reactToMessage(input: QaBusReactToMessageInput) { + const accountId = normalizeAccountId(input.accountId); + const message = messages.get(input.messageId); + if (!message) { + throw new Error(`qa-bus message not found: ${input.messageId}`); + } + const reaction = { + emoji: input.emoji, + senderId: input.senderId?.trim() || DEFAULT_BOT_ID, + timestamp: input.timestamp ?? Date.now(), + }; + message.reactions.push(reaction); + pushEvent({ + kind: "reaction-added", + accountId, + message: cloneMessage(message), + emoji: reaction.emoji, + senderId: reaction.senderId, + }); + return cloneMessage(message); + }, + editMessage(input: QaBusEditMessageInput) { + const accountId = normalizeAccountId(input.accountId); + const message = messages.get(input.messageId); + if (!message) { + throw new Error(`qa-bus message not found: ${input.messageId}`); + } + message.text = input.text; + message.editedAt = input.timestamp ?? Date.now(); + pushEvent({ kind: "message-edited", accountId, message: cloneMessage(message) }); + return cloneMessage(message); + }, + deleteMessage(input: QaBusDeleteMessageInput) { + const accountId = normalizeAccountId(input.accountId); + const message = messages.get(input.messageId); + if (!message) { + throw new Error(`qa-bus message not found: ${input.messageId}`); + } + message.deleted = true; + pushEvent({ kind: "message-deleted", accountId, message: cloneMessage(message) }); + return cloneMessage(message); + }, + readMessage(input: QaBusReadMessageInput) { + return readQaBusMessage({ messages, input }); + }, + searchMessages(input: QaBusSearchMessagesInput) { + return searchQaBusMessages({ messages, input }); + }, + poll(input: QaBusPollInput = {}) { + return pollQaBusEvents({ events, cursor, input }); + }, + async waitFor(input: QaBusWaitForInput) { + return await waiters.waitFor(input); + }, + async waitForCursorAdvance( + afterCursor: number, + timeoutMs: number, + shouldResolve?: (snapshot: QaBusStateSnapshot) => boolean, + ) { + return await waiters.waitForCursorAdvance(afterCursor, timeoutMs, shouldResolve); + }, + }; +} + +export type QaBusState = ReturnType; diff --git a/packages/openclaw-channel-mock-core/src/bus-waiters.ts b/packages/openclaw-channel-mock-core/src/bus-waiters.ts new file mode 100644 index 0000000..407fcfe --- /dev/null +++ b/packages/openclaw-channel-mock-core/src/bus-waiters.ts @@ -0,0 +1,135 @@ +import type { + QaBusEvent, + QaBusMessage, + QaBusStateSnapshot, + QaBusThread, + QaBusWaitForInput, +} from "./protocol.js"; + +export const DEFAULT_WAIT_TIMEOUT_MS = 5_000; + +export type QaBusWaitMatch = QaBusEvent | QaBusMessage | QaBusThread; + +type Waiter = { + resolve: (event: QaBusWaitMatch) => void; + reject: (error: Error) => void; + timer: NodeJS.Timeout; + matcher: (snapshot: QaBusStateSnapshot) => QaBusWaitMatch | null; +}; + +type CursorWaiter = { + resolve: () => void; + reject: (error: Error) => void; + timer: NodeJS.Timeout; + afterCursor: number; + shouldResolve?: (snapshot: QaBusStateSnapshot) => boolean; +}; + +function createQaBusMatcher( + input: QaBusWaitForInput, +): (snapshot: QaBusStateSnapshot) => QaBusWaitMatch | null { + return (snapshot) => { + if (input.kind === "event-kind") { + return snapshot.events.find((event) => event.kind === input.eventKind) ?? null; + } + if (input.kind === "thread-id") { + return snapshot.threads.find((thread) => thread.id === input.threadId) ?? null; + } + return ( + snapshot.messages.find( + (message) => + (!input.direction || message.direction === input.direction) && + message.text.includes(input.textIncludes), + ) ?? null + ); + }; +} + +export function createQaBusWaiterStore(getSnapshot: () => QaBusStateSnapshot) { + const waiters = new Set(); + const cursorWaiters = new Set(); + + return { + reset(reason = "qa-bus reset") { + for (const waiter of waiters) { + clearTimeout(waiter.timer); + waiter.reject(new Error(reason)); + } + waiters.clear(); + for (const waiter of cursorWaiters) { + clearTimeout(waiter.timer); + waiter.reject(new Error(reason)); + } + cursorWaiters.clear(); + }, + settle() { + if (waiters.size === 0 && cursorWaiters.size === 0) { + return; + } + const snapshot = getSnapshot(); + for (const waiter of Array.from(waiters)) { + const match = waiter.matcher(snapshot); + if (!match) { + continue; + } + clearTimeout(waiter.timer); + waiters.delete(waiter); + waiter.resolve(match); + } + for (const waiter of Array.from(cursorWaiters)) { + if (snapshot.cursor <= waiter.afterCursor) { + continue; + } + if (waiter.shouldResolve && !waiter.shouldResolve(snapshot)) { + continue; + } + clearTimeout(waiter.timer); + cursorWaiters.delete(waiter); + waiter.resolve(); + } + }, + async waitFor(input: QaBusWaitForInput) { + const matcher = createQaBusMatcher(input); + const immediate = matcher(getSnapshot()); + if (immediate) { + return immediate; + } + return await new Promise((resolve, reject) => { + const timeoutMs = input.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS; + const waiter: Waiter = { + resolve, + reject, + matcher, + timer: setTimeout(() => { + waiters.delete(waiter); + reject(new Error(`qa-bus wait timeout after ${timeoutMs}ms`)); + }, timeoutMs), + }; + waiters.add(waiter); + }); + }, + async waitForCursorAdvance( + afterCursor: number, + timeoutMs: number, + shouldResolve?: (snapshot: QaBusStateSnapshot) => boolean, + ) { + const snapshot = getSnapshot(); + if (snapshot.cursor > afterCursor && (!shouldResolve || shouldResolve(snapshot))) { + return; + } + return await new Promise((resolve, reject) => { + const waiter: CursorWaiter = { + resolve, + reject, + afterCursor, + shouldResolve, + timer: setTimeout(() => { + cursorWaiters.delete(waiter); + reject(new Error(`qa-bus wait timeout after ${timeoutMs}ms`)); + }, timeoutMs), + }; + cursorWaiters.add(waiter); + }); + }, + }; +} diff --git a/packages/openclaw-channel-mock-core/src/channel-setup-plugin.ts b/packages/openclaw-channel-mock-core/src/channel-setup-plugin.ts new file mode 100644 index 0000000..00a2674 --- /dev/null +++ b/packages/openclaw-channel-mock-core/src/channel-setup-plugin.ts @@ -0,0 +1,42 @@ +import { getChatChannelMeta } from "openclaw/plugin-sdk/channel-plugin-common"; +import { createChannelMockAccountHelpers } from "./accounts.js"; +import { buildChannelMockConfigSchema } from "./config-schema.js"; +import type { ChannelPlugin } from "./runtime-api.js"; +import { createApplyChannelMockSetup } from "./setup.js"; +import type { CoreConfig, ResolvedChannelMockAccount } from "./types.js"; + +export function createChannelMockSetupPlugin(params: { + channelId: string; + label: string; +}): ChannelPlugin { + const { channelId, label } = params; + const meta = { ...getChatChannelMeta(channelId), label }; + const helpers = createChannelMockAccountHelpers({ channelId }); + const applySetup = createApplyChannelMockSetup({ channelId }); + const configSchema = buildChannelMockConfigSchema(channelId); + + return { + id: channelId, + meta, + capabilities: { + chatTypes: ["direct", "group"], + }, + reload: { configPrefixes: [`channels.${channelId}`] }, + configSchema, + setup: { + applyAccountConfig: ({ cfg, accountId, input }) => + applySetup({ cfg, accountId, input: input as Record }), + }, + config: { + listAccountIds: (cfg) => helpers.listAccountIds(cfg as CoreConfig), + resolveAccount: (cfg, accountId) => + helpers.resolveAccount({ cfg: cfg as CoreConfig, accountId }), + defaultAccountId: (cfg) => helpers.resolveDefaultAccountId(cfg as CoreConfig), + isConfigured: (account) => account.configured, + resolveAllowFrom: ({ cfg, accountId }) => + helpers.resolveAccount({ cfg: cfg as CoreConfig, accountId }).config.allowFrom, + resolveDefaultTo: ({ cfg, accountId }) => + helpers.resolveAccount({ cfg: cfg as CoreConfig, accountId }).config.defaultTo, + }, + }; +} diff --git a/packages/openclaw-channel-mock-core/src/config-schema.ts b/packages/openclaw-channel-mock-core/src/config-schema.ts new file mode 100644 index 0000000..7de4347 --- /dev/null +++ b/packages/openclaw-channel-mock-core/src/config-schema.ts @@ -0,0 +1,50 @@ +import { + ToolPolicySchema, + buildChannelConfigSchema, +} from "openclaw/plugin-sdk/channel-config-schema"; +import { z } from "openclaw/plugin-sdk/zod"; + +const ChannelMockActionConfigSchema = z + .object({ + messages: z.boolean().optional(), + reactions: z.boolean().optional(), + search: z.boolean().optional(), + threads: z.boolean().optional(), + }) + .strict(); + +const ChannelMockGroupConfigSchema = z + .object({ + requireMention: z.boolean().optional(), + tools: ToolPolicySchema.optional(), + toolsBySender: z.record(z.string(), ToolPolicySchema).optional(), + }) + .strict(); + +const ChannelMockAccountConfigSchema = z + .object({ + name: z.string().optional(), + enabled: z.boolean().optional(), + baseUrl: z.string().url().optional(), + botUserId: z.string().optional(), + botDisplayName: z.string().optional(), + pollTimeoutMs: z.number().int().min(100).max(30_000).optional(), + allowFrom: z.array(z.union([z.string(), z.number()])).optional(), + groupPolicy: z.enum(["open", "allowlist", "disabled"]).optional(), + groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(), + groups: z.record(z.string(), ChannelMockGroupConfigSchema).optional(), + defaultTo: z.string().optional(), + actions: ChannelMockActionConfigSchema.optional(), + }) + .strict(); + +const ChannelMockConfigSchema = ChannelMockAccountConfigSchema.extend({ + accounts: z.record(z.string(), ChannelMockAccountConfigSchema.partial()).optional(), + defaultAccount: z.string().optional(), +}).strict(); + +export function buildChannelMockConfigSchema( + _channelId: string, +): ReturnType { + return buildChannelConfigSchema(ChannelMockConfigSchema); +} diff --git a/packages/openclaw-channel-mock-core/src/gateway.ts b/packages/openclaw-channel-mock-core/src/gateway.ts new file mode 100644 index 0000000..6aa9160 --- /dev/null +++ b/packages/openclaw-channel-mock-core/src/gateway.ts @@ -0,0 +1,57 @@ +import { pollQaBus } from "./bus-client.js"; +import { handleInbound } from "./inbound.js"; +import type { ChannelGatewayContext, PluginRuntime } from "./runtime-api.js"; +import type { CoreConfig, ResolvedChannelMockAccount } from "./types.js"; + +export async function startChannelMockGatewayAccount(params: { + channelId: string; + channelLabel: string; + ctx: ChannelGatewayContext; + autoThread: boolean; + getRuntime: () => PluginRuntime; +}) { + const { channelId, channelLabel, ctx, autoThread, getRuntime } = params; + const account = ctx.account; + if (!account.configured) { + throw new Error(`${channelId} is not configured for account "${account.accountId}"`); + } + ctx.setStatus({ + accountId: account.accountId, + running: true, + configured: true, + enabled: account.enabled, + baseUrl: account.baseUrl, + }); + let cursor = 0; + try { + while (!ctx.abortSignal.aborted) { + const result = await pollQaBus({ + baseUrl: account.baseUrl, + accountId: account.accountId, + cursor, + timeoutMs: account.pollTimeoutMs, + signal: ctx.abortSignal, + }); + cursor = result.cursor; + for (const event of result.events) { + if (event.kind !== "inbound-message") { + continue; + } + await handleInbound({ + channelId, + channelLabel, + account, + config: ctx.cfg as CoreConfig, + message: event.message, + autoThread, + getRuntime, + }); + } + } + } catch (error) { + if (!(error instanceof Error) || error.name !== "AbortError") { + throw error; + } + } + ctx.setStatus({ accountId: account.accountId, running: false }); +} diff --git a/packages/openclaw-channel-mock-core/src/inbound.ts b/packages/openclaw-channel-mock-core/src/inbound.ts new file mode 100644 index 0000000..957a2e7 --- /dev/null +++ b/packages/openclaw-channel-mock-core/src/inbound.ts @@ -0,0 +1,329 @@ +import { resolveStableChannelMessageIngress } from "openclaw/plugin-sdk/channel-ingress-runtime"; +import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; +import { resolveInboundRouteEnvelopeBuilderWithRuntime } from "openclaw/plugin-sdk/inbound-envelope"; +import { + buildAgentMediaPayload, + saveMediaBuffer, + saveMediaSource, +} from "openclaw/plugin-sdk/media-runtime"; +import { buildQaTarget, createQaBusThread, sendQaBusMessage } from "./bus-client.js"; +import { + sanitizeQaBusToolCallArguments, + type QaBusMessage, + type QaBusToolCall, +} from "./protocol.js"; +import type { PluginRuntime } from "./runtime-api.js"; +import type { CoreConfig, ResolvedChannelMockAccount } from "./types.js"; + +export function isHttpMediaUrl(value: string): boolean { + try { + const parsed = new URL(value); + return parsed.protocol === "http:" || parsed.protocol === "https:"; + } catch { + return false; + } +} + +function normalizeBase64ForCompare(value: string): string { + return value.replace(/=+$/u, "").replace(/-/gu, "+").replace(/_/gu, "/"); +} + +function decodeAttachmentBase64(value: string): Buffer | null { + const buffer = Buffer.from(value, "base64"); + if (normalizeBase64ForCompare(buffer.toString("base64")) !== normalizeBase64ForCompare(value)) { + return null; + } + return buffer; +} + +async function resolveInboundMediaPayload(attachments: QaBusMessage["attachments"]) { + if (!Array.isArray(attachments) || attachments.length === 0) { + return {}; + } + const mediaList: Array<{ path: string; contentType?: string | null }> = []; + for (const attachment of attachments) { + if (!attachment?.mimeType) { + continue; + } + if (typeof attachment.contentBase64 === "string" && attachment.contentBase64.trim()) { + const buffer = decodeAttachmentBase64(attachment.contentBase64); + if (!buffer) { + console.warn("[channel-mock] inbound attachment contentBase64 rejected (invalid base64)"); + continue; + } + const saved = await saveMediaBuffer( + buffer, + attachment.mimeType, + "inbound", + undefined, + attachment.fileName, + ); + mediaList.push({ path: saved.path, contentType: saved.contentType }); + continue; + } + if (typeof attachment.url === "string" && attachment.url.trim()) { + if (!isHttpMediaUrl(attachment.url)) { + console.warn( + `[channel-mock] inbound attachment URL rejected (non-http scheme): ${attachment.url}`, + ); + continue; + } + const saved = await saveMediaSource(attachment.url, undefined, "inbound"); + mediaList.push({ path: saved.path, contentType: saved.contentType }); + } + } + return mediaList.length > 0 ? buildAgentMediaPayload(mediaList) : {}; +} + +function resolveGroupConfig(params: { + account: ResolvedChannelMockAccount; + conversationId: string; + target: string; +}) { + const groups = params.account.config.groups; + return groups?.[params.conversationId] ?? groups?.[params.target] ?? groups?.["*"]; +} + +function extractDeliveryText(payload: unknown): string { + if (payload && typeof payload === "object" && "text" in payload) { + return (payload as { text?: string }).text ?? ""; + } + return ""; +} + +export function buildDeliveryCallback(params: { + account: ResolvedChannelMockAccount; + inbound: QaBusMessage; + target: string; + toolCalls: QaBusToolCall[]; + autoThread: boolean; +}): (payload: unknown) => Promise { + const { account, inbound, target, toolCalls, autoThread } = params; + const slackAutoThread = autoThread && !inbound.threadId; + let autoThreadId: string | undefined; + let autoThreadDeliveries = 0; + + return async (payload: unknown) => { + const text = extractDeliveryText(payload); + if (!text.trim()) { + return; + } + if (slackAutoThread) { + if (!autoThreadId) { + const title = + inbound.threadTitle?.trim() || + inbound.conversation.title?.trim() || + inbound.text.slice(0, 60) || + "thread"; + const { thread } = await createQaBusThread({ + baseUrl: account.baseUrl, + accountId: account.accountId, + conversationId: inbound.conversation.id, + title, + createdBy: account.botUserId, + }); + autoThreadId = thread.id; + } + autoThreadDeliveries += 1; + await sendQaBusMessage({ + baseUrl: account.baseUrl, + accountId: account.accountId, + to: `thread:${inbound.conversation.id}/${autoThreadId}`, + text, + senderId: account.botUserId, + senderName: account.botDisplayName, + threadId: autoThreadId, + replyToId: autoThreadDeliveries === 1 ? inbound.id : undefined, + toolCalls, + }); + return; + } + await sendQaBusMessage({ + baseUrl: account.baseUrl, + accountId: account.accountId, + to: target, + text, + senderId: account.botUserId, + senderName: account.botDisplayName, + threadId: inbound.threadId, + replyToId: inbound.id, + toolCalls, + }); + }; +} + +export async function handleInbound(params: { + channelId: string; + channelLabel: string; + account: ResolvedChannelMockAccount; + config: CoreConfig; + message: QaBusMessage; + autoThread: boolean; + getRuntime: () => PluginRuntime; +}) { + const runtime = params.getRuntime(); + const inbound = params.message; + const target = buildQaTarget({ + chatType: inbound.conversation.kind, + conversationId: inbound.conversation.id, + threadId: inbound.threadId, + }); + const toolCalls: QaBusToolCall[] = []; + const { route, buildEnvelope } = resolveInboundRouteEnvelopeBuilderWithRuntime({ + cfg: params.config as OpenClawConfig, + channel: params.channelId, + accountId: params.account.accountId, + peer: { + kind: + inbound.conversation.kind === "direct" + ? "direct" + : inbound.conversation.kind === "group" + ? "group" + : "channel", + id: target, + }, + runtime: runtime.channel, + sessionStore: params.config.session?.store, + }); + const isGroup = inbound.conversation.kind !== "direct"; + const wasMentioned = isGroup + ? runtime.channel.mentions.matchesMentionPatterns( + inbound.text, + runtime.channel.mentions.buildMentionRegexes( + params.config as OpenClawConfig, + route.agentId, + ), + ) + : undefined; + const groupConfig = isGroup + ? resolveGroupConfig({ + account: params.account, + conversationId: inbound.conversation.id, + target, + }) + : undefined; + const access = await resolveStableChannelMessageIngress({ + channelId: params.channelId, + accountId: params.account.accountId, + identity: { key: "sender", entryIdPrefix: `${params.channelId}-entry` }, + groupAllowFromFallbackToAllowFrom: true, + subject: { stableId: inbound.senderId }, + conversation: { + kind: inbound.conversation.kind, + id: inbound.conversation.id, + threadId: inbound.threadId, + title: inbound.conversation.title, + }, + mentionFacts: isGroup + ? { canDetectMention: true, wasMentioned: wasMentioned ?? false } + : undefined, + dmPolicy: "open", + groupPolicy: params.account.config.groupPolicy ?? "open", + policy: { + activation: isGroup + ? { requireMention: groupConfig?.requireMention ?? false, allowTextCommands: true } + : undefined, + }, + allowFrom: params.account.config.allowFrom, + groupAllowFrom: params.account.config.groupAllowFrom, + }); + if (access.ingress.admission !== "dispatch") { + return; + } + const { storePath, body } = buildEnvelope({ + channel: params.channelLabel, + from: inbound.senderName || inbound.senderId, + timestamp: inbound.timestamp, + body: inbound.text, + }); + const mediaPayload = await resolveInboundMediaPayload(inbound.attachments); + + const ctxPayload = runtime.channel.reply.finalizeInboundContext({ + Body: body, + BodyForAgent: inbound.text, + RawBody: inbound.text, + CommandBody: inbound.text, + From: target, + To: target, + SessionKey: route.sessionKey, + AccountId: route.accountId ?? params.account.accountId, + ChatType: inbound.conversation.kind === "direct" ? "direct" : "group", + WasMentioned: wasMentioned, + ConversationLabel: + inbound.threadTitle || + inbound.conversation.title || + inbound.senderName || + inbound.conversation.id, + GroupSubject: isGroup + ? inbound.threadTitle || inbound.conversation.title || inbound.conversation.id + : undefined, + GroupChannel: inbound.conversation.kind === "channel" ? inbound.conversation.id : undefined, + NativeChannelId: inbound.conversation.id, + MessageThreadId: inbound.threadId, + ThreadLabel: inbound.threadTitle, + ThreadParentId: inbound.threadId ? inbound.conversation.id : undefined, + SenderName: inbound.senderName, + SenderId: inbound.senderId, + Provider: params.channelId, + Surface: params.channelId, + MessageSid: inbound.id, + MessageSidFull: inbound.id, + ReplyToId: inbound.replyToId, + Timestamp: inbound.timestamp, + OriginatingChannel: params.channelId, + OriginatingTo: target, + CommandAuthorized: true, + ...mediaPayload, + }); + + await runtime.channel.turn.runAssembled({ + cfg: params.config as OpenClawConfig, + channel: params.channelId, + accountId: params.account.accountId, + agentId: route.agentId, + routeSessionKey: route.sessionKey, + storePath, + ctxPayload, + recordInboundSession: runtime.channel.session.recordInboundSession, + dispatchReplyWithBufferedBlockDispatcher: + runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher, + delivery: { + deliver: buildDeliveryCallback({ + account: params.account, + inbound, + target, + toolCalls, + autoThread: params.autoThread, + }), + onError: (error) => { + throw error instanceof Error + ? error + : new Error(`${params.channelId} dispatch failed: ${String(error)}`); + }, + }, + replyOptions: { + onToolStart: (payload) => { + if (payload.phase && payload.phase !== "start") { + return; + } + const name = payload.name?.trim(); + if (!name) { + return; + } + const args = sanitizeQaBusToolCallArguments(payload.args); + toolCalls.push({ + name, + ...(args && Object.keys(args).length > 0 ? { arguments: args } : {}), + }); + }, + }, + replyPipeline: {}, + record: { + onRecordError: (error) => { + throw error instanceof Error + ? error + : new Error(`${params.channelId} session record failed: ${String(error)}`); + }, + }, + }); +} diff --git a/packages/openclaw-channel-mock-core/src/index.ts b/packages/openclaw-channel-mock-core/src/index.ts new file mode 100644 index 0000000..e1b8f38 --- /dev/null +++ b/packages/openclaw-channel-mock-core/src/index.ts @@ -0,0 +1,38 @@ +export { createChannelMockAccountHelpers } from "./accounts.js"; +export { createBus } from "./bus-handler.js"; +export { + createQaBusThread, + deleteQaBusMessage, + editQaBusMessage, + getQaBusState, + injectQaBusInboundMessage, + pollQaBus, + reactToQaBusMessage, + readQaBusMessage, + searchQaBusMessages, + sendQaBusMessage, +} from "./bus-client.js"; +export type { + QaBusAttachment, + QaBusConversation, + QaBusConversationKind, + QaBusEvent, + QaBusInboundMessageInput, + QaBusPollResult, + QaBusStateSnapshot, + QaBusThread, + QaBusToolCall, +} from "./protocol.js"; +export { createChannelMockSetupPlugin } from "./channel-setup-plugin.js"; +export { buildDeliveryCallback, handleInbound } from "./inbound.js"; +export { createChannelMockMessageActions } from "./plugin-actions.js"; +export type { ChannelSurface } from "./plugin-actions.js"; +export { createChannelMockPlugin } from "./plugin.js"; +export type { QaBusMessage } from "./protocol.js"; +export { createChannelMockRuntimeStore } from "./runtime.js"; +export type { + ChannelMockAccountConfig, + ChannelMockConfig, + CoreConfig, + ResolvedChannelMockAccount, +} from "./types.js"; diff --git a/packages/openclaw-channel-mock-core/src/outbound.ts b/packages/openclaw-channel-mock-core/src/outbound.ts new file mode 100644 index 0000000..d80838f --- /dev/null +++ b/packages/openclaw-channel-mock-core/src/outbound.ts @@ -0,0 +1,34 @@ +import type { ChannelMockAccountHelpers } from "./accounts.js"; +import { buildQaTarget, parseQaTarget, sendQaBusMessage } from "./bus-client.js"; +import type { CoreConfig } from "./types.js"; + +export function createSendChannelMockText(params: { helpers: ChannelMockAccountHelpers }) { + const { helpers } = params; + return async function sendChannelMockText(input: { + cfg: CoreConfig; + accountId?: string | null; + to: string; + text: string; + threadId?: string | number | null; + replyToId?: string | number | null; + }) { + const account = helpers.resolveAccount({ cfg: input.cfg, accountId: input.accountId }); + const parsed = parseQaTarget(input.to); + const resolvedThreadId = input.threadId == null ? parsed.threadId : String(input.threadId); + const { message } = await sendQaBusMessage({ + baseUrl: account.baseUrl, + accountId: account.accountId, + to: buildQaTarget({ + chatType: parsed.chatType, + conversationId: parsed.conversationId, + threadId: resolvedThreadId, + }), + text: input.text, + senderId: account.botUserId, + senderName: account.botDisplayName, + threadId: resolvedThreadId, + replyToId: input.replyToId == null ? undefined : String(input.replyToId), + }); + return { to: input.to, messageId: message.id }; + }; +} diff --git a/packages/openclaw-channel-mock-core/src/plugin-actions.ts b/packages/openclaw-channel-mock-core/src/plugin-actions.ts new file mode 100644 index 0000000..d47366d --- /dev/null +++ b/packages/openclaw-channel-mock-core/src/plugin-actions.ts @@ -0,0 +1,321 @@ +import { jsonResult, readStringParam } from "openclaw/plugin-sdk/channel-actions"; +import { extractToolSend } from "openclaw/plugin-sdk/tool-send"; +import { Type } from "typebox"; +import type { ChannelMockAccountHelpers } from "./accounts.js"; +import { + buildQaTarget, + createQaBusThread, + deleteQaBusMessage, + editQaBusMessage, + parseQaTarget, + reactToQaBusMessage, + readQaBusMessage, + searchQaBusMessages, + sendQaBusMessage, +} from "./bus-client.js"; +import type { ChannelMessageActionAdapter, ChannelMessageActionName } from "./runtime-api.js"; +import type { CoreConfig } from "./types.js"; + +export type ChannelSurface = "discord" | "slack"; + +function listActions(params: { + surface: ChannelSurface; + helpers: ChannelMockAccountHelpers; + cfg: CoreConfig; + accountId?: string | null; +}): ChannelMessageActionName[] { + const { surface, helpers, cfg, accountId } = params; + // No enabled/configured gate here: this list drives tool-schema discovery, + // which the SDK invokes with an empty cfg before the runtime config is + // merged in. Hiding actions there leaves the agent with only the SDK's + // hardcoded `send` fallback. + const account = helpers.resolveAccount({ cfg, accountId }); + const isSlack = surface === "slack"; + const actions = new Set(); + if (!isSlack) { + actions.add("send"); + } + if (account.config.actions?.messages !== false) { + actions.add("read"); + actions.add("edit"); + actions.add("delete"); + } + if (account.config.actions?.reactions !== false) { + actions.add("react"); + actions.add("reactions"); + } + if (!isSlack && account.config.actions?.threads !== false) { + actions.add("thread-create"); + actions.add("thread-reply"); + } + if (account.config.actions?.search !== false) { + actions.add("search"); + } + return Array.from(actions); +} + +function readSendText(params: Record) { + return ( + readStringParam(params, "message", { allowEmpty: true }) ?? + readStringParam(params, "text", { allowEmpty: true }) ?? + readStringParam(params, "content", { allowEmpty: true }) + ); +} + +// Canonical destination fallback — `to` first, then `target`, then legacy `channelId`. +function resolveDestination(params: Record): string | undefined { + const explicitTo = readStringParam(params, "to"); + if (explicitTo) { + return explicitTo; + } + const target = readStringParam(params, "target"); + if (target) { + if (/^(dm|channel|group):|^thread:[^/]+\/.+/i.test(target)) { + return target; + } + return buildQaTarget({ chatType: "channel", conversationId: target }); + } + const channelId = readStringParam(params, "channelId"); + if (channelId) { + return buildQaTarget({ chatType: "channel", conversationId: channelId }); + } + return undefined; +} + +const SLACK_DISABLED_ACTIONS = new Set([ + "send", + "sendMessage", + "thread-create", + "thread-reply", + "threadReply", +]); + +export function createChannelMockMessageActions(params: { + channelId: string; + surface: ChannelSurface; + helpers: ChannelMockAccountHelpers; +}): ChannelMessageActionAdapter { + const { surface, helpers, channelId } = params; + + return { + describeMessageTool: (context) => ({ + actions: listActions({ + surface, + helpers, + cfg: context.cfg as CoreConfig, + accountId: context.accountId, + }), + capabilities: [], + schema: { + properties: { + to: Type.Optional(Type.String()), + target: Type.Optional(Type.String()), + channelId: Type.Optional(Type.String()), + threadId: Type.Optional(Type.String()), + messageId: Type.Optional(Type.String()), + emoji: Type.Optional(Type.String()), + title: Type.Optional(Type.String()), + query: Type.Optional(Type.String()), + text: Type.Optional(Type.String()), + message: Type.Optional(Type.String()), + content: Type.Optional(Type.String()), + replyTo: Type.Optional(Type.String()), + replyToId: Type.Optional(Type.String()), + }, + }, + }), + extractToolSend: ({ args }: { args: Record }) => { + const action = typeof args.action === "string" ? args.action.trim() : ""; + if (action === "send") { + const to = resolveDestination(args); + const threadId = readStringParam(args, "threadId"); + return to ? { to, threadId } : null; + } + if (action === "sendMessage") { + return extractToolSend(args, "sendMessage") ?? null; + } + if (action === "threadReply") { + const destination = resolveDestination(args); + const threadId = readStringParam(args, "threadId"); + if (!destination || !threadId) { + return null; + } + const { conversationId } = parseQaTarget(destination); + return { to: `thread:${conversationId}/${threadId}` }; + } + return null; + }, + handleAction: async (context) => { + const { action, cfg, accountId, params: actionParams } = context; + if (surface === "slack" && SLACK_DISABLED_ACTIONS.has(action)) { + throw new Error(`${channelId} slack surface does not expose action "${action}"`); + } + const account = helpers.resolveAccount({ cfg: cfg as CoreConfig, accountId }); + const baseUrl = account.baseUrl; + + switch (action) { + case "send": { + const to = resolveDestination(actionParams); + const text = readSendText(actionParams); + if (!to || text === undefined) { + throw new Error( + `${channelId} send requires a destination (to/target/channelId) and message/text`, + ); + } + const parsed = parseQaTarget(to); + const threadId = readStringParam(actionParams, "threadId") ?? parsed.threadId; + const { message } = await sendQaBusMessage({ + baseUrl, + accountId: account.accountId, + to: buildQaTarget({ + chatType: parsed.chatType, + conversationId: parsed.conversationId, + threadId, + }), + text, + senderId: account.botUserId, + senderName: account.botDisplayName, + threadId, + replyToId: + readStringParam(actionParams, "replyTo") ?? + readStringParam(actionParams, "replyToId"), + }); + return jsonResult({ message }); + } + case "thread-create": { + const destination = resolveDestination(actionParams); + if (!destination) { + throw new Error( + `${channelId} thread-create requires a destination (to/target/channelId)`, + ); + } + const { conversationId } = parseQaTarget(destination); + const title = readStringParam(actionParams, "title") ?? "QA thread"; + const { thread } = await createQaBusThread({ + baseUrl, + accountId: account.accountId, + conversationId, + title, + createdBy: account.botUserId, + }); + const body = readSendText(actionParams); + const target = `thread:${conversationId}/${thread.id}`; + if (body !== undefined && body.trim() !== "") { + const { message } = await sendQaBusMessage({ + baseUrl, + accountId: account.accountId, + to: target, + text: body, + senderId: account.botUserId, + senderName: account.botDisplayName, + threadId: thread.id, + }); + return jsonResult({ thread, threadId: thread.id, target, message }); + } + return jsonResult({ thread, threadId: thread.id, target }); + } + case "thread-reply": { + const destination = resolveDestination(actionParams); + const threadId = readStringParam(actionParams, "threadId"); + const text = readSendText(actionParams); + if (!destination) { + throw new Error( + `${channelId} thread-reply requires a destination (to/target/channelId)`, + ); + } + if (!threadId) { + throw new Error(`${channelId} thread-reply requires threadId`); + } + if (text === undefined) { + throw new Error(`${channelId} thread-reply requires text/message`); + } + const { conversationId } = parseQaTarget(destination); + const { message } = await sendQaBusMessage({ + baseUrl, + accountId: account.accountId, + to: `thread:${conversationId}/${threadId}`, + text, + senderId: account.botUserId, + senderName: account.botDisplayName, + threadId, + }); + return jsonResult({ message }); + } + case "react": { + const messageId = readStringParam(actionParams, "messageId"); + const emoji = readStringParam(actionParams, "emoji"); + if (!messageId || !emoji) { + throw new Error(`${channelId} react requires messageId and emoji`); + } + const { message } = await reactToQaBusMessage({ + baseUrl, + accountId: account.accountId, + messageId, + emoji, + senderId: account.botUserId, + }); + return jsonResult({ message }); + } + case "reactions": + case "read": { + const messageId = readStringParam(actionParams, "messageId"); + if (!messageId) { + throw new Error(`${channelId} ${action} requires messageId`); + } + const { message } = await readQaBusMessage({ + baseUrl, + accountId: account.accountId, + messageId, + }); + return jsonResult({ message }); + } + case "edit": { + const messageId = readStringParam(actionParams, "messageId"); + const text = readStringParam(actionParams, "text"); + if (!messageId || !text) { + throw new Error(`${channelId} edit requires messageId and text`); + } + const { message } = await editQaBusMessage({ + baseUrl, + accountId: account.accountId, + messageId, + text, + }); + return jsonResult({ message }); + } + case "delete": { + const messageId = readStringParam(actionParams, "messageId"); + if (!messageId) { + throw new Error(`${channelId} delete requires messageId`); + } + const { message } = await deleteQaBusMessage({ + baseUrl, + accountId: account.accountId, + messageId, + }); + return jsonResult({ message }); + } + case "search": { + const query = readStringParam(actionParams, "query"); + const destination = resolveDestination(actionParams); + const conversationId = destination + ? parseQaTarget(destination).conversationId + : undefined; + const threadId = readStringParam(actionParams, "threadId"); + const { messages } = await searchQaBusMessages({ + baseUrl, + input: { + accountId: account.accountId, + query, + conversationId, + threadId, + }, + }); + return jsonResult({ messages }); + } + default: + throw new Error(`${channelId} action not implemented: ${action}`); + } + }, + }; +} diff --git a/packages/openclaw-channel-mock-core/src/plugin.ts b/packages/openclaw-channel-mock-core/src/plugin.ts new file mode 100644 index 0000000..114c2a9 --- /dev/null +++ b/packages/openclaw-channel-mock-core/src/plugin.ts @@ -0,0 +1,196 @@ +import { + buildChannelOutboundSessionRoute, + buildThreadAwareOutboundSessionRoute, + createChatChannelPlugin, +} from "openclaw/plugin-sdk/channel-core"; +import { + createMessageReceiptFromOutboundResults, + defineChannelMessageAdapter, +} from "openclaw/plugin-sdk/channel-message"; +import { getChatChannelMeta } from "openclaw/plugin-sdk/channel-plugin-common"; +import { DEFAULT_ACCOUNT_ID, createChannelMockAccountHelpers } from "./accounts.js"; +import { buildQaTarget, normalizeQaTarget, parseQaTarget } from "./bus-client.js"; +import { buildChannelMockConfigSchema } from "./config-schema.js"; +import { startChannelMockGatewayAccount } from "./gateway.js"; +import { createSendChannelMockText } from "./outbound.js"; +import { createChannelMockMessageActions, type ChannelSurface } from "./plugin-actions.js"; +import type { ChannelPlugin, PluginRuntime } from "./runtime-api.js"; +import { createApplyChannelMockSetup } from "./setup.js"; +import { createChannelMockStatus } from "./status.js"; +import type { CoreConfig, ResolvedChannelMockAccount } from "./types.js"; + +export function createChannelMockPlugin(params: { + channelId: string; + label: string; + surface: ChannelSurface; + autoThread: boolean; + getRuntime: () => PluginRuntime; +}): ChannelPlugin { + const { channelId, label, surface, autoThread, getRuntime } = params; + const meta = { ...getChatChannelMeta(channelId), label }; + const helpers = createChannelMockAccountHelpers({ channelId }); + const applySetup = createApplyChannelMockSetup({ channelId }); + const sendText = createSendChannelMockText({ helpers }); + const messageActions = createChannelMockMessageActions({ channelId, surface, helpers }); + const status = createChannelMockStatus(); + const configSchema = buildChannelMockConfigSchema(channelId); + + const messageAdapter = defineChannelMessageAdapter({ + id: channelId, + durableFinal: { + capabilities: { + text: true, + replyTo: true, + thread: true, + messageSendingHooks: true, + }, + }, + send: { + text: async (ctx) => { + const result = await sendText({ + cfg: ctx.cfg as CoreConfig, + accountId: ctx.accountId, + to: ctx.to, + text: ctx.text, + threadId: ctx.threadId, + replyToId: ctx.replyToId, + }); + const threadId = ctx.threadId == null ? undefined : String(ctx.threadId); + const replyToId = ctx.replyToId ?? undefined; + return { + messageId: result.messageId, + receipt: createMessageReceiptFromOutboundResults({ + results: [{ channel: channelId, messageId: result.messageId }], + threadId, + replyToId, + kind: "text", + }), + }; + }, + }, + }); + + return createChatChannelPlugin({ + base: { + id: channelId, + meta, + capabilities: { + chatTypes: ["direct", "group"], + }, + reload: { configPrefixes: [`channels.${channelId}`] }, + configSchema, + setup: { + applyAccountConfig: ({ cfg, accountId, input }) => + applySetup({ cfg, accountId, input: input as Record }), + }, + config: { + listAccountIds: (cfg) => helpers.listAccountIds(cfg as CoreConfig), + resolveAccount: (cfg, accountId) => + helpers.resolveAccount({ cfg: cfg as CoreConfig, accountId }), + defaultAccountId: (cfg) => helpers.resolveDefaultAccountId(cfg as CoreConfig), + isConfigured: (account) => account.configured, + resolveAllowFrom: ({ cfg, accountId }) => + helpers.resolveAccount({ cfg: cfg as CoreConfig, accountId }).config.allowFrom, + resolveDefaultTo: ({ cfg, accountId }) => + helpers.resolveAccount({ cfg: cfg as CoreConfig, accountId }).config.defaultTo, + }, + messaging: { + normalizeTarget: normalizeQaTarget, + parseExplicitTarget: ({ raw }) => { + const parsed = parseQaTarget(raw); + return { + to: buildQaTarget(parsed), + threadId: parsed.threadId, + chatType: parsed.chatType, + }; + }, + inferTargetChatType: ({ to }) => parseQaTarget(to).chatType, + targetResolver: { + looksLikeId: (raw) => raw.trim().length > 0, + hint: "", + }, + resolveOutboundSessionRoute: ({ + cfg, + agentId, + accountId, + target, + replyToId, + threadId, + currentSessionKey, + }) => { + const parsed = parseQaTarget(target); + const baseRoute = buildChannelOutboundSessionRoute({ + cfg, + agentId, + channel: channelId, + accountId, + peer: { + kind: + parsed.chatType === "direct" + ? "direct" + : parsed.chatType === "group" + ? "group" + : "channel", + id: buildQaTarget(parsed), + }, + chatType: parsed.chatType, + from: `${channelId}:${accountId ?? DEFAULT_ACCOUNT_ID}`, + to: buildQaTarget(parsed), + }); + return buildThreadAwareOutboundSessionRoute({ + route: baseRoute, + replyToId, + threadId: + threadId ?? (target.trim().startsWith("thread:") ? undefined : parsed.threadId), + currentSessionKey, + canRecoverCurrentThread: ({ route }) => + route.chatType !== "direct" || (cfg.session?.dmScope ?? "main") !== "main", + }); + }, + resolveSessionConversation: ({ rawId }) => { + const parsed = parseQaTarget(rawId); + if (parsed.chatType === "direct") { + return null; + } + return { + id: parsed.conversationId, + threadId: parsed.threadId, + baseConversationId: parsed.conversationId, + parentConversationCandidates: [parsed.conversationId], + }; + }, + }, + status, + gateway: { + startAccount: async (ctx) => { + await startChannelMockGatewayAccount({ + channelId, + channelLabel: meta.label, + ctx, + autoThread, + getRuntime, + }); + }, + }, + actions: messageActions, + message: messageAdapter, + }, + outbound: { + base: { + deliveryMode: "direct", + }, + attachedResults: { + channel: channelId, + sendText: async ({ cfg, to, text, accountId, threadId, replyToId }) => + await sendText({ + cfg: cfg as CoreConfig, + accountId, + to, + text, + threadId, + replyToId, + }), + }, + }, + }); +} diff --git a/packages/openclaw-channel-mock-core/src/protocol.ts b/packages/openclaw-channel-mock-core/src/protocol.ts new file mode 100644 index 0000000..065f528 --- /dev/null +++ b/packages/openclaw-channel-mock-core/src/protocol.ts @@ -0,0 +1,272 @@ +/** + * Vendored from openclaw `src/plugin-sdk/qa-channel-protocol.ts` at version 2026.5.18. + * That file is intentionally excluded from openclaw's published exports + * (`dist/plugin-sdk/qa-channel-protocol.*` is in the package's `files` deny list). + * Keep in sync with upstream when the openclaw pin is bumped. + */ + +export type QaBusConversationKind = "direct" | "channel" | "group"; + +export type QaBusConversation = { + id: string; + kind: QaBusConversationKind; + title?: string; +}; + +export type QaBusAttachment = { + id: string; + kind: "image" | "video" | "audio" | "file"; + mimeType: string; + fileName?: string; + inline?: boolean; + url?: string; + contentBase64?: string; + width?: number; + height?: number; + durationMs?: number; + altText?: string; + transcript?: string; +}; + +export type QaBusToolCall = { + name: string; + arguments?: Record; +}; + +export type QaBusMessage = { + id: string; + accountId: string; + direction: "inbound" | "outbound"; + conversation: QaBusConversation; + senderId: string; + senderName?: string; + text: string; + timestamp: number; + threadId?: string; + threadTitle?: string; + replyToId?: string; + deleted?: boolean; + editedAt?: number; + attachments?: QaBusAttachment[]; + toolCalls?: QaBusToolCall[]; + reactions: Array<{ + emoji: string; + senderId: string; + timestamp: number; + }>; +}; + +export type QaBusThread = { + id: string; + accountId: string; + conversationId: string; + title: string; + createdAt: number; + createdBy: string; +}; + +export type QaBusEvent = + | { cursor: number; kind: "inbound-message"; accountId: string; message: QaBusMessage } + | { cursor: number; kind: "outbound-message"; accountId: string; message: QaBusMessage } + | { cursor: number; kind: "thread-created"; accountId: string; thread: QaBusThread } + | { cursor: number; kind: "message-edited"; accountId: string; message: QaBusMessage } + | { cursor: number; kind: "message-deleted"; accountId: string; message: QaBusMessage } + | { + cursor: number; + kind: "reaction-added"; + accountId: string; + message: QaBusMessage; + emoji: string; + senderId: string; + }; + +export type QaBusInboundMessageInput = { + accountId?: string; + conversation: QaBusConversation; + senderId: string; + senderName?: string; + text: string; + timestamp?: number; + threadId?: string; + threadTitle?: string; + replyToId?: string; + attachments?: QaBusAttachment[]; + toolCalls?: QaBusToolCall[]; +}; + +export type QaBusOutboundMessageInput = { + accountId?: string; + to: string; + senderId?: string; + senderName?: string; + text: string; + timestamp?: number; + threadId?: string; + replyToId?: string; + attachments?: QaBusAttachment[]; + toolCalls?: QaBusToolCall[]; +}; + +export type QaBusCreateThreadInput = { + accountId?: string; + conversationId: string; + title: string; + createdBy?: string; + timestamp?: number; +}; + +export type QaBusReactToMessageInput = { + accountId?: string; + messageId: string; + emoji: string; + senderId?: string; + timestamp?: number; +}; + +export type QaBusEditMessageInput = { + accountId?: string; + messageId: string; + text: string; + timestamp?: number; +}; + +export type QaBusDeleteMessageInput = { + accountId?: string; + messageId: string; + timestamp?: number; +}; + +export type QaBusSearchMessagesInput = { + accountId?: string; + query?: string; + conversationId?: string; + threadId?: string; + limit?: number; +}; + +export type QaBusReadMessageInput = { + accountId?: string; + messageId: string; +}; + +export type QaBusPollInput = { + accountId?: string; + cursor?: number; + timeoutMs?: number; + limit?: number; +}; + +export type QaBusPollResult = { + cursor: number; + events: QaBusEvent[]; +}; + +export type QaBusStateSnapshot = { + cursor: number; + conversations: QaBusConversation[]; + threads: QaBusThread[]; + messages: QaBusMessage[]; + events: QaBusEvent[]; +}; + +const QA_BUS_TOOL_CALL_MAX_COUNT = 50; +const QA_BUS_TOOL_CALL_MAX_DEPTH = 4; +const QA_BUS_TOOL_CALL_MAX_ARRAY_LENGTH = 20; +const QA_BUS_TOOL_CALL_MAX_OBJECT_KEYS = 40; +const QA_BUS_TOOL_CALL_REDACTED = "[redacted]"; + +const QA_BUS_TOOL_CALL_SENSITIVE_KEY_RE = + /authorization|cookie|credential|password|secret|token|api[-_]?key|access[-_]?key|private[-_]?key/iu; + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null && !Array.isArray(value); +} + +function sanitizeQaBusToolCallValue(value: unknown, depth: number, key?: string): unknown { + if (key && QA_BUS_TOOL_CALL_SENSITIVE_KEY_RE.test(key)) { + return QA_BUS_TOOL_CALL_REDACTED; + } + if (value === null || typeof value === "boolean" || typeof value === "number") { + return Number.isFinite(value as number) || typeof value !== "number" ? value : String(value); + } + if (typeof value === "string") { + // Tool args often embed credentials in command/header/env shapes; keep structure, not raw text. + return QA_BUS_TOOL_CALL_REDACTED; + } + if (typeof value === "bigint") { + return value.toString(); + } + if (value === undefined || typeof value === "function" || typeof value === "symbol") { + return undefined; + } + if (depth >= QA_BUS_TOOL_CALL_MAX_DEPTH) { + return "[truncated]"; + } + if (Array.isArray(value)) { + return value.slice(0, QA_BUS_TOOL_CALL_MAX_ARRAY_LENGTH).map((entry) => { + return sanitizeQaBusToolCallValue(entry, depth + 1); + }); + } + if (isRecord(value)) { + return Object.fromEntries( + Object.entries(value) + .slice(0, QA_BUS_TOOL_CALL_MAX_OBJECT_KEYS) + .flatMap(([entryKey, entryValue]) => { + const sanitized = sanitizeQaBusToolCallValue(entryValue, depth + 1, entryKey); + return sanitized === undefined ? [] : [[entryKey, sanitized]]; + }), + ); + } + return undefined; +} + +export function sanitizeQaBusToolCallArguments( + value: unknown, +): Record | undefined { + if (!isRecord(value)) { + return undefined; + } + const sanitized = sanitizeQaBusToolCallValue(value, 0); + return isRecord(sanitized) ? sanitized : undefined; +} + +export function sanitizeQaBusToolCalls(value: unknown): QaBusToolCall[] | undefined { + if (!Array.isArray(value)) { + return undefined; + } + const sanitized = value.slice(0, QA_BUS_TOOL_CALL_MAX_COUNT).flatMap((toolCall) => { + if (!isRecord(toolCall)) { + return []; + } + const name = typeof toolCall.name === "string" ? toolCall.name.trim() : ""; + if (!name) { + return []; + } + const args = sanitizeQaBusToolCallArguments(toolCall.arguments); + return [ + { + name, + ...(args && Object.keys(args).length > 0 ? { arguments: args } : {}), + }, + ]; + }); + return sanitized.length > 0 ? sanitized : undefined; +} + +export type QaBusWaitForInput = + | { + timeoutMs?: number; + kind: "event-kind"; + eventKind: QaBusEvent["kind"]; + } + | { + timeoutMs?: number; + kind: "message-text"; + textIncludes: string; + direction?: QaBusMessage["direction"]; + } + | { + timeoutMs?: number; + kind: "thread-id"; + threadId: string; + }; diff --git a/packages/openclaw-channel-mock-core/src/runtime-api.ts b/packages/openclaw-channel-mock-core/src/runtime-api.ts new file mode 100644 index 0000000..2524ff2 --- /dev/null +++ b/packages/openclaw-channel-mock-core/src/runtime-api.ts @@ -0,0 +1,26 @@ +export type { + ChannelMessageActionAdapter, + ChannelMessageActionName, + ChannelGatewayContext, +} from "openclaw/plugin-sdk/channel-contract"; +export type { ChannelPlugin } from "openclaw/plugin-sdk/channel-core"; +export type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; +export type { RuntimeEnv } from "openclaw/plugin-sdk/runtime"; +export type { PluginRuntime } from "openclaw/plugin-sdk/runtime-store"; +export { + buildChannelConfigSchema, + buildChannelOutboundSessionRoute, + buildThreadAwareOutboundSessionRoute, + createChatChannelPlugin, +} from "openclaw/plugin-sdk/channel-core"; +export { + createMessageReceiptFromOutboundResults, + defineChannelMessageAdapter, +} from "openclaw/plugin-sdk/channel-message"; +export { jsonResult, readStringParam } from "openclaw/plugin-sdk/channel-actions"; +export { getChatChannelMeta } from "openclaw/plugin-sdk/channel-plugin-common"; +export { + createComputedAccountStatusAdapter, + createDefaultChannelRuntimeState, +} from "openclaw/plugin-sdk/status-helpers"; +export { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store"; diff --git a/packages/openclaw-channel-mock-core/src/runtime.ts b/packages/openclaw-channel-mock-core/src/runtime.ts new file mode 100644 index 0000000..09265d7 --- /dev/null +++ b/packages/openclaw-channel-mock-core/src/runtime.ts @@ -0,0 +1,9 @@ +import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store"; +import type { PluginRuntime } from "./runtime-api.js"; + +export function createChannelMockRuntimeStore(channelId: string) { + return createPluginRuntimeStore({ + pluginId: channelId, + errorMessage: `${channelId} runtime not initialized`, + }); +} diff --git a/packages/openclaw-channel-mock-core/src/setup.ts b/packages/openclaw-channel-mock-core/src/setup.ts new file mode 100644 index 0000000..b365cb0 --- /dev/null +++ b/packages/openclaw-channel-mock-core/src/setup.ts @@ -0,0 +1,35 @@ +import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; +import { DEFAULT_ACCOUNT_ID } from "./accounts.js"; +import type { CoreConfig } from "./types.js"; + +export function createApplyChannelMockSetup(params: { channelId: string }) { + const { channelId } = params; + return function applyChannelMockSetup(input: { + cfg: OpenClawConfig; + accountId: string; + input: Record; + }): OpenClawConfig { + const nextCfg = structuredClone(input.cfg) as CoreConfig; + const section = nextCfg.channels?.[channelId] ?? {}; + const accounts = { ...section.accounts }; + const target = + input.accountId === DEFAULT_ACCOUNT_ID ? { ...section } : { ...accounts[input.accountId] }; + if (typeof input.input.baseUrl === "string") { + target.baseUrl = input.input.baseUrl; + } + if (typeof input.input.botUserId === "string") { + target.botUserId = input.input.botUserId; + } + if (typeof input.input.botDisplayName === "string") { + target.botDisplayName = input.input.botDisplayName; + } + nextCfg.channels ??= {}; + if (input.accountId === DEFAULT_ACCOUNT_ID) { + nextCfg.channels[channelId] = { ...section, ...target }; + } else { + accounts[input.accountId] = target; + nextCfg.channels[channelId] = { ...section, accounts }; + } + return nextCfg as OpenClawConfig; + }; +} diff --git a/packages/openclaw-channel-mock-core/src/status.ts b/packages/openclaw-channel-mock-core/src/status.ts new file mode 100644 index 0000000..ed2b1fa --- /dev/null +++ b/packages/openclaw-channel-mock-core/src/status.ts @@ -0,0 +1,25 @@ +import { DEFAULT_ACCOUNT_ID } from "./accounts.js"; +import { + createComputedAccountStatusAdapter, + createDefaultChannelRuntimeState, +} from "./runtime-api.js"; +import type { ResolvedChannelMockAccount } from "./types.js"; + +export function createChannelMockStatus() { + return createComputedAccountStatusAdapter({ + defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID), + buildChannelSummary: ({ snapshot }) => ({ + baseUrl: snapshot.baseUrl ?? "[missing]", + }), + resolveAccountSnapshot: ({ account }) => ({ + accountId: account.accountId, + name: account.name, + enabled: account.enabled, + configured: account.configured, + extra: { + baseUrl: account.baseUrl || "[missing]", + botUserId: account.botUserId, + }, + }), + }); +} diff --git a/packages/openclaw-channel-mock-core/src/types.ts b/packages/openclaw-channel-mock-core/src/types.ts new file mode 100644 index 0000000..a6b7784 --- /dev/null +++ b/packages/openclaw-channel-mock-core/src/types.ts @@ -0,0 +1,52 @@ +type ChannelMockActionConfig = { + messages?: boolean; + reactions?: boolean; + search?: boolean; + threads?: boolean; +}; + +export type ChannelMockAccountConfig = { + name?: string; + enabled?: boolean; + baseUrl?: string; + botUserId?: string; + botDisplayName?: string; + pollTimeoutMs?: number; + allowFrom?: Array; + groupPolicy?: "open" | "allowlist" | "disabled"; + groupAllowFrom?: Array; + groups?: Record< + string, + { + requireMention?: boolean; + tools?: Record; + toolsBySender?: Record>; + } + >; + defaultTo?: string; + actions?: ChannelMockActionConfig; +}; + +export type ChannelMockConfig = ChannelMockAccountConfig & { + accounts?: Record>; + defaultAccount?: string; +}; + +export type CoreConfig = { + channels?: Record; + session?: { + store?: string; + }; +}; + +export type ResolvedChannelMockAccount = { + accountId: string; + enabled: boolean; + configured: boolean; + name?: string; + baseUrl: string; + botUserId: string; + botDisplayName: string; + pollTimeoutMs: number; + config: ChannelMockAccountConfig; +}; diff --git a/packages/openclaw-channel-mock-core/test/bus.test.ts b/packages/openclaw-channel-mock-core/test/bus.test.ts new file mode 100644 index 0000000..4b32ac4 --- /dev/null +++ b/packages/openclaw-channel-mock-core/test/bus.test.ts @@ -0,0 +1,85 @@ +import { createServer, type Server } from "node:http"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { createBus } from "../src/bus-handler.js"; + +type Fixture = { server: Server; baseUrl: string; bus: ReturnType }; + +async function start(): Promise { + const bus = createBus(); + const server = createServer(async (req, res) => { + const handled = await bus.handler(req, res); + if (!handled) { + res.statusCode = 404; + res.end("not found"); + } + }); + await new Promise((resolve) => server.listen(0, "127.0.0.1", () => resolve())); + const address = server.address(); + if (!address || typeof address === "string") throw new Error("bind failed"); + return { server, baseUrl: `http://127.0.0.1:${address.port}`, bus }; +} + +async function stop(fixture: Fixture) { + await new Promise((resolve, reject) => { + fixture.server.close((error) => (error ? reject(error) : resolve())); + fixture.server.closeAllConnections?.(); + }); +} + +async function post(baseUrl: string, path: string, body: unknown): Promise { + const response = await fetch(`${baseUrl}${path}`, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify(body), + }); + const text = await response.text(); + if (!response.ok) { + throw new Error(`request ${path} failed ${response.status}: ${text}`); + } + return JSON.parse(text) as T; +} + +describe("bus HTTP round-trip", () => { + let fixture: Fixture; + beforeEach(async () => { + fixture = await start(); + }); + afterEach(async () => { + await stop(fixture); + }); + + it("thread-create + outbound-message + poll yield expected events", async () => { + const createResp = await post<{ thread: { id: string; conversationId: string } }>( + fixture.baseUrl, + "/v1/actions/thread-create", + { conversationId: "sample-project", title: "T" }, + ); + expect(createResp.thread.id.startsWith("thread-")).toBe(true); + expect(createResp.thread.conversationId).toBe("sample-project"); + + await post<{ message: unknown }>(fixture.baseUrl, "/v1/outbound/message", { + to: `thread:sample-project/${createResp.thread.id}`, + text: "hi", + senderId: "openclaw", + }); + + const poll = await post<{ + cursor: number; + events: Array<{ kind: string; thread?: { id: string }; message?: { threadId?: string } }>; + }>(fixture.baseUrl, "/v1/poll", { cursor: 0, timeoutMs: 0 }); + expect(poll.events.length).toBeGreaterThanOrEqual(2); + const kinds = poll.events.map((e) => e.kind); + expect(kinds).toContain("thread-created"); + expect(kinds).toContain("outbound-message"); + const outbound = poll.events.find((e) => e.kind === "outbound-message"); + expect(outbound?.message?.threadId).toBe(createResp.thread.id); + }); + + it("GET /health and /v1/state work", async () => { + const healthResp = await fetch(`${fixture.baseUrl}/health`); + expect(healthResp.status).toBe(200); + const stateResp = await fetch(`${fixture.baseUrl}/v1/state`); + const state = (await stateResp.json()) as { cursor: number }; + expect(typeof state.cursor).toBe("number"); + }); +}); diff --git a/packages/openclaw-channel-mock-core/tsconfig.build.json b/packages/openclaw-channel-mock-core/tsconfig.build.json new file mode 100644 index 0000000..f1e54d4 --- /dev/null +++ b/packages/openclaw-channel-mock-core/tsconfig.build.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2023", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "outDir": "dist", + "rootDir": "src", + "declaration": true, + "strict": true, + "skipLibCheck": true, + "types": ["node"] + }, + "include": ["src"] +} diff --git a/packages/openclaw-channel-mock-core/tsconfig.json b/packages/openclaw-channel-mock-core/tsconfig.json new file mode 100644 index 0000000..e7861fc --- /dev/null +++ b/packages/openclaw-channel-mock-core/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.build.json", + "compilerOptions": { + "rootDir": ".", + "noEmit": true + }, + "include": ["src", "test"] +} diff --git a/packages/openclaw-discord-mock/CHANGELOG.md b/packages/openclaw-discord-mock/CHANGELOG.md new file mode 100644 index 0000000..6c706d1 --- /dev/null +++ b/packages/openclaw-discord-mock/CHANGELOG.md @@ -0,0 +1,20 @@ +# @paleo/openclaw-discord-mock + +## 0.2.1 + +### Patch Changes + +- Hardened openclaw qa toolkit +- Updated dependencies + - @paleo/openclaw-channel-mock-core@0.2.1 + +## 0.2.0 + +### Minor Changes + +- Initial version + +### Patch Changes + +- Updated dependencies + - @paleo/openclaw-channel-mock-core@0.2.0 diff --git a/packages/openclaw-discord-mock/LICENSE b/packages/openclaw-discord-mock/LICENSE new file mode 100644 index 0000000..91b9589 --- /dev/null +++ b/packages/openclaw-discord-mock/LICENSE @@ -0,0 +1,24 @@ +MIT License + +Copyright (c) 2026 Paleo + +Portions adapted from OpenClaw (https://github.com/steipete/openclaw), +Copyright (c) 2025 Peter Steinberger. Licensed under the MIT License. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/openclaw-discord-mock/NOTICE.md b/packages/openclaw-discord-mock/NOTICE.md new file mode 100644 index 0000000..a82a44b --- /dev/null +++ b/packages/openclaw-discord-mock/NOTICE.md @@ -0,0 +1,16 @@ +# NOTICE — upstream attribution + +Thin Discord-shaped wrapper around `@paleo/openclaw-channel-mock-core`. The plugin shape (manifest + entry points) follows upstream [OpenClaw](https://github.com/steipete/openclaw) `extensions/discord/` (MIT, Copyright 2025 Peter Steinberger). + +## Shape borrowed from `openclaw/extensions/discord/` + +- `channel-plugin-api.ts` → `src/channel-plugin-api.ts` +- `setup-plugin-api.ts` → `src/channel-setup-plugin.ts` +- `setup-entry.ts` → `src/setup-entry.ts` +- `index.ts` → `src/index.ts` +- `runtime-api.ts` → `src/runtime.ts` +- `openclaw.plugin.json` → `openclaw.plugin.json` + +The Discord-specific transport implementation is **not** copied; this package delegates to the channel-mock core with `surface: "discord"` and `autoThread: false`. + +See [`LICENSE`](LICENSE) for the combined MIT license text (Paleo + upstream OpenClaw attribution). diff --git a/packages/openclaw-discord-mock/README.md b/packages/openclaw-discord-mock/README.md new file mode 100644 index 0000000..ff1dec2 --- /dev/null +++ b/packages/openclaw-discord-mock/README.md @@ -0,0 +1,11 @@ +# @paleo/openclaw-discord-mock + +Synthetic Discord-shaped OpenClaw channel plugin. Registers as channel `discord-mock` with the full Discord action surface (`send`, `thread-create`, `thread-reply`, `react`, `read`, `edit`, `delete`, `search`). `thread-create` posts an optional body atomically with the new thread; free-form agent text without a tool call lands in the parent channel. + +Backed by [`@paleo/openclaw-channel-mock-core`](../openclaw-channel-mock-core/) with `surface: "discord"` and `autoThread: false`. Pair with [`@paleo/openclaw-qa-runner`](../openclaw-qa-runner/) for the QA harness. + +`Provider` / `Surface` / `OriginatingChannel` on inbound metadata are claimed as `discord-mock` so the SDK routes tool-schema discovery to this plugin. + +## Attribution + +Adapted from upstream OpenClaw's `extensions/discord/` plugin shape (manifest + entry points only). See [`NOTICE.md`](NOTICE.md). diff --git a/packages/openclaw-discord-mock/openclaw.plugin.json b/packages/openclaw-discord-mock/openclaw.plugin.json new file mode 100644 index 0000000..6bfcc85 --- /dev/null +++ b/packages/openclaw-discord-mock/openclaw.plugin.json @@ -0,0 +1,6 @@ +{ + "id": "discord-mock", + "activation": { "onStartup": false }, + "channels": ["discord-mock"], + "configSchema": { "type": "object", "additionalProperties": false, "properties": {} } +} diff --git a/packages/openclaw-discord-mock/package.json b/packages/openclaw-discord-mock/package.json new file mode 100644 index 0000000..534c87b --- /dev/null +++ b/packages/openclaw-discord-mock/package.json @@ -0,0 +1,76 @@ +{ + "name": "@paleo/openclaw-discord-mock", + "version": "0.2.1", + "description": "Synthetic Discord-shaped OpenClaw channel plugin for automated QA scenarios.", + "keywords": [ + "openclaw", + "qa", + "testing", + "discord", + "channel", + "mock" + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/paleo/alignfirst.git", + "directory": "packages/openclaw-discord-mock" + }, + "engines": { + "node": ">=22.11.0" + }, + "packageManager": "npm@11.11.0", + "type": "module", + "main": "./dist/index.js", + "files": [ + "dist/", + "openclaw.plugin.json", + "LICENSE", + "NOTICE.md" + ], + "publishConfig": { + "access": "public" + }, + "openclaw": { + "extensions": [ + "./dist/index.js" + ], + "setupEntry": "./dist/setup-entry.js", + "channel": { + "id": "discord-mock", + "label": "Discord Mock", + "selectionLabel": "Discord Mock (Synthetic)", + "detailLabel": "Discord Mock", + "docsPath": "/channels/discord-mock", + "docsLabel": "discord-mock", + "blurb": "Synthetic Discord-shaped transport for automated OpenClaw QA scenarios.", + "systemImage": "checklist", + "order": 998, + "exposure": { + "configured": false, + "setup": false, + "docs": false + } + } + }, + "scripts": { + "build": "tsc -p tsconfig.build.json", + "clear": "rimraf dist/*", + "lint": "biome check", + "test": "vitest run", + "prepublishOnly": "npm run clear && npm run build && npm run lint && npm run test" + }, + "peerDependencies": { + "openclaw": "*" + }, + "dependencies": { + "@paleo/openclaw-channel-mock-core": "0.2.1" + }, + "devDependencies": { + "@types/node": "~24.12.4", + "openclaw": "2026.5.18", + "rimraf": "~6.1.3", + "typescript": "~6.0.3", + "vitest": "~4.1.6" + } +} diff --git a/packages/openclaw-discord-mock/src/channel-plugin-api.ts b/packages/openclaw-discord-mock/src/channel-plugin-api.ts new file mode 100644 index 0000000..5e73127 --- /dev/null +++ b/packages/openclaw-discord-mock/src/channel-plugin-api.ts @@ -0,0 +1 @@ +export { discordMockPlugin } from "./plugin.js"; diff --git a/packages/openclaw-discord-mock/src/channel-setup-plugin.ts b/packages/openclaw-discord-mock/src/channel-setup-plugin.ts new file mode 100644 index 0000000..351588c --- /dev/null +++ b/packages/openclaw-discord-mock/src/channel-setup-plugin.ts @@ -0,0 +1,6 @@ +import { createChannelMockSetupPlugin } from "@paleo/openclaw-channel-mock-core"; + +export const discordMockSetupPlugin = createChannelMockSetupPlugin({ + channelId: "discord-mock", + label: "Discord Mock", +}); diff --git a/packages/openclaw-discord-mock/src/index.ts b/packages/openclaw-discord-mock/src/index.ts new file mode 100644 index 0000000..4399e36 --- /dev/null +++ b/packages/openclaw-discord-mock/src/index.ts @@ -0,0 +1,16 @@ +import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract"; + +export default defineBundledChannelEntry({ + id: "discord-mock", + name: "Discord Mock", + description: "Synthetic Discord-shaped QA channel plugin", + importMetaUrl: import.meta.url, + plugin: { + specifier: "./channel-plugin-api.js", + exportName: "discordMockPlugin", + }, + runtime: { + specifier: "./runtime.js", + exportName: "setDiscordMockRuntime", + }, +}); diff --git a/packages/openclaw-discord-mock/src/plugin.ts b/packages/openclaw-discord-mock/src/plugin.ts new file mode 100644 index 0000000..c9bb414 --- /dev/null +++ b/packages/openclaw-discord-mock/src/plugin.ts @@ -0,0 +1,10 @@ +import { createChannelMockPlugin } from "@paleo/openclaw-channel-mock-core"; +import { getDiscordMockRuntime } from "./runtime.js"; + +export const discordMockPlugin = createChannelMockPlugin({ + channelId: "discord-mock", + label: "Discord Mock", + surface: "discord", + autoThread: false, + getRuntime: getDiscordMockRuntime, +}); diff --git a/packages/openclaw-discord-mock/src/runtime.ts b/packages/openclaw-discord-mock/src/runtime.ts new file mode 100644 index 0000000..02e0c7d --- /dev/null +++ b/packages/openclaw-discord-mock/src/runtime.ts @@ -0,0 +1,5 @@ +import { createChannelMockRuntimeStore } from "@paleo/openclaw-channel-mock-core"; + +const store = createChannelMockRuntimeStore("discord-mock"); +export const setDiscordMockRuntime = store.setRuntime; +export const getDiscordMockRuntime = store.getRuntime; diff --git a/packages/openclaw-discord-mock/src/setup-entry.ts b/packages/openclaw-discord-mock/src/setup-entry.ts new file mode 100644 index 0000000..62ef49b --- /dev/null +++ b/packages/openclaw-discord-mock/src/setup-entry.ts @@ -0,0 +1,13 @@ +import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract"; + +export default defineBundledChannelSetupEntry({ + importMetaUrl: import.meta.url, + plugin: { + specifier: "./channel-setup-plugin.js", + exportName: "discordMockSetupPlugin", + }, + runtime: { + specifier: "./runtime.js", + exportName: "setDiscordMockRuntime", + }, +}); diff --git a/packages/openclaw-discord-mock/test/plugin-actions.test.ts b/packages/openclaw-discord-mock/test/plugin-actions.test.ts new file mode 100644 index 0000000..feb9eb8 --- /dev/null +++ b/packages/openclaw-discord-mock/test/plugin-actions.test.ts @@ -0,0 +1,171 @@ +import { createServer, type Server } from "node:http"; +import { + createBus, + createChannelMockAccountHelpers, + createChannelMockMessageActions, +} from "@paleo/openclaw-channel-mock-core"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; + +const CHANNEL_ID = "discord-mock"; + +type BusFixture = { server: Server; baseUrl: string; bus: ReturnType }; + +async function startBus(): Promise { + const bus = createBus(); + const server = createServer(async (req, res) => { + const handled = await bus.handler(req, res); + if (!handled) { + res.statusCode = 404; + res.end("not found"); + } + }); + await new Promise((resolve) => server.listen(0, "127.0.0.1", () => resolve())); + const address = server.address(); + if (!address || typeof address === "string") throw new Error("failed to bind test bus"); + return { server, baseUrl: `http://127.0.0.1:${address.port}`, bus }; +} + +async function stopBus(fixture: BusFixture) { + await new Promise((resolve, reject) => { + fixture.server.close((error) => (error ? reject(error) : resolve())); + fixture.server.closeAllConnections?.(); + }); +} + +const helpers = createChannelMockAccountHelpers({ channelId: CHANNEL_ID }); +const actions = createChannelMockMessageActions({ + channelId: CHANNEL_ID, + surface: "discord", + helpers, +}); + +if (!actions.handleAction || !actions.describeMessageTool) { + throw new Error("discord-mock actions missing handleAction/describeMessageTool"); +} +const handleAction: NonNullable = actions.handleAction; +const describeMessageTool: NonNullable = + actions.describeMessageTool; + +function makeCfg(baseUrl: string) { + return { + channels: { + [CHANNEL_ID]: { + baseUrl, + botUserId: "openclaw", + botDisplayName: "OpenClaw QA", + allowFrom: ["*"], + }, + }, + } as const; +} + +async function runHandler(fixture: BusFixture, action: string, params: Record) { + return await handleAction({ + action, + cfg: makeCfg(fixture.baseUrl) as unknown as Parameters[0]["cfg"], + accountId: "default", + params, + } as unknown as Parameters[0]); +} + +describe("discord-mock handleAction (post-normalization shape)", () => { + let fixture: BusFixture; + beforeEach(async () => { + fixture = await startBus(); + }); + afterEach(async () => { + await stopBus(fixture); + }); + + it("send with `to` posts to the channel", async () => { + await runHandler(fixture, "send", { to: "sample-project", text: "hello" }); + const snap = fixture.bus.state.getSnapshot(); + expect(snap.messages.length).toBe(1); + expect(snap.messages[0].text).toBe("hello"); + expect(snap.messages[0].conversation.id).toBe("sample-project"); + }); + + it("send with thread target posts to the thread", async () => { + await runHandler(fixture, "send", { to: "thread:sample-project/T1", text: "in thread" }); + const snap = fixture.bus.state.getSnapshot(); + expect(snap.messages[0].threadId).toBe("T1"); + }); + + it("thread-create with text posts the body atomically", async () => { + const created = (await runHandler(fixture, "thread-create", { + to: "sample-project", + title: "Topic", + text: "first thread message", + })) as { content: Array<{ text: string }> }; + const payload = JSON.parse(created.content[0].text); + expect(payload.threadId).toBeTruthy(); + expect(payload.message).toBeTruthy(); + const snap = fixture.bus.state.getSnapshot(); + expect(snap.threads.length).toBe(1); + expect(snap.messages.length).toBe(1); + expect(snap.messages[0].text).toBe("first thread message"); + expect(snap.messages[0].threadId).toBe(payload.threadId); + }); + + it("thread-create without text creates the thread only", async () => { + await runHandler(fixture, "thread-create", { to: "sample-project", title: "Topic" }); + const snap = fixture.bus.state.getSnapshot(); + expect(snap.threads.length).toBe(1); + expect(snap.messages.length).toBe(0); + }); + + it("thread-reply posts to the thread", async () => { + const thread = fixture.bus.state.createThread({ + accountId: "default", + conversationId: "sample-project", + title: "T", + }); + await runHandler(fixture, "thread-reply", { + to: "sample-project", + threadId: thread.id, + text: "reply body", + }); + const snap = fixture.bus.state.getSnapshot(); + const reply = snap.messages.find((m) => m.threadId === thread.id); + expect(reply).toBeTruthy(); + expect(reply?.text).toBe("reply body"); + }); + + it("react/read/edit/delete on normalized shape", async () => { + const sent = await runHandler(fixture, "send", { to: "sample-project", text: "first" }); + const messageId = JSON.parse((sent as { content: Array<{ text: string }> }).content[0].text) + .message.id as string; + await runHandler(fixture, "react", { messageId, emoji: "👍" }); + await runHandler(fixture, "edit", { messageId, text: "edited" }); + const read = (await runHandler(fixture, "read", { messageId })) as { + content: Array<{ text: string }>; + }; + const readMsg = JSON.parse(read.content[0].text).message; + expect(readMsg.text).toBe("edited"); + expect(readMsg.reactions.length).toBe(1); + await runHandler(fixture, "delete", { messageId }); + expect(fixture.bus.state.getSnapshot().messages[0].deleted).toBe(true); + }); + + it("describeMessageTool exposes the full Discord surface", () => { + const desc = describeMessageTool({ + cfg: makeCfg("http://x") as unknown as Parameters[0]["cfg"], + accountId: "default", + } as unknown as Parameters[0]); + if (!desc) throw new Error("describeMessageTool returned no descriptor"); + const set = new Set(desc.actions); + for (const wanted of [ + "send", + "thread-create", + "thread-reply", + "read", + "edit", + "delete", + "react", + "reactions", + "search", + ]) { + expect(set.has(wanted as never)).toBe(true); + } + }); +}); diff --git a/packages/openclaw-discord-mock/tsconfig.build.json b/packages/openclaw-discord-mock/tsconfig.build.json new file mode 100644 index 0000000..9b949b7 --- /dev/null +++ b/packages/openclaw-discord-mock/tsconfig.build.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2023", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "outDir": "dist", + "rootDir": "src", + "declaration": false, + "strict": true, + "skipLibCheck": true, + "types": ["node"] + }, + "include": ["src"] +} diff --git a/packages/openclaw-discord-mock/tsconfig.json b/packages/openclaw-discord-mock/tsconfig.json new file mode 100644 index 0000000..e7861fc --- /dev/null +++ b/packages/openclaw-discord-mock/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.build.json", + "compilerOptions": { + "rootDir": ".", + "noEmit": true + }, + "include": ["src", "test"] +} diff --git a/packages/openclaw-qa-runner/CHANGELOG.md b/packages/openclaw-qa-runner/CHANGELOG.md new file mode 100644 index 0000000..08594c0 --- /dev/null +++ b/packages/openclaw-qa-runner/CHANGELOG.md @@ -0,0 +1,24 @@ +# @paleo/openclaw-qa-runner + +## 0.2.1 + +### Patch Changes + +- Hardened openclaw qa toolkit +- Updated dependencies + - @paleo/openclaw-channel-mock-core@0.2.1 + - @paleo/openclaw-discord-mock@0.2.1 + - @paleo/openclaw-slack-mock@0.2.1 + +## 0.2.0 + +### Minor Changes + +- Initial version + +### Patch Changes + +- Updated dependencies + - @paleo/openclaw-channel-mock-core@0.2.0 + - @paleo/openclaw-discord-mock@0.2.0 + - @paleo/openclaw-slack-mock@0.2.0 diff --git a/packages/openclaw-qa-runner/Dockerfile b/packages/openclaw-qa-runner/Dockerfile new file mode 100644 index 0000000..64832a9 --- /dev/null +++ b/packages/openclaw-qa-runner/Dockerfile @@ -0,0 +1,59 @@ +FROM node:24-alpine + +RUN apk add --no-cache shadow git + +ARG CLAW_UID=1000 +ARG CLAW_GID=1000 +RUN (getent passwd ${CLAW_UID} && userdel -r "$(getent passwd ${CLAW_UID} | cut -d: -f1)" || true) && \ + (getent group ${CLAW_GID} && groupdel "$(getent group ${CLAW_GID} | cut -d: -f1)" || true) && \ + groupadd --gid ${CLAW_GID} claw && \ + useradd --create-home --shell /bin/sh --uid ${CLAW_UID} --gid ${CLAW_GID} claw && \ + mkdir -p /home/claw/.openclaw /opt/qa-src /opt/qa-artifacts && \ + chown -R claw:claw /home/claw /opt/qa-src /opt/qa-artifacts + +USER claw +WORKDIR /opt/qa-src + +# Build context is the consumer project root (QA_PROJECT_DIR, the consumer's `qa/`). +# The consumer pins the four @paleo/openclaw-* packages with absolute host `file:` URIs that +# don't exist inside the image. We stage the host-resolved package contents from the +# consumer's node_modules into an in-image staging dir, then rewrite the package.json +# `file:` URIs to point at the staged copies before `npm install`. Host install must use +# `--install-links` so node_modules/@paleo/* are materialized copies (not symlinks). +COPY --chown=claw:claw package.json package-lock.json* /opt/qa-src/ +COPY --chown=claw:claw node_modules/@paleo/openclaw-channel-mock-core/ /opt/qa-staging/openclaw-channel-mock-core/ +COPY --chown=claw:claw node_modules/@paleo/openclaw-discord-mock/ /opt/qa-staging/openclaw-discord-mock/ +COPY --chown=claw:claw node_modules/@paleo/openclaw-slack-mock/ /opt/qa-staging/openclaw-slack-mock/ +COPY --chown=claw:claw node_modules/@paleo/openclaw-qa-runner/ /opt/qa-staging/openclaw-qa-runner/ +COPY --chown=claw:claw openclaw.json /home/claw/.openclaw/openclaw.json + +# Rewrite absolute host `file:` URIs in package.json to the in-image staging paths. +# The host lockfile pins integrity against the original host paths, so it cannot +# survive the rewrite; `npm ci` would reject it. Drop it and let npm regenerate. +RUN node -e "const fs=require('fs');const p='./package.json';const j=JSON.parse(fs.readFileSync(p,'utf8'));for(const k of Object.keys(j.dependencies||{})){if(k.startsWith('@paleo/openclaw-')){const n=k.slice('@paleo/'.length);j.dependencies[k]='file:/opt/qa-staging/'+n;}}fs.writeFileSync(p,JSON.stringify(j,null,2));" && \ + rm -f package-lock.json && \ + npm install --include=dev --install-links && \ + OPENCLAW_CONFIG_PATH=/home/claw/.openclaw/openclaw.json npx openclaw plugins registry --refresh + +# Stage the mock-CLI shim under /opt/qa-mocks. PATH is NOT modified at build +# time — image build's own `npm install` above must use the real `npm`. +# OpenClaw's exec tool runs commands via `/bin/sh -lc`, which sources +# /etc/profile. The Alpine /etc/profile resets PATH to a "safe" set that +# excludes /opt/qa-mocks/bin, so we override it to put the mocks first. +USER root +COPY --chown=claw:claw node_modules/@paleo/openclaw-qa-runner/dist/mock-cli-shim.js /opt/qa-mocks/dist/mock-cli-shim.js +RUN mkdir -p /opt/qa-mocks/bin && \ + printf '#!/bin/sh\nexec node /opt/qa-mocks/dist/mock-cli-shim.js "$0" "$@"\n' > /opt/qa-mocks/bin/mock-cli-shim && \ + chmod +x /opt/qa-mocks/bin/mock-cli-shim && \ + for name in git npm pnpm yarn claude; do ln -sf mock-cli-shim "/opt/qa-mocks/bin/$name"; done && \ + chown -R claw:claw /opt/qa-mocks && \ + printf 'export PATH="/opt/qa-mocks/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"\n' > /etc/profile +USER claw + +# Consumer-supplied extra setup, run as claw after the base image is built. +# Use it to install workspace skills, extra system packages, etc. Receives an +# arbitrary shell snippet via the QA_EXTRA_SETUP build arg. +ARG QA_EXTRA_SETUP="" +RUN if [ -n "$QA_EXTRA_SETUP" ]; then sh -c "$QA_EXTRA_SETUP"; fi + +CMD ["sleep", "infinity"] diff --git a/packages/openclaw-qa-runner/LICENSE b/packages/openclaw-qa-runner/LICENSE new file mode 100644 index 0000000..65d1be8 --- /dev/null +++ b/packages/openclaw-qa-runner/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Paleo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/openclaw-qa-runner/README.md b/packages/openclaw-qa-runner/README.md new file mode 100644 index 0000000..7c02023 --- /dev/null +++ b/packages/openclaw-qa-runner/README.md @@ -0,0 +1,107 @@ +# @paleo/openclaw-qa-runner + +Dockerised regression-test harness for OpenClaw workspaces. Drives the agent through two synthetic channels (`discord-mock`, `slack-mock`) and asserts the results. One gateway, one bus, parallel scenarios. + +Toolkit entry point. Pair with [`@paleo/openclaw-channel-mock-core`](../openclaw-channel-mock-core/), [`@paleo/openclaw-discord-mock`](../openclaw-discord-mock/), [`@paleo/openclaw-slack-mock`](../openclaw-slack-mock/). + +## Install + +```sh +npm i -D @paleo/openclaw-qa-runner @paleo/openclaw-channel-mock-core @paleo/openclaw-discord-mock @paleo/openclaw-slack-mock openclaw +``` + +Requires Docker Compose v2.20+ (consumer overlay uses Compose `include:`). + +## Init + +```sh +npx openclaw-qa-runner init . +``` + +Drops three files into the target directory: + +- `openclaw.json` — gateway config (mode `local`, both channel plugins enabled, main agent). +- `.env.local.example` — copy to `.env.local`, set `ANTHROPIC_API_KEY`. +- `docker-compose.yml` — thin overlay that `include:`s this package's base stack from `node_modules/`. + +Then wire `package.json` scripts (`env:build`, `env:up`, `env:down`, `qa`) — see the templates and the consumer guide below. + +## Configure + +Edit `openclaw.json`: + +- `agents.list[id=main].model` — LiteLLM-style `provider/model` ref (e.g. `anthropic/claude-sonnet-4-6`). The template ships a placeholder; OpenClaw will fail loudly until you pick one. +- `agents.list[id=main].workspace` — host path to your OpenClaw workspace (bind-mounted into the gateway). +- `channels.*` — both `discord-mock` and `slack-mock` blocks point at the same bus. + +The LLM judge runs out-of-process — Anthropic-direct from the runner, never through the gateway — so it is **not** an OpenClaw agent and is not configured via `openclaw.json`. Defaults to `anthropic/claude-haiku-4-5`; override via the `QA_JUDGE_MODEL` env var on the runner. The ref must be LiteLLM-style; only the `anthropic/` provider is wired up today. + +Drop scenarios under `scenarios/.ts`, default-export `async (ctx: ScenarioContext) => void`. Project fixtures live under `projects-fixture/` (bind-mounted to `~/projects/`). + +Scenarios are loaded at runtime by Node's built-in TypeScript stripping (Node 24, which the image uses). Stick to the strip-compatible subset: type annotations, `as`, `satisfies`, generics, interfaces. Avoid `enum`, `namespace`, constructor parameter properties, decorators, and `import =`. + +## Build / up / run + +```sh +npm run env:build # build the gateway / bus / runner image +npm run env:up # bring up bus + gateway (both channels register) +npm run qa -- --channel all # one scenario, both channels +npm run qa -- --channel all --all # every scenario, both channels +npm run qa -- --channel discord-mock # restrict to one channel +npm run qa -- --channel all --iterations 5 # repeat each (scenario, channel) pair 5 times +npm run qa -- --channel all --iterations 5 --max-failures 1 # abort a pair after >1 failure +npm run env:down +``` + +`--concurrency N` (default 4, env `QA_CONCURRENCY`) caps fanout. Artifacts land under `artifacts//-[-][-]/`: `` is the iteration index (omitted when `--iterations 1`), `` is `PASS` / `FAIL`, applied by renaming the dir after `report.json` is written — its absence means the run is still pending or crashed before rename. Exit 0 iff every pair passes. + +## Scenario primitives + +From `@paleo/openclaw-qa-runner` (`src/context.ts`): + +- `channel`, `conversationId`, `accountId` — per-task isolation. Use `ctx.conversationId` everywhere; never hard-code a value. +- `sendInbound(text, opts?)` — push an inbound on the bus. +- `poll(opts?)`, `waitForOutbound(opts?)`, `expectNoOutbound(opts?)` — bus consumers. +- `assertRegex`, `assertEqual`, `assertLength` — structural assertions. +- `judgeLLM(prompt)` — Anthropic-direct judgement (no bus traffic). +- `log`, `getCursor`. + +Prefer structural assertions over `judgeLLM`; reserve the judge for free-form content claims. + +## Channels + +Both plugins register together. Pick which to drive per scenario via `--channel discord-mock|slack-mock|all`. + +- `discord-mock` — full Discord-shaped surface; `thread-create` posts an optional body atomically. +- `slack-mock` — restricted Slack-shaped surface (`react` / `read` / `edit` / `delete` / `reactions` / `search`). Bare-channel inbounds auto-thread on the triggering message. + +## Compose stack + +`docker-compose.yml` is parameterized via env vars (the consumer-side `bin/qa.mjs` and `bin/env-up` set them): + +- `QA_PROJECT_DIR` — consumer's `qa/` (build context, working dir). +- `QA_RUNNER_PACKAGE_DIR` — host path to this package (mounts `dist/`; rebuild with `npm run build` to refresh). +- `OPENCLAW_WORKSPACE_DIR` — mounted at `/home/claw/.openclaw/workspace`. +- `OPENCLAW_CONFIG_PATH` — mounted at `/home/claw/.openclaw/openclaw.json`. +- `PROJECTS_DIR` — mounted at `/home/claw/projects/`. +- `SCENARIOS_DIR` — mounted at `/opt/qa-src/scenarios`. +- `ARTIFACTS_DIR` — mounted at `/opt/qa-artifacts`. +- `GATEWAY_LOGS_DIR` — mounted at `/home/claw/.openclaw/logs`. +- `CLAW_UID` / `CLAW_GID` — propagated to the image so artifacts land owned by the host user. + +Healthchecks: `gateway` waits on `bus`, `runner` waits on `gateway`. + +## Gateway logs (opt-in) + +Set in `.env.local`: + +```sh +OPENCLAW_ANTHROPIC_PAYLOAD_LOG=1 +OPENCLAW_RAW_STREAM=1 +``` + +Writes `anthropic-payload.jsonl` and `raw-stream.jsonl` under `.gateway-logs/`. The runner's cost reporting reads `anthropic-payload.jsonl`. + +## Attribution + +The runner package contains no upstream-adapted code. See sibling packages' `NOTICE.md` for OpenClaw attribution covering the channel plugins. diff --git a/packages/openclaw-qa-runner/bin/cli.mjs b/packages/openclaw-qa-runner/bin/cli.mjs new file mode 100755 index 0000000..52ec4b8 --- /dev/null +++ b/packages/openclaw-qa-runner/bin/cli.mjs @@ -0,0 +1,7 @@ +#!/usr/bin/env node +import { dispatch } from "../dist/cli.js"; + +dispatch(process.argv.slice(2)).catch((err) => { + console.error("openclaw-qa-runner crash:", err); + process.exit(1); +}); diff --git a/packages/openclaw-qa-runner/bin/qa.mjs b/packages/openclaw-qa-runner/bin/qa.mjs new file mode 100755 index 0000000..fa8155f --- /dev/null +++ b/packages/openclaw-qa-runner/bin/qa.mjs @@ -0,0 +1,88 @@ +#!/usr/bin/env node +import { spawnSync } from "node:child_process"; +import { resolve } from "node:path"; +import { parseArgs } from "node:util"; + +const USAGE = `usage: qa/bin/qa.mjs --channel [ ...] [--all] + [--iterations N] [--max-failures N] + + Scenario selection is required: either a positional list or --all (mutually exclusive). + --iterations N run each (scenario, channel) pair N times (default 1). + --max-failures N abort a pair once failures > N (default 1). + Run 'npm run env:up' first.`; + +function fail(msg) { + if (msg) console.error(msg); + console.error(USAGE); + process.exit(1); +} + +let values; +let positionals; +try { + ({ values, positionals } = parseArgs({ + args: process.argv.slice(2), + options: { + channel: { type: "string" }, + iterations: { type: "string" }, + "max-failures": { type: "string" }, + all: { type: "boolean", default: false }, + help: { type: "boolean", short: "h", default: false }, + }, + allowPositionals: true, + strict: true, + })); +} catch (err) { + fail(err.message); +} + +if (values.help) fail(); + +const channel = values.channel; +if (channel !== "discord-mock" && channel !== "slack-mock" && channel !== "all") { + fail("error: --channel must be discord-mock, slack-mock, or all"); +} + +if (values.all && positionals.length > 0) { + fail("error: pass either --all or a positional scenario list, not both"); +} +if (!values.all && positionals.length === 0) { + fail("error: must pass --all or one or more scenario names"); +} + +const qaDir = process.env.QA_PROJECT_DIR || process.cwd(); +const composeFile = resolve(qaDir, "docker-compose.yml"); +const envFile = resolve(qaDir, ".env.local"); + +if (!process.env.CLAW_UID) process.env.CLAW_UID = String(process.getuid()); +if (!process.env.CLAW_GID) process.env.CLAW_GID = String(process.getgid()); +process.env.QA_PROJECT_DIR = qaDir; + +const runnerArgs = ["--channel", channel]; +if (values.iterations) runnerArgs.push("--iterations", values.iterations); +if (values["max-failures"]) runnerArgs.push("--max-failures", values["max-failures"]); +if (values.all) runnerArgs.push("--all"); +else runnerArgs.push(...positionals); + +const result = spawnSync( + "docker", + [ + "compose", + "--env-file", + envFile, + "-f", + composeFile, + "run", + "--rm", + "--use-aliases", + "runner", + ...runnerArgs, + ], + { stdio: "inherit" }, +); + +if (result.error) { + console.error(result.error.message); + process.exit(1); +} +process.exit(result.status ?? 1); diff --git a/packages/openclaw-qa-runner/docker-compose.yml b/packages/openclaw-qa-runner/docker-compose.yml new file mode 100644 index 0000000..c1384ca --- /dev/null +++ b/packages/openclaw-qa-runner/docker-compose.yml @@ -0,0 +1,67 @@ +x-common-build: &common-build + context: ${QA_PROJECT_DIR} + dockerfile: ${QA_RUNNER_PACKAGE_DIR}/Dockerfile + args: + CLAW_UID: ${CLAW_UID:-1000} + CLAW_GID: ${CLAW_GID:-1000} + +x-common-volumes: &common-volumes + - ${OPENCLAW_WORKSPACE_DIR}:/home/claw/.openclaw/workspace + - ${OPENCLAW_CONFIG_PATH}:/home/claw/.openclaw/openclaw.json + - ${PROJECTS_DIR}:/home/claw/projects/ + - ${QA_RUNNER_PACKAGE_DIR}/dist/:/opt/qa-src/dist + - ${SCENARIOS_DIR}:/opt/qa-src/scenarios + - ${ARTIFACTS_DIR}:/opt/qa-artifacts + - ${GATEWAY_LOGS_DIR}:/home/claw/.openclaw/logs + +services: + bus: + build: *common-build + user: claw + working_dir: /opt/qa-src + command: ["node", "/opt/qa-src/dist/bus.js"] + volumes: *common-volumes + healthcheck: + test: ["CMD", "wget", "-qO-", "http://127.0.0.1:43123/v1/state"] + interval: 2s + timeout: 2s + retries: 10 + + gateway: + build: *common-build + user: claw + working_dir: /opt/qa-src + environment: + OPENCLAW_CONFIG_PATH: /home/claw/.openclaw/openclaw.json + ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY} + OPENCLAW_ANTHROPIC_PAYLOAD_LOG: "1" + OPENCLAW_RAW_STREAM: ${OPENCLAW_RAW_STREAM:-} + QA_RUNNER_URL: "http://runner:43124" + PATH: "/opt/qa-mocks/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + command: ["npx", "openclaw", "gateway", "run", "--port", "18789", "--bind", "loopback", "--auth", "none"] + volumes: *common-volumes + depends_on: + bus: + condition: service_healthy + healthcheck: + test: ["CMD-SHELL", "node -e \"require('net').connect(18789, '127.0.0.1').on('connect', () => process.exit(0)).on('error', () => process.exit(1))\""] + interval: 5s + timeout: 5s + retries: 20 + start_period: 30s + + runner: + build: *common-build + user: claw + working_dir: /opt/qa-src + environment: + QA_BUS_URL: http://bus:43123 + OPENCLAW_CONFIG_PATH: /home/claw/.openclaw/openclaw.json + ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY} + entrypoint: ["node", "/opt/qa-src/dist/runner.js"] + volumes: *common-volumes + depends_on: + bus: + condition: service_healthy + gateway: + condition: service_healthy diff --git a/packages/openclaw-qa-runner/package.json b/packages/openclaw-qa-runner/package.json new file mode 100644 index 0000000..d7ca070 --- /dev/null +++ b/packages/openclaw-qa-runner/package.json @@ -0,0 +1,61 @@ +{ + "name": "@paleo/openclaw-qa-runner", + "version": "0.2.1", + "description": "Dockerised regression-test harness for OpenClaw workspaces: bus, scenario driver, judge, Compose stack.", + "keywords": [ + "openclaw", + "qa", + "testing", + "harness", + "scenario", + "docker" + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/paleo/alignfirst.git", + "directory": "packages/openclaw-qa-runner" + }, + "engines": { + "node": ">=22.11.0" + }, + "packageManager": "npm@11.11.0", + "type": "module", + "main": "./dist/index.js", + "bin": { + "openclaw-qa-runner": "./bin/cli.mjs" + }, + "exports": "./dist/index.js", + "files": [ + "dist/", + "bin/", + "templates/", + "docker-compose.yml", + "Dockerfile", + "LICENSE" + ], + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsc -p tsconfig.build.json", + "clear": "rimraf dist/*", + "lint": "biome check", + "prepublishOnly": "npm run clear && npm run build && npm run lint" + }, + "peerDependencies": { + "openclaw": "*" + }, + "dependencies": { + "@anthropic-ai/sdk": "~0.97.1", + "@paleo/openclaw-channel-mock-core": "0.2.1", + "@paleo/openclaw-discord-mock": "0.2.1", + "@paleo/openclaw-slack-mock": "0.2.1" + }, + "devDependencies": { + "@types/node": "~24.12.4", + "openclaw": "2026.5.18", + "rimraf": "~6.1.3", + "typescript": "~6.0.3" + } +} diff --git a/packages/openclaw-qa-runner/src/bus.ts b/packages/openclaw-qa-runner/src/bus.ts new file mode 100644 index 0000000..c400daf --- /dev/null +++ b/packages/openclaw-qa-runner/src/bus.ts @@ -0,0 +1,23 @@ +import { createServer } from "node:http"; +import { createBus } from "@paleo/openclaw-channel-mock-core"; + +const PORT = 43123; +const HOST = "0.0.0.0"; + +export function startBus(): void { + const { handler } = createBus(); + const server = createServer(async (req, res) => { + const handled = await handler(req, res); + if (!handled) { + res.statusCode = 404; + res.end("not found"); + } + }); + server.listen(PORT, HOST, () => { + console.log(`channel-mock bus listening on ${HOST}:${PORT}`); + }); +} + +if (import.meta.url === `file://${process.argv[1]}`) { + startBus(); +} diff --git a/packages/openclaw-qa-runner/src/cli.ts b/packages/openclaw-qa-runner/src/cli.ts new file mode 100644 index 0000000..224fd3b --- /dev/null +++ b/packages/openclaw-qa-runner/src/cli.ts @@ -0,0 +1,53 @@ +import { copyFile, mkdir, readdir } from "node:fs/promises"; +import { dirname, join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { startBus } from "./bus.js"; +import { main as runnerMain } from "./runner.js"; + +const PACKAGE_DIR = resolve(dirname(fileURLToPath(import.meta.url)), ".."); + +export async function dispatch(argv: string[]): Promise { + const [cmd, ...rest] = argv; + switch (cmd) { + case "init": + await initCommand(rest[0]); + return; + case "bus": + startBus(); + return; + case "run": + await runnerMain(rest); + return; + default: + usage(); + } +} + +async function initCommand(targetDirRaw: string | undefined): Promise { + if (!targetDirRaw) usage(); + const target = resolve(process.cwd(), targetDirRaw); + const templatesDir = join(PACKAGE_DIR, "templates"); + await mkdir(target, { recursive: true }); + const entries = await readdir(templatesDir); + for (const name of entries) { + await copyFile(join(templatesDir, name), join(target, name)); + console.log(`copied ${name} -> ${join(target, name)}`); + } +} + +function usage(): never { + console.error( + "usage: openclaw-qa-runner [args]\n\n" + + " init copy templates into target dir\n" + + " bus start the bus HTTP server\n" + + " run [flags] [...] execute scenarios (see runner.ts)\n", + ); + process.exit(1); +} + +if (import.meta.url === `file://${process.argv[1]}`) { + dispatch(process.argv.slice(2)).catch((err) => { + console.error("openclaw-qa-runner crash:", err); + process.exit(1); + }); +} diff --git a/packages/openclaw-qa-runner/src/context.ts b/packages/openclaw-qa-runner/src/context.ts new file mode 100644 index 0000000..4fe733d --- /dev/null +++ b/packages/openclaw-qa-runner/src/context.ts @@ -0,0 +1,317 @@ +import { + getQaBusState, + injectQaBusInboundMessage, + pollQaBus, + type QaBusConversation, + type QaBusMessage, + type QaBusPollResult, +} from "@paleo/openclaw-channel-mock-core"; +import { judgeCostUsd } from "./cost.js"; +import { judgeLLM, type JudgeUsage, type JudgeVerdict } from "./judge.js"; +import type { + AssertionRecord, + ChannelId, + CliMockCall, + CliMockHandler, + EmitSink, + ReportEvent, + ScenarioFailure, +} from "./report.js"; + +const BUS_URL = process.env.QA_BUS_URL ?? "http://bus:43123"; + +export type { ChannelId } from "./report.js"; +export type Conversation = QaBusConversation; +export type BusMessage = QaBusMessage; + +export interface PollResult { + messages: BusMessage[]; + nextCursor: number; +} + +export interface ScenarioContext { + channel: ChannelId; + conversationId: string; + accountId: ChannelId; + log(message: string): void; + sendInbound(input: SendInboundInput): Promise; + poll(opts: { sinceCursor: number; timeoutMs?: number }): Promise; + waitForOutbound( + predicate: (m: BusMessage) => boolean, + opts: { timeoutMs?: number; sinceCursor: number }, + ): Promise<{ match: BusMessage; nextCursor: number }>; + expectNoOutbound( + predicate: (m: BusMessage) => boolean, + opts: { withinMs: number; sinceCursor: number }, + ): Promise<{ nextCursor: number }>; + assertRegex(actual: string, pattern: RegExp, label: string): void; + assertEqual(actual: T, expected: T, label: string): void; + assertLength( + value: { length: number } | string | unknown[], + expected: number, + label: string, + ): void; + judgeLLM(p: { message: string; rubric: string; label: string }): Promise; + getCursor(): Promise; + mockCli(name: string, handler: CliMockHandler): void; +} + +export interface ScenarioInternals { + finalize(opts?: { failure?: ScenarioFailure }): { + events: ReportEvent[]; + judgeUsages: JudgeUsage[]; + }; + emitOutboundReceived(m: BusMessage): void; + emitCliMock(call: CliMockCall): void; + getMockHandlers(): Map; + peekEvents(): { events: ReportEvent[] }; +} + +export interface SendInboundInput { + senderId: string; + senderName?: string; + text: string; + threadId?: string; + conversation?: Conversation; +} + +export class AssertionError extends Error { + constructor(msg: string) { + super(msg); + this.name = "AssertionError"; + } +} + +export function createContext(params: { + channel: ChannelId; + conversationId: string; + emitSink?: EmitSink; +}): { ctx: ScenarioContext; internals: ScenarioInternals } { + const { channel, conversationId, emitSink } = params; + const accountId: ChannelId = channel; + const events: ReportEvent[] = []; + const judgeUsages: JudgeUsage[] = []; + const mockHandlers = new Map(); + let seq = 0; + + const emit = (partial: EmitInput): ReportEvent => { + const sealed = { seq: seq++, ts: new Date().toISOString(), ...partial } as ReportEvent; + events.push(sealed); + if (emitSink) emitSink(sealed); + return sealed; + }; + + const ctx: ScenarioContext = { + channel, + conversationId, + accountId, + log: (message) => void emit({ kind: "log", message }), + sendInbound: (input) => sendInbound(emit, accountId, conversationId, input), + poll: (opts) => poll(accountId, opts), + waitForOutbound: (predicate, opts) => waitForOutbound(accountId, predicate, opts), + expectNoOutbound: (predicate, opts) => expectNoOutbound(accountId, predicate, opts), + assertRegex: (actual, pattern, label) => assertRegex(emit, actual, pattern, label), + assertEqual: (actual, expected, label) => assertEqual(emit, actual, expected, label), + assertLength: (value, expected, label) => assertLength(emit, value, expected, label), + judgeLLM: (p) => callJudge(emit, judgeUsages, p), + getCursor, + mockCli: (name, handler) => registerMockCli(mockHandlers, name, handler), + }; + + const internals: ScenarioInternals = { + finalize: (opts) => { + if (opts?.failure) emit({ kind: "failure", failure: opts.failure }); + return { events, judgeUsages }; + }, + emitOutboundReceived: (m) => + void emit({ kind: "outboundReceived", messageId: m.id, text: m.text, threadId: m.threadId }), + emitCliMock: (call) => void emit({ kind: "cliMock", call }), + getMockHandlers: () => mockHandlers, + peekEvents: () => ({ events }), + }; + + return { ctx, internals }; +} + +type EmitInput = DistributedOmit; +type DistributedOmit = T extends unknown ? Omit : never; +type EmitFn = (partial: EmitInput) => ReportEvent; + +function registerMockCli( + handlers: Map, + name: string, + handler: CliMockHandler, +): void { + if (handlers.has(name)) { + throw new Error(`mockCli: handler for "${name}" already registered`); + } + handlers.set(name, handler); +} + +async function sendInbound( + emit: EmitFn, + accountId: ChannelId, + conversationId: string, + input: SendInboundInput, +): Promise { + const conversation: Conversation = input.conversation ?? { + kind: "channel", + id: conversationId, + title: conversationId, + }; + const r = await injectQaBusInboundMessage({ + baseUrl: BUS_URL, + input: { + accountId, + conversation, + senderId: input.senderId, + senderName: input.senderName, + text: input.text, + threadId: input.threadId, + }, + }); + emit({ + kind: "inboundSent", + messageId: r.message.id, + text: input.text, + senderId: input.senderId, + senderName: input.senderName, + threadId: input.threadId, + }); + return r.message; +} + +async function poll( + accountId: ChannelId, + opts: { sinceCursor: number; timeoutMs?: number }, +): Promise { + const r: QaBusPollResult = await pollQaBus({ + baseUrl: BUS_URL, + accountId, + cursor: opts.sinceCursor, + timeoutMs: opts.timeoutMs ?? 1000, + }); + const messages = r.events + .filter((e) => e.kind === "outbound-message" || e.kind === "message-edited") + .map((e) => (e as { message: BusMessage }).message); + return { messages, nextCursor: r.cursor }; +} + +async function waitForOutbound( + accountId: ChannelId, + predicate: (m: BusMessage) => boolean, + opts: { timeoutMs?: number; sinceCursor: number }, +): Promise<{ match: BusMessage; nextCursor: number }> { + const timeoutMs = opts.timeoutMs ?? 30_000; + const deadline = Date.now() + timeoutMs; + let cursor = opts.sinceCursor; + while (Date.now() < deadline) { + const remaining = Math.max(0, deadline - Date.now()); + const { messages, nextCursor } = await poll(accountId, { + sinceCursor: cursor, + timeoutMs: Math.min(5000, remaining), + }); + cursor = nextCursor; + const match = messages.find(predicate); + if (match) return { match, nextCursor }; + } + throw new AssertionError(`waitForOutbound timed out after ${timeoutMs}ms`); +} + +async function expectNoOutbound( + accountId: ChannelId, + predicate: (m: BusMessage) => boolean, + opts: { withinMs: number; sinceCursor: number }, +): Promise<{ nextCursor: number }> { + const deadline = Date.now() + opts.withinMs; + let cursor = opts.sinceCursor; + while (Date.now() < deadline) { + const remaining = Math.max(0, deadline - Date.now()); + const { messages, nextCursor } = await poll(accountId, { + sinceCursor: cursor, + timeoutMs: Math.min(remaining, 1000), + }); + cursor = nextCursor; + const offender = messages.find(predicate); + if (offender) { + throw new AssertionError( + `expectNoOutbound: forbidden message arrived: ${JSON.stringify({ + id: offender.id, + text: offender.text, + threadId: offender.threadId, + })}`, + ); + } + } + return { nextCursor: cursor }; +} + +function assertRegex(emit: EmitFn, actual: string, pattern: RegExp, label: string): void { + recordAssertion( + emit, + pattern.test(actual), + label, + `value=${JSON.stringify(actual)} pattern=${pattern.source}`, + ); +} + +function assertEqual(emit: EmitFn, actual: T, expected: T, label: string): void { + recordAssertion( + emit, + actual === expected, + label, + `expected=${JSON.stringify(expected)} actual=${JSON.stringify(actual)}`, + ); +} + +function assertLength( + emit: EmitFn, + value: { length: number } | string | unknown[], + expected: number, + label: string, +): void { + const len = value.length; + recordAssertion(emit, len === expected, label, `expected length=${expected} actual=${len}`); +} + +function recordAssertion(emit: EmitFn, ok: boolean, label: string, detail: string): void { + if (ok) { + emit({ kind: "assertion", record: { label, ok: true } }); + return; + } + const record: AssertionRecord = { label, ok: false, detail }; + emit({ kind: "assertion", record }); + throw new AssertionError(`${label}: ${detail}`); +} + +async function callJudge( + emit: EmitFn, + judgeUsages: JudgeUsage[], + p: { message: string; rubric: string; label: string }, +): Promise { + const verdict = await judgeLLM({ message: p.message, rubric: p.rubric }); + judgeUsages.push(verdict.usage); + emit({ + kind: "judge", + record: { + label: p.label, + verdict: verdict.verdict, + reasoning: verdict.reasoning, + model: verdict.usage.model, + usage: { + inputTokens: verdict.usage.inputTokens, + outputTokens: verdict.usage.outputTokens, + }, + costUsd: judgeCostUsd(verdict.usage), + }, + }); + if (verdict.verdict !== "pass") { + throw new AssertionError(`judge[${p.label}] failed: ${verdict.reasoning}`); + } + return verdict; +} + +async function getCursor(): Promise { + const snap = await getQaBusState(BUS_URL); + return snap.cursor; +} diff --git a/packages/openclaw-qa-runner/src/cost.ts b/packages/openclaw-qa-runner/src/cost.ts new file mode 100644 index 0000000..c1a0eb7 --- /dev/null +++ b/packages/openclaw-qa-runner/src/cost.ts @@ -0,0 +1,26 @@ +import type { JudgeUsage } from "./judge.js"; + +// USD per million tokens. Judge never uses prompt caching. Keys are LiteLLM-style +// "provider/model" refs. Add models as needed. +const JUDGE_PRICING: Record = { + "anthropic/claude-haiku-4-5": { input: 1.0, output: 5.0 }, +}; + +const warnedUnknownModels = new Set(); + +export function judgeCostUsd(usage: JudgeUsage): number { + const key = usage.model.replace(/-\d{8}$/, ""); + const price = JUDGE_PRICING[key]; + if (!price) { + if (!warnedUnknownModels.has(key)) { + warnedUnknownModels.add(key); + console.warn( + `cost: no JUDGE_PRICING entry for ${JSON.stringify(key)}; judge cost will report 0. Add it to src/cost.ts.`, + ); + } + return 0; + } + return ( + (usage.inputTokens * price.input) / 1_000_000 + (usage.outputTokens * price.output) / 1_000_000 + ); +} diff --git a/packages/openclaw-qa-runner/src/gateway-log.ts b/packages/openclaw-qa-runner/src/gateway-log.ts new file mode 100644 index 0000000..8197a1b --- /dev/null +++ b/packages/openclaw-qa-runner/src/gateway-log.ts @@ -0,0 +1,183 @@ +import { readFileSync, statSync } from "node:fs"; +import type { AgentToolCall } from "./report.js"; + +const GATEWAY_LOG_PATH = + process.env.QA_GATEWAY_ANTHROPIC_LOG ?? "/home/claw/.openclaw/logs/anthropic-payload.jsonl"; + +export { GATEWAY_LOG_PATH }; + +interface GatewayLogEntry { + ts?: string; + stage?: string; + sessionKey?: string; + payload?: { messages?: Array<{ role: string; content: unknown }> }; + usage?: { cost?: { total?: number } }; +} + +interface ToolResultBlock { + isError: boolean; + content: unknown; +} + +/** + * Polls the gateway log until a `stage:"usage"` entry matching this + * conversation lands (it flushes ~2-10s after the agent's last bus traffic), + * or the budget expires. mtime-based "quiescence" doesn't work: the log can + * stay idle for several seconds *before* the usage record is written, which + * the previous heuristic mistook for "done". + */ +export async function waitForGatewayUsage(opts: { + conversationId: string; + startedAtIso: string; + maxWaitMs?: number; + pollMs?: number; +}): Promise { + const maxWaitMs = opts.maxWaitMs ?? 15_000; + const pollMs = opts.pollMs ?? 250; + const deadline = Date.now() + maxWaitMs; + while (Date.now() < deadline) { + if (!gatewayLogExists()) return; + for (const entry of readGatewayLog()) { + if (!isUsageEntryFor(entry, opts.conversationId, opts.startedAtIso)) continue; + return; + } + await new Promise((r) => setTimeout(r, pollMs)); + } +} + +export function readGatewayCostFor(opts: { startTsIso: string; conversationId?: string }): { + cost: number; + turns: number; +} { + let cost = 0; + let turns = 0; + for (const entry of readGatewayLog()) { + if (entry.stage !== "usage" || !entry.ts || entry.ts < opts.startTsIso) continue; + if (opts.conversationId && !entry.sessionKey?.includes(opts.conversationId)) continue; + const total = entry.usage?.cost?.total; + if (typeof total === "number") { + cost += total; + turns += 1; + } + } + return { cost, turns }; +} + +/** + * Parse agent tool calls from the gateway payload log filtered by conversationId. + * We take the LAST `request` entry for that session (the most complete transcript) + * and walk its messages, collecting assistant `tool_use` blocks and matching them + * with `tool_result` blocks from subsequent user messages. + */ +export function parseAgentToolCalls(opts: { + conversationId: string; + startedAtIso: string; +}): AgentToolCall[] { + const last = findLastRequestEntry(opts); + if (!last) return []; + const messages = last.payload?.messages ?? []; + const results = collectToolResults(messages); + return collectToolUses(messages, results, last.ts ?? ""); +} + +export function gatewayLogExists(): boolean { + try { + statSync(GATEWAY_LOG_PATH); + return true; + } catch { + return false; + } +} + +function readGatewayLog(): GatewayLogEntry[] { + let raw: string; + try { + raw = readFileSync(GATEWAY_LOG_PATH, "utf8"); + } catch { + return []; + } + const out: GatewayLogEntry[] = []; + for (const line of raw.split("\n")) { + if (!line) continue; + try { + out.push(JSON.parse(line) as GatewayLogEntry); + } catch { + // skip malformed lines + } + } + return out; +} + +function isUsageEntryFor( + entry: GatewayLogEntry, + conversationId: string, + startedAtIso: string, +): boolean { + if (entry.stage !== "usage" || !entry.ts || entry.ts < startedAtIso) return false; + return entry.sessionKey?.includes(conversationId) === true; +} + +function findLastRequestEntry(opts: { + conversationId: string; + startedAtIso: string; +}): GatewayLogEntry | undefined { + const matching = readGatewayLog().filter( + (e) => + e.ts !== undefined && + e.ts >= opts.startedAtIso && + e.sessionKey?.includes(opts.conversationId) === true && + e.stage === "request" && + e.payload?.messages !== undefined, + ); + if (matching.length === 0) return; + matching.sort((a, b) => ((a.ts ?? "") < (b.ts ?? "") ? -1 : 1)); + return matching[matching.length - 1]; +} + +function collectToolResults( + messages: Array<{ role: string; content: unknown }>, +): Map { + const results = new Map(); + for (const msg of messages) { + if (msg.role !== "user" || !Array.isArray(msg.content)) continue; + for (const block of msg.content as Array>) { + if (block?.type === "tool_result" && typeof block.tool_use_id === "string") { + results.set(block.tool_use_id, { + isError: block.is_error === true, + content: block.content ?? null, + }); + } + } + } + return results; +} + +function collectToolUses( + messages: Array<{ role: string; content: unknown }>, + results: Map, + ts: string, +): AgentToolCall[] { + const calls: AgentToolCall[] = []; + let turn = 0; + for (const msg of messages) { + if (msg.role !== "assistant" || !Array.isArray(msg.content)) continue; + turn += 1; + for (const block of msg.content as Array>) { + if (block?.type !== "tool_use") continue; + const toolUseId = typeof block.id === "string" ? block.id : ""; + const toolName = typeof block.name === "string" ? block.name : ""; + if (!toolUseId || !toolName) continue; + const call: AgentToolCall = { + toolName, + toolUseId, + input: block.input ?? null, + startedAt: ts, + turn, + }; + const result = results.get(toolUseId); + if (result) call.result = result; + calls.push(call); + } + } + return calls; +} diff --git a/packages/openclaw-qa-runner/src/index.ts b/packages/openclaw-qa-runner/src/index.ts new file mode 100644 index 0000000..294acbc --- /dev/null +++ b/packages/openclaw-qa-runner/src/index.ts @@ -0,0 +1,23 @@ +export type { ChannelId, ScenarioContext } from "./context.js"; +export type { + AgentToolCall, + AgentToolCallEvent, + AssertionEvent, + AssertionRecord, + CliMockCall, + CliMockEvent, + CliMockHandler, + CliMockHandlerArgs, + CostBreakdown, + EmitSink, + FailureEvent, + InboundSentEvent, + JudgeCallRecord, + JudgeEvent, + LogEvent, + OutboundReceivedEvent, + ReportEvent, + ReportEventBase, + ScenarioFailure, + ScenarioReport, +} from "./report.js"; diff --git a/packages/openclaw-qa-runner/src/judge.ts b/packages/openclaw-qa-runner/src/judge.ts new file mode 100644 index 0000000..0f85866 --- /dev/null +++ b/packages/openclaw-qa-runner/src/judge.ts @@ -0,0 +1,94 @@ +import Anthropic from "@anthropic-ai/sdk"; + +const DEFAULT_JUDGE_MODEL = "anthropic/claude-haiku-4-5"; + +let cached: { client: Anthropic; model: string; bareModel: string } | undefined; + +export interface JudgeUsage { + model: string; + inputTokens: number; + outputTokens: number; +} + +export interface JudgeVerdict { + verdict: "pass" | "fail"; + reasoning: string; + raw: string; + usage: JudgeUsage; +} + +export async function judgeLLM(params: { message: string; rubric: string }): Promise { + const { client, model, bareModel } = getJudge(); + const resp = await client.messages.create({ + model: bareModel, + max_tokens: 512, + messages: [ + { + role: "user", + content: `${params.rubric}\n\n---\nMessage under evaluation:\n${params.message}`, + }, + ], + }); + const raw = resp.content.map((b) => (b.type === "text" ? b.text : "")).join(""); + const body = parseVerdictBody(raw); + if (!resp.usage) throw new Error("judge response missing usage"); + const usage: JudgeUsage = { + model, + inputTokens: resp.usage.input_tokens, + outputTokens: resp.usage.output_tokens, + }; + return { ...body, raw, usage }; +} + +function getJudge(): { client: Anthropic; model: string; bareModel: string } { + if (cached) return cached; + const apiKey = process.env.ANTHROPIC_API_KEY; + if (!apiKey) throw new Error("ANTHROPIC_API_KEY is not set — fill in qa/.env.local"); + const model = process.env.QA_JUDGE_MODEL ?? DEFAULT_JUDGE_MODEL; + const bareModel = parseAnthropicModelRef(model); + cached = { client: new Anthropic({ apiKey }), model, bareModel }; + return cached; +} + +function parseAnthropicModelRef(ref: string): string { + const slash = ref.indexOf("/"); + if (slash < 0) { + throw new Error( + `QA_JUDGE_MODEL must be a LiteLLM-style "provider/model" reference (e.g. "anthropic/claude-haiku-4-5"); got ${JSON.stringify(ref)}`, + ); + } + const provider = ref.slice(0, slash); + const name = ref.slice(slash + 1); + if (provider !== "anthropic") { + throw new Error( + `QA_JUDGE_MODEL provider ${JSON.stringify(provider)} is not supported; only "anthropic/" is currently wired up`, + ); + } + if (!name) throw new Error(`QA_JUDGE_MODEL model name is empty after the "${provider}/" prefix`); + return name; +} + +function parseVerdictBody(raw: string): { verdict: "pass" | "fail"; reasoning: string } { + const start = raw.indexOf("{"); + const end = raw.lastIndexOf("}"); + if (start < 0 || end < 0 || end < start) { + throw new Error(`judge response did not contain a JSON object. raw=${JSON.stringify(raw)}`); + } + const slice = raw.slice(start, end + 1); + let parsed: unknown; + try { + parsed = JSON.parse(slice); + } catch (err) { + throw new Error( + `judge JSON parse failed: ${(err as Error).message}. raw=${JSON.stringify(raw)}`, + ); + } + const obj = parsed as { verdict?: unknown; reasoning?: unknown }; + if (obj.verdict !== "pass" && obj.verdict !== "fail") { + throw new Error(`judge verdict invalid: ${JSON.stringify(obj.verdict)}`); + } + if (typeof obj.reasoning !== "string") { + throw new Error(`judge reasoning not a string: ${JSON.stringify(obj.reasoning)}`); + } + return { verdict: obj.verdict, reasoning: obj.reasoning }; +} diff --git a/packages/openclaw-qa-runner/src/mock-cli-server.ts b/packages/openclaw-qa-runner/src/mock-cli-server.ts new file mode 100644 index 0000000..79d706f --- /dev/null +++ b/packages/openclaw-qa-runner/src/mock-cli-server.ts @@ -0,0 +1,236 @@ +/** + * Runner-side HTTP endpoint that the gateway-side shim calls. + * + * POST /mock-cli/invoke + * { cli, argv, cwd, stdin } + * → { stdout, stderr, exitCode } + * + * A single in-flight registry is bound by the runner for the lifetime of one + * scenario. Per-conversation handler registries are populated by `ctx.mockCli`. + */ + +import { createServer, type IncomingMessage, type ServerResponse } from "node:http"; +import { Readable, Writable } from "node:stream"; +import type { CliMockCall, CliMockHandler, CliMockHandlerArgs } from "./report.js"; + +export const MOCK_CLI_PORT = 43124; + +export interface ConversationRegistry { + conversationId: string; + handlers: Map; + /** Emit a `cliMock` event into the scenario's event stream. */ + emitCliMock: (call: CliMockCall) => void; +} + +export interface MockCliServer { + /** Bind the in-flight conversation registry for the lifetime of one scenario. */ + bind(reg: ConversationRegistry): void; + /** Release the binding. Subsequent calls will 1-fail. */ + release(): void; + /** Stop the HTTP listener. */ + close(): Promise; +} + +export function startMockCliServer(): MockCliServer { + let current: ConversationRegistry | undefined; + + const server = createServer(async (req, res) => { + if (req.method !== "POST" || req.url !== "/mock-cli/invoke") { + res.statusCode = 404; + res.end("not found"); + return; + } + await handleInvoke(req, res, current); + }); + + server.listen(MOCK_CLI_PORT, "0.0.0.0", () => { + console.log(`mock-cli server listening on 0.0.0.0:${MOCK_CLI_PORT}`); + }); + + return { + bind: (reg) => { + current = reg; + }, + release: () => { + current = undefined; + }, + close: () => new Promise((resolve) => server.close(() => resolve())), + }; +} + +interface InvokeBody { + cli?: string; + argv?: string[]; + cwd?: string; + stdin?: string; +} + +interface InvokeRequest { + cli: string; + argv: string[]; + cwd: string; + stdin: string; +} + +interface HandlerOutcome { + stdout: string; + stderr: string; + exitCode: number; + handlerError?: { name: string; message: string; stack?: string }; +} + +async function handleInvoke( + req: IncomingMessage, + res: ServerResponse, + reg: ConversationRegistry | undefined, +): Promise { + let body: InvokeBody; + try { + body = await readJsonBody(req); + } catch (err) { + res.statusCode = 400; + res.end(`bad json: ${(err as Error).message}`); + return; + } + + const invoke = normalizeInvoke(body); + if (!invoke) { + res.statusCode = 400; + res.end("missing cli"); + return; + } + + if (!reg) { + respondMissingScenario(res); + return; + } + + const startedAt = new Date().toISOString(); + const startedAtMs = Date.now(); + const handler = reg.handlers.get(invoke.cli); + if (!handler) { + const stderr = `mock-cli: unexpected call to ${invoke.cli}\n`; + emitCallEvent( + reg, + invoke, + { + stdout: "", + stderr, + exitCode: 1, + handlerError: { name: "UnexpectedCall", message: `unexpected call to ${invoke.cli}` }, + }, + startedAt, + startedAtMs, + ); + respondJson(res, { stdout: "", stderr, exitCode: 1, missing: true }); + return; + } + + const outcome = await runHandler(handler, invoke); + emitCallEvent(reg, invoke, outcome, startedAt, startedAtMs); + respondJson(res, { stdout: outcome.stdout, stderr: outcome.stderr, exitCode: outcome.exitCode }); +} + +function normalizeInvoke(body: InvokeBody): InvokeRequest | undefined { + const cli = String(body.cli ?? ""); + if (!cli) return; + return { + cli, + argv: Array.isArray(body.argv) ? body.argv.map(String) : [], + cwd: String(body.cwd ?? ""), + stdin: String(body.stdin ?? ""), + }; +} + +async function runHandler(handler: CliMockHandler, invoke: InvokeRequest): Promise { + const stdinStream = Readable.from([Buffer.from(invoke.stdin, "utf8")]); + const outCap = collectStream(); + const errCap = collectStream(); + const args: CliMockHandlerArgs = { + argv: invoke.argv, + cwd: invoke.cwd, + stdin: stdinStream, + stdout: outCap.writable, + stderr: errCap.writable, + }; + + let exitCode = 0; + let handlerError: HandlerOutcome["handlerError"]; + try { + const result = await handler(args); + exitCode = typeof result === "number" ? result : 0; + } catch (err) { + const e = err as Error; + handlerError = { + name: e?.name ?? "Error", + message: e?.message ?? String(err), + stack: e?.stack, + }; + exitCode = 1; + } + outCap.writable.end(); + errCap.writable.end(); + return { stdout: outCap.read(), stderr: errCap.read(), exitCode, handlerError }; +} + +function emitCallEvent( + reg: ConversationRegistry, + invoke: InvokeRequest, + outcome: HandlerOutcome, + startedAt: string, + startedAtMs: number, +): void { + const call: CliMockCall = { + cli: invoke.cli, + argv: invoke.argv, + cwd: invoke.cwd, + stdin: invoke.stdin, + stdout: outcome.stdout, + stderr: outcome.stderr, + exitCode: outcome.exitCode, + startedAt, + durationMs: Date.now() - startedAtMs, + ...(outcome.handlerError ? { handlerError: outcome.handlerError } : {}), + }; + reg.emitCliMock(call); +} + +function respondMissingScenario(res: ServerResponse): void { + const stderr = "mock-cli: no scenario currently owns this gateway\n"; + respondJson(res, { stdout: "", stderr, exitCode: 1, missing: true }); +} + +function respondJson(res: ServerResponse, body: unknown): void { + res.setHeader("content-type", "application/json"); + res.end(JSON.stringify(body)); +} + +function readJsonBody(req: IncomingMessage): Promise { + return new Promise((resolve, reject) => { + const chunks: Buffer[] = []; + req.on("data", (c: Buffer) => chunks.push(c)); + req.on("end", () => { + const raw = Buffer.concat(chunks).toString("utf8"); + try { + resolve(raw ? (JSON.parse(raw) as InvokeBody) : {}); + } catch (err) { + reject(err); + } + }); + req.on("error", reject); + }); +} + +function collectStream(): { writable: Writable; read: () => string } { + const chunks: Buffer[] = []; + const writable = new Writable({ + write(chunk: Buffer | string, _enc, cb) { + chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk); + cb(); + }, + }); + return { + writable, + read: () => Buffer.concat(chunks).toString("utf8"), + }; +} diff --git a/packages/openclaw-qa-runner/src/mock-cli-shim.ts b/packages/openclaw-qa-runner/src/mock-cli-shim.ts new file mode 100644 index 0000000..f574c59 --- /dev/null +++ b/packages/openclaw-qa-runner/src/mock-cli-shim.ts @@ -0,0 +1,100 @@ +/** + * Mock-CLI shim. Symlinked from `/opt/qa-mocks/bin/{git,npm,pnpm,yarn,claude}`. + * Determines its invoked name from argv[1] basename, POSTs the call to the + * runner's /mock-cli/invoke endpoint, and replays the response locally. + */ + +import { basename } from "node:path"; +import { request as httpRequest } from "node:http"; +import { URL } from "node:url"; + +function die(msg: string, code = 127): never { + process.stderr.write(`mock-cli shim: ${msg}\n`); + process.exit(code); +} + +async function readStdin(): Promise { + if (process.stdin.isTTY) return ""; + const chunks: Buffer[] = []; + for await (const chunk of process.stdin) { + chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk); + } + return Buffer.concat(chunks).toString("utf8"); +} + +interface ShimResponse { + stdout: string; + stderr: string; + exitCode: number; +} + +function post(urlStr: string, body: string): Promise { + const url = new URL(urlStr); + return new Promise((resolve, reject) => { + const req = httpRequest( + { + method: "POST", + hostname: url.hostname, + port: url.port || 80, + path: url.pathname + url.search, + headers: { + "content-type": "application/json", + "content-length": Buffer.byteLength(body), + }, + }, + (res) => { + const chunks: Buffer[] = []; + res.on("data", (c: Buffer) => chunks.push(c)); + res.on("end", () => { + const raw = Buffer.concat(chunks).toString("utf8"); + if (res.statusCode !== 200) { + reject(new Error(`runner returned HTTP ${res.statusCode}: ${raw}`)); + return; + } + try { + resolve(JSON.parse(raw) as ShimResponse); + } catch (err) { + reject(new Error(`invalid JSON response: ${(err as Error).message}`)); + } + }); + res.on("error", reject); + }, + ); + req.on("error", reject); + req.write(body); + req.end(); + }); +} + +async function main(): Promise { + // The sh wrapper at /opt/qa-mocks/bin/mock-cli-shim invokes us as + // exec node mock-cli-shim.js "$0" "$@" + // so argv[2] is the symlink path used to call us (e.g. /opt/qa-mocks/bin/git) + // and argv.slice(3) is the original argv tail. + const invokedAs = basename(process.argv[2] ?? ""); + if (!invokedAs) die("could not determine invoked binary name from argv[2]"); + + const runnerUrl = process.env.QA_RUNNER_URL; + if (!runnerUrl) die("QA_RUNNER_URL is not set"); + + const stdin = await readStdin(); + const body = JSON.stringify({ + cli: invokedAs, + argv: process.argv.slice(3), + cwd: process.cwd(), + stdin, + }); + + let res: ShimResponse; + try { + res = await post(`${runnerUrl}/mock-cli/invoke`, body); + } catch (err) { + die(`POST ${runnerUrl}/mock-cli/invoke failed: ${(err as Error).message}`); + } + + if (res.stdout) process.stdout.write(res.stdout); + if (res.stderr) process.stderr.write(res.stderr); + process.exit(typeof res.exitCode === "number" ? res.exitCode : 1); +} + +main().catch((err) => die((err as Error).message ?? String(err))); diff --git a/packages/openclaw-qa-runner/src/report.ts b/packages/openclaw-qa-runner/src/report.ts new file mode 100644 index 0000000..c4e6baa --- /dev/null +++ b/packages/openclaw-qa-runner/src/report.ts @@ -0,0 +1,206 @@ +/** + * Scenario report typing for QA harness. + * + * Two artifacts per scenario run, under `qa/artifacts/--/`: + * + * - `events.jsonl` — live append, one `ReportEvent` per line, written as things happen. + * Survives runner crash / hang. + * - `report.json` — final `ScenarioReport`, written once at end. Supersedes the old + * `report.md` + `summary.json` pair (both removed). + * + * `report.json` carries the same events as `events.jsonl`, plus terminal data only + * computable at the end (status, durationMs, finishedAt, parsed agentToolCalls, cost). + */ + +import type { Readable, Writable } from "node:stream"; + +// ─── Top-level report ───────────────────────────────────────────────────────── + +export interface ScenarioReport { + schemaVersion: 1; + scenario: string; + channel: ChannelId; + conversationId: string; + accountId: string; + + status: "pass" | "fail"; + startedAt: string; // ISO-8601 + finishedAt: string; // ISO-8601 + durationMs: number; + + events: ReportEvent[]; + + failure?: ScenarioFailure; + cost: CostBreakdown; +} + +export type ChannelId = "discord-mock" | "slack-mock"; + +// ─── Event timeline (also the `events.jsonl` line type) ─────────────────────── + +export type ReportEvent = + | LogEvent + | InboundSentEvent + | OutboundReceivedEvent + | AssertionEvent + | JudgeEvent + | CliMockEvent + | AgentToolCallEvent + | FailureEvent; + +export interface ReportEventBase { + ts: string; // ISO-8601 + seq: number; // monotonic per scenario, starting at 0 +} + +export interface LogEvent extends ReportEventBase { + kind: "log"; + message: string; +} + +export interface InboundSentEvent extends ReportEventBase { + kind: "inboundSent"; + messageId: string; + text: string; + senderId: string; + senderName?: string; + threadId?: string; +} + +export interface OutboundReceivedEvent extends ReportEventBase { + kind: "outboundReceived"; + messageId: string; + text: string; + threadId?: string; +} + +export interface AssertionEvent extends ReportEventBase { + kind: "assertion"; + record: AssertionRecord; +} + +export interface JudgeEvent extends ReportEventBase { + kind: "judge"; + record: JudgeCallRecord; +} + +export interface CliMockEvent extends ReportEventBase { + kind: "cliMock"; + call: CliMockCall; +} + +export interface AgentToolCallEvent extends ReportEventBase { + kind: "agentToolCall"; + call: AgentToolCall; +} + +export interface FailureEvent extends ReportEventBase { + kind: "failure"; + failure: ScenarioFailure; +} + +// ─── Sub-records ────────────────────────────────────────────────────────────── + +export type AssertionRecord = + | { label: string; ok: true } + | { label: string; ok: false; detail: string }; + +export interface JudgeCallRecord { + label: string; + verdict: "pass" | "fail"; + reasoning: string; + model: string; + usage: { inputTokens: number; outputTokens: number }; + costUsd: number; +} + +/** + * One mocked-CLI invocation: the agent shelled out, our shim routed the call to the + * scenario's registered handler, and we captured the round-trip. Emitted by plan A5. + */ +export interface CliMockCall { + cli: "git" | "npm" | "pnpm" | "yarn" | "claude" | (string & {}); + argv: string[]; // does not include argv[0] + cwd: string; + stdin: string; // captured if the agent piped input; "" otherwise + stdout: string; + stderr: string; + exitCode: number; + startedAt: string; + durationMs: number; + handlerError?: { name: string; message: string; stack?: string }; +} + +/** + * One tool call made by the OpenClaw agent during the scenario, parsed from the + * gateway's `anthropic-payload.jsonl` (filtered by `conversationId`). + * + * Best-effort: depends on `OPENCLAW_ANTHROPIC_PAYLOAD_LOG=1` (QA runs force it on). + * If the log is absent, this list is empty and a note lands in `events`. + */ +export interface AgentToolCall { + toolName: string; + toolUseId: string; + input: unknown; + result?: { isError: boolean; content: unknown }; + startedAt: string; + /** + * Best-effort estimate of when the tool call actually started, inferred by + * matching the call's leading CLI against an in-order `cliMock` event from + * the same scenario. `startedAt` carries the synthetic end-of-turn ts + * because the gateway log has no per-tool timestamp; this field, when + * present, lets readers see roughly when the call happened. Not used for + * `events` ordering. + */ + inferredStartedAt?: string; + turn?: number; +} + +export interface ScenarioFailure { + name: string; + message: string; + stack?: string; + source: "assertion" | "judge" | "cliMock" | "timeout" | "scenarioThrow" | "runner"; +} + +export interface CostBreakdown { + gatewayUsd: number; + judgeUsd: number; + totalUsd: number; + gatewayTurns: number; +} + +// ─── Scenario-side mock-CLI registration API (used by plan A5) ──────────────── + +/** + * Handler a scenario registers for a mocked CLI. Implemented by plan A5. + * + * ctx.mockCli("git", async ({ argv, cwd, stdin, stdout, stderr }) => { + * stdout.write("ok\n"); + * if (argv[0] === "push") return 1; + * // returning undefined ⇒ exit code 0 + * }); + * + * If the agent invokes a CLI for which no handler is registered, the scenario fails + * with `source: "cliMock"` and `message: "unexpected call to "`. + */ +export interface CliMockHandlerArgs { + argv: string[]; + cwd: string; + stdin: Readable; + stdout: Writable; + stderr: Writable; +} + +export type CliMockHandler = ( + args: CliMockHandlerArgs, +) => number | undefined | Promise; + +// ─── Runtime helpers (shared between context.ts and runner.ts) ──────────────── + +/** + * The callback `runner.ts` passes into `createContext` so live events can be + * appended to `events.jsonl` as they happen. Receives the already-sealed event + * (`seq` + `ts` set). + */ +export type EmitSink = (event: ReportEvent) => void; diff --git a/packages/openclaw-qa-runner/src/runner-args.ts b/packages/openclaw-qa-runner/src/runner-args.ts new file mode 100644 index 0000000..07940f6 --- /dev/null +++ b/packages/openclaw-qa-runner/src/runner-args.ts @@ -0,0 +1,74 @@ +import type { ChannelId } from "./report.js"; + +export type ChannelArg = ChannelId | "all"; + +export interface RunnerArgs { + channel: ChannelArg; + scenarios: string[]; + all: boolean; + iterations: number; + maxFailures: number; +} + +export function parseArgs(argv: string[]): RunnerArgs { + let channel: ChannelArg | undefined; + let all = false; + const scenarios: string[] = []; + let iterations = 1; + let maxFailures: number | undefined; + for (let i = 0; i < argv.length; ++i) { + const a = argv[i] as string; + if (a === "--channel") { + channel = parseChannel(argv[++i]); + } else if (a.startsWith("--channel=")) { + channel = parseChannel(a.slice("--channel=".length)); + } else if (a === "--iterations") { + iterations = parseNonNegativeInt(argv[++i], "--iterations", 1); + } else if (a.startsWith("--iterations=")) { + iterations = parseNonNegativeInt(a.slice("--iterations=".length), "--iterations", 1); + } else if (a === "--max-failures") { + maxFailures = parseNonNegativeInt(argv[++i], "--max-failures", 0); + } else if (a.startsWith("--max-failures=")) { + maxFailures = parseNonNegativeInt(a.slice("--max-failures=".length), "--max-failures", 0); + } else if (a === "--all") { + all = true; + } else if (a.startsWith("--")) { + throw new Error(`unknown flag: ${a}`); + } else { + scenarios.push(a); + } + } + if (channel === undefined) { + throw new Error("runner: --channel discord-mock|slack-mock|all is required"); + } + if (all && scenarios.length > 0) { + throw new Error("runner: pass either --all or a scenario list, not both"); + } + if (!all && scenarios.length === 0) { + throw new Error("runner: must pass --all or one or more scenario names"); + } + return { channel, scenarios, all, iterations, maxFailures: maxFailures ?? 1 }; +} + +function parseChannel(raw: string | undefined): ChannelArg { + if (raw !== "discord-mock" && raw !== "slack-mock" && raw !== "all") { + throw new Error( + `runner: --channel expects discord-mock|slack-mock|all, got ${JSON.stringify(raw)}`, + ); + } + return raw; +} + +function parseNonNegativeInt(raw: string | undefined, flag: string, min: number): number { + if (raw === undefined || raw === "") { + throw new Error(`runner: ${flag} expects an integer >= ${min}`); + } + if (!/^-?\d+$/.test(raw)) { + throw new Error(`runner: ${flag} expects an integer, got ${JSON.stringify(raw)}`); + } + const n = Number(raw); + if (!Number.isInteger(n) || n < min) { + throw new Error(`runner: ${flag} expects an integer >= ${min}, got ${JSON.stringify(raw)}`); + } + return n; +} diff --git a/packages/openclaw-qa-runner/src/runner.ts b/packages/openclaw-qa-runner/src/runner.ts new file mode 100644 index 0000000..100c7bf --- /dev/null +++ b/packages/openclaw-qa-runner/src/runner.ts @@ -0,0 +1,519 @@ +import { randomBytes } from "node:crypto"; +import { createWriteStream, mkdirSync, readdirSync, renameSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; +import { pollQaBus } from "@paleo/openclaw-channel-mock-core"; +import { + type ChannelId, + createContext, + type ScenarioContext, + type ScenarioInternals, +} from "./context.js"; +import { judgeCostUsd } from "./cost.js"; +import { + GATEWAY_LOG_PATH, + gatewayLogExists, + parseAgentToolCalls, + readGatewayCostFor, + waitForGatewayUsage, +} from "./gateway-log.js"; +import type { JudgeUsage } from "./judge.js"; +import { startMockCliServer } from "./mock-cli-server.js"; +import type { + AgentToolCall, + AgentToolCallEvent, + ReportEvent, + ScenarioFailure, + ScenarioReport, +} from "./report.js"; +import { parseArgs } from "./runner-args.js"; + +const ARTIFACTS_ROOT = process.env.QA_ARTIFACTS_ROOT ?? "/opt/qa-artifacts"; +const SCENARIOS_ROOT = process.env.QA_SCENARIOS_ROOT ?? "/opt/qa-src/scenarios"; +const BUS_URL = process.env.QA_BUS_URL ?? "http://bus:43123"; +const CHANNELS: ChannelId[] = ["discord-mock", "slack-mock"]; + +export async function main(argv: string[] = process.argv.slice(2)): Promise { + const opts = parseArgs(argv); + const scenarios = opts.all ? discoverScenarios() : opts.scenarios; + if (scenarios.length === 0) throw new Error("no scenarios discovered"); + const channels: ChannelId[] = opts.channel === "all" ? CHANNELS : [opts.channel]; + const { iterations, maxFailures } = opts; + + console.log( + `runner: channels=[${channels.join(",")}] scenarios=[${scenarios.join(",")}] iterations=${iterations} maxFailures=${maxFailures}`, + ); + + const mockCliServer = startMockCliServer(); + const baseStamp = new Date().toISOString().replace(/[:.]/g, "-"); + const iterationWidth = String(iterations).length; + const results: TaskResult[] = []; + const aggregates: PairAggregate[] = []; + + try { + for (const scenarioId of scenarios) { + for (const channel of channels) { + const agg = await runPair({ + scenarioId, + channel, + baseStamp, + iterations, + iterationWidth, + maxFailures, + mockCliServer, + results, + }); + aggregates.push(agg); + } + } + } finally { + await mockCliServer.close(); + } + + printSummary(aggregates, baseStamp); + printTotalCost(results); + process.exit(anyPairFailed(aggregates) ? 1 : 0); +} + +interface RunPairParams { + scenarioId: string; + channel: ChannelId; + baseStamp: string; + iterations: number; + iterationWidth: number; + maxFailures: number; + mockCliServer: ReturnType; + results: TaskResult[]; +} + +interface PairAggregate { + scenarioId: string; + channel: ChannelId; + runCount: number; + passCount: number; + durationSumMs: number; + stoppedAfter: number | undefined; +} + +async function runPair(params: RunPairParams): Promise { + const { scenarioId, channel, iterations, maxFailures, results } = params; + const agg: PairAggregate = { + scenarioId, + channel, + runCount: 0, + passCount: 0, + durationSumMs: 0, + stoppedAfter: undefined, + }; + let failures = 0; + for (let iter = 1; iter <= iterations; ++iter) { + const r = await runOne({ + baseStamp: params.baseStamp, + scenarioId, + channel, + iteration: iter, + iterationWidth: params.iterationWidth, + iterations, + mockCliServer: params.mockCliServer, + }); + results.push(r); + agg.runCount += 1; + agg.durationSumMs += r.durationMs; + if (r.status === "pass") { + agg.passCount += 1; + } else { + failures += 1; + if (failures > maxFailures) { + agg.stoppedAfter = failures; + break; + } + } + } + return agg; +} + +interface RunOneParams { + baseStamp: string; + scenarioId: string; + channel: ChannelId; + iteration: number; + iterationWidth: number; + iterations: number; + mockCliServer: ReturnType; +} + +interface TaskResult { + scenarioId: string; + channel: ChannelId; + conversationId: string; + status: "pass" | "fail"; + durationMs: number; + outDir: string; + judgeUsages: JudgeUsage[]; + gatewayCostUsd: number; + gatewayTurns: number; +} + +async function runOne(params: RunOneParams): Promise { + const setup = setupRun(params); + const { + ctx, + internals, + outDir, + conversationId, + accountId, + startedAtIso, + startedAtMs, + eventsStream, + } = setup; + + params.mockCliServer.bind({ + conversationId, + handlers: internals.getMockHandlers(), + emitCliMock: internals.emitCliMock, + }); + + const initialCursor = await ctx.getCursor(); + const subscription = startOutboundSubscription({ + accountId, + conversationId, + initialCursor, + onMessage: internals.emitOutboundReceived, + }); + + let { status, failure } = await executeScenario(params.scenarioId, ctx); + + await subscription.stop(); + params.mockCliServer.release(); + + if (status === "pass") { + const promoted = promoteCliMockFailure(internals); + if (promoted) { + status = "fail"; + failure = promoted; + } + } + + const finishedAtMs = Date.now(); + const durationMs = finishedAtMs - startedAtMs; + const finishedAtIso = new Date(finishedAtMs).toISOString(); + + const { events, judgeUsages } = internals.finalize({ failure }); + await closeStream(eventsStream); + + await waitForGatewayUsage({ conversationId, startedAtIso }); + const { cost: gatewayCostUsd, turns: gatewayTurns } = readGatewayCostFor({ + startTsIso: startedAtIso, + conversationId, + }); + const judgeUsd = judgeUsages.reduce((sum, u) => sum + judgeCostUsd(u), 0); + + const agentCalls = parseAgentToolCalls({ conversationId, startedAtIso }); + pairAgentCallsWithCliMocks(agentCalls, events); + if (agentCalls.length === 0 && !gatewayLogExists()) { + events.push({ + seq: events.length, + ts: finishedAtIso, + kind: "log", + message: `agentToolCall parsing skipped: ${GATEWAY_LOG_PATH} not found`, + }); + } + + const merged = mergeTimeline(events, agentCalls); + const report: ScenarioReport = { + schemaVersion: 1, + scenario: params.scenarioId, + channel: params.channel, + conversationId, + accountId, + status, + startedAt: startedAtIso, + finishedAt: finishedAtIso, + durationMs, + events: merged, + ...(failure ? { failure } : {}), + cost: { + gatewayUsd: gatewayCostUsd, + judgeUsd, + totalUsd: gatewayCostUsd + judgeUsd, + gatewayTurns, + }, + }; + const finalOutDir = writeReportArtifacts(outDir, status, report); + + console.log( + `[${params.channel}] ${params.scenarioId} ${status} in ${durationMs}ms — ${finalOutDir}`, + ); + return { + scenarioId: params.scenarioId, + channel: params.channel, + conversationId, + status, + durationMs, + outDir: finalOutDir, + judgeUsages, + gatewayCostUsd, + gatewayTurns, + }; +} + +interface RunSetup { + ctx: ScenarioContext; + internals: ScenarioInternals; + outDir: string; + conversationId: string; + accountId: ChannelId; + startedAtIso: string; + startedAtMs: number; + eventsStream: ReturnType; +} + +function setupRun(params: RunOneParams): RunSetup { + const conversationId = `${params.scenarioId}-${params.channel}-${shortRand()}`; + const accountId: ChannelId = params.channel; + const iterSuffix = + params.iterations > 1 + ? `-${String(params.iteration).padStart(params.iterationWidth, "0")}` + : ""; + const leaf = `${params.scenarioId}-${params.channel}${iterSuffix}`; + const outDir = join(ARTIFACTS_ROOT, params.baseStamp, leaf); + mkdirSync(outDir, { recursive: true }); + + const startedAtIso = new Date().toISOString(); + const startedAtMs = Date.now(); + const eventsStream = createWriteStream(join(outDir, "events.jsonl"), { flags: "a" }); + const emitSink = (event: ReportEvent) => { + eventsStream.write(`${JSON.stringify(event)}\n`); + }; + const { ctx, internals } = createContext({ channel: params.channel, conversationId, emitSink }); + return { + ctx, + internals, + outDir, + conversationId, + accountId, + startedAtIso, + startedAtMs, + eventsStream, + }; +} + +interface OutboundSubscription { + stop(): Promise; +} + +interface BusOutboundEvent { + kind: string; + message: { conversation: { id: string } } & Parameters< + ScenarioInternals["emitOutboundReceived"] + >[0]; +} + +function startOutboundSubscription(params: { + accountId: ChannelId; + conversationId: string; + initialCursor: number; + onMessage: (m: Parameters[0]) => void; +}): OutboundSubscription { + const abort = new AbortController(); + const done = (async () => { + let cursor = params.initialCursor; + while (!abort.signal.aborted) { + let result: Awaited>; + try { + result = await pollQaBus({ + baseUrl: BUS_URL, + accountId: params.accountId, + cursor, + timeoutMs: 1000, + }); + } catch { + if (abort.signal.aborted) return; + await new Promise((r) => setTimeout(r, 200)); + continue; + } + cursor = result.cursor; + for (const e of result.events as BusOutboundEvent[]) { + if (e.kind !== "outbound-message" && e.kind !== "message-edited") continue; + if (e.message.conversation.id !== params.conversationId) continue; + params.onMessage(e.message); + } + } + })(); + return { + stop: async () => { + abort.abort(); + await done.catch(() => {}); + }, + }; +} + +async function executeScenario( + scenarioId: string, + ctx: ScenarioContext, +): Promise<{ status: "pass" | "fail"; failure: ScenarioFailure | undefined }> { + try { + const mod = await import(`${SCENARIOS_ROOT}/${scenarioId}.ts`); + const fn = mod.default; + if (typeof fn !== "function") { + throw new Error(`scenario ${scenarioId} has no default export function`); + } + await fn(ctx); + return { status: "pass", failure: undefined }; + } catch (err) { + const e = err as Error; + const isAssertion = e?.name === "AssertionError"; + return { + status: "fail", + failure: { + name: e?.name ?? "Error", + message: e?.message ?? String(err), + stack: e?.stack, + source: isAssertion ? "assertion" : "scenarioThrow", + }, + }; + } +} + +function promoteCliMockFailure(internals: ScenarioInternals): ScenarioFailure | undefined { + const { events } = internals.peekEvents(); + for (const e of events) { + if (e.kind !== "cliMock") continue; + const err = e.call.handlerError; + if (!err) continue; + return { name: err.name, message: err.message, stack: err.stack, source: "cliMock" }; + } + return; +} + +/** + * Best-effort: pair each agent tool call that shelled out to a mocked CLI + * with the corresponding cliMock event (matched in order of occurrence) and + * attach the real ts as `inferredStartedAt`. Does NOT change the call's + * `startedAt` or the event's `ts` — purely informational. + */ +function pairAgentCallsWithCliMocks(agentCalls: AgentToolCall[], events: ReportEvent[]): void { + const cliMockQueues = new Map(); + for (const e of events) { + if (e.kind !== "cliMock") continue; + const q = cliMockQueues.get(e.call.cli) ?? []; + q.push(e.ts); + cliMockQueues.set(e.call.cli, q); + } + for (const call of agentCalls) { + const cli = leadingCli(call.input); + if (!cli) continue; + const q = cliMockQueues.get(cli); + if (!q || q.length === 0) continue; + call.inferredStartedAt = q.shift(); + } +} + +function leadingCli(input: unknown): string | undefined { + if (!input || typeof input !== "object") return; + const cmd = (input as { command?: unknown }).command; + if (typeof cmd !== "string") return; + const stripped = cmd.replace(/^(?:\s*cd\s+[^&;|]+(?:&&|;|\|\|)\s*)+/, ""); + const m = stripped.match(/^\s*(?:[A-Za-z_][A-Za-z0-9_]*=\S+\s+)*([A-Za-z0-9_./-]+)/); + if (!m) return; + return m[1].split("/").pop(); +} + +/** + * Merge live events with parsed agent tool calls, sort by ts, re-assign seq. + * agentToolCall entries carry a synthetic end-of-turn ts (gateway log has no + * per-tool ts); tie-break them before sibling live events so the tool call + * appears before its observed side effects. + */ +function mergeTimeline(events: ReportEvent[], agentCalls: AgentToolCall[]): ReportEvent[] { + const agentEvents: AgentToolCallEvent[] = agentCalls.map((call) => ({ + seq: -1, + ts: call.startedAt, + kind: "agentToolCall", + call, + })); + const merged: ReportEvent[] = [...events, ...agentEvents].sort((a, b) => { + if (a.ts !== b.ts) return a.ts < b.ts ? -1 : 1; + return kindOrder(a.kind) - kindOrder(b.kind); + }); + for (let i = 0; i < merged.length; ++i) merged[i].seq = i; + return merged; +} + +function kindOrder(k: ReportEvent["kind"]): number { + return k === "agentToolCall" ? 0 : 1; +} + +function writeReportArtifacts( + outDir: string, + status: "pass" | "fail", + report: ScenarioReport, +): string { + writeFileSync(join(outDir, "report.json"), JSON.stringify(report, null, 2)); + const renamed = `${outDir}-${status.toUpperCase()}`; + try { + renameSync(outDir, renamed); + return renamed; + } catch (err) { + console.warn(`runner: failed to rename ${outDir} -> ${renamed}:`, err); + return outDir; + } +} + +function closeStream(stream: ReturnType): Promise { + return new Promise((resolve, reject) => { + stream.end((err?: Error | null) => (err ? reject(err) : resolve())); + }); +} + +function discoverScenarios(): string[] { + return readdirSync(SCENARIOS_ROOT) + .filter((f) => f.endsWith(".ts")) + .map((f) => f.slice(0, -".ts".length)) + .sort(); +} + +function shortRand(): string { + return randomBytes(3).toString("hex"); +} + +function printSummary(aggregates: PairAggregate[], baseStamp: string): void { + console.log(""); + console.log("Summary:"); + for (const a of aggregates) { + const passed = a.passCount === a.runCount && a.stoppedAfter === undefined; + const verdict = (passed ? "PASS" : "FAIL").padEnd(4, " "); + const counts = `${a.passCount}/${a.runCount}`.padStart(7, " "); + const trailer = + a.stoppedAfter !== undefined ? ` (stopped after ${a.stoppedAfter} failures)` : ""; + console.log( + ` ${verdict} ${a.channel.padEnd(12, " ")} ${a.scenarioId.padEnd(40, " ")} ${counts} in ${a.durationSumMs}ms${trailer}`, + ); + } + console.log(""); + console.log("Artifacts:"); + console.log(` ${join(ARTIFACTS_ROOT, baseStamp)}`); +} + +function printTotalCost(results: TaskResult[]): void { + const judgeCost = results + .flatMap((r) => r.judgeUsages) + .reduce((sum, u) => sum + judgeCostUsd(u), 0); + const gatewayCost = results.reduce((sum, r) => sum + r.gatewayCostUsd, 0); + const gatewayTurns = results.reduce((sum, r) => sum + r.gatewayTurns, 0); + const totalCost = gatewayCost + judgeCost; + console.log(""); + console.log( + `Total LLM cost: $${totalCost.toFixed(4)} (gateway: $${gatewayCost.toFixed(4)} over ${gatewayTurns} turns, judge: $${judgeCost.toFixed(4)})`, + ); +} + +function anyPairFailed(aggregates: PairAggregate[]): boolean { + return aggregates.some((a) => a.passCount !== a.runCount || a.stoppedAfter !== undefined); +} + +if (import.meta.url === `file://${process.argv[1]}`) { + main().catch((err) => { + console.error("runner crash:", err); + process.exit(1); + }); +} diff --git a/packages/openclaw-qa-runner/templates/.env.local.example b/packages/openclaw-qa-runner/templates/.env.local.example new file mode 100644 index 0000000..2c47569 --- /dev/null +++ b/packages/openclaw-qa-runner/templates/.env.local.example @@ -0,0 +1,5 @@ +ANTHROPIC_API_KEY= + +# Opt-in gateway logs. Uncomment to enable. Writes to .gateway-logs/. +# OPENCLAW_ANTHROPIC_PAYLOAD_LOG=1 +# OPENCLAW_RAW_STREAM=1 diff --git a/packages/openclaw-qa-runner/templates/docker-compose.yml b/packages/openclaw-qa-runner/templates/docker-compose.yml new file mode 100644 index 0000000..e0096eb --- /dev/null +++ b/packages/openclaw-qa-runner/templates/docker-compose.yml @@ -0,0 +1,11 @@ +# Thin consumer overlay. Pulls in the parameterized base from +# @paleo/openclaw-qa-runner. Requires Docker Compose v2.20+. +# +# Provide these env vars (export them or list them in .env.local): +# OPENCLAW_WORKSPACE_DIR, OPENCLAW_CONFIG_PATH, PROJECTS_DIR, +# SCENARIOS_DIR, ARTIFACTS_DIR, GATEWAY_LOGS_DIR, +# QA_RUNNER_PACKAGE_DIR (resolved by the consumer's `qa` / `env-up` scripts), +# QA_PROJECT_DIR (build context), CLAW_UID, CLAW_GID, ANTHROPIC_API_KEY. + +include: + - ./node_modules/@paleo/openclaw-qa-runner/docker-compose.yml diff --git a/packages/openclaw-qa-runner/templates/openclaw.json b/packages/openclaw-qa-runner/templates/openclaw.json new file mode 100644 index 0000000..2dba19d --- /dev/null +++ b/packages/openclaw-qa-runner/templates/openclaw.json @@ -0,0 +1,41 @@ +{ + "gateway": { + "mode": "local" + }, + "plugins": { + "load": { + "paths": [ + "/opt/qa-src/node_modules/@paleo/openclaw-discord-mock", + "/opt/qa-src/node_modules/@paleo/openclaw-slack-mock" + ] + }, + "entries": { + "discord-mock": { "enabled": true }, + "slack-mock": { "enabled": true } + } + }, + "agents": { + "list": [ + { + "id": "main", + "name": "OpenClaw Main", + "model": "", + "workspace": "/home/claw/.openclaw/workspace" + } + ] + }, + "channels": { + "discord-mock": { + "baseUrl": "http://bus:43123", + "botUserId": "openclaw", + "botDisplayName": "OpenClaw QA", + "allowFrom": ["*"] + }, + "slack-mock": { + "baseUrl": "http://bus:43123", + "botUserId": "openclaw", + "botDisplayName": "OpenClaw QA", + "allowFrom": ["*"] + } + } +} diff --git a/packages/openclaw-qa-runner/tsconfig.build.json b/packages/openclaw-qa-runner/tsconfig.build.json new file mode 100644 index 0000000..f1e54d4 --- /dev/null +++ b/packages/openclaw-qa-runner/tsconfig.build.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2023", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "outDir": "dist", + "rootDir": "src", + "declaration": true, + "strict": true, + "skipLibCheck": true, + "types": ["node"] + }, + "include": ["src"] +} diff --git a/packages/openclaw-qa-runner/tsconfig.json b/packages/openclaw-qa-runner/tsconfig.json new file mode 100644 index 0000000..1051c54 --- /dev/null +++ b/packages/openclaw-qa-runner/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.build.json", + "compilerOptions": { + "rootDir": ".", + "noEmit": true + }, + "include": ["src"] +} diff --git a/packages/openclaw-slack-mock/CHANGELOG.md b/packages/openclaw-slack-mock/CHANGELOG.md new file mode 100644 index 0000000..8264beb --- /dev/null +++ b/packages/openclaw-slack-mock/CHANGELOG.md @@ -0,0 +1,20 @@ +# @paleo/openclaw-slack-mock + +## 0.2.1 + +### Patch Changes + +- Hardened openclaw qa toolkit +- Updated dependencies + - @paleo/openclaw-channel-mock-core@0.2.1 + +## 0.2.0 + +### Minor Changes + +- Initial version + +### Patch Changes + +- Updated dependencies + - @paleo/openclaw-channel-mock-core@0.2.0 diff --git a/packages/openclaw-slack-mock/LICENSE b/packages/openclaw-slack-mock/LICENSE new file mode 100644 index 0000000..91b9589 --- /dev/null +++ b/packages/openclaw-slack-mock/LICENSE @@ -0,0 +1,24 @@ +MIT License + +Copyright (c) 2026 Paleo + +Portions adapted from OpenClaw (https://github.com/steipete/openclaw), +Copyright (c) 2025 Peter Steinberger. Licensed under the MIT License. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/openclaw-slack-mock/NOTICE.md b/packages/openclaw-slack-mock/NOTICE.md new file mode 100644 index 0000000..f03f8a4 --- /dev/null +++ b/packages/openclaw-slack-mock/NOTICE.md @@ -0,0 +1,16 @@ +# NOTICE — upstream attribution + +Thin Slack-shaped wrapper around `@paleo/openclaw-channel-mock-core`. The plugin shape (manifest + entry points) follows upstream [OpenClaw](https://github.com/steipete/openclaw) `extensions/slack/` (MIT, Copyright 2025 Peter Steinberger). + +## Shape borrowed from `openclaw/extensions/slack/` + +- `channel-plugin-api.ts` → `src/channel-plugin-api.ts` +- `setup-plugin-api.ts` → `src/channel-setup-plugin.ts` +- `setup-entry.ts` → `src/setup-entry.ts` +- `index.ts` / `channel-entry.ts` → `src/index.ts` +- `runtime-api.ts` → `src/runtime.ts` +- `openclaw.plugin.json` → `openclaw.plugin.json` + +The Slack-specific transport implementation is **not** copied; this package delegates to the channel-mock core with `surface: "slack"` and `autoThread: true`. + +See [`LICENSE`](LICENSE) for the combined MIT license text (Paleo + upstream OpenClaw attribution). diff --git a/packages/openclaw-slack-mock/README.md b/packages/openclaw-slack-mock/README.md new file mode 100644 index 0000000..b962804 --- /dev/null +++ b/packages/openclaw-slack-mock/README.md @@ -0,0 +1,11 @@ +# @paleo/openclaw-slack-mock + +Synthetic Slack-shaped OpenClaw channel plugin. Registers as channel `slack-mock` with a restricted action surface (`read`, `edit`, `delete`, `react`, `reactions`, `search`). No `send` / `thread-create` / `thread-reply`. Bare-channel inbounds auto-thread: the first agent outbound creates a thread anchored on the inbound message id; every subsequent outbound from that turn lands in the same thread. + +Backed by [`@paleo/openclaw-channel-mock-core`](../openclaw-channel-mock-core/) with `surface: "slack"` and `autoThread: true`. Pair with [`@paleo/openclaw-qa-runner`](../openclaw-qa-runner/) for the QA harness. + +`Provider` / `Surface` / `OriginatingChannel` on inbound metadata are claimed as `slack-mock` so the SDK routes tool-schema discovery to this plugin. + +## Attribution + +Adapted from upstream OpenClaw's `extensions/slack/` plugin shape (manifest + entry points only). See [`NOTICE.md`](NOTICE.md). diff --git a/packages/openclaw-slack-mock/openclaw.plugin.json b/packages/openclaw-slack-mock/openclaw.plugin.json new file mode 100644 index 0000000..2f41d54 --- /dev/null +++ b/packages/openclaw-slack-mock/openclaw.plugin.json @@ -0,0 +1,6 @@ +{ + "id": "slack-mock", + "activation": { "onStartup": false }, + "channels": ["slack-mock"], + "configSchema": { "type": "object", "additionalProperties": false, "properties": {} } +} diff --git a/packages/openclaw-slack-mock/package.json b/packages/openclaw-slack-mock/package.json new file mode 100644 index 0000000..18fd110 --- /dev/null +++ b/packages/openclaw-slack-mock/package.json @@ -0,0 +1,76 @@ +{ + "name": "@paleo/openclaw-slack-mock", + "version": "0.2.1", + "description": "Synthetic Slack-shaped OpenClaw channel plugin for automated QA scenarios.", + "keywords": [ + "openclaw", + "qa", + "testing", + "slack", + "channel", + "mock" + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/paleo/alignfirst.git", + "directory": "packages/openclaw-slack-mock" + }, + "engines": { + "node": ">=22.11.0" + }, + "packageManager": "npm@11.11.0", + "type": "module", + "main": "./dist/index.js", + "files": [ + "dist/", + "openclaw.plugin.json", + "LICENSE", + "NOTICE.md" + ], + "publishConfig": { + "access": "public" + }, + "openclaw": { + "extensions": [ + "./dist/index.js" + ], + "setupEntry": "./dist/setup-entry.js", + "channel": { + "id": "slack-mock", + "label": "Slack Mock", + "selectionLabel": "Slack Mock (Synthetic)", + "detailLabel": "Slack Mock", + "docsPath": "/channels/slack-mock", + "docsLabel": "slack-mock", + "blurb": "Synthetic Slack-shaped transport for automated OpenClaw QA scenarios.", + "systemImage": "checklist", + "order": 997, + "exposure": { + "configured": false, + "setup": false, + "docs": false + } + } + }, + "scripts": { + "build": "tsc -p tsconfig.build.json", + "clear": "rimraf dist/*", + "lint": "biome check", + "test": "vitest run", + "prepublishOnly": "npm run clear && npm run build && npm run lint && npm run test" + }, + "peerDependencies": { + "openclaw": "*" + }, + "dependencies": { + "@paleo/openclaw-channel-mock-core": "0.2.1" + }, + "devDependencies": { + "@types/node": "~24.12.4", + "openclaw": "2026.5.18", + "rimraf": "~6.1.3", + "typescript": "~6.0.3", + "vitest": "~4.1.6" + } +} diff --git a/packages/openclaw-slack-mock/src/channel-plugin-api.ts b/packages/openclaw-slack-mock/src/channel-plugin-api.ts new file mode 100644 index 0000000..b377b83 --- /dev/null +++ b/packages/openclaw-slack-mock/src/channel-plugin-api.ts @@ -0,0 +1 @@ +export { slackMockPlugin } from "./plugin.js"; diff --git a/packages/openclaw-slack-mock/src/channel-setup-plugin.ts b/packages/openclaw-slack-mock/src/channel-setup-plugin.ts new file mode 100644 index 0000000..95cdafb --- /dev/null +++ b/packages/openclaw-slack-mock/src/channel-setup-plugin.ts @@ -0,0 +1,6 @@ +import { createChannelMockSetupPlugin } from "@paleo/openclaw-channel-mock-core"; + +export const slackMockSetupPlugin = createChannelMockSetupPlugin({ + channelId: "slack-mock", + label: "Slack Mock", +}); diff --git a/packages/openclaw-slack-mock/src/index.ts b/packages/openclaw-slack-mock/src/index.ts new file mode 100644 index 0000000..a9a1c8f --- /dev/null +++ b/packages/openclaw-slack-mock/src/index.ts @@ -0,0 +1,16 @@ +import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract"; + +export default defineBundledChannelEntry({ + id: "slack-mock", + name: "Slack Mock", + description: "Synthetic Slack-shaped QA channel plugin", + importMetaUrl: import.meta.url, + plugin: { + specifier: "./channel-plugin-api.js", + exportName: "slackMockPlugin", + }, + runtime: { + specifier: "./runtime.js", + exportName: "setSlackMockRuntime", + }, +}); diff --git a/packages/openclaw-slack-mock/src/plugin.ts b/packages/openclaw-slack-mock/src/plugin.ts new file mode 100644 index 0000000..758da37 --- /dev/null +++ b/packages/openclaw-slack-mock/src/plugin.ts @@ -0,0 +1,10 @@ +import { createChannelMockPlugin } from "@paleo/openclaw-channel-mock-core"; +import { getSlackMockRuntime } from "./runtime.js"; + +export const slackMockPlugin = createChannelMockPlugin({ + channelId: "slack-mock", + label: "Slack Mock", + surface: "slack", + autoThread: true, + getRuntime: getSlackMockRuntime, +}); diff --git a/packages/openclaw-slack-mock/src/runtime.ts b/packages/openclaw-slack-mock/src/runtime.ts new file mode 100644 index 0000000..a223b5e --- /dev/null +++ b/packages/openclaw-slack-mock/src/runtime.ts @@ -0,0 +1,5 @@ +import { createChannelMockRuntimeStore } from "@paleo/openclaw-channel-mock-core"; + +const store = createChannelMockRuntimeStore("slack-mock"); +export const setSlackMockRuntime = store.setRuntime; +export const getSlackMockRuntime = store.getRuntime; diff --git a/packages/openclaw-slack-mock/src/setup-entry.ts b/packages/openclaw-slack-mock/src/setup-entry.ts new file mode 100644 index 0000000..9b09307 --- /dev/null +++ b/packages/openclaw-slack-mock/src/setup-entry.ts @@ -0,0 +1,13 @@ +import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract"; + +export default defineBundledChannelSetupEntry({ + importMetaUrl: import.meta.url, + plugin: { + specifier: "./channel-setup-plugin.js", + exportName: "slackMockSetupPlugin", + }, + runtime: { + specifier: "./runtime.js", + exportName: "setSlackMockRuntime", + }, +}); diff --git a/packages/openclaw-slack-mock/test/inbound.test.ts b/packages/openclaw-slack-mock/test/inbound.test.ts new file mode 100644 index 0000000..610e978 --- /dev/null +++ b/packages/openclaw-slack-mock/test/inbound.test.ts @@ -0,0 +1,115 @@ +import { createServer, type Server } from "node:http"; +import { + buildDeliveryCallback, + createBus, + type QaBusMessage, + type ResolvedChannelMockAccount, +} from "@paleo/openclaw-channel-mock-core"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; + +type BusFixture = { server: Server; baseUrl: string; bus: ReturnType }; + +async function startBus(): Promise { + const bus = createBus(); + const server = createServer(async (req, res) => { + const handled = await bus.handler(req, res); + if (!handled) { + res.statusCode = 404; + res.end("not found"); + } + }); + await new Promise((resolve) => server.listen(0, "127.0.0.1", () => resolve())); + const address = server.address(); + if (!address || typeof address === "string") throw new Error("failed to bind test bus"); + return { server, baseUrl: `http://127.0.0.1:${address.port}`, bus }; +} + +async function stopBus(fixture: BusFixture) { + await new Promise((resolve, reject) => { + fixture.server.close((error) => (error ? reject(error) : resolve())); + fixture.server.closeAllConnections?.(); + }); +} + +function makeAccount(baseUrl: string): ResolvedChannelMockAccount { + return { + accountId: "default", + enabled: true, + configured: true, + baseUrl, + botUserId: "openclaw", + botDisplayName: "OpenClaw QA", + pollTimeoutMs: 1000, + config: { + baseUrl, + botUserId: "openclaw", + botDisplayName: "OpenClaw QA", + allowFrom: ["*"], + }, + }; +} + +function makeInbound(opts: { threadId?: string }): QaBusMessage { + return { + id: "msg-in-1", + accountId: "default", + direction: "inbound", + conversation: { kind: "channel", id: "sample-project", title: "sample-project" }, + threadId: opts.threadId, + senderId: "paleo6571", + senderName: "paleo6571", + text: "We have some work to do on sample-project.", + timestamp: Date.now(), + reactions: [], + }; +} + +describe("slack-mock buildDeliveryCallback (autoThread=true)", () => { + let fixture: BusFixture; + beforeEach(async () => { + fixture = await startBus(); + }); + afterEach(async () => { + await stopBus(fixture); + }); + + it("bare-channel inbound auto-threads and routes both outbounds into one thread", async () => { + const inbound = makeInbound({ threadId: undefined }); + const deliver = buildDeliveryCallback({ + account: makeAccount(fixture.baseUrl), + inbound, + target: "channel:sample-project", + toolCalls: [], + autoThread: true, + }); + + await deliver({ text: "starter announcement" }); + await deliver({ text: "follow-up question?" }); + + const snap = fixture.bus.state.getSnapshot(); + expect(snap.threads.length).toBe(1); + const threadId = snap.threads[0].id; + const messagesInThread = snap.messages.filter((m) => m.threadId === threadId); + expect(messagesInThread.length).toBe(2); + expect(messagesInThread[0].replyToId).toBe(inbound.id); + expect(messagesInThread[1].replyToId).toBe(undefined); + }); + + it("thread inbound routes outbounds back into the existing thread", async () => { + const inbound = makeInbound({ threadId: "T-existing" }); + const deliver = buildDeliveryCallback({ + account: makeAccount(fixture.baseUrl), + inbound, + target: "thread:sample-project/T-existing", + toolCalls: [], + autoThread: true, + }); + + await deliver({ text: "thread reply" }); + + const snap = fixture.bus.state.getSnapshot(); + expect(snap.threads.length).toBe(0); + expect(snap.messages.length).toBe(1); + expect(snap.messages[0].threadId).toBe("T-existing"); + }); +}); diff --git a/packages/openclaw-slack-mock/test/plugin-actions.test.ts b/packages/openclaw-slack-mock/test/plugin-actions.test.ts new file mode 100644 index 0000000..1152f9c --- /dev/null +++ b/packages/openclaw-slack-mock/test/plugin-actions.test.ts @@ -0,0 +1,67 @@ +import { + createChannelMockAccountHelpers, + createChannelMockMessageActions, +} from "@paleo/openclaw-channel-mock-core"; +import { describe, expect, it } from "vitest"; + +const CHANNEL_ID = "slack-mock"; +const helpers = createChannelMockAccountHelpers({ channelId: CHANNEL_ID }); +const actions = createChannelMockMessageActions({ + channelId: CHANNEL_ID, + surface: "slack", + helpers, +}); + +if (!actions.handleAction || !actions.describeMessageTool) { + throw new Error("slack-mock actions missing handleAction/describeMessageTool"); +} +const handleAction: NonNullable = actions.handleAction; +const describeMessageTool: NonNullable = + actions.describeMessageTool; + +const cfg = { + channels: { + [CHANNEL_ID]: { + baseUrl: "http://bus", + botUserId: "openclaw", + botDisplayName: "OpenClaw QA", + allowFrom: ["*"], + }, + }, +}; + +describe("slack-mock action surface", () => { + it("describeMessageTool exposes only read/edit/delete/react/reactions/search", () => { + const desc = describeMessageTool({ + cfg: cfg as unknown as Parameters[0]["cfg"], + accountId: "default", + } as unknown as Parameters[0]); + if (!desc) throw new Error("describeMessageTool returned no descriptor"); + const set = new Set(desc.actions); + for (const wanted of ["read", "edit", "delete", "react", "reactions", "search"]) { + expect(set.has(wanted as never)).toBe(true); + } + for (const forbidden of ["send", "thread-create", "thread-reply"]) { + expect(set.has(forbidden as never)).toBe(false); + } + }); + + it("rejects send / thread-create / thread-reply", async () => { + const run = (action: string, params: Record) => + handleAction({ + action, + cfg: cfg as unknown as Parameters[0]["cfg"], + accountId: "default", + params, + } as unknown as Parameters[0]); + await expect(run("send", { to: "sample-project", text: "x" })).rejects.toThrow( + /does not expose action/, + ); + await expect(run("thread-create", { to: "sample-project", title: "x" })).rejects.toThrow( + /does not expose action/, + ); + await expect( + run("thread-reply", { to: "sample-project", threadId: "T", text: "x" }), + ).rejects.toThrow(/does not expose action/); + }); +}); diff --git a/packages/openclaw-slack-mock/tsconfig.build.json b/packages/openclaw-slack-mock/tsconfig.build.json new file mode 100644 index 0000000..9b949b7 --- /dev/null +++ b/packages/openclaw-slack-mock/tsconfig.build.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2023", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "outDir": "dist", + "rootDir": "src", + "declaration": false, + "strict": true, + "skipLibCheck": true, + "types": ["node"] + }, + "include": ["src"] +} diff --git a/packages/openclaw-slack-mock/tsconfig.json b/packages/openclaw-slack-mock/tsconfig.json new file mode 100644 index 0000000..e7861fc --- /dev/null +++ b/packages/openclaw-slack-mock/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.build.json", + "compilerOptions": { + "rootDir": ".", + "noEmit": true + }, + "include": ["src", "test"] +} diff --git a/packages/worktree-env/CHANGELOG.md b/packages/worktree-env/CHANGELOG.md new file mode 100644 index 0000000..66553f0 --- /dev/null +++ b/packages/worktree-env/CHANGELOG.md @@ -0,0 +1,7 @@ +# @paleo/worktree-env + +## 0.10.2 + +### Patch Changes + +- First version in changelog diff --git a/packages/worktree-env/package.json b/packages/worktree-env/package.json index a0692f8..4b63b5e 100644 --- a/packages/worktree-env/package.json +++ b/packages/worktree-env/package.json @@ -1,6 +1,6 @@ { "name": "@paleo/worktree-env", - "version": "0.10.1", + "version": "0.10.2", "description": "Worktree-based concurrent local environment kernel.", "keywords": [ "worktree", @@ -30,16 +30,19 @@ "files": [ "dist" ], + "publishConfig": { + "access": "public" + }, "scripts": { "build": "tsc -p tsconfig.build.json", "clear": "rimraf dist/*", "lint": "biome check", "test": "vitest run", "test:watch": "vitest", - "prepublishOnly": "npm i && npm run clear && npm run build && npm run lint && npm run test" + "prepublishOnly": "npm run clear && npm run build && npm run lint && npm run test" }, "devDependencies": { - "@types/node": "~24.12.3", + "@types/node": "~24.12.4", "rimraf": "~6.1.3", "typescript": "~6.0.3", "vitest": "~4.1.6" diff --git a/packages/worktree-env/tsconfig.build.json b/packages/worktree-env/tsconfig.build.json index fef656c..f1e54d4 100644 --- a/packages/worktree-env/tsconfig.build.json +++ b/packages/worktree-env/tsconfig.build.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "ES2022", + "target": "ES2023", "module": "NodeNext", "moduleResolution": "NodeNext", "outDir": "dist",