From 75c06b270438db7ed060930330d1ac4f6997d3ba Mon Sep 17 00:00:00 2001 From: alex-w-99 Date: Mon, 12 Oct 2026 02:50:26 +0000 Subject: [PATCH 1/4] Support OpenCode V2 and update patched development dependency --- .env.example | 2 + .github/workflows/canary.yml | 15 + .github/workflows/tests.yml | 26 +- CHANGELOG.md | 7 + README.md | 74 +- install.sh | 32 +- package-lock.json | 3844 +++++++++++++++++++-- package.json | 11 +- scripts/smoke-loader.sh | 7 +- scripts/smoke-v2.mjs | 463 +++ skills/inkbox-contact-lookup/SKILL.md | 4 +- skills/inkbox-credential-use/SKILL.md | 4 +- skills/inkbox-email-triage/SKILL.md | 4 +- skills/inkbox-identity-access/SKILL.md | 4 +- skills/inkbox-imessage-responder/SKILL.md | 4 +- skills/inkbox-notes-memory/SKILL.md | 4 +- skills/inkbox-outbound-calling/SKILL.md | 4 +- skills/inkbox-outreach-sequence/SKILL.md | 4 +- skills/inkbox-sms-responder/SKILL.md | 4 +- skills/inkbox-troubleshooting/SKILL.md | 3 +- src/cli/autostart.ts | 6 +- src/cli/doctor.ts | 39 +- src/cli/index.ts | 6 +- src/cli/run.ts | 5 +- src/cli/serve.ts | 17 +- src/cli/setup.ts | 20 +- src/cli/v2-server.ts | 60 + src/cli/wrappers.ts | 104 + src/gateway/commands.ts | 2 +- src/gateway/escalation.ts | 2 +- src/gateway/events.ts | 2 +- src/gateway/index.ts | 5 +- src/gateway/permission-inventory.ts | 3 +- src/gateway/sessions.ts | 26 +- src/gateway/types.ts | 2 +- src/host/gateway-owner.ts | 47 + src/host/state.ts | 37 + src/host/types.ts | 24 + src/host/v2-approval.ts | 92 + src/host/v2-client.ts | 252 ++ src/host/v2-connection.ts | 57 + src/host/v2-history.ts | 106 + src/host/v2-rpc.ts | 46 + src/imessage-native.ts | 2 +- src/slack-upload.ts | 2 +- src/tools/types.ts | 2 +- src/v2.ts | 204 ++ tests/cli/autostart.test.ts | 10 + tests/cli/setup.test.ts | 15 +- tests/host-v2.test.ts | 308 ++ 50 files changed, 5731 insertions(+), 292 deletions(-) create mode 100644 scripts/smoke-v2.mjs create mode 100644 src/cli/v2-server.ts create mode 100644 src/cli/wrappers.ts create mode 100644 src/host/gateway-owner.ts create mode 100644 src/host/state.ts create mode 100644 src/host/types.ts create mode 100644 src/host/v2-approval.ts create mode 100644 src/host/v2-client.ts create mode 100644 src/host/v2-connection.ts create mode 100644 src/host/v2-history.ts create mode 100644 src/host/v2-rpc.ts create mode 100644 src/v2.ts create mode 100644 tests/host-v2.test.ts diff --git a/.env.example b/.env.example index cab2845..190e4fe 100644 --- a/.env.example +++ b/.env.example @@ -36,6 +36,8 @@ INKBOX_SIGNING_KEY=whsec_xxxxxxxxxxxx # INKBOX_GATEWAY_AGENT=inkbox-channel # opencode agent for gateway sessions # INKBOX_GATEWAY_MODEL=openai/gpt-5.4-mini # "provider/model" override # OPENCODE_SERVER_URL=http://127.0.0.1:4096 # attach to an existing server instead +# INKBOX_OPENCODE_SERVER_PASSWORD= # explicit V2 server authentication +# INKBOX_OPENCODE_SERVER_USERNAME=opencode # INKBOX_GATEWAY_SERVE_PORT=4097 # port for the managed `opencode serve` # INKBOX_OPENCODE_BIN=opencode # binary for the managed server # INKBOX_TEXT_BATCH_WINDOW_MS=0 # batch rapid-fire texts into one turn diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml index c61b765..bf79bf9 100644 --- a/.github/workflows/canary.yml +++ b/.github/workflows/canary.yml @@ -48,3 +48,18 @@ jobs: run: bash "$GITHUB_WORKSPACE/tests/ci/npm_with_retry.sh" install -g opencode-ai@latest - name: Packed native loader on latest host run: bash scripts/smoke-loader.sh + + loader-v2: + runs-on: blacksmith-2vcpu-ubuntu-2404 + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 + with: + node-version: 22 + cache: npm + - run: bash tests/ci/npm_with_retry.sh ci + - run: bash tests/ci/npm_with_retry.sh install -g @opencode/cli@latest + - run: npm run build + - run: | + ARCHIVE=$(npm pack --silent | tail -1) + node scripts/smoke-v2.mjs "$ARCHIVE" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f8ad994..e186eb3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -69,7 +69,27 @@ jobs: run: | test "$(opencode --version)" = "1.15.0" bash scripts/smoke-loader.sh - - name: Install latest native host - run: bash "$GITHUB_WORKSPACE/tests/ci/npm_with_retry.sh" install -g opencode-ai@latest - - name: Packed native loader on latest host + - name: Install recent V1 host + run: bash "$GITHUB_WORKSPACE/tests/ci/npm_with_retry.sh" install -g opencode-ai@1.19.0 + - name: Packed native loader on recent V1 host run: bash scripts/smoke-loader.sh + + loader-v2: + runs-on: blacksmith-2vcpu-ubuntu-2404 + strategy: + fail-fast: false + matrix: + version: ["2.0.26", "latest"] + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 + with: + node-version: 22 + cache: npm + - run: bash tests/ci/npm_with_retry.sh ci + - run: bash tests/ci/npm_with_retry.sh install -g @opencode/cli@${{ matrix.version }} + - run: npm run build + - name: Packed V2 host contract + run: | + ARCHIVE=$(npm pack --silent | tail -1) + node scripts/smoke-v2.mjs "$ARCHIVE" diff --git a/CHANGELOG.md b/CHANGELOG.md index 91553fa..e84128b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 0.2.18 (unreleased) + +- Adds an OpenCode V2 entrypoint while retaining the existing V1 entrypoint and minimum supported version. +- Adds V2 gateway sessions, shared-service connections, and native tool approvals. +- Updates generated wrappers for the installed OpenCode version and preserves custom wrappers for manual migration. +- Updates the development source-map dependency to its patched release. + ## 0.2.17 (unreleased) - Delivers actual local Slack attachments with exact native tool/source ownership and durable unknown-outcome protection. diff --git a/README.md b/README.md index ea50175..ca349ab 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ ## Prerequisites +- **Supported hosts:** OpenCode 1.15.0–1.x or 2.0.26–2.0.x. Verified on 1.15.0, 1.18.35, 1.19.0, and 2.0.26. V1 and V2 use separate plugin entrypoints; the installer selects the installed host version. - **opencode installed and logged in.** The plugin loads into real opencode sessions and the gateway drives an `opencode serve` — install it ([opencode.ai](https://opencode.ai)) and authenticate a model provider. @@ -25,7 +26,7 @@ ```bash curl -fsSL https://opencode.ai/install | bash ``` -- **Node.js 20+ and git.** The installer builds the plugin from source. +- **Node.js 20+ for V1, Node.js 22+ for the V2 gateway, and git.** The installer builds the plugin from source. - **macOS or Linux.** Boot persistence uses a systemd user unit on Linux and a launchd agent on macOS. - **An Inkbox agent** — nothing to set up in advance; the setup wizard @@ -55,6 +56,35 @@ run `./install.sh` instead; it uses the checkout in place. Rerun the wizard anytime with `inkbox-opencode setup` (`setup --print` for the static checklist), and check the wiring with `inkbox-opencode doctor`. +### Upgrading to OpenCode V2 + +Rerun `./install.sh --no-setup` from the checkout after upgrading OpenCode. +The installer backs up and updates its generated wrapper. Custom wrappers and +their options are preserved; migrate them using the V2 example below. Check +project-local wrappers too, then run `inkbox-opencode doctor`. + +V2 uses OpenCode's authenticated shared service. Configure the same Inkbox +identity and channel settings for the service and gateway. Tool selection and +outbound approval options remain in the plugin wrapper. Environment variables loaded +by the gateway do not change an already-running service's environment. Update +the service's launch environment or use the shared `~/.inkbox/config` credential +file, and restart OpenCode when configuration changes require it. + +For an explicitly selected V2 server, set `OPENCODE_SERVER_URL` and +`INKBOX_OPENCODE_SERVER_PASSWORD` in both the plugin and gateway environments. +`INKBOX_OPENCODE_SERVER_USERNAME` defaults to `opencode`. These settings also +apply to standalone V2 servers. Stopping the gateway leaves an attached shared +service running. + +The gateway and plugin must share the same accessible `INKBOX_OPENCODE_HOME` +directory (default `~/.inkbox-opencode`), including when using an explicit server. + +Finish outstanding conversations and stop the gateway before switching host versions. If doctor or +startup reports unfinished work from another host, reconnect that host and +resolve the work first. Rerun the installer after switching back to V1 as well. +If using boot autostart, rerun `inkbox-opencode autostart install` after changing +host versions to update the service definition. + ### Bootstrap an existing identity without prompts An agent can complete setup non-interactively after a human assigns it an @@ -123,20 +153,40 @@ local wrapper: ```bash git clone https://github.com/inkbox-ai/opencode-plugin.git ~/.inkbox-opencode/app cd ~/.inkbox-opencode/app -npm install && npm run build && npm pack +npm install && npm run build ``` -Then, from your opencode project (or your global `~/.config/opencode` -directory), install the packed build and add a one-file plugin wrapper: +Then, from your opencode project, install the packed build and add a one-file +plugin wrapper: ```bash -npm install ~/.inkbox-opencode/app/inkbox-opencode-plugin-*.tgz +archive=$(cd ~/.inkbox-opencode/app && npm pack --silent | tail -1) +npm install "$HOME/.inkbox-opencode/app/$archive" mkdir -p .opencode/plugins ``` -Create `.opencode/plugins/inkbox.ts` — opencode auto-loads every file under -`.opencode/plugins/`. **Every option shown in this README goes inside the -`InkboxPlugin(input, { ... })` call here**: +Create `.opencode/plugins/inkbox.ts`; for global installation use +`~/.config/opencode/plugins/inkbox.ts` and install the tarball from +`~/.config/opencode` instead. Configure only one wrapper or package +entry for each location. + +**OpenCode V2:** options go inside `options` in this wrapper: + +```ts +import InkboxPlugin from "@inkbox/opencode-plugin/v2"; + +export default { + ...InkboxPlugin, + setup: (ctx: Parameters[0]) => InkboxPlugin.setup({ + ...ctx, + options: { ...ctx.options, tools: { enable: ["inkbox_place_call"] } }, + }), +}; +``` + +**OpenCode V1:** options go inside the second argument below. Later examples +using `InkboxPlugin(input, { ... })` show this V1 form; on V2 place the same +option object in `options` above. ```ts import InkboxPlugin from "@inkbox/opencode-plugin"; @@ -221,7 +271,8 @@ Sends, calls, and A2A writes are gated before anything leaves: `inkbox_send_imessage`, `inkbox_forward_email`, `inkbox_place_call`, `inkbox_a2a_call`, and `inkbox_a2a_reply` request approval through opencode's native permission system. Approve once, - or persist an allow rule from the prompt. + or persist an allow rule from the prompt. OpenCode `allow` rules can approve + without a prompt; configure explicit `ask` rules when prompts are required. - **Recipient allowlist**: set `outbound.allowedRecipients` (exact email addresses / E.164 numbers) and anything else is rejected regardless of approval mode. @@ -340,8 +391,9 @@ Two ways to run it: ``` The sidecar needs an opencode server and finds one on its own: an explicit - `gateway.serverUrl` / `OPENCODE_SERVER_URL` must answer; otherwise it - attaches to a server already on `http://127.0.0.1:4096`, and failing that it + `gateway.serverUrl` / `OPENCODE_SERVER_URL` must answer. V2 discovers or starts + the shared authenticated service and leaves it running when the gateway stops. + V1 otherwise attaches to a server already on `http://127.0.0.1:4096`, and failing that it launches its own managed `opencode serve` (port `4097` by default; tune with `gateway.serve.{bin,port}` or `INKBOX_OPENCODE_BIN` / `INKBOX_GATEWAY_SERVE_PORT`). A managed server is stopped with the gateway, diff --git a/install.sh b/install.sh index 5d3b776..a140162 100755 --- a/install.sh +++ b/install.sh @@ -38,7 +38,7 @@ command -v node >/dev/null 2>&1 || die "Node.js 20+ is required." node -e 'process.exit(Number(process.versions.node.split(".")[0]) >= 20 ? 0 : 1)' || die "Node.js 20+ is required (found $(node --version))." command -v opencode >/dev/null 2>&1 || - warn "the opencode CLI is not on PATH — install it (npm install -g opencode-ai); the gateway launches \`opencode serve\` and the plugin loads into opencode sessions." + warn "the opencode CLI is not on PATH — install OpenCode from https://opencode.ai before configuring the plugin." # --- 2. source: local checkout, or clone/update the app dir ----------------- SOURCE_DIR="" @@ -82,22 +82,22 @@ if [ ! -f "$OPENCODE_CONFIG_DIR/agent/inkbox-channel.md" ]; then fi WRAPPER="$OPENCODE_CONFIG_DIR/plugins/inkbox.ts" -if [ -f "$WRAPPER" ]; then - say "Keeping your existing plugin wrapper at $WRAPPER" -else - cat > "$WRAPPER" <<'EOF' -// Inkbox plugin wrapper — written by install.sh; edit freely (never overwritten). -// Credentials resolve from env vars or ~/.inkbox/config; options: see README. -// inkbox_place_call is enabled so the agent can dial out (calls stay gated -// by outbound approval like every other send). -import InkboxPlugin from "@inkbox/opencode-plugin"; - -export default async (input: any) => InkboxPlugin(input, { - tools: { enable: ["inkbox_place_call"] }, -}); +node --input-type=module - "$SOURCE_DIR" "$WRAPPER" <<'EOF' +import { pathToFileURL } from "node:url"; +import { dirname } from "node:path"; +const [source, file] = process.argv.slice(2); +const { hostMajor, installWrapper, wrapperSource, configuredPackages } = await import(pathToFileURL(`${source}/dist/cli/wrappers.js`).href); +const configured = configuredPackages(dirname(dirname(file))); +if (configured.length) throw new Error(`Inkbox already has package entries in ${configured.join(", ")}. Migrate those entries or remove them before installing a wrapper; preserve their options.`); +const major = hostMajor(process.env.INKBOX_OPENCODE_BIN ?? "opencode"); +if (!major) throw new Error("Install a supported OpenCode version before configuring the plugin."); +const result = installWrapper(file, major); +console.log(`OpenCode V${major} wrapper: ${result} (${file})`); +if (result === "custom") { + console.log("Custom wrapper preserved. Adapt its options to this entrypoint before starting OpenCode:"); + console.log(wrapperSource(major)); +} EOF - say "Wrote plugin wrapper $WRAPPER" -fi # --- 5. launcher on PATH ------------------------------------------------------ mkdir -p "$BIN_DIR" diff --git a/package-lock.json b/package-lock.json index 21d025b..fd57801 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,19 @@ { "name": "@inkbox/opencode-plugin", - "version": "0.2.17", + "version": "0.2.18", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@inkbox/opencode-plugin", - "version": "0.2.17", + "version": "0.2.18", "license": "MIT", "dependencies": { "@inkbox/sdk": "^0.7.15", "@opencode-ai/sdk": ">=1.17.18 <1.19.0", + "@opencode/client": "2.0.26", + "@opencode/plugin": "2.0.26", + "jsonc-parser": "3.3.1", "ws": "^8.21.0", "zod": "4.1.8" }, @@ -27,7 +30,7 @@ "vitest": "^4.1.11" }, "engines": { - "opencode": ">=1.15.0 <1.19.0" + "opencode": ">=1.15.0 <2.0.0 || >=2.0.26 <2.1.0" }, "peerDependencies": { "@opencode-ai/plugin": ">=1.0.0" @@ -42,7 +45,6 @@ "version": "3.0.8", "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.8.tgz", "integrity": "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "json-schema": "^0.4.0" @@ -51,6 +53,451 @@ "node": ">=18" } }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity": { + "version": "3.1057.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.1057.0.tgz", + "integrity": "sha512-5MliYkp2u0+2arTp5fZIaxl+xmm90LEKv/VeSxhfNQW4t0fvWJrNO429/jchWQenNoDRrOGE59VfbuZUfwFujg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.974.15", + "@aws-sdk/credential-provider-node": "^3.972.47", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.5", + "@smithy/fetch-http-handler": "^5.4.5", + "@smithy/node-http-handler": "^4.7.5", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.978.2", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.978.2.tgz", + "integrity": "sha512-ViOBLuGSSyPAmkKGJ6KJXtVN7MOEp3rnI7uH+kWxnHccvo8C9cvunEAnN5/oaZH1laQfsTBw7IOnmNkWlFZl2A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.974.6", + "@aws-sdk/xml-builder": "^3.972.41", + "@aws/lambda-invoke-store": "^0.3.0", + "@smithy/core": "^3.35.2", + "@smithy/signature-v4": "^5.7.4", + "@smithy/types": "^4.19.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity": { + "version": "3.972.72", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.972.72.tgz", + "integrity": "sha512-UlY1+6rV1K7bFeMivM6qfQuVSBrkm/3fXBDzQO+3izAMZ64JAm0QQquCV+Ib/fGJgMNe02koq/Bgs1SBZTwfZw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/nested-clients": "^3.997.47", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.2", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.73", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.73.tgz", + "integrity": "sha512-PxlFZiF5IPUsPnuVSqjM9jlmRwZhL0xx40ccWfZiLQS8TRnHQpegpK1uAidatWuEqbJtbbY97A2Sezikz2AMUw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.2", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.2", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.75", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.75.tgz", + "integrity": "sha512-Q/uIDy8eAVN7JgSKDg16F/f+hmNJj/ZyLJN8AkW2ofVdDgJVh8fO/A26iZ7YsvM2/k76hsf/u2lLAtyXsK8Ziw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.2", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.2", + "@smithy/fetch-http-handler": "^5.8.0", + "@smithy/node-http-handler": "^4.12.1", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.973.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.18.tgz", + "integrity": "sha512-KoyjZ9vdjuxEQ8obAH06xKP8H0WKqGhwUZLMqAOiS7G0xgUAht4X8f0ASLC/vhNLCWGblZOD3VQHWyTROGJHLg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.2", + "@aws-sdk/credential-provider-env": "^3.972.73", + "@aws-sdk/credential-provider-http": "^3.972.75", + "@aws-sdk/credential-provider-login": "^3.972.80", + "@aws-sdk/credential-provider-process": "^3.972.73", + "@aws-sdk/credential-provider-sso": "^3.973.17", + "@aws-sdk/credential-provider-web-identity": "^3.972.79", + "@aws-sdk/nested-clients": "^3.997.47", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.2", + "@smithy/credential-provider-imds": "^4.5.2", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.80", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.80.tgz", + "integrity": "sha512-xF9A0gaO7k+1dYPFoo69OQatA9Ov0QuDZfqE8H0FLyIoOph+mlEC+I1Qo9UULupGuDfSYh+kVpfcDSxSYf/2fg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.2", + "@aws-sdk/nested-clients": "^3.997.47", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.2", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.85", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.85.tgz", + "integrity": "sha512-CrFcML/xImiSlquH4W3V6Npz3aSkPXHDVciAU1JOBN0vqjmkcz9H9up2sCHQqN76J/WrT+TJ580L8h2joksQrg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.73", + "@aws-sdk/credential-provider-http": "^3.972.75", + "@aws-sdk/credential-provider-ini": "^3.973.18", + "@aws-sdk/credential-provider-process": "^3.972.73", + "@aws-sdk/credential-provider-sso": "^3.973.17", + "@aws-sdk/credential-provider-web-identity": "^3.972.79", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.2", + "@smithy/credential-provider-imds": "^4.5.2", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.73", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.73.tgz", + "integrity": "sha512-tyNnT4aVuOEXmKKKZPExBi828vRd9heshobBsKF0bov5EORHSJc1AGsHOMRP57sDt+dIuVASUOywaixiX26t4g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.2", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.2", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.973.17", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.17.tgz", + "integrity": "sha512-gxKTWaUk2gSbBOH4m3NPRfEKcGXeDAOgHr7nNG5JktR1xZLo0R+DpMjbcleoHbB0uJREOG7hjIRE635IuBWsRQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.2", + "@aws-sdk/nested-clients": "^3.997.47", + "@aws-sdk/token-providers": "3.1149.0", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.2", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.79", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.79.tgz", + "integrity": "sha512-/jn0Gs/qm6fs6nTIA5yncHayybz42SATZ3rNxCEBYPJ9v69PLrDRgk07E9xTT2gpRcNmDXXbi27ZbuU06N0Z6Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.2", + "@aws-sdk/nested-clients": "^3.997.47", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.2", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers": { + "version": "3.1057.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.1057.0.tgz", + "integrity": "sha512-rbrEHtz11g0kxsSkYr3fx2HABNNblp4AhB2MgPvJHgYOWfJ2eBviU7Mvoaef0PW8QH6lbZDfJcnM7eKvtvz3sw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.1057.0", + "@aws-sdk/core": "^3.974.15", + "@aws-sdk/credential-provider-cognito-identity": "^3.972.38", + "@aws-sdk/credential-provider-env": "^3.972.41", + "@aws-sdk/credential-provider-http": "^3.972.43", + "@aws-sdk/credential-provider-ini": "^3.972.46", + "@aws-sdk/credential-provider-login": "^3.972.45", + "@aws-sdk/credential-provider-node": "^3.972.47", + "@aws-sdk/credential-provider-process": "^3.972.41", + "@aws-sdk/credential-provider-sso": "^3.972.45", + "@aws-sdk/credential-provider-web-identity": "^3.972.45", + "@aws-sdk/nested-clients": "^3.997.13", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.5", + "@smithy/credential-provider-imds": "^4.3.6", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.997.47", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.47.tgz", + "integrity": "sha512-bovCKvDsLocNTzaj6VrrMVJ2BGROqO0JlwcGseO67a+I9Bo+08nUJhSQfZUIx4+A4a7I4101GEJd/AYm7dQ68w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.2", + "@aws-sdk/signature-v4-multi-region": "^3.996.48", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.2", + "@smithy/fetch-http-handler": "^5.8.0", + "@smithy/node-http-handler": "^4.12.1", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.996.48", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.48.tgz", + "integrity": "sha512-cFDXfEbXaGwbnpvR/DyqfxfjxpjXVRVthQLgkwvcfDS5u3+8G9WSj1X1Ag/pTRg/QOeOno1Tq3ycEdrKsCUcOA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.974.6", + "@smithy/signature-v4": "^5.7.4", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.1149.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1149.0.tgz", + "integrity": "sha512-CAwYyA8eVk1w6h8YgLvkjIOakWPwURaUFrP7BRVgkxTFUM+O87thyKsGKJ6rVPbP8eoEV10cBWQ5YfaYl3qSVQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.2", + "@aws-sdk/nested-clients": "^3.997.47", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.2", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.974.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.6.tgz", + "integrity": "sha512-v/clNZzZnDxGyvpHMOGpJKVXFAExJzUNAAjaWGdcx8QAcXLGwTaOkw33p5SHAi0YAioK32xB3hWwOekRVfmfKg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.965.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.10.tgz", + "integrity": "sha512-ycwH6Zd2GhuSqdXX9ihbCjeGTB6xOJs+O3+Jb8/zDG9978XU80qs75dfkPJRMNKe5MvBZPuNeFpd4JZKPoUF4g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.972.41", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.41.tgz", + "integrity": "sha512-ctjVSyCMegrWfXlx6VqzSBFI6UqmQ5ZlnfMhdLIiWmhoH8UAQxSCP5N3OpG7X3k4LnS7ou74C4mt20+bfTW2aQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.3.0.tgz", + "integrity": "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@biomejs/biome": { "version": "2.5.6", "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.6.tgz", @@ -120,9 +567,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -140,9 +584,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -160,9 +601,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -180,9 +618,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -226,6 +661,96 @@ "node": ">=14.21.3" } }, + "node_modules/@effect/opentelemetry": { + "version": "4.0.0-rc.112", + "resolved": "https://registry.npmjs.org/@effect/opentelemetry/-/opentelemetry-4.0.0-rc.112.tgz", + "integrity": "sha512-OTRv1DxTHUmnakgJ6XVM8wVgF1KgZH4UXnOemwSLUwUjXO+RCikzF8oR/rlVmOGq81KtQzj1URM4M4nchlQOuQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <2.0.0", + "@opentelemetry/api-logs": ">=0.203.0 <0.300.0", + "@opentelemetry/resources": ">=2.0.0 <3.0.0", + "@opentelemetry/sdk-logs": ">=0.203.0 <0.300.0", + "@opentelemetry/sdk-metrics": ">=2.0.0 <3.0.0", + "@opentelemetry/sdk-trace-base": ">=2.0.0 <3.0.0", + "@opentelemetry/sdk-trace-node": ">=2.0.0 <3.0.0", + "@opentelemetry/sdk-trace-web": ">=2.0.0 <3.0.0", + "@opentelemetry/semantic-conventions": ">=1.33.0 <2.0.0", + "effect": "^4.0.0-rc.112" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@opentelemetry/api-logs": { + "optional": true + }, + "@opentelemetry/resources": { + "optional": true + }, + "@opentelemetry/sdk-logs": { + "optional": true + }, + "@opentelemetry/sdk-metrics": { + "optional": true + }, + "@opentelemetry/sdk-trace-base": { + "optional": true + }, + "@opentelemetry/sdk-trace-node": { + "optional": true + }, + "@opentelemetry/sdk-trace-web": { + "optional": true + } + } + }, + "node_modules/@effect/platform-node": { + "version": "4.0.0-rc.112", + "resolved": "https://registry.npmjs.org/@effect/platform-node/-/platform-node-4.0.0-rc.112.tgz", + "integrity": "sha512-/BMAcdNGQQskLmI0Zoa95KfTZkr9HV9N4NSxaSrusG6GeW6Ulp9KvZ+Rlaiw8lnOt43CXjFLdfll5/k5rxL4hQ==", + "license": "MIT", + "dependencies": { + "@effect/platform-node-shared": "^4.0.0-rc.112", + "mime": "^4.1.0", + "undici": "^8.10.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "effect": "^4.0.0-rc.112", + "redis": ">=5.0.0 <7.0.0" + } + }, + "node_modules/@effect/platform-node-shared": { + "version": "4.0.0-rc.112", + "resolved": "https://registry.npmjs.org/@effect/platform-node-shared/-/platform-node-shared-4.0.0-rc.112.tgz", + "integrity": "sha512-ttjz0xKamFN7vL8pNDYVwddJLjZvqKePc05djlz2VcdaKbLsnYbtMnL1rbOfHgEnIUSHGh7FkjaN4DM1Ov81sQ==", + "license": "MIT", + "dependencies": { + "@types/ws": "^8.18.1", + "ws": "^8.21.3" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "effect": "^4.0.0-rc.112" + } + }, + "node_modules/@effect/platform-node/node_modules/undici": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.11.2.tgz", + "integrity": "sha512-u4UB2/IrKdU6lFxumHmmo1a3fCQO5tzQllRorfoRS63txhrB7xTpSn1PftwC4qEHkOaqP95fCWW4lJzwErwzhQ==", + "license": "MIT", + "engines": { + "node": ">=22.19.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", @@ -668,6 +1193,15 @@ "node": ">=18" } }, + "node_modules/@gar/promise-retry": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@gar/promise-retry/-/promise-retry-1.0.3.tgz", + "integrity": "sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA==", + "license": "MIT", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/@inkbox/sdk": { "version": "0.7.15", "resolved": "https://registry.npmjs.org/@inkbox/sdk/-/sdk-0.7.15.tgz", @@ -683,21 +1217,55 @@ "node": ">=22" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", - "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", - "dev": true, - "license": "MIT" + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "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" + }, + "engines": { + "node": ">=12" + } }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { - "version": "3.0.4", + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz", + "integrity": "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==", + "license": "ISC" + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.4", "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.4.tgz", "integrity": "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -711,7 +1279,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -725,7 +1292,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -739,7 +1305,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -753,7 +1318,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -767,13 +1331,305 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ] }, + "node_modules/@npmcli/agent": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-4.0.2.tgz", + "integrity": "sha512-EUEuWAxnL07Sp5/iC/1X6Xj+XThUvnbei9zfRWZdEXa7lss9RTHMhAHBeg+MZ5To9s/gGaSI+UwZTPdYMvKSeg==", + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^11.2.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/arborist": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-9.4.0.tgz", + "integrity": "sha512-4Bm8hNixJG/sii1PMnag0V9i/sGOX9VRzFrUiZMSBJpGlLR38f+Btl85d07G9GL56xO0l0OZjvrGNYsDYp0xKA==", + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^5.0.0", + "@npmcli/installed-package-contents": "^4.0.0", + "@npmcli/map-workspaces": "^5.0.0", + "@npmcli/metavuln-calculator": "^9.0.2", + "@npmcli/name-from-folder": "^4.0.0", + "@npmcli/node-gyp": "^5.0.0", + "@npmcli/package-json": "^7.0.0", + "@npmcli/query": "^5.0.0", + "@npmcli/redact": "^4.0.0", + "@npmcli/run-script": "^10.0.0", + "bin-links": "^6.0.0", + "cacache": "^20.0.1", + "common-ancestor-path": "^2.0.0", + "hosted-git-info": "^9.0.0", + "json-stringify-nice": "^1.1.4", + "lru-cache": "^11.2.1", + "minimatch": "^10.0.3", + "nopt": "^9.0.0", + "npm-install-checks": "^8.0.0", + "npm-package-arg": "^13.0.0", + "npm-pick-manifest": "^11.0.1", + "npm-registry-fetch": "^19.0.0", + "pacote": "^21.0.2", + "parse-conflict-json": "^5.0.1", + "proc-log": "^6.0.0", + "proggy": "^4.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^3.0.1", + "semver": "^7.3.7", + "ssri": "^13.0.0", + "treeverse": "^3.0.0", + "walk-up-path": "^4.0.0" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/config": { + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/@npmcli/config/-/config-10.8.1.tgz", + "integrity": "sha512-MAYk9IlIGiyC0c9fnjdBSQfIFPZT0g1MfeSiD1UXTq2zJOLX55jS9/sETJHqw/7LN18JjITrhYfgCfapbmZHiQ==", + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^5.0.0", + "@npmcli/package-json": "^7.0.0", + "ci-info": "^4.0.0", + "ini": "^6.0.0", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "walk-up-path": "^4.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/fs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-5.0.0.tgz", + "integrity": "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==", + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/git": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-7.0.2.tgz", + "integrity": "sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg==", + "license": "ISC", + "dependencies": { + "@gar/promise-retry": "^1.0.0", + "@npmcli/promise-spawn": "^9.0.0", + "ini": "^6.0.0", + "lru-cache": "^11.2.1", + "npm-pick-manifest": "^11.0.1", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "which": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/git/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/@npmcli/git/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-4.0.0.tgz", + "integrity": "sha512-yNyAdkBxB72gtZ4GrwXCM0ZUedo9nIbOMKfGjt6Cu6DXf0p8y1PViZAKDC8q8kv/fufx0WTjRBdSlyrvnP7hmA==", + "license": "ISC", + "dependencies": { + "npm-bundled": "^5.0.0", + "npm-normalize-package-bin": "^5.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/map-workspaces": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-5.0.3.tgz", + "integrity": "sha512-o2grssXo1e774E5OtEwwrgoszYRh0lqkJH+Pb9r78UcqdGJRDRfhpM8DvZPjzNLLNYeD/rNbjOKM3Ss5UABROw==", + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^4.0.0", + "@npmcli/package-json": "^7.0.0", + "glob": "^13.0.0", + "minimatch": "^10.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/metavuln-calculator": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-9.0.3.tgz", + "integrity": "sha512-94GLSYhLXF2t2LAC7pDwLaM4uCARzxShyAQKsirmlNcpidH89VA4/+K1LbJmRMgz5gy65E/QBBWQdUvGLe2Frg==", + "license": "ISC", + "dependencies": { + "cacache": "^20.0.0", + "json-parse-even-better-errors": "^5.0.0", + "pacote": "^21.0.0", + "proc-log": "^6.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/name-from-folder": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-4.0.0.tgz", + "integrity": "sha512-qfrhVlOSqmKM8i6rkNdZzABj8MKEITGFAY+4teqBziksCQAOLutiAxM1wY2BKEd8KjUSpWmWCYxvXr0y4VTlPg==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-5.0.0.tgz", + "integrity": "sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/package-json": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-7.0.5.tgz", + "integrity": "sha512-iVuTlG3ORq2iaVa1IWUxAO/jIp77tUKBhoMjuzYW2kL4MLN1bi/ofqkZ7D7OOwh8coAx1/S2ge0rMdGv8sLSOQ==", + "license": "ISC", + "dependencies": { + "@npmcli/git": "^7.0.0", + "glob": "^13.0.0", + "hosted-git-info": "^9.0.0", + "json-parse-even-better-errors": "^5.0.0", + "proc-log": "^6.0.0", + "semver": "^7.5.3", + "spdx-expression-parse": "^4.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz", + "integrity": "sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q==", + "license": "ISC", + "dependencies": { + "which": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/query": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/query/-/query-5.0.0.tgz", + "integrity": "sha512-8TZWfTQOsODpLqo9SVhVjHovmKXNpevHU0gO9e+y4V4fRIOneiXy0u0sMP9LmS71XivrEWfZWg50ReH4WRT4aQ==", + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/redact": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-4.0.0.tgz", + "integrity": "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-10.0.4.tgz", + "integrity": "sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg==", + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^5.0.0", + "@npmcli/package-json": "^7.0.0", + "@npmcli/promise-spawn": "^9.0.0", + "node-gyp": "^12.1.0", + "proc-log": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/@opencode-ai/plugin": { "version": "1.17.18", "resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-1.17.18.tgz", @@ -803,6 +1659,35 @@ } } }, + "node_modules/@opencode-ai/plugin/node_modules/effect": { + "version": "4.0.0-beta.83", + "resolved": "https://registry.npmjs.org/effect/-/effect-4.0.0-beta.83.tgz", + "integrity": "sha512-0wsak8RtgGAr9UWSbVDgJHZcUqMSvicHcvaZv1MbMM7MCGgW4Rn/137J1MHQbwYPcwYGxT/IqehFd+UbYuj78w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "fast-check": "^4.8.0", + "find-my-way-ts": "^0.1.6", + "ini": "^7.0.0", + "kubernetes-types": "^1.30.0", + "msgpackr": "^2.0.1", + "multipasta": "^0.2.7", + "toml": "^4.1.1", + "uuid": "^14.0.0", + "yaml": "^2.9.0" + } + }, + "node_modules/@opencode-ai/plugin/node_modules/ini": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-7.0.0.tgz", + "integrity": "sha512-ifK0CgjALofS5bkrcTy4RaQ9Vx2Knf/eLeIO+NaswQEpH1UblrtTSCIvN71qQDMq0PeQ/SSPojvEJp9vvvfr+w==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" + } + }, "node_modules/@opencode-ai/sdk": { "version": "1.17.18", "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.17.18.tgz", @@ -812,99 +1697,417 @@ "cross-spawn": "7.0.6" } }, - "node_modules/@peculiar/asn1-cms": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.8.0.tgz", - "integrity": "sha512-NgekZOrSJFSBFLFoLfwePguAWAx7z1+f2TEsWFUMyiqqfntZ4+S/S5hzqME3q4pCA0iOsFKdwiQ35dwY24eVqA==", + "node_modules/@opencode/ai": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/@opencode/ai/-/ai-2.0.26.tgz", + "integrity": "sha512-CB+k3Ed6mtat2Or/6YZiXSPrI8q02NfECEKKO9HNeYLKFG3UrbIxrhnaRLbA9YMQp9mKOA1qWIWXJDnkxdVjMQ==", "license": "MIT", "dependencies": { - "@peculiar/asn1-schema": "^2.8.0", - "@peculiar/asn1-x509": "^2.8.0", - "@peculiar/asn1-x509-attr": "^2.8.0", - "asn1js": "^3.0.10", - "tslib": "^2.8.1" + "@aws-sdk/credential-providers": "3.1057.0", + "@opencode/schema": "2.0.26", + "@smithy/eventstream-codec": "4.2.14", + "@smithy/util-utf8": "4.2.2", + "aws4fetch": "1.0.20", + "effect": "4.0.0-rc.112", + "google-auth-library": "10.5.0" } }, - "node_modules/@peculiar/asn1-csr": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.8.0.tgz", - "integrity": "sha512-akbF8+uvleHs8sejNPQxwmVFuInAg6FMNHOwMILXfP518YfFJwdR3jr6oNUPOaEJfuEhn/vkNOCIT6ASUd4mbg==", + "node_modules/@opencode/client": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/@opencode/client/-/client-2.0.26.tgz", + "integrity": "sha512-0uqhXq6DJYjas3mc4roTyWAmhJpyO2gJaMcenoiA22RXJSOSIwPFho5K4qGmYDJGd5Fvvswh++Nr/wfjaHAYyg==", "license": "MIT", "dependencies": { - "@peculiar/asn1-schema": "^2.8.0", - "@peculiar/asn1-x509": "^2.8.0", - "asn1js": "^3.0.10", - "tslib": "^2.8.1" + "@opencode/protocol": "2.0.26", + "@opencode/schema": "2.0.26" + }, + "peerDependencies": { + "effect": "4.0.0-rc.112", + "solid-js": ">=1.9.0" + }, + "peerDependenciesMeta": { + "effect": { + "optional": true + }, + "solid-js": { + "optional": true + } } }, - "node_modules/@peculiar/asn1-ecc": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.8.0.tgz", - "integrity": "sha512-ohwlk+u9Rv2NOAY1c6MfHj45ATVF8R1DUN/WCgABiRtLi2ZftlZWZX7KvpAbU8v9xPcmoILfELeEABj/rn18AQ==", + "node_modules/@opencode/plugin": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/@opencode/plugin/-/plugin-2.0.26.tgz", + "integrity": "sha512-8fu/WokR5hxGjclLQJKL0PAgsMEfh81MHnUBsJuhcnYUiTJ2ZQPLqSYAhzoYEQu/Vj4M65D3Xx7QS0Q9b9Z4fw==", "license": "MIT", "dependencies": { - "@peculiar/asn1-schema": "^2.8.0", - "@peculiar/asn1-x509": "^2.8.0", - "asn1js": "^3.0.10", - "tslib": "^2.8.1" + "@ai-sdk/provider": "3.0.8", + "@opencode/ai": "2.0.26", + "@opencode/client": "2.0.26", + "@opencode/protocol": "2.0.26", + "@opencode/schema": "2.0.26", + "@opencode/util": "2.0.26", + "effect": "4.0.0-rc.112" + }, + "peerDependencies": { + "@opencode/theme": "2.0.26", + "@opentui/core": ">=0.5.17", + "@opentui/solid": ">=0.5.17", + "solid-js": ">=1.9.0" + }, + "peerDependenciesMeta": { + "@opencode/theme": { + "optional": true + }, + "@opentui/core": { + "optional": true + }, + "@opentui/solid": { + "optional": true + }, + "solid-js": { + "optional": true + } } }, - "node_modules/@peculiar/asn1-pfx": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.8.0.tgz", - "integrity": "sha512-5yof1ytoB++RQtaFbqSUJ8pxDJtZT6vbVqZ8XoJ61ph7UjNVvfFwAilnCodqkNsAodpy13gDhoxZXw00pghnyg==", + "node_modules/@opencode/protocol": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/@opencode/protocol/-/protocol-2.0.26.tgz", + "integrity": "sha512-Q+kbu0zTneh0RC4jhiuB19WeP8et9dWXGJVjb5EbUdJ8E1j5Eh0lAyqLXlM2tzTxfMfK3gos9ss6mxMVsvDFIg==", "license": "MIT", "dependencies": { - "@peculiar/asn1-cms": "^2.8.0", - "@peculiar/asn1-pkcs8": "^2.8.0", - "@peculiar/asn1-rsa": "^2.8.0", - "@peculiar/asn1-schema": "^2.8.0", - "asn1js": "^3.0.10", - "tslib": "^2.8.1" + "@opencode/schema": "2.0.26", + "effect": "4.0.0-rc.112" } }, - "node_modules/@peculiar/asn1-pkcs8": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.8.0.tgz", - "integrity": "sha512-qAKXtLpBEw9LqhKpjw3ajZSXlBur+ipW+y2ivVBQAG6F6qRx94yO+1ZR4mvw+YaCfKSaOzLeYEzsPaBp4SJELA==", + "node_modules/@opencode/schema": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/@opencode/schema/-/schema-2.0.26.tgz", + "integrity": "sha512-DZ09xXCTkTI3Y9VzuQHsm5AMGh67EqkUM13lC0LGKvD12RAYaVmRX5lDv5AnD3OkmUmPPWLYWjicT18CDloy6Q==", "license": "MIT", "dependencies": { - "@peculiar/asn1-schema": "^2.8.0", - "@peculiar/asn1-x509": "^2.8.0", - "asn1js": "^3.0.10", - "tslib": "^2.8.1" + "@standard-schema/spec": "1.1.0", + "effect": "4.0.0-rc.112" } }, - "node_modules/@peculiar/asn1-pkcs9": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.8.0.tgz", - "integrity": "sha512-b5nDWCnkV60+cQ141D6sVVwK9nz64R5n3zSVnklGd+ECdkW2Ol3U1a6yYFlalpSOaD557yuJB64A+q42jG7lUQ==", + "node_modules/@opencode/util": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/@opencode/util/-/util-2.0.26.tgz", + "integrity": "sha512-RebqfdFEEby7IFA7agajYvCpeW4VoaPkKu/ZMhBXsEYRnpz2NMyoI7zapFvvDJdYl4B0/kPnmt5vUVuqRWm0lQ==", "license": "MIT", "dependencies": { - "@peculiar/asn1-cms": "^2.8.0", - "@peculiar/asn1-pfx": "^2.8.0", - "@peculiar/asn1-pkcs8": "^2.8.0", - "@peculiar/asn1-schema": "^2.8.0", - "@peculiar/asn1-x509": "^2.8.0", - "@peculiar/asn1-x509-attr": "^2.8.0", - "asn1js": "^3.0.10", - "tslib": "^2.8.1" + "@effect/opentelemetry": "4.0.0-rc.112", + "@effect/platform-node": "4.0.0-rc.112", + "@effect/platform-node-shared": "4.0.0-rc.112", + "@npmcli/arborist": "9.4.0", + "@npmcli/config": "10.8.1", + "@opentelemetry/api": "1.9.0", + "@opentelemetry/api-logs": "0.219.0", + "@opentelemetry/context-async-hooks": "2.8.0", + "@opentelemetry/exporter-trace-otlp-http": "0.219.0", + "@opentelemetry/resources": "2.8.0", + "@opentelemetry/sdk-logs": "0.219.0", + "@opentelemetry/sdk-metrics": "2.8.0", + "@opentelemetry/sdk-trace-base": "2.8.0", + "@opentelemetry/sdk-trace-node": "2.8.0", + "cross-spawn": "7.0.6", + "effect": "4.0.0-rc.112", + "glob": "13.0.5", + "mime-types": "3.0.2", + "minimatch": "10.2.5", + "npm-package-arg": "13.0.2", + "open": "11.0.4", + "pacote": "21.5.1" } }, - "node_modules/@peculiar/asn1-rsa": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.8.0.tgz", - "integrity": "sha512-zHEUlCqB2mk7x2lxDwHHJy7hWZOPdGHVlsmITWKB5/PbQo61atbu9PJ/0r9dQNMwFzbKPXZ8uK8/91eUhRznSg==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.8.0", - "@peculiar/asn1-x509": "^2.8.0", - "asn1js": "^3.0.10", - "tslib": "^2.8.1" + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" } }, - "node_modules/@peculiar/asn1-schema": { - "version": "2.8.0", + "node_modules/@opentelemetry/api-logs": { + "version": "0.219.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.219.0.tgz", + "integrity": "sha512-FFx7YnaYJlIjqWW/AG/yAZ0L/NEY724PipXXXQLdtZPbLwBGbUMTGL1i/esI56TWfTUXxhLfpgrnWJCG8aUJyg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.8.0.tgz", + "integrity": "sha512-/3FIraneMcng67SUJCxvyInk/oxzwsxyadufk0wwfOBLf5wqtAGX4MoQASwSbndBPeARzBryUM9Azr5kHIdWLw==", + "license": "Apache-2.0", + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.8.0.tgz", + "integrity": "sha512-hd1Lfh8p545nNz+jq1Ejfz+Mn1hyLuxYn1YzTfFNrxr8urEWMNQLPf1Th8kjOH+HxwawCrtgBp8JpBUR4ZSgww==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.219.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.219.0.tgz", + "integrity": "sha512-9t6SvBXXBEjOBcIzgozvBbd3jWrv3Gt3ngGhl1fhdZ/zRc7oZDVOFEqbi2zlBpW9BXhgDMKv422J0DL/3iQWfw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.8.0", + "@opentelemetry/otlp-exporter-base": "0.219.0", + "@opentelemetry/otlp-transformer": "0.219.0", + "@opentelemetry/resources": "2.8.0", + "@opentelemetry/sdk-trace-base": "2.8.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.219.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.219.0.tgz", + "integrity": "sha512-zvIxQX/AZUVKDU+hCuYx+7UkiP7GRdnk1ZbFQRYzHvYp47cAWR4j3IhoPhV9KaeXEv2xdGq3IA6PnpzDmLcmSA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.8.0", + "@opentelemetry/otlp-transformer": "0.219.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.219.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.219.0.tgz", + "integrity": "sha512-aaYKAyXhw9VchKZVGOopD3Gw/kPsyrX2c6IQ0AW32mTjqmZOh5Y6Gf5OYqTNqVktAeBjmFinhyFaCwW6GYK9YQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.219.0", + "@opentelemetry/core": "2.8.0", + "@opentelemetry/resources": "2.8.0", + "@opentelemetry/sdk-logs": "0.219.0", + "@opentelemetry/sdk-metrics": "2.8.0", + "@opentelemetry/sdk-trace-base": "2.8.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.8.0.tgz", + "integrity": "sha512-qmXQ27ilDbUK/vGMqwL8D4/rhn76C+sherM4wTbjlfknR8Nvfc/hCxjRJPhkzZzUsPiNg16SA31NxMabwttRjg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.8.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.219.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.219.0.tgz", + "integrity": "sha512-s6lTKRakaPClvKoWHRChxnXjDMkM/TQ30ff78jN6EBGf7MI7VzANE5PU3f4z9qDUudWjvZjOLHG0rBnBKYvoXA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.219.0", + "@opentelemetry/core": "2.8.0", + "@opentelemetry/resources": "2.8.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.8.0.tgz", + "integrity": "sha512-UDBGaj6W0Rgy5rTTaoxs8gVGF/aGkAKyjurJv7se6wjRxJu7FoquTLT/vt54DZfo4crbprYfhX/SOK9+BPw1qg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.8.0", + "@opentelemetry/resources": "2.8.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.8.0.tgz", + "integrity": "sha512-mhU4jp+vW0mGbFRd+GeXHvmfA4aDqWjBjLC3pE5XMpLs0IE2ryYb019Ts2AQrOq67gaTF25D91+fgvEHDZEnuQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.8.0", + "@opentelemetry/resources": "2.8.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.8.0.tgz", + "integrity": "sha512-nZt9OGufioAc3AfoLTqA9bsAeaMJAictYDdI2VcNQ+PmT+3rfKjAZDZvgPfd8VPX0O5Bw1hdQF6kDK8VSpZiWg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/context-async-hooks": "2.8.0", + "@opentelemetry/core": "2.8.0", + "@opentelemetry/sdk-trace-base": "2.8.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.43.0.tgz", + "integrity": "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@peculiar/asn1-cms": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.8.0.tgz", + "integrity": "sha512-NgekZOrSJFSBFLFoLfwePguAWAx7z1+f2TEsWFUMyiqqfntZ4+S/S5hzqME3q4pCA0iOsFKdwiQ35dwY24eVqA==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.8.0", + "@peculiar/asn1-x509": "^2.8.0", + "@peculiar/asn1-x509-attr": "^2.8.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-csr": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.8.0.tgz", + "integrity": "sha512-akbF8+uvleHs8sejNPQxwmVFuInAg6FMNHOwMILXfP518YfFJwdR3jr6oNUPOaEJfuEhn/vkNOCIT6ASUd4mbg==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.8.0", + "@peculiar/asn1-x509": "^2.8.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-ecc": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.8.0.tgz", + "integrity": "sha512-ohwlk+u9Rv2NOAY1c6MfHj45ATVF8R1DUN/WCgABiRtLi2ZftlZWZX7KvpAbU8v9xPcmoILfELeEABj/rn18AQ==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.8.0", + "@peculiar/asn1-x509": "^2.8.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pfx": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.8.0.tgz", + "integrity": "sha512-5yof1ytoB++RQtaFbqSUJ8pxDJtZT6vbVqZ8XoJ61ph7UjNVvfFwAilnCodqkNsAodpy13gDhoxZXw00pghnyg==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.8.0", + "@peculiar/asn1-pkcs8": "^2.8.0", + "@peculiar/asn1-rsa": "^2.8.0", + "@peculiar/asn1-schema": "^2.8.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pkcs8": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.8.0.tgz", + "integrity": "sha512-qAKXtLpBEw9LqhKpjw3ajZSXlBur+ipW+y2ivVBQAG6F6qRx94yO+1ZR4mvw+YaCfKSaOzLeYEzsPaBp4SJELA==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.8.0", + "@peculiar/asn1-x509": "^2.8.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pkcs9": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.8.0.tgz", + "integrity": "sha512-b5nDWCnkV60+cQ141D6sVVwK9nz64R5n3zSVnklGd+ECdkW2Ol3U1a6yYFlalpSOaD557yuJB64A+q42jG7lUQ==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.8.0", + "@peculiar/asn1-pfx": "^2.8.0", + "@peculiar/asn1-pkcs8": "^2.8.0", + "@peculiar/asn1-schema": "^2.8.0", + "@peculiar/asn1-x509": "^2.8.0", + "@peculiar/asn1-x509-attr": "^2.8.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-rsa": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.8.0.tgz", + "integrity": "sha512-zHEUlCqB2mk7x2lxDwHHJy7hWZOPdGHVlsmITWKB5/PbQo61atbu9PJ/0r9dQNMwFzbKPXZ8uK8/91eUhRznSg==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.8.0", + "@peculiar/asn1-x509": "^2.8.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-schema": { + "version": "2.8.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.8.0.tgz", "integrity": "sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q==", "license": "MIT", @@ -968,6 +2171,90 @@ "node": ">=20.0.0" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@redis/bloom": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-6.3.0.tgz", + "integrity": "sha512-NQ5poYpZr0jv6zazN1cy6JY5kIipKFZ3R9GTL4kOUTX3a9YAfY29U8bsE4lWhVQEM+G4V0BXVC57mubIdDijeA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 20.0.0" + }, + "peerDependencies": { + "@redis/client": "^6.3.0" + } + }, + "node_modules/@redis/client": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-6.3.0.tgz", + "integrity": "sha512-fHrjwGCBvAANGi+TuRuwKi83sIOzgHr8eh+Uf48w3fSX7Hhrz174NrWqfJhAC2vnN0d/tfaZpdr6+o6JC3QHwg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 20.0.0" + }, + "peerDependencies": { + "@node-rs/xxhash": "^1.1.0", + "@opentelemetry/api": ">=1 <2" + }, + "peerDependenciesMeta": { + "@node-rs/xxhash": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + } + } + }, + "node_modules/@redis/json": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-6.3.0.tgz", + "integrity": "sha512-yt6vRfPBXtW/3qm53cF81TSQpLMjWdW4/2E9WQ2GgbvsKkuCKCFQIcMiKlil1ZMnezK+IQuS795xKU5psbaegw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 20.0.0" + }, + "peerDependencies": { + "@redis/client": "^6.3.0" + } + }, + "node_modules/@redis/search": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-6.3.0.tgz", + "integrity": "sha512-RPXAZmKjZZG7Tm7m1w9keFdXzbL91PiSHQBfQRe9xwW40jNsTRbG0FwhIz4QlHPU4gKDAsCy8IQHj4yZl4H9rw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 20.0.0" + }, + "peerDependencies": { + "@redis/client": "^6.3.0" + } + }, + "node_modules/@redis/time-series": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-6.3.0.tgz", + "integrity": "sha512-+fJOB8mN1z5WRakCJNh5lVdz1KqvHGSOistsKeitD24QsCtPwyRQXWC0iq6T/i/ubLE/YcguZqPm8F598MKk4A==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 20.0.0" + }, + "peerDependencies": { + "@redis/client": "^6.3.0" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.62.2", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", @@ -1318,30 +2605,272 @@ "win32" ] }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/chai": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", - "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", - "dev": true, - "license": "MIT", + "node_modules/@sigstore/bundle": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-4.0.0.tgz", + "integrity": "sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==", + "license": "Apache-2.0", "dependencies": { - "@types/deep-eql": "*", - "assertion-error": "^2.0.1" + "@sigstore/protobuf-specs": "^0.5.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", - "dev": true, - "license": "MIT" + "node_modules/@sigstore/core": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-3.2.1.tgz", + "integrity": "sha512-qRsxPnCrbC/puegGxKuynfnxgLiHqWStrSjxkoB4YKqq3Z3s4cyZyj42ZdWFAEblNP65C+rBH8EuREHIXoi83g==", + "license": "Apache-2.0", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/protobuf-specs": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.5.2.tgz", + "integrity": "sha512-SQqvFMt4V78fdjcDdYX6HbiVSOR4QK3ZgwCa2KOsopAgPIHy1rU5UDUmzLl02r5oyyaYcYHR1hpwDRk/yUe+Mw==", + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@sigstore/sign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-4.1.1.tgz", + "integrity": "sha512-Hf4xglukg0XXQ2RiD5vSoLjdPe8OBUPA8XeVjUObheuDcWdYWrnH/BNmxZCzkAy68MzmNCxXLeurJvs6hcP2OQ==", + "license": "Apache-2.0", + "dependencies": { + "@gar/promise-retry": "^1.0.2", + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.2.0", + "@sigstore/protobuf-specs": "^0.5.0", + "make-fetch-happen": "^15.0.4", + "proc-log": "^6.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/tuf": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-4.0.2.tgz", + "integrity": "sha512-TCAzTy0xzdP79EnxSjq9KQ3eaR7+FmudLC6eRKknVKZbV7ZNlGLClAAQb/HMNJ5n2OBNk2GT1tEmU0xuPr+SLQ==", + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.5.0", + "tuf-js": "^4.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/verify": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-3.1.1.tgz", + "integrity": "sha512-qv7+G3J2cc6wwFj3yKvXOamzqhMwSk1ogPGmhpS8iXllcPrJaIIBA+4HbttlHVu1pqWTdmaCH/WE7UOC51kdoA==", + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.2.1", + "@sigstore/protobuf-specs": "^0.5.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.35.2", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.35.2.tgz", + "integrity": "sha512-ejTukBuP/Pg3t9N8Ux+0zKsXeHWm7p6W3HbXFOyltKasSxZwCFmvTut7kBdZSCF3m9y1bXXyVu6p2xuMFcsTyQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.5.2.tgz", + "integrity": "sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.14.tgz", + "integrity": "sha512-erZq0nOIpzfeZdCyzZjdJb4nVSKLUmSkaQUVkRGQTXs30gyUGeKnrYEg+Xe1W5gE3aReS7IgsvANwVPxSzY6Pw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.14.1", + "@smithy/util-hex-encoding": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.8.0.tgz", + "integrity": "sha512-ycSJu3tFAQ4v04CBB0agqFMVsSQ1iG3yw+SpgxRqKfaURpQD4CZ8Wn0zPMmSnOuTpTh65Vz+EA0rMrw089wvkA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.18.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.12.1.tgz", + "integrity": "sha512-ThMkboGeONWXAelq9FvGsuJC4rOi+qyC4/zhUF58xYpxUg5sQKx2VXZYJmtNjr4dSuBJ1HeJXETQILCz3wOHvw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.18.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.7.4.tgz", + "integrity": "sha512-tHy0K0VtqNd5Y7Y41h0a0Lhh0L1GzC08dTWg0F7vRJWFtTENg7IZikf3wQkanYIRdb7ngoIPMTmqgUi401fEeQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.35.0", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.19.0.tgz", + "integrity": "sha512-r7jh49VJxGerfAcTQA6gXcKc+98zOp/tqRwzYjgOE+iSQsP6cEU1hq2QzbuipmP68QtYdY9wKEhiCQZIzHgZ4Q==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.5.2.tgz", + "integrity": "sha512-nxu3SgmAw9JXT2CtkU0m/XNLWpP9MsaBx1zAGAypCbYj15tIFlmcYwpF+Oh18le83d+IM9PT7ENdXnE4C+d5mA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.5.2.tgz", + "integrity": "sha512-iq+cW3mAb7vfcxEEpYi3zXKpDtbrIFyanWjQl4zBq4seWD4OSxXDWSfespZxenX6aEaighn+NR3u1nU1DSvs3w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.2.tgz", + "integrity": "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", + "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-4.1.0.tgz", + "integrity": "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww==", + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^10.1.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" }, "node_modules/@types/estree": { "version": "1.0.9", @@ -1354,7 +2883,6 @@ "version": "22.20.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -1364,7 +2892,6 @@ "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -1483,6 +3010,48 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/abbrev": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/asn1js": { "version": "3.0.10", "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz", @@ -1507,6 +3076,126 @@ "node": ">=12" } }, + "node_modules/aws4fetch": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/aws4fetch/-/aws4fetch-1.0.20.tgz", + "integrity": "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==", + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/bin-links": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-6.0.2.tgz", + "integrity": "sha512-frE1t78WOwJ45PKV2cF2tNPjTcs9L1J9s6VkrV59wanRP4GlaomuxYPVma7BwthMg8WnfSory4w5PTE6FZZ81w==", + "license": "ISC", + "dependencies": { + "cmd-shim": "^8.0.0", + "npm-normalize-package-bin": "^5.0.0", + "proc-log": "^6.0.0", + "read-cmd-shim": "^6.0.0", + "write-file-atomic": "^7.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/bowser": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/bundle-name": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.1.tgz", + "integrity": "sha512-DdH81/zPLVS11EUgWq3tEu/xn+EzljlMYooDNdzWEnFha3R3NBMpMV1UqYIpjYHV/SgpFKMIX1Oh7o06SjM/oA==", + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cacache": { + "version": "20.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.4.tgz", + "integrity": "sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA==", + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^5.0.0", + "fs-minipass": "^3.0.0", + "glob": "^13.0.0", + "lru-cache": "^11.1.0", + "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": "^13.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/chai": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", @@ -1517,6 +3206,79 @@ "node": ">=18" } }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cmd-shim": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-8.0.0.tgz", + "integrity": "sha512-Jk/BK6NCapZ58BKUxlSI+ouKRbjH1NLZCgJkYoab+vEHUY3f6OzpNBN9u7HFSv9J6TRDGs4PLOHezoKGaFRSCA==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/common-ancestor-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-2.0.0.tgz", + "integrity": "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">= 18" + } + }, + "node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -1538,34 +3300,132 @@ "node": ">= 8" } }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, - "license": "Apache-2.0", - "optional": true, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/effect": { - "version": "4.0.0-beta.83", - "resolved": "https://registry.npmjs.org/effect/-/effect-4.0.0-beta.83.tgz", - "integrity": "sha512-0wsak8RtgGAr9UWSbVDgJHZcUqMSvicHcvaZv1MbMM7MCGgW4Rn/137J1MHQbwYPcwYGxT/IqehFd+UbYuj78w==", - "dev": true, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { - "@standard-schema/spec": "^1.1.0", - "fast-check": "^4.8.0", - "find-my-way-ts": "^0.1.6", - "ini": "^7.0.0", - "kubernetes-types": "^1.30.0", - "msgpackr": "^2.0.1", - "multipasta": "^0.2.7", - "toml": "^4.1.1", - "uuid": "^14.0.0", - "yaml": "^2.9.0" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/default-browser": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz", + "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==", + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/effect": { + "version": "4.0.0-rc.112", + "resolved": "https://registry.npmjs.org/effect/-/effect-4.0.0-rc.112.tgz", + "integrity": "sha512-wXxwuh1Ywnv4cPRM3Wfa0vDwuOHnZ1TsTgHJkG9XgzND6inhBH9n1vBxhg3iIXOia/OrpmvVmd3lrD4vq6bF3A==", + "license": "MIT", + "dependencies": { + "fast-check": "^4.9.0", + "msgpackr": "^2.0.5" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "engines": { + "node": ">=6" } }, "node_modules/es-module-lexer": { @@ -1637,11 +3497,22 @@ "node": ">=12.0.0" } }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "license": "Apache-2.0" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/fast-check": { "version": "4.9.0", "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.9.0.tgz", "integrity": "sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg==", - "dev": true, "funding": [ { "type": "individual", @@ -1664,7 +3535,6 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -1678,6 +3548,29 @@ } } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/find-my-way-ts": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/find-my-way-ts/-/find-my-way-ts-0.1.6.tgz", @@ -1685,6 +3578,46 @@ "dev": true, "license": "MIT" }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/fs-minipass": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1700,57 +3633,625 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/gaxios": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.3.1.tgz", + "integrity": "sha512-kB3rzJV7d9juLZh8/56QTXCwQfxyhdOMdyYk1HdQKFtF8TJTDTZQJtixWIwXdE9Jji91mC41DUNpjleo4L4eAQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gcp-metadata": { + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.4.tgz", + "integrity": "sha512-iJ9KMsiu+xKtNRX0PmGLSaIU3bUBAyzWTyqKemKPzNPsmmsBCQYmlNg+brEbES7IHSXtdVwzBPzx1vz3FAaipw==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "7.1.3", + "google-logging-utils": "1.1.3", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gcp-metadata/node_modules/gaxios": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz", + "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2", + "rimraf": "^5.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gcp-metadata/node_modules/google-logging-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/glob": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.5.tgz", + "integrity": "sha512-BzXxZg24Ibra1pbQ/zE7Kys4Ua1ks7Bn6pKLkVPZ9FZe4JQS6/Q7ef3LG1H+k7lUf5l4T3PLSyYyYJVYUvfgTw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.1", + "minipass": "^7.1.2", + "path-scurry": "^2.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/google-auth-library": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.5.0.tgz", + "integrity": "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.0.0", + "gcp-metadata": "^8.0.0", + "google-logging-utils": "^1.0.0", + "gtoken": "^8.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/google-logging-utils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.2.0.tgz", + "integrity": "sha512-WE9av4wKDZgRjBwgVUabocx8T6/7o3Ca1Fat46FXDhXVAFibzNadedcOXrdgd1Kzmk8tsk/9ZH89Wyf/SqeZ3A==", + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/gtoken": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz", + "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==", + "license": "MIT", + "dependencies": { + "gaxios": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/hash-wasm": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/hash-wasm/-/hash-wasm-4.12.0.tgz", "integrity": "sha512-+/2B2rYLb48I/evdOIhP+K/DD2ca2fgBjp6O+GBEnCDk2e4rpeXIK8GvIyRPjTezgmWn9gmKwkQjjx6BtqDHVQ==", "license": "MIT" }, + "node_modules/hosted-git-info": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.3.tgz", + "integrity": "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==", + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.3.0.tgz", + "integrity": "sha512-M5t5LlJpS1UHMjvwRQVdFHvPISGeLAxNcrWuJkeGh0KxsqCHZ1O3NXZU/8x7cD0BDcGW8kapxMKTvwlqrNkHkA==", + "license": "BSD-2-Clause" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ignore-walk": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-8.0.0.tgz", + "integrity": "sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A==", + "license": "ISC", + "dependencies": { + "minimatch": "^10.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/ini": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-7.0.0.tgz", - "integrity": "sha512-ifK0CgjALofS5bkrcTy4RaQ9Vx2Knf/eLeIO+NaswQEpH1UblrtTSCIvN71qQDMq0PeQ/SSPojvEJp9vvvfr+w==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", + "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/ip-address": { + "version": "10.7.4", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.4.tgz", + "integrity": "sha512-DWBMKyeM25D70nrIiTkgQth59/G+naKOTep6huF9j+GmeK3DMD60Zvxg5d5qQnz+38AHgou6ean2LECX+lrKDg==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-in-ssh": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", + "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-5.0.0.tgz", + "integrity": "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==", + "license": "MIT", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-stringify-nice": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz", + "integrity": "sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==", + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/just-diff": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/just-diff/-/just-diff-6.0.2.tgz", + "integrity": "sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA==", + "license": "MIT" + }, + "node_modules/just-diff-apply": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/just-diff-apply/-/just-diff-apply-5.5.0.tgz", + "integrity": "sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==", + "license": "MIT" + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kubernetes-types": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/kubernetes-types/-/kubernetes-types-1.30.0.tgz", + "integrity": "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q==", "dev": true, + "license": "Apache-2.0" + }, + "node_modules/lru-cache": { + "version": "11.5.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", + "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/make-fetch-happen": { + "version": "15.0.6", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.6.tgz", + "integrity": "sha512-Je0fLJ0F5atA7F+eIlLzk+Wkcl57JDf4kf+EW8xiP5E31xOQxkIxTbgf1Oi1Lw9tRI9UEMRdI5Vz2xTzoNU1Jw==", + "license": "ISC", + "dependencies": { + "@gar/promise-retry": "^1.0.0", + "@npmcli/agent": "^4.0.0", + "@npmcli/redact": "^4.0.0", + "cacache": "^20.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^5.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^6.0.0", + "ssri": "^13.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/mime": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz", + "integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==", + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-collect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-fetch": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-5.0.2.tgz", + "integrity": "sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ==", + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^2.0.0", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + }, + "optionalDependencies": { + "iconv-lite": "^0.7.2" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.7.tgz", + "integrity": "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, "engines": { - "node": "^22.22.2 || ^24.15.0 || >=26.0.0" + "node": ">=8" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "license": "ISC" }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true, - "license": "(AFL-2.1 OR BSD-3-Clause)" - }, - "node_modules/kubernetes-types": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/kubernetes-types/-/kubernetes-types-1.30.0.tgz", - "integrity": "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q==", - "dev": true, - "license": "Apache-2.0" + "node_modules/minipass-sized": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-2.0.0.tgz", + "integrity": "sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA==", + "license": "ISC", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" } }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/msgpackr": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-2.0.4.tgz", - "integrity": "sha512-o1C5KRmuRt+apqMr1HuGSqWStZoRBUpEsCsl15uM9VdAF1qHLtvMOU2En747EnTyEl6c4pzPewRMFF31s1CNbA==", - "dev": true, + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-2.1.0.tgz", + "integrity": "sha512-p/pBCVO63CsvvpkomUnNNag6+n38rULuDA6HHe70o2gtC8ODI52foF/4ko2qQcp6OiErJXTmrZeXmsGGHsIQNQ==", "license": "MIT", "optionalDependencies": { "msgpackr-extract": "^3.0.4" @@ -1760,7 +4261,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.4.tgz", "integrity": "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -1805,11 +4305,88 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/negotiator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", + "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", + "license": "MIT", + "dependencies": { + "content-type": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-gyp": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.4.0.tgz", + "integrity": "sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==", + "license": "MIT", + "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" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/node-gyp-build-optional-packages": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1821,6 +4398,149 @@ "node-gyp-build-optional-packages-test": "build-test.js" } }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/node-gyp/node_modules/undici": { + "version": "6.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.29.0.tgz", + "integrity": "sha512-R+RODBqp6i2pPflGdq+xIOUkl+RNfGgHwoinecKu/JCuf2uO06cOKoDbI2P7Dn6KcswdKwrczbU6IYJ6K8X+wg==", + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/nopt": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", + "license": "ISC", + "dependencies": { + "abbrev": "^4.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-bundled": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-5.0.0.tgz", + "integrity": "sha512-JLSpbzh6UUXIEoqPsYBvVNVmyrjVZ1fzEFbqxKkTJQkWBO3xFzFT+KDnSKQWwOQNbuWRwt5LSD6HOTLGIWzfrw==", + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^5.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-install-checks": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-8.0.0.tgz", + "integrity": "sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA==", + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-5.0.0.tgz", + "integrity": "sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-package-arg": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-13.0.2.tgz", + "integrity": "sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA==", + "license": "ISC", + "dependencies": { + "hosted-git-info": "^9.0.0", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^7.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-packlist": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.4.tgz", + "integrity": "sha512-uMW73iajD8hiH4ZBxEV3HC+eTnppIqwakjOYuvgddnalIw2lJguKviK1pcUJDlIWm1wSJkchpDZDSVVsZEYRng==", + "license": "ISC", + "dependencies": { + "ignore-walk": "^8.0.0", + "proc-log": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-pick-manifest": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz", + "integrity": "sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==", + "license": "ISC", + "dependencies": { + "npm-install-checks": "^8.0.0", + "npm-normalize-package-bin": "^5.0.0", + "npm-package-arg": "^13.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-registry-fetch": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-19.1.1.tgz", + "integrity": "sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw==", + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^4.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^15.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^5.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^13.0.0", + "proc-log": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/obug": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/obug/-/obug-2.2.1.tgz", @@ -1835,6 +4555,89 @@ "node": ">=12.20.0" } }, + "node_modules/open": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/open/-/open-11.0.4.tgz", + "integrity": "sha512-++Zlftm0kVLPmzC06t6epuWmcRMDbI4z5P3NNX979WA/k23+NtSOynEGzsVfZwguKw2mi5umVgnBlJQMwRz4Pg==", + "license": "MIT", + "dependencies": { + "default-browser": "^5.5.1", + "define-lazy-prop": "^3.0.0", + "is-in-ssh": "^1.0.0", + "is-inside-container": "^1.0.0", + "powershell-utils": "^0.2.1", + "wsl-utils": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.1.1.tgz", + "integrity": "sha512-6Lh3jQPLthYujSrlw6ccWXBGZTkoTCz27Hd92g79LH/kjAbgoEu0t442cCCNbegPSJsA0lwDX8z0bcKtkpA32g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/pacote": { + "version": "21.5.1", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-21.5.1.tgz", + "integrity": "sha512-KvcJ9iy3crysCsgqc4+PknH/w6jkrp8JN36mpZBPwNaDRwTfMZD37YzRazNstiZUOhuF5pno9f78n9mEJBavwg==", + "license": "ISC", + "dependencies": { + "@gar/promise-retry": "^1.0.0", + "@npmcli/git": "^7.0.0", + "@npmcli/installed-package-contents": "^4.0.0", + "@npmcli/package-json": "^7.0.0", + "@npmcli/promise-spawn": "^9.0.0", + "@npmcli/run-script": "^10.0.0", + "cacache": "^20.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^13.0.0", + "npm-packlist": "^10.0.1", + "npm-pick-manifest": "^11.0.1", + "npm-registry-fetch": "^19.0.0", + "proc-log": "^6.0.0", + "sigstore": "^4.0.0", + "ssri": "^13.0.0", + "tar": "^7.4.3" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/parse-conflict-json": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-5.0.1.tgz", + "integrity": "sha512-ZHEmNKMq1wyJXNwLxyHnluPfRAFSIliBvbK/UiOceROt4Xh9Pz0fq49NytIaeaCUf5VR86hwQ/34FCcNU5/LKQ==", + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^5.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -1844,6 +4647,22 @@ "node": ">=8" } }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -1862,7 +4681,6 @@ "version": "4.0.7", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -1900,11 +4718,71 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-selector-parser": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.6.tgz", + "integrity": "sha512-7qASPzhKF2l2KLboRZux8CCTRMdGiV08vWmyKzPz22qZ7ZjQBOeY7rNzNoCLSUiftJ7HUq0GERHmxw/t0dCdMw==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/powershell-utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.2.1.tgz", + "integrity": "sha512-C+y9x90UElAddDZmV4qOx9W53B61PO7cIqWz2dQsWlwswuq4mr8NEwytdGKboYbQlGZ3awrkTeNvcZiZNHnQ8A==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/proggy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/proggy/-/proggy-4.0.0.tgz", + "integrity": "sha512-MbA4R+WQT76ZBm/5JUpV9yqcJt92175+Y0Bodg3HgiXzrmKu7Ggq+bpn6y6wHH+gN9NcyKn3yg1+d47VaKwNAQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/promise-all-reject-late": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz", + "integrity": "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==", + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/promise-call-limit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/promise-call-limit/-/promise-call-limit-3.0.2.tgz", + "integrity": "sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw==", + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/pure-rand": { "version": "8.4.1", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-8.4.1.tgz", "integrity": "sha512-c58R2+SPFcSIPXoU834QN/KPDDOSd8sXcSrqf6e83Me6Rrp1EYkxukkjXMVrKvKaADs1SOyNkWdfvLf6zY8qLQ==", - "dev": true, "funding": [ { "type": "individual", @@ -1923,23 +4801,137 @@ "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", "license": "MIT", "dependencies": { - "tslib": "^2.8.1" + "tslib": "^2.8.1" + } + }, + "node_modules/pvutils": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz", + "integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==", + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/read-cmd-shim": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-6.0.0.tgz", + "integrity": "sha512-1zM5HuOfagXCBWMN83fuFI/x+T/UhZ7k+KIzhrHXcQoeX5+7gmaDYjELQHmmzIodumBHeByBJT4QYS7ufAgs7A==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/redis": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-6.3.0.tgz", + "integrity": "sha512-XFQbPie1lGpKeUZ8ySYY43yLnQy/iIQtHNvIr/1XdaABpb96rUSdm3Mfl+98FEVbv9ZJIzINMno5s0Nh8E5LEw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@redis/bloom": "6.3.0", + "@redis/client": "6.3.0", + "@redis/json": "6.3.0", + "@redis/search": "6.3.0", + "@redis/time-series": "6.3.0" + }, + "engines": { + "node": ">= 20.0.0" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0" + }, + "node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/pvutils": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz", - "integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==", - "license": "MIT", + "node_modules/rimraf/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "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" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, "engines": { - "node": ">=16.0.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/reflect-metadata": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", - "license": "Apache-2.0" + "node_modules/rimraf/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/rollup": { "version": "4.62.2", @@ -1986,6 +4978,57 @@ "fsevents": "~2.3.2" } }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT", + "optional": true + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2014,16 +5057,117 @@ "dev": true, "license": "ISC" }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sigstore": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-4.1.1.tgz", + "integrity": "sha512-endqECJkfhozrXMK5ngu/UAA0xVcVEFdnHJCElGaExypjW+HK5i6zu3NteLoaX/iFbRUbC3+DjttQs0GARr+5w==", + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.2.1", + "@sigstore/protobuf-specs": "^0.5.0", + "@sigstore/sign": "^4.1.1", + "@sigstore/tuf": "^4.0.2", + "@sigstore/verify": "^3.1.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.10.tgz", + "integrity": "sha512-e0VyvkVTwVYViNovRkZ9aodhxVlyoMn7eJhVUPxZ+eK9P/7CBkxvvsBOHqFPEH416726W8tLXXXjKwqgTErrCQ==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.1.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.24", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.24.tgz", + "integrity": "sha512-cLS9TtWkIQFyLkJ3/5aFQAOHOSKTlOs/7WDut/XPSdjom1fJhUdkepeJAKXa9Y05+FabHd0sti+Et559vDtkpQ==", + "license": "CC0-1.0" + }, + "node_modules/ssri": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.1.tgz", + "integrity": "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -2038,6 +5182,118 @@ "dev": true, "license": "MIT" }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "7.5.22", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz", + "integrity": "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -2059,7 +5315,6 @@ "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -2092,6 +5347,15 @@ "node": ">=20" } }, + "node_modules/treeverse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/treeverse/-/treeverse-3.0.0.tgz", + "integrity": "sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -2116,6 +5380,20 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "license": "0BSD" }, + "node_modules/tuf-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-4.1.0.tgz", + "integrity": "sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ==", + "license": "MIT", + "dependencies": { + "@tufjs/models": "4.1.0", + "debug": "^4.4.3", + "make-fetch-happen": "^15.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -2143,13 +5421,18 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, + "license": "MIT" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, "node_modules/uuid": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.1.tgz", - "integrity": "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==", + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.3.tgz", + "integrity": "sha512-Sc5u1VVln93y1Xnz4R+CunOZc7R9LUcTmhj7Rc7Eg3HVcf7rVn2FOoa812fe0W8p3jD7xeoGSYbfqCGGH2B6xw==", "dev": true, "funding": [ "https://github.com/sponsors/broofa", @@ -2160,6 +5443,15 @@ "uuid": "dist-node/bin/uuid" } }, + "node_modules/validate-npm-package-name": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-7.0.2.tgz", + "integrity": "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/vite": { "version": "6.4.3", "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", @@ -2325,6 +5617,24 @@ } } }, + "node_modules/walk-up-path": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-4.0.0.tgz", + "integrity": "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==", + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2357,10 +5667,113 @@ "node": ">=8" } }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/write-file-atomic": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-7.0.1.tgz", + "integrity": "sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg==", + "license": "ISC", + "dependencies": { + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/ws": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", - "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.0.tgz", + "integrity": "sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -2378,6 +5791,31 @@ } } }, + "node_modules/wsl-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-1.0.1.tgz", + "integrity": "sha512-c16PKLIRxBYpR3jSWsdS7SFp0COm5SOQdiFjY818dYFHJbMGuLBszpF07LBWmOS1uzrHa9VGdynsEgoeKy0oOQ==", + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0", + "powershell-utils": "^0.2.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/yaml": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", diff --git a/package.json b/package.json index f3c20cf..598033b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@inkbox/opencode-plugin", - "version": "0.2.17", + "version": "0.2.18", "private": true, "description": "Inkbox for opencode — give your agent an email address, a phone number (SMS/MMS + voice), iMessage, contacts, notes, and an encrypted credential vault.", "license": "MIT", @@ -8,6 +8,10 @@ "main": "./dist/index.js", "types": "./dist/index.d.ts", "exports": { + "./v2": { + "types": "./dist/v2.d.ts", + "import": "./dist/v2.js" + }, ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" @@ -52,11 +56,14 @@ "url": "https://github.com/inkbox-ai/opencode-plugin/issues" }, "engines": { - "opencode": ">=1.15.0 <1.19.0" + "opencode": ">=1.15.0 <2.0.0 || >=2.0.26 <2.1.0" }, "dependencies": { "@inkbox/sdk": "^0.7.15", "@opencode-ai/sdk": ">=1.17.18 <1.19.0", + "@opencode/client": "2.0.26", + "@opencode/plugin": "2.0.26", + "jsonc-parser": "3.3.1", "ws": "^8.21.0", "zod": "4.1.8" }, diff --git a/scripts/smoke-loader.sh b/scripts/smoke-loader.sh index 28f9f6c..1cfe4cb 100644 --- a/scripts/smoke-loader.sh +++ b/scripts/smoke-loader.sh @@ -7,6 +7,7 @@ # # Requires the `opencode` CLI on PATH. Exits non-zero on any failure. set -euo pipefail +unset OPENCODE_CONFIG_CONTENT OPENCODE_CONFIG OPENCODE_CONFIG_DIR ROOT="$(cd "$(dirname "$0")/.." && pwd)" PORT="${SMOKE_PORT:-14096}" @@ -22,7 +23,11 @@ MARKER="$WORKDIR/loaded.json" cleanup() { kill "${MOCK_PID:-}" "${SERVE_PID:-}" 2>/dev/null || true wait "${MOCK_PID:-}" "${SERVE_PID:-}" 2>/dev/null || true - rm -rf "$WORKDIR" "$SMOKE_STATE_DIR" + if [ "${KEEP_SMOKE_FILES:-}" = "1" ]; then + echo "Fixture directories: $WORKDIR $SMOKE_STATE_DIR" + else + rm -rf "$WORKDIR" "$SMOKE_STATE_DIR" + fi } trap cleanup EXIT diff --git a/scripts/smoke-v2.mjs b/scripts/smoke-v2.mjs new file mode 100644 index 0000000..69d8264 --- /dev/null +++ b/scripts/smoke-v2.mjs @@ -0,0 +1,463 @@ +import assert from "node:assert/strict"; +import { execFileSync, spawn } from "node:child_process"; +import { randomUUID } from "node:crypto"; +import { mkdirSync, mkdtempSync, openSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { createServer } from "node:http"; +import { tmpdir } from "node:os"; +import { join, resolve } from "node:path"; +import { pathToFileURL } from "node:url"; +import { OpenCode } from "@opencode/client"; +import { Service } from "@opencode/client/service"; + +const root = mkdtempSync(join(tmpdir(), "inkbox-v2-")); +const home = join(root, "home"); +const archive = resolve(process.argv[2]); +const originalEnv = { ...process.env }; +const originalCwd = process.cwd(); +const binary = process.env.OPENCODE_V2_BIN ?? "opencode"; +mkdirSync(home); +const env = { + PATH: process.env.PATH, + HOME: home, + XDG_CONFIG_HOME: join(home, "config"), + XDG_DATA_HOME: join(home, "data"), + XDG_STATE_HOME: join(home, "state"), + XDG_CACHE_HOME: join(home, "cache"), + INKBOX_OPENCODE_HOME: join(home, "inkbox"), + TMPDIR: tmpdir(), +}; +for (const key of Object.keys(process.env)) delete process.env[key]; +Object.assign(process.env, env); +process.chdir(root); +const seenRequests = []; +const model = createServer(async (req, res) => { + let raw = ""; + for await (const chunk of req) raw += chunk; + const body = JSON.parse(raw || "{}"); + seenRequests.push(body); + const lastUser = body.messages?.findLastIndex((m) => m.role === "user") ?? -1; + const text = JSON.stringify(body.messages?.[lastUser] ?? ""); + const tool = + text.includes("USE_FIXTURE") && + !body.messages.slice(lastUser + 1).some((m) => m.role === "tool"); + const delta = tool + ? { + role: "assistant", + tool_calls: [ + { + index: 0, + id: `call_${randomUUID()}`, + type: "function", + function: { + name: text.includes("DOCTOR_FIXTURE") ? "inkbox_doctor" : "fixture_echo", + arguments: JSON.stringify( + text.includes("DOCTOR_FIXTURE") + ? {} + : { + value: text.includes("TIMEOUT_FIXTURE") ? "timeout" : "Synthetic result", + }, + ), + }, + }, + ], + } + : { role: "assistant", content: "Synthetic completed" }; + const finish = tool ? "tool_calls" : "stop"; + const base = { + id: "synthetic", + created: 1, + model: body.model, + usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 }, + }; + if (!body.stream) { + res.writeHead(200, { "content-type": "application/json" }); + res.end( + JSON.stringify({ + ...base, + object: "chat.completion", + choices: [{ index: 0, message: delta, finish_reason: finish }], + }), + ); + return; + } + res.writeHead(200, { "content-type": "text/event-stream" }); + const send = (value, reason) => + res.write( + `data: ${JSON.stringify({ ...base, object: "chat.completion.chunk", choices: [{ index: 0, delta: value, finish_reason: reason }] })}\n\n`, + ); + send(delta, null); + if (text.includes("HOLD_FIXTURE")) { + const timer = setTimeout(() => { + send({}, "stop"); + res.end("data: [DONE]\n\n"); + }, 60000); + res.on("close", () => clearTimeout(timer)); + } else { + send({}, finish); + res.end("data: [DONE]\n\n"); + } +}); +await new Promise((r) => model.listen(0, "127.0.0.1", r)); +let child; +let abort; +let reader; +const until = async (predicate, label) => { + const deadline = Date.now() + 30000; + while (Date.now() < deadline) { + if (await predicate()) return; + await new Promise((r) => setTimeout(r, 50)); + } + throw new Error(`Timed out: ${label}`); +}; +try { + writeFileSync(join(root, "package.json"), '{"private":true,"type":"module"}'); + execFileSync("npm", ["install", "--ignore-scripts", "--no-audit", "--no-fund", archive], { + cwd: root, + env, + stdio: "pipe", + }); + const packed = join(root, "node_modules/@inkbox/opencode-plugin/dist"); + const load = (file) => import(pathToFileURL(join(packed, file)).href); + const { resolveConfig } = await load("config.js"); + const { createV2Host } = await load("host/v2-client.js"); + const { createV2Connection } = await load("host/v2-connection.js"); + const { readHistory } = await load("host/v2-history.js"); + const { InkboxHost } = await load("host/v2-rpc.js"); + const { createSessionManager } = await load("gateway/sessions.js"); + const { createStateStore } = await load("gateway/state.js"); + const pluginDir = join(root, ".opencode/plugins"); + mkdirSync(pluginDir, { recursive: true }); + writeFileSync( + join(pluginDir, "inkbox.ts"), + ` +import Inkbox from '@inkbox/opencode-plugin/v2'; +import { OpenCode } from '@opencode/client'; +import { Service } from '@opencode/client/service'; +import { z } from 'zod'; +import { requestApproval } from ${JSON.stringify(join(packed, "host/v2-approval.js"))}; +import { createV2Host } from ${JSON.stringify(join(packed, "host/v2-client.js"))}; +import { resolveConfig } from ${JSON.stringify(join(packed, "config.js"))}; +export default { ...Inkbox, async setup(ctx) { + const close = await Inkbox.setup(ctx); + await ctx.tool.transform(editor => editor.add({ name:'fixture_echo',description:'Synthetic echo',input:z.object({value:z.string().min(1)}),options:{codemode:false},async execute(input, context) { + const endpoint = await Service.discover(); + const client = OpenCode.make({baseUrl:endpoint.url,headers:Service.headers(endpoint)}); + const host = createV2Host({get:async()=>client,invalidate(){}},resolveConfig(ctx.options)); + const source = (await host.session.message({path:{id:context.sessionID,messageID:context.messageID}})).data; + if (!source.info.parentID || !source.parts.some(part=>part.type==='tool'&&part.callID===context.id&&part.state.input?.value===input.value)) throw new Error('Synthetic source mismatch'); + await requestApproval(client,{sessionID:context.sessionID,messageID:context.messageID,callID:context.id,agent:context.agent,permission:'fixture_send',patterns:['fixture@example.test'],always:['fixture@example.test'],metadata:{}},context.signal,input.value==='timeout'?500:10000); + return {content:input.value}; + }})); + return close; +}}; +`, + ); + writeFileSync( + join(root, "opencode.json"), + JSON.stringify({ + model: "fixture/fixture", + agents: { alternate: { mode: "primary", description: "Synthetic alternative" } }, + providers: { + fixture: { + package: "aisdk:@ai-sdk/openai-compatible", + settings: { baseURL: `http://127.0.0.1:${model.address().port}/v1`, apiKey: "synthetic" }, + models: { + fixture: { + name: "fixture", + limit: { context: 32000, output: 2000 }, + capabilities: { tools: true, input: ["text"], output: ["text"] }, + }, + }, + }, + }, + }), + ); + const registration = join(env.XDG_STATE_HOME, "opencode/service.json"); + const start = () => { + child = spawn(binary, ["serve", "--service", "--hostname", "127.0.0.1", "--port", "0"], { + cwd: root, + env, + stdio: [ + "ignore", + openSync(join(root, "serve.log"), "a"), + openSync(join(root, "serve-error.log"), "a"), + ], + }); + }; + const stop = async () => { + if (!child || child.exitCode !== null) return; + const exit = new Promise((r) => child.once("exit", r)); + child.kill("SIGTERM"); + await exit; + }; + start(); + let endpoint; + await until(async () => { + endpoint = await Service.discover({ file: registration }); + return Boolean(endpoint); + }, "service startup"); + let client = OpenCode.make({ baseUrl: endpoint.url, headers: Service.headers(endpoint) }); + const connection = createV2Connection(undefined, undefined, [ + binary, + "serve", + "--service", + "--hostname", + "127.0.0.1", + "--port", + "0", + ]); + const host = createV2Host(connection, resolveConfig({}, {})); + const permissionEvents = []; + abort = new AbortController(); + const eventStream = await host.event.subscribe({ + query: { directory: root }, + signal: abort.signal, + }); + reader = (async () => { + for await (const event of eventStream.stream) permissionEvents.push(event); + })().catch(() => {}); + const create = async (title) => { + const s = await client.session.create({ + title, + location: { directory: root }, + permissions: [{ action: "fixture_send", resource: "*", effect: "ask" }], + }); + await client.session.switchModel({ + sessionID: s.id, + model: { providerID: "fixture", id: "fixture" }, + }); + return s.id; + }; + const sessionID = await create("Synthetic lifecycle"); + await host.ready(root); + const sessions = createSessionManager({ + opencode: host, + config: resolveConfig({ gateway: { model: "fixture/fixture" } }, {}), + state: createStateStore(join(home, "gateway-fixture")), + directory: root, + inkbox: { + getIdentity: async () => ({ agentHandle: "synthetic", emailAddress: "fixture@example.test" }), + }, + logger: { info() {}, warn() {}, error() {} }, + }); + try { + assert.equal( + await sessions.runText("first", "Synthetic gateway question"), + "Synthetic completed", + ); + assert.deepEqual( + await Promise.all([ + sessions.runText("first", "Synthetic follow-up"), + sessions.runText("second", "Synthetic concurrent question"), + ]), + ["Synthetic completed", "Synthetic completed"], + ); + } finally { + await sessions.close(); + } + const info = await client.rpc(InkboxHost).hello({}, { location: { directory: root } }); + assert.ok(info.tools.includes("inkbox_send_email")); + assert.ok(!info.tools.includes("inkbox_credentials_get_secret")); + assert.ok( + (await client.plugin.list({ location: { directory: root } })).data.some( + (p) => p.id === "inkbox", + ), + ); + const prompt = async (id, text, extra = {}, session = sessionID) => + host.session.promptAsync({ + path: { id: session }, + query: { directory: root }, + body: { messageID: id, parts: [{ type: "text", text }], ...extra }, + }); + const wait = (session) => + client.session.wait({ sessionID: session }, { signal: AbortSignal.timeout(30000) }); + const messageID = `msg_${randomUUID()}`; + await prompt(messageID, "Synthetic question", { system: "FIXTURE_CONTEXT", tools: {} }); + await wait(sessionID); + assert.ok(JSON.stringify(seenRequests).includes("FIXTURE_CONTEXT")); + assert.equal(seenRequests.at(-1).tools?.length ?? 0, 0); + const normalized = (await host.session.messages({ path: { id: sessionID } })).data; + assert.equal(normalized.findLast((m) => m.info.role === "assistant").info.parentID, messageID); + const count = seenRequests.length; + await prompt(messageID, "Synthetic question", { system: "FIXTURE_CONTEXT", tools: {} }); + await wait(sessionID); + assert.equal(seenRequests.length, count); + await prompt(`msg_${randomUUID()}`, "Synthetic alternate", { agent: "alternate", tools: {} }); + await wait(sessionID); + assert.equal((await client.session.get({ sessionID })).agent, "alternate"); + await prompt(`msg_${randomUUID()}`, "Synthetic default", { tools: {} }); + await wait(sessionID); + assert.equal((await client.session.get({ sessionID })).agent, "build"); + await client.session.synthetic({ + sessionID, + text: "Synthetic background completion", + resume: true, + }); + await wait(sessionID); + assert.equal( + (await host.session.messages({ path: { id: sessionID } })).data.findLast( + (m) => m.info.role === "assistant", + ).info.parentID, + undefined, + ); + const defaults = await client.session.create({ + title: "Synthetic native defaults", + location: { directory: root }, + }); + await client.session.switchModel({ + sessionID: defaults.id, + model: { providerID: "fixture", id: "fixture" }, + }); + await prompt( + `msg_${randomUUID()}`, + "USE_FIXTURE", + { tools: { fixture_echo: true } }, + defaults.id, + ); + await wait(defaults.id); + const defaultTool = (await readHistory(client, defaults.id)) + .filter((m) => m.type === "assistant") + .flatMap((m) => m.content) + .find((p) => p.type === "tool"); + assert.equal(defaultTool.state.status, "completed"); + assert.equal((await client.permission.list({ sessionID: defaults.id })).length, 0); + for (const decision of ["once", "reject", "timeout", "cancel", "always"]) { + const sid = await create(`Synthetic approval ${decision}`); + await prompt( + `msg_${randomUUID()}`, + decision === "timeout" ? "USE_FIXTURE TIMEOUT_FIXTURE" : "USE_FIXTURE", + { tools: { fixture_echo: true } }, + sid, + ); + let request; + await until(async () => { + request = (await client.permission.list({ sessionID: sid })).find( + (p) => p.action === "fixture_send", + ); + return Boolean(request); + }, "approval request"); + assert.ok(request.source?.messageID); + const inventory = await host.permissionInventory(root, AbortSignal.timeout(5000)); + assert.ok( + inventory.some((p) => p.id === request.id && p.tool?.messageID === request.source.messageID), + ); + if (decision === "cancel") await host.session.abort({ path: { id: sid } }); + else if (decision !== "timeout") + assert.equal( + ( + await host.postSessionIdPermissionsPermissionId({ + path: { id: sid, permissionID: request.id }, + body: { response: decision }, + }) + ).data, + true, + ); + await wait(sid); + const messages = await readHistory(client, sid); + const tool = messages + .filter((m) => m.type === "assistant") + .flatMap((m) => m.content) + .find((p) => p.type === "tool"); + assert.equal(tool.state.status, ["once", "always"].includes(decision) ? "completed" : "error"); + if (["timeout", "cancel"].includes(decision)) { + await until( + async () => + !(await client.permission.list({ sessionID: sid })).some((p) => p.id === request.id), + "approval cleanup", + ); + await assert.rejects(() => + client.permission.reply({ sessionID: sid, requestID: request.id, decision: "once" }), + ); + } + } + assert.ok( + permissionEvents.some((e) => e.type === "permission.asked" && e.properties?.tool?.messageID), + ); + const sid = await create("Synthetic remembered approval"); + await prompt(`msg_${randomUUID()}`, "USE_FIXTURE", { tools: { fixture_echo: true } }, sid); + await wait(sid); + assert.equal((await client.permission.list({ sessionID: sid })).length, 0); + const held = await create("Synthetic cancellation"); + await prompt(`msg_${randomUUID()}`, "HOLD_FIXTURE", {}, held); + await until( + async () => + seenRequests.some((request) => JSON.stringify(request.messages).includes("HOLD_FIXTURE")) && + Boolean((await client.session.active())[held]), + "active generation", + ); + await client.session.prompt({ + sessionID: held, + id: `msg_${randomUUID()}`, + text: "Queued fixture", + delivery: "queue", + resume: false, + }); + assert.equal((await host.session.abort({ path: { id: held } })).data, true); + assert.equal((await client.session.inbox.list({ sessionID: held })).length, 0); + assert.equal((await client.session.active())[held], undefined); + await client.session.compact({ sessionID, delivery: "queue" }); + await wait(sessionID); + assert.ok((await readHistory(client, sessionID)).some((m) => m.id === messageID)); + await stop(); + await host.ready(root); + await until(async () => { + endpoint = await Service.discover({ file: registration }); + return Boolean(endpoint); + }, "service restart"); + client = OpenCode.make({ baseUrl: endpoint.url, headers: Service.headers(endpoint) }); + await host.ready(root); + await prompt(`msg_${randomUUID()}`, "Synthetic after restart", { tools: {} }); + await wait(sessionID); + const beforeReload = await client.rpc(InkboxHost).hello({}, { location: { directory: root } }); + const wrapper = join(pluginDir, "inkbox.ts"); + writeFileSync( + wrapper, + readFileSync(wrapper, "utf8").replace( + "Inkbox.setup(ctx)", + "Inkbox.setup({...ctx, options: {...ctx.options, gateway: {serverUrl: 'http://127.0.0.1:1'}}})", + ), + ); + await client.location.reload(); + await until(async () => { + const current = await client + .rpc(InkboxHost) + .hello({}, { location: { directory: root } }) + .catch(() => undefined); + return current && current.instance !== beforeReload.instance; + }, "plugin reload"); + const interactive = await create("Synthetic interactive request"); + await client.session.prompt({ sessionID: interactive, text: "USE_FIXTURE DOCTOR_FIXTURE" }); + await wait(interactive); + const interactiveMessages = await readHistory(client, interactive); + assert.ok( + interactiveMessages + .filter((m) => m.type === "assistant") + .flatMap((m) => m.content) + .some( + (part) => + part.type === "tool" && + part.name === "inkbox_doctor" && + part.state.status === "completed", + ), + ); + await stop(); + console.log( + "PASS: packed OpenCode V2 plugin, approvals, prompt lifecycle, compaction, cancellation, and restart", + ); +} finally { + abort?.abort(); + await reader; + await Service.stop({ file: join(env.XDG_STATE_HOME, "opencode/service.json") }); + if (child?.exitCode === null) { + const exit = new Promise((r) => child.once("exit", r)); + child.kill("SIGTERM"); + await exit; + } + model.closeAllConnections(); + model.close(); + process.chdir(originalCwd); + for (const key of Object.keys(process.env)) delete process.env[key]; + Object.assign(process.env, originalEnv); + if (process.env.KEEP_SMOKE_FILES) console.log(`Fixture directory: ${root}`); + else rmSync(root, { recursive: true, force: true }); +} diff --git a/skills/inkbox-contact-lookup/SKILL.md b/skills/inkbox-contact-lookup/SKILL.md index 49002e8..cf50a9d 100644 --- a/skills/inkbox-contact-lookup/SKILL.md +++ b/skills/inkbox-contact-lookup/SKILL.md @@ -21,8 +21,8 @@ All six are enabled by default: There is no vCard export/import tool. Contact rule tools, note access tools, and `inkbox_place_call` are opt-in — the user must enable them in your .opencode/plugins/inkbox.ts wrapper and restart opencode: ```ts -// in your .opencode/plugins/inkbox.ts wrapper: -InkboxPlugin(input, { "tools": { "enable": ["inkbox_place_call", "access", "contact-rules"] } }) +// Merge into wrapper options: V1's second argument or V2's setup options. +const options = { "tools": { "enable": ["inkbox_place_call", "access", "contact-rules"] } }; ``` Use note access and contact rule tools only when the user explicitly asks to manage note sharing or allow/block rules. `inkbox_doctor` reports which tools are enabled or disabled. diff --git a/skills/inkbox-credential-use/SKILL.md b/skills/inkbox-credential-use/SKILL.md index 941ee06..b097028 100644 --- a/skills/inkbox-credential-use/SKILL.md +++ b/skills/inkbox-credential-use/SKILL.md @@ -19,8 +19,8 @@ When an action requires plaintext credentials (a password, an API key, an SSH ke Enabling: `inkbox_credentials_list` turns on with the `vault` group, but the five plaintext-returning tools are sensitive — each must be enabled by its exact name (group enable never turns them on). After editing opencode.json, opencode must be restarted: ```ts -// in your .opencode/plugins/inkbox.ts wrapper: -InkboxPlugin(input, { "tools": { "enable": ["vault", "inkbox_credentials_get_login", "inkbox_credentials_get_api_key", "inkbox_credentials_get_ssh_key", "inkbox_totp_code"] } }) +// Merge into wrapper options: V1's second argument or V2's setup options. +const options = { "tools": { "enable": ["vault", "inkbox_credentials_get_login", "inkbox_credentials_get_api_key", "inkbox_credentials_get_ssh_key", "inkbox_totp_code"] } }; ``` `inkbox_doctor` reports which of these are currently enabled or disabled, and whether the vault key is present. diff --git a/skills/inkbox-email-triage/SKILL.md b/skills/inkbox-email-triage/SKILL.md index a379626..68da875 100644 --- a/skills/inkbox-email-triage/SKILL.md +++ b/skills/inkbox-email-triage/SKILL.md @@ -20,8 +20,8 @@ Enabled by default: Opt-in — `inkbox_mark_emails_read` (clear processed messages) and `inkbox_forward_email` (forward with original parts) must be enabled by the user in your .opencode/plugins/inkbox.ts wrapper, then opencode restarted: ```ts -// in your .opencode/plugins/inkbox.ts wrapper: -InkboxPlugin(input, { "tools": { "enable": ["inkbox_mark_emails_read", "inkbox_forward_email"] } }) +// Merge into wrapper options: V1's second argument or V2's setup options. +const options = { "tools": { "enable": ["inkbox_mark_emails_read", "inkbox_forward_email"] } }; ``` (Enabling the `"email"` group turns both on at once.) `inkbox_doctor` reports which tools are currently enabled or disabled. diff --git a/skills/inkbox-identity-access/SKILL.md b/skills/inkbox-identity-access/SKILL.md index 4d4cf63..e14cbda 100644 --- a/skills/inkbox-identity-access/SKILL.md +++ b/skills/inkbox-identity-access/SKILL.md @@ -12,8 +12,8 @@ Use this skill when explaining organization-wide contact visibility or managing All identity-access tools are opt-in. Enable the `access` group in your .opencode/plugins/inkbox.ts wrapper, then restart opencode: ```ts -// in your .opencode/plugins/inkbox.ts wrapper: -InkboxPlugin(input, { "tools": { "enable": ["access"] } }) +// Merge into wrapper options: V1's second argument or V2's setup options. +const options = { "tools": { "enable": ["access"] } }; ``` This enables `inkbox_list_note_access`, `inkbox_grant_note_access`, and `inkbox_revoke_note_access`. Run `inkbox_doctor` to confirm which tools are currently enabled. diff --git a/skills/inkbox-imessage-responder/SKILL.md b/skills/inkbox-imessage-responder/SKILL.md index 351de9c..79781e8 100644 --- a/skills/inkbox-imessage-responder/SKILL.md +++ b/skills/inkbox-imessage-responder/SKILL.md @@ -30,8 +30,8 @@ Opt-in — the user must enable these in your .opencode/plugins/inkbox.ts wrappe - `inkbox_mark_imessage_conversation_read` — send a read receipt and clear unread state ```ts -// in your .opencode/plugins/inkbox.ts wrapper: -InkboxPlugin(input, { "tools": { "enable": ["inkbox_send_imessage_reaction", "inkbox_mark_imessage_conversation_read"] } }) +// Merge into wrapper options: V1's second argument or V2's setup options. +const options = { "tools": { "enable": ["inkbox_send_imessage_reaction", "inkbox_mark_imessage_conversation_read"] } }; ``` (Enabling the `"imessage"` group turns all four on at once.) `inkbox_doctor` reports which tools are currently enabled or disabled. diff --git a/skills/inkbox-notes-memory/SKILL.md b/skills/inkbox-notes-memory/SKILL.md index 3db1e29..ce148d9 100644 --- a/skills/inkbox-notes-memory/SKILL.md +++ b/skills/inkbox-notes-memory/SKILL.md @@ -21,8 +21,8 @@ The Inkbox plugin exposes persistent notes scoped by the configured Inkbox ident These are disabled by default. The user must enable them in your .opencode/plugins/inkbox.ts wrapper (by exact tool name, or the `notes` group) and restart opencode: ```ts -// in your .opencode/plugins/inkbox.ts wrapper: -InkboxPlugin(input, { "tools": { "enable": ["inkbox_update_note", "inkbox_delete_note"] } }) +// Merge into wrapper options: V1's second argument or V2's setup options. +const options = { "tools": { "enable": ["inkbox_update_note", "inkbox_delete_note"] } }; ``` `inkbox_doctor` reports which tools are currently enabled or disabled. diff --git a/skills/inkbox-outbound-calling/SKILL.md b/skills/inkbox-outbound-calling/SKILL.md index f542559..55be333 100644 --- a/skills/inkbox-outbound-calling/SKILL.md +++ b/skills/inkbox-outbound-calling/SKILL.md @@ -14,8 +14,8 @@ Use this skill when the user asks you to call someone. It is disabled by default. The user must enable it in your .opencode/plugins/inkbox.ts wrapper (by exact name, or via the `"calls"` group) and restart opencode: ```ts -// in your .opencode/plugins/inkbox.ts wrapper: -InkboxPlugin(input, { "tools": { "enable": ["inkbox_place_call"] } }) +// Merge into wrapper options: V1's second argument or V2's setup options. +const options = { "tools": { "enable": ["inkbox_place_call"] } }; ``` `inkbox_doctor` reports whether it is currently enabled. diff --git a/skills/inkbox-outreach-sequence/SKILL.md b/skills/inkbox-outreach-sequence/SKILL.md index 5990ac5..578a43e 100644 --- a/skills/inkbox-outreach-sequence/SKILL.md +++ b/skills/inkbox-outreach-sequence/SKILL.md @@ -21,8 +21,8 @@ Use this skill whenever the user asks for **multi-step outbound communication** - `inkbox_forward_email` — escalate to a third party if no reply. Disabled by default; the user enables it in your .opencode/plugins/inkbox.ts wrapper and restarts opencode: ```ts -// in your .opencode/plugins/inkbox.ts wrapper: -InkboxPlugin(input, { "tools": { "enable": ["inkbox_forward_email"] } }) +// Merge into wrapper options: V1's second argument or V2's setup options. +const options = { "tools": { "enable": ["inkbox_forward_email"] } }; ``` If unsure what is currently enabled, run `inkbox_doctor`. diff --git a/skills/inkbox-sms-responder/SKILL.md b/skills/inkbox-sms-responder/SKILL.md index c9b2b77..a111dcb 100644 --- a/skills/inkbox-sms-responder/SKILL.md +++ b/skills/inkbox-sms-responder/SKILL.md @@ -21,8 +21,8 @@ Opt-in — the user must enable these in your .opencode/plugins/inkbox.ts wrappe - `inkbox_mark_text_read`, `inkbox_mark_text_conversation_read` — clear unread state ```ts -// in your .opencode/plugins/inkbox.ts wrapper: -InkboxPlugin(input, { "tools": { "enable": ["inkbox_mark_text_conversation_read"] } }) +// Merge into wrapper options: V1's second argument or V2's setup options. +const options = { "tools": { "enable": ["inkbox_mark_text_conversation_read"] } }; ``` (Enabling the `"sms"` group turns all four on at once.) `inkbox_doctor` reports which tools are currently enabled or disabled. diff --git a/skills/inkbox-troubleshooting/SKILL.md b/skills/inkbox-troubleshooting/SKILL.md index 6dbd59a..82500e0 100644 --- a/skills/inkbox-troubleshooting/SKILL.md +++ b/skills/inkbox-troubleshooting/SKILL.md @@ -14,7 +14,8 @@ Start with `inkbox_doctor` (default-enabled). It reports config presence, API re | Error | Fix | |---|---| | `Inkbox plugin is not configured` | Set `INKBOX_API_KEY` + `INKBOX_IDENTITY` in the environment launching opencode, or pass `apiKey`/`identity` as plugin options in your .opencode/plugins/inkbox.ts wrapper (`~/.inkbox/config` is also read, at lowest precedence). Restart opencode after changes. | -| Tool not found / not available | The tool is opt-in and not enabled. Add its exact name (or its group) to `tools.enable` in your `.opencode/plugins/inkbox.ts` wrapper, then restart: `InkboxPlugin(input, { tools: { enable: ["inkbox_..."] } })`. Check `inkbox_doctor` for the enabled/disabled list. | +| Tool not found / not available | Add its exact name (or group) to the wrapper's `tools.enable` options. V1 uses the second argument to `InkboxPlugin(input, options)`; V2 imports `@inkbox/opencode-plugin/v2` and passes options through `setup(ctx)`. Preserve other options. Check `inkbox_doctor` for the enabled/disabled list. | +| Plugin fails after a host upgrade | Rerun the installer to update generated wrappers, or migrate custom wrappers using the README. Run `inkbox-opencode doctor` to identify incompatible project wrappers. V2's shared service needs the same Inkbox configuration as the gateway. | | `whoami failed: 401 Unauthorized` | API key is wrong, revoked, or has a typo. Ask the user to mint or paste a fresh key. | | `API key is not agent-scoped` | Outbound may work, but access-scoped reads (contacts, notes, vault) can behave differently. Prefer an agent-scoped key bound to the configured identity. | | `404` on identity resolution | The configured `identity` handle does not exist under this key or does not match the key's bound identity. | diff --git a/src/cli/autostart.ts b/src/cli/autostart.ts index 3aa6197..2f7ee50 100644 --- a/src/cli/autostart.ts +++ b/src/cli/autostart.ts @@ -4,10 +4,11 @@ import * as os from "node:os"; import * as path from "node:path"; import { gatewayHome } from "../gateway/state.js"; import { daemonPaths, pidAlive, readPidFile, stopDaemon } from "./daemon.js"; +import { resolveOpencodeBin } from "./serve.js"; +import { hostMajor } from "./wrappers.js"; // Boot/login autostart for the gateway: a systemd user unit on Linux, a -// launchd agent on macOS. The service runs `inkbox-opencode run`, which owns -// its opencode server, so one service is the whole always-on stack. +// launchd agent on macOS. The service runs `inkbox-opencode run`. export const SERVICE_NAME = "inkbox-opencode"; export const LAUNCHD_LABEL = "ai.inkbox.opencode"; @@ -157,6 +158,7 @@ async function installSystemd(c: Ctx): Promise { "", "[Service]", "Type=simple", + `KillMode=${hostMajor(resolveOpencodeBin(c.env.INKBOX_OPENCODE_BIN ?? "opencode", c.env, c.execPath)) === 2 ? "process" : "control-group"}`, `Environment=INKBOX_OPENCODE_ENV_FILE=${envFile}`, `WorkingDirectory=${c.projectDirectory}`, `ExecStart="${c.execPath}" "${c.entry}" run`, diff --git a/src/cli/doctor.ts b/src/cli/doctor.ts index 15756d7..d68c920 100644 --- a/src/cli/doctor.ts +++ b/src/cli/doctor.ts @@ -1,12 +1,14 @@ -import { createOpencodeClient, type OpencodeClient } from "@opencode-ai/sdk"; +import { createOpencodeClient } from "@opencode-ai/sdk"; import { createInkboxRuntime, type InkboxRuntime, NOT_CONFIGURED_MESSAGE } from "../client.js"; import type { ResolvedConfig } from "../config.js"; import { inkboxErrorMessage } from "../errors.js"; import { channelReadiness } from "../gateway/channel-readiness.js"; import { queueReadiness } from "../gateway/readiness.js"; import { CALL_MEDIA_WS_PATH, WEBHOOK_PATH } from "../gateway/subscriptions.js"; +import type { HostClient as OpencodeClient } from "../host/types.js"; import { envFileCandidates, readEnvFile } from "./env-file.js"; import { DEFAULT_OPENCODE_SERVER_URL, opencodeBinAvailable, opencodeReachable } from "./serve.js"; +import { hostMajor, incompatibleWrappers } from "./wrappers.js"; export type Severity = "error" | "warning" | "info"; @@ -162,8 +164,35 @@ export async function runDoctor( // An explicitly configured server must answer; the default URL falling // through to a managed `opencode serve` only needs the binary to exist. const explicit = config.gateway.serverUrl; - const serverUrl = explicit ?? DEFAULT_OPENCODE_SERVER_URL; - const opencode = deps.opencode ?? createOpencodeClient({ baseUrl: serverUrl }); + let serverUrl = explicit ?? DEFAULT_OPENCODE_SERVER_URL; + let opencode = deps.opencode; + if (!opencode) { + try { + const { connectV2Server } = await import("./v2-server.js"); + const server = await connectV2Server(config, false); + if (server) { + serverUrl = server.url; + opencode = server.client; + await opencode?.tool.ids({ + query: { directory: config.gateway.projectDirectory ?? deps.cwd ?? process.cwd() }, + }); + add("info", "OpenCode V2 adapter and plugin configuration verified."); + } + } catch (error) { + add("error", error instanceof Error ? error.message : "OpenCode V2 connection failed."); + } + const major = hostMajor(config.gateway.serve.bin); + if (major) + for (const file of incompatibleWrappers( + [deps.cwd ?? process.cwd(), config.gateway.projectDirectory ?? deps.cwd ?? process.cwd()], + major, + )) + add( + "error", + `Update the plugin wrapper at ${file} for OpenCode V${major}, preserving its options. See the README.`, + ); + } + opencode ??= createOpencodeClient({ baseUrl: serverUrl }); if (await opencodeReachable(opencode)) { add("info", `opencode server reachable at ${serverUrl}.`); } else if (explicit) { @@ -177,7 +206,9 @@ export async function runDoctor( if (found) { add( "info", - `no opencode server at ${serverUrl}; the gateway will launch its own \`${bin} serve\` on port ${port}.`, + !deps.opencode && hostMajor(bin) === 2 + ? "No shared OpenCode V2 service is running; the gateway will start one." + : `no opencode server at ${serverUrl}; the gateway will launch its own \`${bin} serve\` on port ${port}.`, ); } else { add( diff --git a/src/cli/index.ts b/src/cli/index.ts index fdf24c1..a53b0b6 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -44,9 +44,9 @@ Commands: bootstrap Configure an existing identity without interactive prompts. uninstall Stop the daemon, remove the boot service and local state. -The gateway attaches to an opencode server at OPENCODE_SERVER_URL (or -http://127.0.0.1:4096); when neither answers it launches its own managed -\`opencode serve\`. Credentials resolve from plugin options, env vars, an env +The gateway uses OPENCODE_SERVER_URL when set. V2 discovers or starts the +authenticated shared service; V1 attaches at http://127.0.0.1:4096 or launches +its own managed \`opencode serve\`. Credentials resolve from plugin options, env vars, an env file (INKBOX_OPENCODE_ENV_FILE, ./.env, ~/.inkbox-opencode/.env), then ~/.inkbox/config. `; diff --git a/src/cli/run.ts b/src/cli/run.ts index 2c4b1c8..b62003e 100644 --- a/src/cli/run.ts +++ b/src/cli/run.ts @@ -1,9 +1,10 @@ -import { createOpencodeClient, type OpencodeClient } from "@opencode-ai/sdk"; +import { createOpencodeClient } from "@opencode-ai/sdk"; import { createInkboxRuntime, type InkboxRuntime, NOT_CONFIGURED_MESSAGE } from "../client.js"; import type { ResolvedConfig } from "../config.js"; import { inkboxErrorMessage } from "../errors.js"; import { startGateway } from "../gateway/index.js"; import type { GatewayLogger } from "../gateway/types.js"; +import type { HostClient as OpencodeClient } from "../host/types.js"; import { DEFAULT_OPENCODE_SERVER_URL, type EnsuredServer, @@ -60,7 +61,7 @@ export async function runForeground( if (!ensured) return 1; server = ensured; } - const opencode = deps.opencode ?? createOpencodeClient({ baseUrl: server.url }); + const opencode = deps.opencode ?? server.client ?? createOpencodeClient({ baseUrl: server.url }); const runtime = deps.runtime ?? runtimeFor(config); const directory = config.gateway.projectDirectory ?? process.cwd(); diff --git a/src/cli/serve.ts b/src/cli/serve.ts index 92d3047..918d22c 100644 --- a/src/cli/serve.ts +++ b/src/cli/serve.ts @@ -1,9 +1,11 @@ import { spawn as nodeSpawn } from "node:child_process"; import * as fs from "node:fs"; import * as path from "node:path"; -import { createOpencodeClient, type OpencodeClient } from "@opencode-ai/sdk"; +import { createOpencodeClient } from "@opencode-ai/sdk"; import type { ResolvedConfig } from "../config.js"; import type { GatewayLogger } from "../gateway/types.js"; +import type { HostClient as OpencodeClient } from "../host/types.js"; +import { hostMajor } from "./wrappers.js"; // opencode's default server bind: where an interactive `opencode serve` (or a // CI harness) listens. The sidecar attaches here when nothing is configured, @@ -14,8 +16,8 @@ export const DEFAULT_OPENCODE_SERVER_URL = "http://127.0.0.1:4096"; // response counts as reachable; a rejected fetch (server down) does not. export async function opencodeReachable(opencode: OpencodeClient): Promise { try { - await opencode.config.get(); - return true; + const result = await opencode.config.get(); + return !result.error && Boolean(result.data); } catch { return false; } @@ -67,6 +69,7 @@ export type SpawnServe = ( export interface EnsuredServer { url: string; owned: boolean; + client?: OpencodeClient; // Fires when an owned server dies out from under the gateway. Never fires // for an attached server, nor during a deliberate stop(). onExit(cb: (code: number | null) => void): void; @@ -105,6 +108,14 @@ export async function ensureOpencodeServer( }); const explicit = config.gateway.serverUrl; + if (!deps.probe) { + const { connectV2Server } = await import("./v2-server.js"); + const connected = await connectV2Server( + config, + !explicit && hostMajor(resolveOpencodeBin(config.gateway.serve.bin)) === 2, + ); + if (connected) return connected; + } if (explicit) { if (await probe(explicit)) return attached(explicit); logger.error( diff --git a/src/cli/setup.ts b/src/cli/setup.ts index 253abea..17ba4b2 100644 --- a/src/cli/setup.ts +++ b/src/cli/setup.ts @@ -1,5 +1,6 @@ import type { ResolvedConfig } from "../config.js"; import { DEFAULT_OPENCODE_SERVER_URL } from "./serve.js"; +import { hostMajor, incompatibleWrappers, wrapperSource } from "./wrappers.js"; export interface SetupOptions { print?: (line: string) => void; @@ -33,7 +34,14 @@ export function runSetup(config: ResolvedConfig, opts: SetupOptions = {}): numbe print(" The installer (install.sh) wires the global ~/.config/opencode for"); print(" you. For a per-project setup, add .opencode/plugins/inkbox.ts:"); print(""); - for (const line of wrapperSnippet(serverUrl).split("\n")) print(` ${line}`); + const major = hostMajor(config.gateway.serve.bin) ?? 1; + for (const line of (major === 2 ? wrapperSource(2) : wrapperSnippet(serverUrl)).split("\n")) + print(` ${line}`); + for (const file of incompatibleWrappers( + [process.cwd(), config.gateway.projectDirectory ?? process.cwd()], + major, + )) + print(` Update the V${major === 2 ? 1 : 2} wrapper at ${file}, preserving its options.`); print(""); print("3. Provisioning (done in the Inkbox console / CLI)"); print(" - Provision a phone number and enable iMessage for your identity."); @@ -42,8 +50,14 @@ export function runSetup(config: ResolvedConfig, opts: SetupOptions = {}): numbe print("4. Start it"); print(" inkbox-opencode start # or 'run' to stay foreground"); print(" inkbox-opencode status"); - print(" The gateway attaches to an `opencode serve` on :4096 if one is running;"); - print(` otherwise it launches its own managed server (port ${config.gateway.serve.port}).`); + if (major === 2) + print( + " V2 uses the authenticated shared OpenCode service. Configure Inkbox in that service's environment too.", + ); + else { + print(" The gateway attaches to an `opencode serve` on :4096 if one is running;"); + print(` otherwise it launches its own managed server (port ${config.gateway.serve.port}).`); + } print(""); print("5. Keep it running on boot (optional)"); print(" inkbox-opencode autostart install"); diff --git a/src/cli/v2-server.ts b/src/cli/v2-server.ts new file mode 100644 index 0000000..9194437 --- /dev/null +++ b/src/cli/v2-server.ts @@ -0,0 +1,60 @@ +import { Service } from "@opencode/client/service"; +import type { ResolvedConfig } from "../config.js"; +import { createV2Host } from "../host/v2-client.js"; +import { createV2Connection } from "../host/v2-connection.js"; +import { type EnsuredServer, resolveOpencodeBin } from "./serve.js"; +import { hostMajor } from "./wrappers.js"; + +export async function connectV2Server( + config: ResolvedConfig, + start: boolean, +): Promise { + const explicit = config.gateway.serverUrl; + if (!explicit && hostMajor(resolveOpencodeBin(config.gateway.serve.bin)) !== 2) return undefined; + if (explicit) { + const response = await fetch(new URL("/api/info", explicit), { + signal: AbortSignal.timeout(3000), + headers: { + ...(process.env.INKBOX_OPENCODE_SERVER_PASSWORD + ? { + authorization: `Basic ${Buffer.from(`${process.env.INKBOX_OPENCODE_SERVER_USERNAME ?? "opencode"}:${process.env.INKBOX_OPENCODE_SERVER_PASSWORD}`).toString("base64")}`, + } + : {}), + }, + }).catch(() => undefined); + if (!response) return undefined; + if (response.status === 401 || response.status === 403) + throw new Error( + "OpenCode server authentication failed. Check INKBOX_OPENCODE_SERVER_PASSWORD.", + ); + const info = (await response.json().catch(() => undefined)) as { version?: string } | undefined; + if (!response.ok || !info?.version?.startsWith("2.")) return undefined; + } + const endpoint = explicit + ? undefined + : ((await Service.discover({ version: (v) => v.startsWith("2.") })) ?? + (start + ? await Service.ensure({ + version: (v) => v.startsWith("2."), + command: [resolveOpencodeBin(config.gateway.serve.bin), "serve", "--service"], + }) + : undefined)); + const url = explicit ?? endpoint?.url; + if (!url) return undefined; + return { + url, + owned: false, + client: createV2Host( + createV2Connection( + explicit, + undefined, + start && !explicit + ? [resolveOpencodeBin(config.gateway.serve.bin), "serve", "--service"] + : undefined, + ), + config, + ), + onExit() {}, + async stop() {}, + }; +} diff --git a/src/cli/wrappers.ts b/src/cli/wrappers.ts new file mode 100644 index 0000000..2403af9 --- /dev/null +++ b/src/cli/wrappers.ts @@ -0,0 +1,104 @@ +import { execFileSync } from "node:child_process"; +import { randomUUID } from "node:crypto"; +import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs"; +import { homedir } from "node:os"; +import { dirname, join, resolve } from "node:path"; +import { parse } from "jsonc-parser"; + +export function hostMajor(bin = "opencode"): 1 | 2 | undefined { + try { + const match = execFileSync(bin, ["--version"], { encoding: "utf8", timeout: 5000 }).match( + /(?:^|\s)v?([12])\./, + ); + return match ? (Number(match[1]) as 1 | 2) : undefined; + } catch { + return undefined; + } +} + +export function wrapperSource(major: 1 | 2): string { + if (major === 1) + return 'import InkboxPlugin from "@inkbox/opencode-plugin";\n\nexport default async (input: any) => InkboxPlugin(input, {\n tools: { enable: ["inkbox_place_call"] },\n});\n'; + return 'import InkboxPlugin from "@inkbox/opencode-plugin/v2";\n\nexport default {\n ...InkboxPlugin,\n setup: (ctx: Parameters[0]) => InkboxPlugin.setup({\n ...ctx,\n options: { ...ctx.options, tools: { enable: ["inkbox_place_call"] } },\n }),\n};\n'; +} + +function normalized(source: string): string { + const header = [ + "// Inkbox plugin wrapper — written by install.sh; edit freely (never overwritten).", + "// Credentials resolve from env vars or ~/.inkbox/config; options: see README.", + "// inkbox_place_call is enabled so the agent can dial out (calls stay gated", + "// by outbound approval like every other send).", + "", + ].join("\n"); + const text = source.replace(/\r\n/g, "\n").trim(); + return text.startsWith(header) ? text.slice(header.length).trim() : text; +} + +export function installWrapper( + file: string, + major: 1 | 2, +): "created" | "updated" | "unchanged" | "custom" { + const source = wrapperSource(major); + if (!existsSync(file)) { + writeFileSync(file, source); + return "created"; + } + const existing = readFileSync(file, "utf8"); + if (normalized(existing) === normalized(source)) return "unchanged"; + if ( + ![1, 2].some((version) => normalized(existing) === normalized(wrapperSource(version as 1 | 2))) + ) + return "custom"; + writeFileSync(`${file}.backup-${randomUUID()}`, existing, { flag: "wx", mode: 0o600 }); + writeFileSync(file, source); + return "updated"; +} + +export function incompatibleWrappers(roots: string[], major: 1 | 2): string[] { + const dirs = new Set([ + join(process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"), "opencode"), + ]); + for (const root of roots) { + let dir = resolve(root); + for (;;) { + dirs.add(join(dir, ".opencode")); + if (dirname(dir) === dir) break; + dir = dirname(dir); + } + } + const found = new Set(); + for (const dir of dirs) + for (const sub of ["plugin", "plugins"]) { + const parent = join(dir, sub); + if (!existsSync(parent)) continue; + for (const entry of readdirSync(parent, { withFileTypes: true })) { + if (!entry.isFile() || !/\.[jt]s$/.test(entry.name)) continue; + const file = join(parent, entry.name); + const text = readFileSync(file, "utf8"); + if (!text.includes("@inkbox/opencode-plugin")) continue; + if ( + (major === 2 && !text.includes("@inkbox/opencode-plugin/v2")) || + (major === 1 && text.includes("@inkbox/opencode-plugin/v2")) + ) + found.add(file); + } + } + return [...found]; +} + +export function configuredPackages(directory: string): string[] { + const files = ["opencode.json", "opencode.jsonc"].map((name) => join(directory, name)); + return files.filter((file) => { + if (!existsSync(file)) return false; + const config = parse(readFileSync(file, "utf8")); + return [config?.plugin, config?.plugins].some( + (entries) => + Array.isArray(entries) && + entries.some((entry) => { + const target = + typeof entry === "string" ? entry : Array.isArray(entry) ? entry[0] : entry?.package; + return typeof target === "string" && /^@inkbox\/opencode-plugin(?:$|[@/])/.test(target); + }), + ); + }); +} diff --git a/src/gateway/commands.ts b/src/gateway/commands.ts index 0da2ebc..4eeeed1 100644 --- a/src/gateway/commands.ts +++ b/src/gateway/commands.ts @@ -1,5 +1,5 @@ -import type { OpencodeClient } from "@opencode-ai/sdk"; import type { InkboxRuntime } from "../client.js"; +import type { HostClient as OpencodeClient } from "../host/types.js"; import type { GatewayLogger, SessionManager } from "./types.js"; export interface CommandDeps { diff --git a/src/gateway/escalation.ts b/src/gateway/escalation.ts index 99a92d1..e0a2f11 100644 --- a/src/gateway/escalation.ts +++ b/src/gateway/escalation.ts @@ -1,4 +1,4 @@ -import type { OpencodeClient } from "@opencode-ai/sdk"; +import type { HostClient as OpencodeClient } from "../host/types.js"; import type { DurablePermission, StateStore } from "./state.js"; import type { GatewayLogger, ReplyTarget } from "./types.js"; diff --git a/src/gateway/events.ts b/src/gateway/events.ts index 13ef4aa..132a17a 100644 --- a/src/gateway/events.ts +++ b/src/gateway/events.ts @@ -1,4 +1,4 @@ -import type { OpencodeClient } from "@opencode-ai/sdk"; +import type { HostClient as OpencodeClient } from "../host/types.js"; import type { createEscalationBridge, PendingPermission } from "./escalation.js"; import type { GatewayLogger } from "./types.js"; diff --git a/src/gateway/index.ts b/src/gateway/index.ts index 262514b..aaaa95a 100644 --- a/src/gateway/index.ts +++ b/src/gateway/index.ts @@ -1,6 +1,7 @@ -import type { OpencodeClient } from "@opencode-ai/sdk"; import type { InkboxRuntime } from "../client.js"; import type { ResolvedConfig } from "../config.js"; +import { bindHostState } from "../host/state.js"; +import type { HostClient as OpencodeClient } from "../host/types.js"; import { checkOutboundRecipient } from "../permissions.js"; import { slackRouteKey } from "../slack.js"; import { createA2AHandler } from "./a2a.js"; @@ -56,9 +57,11 @@ const consoleLogger: GatewayLogger = { // close() tears everything down (used by signal handlers in sidecar mode and // dispose in plugin mode). export async function startGateway(opts: StartGatewayOptions): Promise { + await opts.opencode.ready?.(opts.directory); const logger = opts.logger ?? consoleLogger; const g = opts.config.gateway; const state = createStateStore(); + if (opts.opencode.protocol !== 2) bindHostState(state, 1); // A prior process's successful inventory is not evidence for this attachment. // Do this before opening any gateway resources or serving readiness requests. state.update({ permissionInventory: { ready: false } }); diff --git a/src/gateway/permission-inventory.ts b/src/gateway/permission-inventory.ts index 591dae3..1e62e42 100644 --- a/src/gateway/permission-inventory.ts +++ b/src/gateway/permission-inventory.ts @@ -1,5 +1,5 @@ -import type { OpencodeClient } from "@opencode-ai/sdk"; import { OpencodeClient as NativeClient } from "@opencode-ai/sdk/v2/client"; +import type { HostClient as OpencodeClient } from "../host/types.js"; export interface NativePermissionInventory { list(directory: string, signal: AbortSignal): Promise; @@ -12,6 +12,7 @@ export interface NativePermissionInventory { export function createNativePermissionInventory(client: OpencodeClient): NativePermissionInventory { return { async list(directory, signal) { + if (client.permissionInventory) return client.permissionInventory(directory, signal); const transport = (client as unknown as { _client?: unknown })._client as | { get?: unknown; getConfig?: unknown } | undefined; diff --git a/src/gateway/sessions.ts b/src/gateway/sessions.ts index ea26e64..bab8125 100644 --- a/src/gateway/sessions.ts +++ b/src/gateway/sessions.ts @@ -1,6 +1,5 @@ import { randomBytes, randomUUID } from "node:crypto"; import { dirname, join } from "node:path"; -import type { OpencodeClient } from "@opencode-ai/sdk"; import { type ActiveA2ATurn, clearActiveA2ATurn, setActiveA2ATurn } from "../a2a-context.js"; import { a2aProgressSystemPrompt, @@ -11,6 +10,7 @@ import { } from "../a2a-progress.js"; import type { InkboxRuntime } from "../client.js"; import type { ResolvedConfig } from "../config.js"; +import type { HostClient as OpencodeClient } from "../host/types.js"; import { ownSlackConnection, type SlackRoute, slackRouteKey } from "../slack.js"; import { createSlackActivity } from "../slack-activity.js"; import { createSlackProgress } from "../slack-progress.js"; @@ -439,14 +439,17 @@ export function createSessionManager( if (turn.companion) assertCompanionSize(JSON.stringify(body)); // Native counters are process-local, so wall time alone is insufficient // when the preceding assistant and this submission share a millisecond. - const precedingMessageID = (await listMessages(sessionID)) - .map((message) => message?.info?.id) - .filter( - (id): id is string => - typeof id === "string" && /^msg_[a-f0-9]{12}[0-9A-Za-z]{14}$/.test(id), - ) - .sort() - .at(-1); + const precedingMessageID = + deps.opencode.protocol === 2 + ? undefined + : (await listMessages(sessionID)) + .map((message) => message?.info?.id) + .filter( + (id): id is string => + typeof id === "string" && /^msg_[a-f0-9]{12}[0-9A-Za-z]{14}$/.test(id), + ) + .sort() + .at(-1); if (closing) throw new HostedCaptureDeferredError(); assertCompanionLocallyAllowed(turn); await authorizeSlack(turn); @@ -459,7 +462,10 @@ export function createSessionManager( state: "submitting", sessionID, // Native history ordering follows the prompt ID, not queue admission. - messageID: createMessageID(precedingMessageID), + messageID: + deps.opencode.protocol === 2 + ? `msg_${randomUUID()}` + : createMessageID(precedingMessageID), }, turn.companion ? ownerId : undefined, ); diff --git a/src/gateway/types.ts b/src/gateway/types.ts index 9dfba32..863ff1e 100644 --- a/src/gateway/types.ts +++ b/src/gateway/types.ts @@ -1,7 +1,7 @@ -import type { OpencodeClient } from "@opencode-ai/sdk"; import type { ActiveA2ATurn } from "../a2a-context.js"; import type { InkboxRuntime } from "../client.js"; import type { ResolvedConfig } from "../config.js"; +import type { HostClient as OpencodeClient } from "../host/types.js"; import type { SlackRoute } from "../slack.js"; import type { CompanionTurn } from "./companion.js"; import type { HostedSmsAttempt } from "./hosted-call-registry.js"; diff --git a/src/host/gateway-owner.ts b/src/host/gateway-owner.ts new file mode 100644 index 0000000..66ef7f9 --- /dev/null +++ b/src/host/gateway-owner.ts @@ -0,0 +1,47 @@ +type Handle = { close(): Promise }; +type Candidate = { directory: string; start(): Promise }; +type Slot = { candidates: Set; owner?: Candidate; handle?: Handle; work: Promise }; +const slots = new Map(); + +export function registerGatewayOwner( + key: string, + candidate: Candidate, + report: (error: unknown) => void, +): () => Promise { + let slot = slots.get(key); + if (!slot) { + slot = { candidates: new Set(), work: Promise.resolve() }; + slots.set(key, slot); + } + const current = slot; + const reconcile = async () => { + if (current.owner && !current.candidates.has(current.owner)) { + await current.handle?.close(); + current.owner = undefined; + current.handle = undefined; + } + if (!current.owner) { + const next = [...current.candidates].sort((a, b) => + a.directory.localeCompare(b.directory), + )[0]; + if (next) { + current.handle = await next.start(); + current.owner = next; + } + } + if (!current.candidates.size && !current.owner) slots.delete(key); + }; + const schedule = () => { + current.work = current.work.catch(() => {}).then(reconcile); + return current.work; + }; + current.candidates.add(candidate); + const timer = setTimeout(() => { + void schedule().catch(report); + }, 0); + return async () => { + clearTimeout(timer); + current.candidates.delete(candidate); + await schedule(); + }; +} diff --git a/src/host/state.ts b/src/host/state.ts new file mode 100644 index 0000000..9f81ac5 --- /dev/null +++ b/src/host/state.ts @@ -0,0 +1,37 @@ +import type { StateStore } from "../gateway/state.js"; + +export function bindHostState(state: StateStore, protocol: 1 | 2, binding?: string): void { + const data = state.read(); + const changed = + (data.hostProtocol ?? 1) !== protocol || + (data.hostBinding !== undefined && data.hostBinding !== binding); + if (changed) { + if ( + Object.values(data.turns).some( + (turn) => + turn.sessionID && + ([ + "hydrating", + "queued", + "submitting", + "submitted", + "paused", + "delivery_started", + ].includes(turn.state) || + (turn.state === "completed" && turn.deliver) || + (turn.state === "failed" && + turn.uncertainStage === "submission" && + !turn.executionFenced)), + ) + ) + throw new Error( + "Existing gateway work belongs to another OpenCode host. Finish or reconcile it with that host before switching.", + ); + state.update({ + previousHostSessions: data.sessions, + sessions: {}, + hostProtocol: protocol, + hostBinding: binding, + }); + } else state.update({ hostProtocol: protocol, hostBinding: binding }); +} diff --git a/src/host/types.ts b/src/host/types.ts new file mode 100644 index 0000000..210cb30 --- /dev/null +++ b/src/host/types.ts @@ -0,0 +1,24 @@ +import type { OpencodeClient } from "@opencode-ai/sdk"; + +export type HostClient = { + session: Pick< + OpencodeClient["session"], + | "create" + | "get" + | "list" + | "messages" + | "message" + | "prompt" + | "promptAsync" + | "abort" + | "status" + | "delete" + >; + config: Pick; + tool: Pick; + event: Pick; + postSessionIdPermissionsPermissionId: OpencodeClient["postSessionIdPermissionsPermissionId"]; + protocol?: 2; + permissionInventory?(directory: string, signal: AbortSignal): Promise; + ready?(directory: string): Promise; +}; diff --git a/src/host/v2-approval.ts b/src/host/v2-approval.ts new file mode 100644 index 0000000..86541e3 --- /dev/null +++ b/src/host/v2-approval.ts @@ -0,0 +1,92 @@ +import { randomUUID } from "node:crypto"; +import type { V2Client } from "./v2-connection.js"; + +export async function requestApproval( + client: V2Client, + input: { + sessionID: string; + messageID: string; + callID: string; + agent: string; + permission: string; + patterns: string[]; + always: string[]; + metadata: Record; + }, + signal: AbortSignal, + timeoutMs: number, +): Promise { + const controller = new AbortController(); + const signals = [signal, controller.signal]; + if (timeoutMs > 0) signals.push(AbortSignal.timeout(timeoutMs)); + const combined = AbortSignal.any(signals); + const id = `per_${randomUUID()}`; + let resolveReady!: () => void; + let rejectReady!: (error: unknown) => void; + const ready = new Promise((resolve, reject) => { + resolveReady = resolve; + rejectReady = reject; + }); + let resolveReply!: (reply: string) => void; + let rejectReply!: (error: unknown) => void; + const reply = new Promise((resolve, reject) => { + resolveReply = resolve; + rejectReply = reject; + }); + void reply.catch(() => {}); + const reader = (async () => { + try { + for await (const event of client.event.subscribe({ signal: combined })) { + if (event.type === "server.connected") resolveReady(); + if ( + event.type === "permission.replied" && + event.data.sessionID === input.sessionID && + event.data.requestID === id + ) + resolveReply(event.data.reply); + } + throw new Error("OpenCode approval connection ended."); + } catch (error) { + rejectReady(error); + rejectReply(error); + } + })(); + let pending = false; + try { + await ready; + combined.throwIfAborted(); + pending = true; + const result = await client.permission.create( + { + id, + sessionID: input.sessionID, + agent: input.agent, + action: input.permission, + resources: input.patterns, + save: input.always, + metadata: JSON.parse(JSON.stringify(input.metadata)), + source: { type: "tool", messageID: input.messageID, id: input.callID }, + }, + { signal: combined }, + ); + pending = result.effect === "ask"; + if (result.effect === "deny") throw new Error("Outbound request was declined."); + if (pending) { + const decision = await reply; + pending = false; + if (decision !== "once" && decision !== "always") + throw new Error("Outbound request was declined."); + } + combined.throwIfAborted(); + } finally { + if (pending) + await client.permission + .reply( + { sessionID: input.sessionID, requestID: id, decision: "reject" }, + { signal: AbortSignal.timeout(3000) }, + ) + .catch(() => {}); + controller.abort(); + await reader; + } +} diff --git a/src/host/v2-client.ts b/src/host/v2-client.ts new file mode 100644 index 0000000..c59df04 --- /dev/null +++ b/src/host/v2-client.ts @@ -0,0 +1,252 @@ +import { randomUUID } from "node:crypto"; +import type { ResolvedConfig } from "../config.js"; +import { createStateStore } from "../gateway/state.js"; +import { bindHostState } from "./state.js"; +import type { HostClient } from "./types.js"; +import type { V2Connection } from "./v2-connection.js"; +import { normalizeHistory, readHistory } from "./v2-history.js"; +import { configurationKey, InkboxHost } from "./v2-rpc.js"; + +export function createV2Host(connection: V2Connection, config: ResolvedConfig): HostClient { + const sessions = new Map>(); + const serial = (sessionID: string, operation: () => Promise): Promise => { + const result = (sessions.get(sessionID) ?? Promise.resolve()).catch(() => {}).then(operation); + sessions.set(sessionID, result); + void result + .finally(() => { + if (sessions.get(sessionID) === result) sessions.delete(sessionID); + }) + .catch(() => {}); + return result; + }; + const call = async ( + operation: (client: Awaited>) => Promise, + retryRead = false, + ): Promise => { + try { + return await operation(await connection.get()); + } catch (error) { + connection.invalidate(); + if (retryRead && isConnectionFailure(error)) return call(operation); + if ((error as { reason?: string })?.reason === "Transport") + throw Object.assign(new Error("OpenCode connection interrupted.", { cause: error }), { + code: "ECONNRESET", + }); + throw error; + } + }; + const hello = (directory: string) => + call(async (client) => { + const result = await client.rpc(InkboxHost).hello({}, { location: { directory } }); + if (result.configuration !== configurationKey(config)) + throw new Error( + "Inkbox configuration differs between the gateway and OpenCode. Check identity, channel options, and state directory.", + ); + return result; + }, true); + const history = (sessionID: string, signal?: AbortSignal) => + call( + async (client) => normalizeHistory(await readHistory(client, sessionID, signal), sessionID), + true, + ); + const submit = async (args: any) => + serial(args.path.id, () => + call(async (client) => { + const directory = args.query?.directory; + const sessionID = args.path.id; + const body = args.body; + await hello(directory); + const id = body.messageID ?? `msg_${randomUUID()}`; + await client + .rpc(InkboxHost) + .prepare( + { sessionID, messageID: id, system: body.system, tools: body.tools }, + { location: { directory } }, + ); + const agent = + body.agent ?? + (await client.agent.list({ location: { directory } })).data.find( + (agent) => agent.mode !== "subagent" && !agent.hidden, + )?.id; + if (!agent) throw new Error("OpenCode has no default agent for this directory."); + await client.session.switchAgent({ sessionID, agent }); + if (body.model) + await client.session.switchModel({ + sessionID, + model: { providerID: body.model.providerID, id: body.model.modelID }, + }); + await client.session.prompt( + { + sessionID, + id, + text: body.parts + .filter((p: any) => p.type === "text") + .map((p: any) => p.text) + .join("\n"), + delivery: "queue", + metadata: { inkboxHost: 1 }, + }, + { signal: args.signal }, + ); + return id; + }), + ); + const host = { + protocol: 2 as const, + ready: async (directory: string) => { + const state = createStateStore(); + const probe = randomUUID(); + state.update({ hostProbe: probe }); + const info = await hello(directory); + if (info.stateProbe !== probe) + throw new Error( + "The gateway and OpenCode plugin must use the same accessible Inkbox state directory.", + ); + bindHostState(state, 2, `${info.installation}:${directory}`); + }, + config: { get: async () => ({ data: await call((c) => c.server.info(), true) }) }, + tool: { ids: async (a: any) => ({ data: (await hello(a.query.directory)).tools }) }, + session: { + create: async (a: any) => { + await hello(a.query.directory); + const session = await call((c) => + c.session.create({ title: a.body?.title, location: { directory: a.query.directory } }), + ); + return { data: { ...session, directory: session.location.directory } }; + }, + get: async (a: any) => { + try { + const s = await call( + (c) => c.session.get({ sessionID: a.path.id }, { signal: a.signal }), + true, + ); + return { data: { ...s, directory: s.location.directory } }; + } catch (error) { + if ((error as any)?._tag === "SessionNotFoundError") + return { error, response: { status: 404 } }; + throw error; + } + }, + list: async (a: any) => ({ + data: (await call((c) => c.session.list({ directory: a.query.directory }), true)).data, + }), + messages: async (a: any) => { + const messages = await history(a.path.id, a.signal); + const pending = await call( + (c) => c.session.inbox.list({ sessionID: a.path.id }, { signal: a.signal }), + true, + ); + for (const p of pending) + if (p.type === "user" && !messages.some((m) => m.info.id === p.id)) + messages.push({ + info: { id: p.id, sessionID: p.sessionID, role: "user" }, + parts: [{ type: "text", text: p.payload.text }], + }); + return { data: messages }; + }, + message: async (a: any) => { + const message = (await history(a.path.id, a.signal)).find( + (m) => m.info.id === a.path.messageID, + ); + if (!message) throw new Error("OpenCode message is unavailable."); + return { data: message }; + }, + promptAsync: async (a: any) => { + await submit(a); + return { data: undefined }; + }, + prompt: async (a: any) => { + const id = await submit(a); + await call((c) => c.session.wait({ sessionID: a.path.id }, { signal: a.signal })); + const message = (await history(a.path.id)) + .filter((m) => m.info.role === "assistant" && m.info.parentID === id) + .at(-1); + if (!message) throw new Error("OpenCode did not complete this prompt."); + return { data: message }; + }, + abort: async (a: any) => + serial(a.path.id, () => + call(async (c) => { + const sessionID = a.path.id; + for (const item of await c.session.inbox.list({ sessionID })) + await c.session.inbox.cancel({ sessionID, inboxID: item.id }); + await c.session.interrupt({ sessionID, resume: false }); + await c.session.wait({ sessionID }, { signal: AbortSignal.timeout(10000) }); + const pending = await c.session.inbox.list({ sessionID }); + const active = await c.session.active(); + if (pending.length || active[sessionID]) + throw new Error("OpenCode has not confirmed this session stopped."); + return { data: true }; + }), + ), + status: async () => ({ data: await call((c) => c.session.active(), true) }), + delete: async (a: any) => { + await call((c) => c.session.remove({ sessionID: a.path.id })); + return { data: true }; + }, + }, + permissionInventory: async (directory: string, signal: AbortSignal) => { + const result = await call( + (c) => c.permission.request.list({ location: { directory } }, { signal }), + true, + ); + return result.data.map(normalizePermission); + }, + postSessionIdPermissionsPermissionId: async (a: any) => { + await call((c) => + c.permission.reply( + { sessionID: a.path.id, requestID: a.path.permissionID, decision: a.body.response }, + { signal: a.signal }, + ), + ); + return { data: true }; + }, + event: { + subscribe: async (a: any) => { + const client = await connection.get(); + return { + stream: (async function* () { + try { + for await (const event of client.event.subscribe({ signal: a.signal })) { + const e = event as any; + if (e.location?.directory && e.location.directory !== a.query?.directory) continue; + yield { + type: e.type, + properties: e.type === "permission.asked" ? normalizePermission(e.data) : e.data, + }; + } + } finally { + connection.invalidate(); + } + })(), + }; + }, + }, + }; + return host as unknown as HostClient; +} + +function normalizePermission(p: any) { + return { + ...p, + permission: p.action, + patterns: p.resources, + tool: p.source ? { messageID: p.source.messageID, callID: p.source.id } : undefined, + }; +} + +function isConnectionFailure(error: unknown): boolean { + const value = error as { + _tag?: string; + reason?: string; + code?: string; + cause?: { code?: string; name?: string }; + }; + return ( + value?._tag === "UnauthorizedError" || + (value?.reason === "Transport" && value.cause?.name !== "AbortError") || + ["ECONNREFUSED", "ECONNRESET", "UND_ERR_SOCKET"].includes( + value?.code ?? value?.cause?.code ?? "", + ) + ); +} diff --git a/src/host/v2-connection.ts b/src/host/v2-connection.ts new file mode 100644 index 0000000..4b10c49 --- /dev/null +++ b/src/host/v2-connection.ts @@ -0,0 +1,57 @@ +import { OpenCode } from "@opencode/client"; +import { Service } from "@opencode/client/service"; + +export type V2Client = ReturnType; +export interface V2Connection { + get(): Promise; + invalidate(): void; +} + +export function createV2Connection( + url?: string, + headers?: RequestInit["headers"], + startCommand?: string[], +): V2Connection { + let pending: Promise | undefined; + return { + invalidate() { + pending = undefined; + }, + get() { + pending ??= (async () => { + if (typeof (Promise as unknown as { withResolvers?: unknown }).withResolvers !== "function") + throw new Error("The OpenCode V2 gateway requires Node.js 22 or newer."); + const endpoint = url + ? undefined + : ((await Service.discover({ version: (v) => v.startsWith("2.") })) ?? + (startCommand + ? await Service.ensure({ version: (v) => v.startsWith("2."), command: startCommand }) + : undefined)); + const baseUrl = url ?? endpoint?.url; + if (!baseUrl) + throw Object.assign( + new Error("OpenCode V2 service is unavailable. Start OpenCode and retry."), + { code: "ECONNREFUSED" }, + ); + const client = OpenCode.make({ + baseUrl, + headers: headers ?? (endpoint ? Service.headers(endpoint) : explicitHeaders()), + }); + const info = await client.server.info({ signal: AbortSignal.timeout(5000) }); + if (!info.version.startsWith("2.")) throw new Error("Expected an OpenCode V2 server."); + return client; + })().catch((error) => { + pending = undefined; + throw error; + }); + return pending; + }, + }; +} + +function explicitHeaders(): Record { + const password = process.env.INKBOX_OPENCODE_SERVER_PASSWORD; + if (!password) return {}; + const username = process.env.INKBOX_OPENCODE_SERVER_USERNAME ?? "opencode"; + return { authorization: `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}` }; +} diff --git a/src/host/v2-history.ts b/src/host/v2-history.ts new file mode 100644 index 0000000..d2cbeed --- /dev/null +++ b/src/host/v2-history.ts @@ -0,0 +1,106 @@ +import type { SessionMessageInfo } from "@opencode/client"; +import type { V2Client } from "./v2-connection.js"; + +export async function readHistory( + client: V2Client, + sessionID: string, + signal?: AbortSignal, +): Promise { + const messages: SessionMessageInfo[] = []; + let cursor: string | undefined; + const seen = new Set(); + for (;;) { + const page = await client.message.list( + { sessionID, limit: 200, ...(cursor ? { cursor } : { order: "asc" as const }) }, + { signal }, + ); + if (!page.data.length) break; + messages.push(...page.data); + const next = page.cursor.next; + if (!next) break; + if (seen.has(next)) throw new Error("OpenCode history pagination did not advance."); + seen.add(next); + cursor = next; + } + return messages; +} + +export function normalizeHistory(messages: SessionMessageInfo[], sessionID: string) { + return projectHistory(messages, sessionID).messages; +} + +export function projectHistory(messages: SessionMessageInfo[], sessionID: string) { + let parentID: string | undefined; + let ambiguous = false; + let inputs: string[] = []; + let canStart = true; + const projected = messages.flatMap((message): any[] => { + if (message.type === "idle") { + const failed = message.outcome !== "succeeded" ? inputs : []; + canStart = true; + parentID = undefined; + inputs = []; + ambiguous = false; + return failed.map((input) => ({ + info: { + id: `${message.id}_${input}`, + sessionID, + role: "assistant", + parentID: input, + finish: "error", + error: { message: `OpenCode session ${message.outcome}.` }, + }, + parts: [], + })); + } + if (message.type === "user") { + ambiguous = !canStart && inputs.length > 0; + parentID = ambiguous ? undefined : message.id; + inputs = ambiguous ? [...inputs, message.id] : [message.id]; + canStart = false; + return [ + { + info: { + id: message.id, + sessionID, + role: "user", + parentID, + sourceAmbiguous: ambiguous, + time: message.time, + }, + parts: [{ type: "text", text: message.text }], + }, + ]; + } + if (["synthetic", "shell", "skill", "compaction"].includes(message.type) && canStart) { + parentID = undefined; + inputs = []; + ambiguous = false; + canStart = false; + } + if (message.type !== "assistant") return []; + const parts = message.content.map((part): any => { + if (part.type !== "tool") return part; + const state = part.state as any; + return { + type: "tool", + tool: part.name, + callID: part.id, + state: { + ...state, + status: state.status === "streaming" ? "pending" : state.status, + input: state.input, + output: state.result?.content ?? state.output, + }, + }; + }); + if (message.finish && !["tool-calls", "unknown"].includes(message.finish)) canStart = true; + return [ + { + info: { ...message, sessionID, role: "assistant", parentID, sourceAmbiguous: ambiguous }, + parts, + }, + ]; + }); + return { messages: projected, parentID, ambiguous }; +} diff --git a/src/host/v2-rpc.ts b/src/host/v2-rpc.ts new file mode 100644 index 0000000..8d0a71b --- /dev/null +++ b/src/host/v2-rpc.ts @@ -0,0 +1,46 @@ +import { createHash } from "node:crypto"; +import { Rpc } from "@opencode/plugin/rpc"; +import { z } from "zod"; +import type { ResolvedConfig } from "../config.js"; +import { gatewayHome } from "../gateway/state.js"; + +export const TurnPolicy = z.object({ + sessionID: z.string(), + messageID: z.string(), + system: z.string().optional(), + tools: z.record(z.string(), z.boolean()).optional(), +}); +export type TurnPolicy = z.infer; + +export const InkboxHost = Rpc.define({ + id: "inkbox.host", + events: {}, + methods: { + hello: { + input: z.object({}), + output: z.object({ + version: z.literal(1), + instance: z.string(), + installation: z.string(), + stateProbe: z.string().optional(), + configuration: z.string(), + tools: z.array(z.string()), + }), + }, + prepare: { input: TurnPolicy, output: z.object({ ready: z.literal(true) }) }, + }, +}); + +export function configurationKey(config: ResolvedConfig): string { + return createHash("sha256") + .update( + JSON.stringify({ + identity: config.identity, + baseUrl: config.baseUrl, + home: gatewayHome(), + slack: config.gateway.slackEnabled, + imessage: config.gateway.imessageThreadedReplies, + }), + ) + .digest("hex"); +} diff --git a/src/imessage-native.ts b/src/imessage-native.ts index 3d0e0a2..5f70068 100644 --- a/src/imessage-native.ts +++ b/src/imessage-native.ts @@ -1,8 +1,8 @@ import { createHash } from "node:crypto"; import type { AgentIdentity } from "@inkbox/sdk"; -import type { OpencodeClient } from "@opencode-ai/sdk"; import { createStateStore, type StateStore } from "./gateway/state.js"; import type { ReplyTarget } from "./gateway/types.js"; +import type { HostClient as OpencodeClient } from "./host/types.js"; export interface NativeSend { key: string; diff --git a/src/slack-upload.ts b/src/slack-upload.ts index fba0253..2144196 100644 --- a/src/slack-upload.ts +++ b/src/slack-upload.ts @@ -3,9 +3,9 @@ import { constants } from "node:fs"; import { open, realpath } from "node:fs/promises"; import { basename, resolve } from "node:path"; import type { ToolContext } from "@opencode-ai/plugin"; -import type { OpencodeClient } from "@opencode-ai/sdk"; import type { InkboxRuntime } from "./client.js"; import { createStateStore, type StateStore } from "./gateway/state.js"; +import type { HostClient as OpencodeClient } from "./host/types.js"; import { ownSlackConnection, type SlackRoute, slackRouteKey } from "./slack.js"; const MAX_BYTES = 10 * 1024 * 1024; diff --git a/src/tools/types.ts b/src/tools/types.ts index 7cad137..80349a1 100644 --- a/src/tools/types.ts +++ b/src/tools/types.ts @@ -1,7 +1,7 @@ import type { ToolDefinition } from "@opencode-ai/plugin"; -import type { OpencodeClient } from "@opencode-ai/sdk"; import type { InkboxRuntime } from "../client.js"; import type { ResolvedConfig } from "../config.js"; +import type { HostClient as OpencodeClient } from "../host/types.js"; import type { VaultRuntime } from "../vault.js"; export interface ToolDeps { diff --git a/src/v2.ts b/src/v2.ts new file mode 100644 index 0000000..981f13e --- /dev/null +++ b/src/v2.ts @@ -0,0 +1,204 @@ +import { randomUUID } from "node:crypto"; +import { resolve } from "node:path"; +import { Plugin } from "@opencode/plugin"; +import { z } from "zod"; +import { createInkboxRuntime } from "./client.js"; +import { resolveConfig } from "./config.js"; +import { createStateStore, gatewayHome } from "./gateway/state.js"; +import { registerGatewayOwner } from "./host/gateway-owner.js"; +import { requestApproval } from "./host/v2-approval.js"; +import { createV2Host } from "./host/v2-client.js"; +import { createV2Connection, type V2Connection } from "./host/v2-connection.js"; +import { projectHistory, readHistory } from "./host/v2-history.js"; +import { configurationKey, InkboxHost, TurnPolicy } from "./host/v2-rpc.js"; +import { registerTools } from "./tools/index.js"; +import { createVaultRuntime } from "./vault.js"; + +export default Plugin.define({ + id: "inkbox", + async setup(ctx) { + const config = resolveConfig(ctx.options); + const instance = randomUUID(); + const installationKey = `installation/${ctx.location.directory}`; + const savedInstallation = await ctx.storage.get(installationKey); + const installation = typeof savedInstallation === "string" ? savedInstallation : randomUUID(); + await ctx.storage.set(installationKey, installation); + const runtime = createInkboxRuntime(() => ({ + apiKey: config.apiKey, + identity: config.identity, + baseUrl: config.baseUrl, + })); + const vault = createVaultRuntime(runtime, { keyEnvVar: config.vaultKeyEnvVar }); + const connection = createV2Connection(config.gateway.serverUrl); + const checked: V2Connection = { + invalidate: () => connection.invalidate(), + async get() { + try { + const client = await connection.get(); + const result = await client + .rpc(InkboxHost) + .hello({}, { location: { directory: ctx.location.directory } }); + if (result.instance !== instance) + throw new Error( + "OpenCode connection does not match this plugin instance. Configure the gateway server URL and authentication for this host.", + ); + return client; + } catch (error) { + connection.invalidate(); + throw error; + } + }, + }; + const host = createV2Host(checked, config); + const { tools } = registerTools({ runtime, config, vault, opencode: host }); + await ctx.tool.transform((editor) => { + for (const [name, definition] of Object.entries(tools)) + editor.add({ + name, + description: definition.description, + input: z.object(definition.args), + options: { codemode: false }, + async execute(input, context) { + const session = await ctx.session.get({ sessionID: context.sessionID }); + const directory = session.location.directory; + if (!directory) throw new Error("OpenCode session directory is unavailable."); + context.signal.throwIfAborted(); + const result = await definition.execute(input, { + sessionID: context.sessionID, + messageID: context.messageID, + agent: context.agent, + directory, + worktree: directory, + abort: context.signal, + metadata: (update) => { + void context.progress(update).catch(() => {}); + }, + ask: async (request) => + requestApproval( + await checked.get(), + { + ...request, + sessionID: context.sessionID, + messageID: context.messageID, + callID: context.id, + agent: context.agent, + }, + context.signal, + config.outbound.askTimeoutMs, + ), + }); + if (typeof result === "string") return { content: result }; + return { + content: [ + { type: "text" as const, text: result.output }, + ...(result.attachments ?? []).map((file) => ({ + type: "file" as const, + uri: file.url, + mime: file.mime, + name: file.filename, + })), + ], + metadata: { ...result.metadata, title: result.title }, + }; + }, + }); + }); + const key = (sessionID: string, messageID: string) => `turn/${sessionID}/${messageID}`; + await ctx.rpc.register(InkboxHost, { + hello: async () => ({ + version: 1 as const, + instance, + installation, + stateProbe: createStateStore().read().hostProbe as string | undefined, + configuration: configurationKey(config), + tools: (await ctx.tool.list()).map((t) => t.id), + }), + prepare: async (policy) => { + const session = await ctx.session.get({ sessionID: policy.sessionID }); + if (session.location.directory !== ctx.location.directory) + throw new Error("OpenCode session moved to another directory."); + const saved = await ctx.storage.get(key(policy.sessionID, policy.messageID)); + if (saved && JSON.stringify(saved) !== JSON.stringify(policy)) + throw new Error("OpenCode prompt options changed for an existing message."); + await ctx.storage.set(key(policy.sessionID, policy.messageID), policy); + if (session.metadata?.inkboxHost !== 1) + await ctx.session.update({ + sessionID: policy.sessionID, + metadata: { ...session.metadata, inkboxHost: 1 }, + }); + return { ready: true as const }; + }, + }); + await ctx.session.hook("prompt", async (event) => { + if (event.metadata?.inkboxHost !== 1) return; + if (!(await ctx.storage.get(key(event.sessionID, event.messageID)))) + throw new Error( + "Inkbox prompt options are unavailable. Reconnect the gateway before retrying.", + ); + }); + const policyFor = async (sessionID: string, messageID?: string) => { + if ((await ctx.session.get({ sessionID })).metadata?.inkboxHost !== 1) return undefined; + const client = await checked.get(); + const history = await readHistory(client, sessionID); + const projection = projectHistory(history, sessionID); + const source = messageID + ? projection.messages.find((message) => message.info.id === messageID)?.info + : { parentID: projection.parentID, sourceAmbiguous: projection.ambiguous }; + if (!source || source.sourceAmbiguous) { + if ( + history.some((message) => message.type === "user" && message.metadata?.inkboxHost === 1) + ) + throw new Error( + "OpenCode request source is unavailable. Stop overlapping work before retrying.", + ); + return undefined; + } + if (!source.parentID) return undefined; + const user = history.find((message) => message.id === source.parentID); + if (!user) return undefined; + const saved = await ctx.storage.get(key(sessionID, user.id)); + if (!saved && user.metadata?.inkboxHost === 1) + throw new Error( + "Inkbox prompt options are unavailable. Reconnect the gateway before retrying.", + ); + return saved ? TurnPolicy.parse(saved) : undefined; + }; + await ctx.session.hook("context", async (event) => { + const policy = await policyFor(event.sessionID); + if (!policy) return; + if (policy.system) event.system.push({ type: "text", text: policy.system }); + if (policy.tools) + for (const name of Object.keys(event.tools)) + if (policy.tools[name] !== true) delete event.tools[name]; + }); + await ctx.tool.hook("execute.before", async (event) => { + const policy = await policyFor(event.sessionID, event.messageID); + if (policy?.tools && policy.tools[event.tool] !== true) + throw new Error("This tool is unavailable for the current request."); + }); + const directory = resolve(config.gateway.projectDirectory ?? ctx.location.directory); + if ( + config.gateway.enabled && + config.gateway.mode === "plugin" && + resolve(ctx.location.directory) === directory + ) { + return registerGatewayOwner( + resolve(gatewayHome()), + { + directory, + async start() { + const { startGateway } = await import("./gateway/index.js"); + return startGateway({ + inkbox: runtime, + opencode: host, + config, + directory, + ownsProcess: false, + }); + }, + }, + () => console.error("[inkbox] gateway startup failed. Run inkbox-opencode doctor."), + ); + } + }, +}); diff --git a/tests/cli/autostart.test.ts b/tests/cli/autostart.test.ts index c5b46c2..34b3e34 100644 --- a/tests/cli/autostart.test.ts +++ b/tests/cli/autostart.test.ts @@ -14,6 +14,9 @@ import { systemdUnitPath, uninstallAutostart, } from "../../src/cli/autostart.js"; +import { hostMajor } from "../../src/cli/wrappers.js"; + +vi.mock("../../src/cli/wrappers.js", () => ({ hostMajor: vi.fn(() => 1) })); let home: string; let osHome: string; @@ -57,6 +60,12 @@ function deps(overrides: Partial = {}): AutostartDeps { } describe("installAutostart (systemd)", () => { + it("keeps the V2 shared service outside gateway stop handling", async () => { + vi.mocked(hostMajor).mockReturnValueOnce(2); + const { run } = fakeRunner(); + await installAutostart(deps({ run })); + expect(fs.readFileSync(systemdUnitPath(osHome), "utf8")).toContain("KillMode=process"); + }); it("writes the unit + env snapshot, enables, restarts, and lingers", async () => { const { run, calls } = fakeRunner(); const env = { @@ -75,6 +84,7 @@ describe("installAutostart (systemd)", () => { expect(unitText).toContain(`Environment=INKBOX_OPENCODE_ENV_FILE=${autostartEnvPath(home)}`); expect(unitText).toContain("WorkingDirectory=/work/agent"); expect(unitText).toContain("WantedBy=default.target"); + expect(unitText).toContain("KillMode=control-group"); const envFile = autostartEnvPath(home); const envText = fs.readFileSync(envFile, "utf-8"); diff --git a/tests/cli/setup.test.ts b/tests/cli/setup.test.ts index ecdd520..2c3fa40 100644 --- a/tests/cli/setup.test.ts +++ b/tests/cli/setup.test.ts @@ -1,7 +1,14 @@ -import { describe, expect, it } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import { runSetup } from "../../src/cli/setup.js"; +import { hostMajor } from "../../src/cli/wrappers.js"; import { defaultGatewayConfig, type ResolvedConfig } from "../../src/config.js"; +vi.mock("../../src/cli/wrappers.js", async (importOriginal) => ({ + ...(await importOriginal()), + hostMajor: vi.fn(() => 1), + incompatibleWrappers: vi.fn(() => []), +})); + function makeConfig(overrides?: Partial): ResolvedConfig { return { apiKey: "k", @@ -22,6 +29,12 @@ function render(config: ResolvedConfig): { code: number; out: string } { } describe("runSetup", () => { + it("shows the V2 entrypoint and shared-service guidance", () => { + vi.mocked(hostMajor).mockReturnValueOnce(2); + const { out } = render(makeConfig()); + expect(out).toContain("@inkbox/opencode-plugin/v2"); + expect(out).toContain("shared OpenCode service"); + }); it("prints the required env var names and the wrapper snippet", () => { const { code, out } = render(makeConfig()); expect(code).toBe(0); diff --git a/tests/host-v2.test.ts b/tests/host-v2.test.ts new file mode 100644 index 0000000..1aaad12 --- /dev/null +++ b/tests/host-v2.test.ts @@ -0,0 +1,308 @@ +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import { configuredPackages, installWrapper, wrapperSource } from "../src/cli/wrappers.js"; +import { resolveConfig } from "../src/config.js"; +import { createStateStore } from "../src/gateway/state.js"; +import { registerGatewayOwner } from "../src/host/gateway-owner.js"; +import { bindHostState } from "../src/host/state.js"; +import { createV2Host } from "../src/host/v2-client.js"; +import type { V2Client } from "../src/host/v2-connection.js"; +import { normalizeHistory, projectHistory, readHistory } from "../src/host/v2-history.js"; +import { configurationKey } from "../src/host/v2-rpc.js"; + +const dirs: string[] = []; +const temporary = () => { + const dir = mkdtempSync(join(tmpdir(), "inkbox-host-")); + dirs.push(dir); + return dir; +}; +afterEach(() => { + for (const dir of dirs.splice(0)) rmSync(dir, { recursive: true, force: true }); +}); + +describe("V2 history", () => { + const user = (id: string) => ({ id, type: "user", text: id, time: { created: 1 } }); + const assistant = (id: string, finish: string) => ({ + id, + type: "assistant", + finish, + content: [{ type: "text", text: id }], + time: { created: 2 }, + }); + it("retains turn linkage through compaction and tool continuations", () => { + const history = normalizeHistory( + [ + user("msg_first"), + assistant("msg_tool", "tool-calls"), + { id: "msg_checkpoint", type: "compaction" }, + assistant("msg_answer", "stop"), + user("msg_second"), + assistant("msg_second_answer", "stop"), + ] as any, + "ses_test", + ); + expect(history.filter((m) => m.info.role === "assistant").map((m) => m.info.parentID)).toEqual([ + "msg_first", + "msg_first", + "msg_second", + ]); + }); + it("does not assign ambiguous merged input to either source", () => { + const history = normalizeHistory( + [user("msg_first"), user("msg_second"), assistant("msg_answer", "stop")] as any, + "ses_test", + ); + expect(history.at(-1)?.info.parentID).toBeUndefined(); + }); + it.each(["synthetic", "shell", "skill", "compaction"])( + "does not attribute a later %s run to an earlier input", + (type) => { + const projection = projectHistory( + [ + user("msg_first"), + assistant("msg_answer", "stop"), + { id: "msg_notice", type }, + assistant("msg_background", "stop"), + ] as any, + "ses_test", + ); + expect(projection.messages.at(-1)?.info.parentID).toBeUndefined(); + expect(projection.parentID).toBeUndefined(); + }, + ); + it("reports a failed merged run to its waiting inputs without assigning its output", () => { + const history = normalizeHistory( + [ + user("msg_first"), + user("msg_second"), + { id: "msg_idle", type: "idle", outcome: "failed" }, + ] as any, + "ses_test", + ); + expect(history.filter((m) => m.info.error).map((m) => m.info.parentID)).toEqual([ + "msg_first", + "msg_second", + ]); + }); + it("reads beyond the first history page without repeating order with a cursor", async () => { + const list = vi + .fn() + .mockResolvedValueOnce({ data: [user("msg_old")], cursor: { next: "page2" } }) + .mockResolvedValueOnce({ data: [assistant("msg_answer", "stop")], cursor: { next: "end" } }) + .mockResolvedValueOnce({ data: [], cursor: {} }); + expect(await readHistory({ message: { list } } as any, "ses_test")).toHaveLength(2); + expect(list.mock.calls[1][0]).toEqual({ sessionID: "ses_test", limit: 200, cursor: "page2" }); + }); +}); + +describe("host migration", () => { + it("retains unfinished records when switching hosts", () => { + const state = createStateStore(temporary()); + state.setSession("contact", "ses_old"); + state.saveTurn({ id: "turn", sessionID: "ses_old", state: "submitted" } as any); + expect(() => bindHostState(state, 2, "new")).toThrow("another OpenCode host"); + expect(state.getSession("contact")).toBe("ses_old"); + expect(state.getTurn("turn")?.state).toBe("submitted"); + }); + it("archives completed session mappings on upgrade and downgrade", () => { + const state = createStateStore(temporary()); + state.setSession("contact", "ses_old"); + bindHostState(state, 2, "new"); + expect(state.read().previousHostSessions).toEqual({ contact: "ses_old" }); + state.setSession("contact", "ses_new"); + bindHostState(state, 1); + expect(state.getSession("contact")).toBeUndefined(); + expect(state.read().previousHostSessions).toEqual({ contact: "ses_new" }); + }); + it("allows finished captures and settled failures to move hosts", () => { + const state = createStateStore(temporary()); + state.setSession("contact", "ses_old"); + state.saveTurn({ + id: "capture", + sessionID: "ses_old", + state: "completed", + deliver: false, + } as any); + state.saveTurn({ + id: "failed", + sessionID: "ses_old", + state: "failed", + executionFenced: true, + } as any); + bindHostState(state, 2, "new"); + expect(state.getTurn("capture")?.state).toBe("completed"); + expect(state.getTurn("failed")?.state).toBe("failed"); + }); + it("retains unconfirmed submissions even without a session mapping", () => { + const state = createStateStore(temporary()); + state.saveTurn({ + id: "failed", + sessionID: "ses_old", + state: "failed", + uncertainStage: "submission", + } as any); + expect(() => bindHostState(state, 2, "new")).toThrow("another OpenCode host"); + }); +}); + +describe("wrapper upgrades", () => { + it("migrates the generated wrapper in both directions", () => { + const file = join(temporary(), "inkbox.ts"); + writeFileSync(file, wrapperSource(1)); + expect(installWrapper(file, 2)).toBe("updated"); + expect(readFileSync(file, "utf8")).toBe(wrapperSource(2)); + expect(installWrapper(file, 2)).toBe("unchanged"); + expect(installWrapper(file, 1)).toBe("updated"); + expect(readFileSync(file, "utf8")).toBe(wrapperSource(1)); + }); + it("preserves custom options rather than replacing the wrapper", () => { + const file = join(temporary(), "inkbox.ts"); + const source = wrapperSource(1).replace("inkbox_place_call", "inkbox_credentials_get_secret"); + writeFileSync(file, source); + expect(installWrapper(file, 2)).toBe("custom"); + expect(readFileSync(file, "utf8")).toBe(source); + }); + it("does not rewrite custom comments or whitespace inside option values", () => { + const file = join(temporary(), "inkbox.ts"); + for (const source of [ + `// Custom setup notes\n${wrapperSource(1)}`, + wrapperSource(1).replace("inkbox_place_call", "inkbox_ place_call"), + ]) { + writeFileSync(file, source); + expect(installWrapper(file, 2)).toBe("custom"); + expect(readFileSync(file, "utf8")).toBe(source); + } + }); + it("recognizes explicit package entries in JSONC without matching comments", () => { + const dir = temporary(); + const file = join(dir, "opencode.jsonc"); + writeFileSync(file, '{ // @inkbox/opencode-plugin\n "plugins": [] }'); + expect(configuredPackages(dir)).toEqual([]); + writeFileSync( + file, + '{ "plugins": [{ "package": "@inkbox/opencode-plugin/v2", "options": {} }], }', + ); + expect(configuredPackages(dir)).toEqual([file]); + }); +}); + +describe("V2 cancellation", () => { + it("cancels admitted input as well as active execution", async () => { + const cancel = vi.fn().mockResolvedValue(undefined); + const interrupt = vi.fn().mockResolvedValue({ interrupted: false }); + const native = { + session: { + inbox: { + list: vi + .fn() + .mockResolvedValueOnce([{ id: "msg_pending" }]) + .mockResolvedValueOnce([]), + cancel, + }, + interrupt, + wait: vi.fn().mockResolvedValue(undefined), + active: vi.fn().mockResolvedValue({}), + }, + } as unknown as V2Client; + const host = createV2Host({ get: async () => native, invalidate() {} }, resolveConfig({}, {})); + expect((await host.session.abort({ path: { id: "ses_test" } })).data).toBe(true); + expect(cancel).toHaveBeenCalledWith({ sessionID: "ses_test", inboxID: "msg_pending" }); + expect(interrupt).toHaveBeenCalledWith({ sessionID: "ses_test", resume: false }); + }); + it("does not acknowledge stop while admitted input remains", async () => { + const native = { + session: { + inbox: { list: vi.fn().mockResolvedValue([{ id: "msg_pending" }]), cancel: vi.fn() }, + interrupt: vi.fn(), + wait: vi.fn(), + active: vi.fn().mockResolvedValue({}), + }, + } as unknown as V2Client; + const host = createV2Host({ get: async () => native, invalidate() {} }, resolveConfig({}, {})); + await expect(host.session.abort({ path: { id: "ses_test" } })).rejects.toThrow( + "has not confirmed", + ); + }); +}); + +describe("gateway location ownership", () => { + it("keeps one gateway and transfers ownership only after close", async () => { + const closeFirst = vi.fn().mockResolvedValue(undefined); + const closeSecond = vi.fn().mockResolvedValue(undefined); + const first = { directory: "first", start: vi.fn().mockResolvedValue({ close: closeFirst }) }; + const second = { + directory: "second", + start: vi.fn().mockResolvedValue({ close: closeSecond }), + }; + const report = vi.fn(); + const key = temporary(); + const disposeFirst = registerGatewayOwner(key, first, report); + await vi.waitFor(() => expect(first.start).toHaveBeenCalledTimes(1)); + const disposeSecond = registerGatewayOwner(key, second, report); + await disposeSecond(); + expect(closeFirst).not.toHaveBeenCalled(); + expect(second.start).not.toHaveBeenCalled(); + const disposeReplacement = registerGatewayOwner(key, second, report); + await disposeFirst(); + expect(closeFirst).toHaveBeenCalledTimes(1); + expect(second.start).toHaveBeenCalledTimes(1); + await disposeReplacement(); + expect(closeSecond).toHaveBeenCalledTimes(1); + expect(report).not.toHaveBeenCalled(); + }); + it("waits for startup before disposing its handle", async () => { + let started!: (handle: { close(): Promise }) => void; + const start = vi.fn( + () => + new Promise<{ close(): Promise }>((resolve) => { + started = resolve; + }), + ); + const close = vi.fn().mockResolvedValue(undefined); + const dispose = registerGatewayOwner(temporary(), { directory: "first", start }, vi.fn()); + await vi.waitFor(() => expect(start).toHaveBeenCalledTimes(1)); + const disposed = dispose(); + expect(close).not.toHaveBeenCalled(); + started({ close }); + await disposed; + expect(close).toHaveBeenCalledTimes(1); + }); +}); + +describe("V2 reconnection", () => { + it("refreshes the connection for a failed read", async () => { + const get = vi + .fn() + .mockRejectedValueOnce({ reason: "Transport" }) + .mockResolvedValue({ id: "ses_test", location: { directory: "/fixture" } }); + const invalidate = vi.fn(); + const native = { session: { get } } as unknown as V2Client; + const host = createV2Host({ get: async () => native, invalidate }, resolveConfig({}, {})); + expect((await host.session.get({ path: { id: "ses_test" } })).data?.id).toBe("ses_test"); + expect(get).toHaveBeenCalledTimes(2); + expect(invalidate).toHaveBeenCalledTimes(1); + }); + it("does not retry an ambiguous prompt submission", async () => { + const config = resolveConfig({}, {}); + const prompt = vi.fn().mockRejectedValue({ reason: "Transport" }); + const native = { + rpc: () => ({ + hello: async () => ({ configuration: configurationKey(config) }), + prepare: async () => ({ ready: true }), + }), + agent: { list: async () => ({ data: [{ id: "build", mode: "primary", hidden: false }] }) }, + session: { prompt, switchAgent: vi.fn() }, + } as unknown as V2Client; + const host = createV2Host({ get: async () => native, invalidate() {} }, config); + await expect( + host.session.promptAsync({ + path: { id: "ses_test" }, + query: { directory: "/fixture" }, + body: { parts: [{ type: "text", text: "Synthetic question" }] }, + }), + ).rejects.toMatchObject({ code: "ECONNRESET" }); + expect(prompt).toHaveBeenCalledTimes(1); + }); +}); From accaf8332ccc7942bc08fb7c14d211b8fb5db242 Mon Sep 17 00:00:00 2001 From: alex-w-99 Date: Wed, 14 Oct 2026 18:50:43 +0000 Subject: [PATCH 2/4] Select the channel agent during noninteractive bootstrap --- src/cli/bootstrap.ts | 1 + tests/cli/bootstrap.test.ts | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/cli/bootstrap.ts b/src/cli/bootstrap.ts index cd4b664..ba12ae8 100644 --- a/src/cli/bootstrap.ts +++ b/src/cli/bootstrap.ts @@ -224,6 +224,7 @@ export async function bootstrap(options: BootstrapOptions): Promise { process.env.INKBOX_OPENCODE_ENV_FILE = path.join(temp, ".env"); delete process.env.INKBOX_IDENTITY; delete process.env.INKBOX_SIGNING_KEY; + delete process.env.INKBOX_GATEWAY_AGENT; }); afterEach(() => { @@ -34,6 +35,7 @@ afterEach(() => { delete process.env.INKBOX_API_KEY; delete process.env.INKBOX_IDENTITY; delete process.env.INKBOX_SIGNING_KEY; + delete process.env.INKBOX_GATEWAY_AGENT; vi.clearAllMocks(); }); @@ -94,6 +96,19 @@ describe("bootstrap", () => { expect(saved).toContain("INKBOX_API_KEY=agent-secret"); expect(saved).toContain("INKBOX_SIGNING_KEY=signing-secret"); expect(saved).toContain(`INKBOX_PROJECT_DIR=${temp}`); + expect(saved).toContain("INKBOX_GATEWAY_AGENT=inkbox-channel"); + expect(process.env.INKBOX_GATEWAY_AGENT).toBe("inkbox-channel"); + }); + + it("preserves a configured gateway agent", async () => { + fakeWorld(); + process.env.INKBOX_GATEWAY_AGENT = "custom-channel"; + const result = await bootstrap({ identity: "helper", apiKey: "agent-secret" }); + expect(result.status).toBe("configured"); + expect(fs.readFileSync(path.join(temp, ".env"), "utf8")).toContain( + "INKBOX_GATEWAY_AGENT=custom-channel", + ); + expect(process.env.INKBOX_GATEWAY_AGENT).toBe("custom-channel"); }); it("requires explicit rotation when the remote signing key is not local", async () => { From fc00a5fb7bc8ec76ce80ee4c2cc2d4612eda1bcb Mon Sep 17 00:00:00 2001 From: alex-w-99 Date: Wed, 14 Oct 2026 19:31:18 +0000 Subject: [PATCH 3/4] Prevent duplicate threaded email replies and fix V2 diagnostics --- CHANGELOG.md | 3 ++ README.md | 3 ++ src/gateway/reply.ts | 20 ++++++++++++ src/gateway/sessions.ts | 15 +++++++-- src/host/v2-history.ts | 8 ++++- src/v2.ts | 19 +++++++----- tests/gateway/reply.test.ts | 36 ++++++++++++++++++++- tests/gateway/sessions.test.ts | 57 ++++++++++++++++++++++++++++++++++ tests/host-v2.test.ts | 49 +++++++++++++++++++++++++++++ 9 files changed, 197 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e84128b..3da836c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## 0.2.18 (unreleased) +- Prevents duplicate automatic email replies after a successful threaded tool send, including recovered turns. +- Fixes V2 diagnostics before the gateway's first startup and preserves completed tool output from V2 history. +- Selects the channel agent during noninteractive setup while preserving custom agent choices. - Adds an OpenCode V2 entrypoint while retaining the existing V1 entrypoint and minimum supported version. - Adds V2 gateway sessions, shared-service connections, and native tool approvals. - Updates generated wrappers for the installed OpenCode version and preserves custom wrappers for manual migration. diff --git a/README.md b/README.md index ca349ab..5b839de 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,9 @@ For an explicitly selected V2 server, set `OPENCODE_SERVER_URL` and apply to standalone V2 servers. Stopping the gateway leaves an attached shared service running. +Set `INKBOX_GATEWAY_MODEL=provider/model` to pin the gateway model. An interactive +session's model selection does not configure the default for new gateway sessions. + The gateway and plugin must share the same accessible `INKBOX_OPENCODE_HOME` directory (default `~/.inkbox-opencode`), including when using an explicit server. diff --git a/src/gateway/reply.ts b/src/gateway/reply.ts index bd24586..a7a81b0 100644 --- a/src/gateway/reply.ts +++ b/src/gateway/reply.ts @@ -8,6 +8,26 @@ import type { GatewayLogger, ReplyTarget } from "./types.js"; export class ReplyPreparationError extends Error {} +export function sentEmailReply(target: ReplyTarget | undefined, parts: any[]): boolean { + if (target?.channel !== "email") return false; + const parents = [target.messageId, target.rfcMessageId].filter(Boolean); + return parts.some((part) => { + const state = part.state; + return ( + part.type === "tool" && + part.tool === "inkbox_send_email" && + state?.status === "completed" && + typeof state.output === "string" && + state.output.startsWith("Sent email id=") && + parents.includes(state.input?.inReplyToMessageId) && + Array.isArray(state.input?.to) && + state.input.to.some((to: string) => + [target.to, target.sender].some((address) => address?.toLowerCase() === to.toLowerCase()), + ) + ); + }); +} + export interface ReplyResult { delivered: boolean; reason?: "silent" | "empty" | "sent"; diff --git a/src/gateway/sessions.ts b/src/gateway/sessions.ts index bab8125..621c063 100644 --- a/src/gateway/sessions.ts +++ b/src/gateway/sessions.ts @@ -32,8 +32,8 @@ import { clearHostedSmsCapture, getHostedCall, } from "./hosted-call-registry.js"; -import { buildIdentitySystem, frameCapture, frameInbound } from "./prompts.js"; -import { prepareReply, ReplyPreparationError } from "./reply.js"; +import { buildIdentitySystem, frameCapture, frameInbound, SILENT } from "./prompts.js"; +import { prepareReply, ReplyPreparationError, sentEmailReply } from "./reply.js"; import { companionWakes, mentionsAgent, sameAuthor } from "./response-policy.js"; import { slackSenderAllowed } from "./slack.js"; import type { DurableHostedCapture, DurableTurn, StateStore } from "./state.js"; @@ -898,7 +898,16 @@ export function createSessionManager( // A completed assistant step can still be followed by tool work or a // model retry. Keep the turn's side-effect guards until the final answer. const finish = last?.info?.finish; - if (finish && finish !== "tool-calls" && finish !== "unknown") return extractText(last); + if (finish && finish !== "tool-calls" && finish !== "unknown") { + if ( + sentEmailReply( + turn.replyTarget, + assistants.flatMap((message) => message.parts ?? []), + ) + ) + return SILENT; + return extractText(last); + } await delay(POLL_MS); } throw new HostedCaptureDeferredError(); diff --git a/src/host/v2-history.ts b/src/host/v2-history.ts index d2cbeed..8e6e145 100644 --- a/src/host/v2-history.ts +++ b/src/host/v2-history.ts @@ -90,7 +90,13 @@ export function projectHistory(messages: SessionMessageInfo[], sessionID: string ...state, status: state.status === "streaming" ? "pending" : state.status, input: state.input, - output: state.result?.content ?? state.output, + output: + state.content + ?.filter((content: any) => content.type === "text") + .map((content: any) => content.text) + .join("\n") ?? + state.result?.content ?? + state.output, }, }; }); diff --git a/src/v2.ts b/src/v2.ts index 981f13e..d616474 100644 --- a/src/v2.ts +++ b/src/v2.ts @@ -105,14 +105,17 @@ export default Plugin.define({ }); const key = (sessionID: string, messageID: string) => `turn/${sessionID}/${messageID}`; await ctx.rpc.register(InkboxHost, { - hello: async () => ({ - version: 1 as const, - instance, - installation, - stateProbe: createStateStore().read().hostProbe as string | undefined, - configuration: configurationKey(config), - tools: (await ctx.tool.list()).map((t) => t.id), - }), + hello: async () => { + const stateProbe = createStateStore().read().hostProbe; + return { + version: 1 as const, + instance, + installation, + ...(typeof stateProbe === "string" ? { stateProbe } : {}), + configuration: configurationKey(config), + tools: (await ctx.tool.list()).map((t) => t.id), + }; + }, prepare: async (policy) => { const session = await ctx.session.get({ sessionID: policy.sessionID }); if (session.location.directory !== ctx.location.directory) diff --git a/tests/gateway/reply.test.ts b/tests/gateway/reply.test.ts index e874188..4c1e4fa 100644 --- a/tests/gateway/reply.test.ts +++ b/tests/gateway/reply.test.ts @@ -2,10 +2,44 @@ // email subject threading, markdown stripping, and length caps. import { describe, expect, it, vi } from "vitest"; import type { InkboxRuntime } from "../../src/client.js"; -import { deliverReply, prepareReply } from "../../src/gateway/reply.js"; +import { deliverReply, prepareReply, sentEmailReply } from "../../src/gateway/reply.js"; import type { GatewayLogger, ReplyTarget } from "../../src/gateway/types.js"; import { IMESSAGE_MAX_TEXT_CHARS, SMS_MAX_TEXT_CHARS } from "../../src/limits.js"; +describe("tool-delivered email replies", () => { + const target: ReplyTarget = { + channel: "email", + to: "sender@example.com", + messageId: "inbound-id", + rfcMessageId: "", + }; + const part = (input = {}, state = {}) => ({ + type: "tool", + tool: "inkbox_send_email", + state: { + status: "completed", + output: "Sent email id=sent-id", + input: { to: ["sender@example.com"], inReplyToMessageId: "inbound-id", ...input }, + ...state, + }, + }); + it("recognizes both supported parent identifiers", () => { + expect(sentEmailReply(target, [part()])).toBe(true); + expect(sentEmailReply(target, [part({ inReplyToMessageId: target.rfcMessageId })])).toBe(true); + }); + it("does not suppress replies after failed, unrelated, or unfinished sends", () => { + for (const item of [ + part({}, { status: "error" }), + part({}, { status: "running" }), + part({}, { output: "Error sending email" }), + part({ to: ["other@example.com"] }), + part({ inReplyToMessageId: "another-message" }), + ]) + expect(sentEmailReply(target, [item])).toBe(false); + expect(sentEmailReply({ ...target, channel: "sms" }, [part()])).toBe(false); + }); +}); + it("completes Slack reads before the send checkpoint and never reclassifies a read as an uncertain send", async () => { const route = { identityId: "identity", diff --git a/tests/gateway/sessions.test.ts b/tests/gateway/sessions.test.ts index d8bea7b..9784d67 100644 --- a/tests/gateway/sessions.test.ts +++ b/tests/gateway/sessions.test.ts @@ -204,6 +204,63 @@ function prepareHostedCall(dir: string): void { } describe("durable async turns", () => { + it("does not resend a tool-delivered email after recovering the completed turn", async () => { + const d = makeManager(); + const now = Date.now(); + const turn: DurableTurn = { + id: "email-turn", + messageID: "msg_email", + chatKey: "email", + sessionID: "sess-email", + state: "submitted", + kind: "normal", + text: "hello", + deliver: true, + replyTarget: { channel: "email", to: "sender@example.com", messageId: "inbound-id" }, + createdAt: now, + updatedAt: now, + }; + d.state.saveTurn(turn); + d.messages.set("sess-email", [ + { info: { id: turn.messageID, role: "user" }, parts: [] }, + { + info: { + id: "assistant-send", + role: "assistant", + parentID: turn.messageID, + finish: "tool-calls", + }, + parts: [ + { + type: "tool", + tool: "inkbox_send_email", + state: { + status: "completed", + input: { to: ["sender@example.com"], inReplyToMessageId: "inbound-id" }, + output: "Sent email id=sent-id", + }, + }, + ], + }, + { + info: { + id: "assistant-summary", + role: "assistant", + parentID: turn.messageID, + finish: "stop", + }, + parts: [{ type: "text", text: "Replied in-thread." }], + }, + ]); + try { + await d.mgr.catchUp(); + await vi.waitFor(() => expect(d.state.getTurn(turn.id)?.state).toBe("delivered")); + expect(d.identity.replyAllEmail).not.toHaveBeenCalled(); + expect(d.opencode.session.promptAsync).not.toHaveBeenCalled(); + } finally { + await d.mgr.close(); + } + }); it("isolates an anomalous native history floor from unrelated prompt and receipt IDs", async () => { vi.resetModules(); const { createSessionManager: isolated } = await import("../../src/gateway/sessions.js"); diff --git a/tests/host-v2.test.ts b/tests/host-v2.test.ts index 1aaad12..a97e65d 100644 --- a/tests/host-v2.test.ts +++ b/tests/host-v2.test.ts @@ -11,6 +11,7 @@ import { createV2Host } from "../src/host/v2-client.js"; import type { V2Client } from "../src/host/v2-connection.js"; import { normalizeHistory, projectHistory, readHistory } from "../src/host/v2-history.js"; import { configurationKey } from "../src/host/v2-rpc.js"; +import plugin from "../src/v2.js"; const dirs: string[] = []; const temporary = () => { @@ -23,6 +24,54 @@ afterEach(() => { }); describe("V2 history", () => { + it("returns JSON-safe hello output before the first gateway startup", async () => { + vi.stubEnv("INKBOX_OPENCODE_HOME", temporary()); + let methods: any; + try { + await plugin.setup({ + options: {}, + location: { directory: "/fixture" }, + storage: { get: async () => undefined, set: async () => {} }, + tool: { transform: async () => {}, list: async () => [], hook: async () => {} }, + session: { hook: async () => {} }, + rpc: { + register: async (_contract: unknown, handlers: unknown) => { + methods = handlers; + }, + }, + } as any); + const result = await methods.hello(); + expect(result).not.toHaveProperty("stateProbe"); + expect(JSON.parse(JSON.stringify(result))).toEqual(result); + } finally { + vi.unstubAllEnvs(); + } + }); + it("preserves completed tool text for delivery detection", () => { + const messages = normalizeHistory( + [ + { + id: "msg_answer", + type: "assistant", + time: { created: 2 }, + content: [ + { + type: "tool", + id: "call_send", + name: "inkbox_send_email", + state: { + status: "completed", + input: {}, + content: [{ type: "text", text: "Sent email id=sent" }], + }, + }, + ], + }, + ] as any, + "ses_test", + ); + expect(messages[0].parts[0].state.output).toBe("Sent email id=sent"); + }); const user = (id: string) => ({ id, type: "user", text: id, time: { created: 1 } }); const assistant = (id: string, finish: string) => ({ id, From b52725e2b640bdcc337c1c8543dc0248fcc78a4b Mon Sep 17 00:00:00 2001 From: alex-w-99 Date: Wed, 14 Oct 2026 19:50:15 +0000 Subject: [PATCH 4/4] Update Inkbox SDK to 0.7.19 --- CHANGELOG.md | 1 + package-lock.json | 8 ++++---- package.json | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3da836c..76563fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 0.2.18 (unreleased) +- Updates the SDK to 0.7.19. - Prevents duplicate automatic email replies after a successful threaded tool send, including recovered turns. - Fixes V2 diagnostics before the gateway's first startup and preserves completed tool output from V2 history. - Selects the channel agent during noninteractive setup while preserving custom agent choices. diff --git a/package-lock.json b/package-lock.json index fd57801..17b8999 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.2.18", "license": "MIT", "dependencies": { - "@inkbox/sdk": "^0.7.15", + "@inkbox/sdk": "0.7.19", "@opencode-ai/sdk": ">=1.17.18 <1.19.0", "@opencode/client": "2.0.26", "@opencode/plugin": "2.0.26", @@ -1203,9 +1203,9 @@ } }, "node_modules/@inkbox/sdk": { - "version": "0.7.15", - "resolved": "https://registry.npmjs.org/@inkbox/sdk/-/sdk-0.7.15.tgz", - "integrity": "sha512-V4UXcbhGt+FcrWKjNZPO6+WufN0NZXnwfkeRyp9YGwqkq6QR8GJWK3E8V7F7MnGABXsPU3HzI40KxYGJMMqnBw==", + "version": "0.7.19", + "resolved": "https://registry.npmjs.org/@inkbox/sdk/-/sdk-0.7.19.tgz", + "integrity": "sha512-/s+nhlZUGwXQDQ0aaMTjh6iKQEkC+mwM5/DrpU4yTKtBWh2eys91M7Ewzh1rUqHbgvaXUbzGLwVXypRId6ouPg==", "license": "MIT", "dependencies": { "@peculiar/x509": "^2.0.0", diff --git a/package.json b/package.json index 598033b..5d7753b 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "opencode": ">=1.15.0 <2.0.0 || >=2.0.26 <2.1.0" }, "dependencies": { - "@inkbox/sdk": "^0.7.15", + "@inkbox/sdk": "0.7.19", "@opencode-ai/sdk": ">=1.17.18 <1.19.0", "@opencode/client": "2.0.26", "@opencode/plugin": "2.0.26",