diff --git a/.env.example b/.env.example index bbc219f86e..eeef109d39 100644 --- a/.env.example +++ b/.env.example @@ -25,6 +25,17 @@ VITE_ENABLE_GOOGLE_CLOUD_SUBMITTER= # Auth VITE_REQUIRE_AUTHORIZATION= +# OktaSSO (optional SSO gate used by the VS Code extension; leave blank to disable) +VITE_OKTASSO_ENABLED= +VITE_OKTASSO_OAUTH_ENDPOINT_BASE= +VITE_OKTASSO_TOKEN_EXCHANGE_URL= +VITE_OKTASSO_CLIENT_ID= +VITE_OKTASSO_TOKEN_COOKIE_NAME= +VITE_OKTASSO_SCOPES=profile email openid +VITE_OKTASSO_REDIRECT_URI=http://localhost:3001/auth +VITE_OKTASSO_CALLBACK_PORT=3001 +VITE_OKTASSO_REFRESH_AFTER_MS=3300000 + # Github VITE_GITHUB_CLIENT_ID= VITE_GITHUB_PAGES= diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 81b2557a46..6fab01245d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,3 +109,29 @@ jobs: - name: Run knip run: pnpm run knip + + vscode-extension: + name: VS Code extension + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + + - name: Install dependencies + run: pnpm --dir vscode-extension install --frozen-lockfile + + - name: Run type checking + run: pnpm --dir vscode-extension typecheck + + - name: Run linting + run: pnpm --dir vscode-extension lint + + - name: Run tests + run: pnpm --dir vscode-extension test diff --git a/.gitignore b/.gitignore index 266654873c..04696944c9 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,7 @@ playwright-report #local files .local/ .planning/ -.codanna/index/ \ No newline at end of file +.codanna/index/ + +# generated extension artifacts (built into public/ during `vite build`) +public/extension/ diff --git a/Dockerfile b/Dockerfile index e16990f323..db4986662b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,10 @@ WORKDIR /app # Enable Corepack so the pinned pnpm version from package.json is used. RUN corepack enable +# git is needed to resolve the VS Code extension's `@tangent/*` GitHub deps, +# which `vite build` installs and bundles into a .vsix under public/extension/. +RUN apk add --no-cache git + # Leverage caching by installing dependencies first COPY package.json pnpm-lock.yaml ./ RUN pnpm install --frozen-lockfile diff --git a/README.md b/README.md index 3bbf8dfb91..394aba2dc0 100644 --- a/README.md +++ b/README.md @@ -79,13 +79,14 @@ Add these to a `.env` file at the root of `tangle-ui`. Vite reads these values at development server startup or build time. Restart the development server after changes, or rebuild the frontend for deployment. -| Variable | Required | Description | -| ------------------------------ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `VITE_BACKEND_API_URL` | For backend features | URL of your Tangle backend (e.g. `http://127.0.0.1:8000`). | -| `VITE_REMOTE_PIPELINES` | No | Opt in to remote pipeline storage with `true` on a backend that supports saved pipelines. Unset or `false` keeps new pipelines, imports, and copies in browser storage. Without a configured backend, storage stays local even when enabled. | -| `VITE_ARTIFACT_RETENTION_DAYS` | No | Number of days artifacts are stored by your backend. When set, the UI shows artifact expiry dates and warns when artifacts may no longer be available. | -| `VITE_BUGSNAG_API_KEY` | No | Bugsnag API key. Required alongside `VITE_TANGLE_ENV` to enable error reporting. | -| `VITE_TANGLE_ENV` | No | Release stage passed to Bugsnag (e.g. `development`, `staging`, `production`). | +| Variable | Required | Description | +| ------------------------------ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `VITE_BACKEND_API_URL` | For backend features | URL of your Tangle backend (e.g. `http://127.0.0.1:8000`). | +| `VITE_REMOTE_PIPELINES` | No | Opt in to remote pipeline storage with `true` on a backend that supports saved pipelines. Unset or `false` keeps new pipelines, imports, and copies in browser storage. Without a configured backend, storage stays local even when enabled. | +| `VITE_ARTIFACT_RETENTION_DAYS` | No | Number of days artifacts are stored by your backend. When set, the UI shows artifact expiry dates and warns when artifacts may no longer be available. | +| `VITE_BUGSNAG_API_KEY` | No | Bugsnag API key. Required alongside `VITE_TANGLE_ENV` to enable error reporting. | +| `VITE_TANGLE_ENV` | No | Release stage passed to Bugsnag (e.g. `development`, `staging`, `production`). | +| `VITE_OKTASSO_*` | No | Optional SSO gate for the VS Code extension. When `VITE_OKTASSO_OAUTH_ENDPOINT_BASE`, `VITE_OKTASSO_TOKEN_EXCHANGE_URL`, `VITE_OKTASSO_CLIENT_ID`, and `VITE_OKTASSO_TOKEN_COOKIE_NAME` are set, the extension runs an Okta PKCE flow and attaches the resulting session cookie to its API calls. Leave blank to disable; set `VITE_OKTASSO_ENABLED=false` as a kill switch. See `.env.example` for all keys and defaults. | The remote-pipeline flag controls new remote storage, migration, and dashboard listing. Turning it off leaves existing remote links, favorites, and recently diff --git a/knip.json b/knip.json index a619334e40..7f90916fcd 100644 --- a/knip.json +++ b/knip.json @@ -12,7 +12,8 @@ "src/api/**", "src/components/ui/**", "src/config/preSubmitHooks.ts", - "src/components/shared/BetaFeatureWrapper/BetaFeatureWrapper.tsx" + "src/components/shared/BetaFeatureWrapper/BetaFeatureWrapper.tsx", + "vscode-extension/**" ], "ignoreDependencies": [ "@tanstack/react-query-devtools", diff --git a/package.json b/package.json index 7a9040482b..a78c7d2fe6 100644 --- a/package.json +++ b/package.json @@ -85,9 +85,9 @@ "@radix-ui/react-tooltip": "^1.4.1", "@reactour/tour": "3.8.0", "@tailwindcss/vite": "^4.3.3", - "@tangent/embed-react": "github:TangleML/tangent-shell#1998f551d910e229d5108045b552b5549635cd58&path:packages/embed-react", - "@tangent/remote-subagent": "github:TangleML/tangent-shell#1998f551d910e229d5108045b552b5549635cd58&path:packages/remote-subagent", - "@tangent/shared": "github:TangleML/tangent-shell#1998f551d910e229d5108045b552b5549635cd58&path:packages/shared", + "@tangent/embed-react": "github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/embed-react", + "@tangent/remote-subagent": "github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/remote-subagent", + "@tangent/shared": "github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/shared", "@tanstack/history": "1.162.5", "@tanstack/react-query": "^5.104.5", "@tanstack/react-query-devtools": "^5.104.5", @@ -179,7 +179,7 @@ }, "pnpm": { "overrides": { - "@tangent/shared": "github:TangleML/tangent-shell#1998f551d910e229d5108045b552b5549635cd58&path:packages/shared" + "@tangent/shared": "github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/shared" } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5c13cf5980..77e3b3f0b8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,7 +5,7 @@ settings: excludeLinksFromLockfile: false overrides: - '@tangent/shared': github:TangleML/tangent-shell#1998f551d910e229d5108045b552b5549635cd58&path:packages/shared + '@tangent/shared': github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/shared importers: @@ -37,10 +37,10 @@ importers: version: 4.7.0(monaco-editor@0.54.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) '@openai/agents': specifier: ^0.21.0 - version: 0.21.0(undici@8.12.2)(ws@8.22.0)(zod@4.6.7) + version: 0.21.0(undici@8.12.3)(ws@8.22.0)(zod@4.6.7) '@openai/agents-core': specifier: ^0.21.0 - version: 0.21.0(undici@8.12.2)(ws@8.22.0)(zod@4.6.7) + version: 0.21.0(undici@8.12.3)(ws@8.22.0)(zod@4.6.7) '@radix-ui/react-alert-dialog': specifier: ^1.2.1 version: 1.2.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) @@ -96,14 +96,14 @@ importers: specifier: ^4.3.3 version: 4.3.3(vite@8.3.5(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) '@tangent/embed-react': - specifier: github:TangleML/tangent-shell#1998f551d910e229d5108045b552b5549635cd58&path:packages/embed-react - version: https://codeload.github.com/TangleML/tangent-shell/tar.gz/1998f551d910e229d5108045b552b5549635cd58#path:packages/embed-react(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + specifier: github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/embed-react + version: https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/embed-react(react-dom@19.3.1(react@19.3.1))(react@19.3.1) '@tangent/remote-subagent': - specifier: github:TangleML/tangent-shell#1998f551d910e229d5108045b552b5549635cd58&path:packages/remote-subagent - version: https://codeload.github.com/TangleML/tangent-shell/tar.gz/1998f551d910e229d5108045b552b5549635cd58#path:packages/remote-subagent + specifier: github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/remote-subagent + version: https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/remote-subagent '@tangent/shared': - specifier: github:TangleML/tangent-shell#1998f551d910e229d5108045b552b5549635cd58&path:packages/shared - version: https://codeload.github.com/TangleML/tangent-shell/tar.gz/1998f551d910e229d5108045b552b5549635cd58#path:packages/shared + specifier: github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/shared + version: https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/shared '@tanstack/history': specifier: 1.162.5 version: 1.162.5 @@ -190,7 +190,7 @@ importers: version: 6.0.2 openai: specifier: ^7.43.0 - version: 7.43.0(undici@8.12.2)(ws@8.22.0)(zod@4.6.7) + version: 7.43.0(undici@8.12.3)(ws@8.22.0)(zod@4.6.7) papaparse: specifier: ^5.7.0 version: 5.7.0 @@ -226,7 +226,7 @@ importers: version: 10.1.0(@types/react@19.3.0)(react@19.3.1) react-scan: specifier: ^0.5.7 - version: 0.5.7(@opentelemetry/core@2.12.0(@opentelemetry/api@1.10.0))(esbuild@0.28.2)(eslint@9.39.4(jiti@2.7.0))(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(rollup@4.61.1) + version: 0.5.7(@opentelemetry/core@2.12.0(@opentelemetry/api@1.9.1))(esbuild@0.28.2)(eslint@9.39.4(jiti@2.7.0))(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(rollup@4.61.1) react-toastify: specifier: ^11.1.0 version: 11.1.0(react-dom@19.3.1(react@19.3.1))(react@19.3.1) @@ -356,30 +356,30 @@ importers: version: 2.0.2(vite@8.3.5(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) vitest: specifier: ^4.1.10 - version: 4.1.10(@opentelemetry/api@1.10.0)(@types/node@26.6.5)(@vitest/coverage-v8@5.0.5)(jsdom@30.1.3)(vite@8.3.5(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) + version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.6.5)(@vitest/coverage-v8@5.0.5)(jsdom@30.1.3)(vite@8.3.5(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) packages: '@adobe/css-tools@4.5.0': resolution: {integrity: sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==} - '@asamuzakjp/css-color@7.1.7': - resolution: {integrity: sha512-Q7ioeear07HwixdIitIn9KS1LZi97CEDFCSjPNm9v+y8KNbsu9q4Lrd4zUQW4faED+E7qxvfeVnbNRW46wFKuw==} + '@asamuzakjp/css-color@7.1.5': + resolution: {integrity: sha512-jCpAvj8apa7SKHZYogk0rbXB3Kb0hLFrpkWF+Ap+vBA5rirItnpv9NKyAyEuDuFizvl0qwnj6JWR70RGJ0rOlw==, tarball: https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-7.1.5.tgz} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} '@asamuzakjp/dom-selector@10.0.4': - resolution: {integrity: sha512-A6f1nKy99I6tyoK82U6kBHvQQZmdHmTmPzJYPY/0eaOW0r8/qPmEOwjGHs+hL/vCgOwQTupu4LT1XtXNUuDruA==} + resolution: {integrity: sha512-A6f1nKy99I6tyoK82U6kBHvQQZmdHmTmPzJYPY/0eaOW0r8/qPmEOwjGHs+hL/vCgOwQTupu4LT1XtXNUuDruA==, tarball: https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-10.0.4.tgz} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} '@astrojs/compiler@4.0.0': - resolution: {integrity: sha512-eouss7G8ygdZqHuke033VMcVw5HTZUu+PXd/h06DGDUg/jt5btPYPqh66ENWw/mU78rBrf/oeC4oqoBwMtDMNA==} + resolution: {integrity: sha512-eouss7G8ygdZqHuke033VMcVw5HTZUu+PXd/h06DGDUg/jt5btPYPqh66ENWw/mU78rBrf/oeC4oqoBwMtDMNA==, tarball: https://registry.npmjs.org/@astrojs/compiler/-/compiler-4.0.0.tgz} '@babel/code-frame@7.29.7': resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} '@babel/code-frame@8.0.6': - resolution: {integrity: sha512-uMHhvmDmwFiLuiozaoRYTZHqMFuu3fem/X9JY78lohAuLoh8LSGGC8JqUzo2cVuBsBxkjMZNk56CE7ock8Xxkw==} + resolution: {integrity: sha512-uMHhvmDmwFiLuiozaoRYTZHqMFuu3fem/X9JY78lohAuLoh8LSGGC8JqUzo2cVuBsBxkjMZNk56CE7ock8Xxkw==, tarball: https://registry.npmjs.org/@babel/code-frame/-/code-frame-8.0.6.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/compat-data@7.29.7': @@ -387,23 +387,23 @@ packages: engines: {node: '>=6.9.0'} '@babel/compat-data@8.0.5': - resolution: {integrity: sha512-YLsYoQMvL8l8WrGpN3Zj7O1wK5LEBN+cQtux7BcuHyxIXve724XG+zuJ1n3U1cUweRtTzQOA4IHbuQw3N34SZw==} + resolution: {integrity: sha512-YLsYoQMvL8l8WrGpN3Zj7O1wK5LEBN+cQtux7BcuHyxIXve724XG+zuJ1n3U1cUweRtTzQOA4IHbuQw3N34SZw==, tarball: https://registry.npmjs.org/@babel/compat-data/-/compat-data-8.0.5.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/core@7.29.7': - resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==, tarball: https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/core@8.0.9': resolution: {integrity: sha512-wXtyKlpR/pXf/u0vmib+OBakQaIzLglLcRoKpWxCHW/whqq7YfQYPD0IAAl9H/evi40MyLIjYYdvb6N+9DI2yA==} engines: {node: ^22.18.0 || >=24.11.0} - '@babel/generator@7.29.8': - resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} + '@babel/generator@7.29.7': + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} engines: {node: '>=6.9.0'} '@babel/generator@8.0.6': - resolution: {integrity: sha512-/pcanjdRsonCexCuxgGD8fKnBLLzk2i9uM4kmcmGyyev2R5YDs+rafMYZ89RvaGY1mW8T7C5au0TyYaEeHBGng==} + resolution: {integrity: sha512-/pcanjdRsonCexCuxgGD8fKnBLLzk2i9uM4kmcmGyyev2R5YDs+rafMYZ89RvaGY1mW8T7C5au0TyYaEeHBGng==, tarball: https://registry.npmjs.org/@babel/generator/-/generator-8.0.6.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-annotate-as-pure@7.29.7': @@ -411,7 +411,7 @@ packages: engines: {node: '>=6.9.0'} '@babel/helper-annotate-as-pure@8.0.0': - resolution: {integrity: sha512-NSpMkMsvvZqzThJ0p1B02cbtA2ObEyfBvq950bmNkyxsxvcxwhvvCB036rKhlEnuBBo30bOrk13u3FzlKSoRrw==} + resolution: {integrity: sha512-NSpMkMsvvZqzThJ0p1B02cbtA2ObEyfBvq950bmNkyxsxvcxwhvvCB036rKhlEnuBBo30bOrk13u3FzlKSoRrw==, tarball: https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-8.0.0.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-compilation-targets@7.29.7': @@ -419,7 +419,7 @@ packages: engines: {node: '>=6.9.0'} '@babel/helper-compilation-targets@8.0.7': - resolution: {integrity: sha512-ilAZCdOOlOVA7dQbaFNw2f/59p6GW33NxTopc3DvEE3efp3ZXTrGX/yZScaKw8/vd9bq31Ag0EcXiLw32bAgJg==} + resolution: {integrity: sha512-ilAZCdOOlOVA7dQbaFNw2f/59p6GW33NxTopc3DvEE3efp3ZXTrGX/yZScaKw8/vd9bq31Ag0EcXiLw32bAgJg==, tarball: https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-8.0.7.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-create-class-features-plugin@7.29.7': @@ -429,7 +429,7 @@ packages: '@babel/core': ^7.0.0 '@babel/helper-create-class-features-plugin@8.0.7': - resolution: {integrity: sha512-7ug3KrOhNVPPxFFLTel0/pMzimKzU/vTlNJKVL/dKguCpmNJZjjibyFbWg+Qs8QHmiIZPNuZ2UNnDGdHWGEpOg==} + resolution: {integrity: sha512-7ug3KrOhNVPPxFFLTel0/pMzimKzU/vTlNJKVL/dKguCpmNJZjjibyFbWg+Qs8QHmiIZPNuZ2UNnDGdHWGEpOg==, tarball: https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-8.0.7.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 @@ -439,7 +439,7 @@ packages: engines: {node: '>=6.9.0'} '@babel/helper-globals@8.0.6': - resolution: {integrity: sha512-OdnA/N52LhzFpeUuVz5YGuSWjc7TPHiFmVYZbPlSgExocO2GwbxVpSni9MVs5wwW+wZ4kPuwBEBN9pIPDYz1mA==} + resolution: {integrity: sha512-OdnA/N52LhzFpeUuVz5YGuSWjc7TPHiFmVYZbPlSgExocO2GwbxVpSni9MVs5wwW+wZ4kPuwBEBN9pIPDYz1mA==, tarball: https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-8.0.6.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-member-expression-to-functions@7.29.7': @@ -447,7 +447,7 @@ packages: engines: {node: '>=6.9.0'} '@babel/helper-member-expression-to-functions@8.0.5': - resolution: {integrity: sha512-GLe05QD98BkNFTkjaqeqF9QSRoHLKrrB4tpoplyQuuPrQJ72rtmkM4wvqJLX/sjZPqkbK5MUYSsuUTqdJoOVjA==} + resolution: {integrity: sha512-GLe05QD98BkNFTkjaqeqF9QSRoHLKrrB4tpoplyQuuPrQJ72rtmkM4wvqJLX/sjZPqkbK5MUYSsuUTqdJoOVjA==, tarball: https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-8.0.5.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-module-imports@7.29.7': @@ -465,7 +465,7 @@ packages: engines: {node: '>=6.9.0'} '@babel/helper-optimise-call-expression@8.0.0': - resolution: {integrity: sha512-3W6satvtPuCUkUx63S2jMoW9EQNYkADgs1HTfufmL7gCmAulHMKupA/12WNz4A0GMMFn/YnWWwqOT9IZrJHQjg==} + resolution: {integrity: sha512-3W6satvtPuCUkUx63S2jMoW9EQNYkADgs1HTfufmL7gCmAulHMKupA/12WNz4A0GMMFn/YnWWwqOT9IZrJHQjg==, tarball: https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-8.0.0.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-plugin-utils@7.29.7': @@ -473,7 +473,7 @@ packages: engines: {node: '>=6.9.0'} '@babel/helper-plugin-utils@8.0.1': - resolution: {integrity: sha512-3PKFgjTyPlhFhorfP+SjKQxLViIL++zWjFOO4hGriYU+Bsm983DxEM1JmDRJVWXV0O9npu+xXRqz7Pbd3mh70g==} + resolution: {integrity: sha512-3PKFgjTyPlhFhorfP+SjKQxLViIL++zWjFOO4hGriYU+Bsm983DxEM1JmDRJVWXV0O9npu+xXRqz7Pbd3mh70g==, tarball: https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 @@ -485,7 +485,7 @@ packages: '@babel/core': ^7.0.0 '@babel/helper-replace-supers@8.0.1': - resolution: {integrity: sha512-B1SZADIcy3tmH8CmWvj4SHi/oAPom4UL3uknTc2QRNsPVLFk/sPnZvQL/8kj7Y5omvjMqie0vklvs6XM4OLW5Q==} + resolution: {integrity: sha512-B1SZADIcy3tmH8CmWvj4SHi/oAPom4UL3uknTc2QRNsPVLFk/sPnZvQL/8kj7Y5omvjMqie0vklvs6XM4OLW5Q==, tarball: https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 @@ -495,7 +495,7 @@ packages: engines: {node: '>=6.9.0'} '@babel/helper-skip-transparent-expression-wrappers@8.0.0': - resolution: {integrity: sha512-xmCA9kP3IhySsqhzwIdWGlDN/1A4cCKNBO/uwZx/3YzmDoMePwno2Q5/Bq0q+tYaKbeF940YiKV/kaW8Mzvpjw==} + resolution: {integrity: sha512-xmCA9kP3IhySsqhzwIdWGlDN/1A4cCKNBO/uwZx/3YzmDoMePwno2Q5/Bq0q+tYaKbeF940YiKV/kaW8Mzvpjw==, tarball: https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-8.0.0.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-string-parser@7.27.1': @@ -523,7 +523,7 @@ packages: engines: {node: '>=6.9.0'} '@babel/helper-validator-identifier@8.0.6': - resolution: {integrity: sha512-HYx0YmuchRxRSpnjvcNffnPS8DCauKNmnumIuHG3jnQAVewRA7kfJGwF13ayEpSZs7s4R/oBH4eppuXcAQd5JQ==} + resolution: {integrity: sha512-HYx0YmuchRxRSpnjvcNffnPS8DCauKNmnumIuHG3jnQAVewRA7kfJGwF13ayEpSZs7s4R/oBH4eppuXcAQd5JQ==, tarball: https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.6.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-validator-option@7.29.7': @@ -531,11 +531,11 @@ packages: engines: {node: '>=6.9.0'} '@babel/helper-validator-option@8.0.0': - resolution: {integrity: sha512-U4Dybxh4WESWHt5XhBeExi4DrY0/DNK1aHpQbsrQXCUbFHuMweT0TpLEWKvaraV2Y6fS+ZXunsZ8zIuZIgvF2Q==} + resolution: {integrity: sha512-U4Dybxh4WESWHt5XhBeExi4DrY0/DNK1aHpQbsrQXCUbFHuMweT0TpLEWKvaraV2Y6fS+ZXunsZ8zIuZIgvF2Q==, tarball: https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-8.0.0.tgz} engines: {node: ^22.18.0 || >=24.11.0} - '@babel/helpers@7.29.10': - resolution: {integrity: sha512-EMCx3AGZ3WfAl6ht7AyQoPm2Lh+BGYEMdc4Q6Hf7uSCDOZFuLq44qNwL+iaim8VlyYhRAo4X43+weadK8ZB4Yg==} + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==, tarball: https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz} engines: {node: '>=6.9.0'} '@babel/helpers@8.0.8': @@ -558,7 +558,7 @@ packages: hasBin: true '@babel/plugin-proposal-decorators@8.0.2': - resolution: {integrity: sha512-+C6O6KKXU7BBq1GNaIkFJxrALUVGRcr+WeWm4OcuRl3h+l/CmNfcTLMrT2Lm3uvGBimBH/8pEBRrXJFLoO67Gg==} + resolution: {integrity: sha512-+C6O6KKXU7BBq1GNaIkFJxrALUVGRcr+WeWm4OcuRl3h+l/CmNfcTLMrT2Lm3uvGBimBH/8pEBRrXJFLoO67Gg==, tarball: https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-8.0.2.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 @@ -571,7 +571,7 @@ packages: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-decorators@8.0.1': - resolution: {integrity: sha512-NI+0S/6MvR6GlcQFwjDZ+WIc2qvG6TXN534lYs9llNldwW4b7Dh6KTtk030FA0xWdYGs4t1lWo+OEWN8wGB+Nw==} + resolution: {integrity: sha512-NI+0S/6MvR6GlcQFwjDZ+WIc2qvG6TXN534lYs9llNldwW4b7Dh6KTtk030FA0xWdYGs4t1lWo+OEWN8wGB+Nw==, tarball: https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-8.0.1.tgz} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 @@ -585,15 +585,15 @@ packages: engines: {node: '>=6.9.0'} '@babel/template@8.0.0': - resolution: {integrity: sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==} + resolution: {integrity: sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==, tarball: https://registry.npmjs.org/@babel/template/-/template-8.0.0.tgz} engines: {node: ^22.18.0 || >=24.11.0} - '@babel/traverse@7.29.10': - resolution: {integrity: sha512-B3lYAs2nkKskbgQ1nfy8Ma9ao9Q1rntTAQHrzzyDuj+TSO+X3le6PfWwqDvodsb+OumE10eMFfuMCTQSQ5iuIQ==} + '@babel/traverse@7.29.7': + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} engines: {node: '>=6.9.0'} '@babel/traverse@8.0.7': - resolution: {integrity: sha512-bVV0brinsTWgc6ptSIoQo3GbITtl0VZAqEUJbHHcyfDPEu+q/fEvlYEa+6H85HoK8IdksKBFtKJbJFZ6Kff+zg==} + resolution: {integrity: sha512-bVV0brinsTWgc6ptSIoQo3GbITtl0VZAqEUJbHHcyfDPEu+q/fEvlYEa+6H85HoK8IdksKBFtKJbJFZ6Kff+zg==, tarball: https://registry.npmjs.org/@babel/traverse/-/traverse-8.0.7.tgz} engines: {node: ^22.18.0 || >=24.11.0} '@babel/types@7.29.0': @@ -613,11 +613,11 @@ packages: engines: {node: ^22.18.0 || >=24.11.0} '@bcoe/v8-coverage@1.0.2': - resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==, tarball: https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz} engines: {node: '>=18'} '@bramus/specificity@2.4.3': - resolution: {integrity: sha512-0GEz8qJWokkUVYQYRukmkSHIFNoBH8QTreeHrAFbvz1XFYc3Y+puhMs8PQ5x8GNugiC2l5jvDBXKLbBIByeSBw==} + resolution: {integrity: sha512-0GEz8qJWokkUVYQYRukmkSHIFNoBH8QTreeHrAFbvz1XFYc3Y+puhMs8PQ5x8GNugiC2l5jvDBXKLbBIByeSBw==, tarball: https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.3.tgz} hasBin: true '@bugsnag/browser@9.1.0': @@ -651,36 +651,36 @@ packages: hasBin: true '@corvu/utils@0.4.2': - resolution: {integrity: sha512-Ox2kYyxy7NoXdKWdHeDEjZxClwzO4SKM8plAaVwmAJPxHMqA0rLOoAsa+hBDwRLpctf+ZRnAd/ykguuJidnaTA==} + resolution: {integrity: sha512-Ox2kYyxy7NoXdKWdHeDEjZxClwzO4SKM8plAaVwmAJPxHMqA0rLOoAsa+hBDwRLpctf+ZRnAd/ykguuJidnaTA==, tarball: https://registry.npmjs.org/@corvu/utils/-/utils-0.4.2.tgz} peerDependencies: solid-js: ^1.8 '@csstools/color-helpers@6.1.2': - resolution: {integrity: sha512-grhRy3OKmniaAEKXMjua5z/EODX0MSqBGjunw8+j/3HQjOnahs2AGhvEOIYVUWcU6ScApbhLhVrQTX8XqrMrow==} + resolution: {integrity: sha512-grhRy3OKmniaAEKXMjua5z/EODX0MSqBGjunw8+j/3HQjOnahs2AGhvEOIYVUWcU6ScApbhLhVrQTX8XqrMrow==, tarball: https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.2.tgz} engines: {node: '>=20.19.0'} '@csstools/css-calc@3.4.3': - resolution: {integrity: sha512-iex20d8CHVkyvg6B7UKV7uHnI2Bqo9g+EFfT9E0y+GvTvhZ/DwONJ+9aKb1dlqm0ZiGsL5RXjp0fCoJYnkeDjA==} + resolution: {integrity: sha512-iex20d8CHVkyvg6B7UKV7uHnI2Bqo9g+EFfT9E0y+GvTvhZ/DwONJ+9aKb1dlqm0ZiGsL5RXjp0fCoJYnkeDjA==, tarball: https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.4.3.tgz} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.2 '@csstools/css-tokenizer': ^4.0.2 '@csstools/css-color-parser@4.2.6': - resolution: {integrity: sha512-iiPQ3iRWwnJkeEn6RIu6SJPr7hYrLz6XZ9s/QZl+2/LI5KQVjpl2fdmDSZKuD4xP6GMmMPgHFFXg6k1Wkz0Trg==} + resolution: {integrity: sha512-iiPQ3iRWwnJkeEn6RIu6SJPr7hYrLz6XZ9s/QZl+2/LI5KQVjpl2fdmDSZKuD4xP6GMmMPgHFFXg6k1Wkz0Trg==, tarball: https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.2.6.tgz} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.2 '@csstools/css-tokenizer': ^4.0.2 '@csstools/css-parser-algorithms@4.0.2': - resolution: {integrity: sha512-40cSKyMvK+tq4qz6Awrlye2WGuOKt3FwPgtGg6KTfbHOWNw+Rk1rzbAtZnZ6IBhsY491HLRnDXwoyBAijmmILA==} + resolution: {integrity: sha512-40cSKyMvK+tq4qz6Awrlye2WGuOKt3FwPgtGg6KTfbHOWNw+Rk1rzbAtZnZ6IBhsY491HLRnDXwoyBAijmmILA==, tarball: https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.2.tgz} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-tokenizer': ^4.0.2 '@csstools/css-syntax-patches-for-csstree@1.1.15': - resolution: {integrity: sha512-J0u7HkVl2nzSlhsiTOp4AmwcUQ3D+mGEEKfBy/7To5/y7F2OHwyLrXfrhR0SMgr4p5Lo+eaMVSeai24zUcBIxA==} + resolution: {integrity: sha512-J0u7HkVl2nzSlhsiTOp4AmwcUQ3D+mGEEKfBy/7To5/y7F2OHwyLrXfrhR0SMgr4p5Lo+eaMVSeai24zUcBIxA==, tarball: https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.15.tgz} peerDependencies: css-tree: ^3.2.1 peerDependenciesMeta: @@ -688,7 +688,7 @@ packages: optional: true '@csstools/css-tokenizer@4.0.2': - resolution: {integrity: sha512-OoKoR0f76dCY666JlcbhmVTs2drYj1GUXZTYTcbUgJjh9Nv41aFfZ21bPQTERm5+L5cBDo466NltB2lplS5GBw==} + resolution: {integrity: sha512-OoKoR0f76dCY666JlcbhmVTs2drYj1GUXZTYTcbUgJjh9Nv41aFfZ21bPQTERm5+L5cBDo466NltB2lplS5GBw==, tarball: https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.2.tgz} engines: {node: '>=20.19.0'} '@dagrejs/dagre@3.1.2': @@ -717,172 +717,172 @@ packages: react: '>=16.8.0' '@emnapi/core@1.11.2': - resolution: {integrity: sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==} + resolution: {integrity: sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==, tarball: https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz} '@emnapi/runtime@1.11.2': - resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==} + resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==, tarball: https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz} '@emnapi/wasi-threads@1.2.2': - resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==, tarball: https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz} '@esbuild/aix-ppc64@0.28.2': - resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} + resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==, tarball: https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz} engines: {node: '>=18'} cpu: [ppc64] os: [aix] '@esbuild/android-arm64@0.28.2': - resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} + resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==, tarball: https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz} engines: {node: '>=18'} cpu: [arm64] os: [android] '@esbuild/android-arm@0.28.2': - resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} + resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==, tarball: https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz} engines: {node: '>=18'} cpu: [arm] os: [android] '@esbuild/android-x64@0.28.2': - resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} + resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==, tarball: https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz} engines: {node: '>=18'} cpu: [x64] os: [android] '@esbuild/darwin-arm64@0.28.2': - resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} + resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==, tarball: https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz} engines: {node: '>=18'} cpu: [arm64] os: [darwin] '@esbuild/darwin-x64@0.28.2': - resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} + resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==, tarball: https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz} engines: {node: '>=18'} cpu: [x64] os: [darwin] '@esbuild/freebsd-arm64@0.28.2': - resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} + resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==, tarball: https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] '@esbuild/freebsd-x64@0.28.2': - resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} + resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==, tarball: https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz} engines: {node: '>=18'} cpu: [x64] os: [freebsd] '@esbuild/linux-arm64@0.28.2': - resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} + resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==, tarball: https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz} engines: {node: '>=18'} cpu: [arm64] os: [linux] '@esbuild/linux-arm@0.28.2': - resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} + resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==, tarball: https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz} engines: {node: '>=18'} cpu: [arm] os: [linux] '@esbuild/linux-ia32@0.28.2': - resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} + resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==, tarball: https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz} engines: {node: '>=18'} cpu: [ia32] os: [linux] '@esbuild/linux-loong64@0.28.2': - resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} + resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==, tarball: https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz} engines: {node: '>=18'} cpu: [loong64] os: [linux] '@esbuild/linux-mips64el@0.28.2': - resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} + resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==, tarball: https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz} engines: {node: '>=18'} cpu: [mips64el] os: [linux] '@esbuild/linux-ppc64@0.28.2': - resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} + resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==, tarball: https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz} engines: {node: '>=18'} cpu: [ppc64] os: [linux] '@esbuild/linux-riscv64@0.28.2': - resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} + resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==, tarball: https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz} engines: {node: '>=18'} cpu: [riscv64] os: [linux] '@esbuild/linux-s390x@0.28.2': - resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} + resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==, tarball: https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz} engines: {node: '>=18'} cpu: [s390x] os: [linux] '@esbuild/linux-x64@0.28.2': - resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} + resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==, tarball: https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz} engines: {node: '>=18'} cpu: [x64] os: [linux] '@esbuild/netbsd-arm64@0.28.2': - resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} + resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==, tarball: https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] '@esbuild/netbsd-x64@0.28.2': - resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} + resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==, tarball: https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz} engines: {node: '>=18'} cpu: [x64] os: [netbsd] '@esbuild/openbsd-arm64@0.28.2': - resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} + resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==, tarball: https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] '@esbuild/openbsd-x64@0.28.2': - resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} + resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==, tarball: https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz} engines: {node: '>=18'} cpu: [x64] os: [openbsd] '@esbuild/openharmony-arm64@0.28.2': - resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} + resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==, tarball: https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] '@esbuild/sunos-x64@0.28.2': - resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} + resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==, tarball: https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz} engines: {node: '>=18'} cpu: [x64] os: [sunos] '@esbuild/win32-arm64@0.28.2': - resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} + resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==, tarball: https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz} engines: {node: '>=18'} cpu: [arm64] os: [win32] '@esbuild/win32-ia32@0.28.2': - resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} + resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==, tarball: https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz} engines: {node: '>=18'} cpu: [ia32] os: [win32] '@esbuild/win32-x64@0.28.2': - resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} + resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==, tarball: https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz} engines: {node: '>=18'} cpu: [x64] os: [win32] '@eslint-community/eslint-utils@4.10.1': - resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} + resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==, tarball: https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -914,11 +914,11 @@ packages: engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/js@9.39.4': - resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} + resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==, tarball: https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/js@9.39.5': - resolution: {integrity: sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==} + resolution: {integrity: sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==, tarball: https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.7': @@ -930,7 +930,7 @@ packages: engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@exodus/bytes@1.16.0': - resolution: {integrity: sha512-IcpW84uEn3N7ETtNZMlxKhfl6Pec8rUNGOTBtWbK1FKhJxIFAptZyVrvVRVBimAJxJCgc3PxepxkdWWG4DVzfA==} + resolution: {integrity: sha512-IcpW84uEn3N7ETtNZMlxKhfl6Pec8rUNGOTBtWbK1FKhJxIFAptZyVrvVRVBimAJxJCgc3PxepxkdWWG4DVzfA==, tarball: https://registry.npmjs.org/@exodus/bytes/-/bytes-1.16.0.tgz} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: '@noble/hashes': ^1.8.0 || ^2.0.0 @@ -1025,16 +1025,16 @@ packages: resolution: {integrity: sha512-cxG5vyIdx3gUb/OQD/FDruQQcGNzF2aKTXWJ6LzlAC1kh97YZQSRsF3hL274xlTDJ1nWGJGRtknlW6ZpU3Ch+A==} '@iarna/toml@2.2.5': - resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==} + resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==, tarball: https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz} '@internationalized/number@3.6.9': - resolution: {integrity: sha512-GgWy2caO/+hV9sT/oCTWGHvbjmJMPT9kkXzoKRLnkEq7adghhR7UprhvY9sXrKfrp3NcDfhF6QQHXR1KfQbB8g==} + resolution: {integrity: sha512-GgWy2caO/+hV9sT/oCTWGHvbjmJMPT9kkXzoKRLnkEq7adghhR7UprhvY9sXrKfrp3NcDfhF6QQHXR1KfQbB8g==, tarball: https://registry.npmjs.org/@internationalized/number/-/number-3.6.9.tgz} '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} '@jridgewell/gen-mapping@0.4.0-beta.0': - resolution: {integrity: sha512-JdGNkbE4GlNPYQhM0L95fBQr7ctLZJ276QXQLTad4t1oSdnnCI3fDq9DW3BqYAWv8Wc3+HS+4Gsii1oPMCfz1w==} + resolution: {integrity: sha512-JdGNkbE4GlNPYQhM0L95fBQr7ctLZJ276QXQLTad4t1oSdnnCI3fDq9DW3BqYAWv8Wc3+HS+4Gsii1oPMCfz1w==, tarball: https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.4.0-beta.0.tgz} '@jridgewell/remapping@2.3.5': resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} @@ -1047,7 +1047,7 @@ packages: resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} '@jridgewell/sourcemap-codec@1.6.0': - resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==, tarball: https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz} '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} @@ -1056,12 +1056,12 @@ packages: resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} '@kobalte/core@0.13.14': - resolution: {integrity: sha512-SBVB1lnvYbYt2x3t7fG0lo5fGLEbHQ6PII3NDPva8vD5e2v53On84ZVQiuIk0XI/55R4GQhden7Yem+R/XR8uQ==} + resolution: {integrity: sha512-SBVB1lnvYbYt2x3t7fG0lo5fGLEbHQ6PII3NDPva8vD5e2v53On84ZVQiuIk0XI/55R4GQhden7Yem+R/XR8uQ==, tarball: https://registry.npmjs.org/@kobalte/core/-/core-0.13.14.tgz} peerDependencies: solid-js: ^1.9.8 '@kobalte/utils@0.9.2': - resolution: {integrity: sha512-jRVXr+zsVHxzDXRoh+CDeXzvCsFJ6uiHhqqNQ26Cw9ZsZ3D6nqPUBt1gGVtj2ZPmRL3a9Uk1v8D1aJ8/I12Dow==} + resolution: {integrity: sha512-jRVXr+zsVHxzDXRoh+CDeXzvCsFJ6uiHhqqNQ26Cw9ZsZ3D6nqPUBt1gGVtj2ZPmRL3a9Uk1v8D1aJ8/I12Dow==, tarball: https://registry.npmjs.org/@kobalte/utils/-/utils-0.9.2.tgz} peerDependencies: solid-js: ^1.8.8 @@ -1070,11 +1070,11 @@ packages: engines: {node: '>=8'} '@modelcontextprotocol/client@2.3.1': - resolution: {integrity: sha512-mIGZXpHsjnZ6lD+gD/WCMpR5k8yVQQ8nNFH1N0Srf7AvnwTUMYD6pvTY8ng2+He5FfV7YMZLmrjL7cLa/cc3dQ==} + resolution: {integrity: sha512-mIGZXpHsjnZ6lD+gD/WCMpR5k8yVQQ8nNFH1N0Srf7AvnwTUMYD6pvTY8ng2+He5FfV7YMZLmrjL7cLa/cc3dQ==, tarball: https://registry.npmjs.org/@modelcontextprotocol/client/-/client-2.3.1.tgz} engines: {node: '>=20'} '@modelcontextprotocol/core@2.3.1': - resolution: {integrity: sha512-laVmIhPGpWi7rG9q+0UigblRN0Hws/Yjlb6qLFtVqryXVLkc4h+yN8z731POD2LnAGQlFe7/tXhUm7Bi0YB91g==} + resolution: {integrity: sha512-laVmIhPGpWi7rG9q+0UigblRN0Hws/Yjlb6qLFtVqryXVLkc4h+yN8z731POD2LnAGQlFe7/tXhUm7Bi0YB91g==, tarball: https://registry.npmjs.org/@modelcontextprotocol/core/-/core-2.3.1.tgz} engines: {node: '>=20'} '@monaco-editor/loader@1.7.0': @@ -1088,7 +1088,7 @@ packages: react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 '@napi-rs/wasm-runtime@1.2.5': - resolution: {integrity: sha512-HshSvXzmBxNzqejd3k/KemgQ3hdREUYFRRrgxOZ+gPljDIsO2SDZrVZMd4Z/HsxudQbE62OVyVFE8z9BCurfjA==} + resolution: {integrity: sha512-HshSvXzmBxNzqejd3k/KemgQ3hdREUYFRRrgxOZ+gPljDIsO2SDZrVZMd4Z/HsxudQbE62OVyVFE8z9BCurfjA==, tarball: https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.5.tgz} engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} peerDependencies: '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.4 @@ -1107,7 +1107,7 @@ packages: engines: {node: '>= 8'} '@openai/agents-core@0.21.0': - resolution: {integrity: sha512-P2jJhc85xrKpe9GUOHAVfPHqK52my49fD1CI0LnfYEvBUwZxJXQI6lgSwby9nWhvQECveM9OvBUglAXuArsHXw==} + resolution: {integrity: sha512-P2jJhc85xrKpe9GUOHAVfPHqK52my49fD1CI0LnfYEvBUwZxJXQI6lgSwby9nWhvQECveM9OvBUglAXuArsHXw==, tarball: https://registry.npmjs.org/@openai/agents-core/-/agents-core-0.21.0.tgz} peerDependencies: zod: ^4.0.0 peerDependenciesMeta: @@ -1115,540 +1115,540 @@ packages: optional: true '@openai/agents-openai@0.21.0': - resolution: {integrity: sha512-Y1u1ek6FwKx8j+T7gRYcm4kKyN0irqsyZm0XUDZ+g+8s15tu4EhNwNJ92zOHGx7Vjkr2RugWBxTeNspHjw/lQw==} + resolution: {integrity: sha512-Y1u1ek6FwKx8j+T7gRYcm4kKyN0irqsyZm0XUDZ+g+8s15tu4EhNwNJ92zOHGx7Vjkr2RugWBxTeNspHjw/lQw==, tarball: https://registry.npmjs.org/@openai/agents-openai/-/agents-openai-0.21.0.tgz} peerDependencies: zod: ^4.0.0 '@openai/agents-realtime@0.21.0': - resolution: {integrity: sha512-nI0jGkSdivlXzS7HXAB1EVplaeSyoAvfM2ofHheJDJdshs3UQl3x405nmTflKWye+wQrqemK4uxL06NIhgvjHg==} + resolution: {integrity: sha512-nI0jGkSdivlXzS7HXAB1EVplaeSyoAvfM2ofHheJDJdshs3UQl3x405nmTflKWye+wQrqemK4uxL06NIhgvjHg==, tarball: https://registry.npmjs.org/@openai/agents-realtime/-/agents-realtime-0.21.0.tgz} peerDependencies: zod: ^4.0.0 '@openai/agents@0.21.0': - resolution: {integrity: sha512-NpJ74/dGbpYZiRYF474pCaBpxqh4A8+WHbZmaEZVQpRqieZAVH+MV4fesR9MtRGUZfpW5B91zuHzIilQp2iSxQ==} + resolution: {integrity: sha512-NpJ74/dGbpYZiRYF474pCaBpxqh4A8+WHbZmaEZVQpRqieZAVH+MV4fesR9MtRGUZfpW5B91zuHzIilQp2iSxQ==, tarball: https://registry.npmjs.org/@openai/agents/-/agents-0.21.0.tgz} peerDependencies: zod: ^4.0.0 '@opentelemetry/api-logs@0.220.0': - resolution: {integrity: sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w==} + resolution: {integrity: sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w==, tarball: https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.220.0.tgz} engines: {node: '>=8.0.0'} - '@opentelemetry/api@1.10.0': - resolution: {integrity: sha512-lMkyes8iHrG+mk6WH7X70MdCLI2RS0FwNBY1PyQ8UemaijE2m5mv5A0Niy3HtbYTFrgkUEQjFMXmSAk2hQDuPQ==} + '@opentelemetry/api@1.9.1': + resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==, tarball: https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz} engines: {node: '>=8.0.0'} '@opentelemetry/core@2.12.0': - resolution: {integrity: sha512-1HsSAuvT4/my0QrXWsyXFdWjKaPeCHsismKZgEEIb8NK13LP6DrMRlsUbvD/0F9mDWK3xDom9AMv3IscxgOnOQ==} + resolution: {integrity: sha512-1HsSAuvT4/my0QrXWsyXFdWjKaPeCHsismKZgEEIb8NK13LP6DrMRlsUbvD/0F9mDWK3xDom9AMv3IscxgOnOQ==, tarball: https://registry.npmjs.org/@opentelemetry/core/-/core-2.12.0.tgz} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' '@opentelemetry/instrumentation@0.220.0': - resolution: {integrity: sha512-xQx3E2WxP1mDvKzxLxX+CTCtNLa560YJZ3087qYHerl2YmiKpv7AH+dAy7vmx+eVrZ5BwhfWUAVoKOoxCNHcpw==} + resolution: {integrity: sha512-xQx3E2WxP1mDvKzxLxX+CTCtNLa560YJZ3087qYHerl2YmiKpv7AH+dAy7vmx+eVrZ5BwhfWUAVoKOoxCNHcpw==, tarball: https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.220.0.tgz} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 '@opentelemetry/resources@2.12.0': - resolution: {integrity: sha512-+rSRoOdln6NhXTVcBdHSlZxJDNzwxadeweK2ltZO3tVtfp6aklABc06xclDk6ovRR+EiGgIcSbh/6NgjFhnb3A==} + resolution: {integrity: sha512-+rSRoOdln6NhXTVcBdHSlZxJDNzwxadeweK2ltZO3tVtfp6aklABc06xclDk6ovRR+EiGgIcSbh/6NgjFhnb3A==, tarball: https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.12.0.tgz} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' '@opentelemetry/sdk-trace-base@2.12.0': - resolution: {integrity: sha512-JOERgZCxRM06FEJ5kEWxlSRChah6MmXDx3KwCruqEv/iEyLkTokCSemf2y1w8oIHtfrr8EVTpzebq/31vn2Cpg==} + resolution: {integrity: sha512-JOERgZCxRM06FEJ5kEWxlSRChah6MmXDx3KwCruqEv/iEyLkTokCSemf2y1w8oIHtfrr8EVTpzebq/31vn2Cpg==, tarball: https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.12.0.tgz} engines: {node: ^18.19.0 || >=20.6.0} deprecated: Use @opentelemetry/sdk-trace package peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' '@opentelemetry/sdk-trace@2.12.0': - resolution: {integrity: sha512-ImVuRQ6faOsTi1Wna88s2avgrrf/iDgXPigIeynkUhjstMuoQUDOJXgkez2j1mlU4btjXcXBGa+q7qKFYR3GsA==} + resolution: {integrity: sha512-ImVuRQ6faOsTi1Wna88s2avgrrf/iDgXPigIeynkUhjstMuoQUDOJXgkez2j1mlU4btjXcXBGa+q7qKFYR3GsA==, tarball: https://registry.npmjs.org/@opentelemetry/sdk-trace/-/sdk-trace-2.12.0.tgz} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' '@opentelemetry/semantic-conventions@1.43.0': - resolution: {integrity: sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==} + resolution: {integrity: sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==, tarball: https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.43.0.tgz} engines: {node: '>=14'} '@oxc-parser/binding-android-arm-eabi@0.148.0': - resolution: {integrity: sha512-pHASv9g5pASxb7akHERZNSkrEqPhFaUix98o7d9hbTpolnnFWl7UiRrcMhCsV1+iVO4/cJwKsbKRJTFNs2tdBQ==} + resolution: {integrity: sha512-pHASv9g5pASxb7akHERZNSkrEqPhFaUix98o7d9hbTpolnnFWl7UiRrcMhCsV1+iVO4/cJwKsbKRJTFNs2tdBQ==, tarball: https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.148.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] '@oxc-parser/binding-android-arm-eabi@0.155.0': - resolution: {integrity: sha512-A6xv7hDtnTm+xlZ7utn6rRQpRMJHRhVr3gjRGqlx0RUX84mNk0pAsy6YN/wWhuOTkaZhRrrzaLxf0NfbH77j2w==} + resolution: {integrity: sha512-A6xv7hDtnTm+xlZ7utn6rRQpRMJHRhVr3gjRGqlx0RUX84mNk0pAsy6YN/wWhuOTkaZhRrrzaLxf0NfbH77j2w==, tarball: https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.155.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] '@oxc-parser/binding-android-arm64@0.148.0': - resolution: {integrity: sha512-sg/6Ez0KdAygsu0POELux9wN1Po2CP93WY8eNl4DBKIGprsd4QSHBXOb471Pu9i2OCD5sLkISSb2agZEhVn2Zw==} + resolution: {integrity: sha512-sg/6Ez0KdAygsu0POELux9wN1Po2CP93WY8eNl4DBKIGprsd4QSHBXOb471Pu9i2OCD5sLkISSb2agZEhVn2Zw==, tarball: https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.148.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] '@oxc-parser/binding-android-arm64@0.155.0': - resolution: {integrity: sha512-T1wWwH9/JI5g7Z4kEQwDOwhToxdDhRyOTklH+uuNpJ16m3MZFmGl+l2rKc0NHQdbVRG2LTOBsNjpW3h1BWytPA==} + resolution: {integrity: sha512-T1wWwH9/JI5g7Z4kEQwDOwhToxdDhRyOTklH+uuNpJ16m3MZFmGl+l2rKc0NHQdbVRG2LTOBsNjpW3h1BWytPA==, tarball: https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.155.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] '@oxc-parser/binding-darwin-arm64@0.148.0': - resolution: {integrity: sha512-yiSJmzGUvCUaJT8X3j40gVcX+ckuHQMuiOtF8DvzTs5+JtB/7XuHFPp4M+vv5u+HlBtDUd4Ks5pyHpWz8mfnkg==} + resolution: {integrity: sha512-yiSJmzGUvCUaJT8X3j40gVcX+ckuHQMuiOtF8DvzTs5+JtB/7XuHFPp4M+vv5u+HlBtDUd4Ks5pyHpWz8mfnkg==, tarball: https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.148.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] '@oxc-parser/binding-darwin-arm64@0.155.0': - resolution: {integrity: sha512-ZoRB3qJxJ8DWA41jHja+YeB0lLfjBy4T+Kr15vnN1k65X3zbJt3rPZftEwN4Ju4tmA4K/T9dWWD7Z8ewQLnrVA==} + resolution: {integrity: sha512-ZoRB3qJxJ8DWA41jHja+YeB0lLfjBy4T+Kr15vnN1k65X3zbJt3rPZftEwN4Ju4tmA4K/T9dWWD7Z8ewQLnrVA==, tarball: https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.155.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] '@oxc-parser/binding-darwin-x64@0.148.0': - resolution: {integrity: sha512-6ZeklaamrMy4H2JmhvcJg6iip59tYILtuLaILxyAHT3l5FDxnI5ihVievAft5ZmAbqtlWHErOi1OpJK8gy1wcA==} + resolution: {integrity: sha512-6ZeklaamrMy4H2JmhvcJg6iip59tYILtuLaILxyAHT3l5FDxnI5ihVievAft5ZmAbqtlWHErOi1OpJK8gy1wcA==, tarball: https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.148.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] '@oxc-parser/binding-darwin-x64@0.155.0': - resolution: {integrity: sha512-vxtL86BtM+IgrpejA86nbTk5j0HKTIMeuaJ1fQciw6p9RfavPtIPjI7gN34owap0yG9txXBaAWpGdxokNwdqIw==} + resolution: {integrity: sha512-vxtL86BtM+IgrpejA86nbTk5j0HKTIMeuaJ1fQciw6p9RfavPtIPjI7gN34owap0yG9txXBaAWpGdxokNwdqIw==, tarball: https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.155.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] '@oxc-parser/binding-freebsd-x64@0.148.0': - resolution: {integrity: sha512-vFsPx+a/qFECPnz/H8nC6x6MDvnWscLTCo/5muojEF54ERUq1kdgbvnWo95YnkhjF9sTIcG/uDxQBh1gffaufQ==} + resolution: {integrity: sha512-vFsPx+a/qFECPnz/H8nC6x6MDvnWscLTCo/5muojEF54ERUq1kdgbvnWo95YnkhjF9sTIcG/uDxQBh1gffaufQ==, tarball: https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.148.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] '@oxc-parser/binding-freebsd-x64@0.155.0': - resolution: {integrity: sha512-tN6Ru5e0UVnWaIuiH8jEwnvxuxg7FvhWwSd3oe5oDhOD4ObsSr9/N6WfHLjilUWlPPj65ptwrSDH0Y7lzwTzmw==} + resolution: {integrity: sha512-tN6Ru5e0UVnWaIuiH8jEwnvxuxg7FvhWwSd3oe5oDhOD4ObsSr9/N6WfHLjilUWlPPj65ptwrSDH0Y7lzwTzmw==, tarball: https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.155.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] '@oxc-parser/binding-linux-arm-gnueabihf@0.148.0': - resolution: {integrity: sha512-eOr3M+6iGbbxNL4PSS0VtsyQ2eOUxSBh00BqO22SbolDimPSYsBuLr/LCrZBkiqW2BoabhR6V4R8jrRAay7hjg==} + resolution: {integrity: sha512-eOr3M+6iGbbxNL4PSS0VtsyQ2eOUxSBh00BqO22SbolDimPSYsBuLr/LCrZBkiqW2BoabhR6V4R8jrRAay7hjg==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.148.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@oxc-parser/binding-linux-arm-gnueabihf@0.155.0': - resolution: {integrity: sha512-h4ozA5n6L6lwolsz9iwunEhKWnIVbmFRots5QxEix0H6nJ5ICpuDa7PuShuhfpIDFe3+Qvyscioyiv+9g+4Syg==} + resolution: {integrity: sha512-h4ozA5n6L6lwolsz9iwunEhKWnIVbmFRots5QxEix0H6nJ5ICpuDa7PuShuhfpIDFe3+Qvyscioyiv+9g+4Syg==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.155.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@oxc-parser/binding-linux-arm-musleabihf@0.148.0': - resolution: {integrity: sha512-58ZKDw0mQRbCNfrd2IDyV4o8T7enzGERJn41BH2tjrZVGyiKiFzcfDicuB7Zcpb/1xIOrObovr8Dja6lZi8dLw==} + resolution: {integrity: sha512-58ZKDw0mQRbCNfrd2IDyV4o8T7enzGERJn41BH2tjrZVGyiKiFzcfDicuB7Zcpb/1xIOrObovr8Dja6lZi8dLw==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.148.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@oxc-parser/binding-linux-arm-musleabihf@0.155.0': - resolution: {integrity: sha512-058/+LardpfuUlsKIvBGk/CYoakEVcMgqYx8GgyUZeOxFWTojYLlCc1Gb60/y21v3esDhHidgiY/Zbq9LpBhaQ==} + resolution: {integrity: sha512-058/+LardpfuUlsKIvBGk/CYoakEVcMgqYx8GgyUZeOxFWTojYLlCc1Gb60/y21v3esDhHidgiY/Zbq9LpBhaQ==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.155.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@oxc-parser/binding-linux-arm64-gnu@0.148.0': - resolution: {integrity: sha512-Fnu95O4eZ5i++GPvIzBEZ8y4ddTLR+D9paYa8JRaRk6ZK7nHQiWP5xtrhcPQsXqgat1d7sU/d5rbbI0p1FTHSQ==} + resolution: {integrity: sha512-Fnu95O4eZ5i++GPvIzBEZ8y4ddTLR+D9paYa8JRaRk6ZK7nHQiWP5xtrhcPQsXqgat1d7sU/d5rbbI0p1FTHSQ==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.148.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] '@oxc-parser/binding-linux-arm64-gnu@0.155.0': - resolution: {integrity: sha512-nrOt2Y3xIAzu+lSPzH0864ieO+iCCXgoo9qFlGANE8Xh0cQ+RP8Wstfp+/wXN2YCyb98cAGzX0VruKYw7it/jw==} + resolution: {integrity: sha512-nrOt2Y3xIAzu+lSPzH0864ieO+iCCXgoo9qFlGANE8Xh0cQ+RP8Wstfp+/wXN2YCyb98cAGzX0VruKYw7it/jw==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.155.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] '@oxc-parser/binding-linux-arm64-musl@0.148.0': - resolution: {integrity: sha512-3CQy/BMdx7N7H3qrcPxUL+a2CwUZodUcf6oq8iJuNZ9C6Ol1aq3mcWzsgySJ7CHFLvpX21ZDPp1r1X0QLbu/AQ==} + resolution: {integrity: sha512-3CQy/BMdx7N7H3qrcPxUL+a2CwUZodUcf6oq8iJuNZ9C6Ol1aq3mcWzsgySJ7CHFLvpX21ZDPp1r1X0QLbu/AQ==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.148.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] '@oxc-parser/binding-linux-arm64-musl@0.155.0': - resolution: {integrity: sha512-KYAayyN5hQbJPtGKtRxlyc8XYmyntOplyXSQzTDrmbz7l/GCrzQbSswvyJFBTH1LGtsZrbXOAfN52i/Kz6kLKg==} + resolution: {integrity: sha512-KYAayyN5hQbJPtGKtRxlyc8XYmyntOplyXSQzTDrmbz7l/GCrzQbSswvyJFBTH1LGtsZrbXOAfN52i/Kz6kLKg==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.155.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] '@oxc-parser/binding-linux-ppc64-gnu@0.148.0': - resolution: {integrity: sha512-9LkaYvfiF8hMOw900csAvkf1oxE8XlmMeGowu5BcastSSwV8mKvKRMNU7HsV+ycyj1dQD8pX5qgOw8ja6SJacg==} + resolution: {integrity: sha512-9LkaYvfiF8hMOw900csAvkf1oxE8XlmMeGowu5BcastSSwV8mKvKRMNU7HsV+ycyj1dQD8pX5qgOw8ja6SJacg==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.148.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] '@oxc-parser/binding-linux-ppc64-gnu@0.155.0': - resolution: {integrity: sha512-CSuSY5hfjtsBnHXydOj8jiNfS1qHbdPZuPz1YL1cSgK7VmNYP+QlZIbG0BBYYBgX/vYEgZwbG9mBdW4YairZ5w==} + resolution: {integrity: sha512-CSuSY5hfjtsBnHXydOj8jiNfS1qHbdPZuPz1YL1cSgK7VmNYP+QlZIbG0BBYYBgX/vYEgZwbG9mBdW4YairZ5w==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.155.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] '@oxc-parser/binding-linux-riscv64-gnu@0.148.0': - resolution: {integrity: sha512-2GBiM9h26dR4WJfhoMvnFMnFLf7m/kYs4UMqjvrOfQG4BV1nuTJDH22Zc2MQr3INZF7nSKYQ6xlhD3hQ7A6gug==} + resolution: {integrity: sha512-2GBiM9h26dR4WJfhoMvnFMnFLf7m/kYs4UMqjvrOfQG4BV1nuTJDH22Zc2MQr3INZF7nSKYQ6xlhD3hQ7A6gug==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.148.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] '@oxc-parser/binding-linux-riscv64-gnu@0.155.0': - resolution: {integrity: sha512-7KCfOQm+Nz2N8trAXbQkal4TQ5756WVEmhm35y816YHvyFnEfVsICsiV5k0pEszNf/fRm4cm2i6i18SJ6/iCWg==} + resolution: {integrity: sha512-7KCfOQm+Nz2N8trAXbQkal4TQ5756WVEmhm35y816YHvyFnEfVsICsiV5k0pEszNf/fRm4cm2i6i18SJ6/iCWg==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.155.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] '@oxc-parser/binding-linux-riscv64-musl@0.148.0': - resolution: {integrity: sha512-uPqZexvKJmEgq4mAu36qe2xTfXZE7oyik1R7KtZ5tl8qKlq1U1fIqTFRUEBZqRGvforoTrGIpatRzcoPKO66RA==} + resolution: {integrity: sha512-uPqZexvKJmEgq4mAu36qe2xTfXZE7oyik1R7KtZ5tl8qKlq1U1fIqTFRUEBZqRGvforoTrGIpatRzcoPKO66RA==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.148.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] '@oxc-parser/binding-linux-riscv64-musl@0.155.0': - resolution: {integrity: sha512-JSlhgoK+/g1TGJI5NuADx2+6syhxnQNLCWLXyO5QLFyetomcr/vIxSdiNlmyAv963YXUzCuwAwRG6npZ5xyJ1A==} + resolution: {integrity: sha512-JSlhgoK+/g1TGJI5NuADx2+6syhxnQNLCWLXyO5QLFyetomcr/vIxSdiNlmyAv963YXUzCuwAwRG6npZ5xyJ1A==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.155.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] '@oxc-parser/binding-linux-s390x-gnu@0.148.0': - resolution: {integrity: sha512-9oUHvnTbp7ZraFsTC8PN6XhdhPSSxZumYvixWl7Smi353gEULvK6yV0sXNVrdFMHQeaDKFCi8TgDhNK7/A+Y+Q==} + resolution: {integrity: sha512-9oUHvnTbp7ZraFsTC8PN6XhdhPSSxZumYvixWl7Smi353gEULvK6yV0sXNVrdFMHQeaDKFCi8TgDhNK7/A+Y+Q==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.148.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] '@oxc-parser/binding-linux-s390x-gnu@0.155.0': - resolution: {integrity: sha512-WFIX+dPokWp64mi434Md3U3lJaTwNxAphvGjpltoyQjGUsdXavOIRrnp9dOS34Hd75HOAwDxAI+YwWOwqmJbtg==} + resolution: {integrity: sha512-WFIX+dPokWp64mi434Md3U3lJaTwNxAphvGjpltoyQjGUsdXavOIRrnp9dOS34Hd75HOAwDxAI+YwWOwqmJbtg==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.155.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] '@oxc-parser/binding-linux-x64-gnu@0.148.0': - resolution: {integrity: sha512-2qhDSJwKzbSZzF7lDqqk8sr/yXsmwr3PeUa4/nazIF+zFAYz1gVPEfC34GQtGxzJUUmklaYAL63368LEfrMeyw==} + resolution: {integrity: sha512-2qhDSJwKzbSZzF7lDqqk8sr/yXsmwr3PeUa4/nazIF+zFAYz1gVPEfC34GQtGxzJUUmklaYAL63368LEfrMeyw==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.148.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] '@oxc-parser/binding-linux-x64-gnu@0.155.0': - resolution: {integrity: sha512-g6xSDC/JpbnVOz5uafMGyK3txBVRFMQTx8opv/yHXGxqV3ZMVl3bf7W8XrSnF68dmy3gM4dgReNryuE53+WkeQ==} + resolution: {integrity: sha512-g6xSDC/JpbnVOz5uafMGyK3txBVRFMQTx8opv/yHXGxqV3ZMVl3bf7W8XrSnF68dmy3gM4dgReNryuE53+WkeQ==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.155.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] '@oxc-parser/binding-linux-x64-musl@0.148.0': - resolution: {integrity: sha512-qQoPDZUFV0bh9xA09XydmkjMBpgc1ukJuhMvzQ9QeVmFaHTS9W5TE5CoLmSl3QQyUP9OuHO3x/WPZTIIZPWR3Q==} + resolution: {integrity: sha512-qQoPDZUFV0bh9xA09XydmkjMBpgc1ukJuhMvzQ9QeVmFaHTS9W5TE5CoLmSl3QQyUP9OuHO3x/WPZTIIZPWR3Q==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.148.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] '@oxc-parser/binding-linux-x64-musl@0.155.0': - resolution: {integrity: sha512-6/4xlxKlZJWrQ0uwDFPc0H9+xLu7068CKhX3vIDW3SbvP/lVgnVuT5v6dyugeWskWpuxqF/WJ4WKwhxjU3pG2w==} + resolution: {integrity: sha512-6/4xlxKlZJWrQ0uwDFPc0H9+xLu7068CKhX3vIDW3SbvP/lVgnVuT5v6dyugeWskWpuxqF/WJ4WKwhxjU3pG2w==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.155.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] '@oxc-parser/binding-openharmony-arm64@0.148.0': - resolution: {integrity: sha512-1UGbaQWEXUCLqAmaR5kwRDjx/R4S5LQKZkM9CHmaHkuKhriOF32aRLfS0jCRNE2yGQJLMEA1z9UucbBVqjXnDw==} + resolution: {integrity: sha512-1UGbaQWEXUCLqAmaR5kwRDjx/R4S5LQKZkM9CHmaHkuKhriOF32aRLfS0jCRNE2yGQJLMEA1z9UucbBVqjXnDw==, tarball: https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.148.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] '@oxc-parser/binding-openharmony-arm64@0.155.0': - resolution: {integrity: sha512-tltklL0J0XjDxIRTBXWOBhvc2mitcmES45pIxhwDWfxRyqz7Vkeu0NekTZEIxGE4f/lXgq3vZcjjffteQOVorg==} + resolution: {integrity: sha512-tltklL0J0XjDxIRTBXWOBhvc2mitcmES45pIxhwDWfxRyqz7Vkeu0NekTZEIxGE4f/lXgq3vZcjjffteQOVorg==, tarball: https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.155.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] '@oxc-parser/binding-win32-arm64-msvc@0.148.0': - resolution: {integrity: sha512-pWKdzRDNG2+NK4h/V6U/CYERcfYD6u28h5IB/VJVsrZaD3muvE58tUj22lieL5vLZ+XFi1GPv9YXckZbJZ9BLA==} + resolution: {integrity: sha512-pWKdzRDNG2+NK4h/V6U/CYERcfYD6u28h5IB/VJVsrZaD3muvE58tUj22lieL5vLZ+XFi1GPv9YXckZbJZ9BLA==, tarball: https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.148.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] '@oxc-parser/binding-win32-arm64-msvc@0.155.0': - resolution: {integrity: sha512-alAb9TzjatUEsOeoncAziUnfdTDxfyswhalFZ9sC6plUgATIUx2NIZ0+bMn6aJ28AduS1OpG/OAisr/YISn4Ig==} + resolution: {integrity: sha512-alAb9TzjatUEsOeoncAziUnfdTDxfyswhalFZ9sC6plUgATIUx2NIZ0+bMn6aJ28AduS1OpG/OAisr/YISn4Ig==, tarball: https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.155.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] '@oxc-parser/binding-win32-ia32-msvc@0.148.0': - resolution: {integrity: sha512-i3p4x+mvwtjcE1J5HM6V7ggsbXiznExN/4MkNyOy3dfXrVV3bnkSfmZxvo6/84qCVX4ShkpNE1SKt9biIF31GQ==} + resolution: {integrity: sha512-i3p4x+mvwtjcE1J5HM6V7ggsbXiznExN/4MkNyOy3dfXrVV3bnkSfmZxvo6/84qCVX4ShkpNE1SKt9biIF31GQ==, tarball: https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.148.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] '@oxc-parser/binding-win32-ia32-msvc@0.155.0': - resolution: {integrity: sha512-yc5cbSfcGmRv/KpxDFuyk8adEBaWd/9HilWvBbQ4bFbus1V2PUh2mGB2Cfq9rqZTqLeiMT8x0skV+LKp9hOMgQ==} + resolution: {integrity: sha512-yc5cbSfcGmRv/KpxDFuyk8adEBaWd/9HilWvBbQ4bFbus1V2PUh2mGB2Cfq9rqZTqLeiMT8x0skV+LKp9hOMgQ==, tarball: https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.155.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] '@oxc-parser/binding-win32-x64-msvc@0.148.0': - resolution: {integrity: sha512-Ye6vB7VQulghWYkYkECOBYFRVEizz4XyRTUAv+t8BuyurhKU7uD0P9eowL+mKG5Mf8MSYx+DI3Cm8SKZvYG7bQ==} + resolution: {integrity: sha512-Ye6vB7VQulghWYkYkECOBYFRVEizz4XyRTUAv+t8BuyurhKU7uD0P9eowL+mKG5Mf8MSYx+DI3Cm8SKZvYG7bQ==, tarball: https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.148.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] '@oxc-parser/binding-win32-x64-msvc@0.155.0': - resolution: {integrity: sha512-8heq6jGq+rfpTDRDjoQiBHw0maQZf3IXkS99pdHQZPoTL5kyF5qyPS0RidUlSdnWDIqxXb5nux7GBD9cT+imUw==} + resolution: {integrity: sha512-8heq6jGq+rfpTDRDjoQiBHw0maQZf3IXkS99pdHQZPoTL5kyF5qyPS0RidUlSdnWDIqxXb5nux7GBD9cT+imUw==, tarball: https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.155.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] '@oxc-project/types@0.148.0': - resolution: {integrity: sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A==} + resolution: {integrity: sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A==, tarball: https://registry.npmjs.org/@oxc-project/types/-/types-0.148.0.tgz} '@oxc-project/types@0.155.0': resolution: {integrity: sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q==} '@oxc-resolver/binding-android-arm-eabi@11.24.2': - resolution: {integrity: sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA==} + resolution: {integrity: sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.24.2.tgz} cpu: [arm] os: [android] '@oxc-resolver/binding-android-arm64@11.24.2': - resolution: {integrity: sha512-cl4icWaZFnLdg8m6qtnh5rBMuGbxc/ptStFHLeCNwr+2cZjkjNwQu/jYRS0CHlnPecOJMpuS5M6/BH+0J/YkEg==} + resolution: {integrity: sha512-cl4icWaZFnLdg8m6qtnh5rBMuGbxc/ptStFHLeCNwr+2cZjkjNwQu/jYRS0CHlnPecOJMpuS5M6/BH+0J/YkEg==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.24.2.tgz} cpu: [arm64] os: [android] '@oxc-resolver/binding-darwin-arm64@11.24.2': - resolution: {integrity: sha512-At29QEMF6HajbQvgY8K6OXnHD1x9rad74xBEfmCB6ZqCGsdq75aK7tOYcTbOanMy8qdIBrfL3SMr3p/lfSlb9w==} + resolution: {integrity: sha512-At29QEMF6HajbQvgY8K6OXnHD1x9rad74xBEfmCB6ZqCGsdq75aK7tOYcTbOanMy8qdIBrfL3SMr3p/lfSlb9w==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.24.2.tgz} cpu: [arm64] os: [darwin] '@oxc-resolver/binding-darwin-x64@11.24.2': - resolution: {integrity: sha512-A5Kqr1EUj4oIL5CF4WRssq/o5P0Y11cwoFouMRmQ7YnC/A8V93nv1nb7aSU8HwcgmXropjLNkVTl4MN87cu28Q==} + resolution: {integrity: sha512-A5Kqr1EUj4oIL5CF4WRssq/o5P0Y11cwoFouMRmQ7YnC/A8V93nv1nb7aSU8HwcgmXropjLNkVTl4MN87cu28Q==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.24.2.tgz} cpu: [x64] os: [darwin] '@oxc-resolver/binding-freebsd-x64@11.24.2': - resolution: {integrity: sha512-R5xkRBRRz7ceH/P5Jrc6G7FmdUdgpLYyESFAUDVTNQ9K0sGPxcp4ljiwEwEqsvNcQ4sYbMRrWcHHBCu7ksAJVw==} + resolution: {integrity: sha512-R5xkRBRRz7ceH/P5Jrc6G7FmdUdgpLYyESFAUDVTNQ9K0sGPxcp4ljiwEwEqsvNcQ4sYbMRrWcHHBCu7ksAJVw==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.24.2.tgz} cpu: [x64] os: [freebsd] '@oxc-resolver/binding-linux-arm-gnueabihf@11.24.2': - resolution: {integrity: sha512-k/RuYL4L/R58IBn3wT5ma3Wh4k62bp1eYCFRWCmMsasUOqL+H6sW0VGFadEzKWXFFlz+2uIMoeMk9ySSZJHgbg==} + resolution: {integrity: sha512-k/RuYL4L/R58IBn3wT5ma3Wh4k62bp1eYCFRWCmMsasUOqL+H6sW0VGFadEzKWXFFlz+2uIMoeMk9ySSZJHgbg==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.24.2.tgz} cpu: [arm] os: [linux] '@oxc-resolver/binding-linux-arm-musleabihf@11.24.2': - resolution: {integrity: sha512-bnHAak3ujYfH5pKk4NieFNbvYvernfoQDgwLddbZ3OtMYrem87/qjlA+u+aKG0oZcqSLGCful/6/CEA+aeAgaA==} + resolution: {integrity: sha512-bnHAak3ujYfH5pKk4NieFNbvYvernfoQDgwLddbZ3OtMYrem87/qjlA+u+aKG0oZcqSLGCful/6/CEA+aeAgaA==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.24.2.tgz} cpu: [arm] os: [linux] '@oxc-resolver/binding-linux-arm64-gnu@11.24.2': - resolution: {integrity: sha512-vDT3KHgzYp47gmtNOqL2VNhCyl5Zv643eyxm//A68J8DeUGXrvD1pZFiaT4jSfe+RInfnn1R2yVHye4enx6RnA==} + resolution: {integrity: sha512-vDT3KHgzYp47gmtNOqL2VNhCyl5Zv643eyxm//A68J8DeUGXrvD1pZFiaT4jSfe+RInfnn1R2yVHye4enx6RnA==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.24.2.tgz} cpu: [arm64] os: [linux] libc: [glibc] '@oxc-resolver/binding-linux-arm64-musl@11.24.2': - resolution: {integrity: sha512-+kMlQvbzfyEYtu5FcjE4p+ttBLpKW4d/AsAsuE69BxV6V4twZJeIQZFfD8gh/wqglY0MkPSezWXQH0jBV13MUw==} + resolution: {integrity: sha512-+kMlQvbzfyEYtu5FcjE4p+ttBLpKW4d/AsAsuE69BxV6V4twZJeIQZFfD8gh/wqglY0MkPSezWXQH0jBV13MUw==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.24.2.tgz} cpu: [arm64] os: [linux] libc: [musl] '@oxc-resolver/binding-linux-ppc64-gnu@11.24.2': - resolution: {integrity: sha512-shjfMhmZ3gq9fv/w7bi3PnZlgOPG+2QAOFf0BJF0EgBSIGZ6PMLN2zbGEblTUYB/NKVDRyYhE2ff3dJ1QqNPkA==} + resolution: {integrity: sha512-shjfMhmZ3gq9fv/w7bi3PnZlgOPG+2QAOFf0BJF0EgBSIGZ6PMLN2zbGEblTUYB/NKVDRyYhE2ff3dJ1QqNPkA==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.24.2.tgz} cpu: [ppc64] os: [linux] libc: [glibc] '@oxc-resolver/binding-linux-riscv64-gnu@11.24.2': - resolution: {integrity: sha512-zGelwFR5oRo+b69k8Lrzun86DyUHzfKN6cnjbR9l7Z7NIRznOE/2ZvPa1IUKqAL2PzAXOdwkfVqNvO1H2RlpAw==} + resolution: {integrity: sha512-zGelwFR5oRo+b69k8Lrzun86DyUHzfKN6cnjbR9l7Z7NIRznOE/2ZvPa1IUKqAL2PzAXOdwkfVqNvO1H2RlpAw==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.24.2.tgz} cpu: [riscv64] os: [linux] libc: [glibc] '@oxc-resolver/binding-linux-riscv64-musl@11.24.2': - resolution: {integrity: sha512-qxZ1SWCXJY0eyhAlP6Lmo9F2Nrtx7EkYj9oCgL8apDPCwXwCEDA2U697bbT81JIc2IrVjxO4KX6WU2N+oN9Z4w==} + resolution: {integrity: sha512-qxZ1SWCXJY0eyhAlP6Lmo9F2Nrtx7EkYj9oCgL8apDPCwXwCEDA2U697bbT81JIc2IrVjxO4KX6WU2N+oN9Z4w==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.24.2.tgz} cpu: [riscv64] os: [linux] libc: [musl] '@oxc-resolver/binding-linux-s390x-gnu@11.24.2': - resolution: {integrity: sha512-sGCecF3cx2DFlH4t/z7ApnOnXqN48p5p5mlHDEnHTAukQa2P+qMVE4CwyWE9W+q/m3QJ7kKfGrIjax31f44oFQ==} + resolution: {integrity: sha512-sGCecF3cx2DFlH4t/z7ApnOnXqN48p5p5mlHDEnHTAukQa2P+qMVE4CwyWE9W+q/m3QJ7kKfGrIjax31f44oFQ==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.24.2.tgz} cpu: [s390x] os: [linux] libc: [glibc] '@oxc-resolver/binding-linux-x64-gnu@11.24.2': - resolution: {integrity: sha512-k/VlMMcSzMlahb3/fENM4rTlsJ0s3fFROA0KXPBmKggqmTSaE383sl8F3KCOXPLmVsYfW6hCitMhXCEtNeZxxg==} + resolution: {integrity: sha512-k/VlMMcSzMlahb3/fENM4rTlsJ0s3fFROA0KXPBmKggqmTSaE383sl8F3KCOXPLmVsYfW6hCitMhXCEtNeZxxg==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.24.2.tgz} cpu: [x64] os: [linux] libc: [glibc] '@oxc-resolver/binding-linux-x64-musl@11.24.2': - resolution: {integrity: sha512-8hbnZyNi97b/8wapYaIF9+t9GmZKBW2vunaOc3h9HGJptH7b7XpvZqOTBSm/MpTjr7H497BlgOaSfLUdhmy2bw==} + resolution: {integrity: sha512-8hbnZyNi97b/8wapYaIF9+t9GmZKBW2vunaOc3h9HGJptH7b7XpvZqOTBSm/MpTjr7H497BlgOaSfLUdhmy2bw==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.24.2.tgz} cpu: [x64] os: [linux] libc: [musl] '@oxc-resolver/binding-openharmony-arm64@11.24.2': - resolution: {integrity: sha512-MvyGik3a6pVgZ0t/kWlbmFxFLmXQJwgLsY2eYFHLpy0wGwRbfzeIGgDwQ3kXqE30z+kSXennRkCrT7TUvkptNg==} + resolution: {integrity: sha512-MvyGik3a6pVgZ0t/kWlbmFxFLmXQJwgLsY2eYFHLpy0wGwRbfzeIGgDwQ3kXqE30z+kSXennRkCrT7TUvkptNg==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.24.2.tgz} cpu: [arm64] os: [openharmony] '@oxc-resolver/binding-wasm32-wasi@11.24.2': - resolution: {integrity: sha512-vHcssMPwO08RTvj/c0iOBz90attxyG3wQJ0dTcyEQK43LRpcdLWZlV5feBhv6Isn6ahbQIzHbCgfa81+RiML0Q==} + resolution: {integrity: sha512-vHcssMPwO08RTvj/c0iOBz90attxyG3wQJ0dTcyEQK43LRpcdLWZlV5feBhv6Isn6ahbQIzHbCgfa81+RiML0Q==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.24.2.tgz} engines: {node: '>=14.0.0'} cpu: [wasm32] '@oxc-resolver/binding-win32-arm64-msvc@11.24.2': - resolution: {integrity: sha512-uokJqro2iBqkFvJdKQLP7d8/BUmFwESQFVmIJUQKj1Xn1a/LysJoe1vmeECLF5b3jsV8CAL5sEMJXX6SdK9Nhg==} + resolution: {integrity: sha512-uokJqro2iBqkFvJdKQLP7d8/BUmFwESQFVmIJUQKj1Xn1a/LysJoe1vmeECLF5b3jsV8CAL5sEMJXX6SdK9Nhg==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.24.2.tgz} cpu: [arm64] os: [win32] '@oxc-resolver/binding-win32-x64-msvc@11.24.2': - resolution: {integrity: sha512-UqGPmo56KDfLlfXFAFIrNflHT8tFxWGEivWg3Zeyp4Uy2NlKN1FGPr6/BxcLGG3+kZ6Wp14g5Uj+n71boqZfiw==} + resolution: {integrity: sha512-UqGPmo56KDfLlfXFAFIrNflHT8tFxWGEivWg3Zeyp4Uy2NlKN1FGPr6/BxcLGG3+kZ6Wp14g5Uj+n71boqZfiw==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.24.2.tgz} cpu: [x64] os: [win32] '@oxlint/binding-android-arm-eabi@1.81.0': - resolution: {integrity: sha512-IcCRsXiedJoJopY6mpZUBEeVFsUrutmrG7dZ87zMuKJlhg70Ora9bBl1WcCxZQtyI10YpnVdEso5oCg7YcfSHw==} + resolution: {integrity: sha512-IcCRsXiedJoJopY6mpZUBEeVFsUrutmrG7dZ87zMuKJlhg70Ora9bBl1WcCxZQtyI10YpnVdEso5oCg7YcfSHw==, tarball: https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.81.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] '@oxlint/binding-android-arm64@1.81.0': - resolution: {integrity: sha512-GRrIPyTGVhx3L3h+0T5xT2A0jFAcdPv4+IfuXpGDLIdl6XeYhgg/zw72A5ILZoUgRqZuM8F1y+V/gfDriXSxzQ==} + resolution: {integrity: sha512-GRrIPyTGVhx3L3h+0T5xT2A0jFAcdPv4+IfuXpGDLIdl6XeYhgg/zw72A5ILZoUgRqZuM8F1y+V/gfDriXSxzQ==, tarball: https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.81.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] '@oxlint/binding-darwin-arm64@1.81.0': - resolution: {integrity: sha512-qNQ9tXRgLuKbqSV1S2h9h4KPHjbovO7RRR2/enUOtHzTkFZ7B9X5zqqHJua8dRyc7dBy7Aoyq5pqTSLFVcAzGQ==} + resolution: {integrity: sha512-qNQ9tXRgLuKbqSV1S2h9h4KPHjbovO7RRR2/enUOtHzTkFZ7B9X5zqqHJua8dRyc7dBy7Aoyq5pqTSLFVcAzGQ==, tarball: https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.81.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] '@oxlint/binding-darwin-x64@1.81.0': - resolution: {integrity: sha512-q0QTm32jWga2Gv4j7IaVZN0jYMi9UV73sWVgFtDA4iIfqwMCLLZ3ve+9KwfYtsaKZSgQhmPaogeZWqDZpcY1Pw==} + resolution: {integrity: sha512-q0QTm32jWga2Gv4j7IaVZN0jYMi9UV73sWVgFtDA4iIfqwMCLLZ3ve+9KwfYtsaKZSgQhmPaogeZWqDZpcY1Pw==, tarball: https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.81.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] '@oxlint/binding-freebsd-x64@1.81.0': - resolution: {integrity: sha512-/+8wVWDXEC7wHVAhOc59Fw/SkMc1arLkFD8iQCaSsmzenK1X4doFqquL9H1wrtGUzaiycVqkf/sSpcILK6W1UA==} + resolution: {integrity: sha512-/+8wVWDXEC7wHVAhOc59Fw/SkMc1arLkFD8iQCaSsmzenK1X4doFqquL9H1wrtGUzaiycVqkf/sSpcILK6W1UA==, tarball: https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.81.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] '@oxlint/binding-linux-arm-gnueabihf@1.81.0': - resolution: {integrity: sha512-4xt422FEgioRq9hAL4Tq7fujGUWnc8z1BJ+Oi8RN8vB8axaP+sdK6a2xdlcQCCYnJg9QMuMFS0AucuIFx/EacA==} + resolution: {integrity: sha512-4xt422FEgioRq9hAL4Tq7fujGUWnc8z1BJ+Oi8RN8vB8axaP+sdK6a2xdlcQCCYnJg9QMuMFS0AucuIFx/EacA==, tarball: https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.81.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@oxlint/binding-linux-arm-musleabihf@1.81.0': - resolution: {integrity: sha512-u3vna8KdGplH4DRCW9K54D68fcMo7IxVrkCJWwXnIhwtBdnDnYrmzOUA/XjmBlPpcLsgw9Z5BNdY4za9+Dj+MQ==} + resolution: {integrity: sha512-u3vna8KdGplH4DRCW9K54D68fcMo7IxVrkCJWwXnIhwtBdnDnYrmzOUA/XjmBlPpcLsgw9Z5BNdY4za9+Dj+MQ==, tarball: https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.81.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@oxlint/binding-linux-arm64-gnu@1.81.0': - resolution: {integrity: sha512-3j9k+gsYsE7nv71GWotXsqsa2l9/aJenD7dVHNt/CBvsb0SgRjSMnHFeP59IXUAl1wvVFhqGl2wJNMwWU3UBlA==} + resolution: {integrity: sha512-3j9k+gsYsE7nv71GWotXsqsa2l9/aJenD7dVHNt/CBvsb0SgRjSMnHFeP59IXUAl1wvVFhqGl2wJNMwWU3UBlA==, tarball: https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.81.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] '@oxlint/binding-linux-arm64-musl@1.81.0': - resolution: {integrity: sha512-k5iAp3dNxW0/uDCBY+WSm8jKB2szu7SkEQZdgRRpDXvuDd69vvDcqhB3A/pWCfCwXyenjNjFn9Td1fVoyAc+Yg==} + resolution: {integrity: sha512-k5iAp3dNxW0/uDCBY+WSm8jKB2szu7SkEQZdgRRpDXvuDd69vvDcqhB3A/pWCfCwXyenjNjFn9Td1fVoyAc+Yg==, tarball: https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.81.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] '@oxlint/binding-linux-ppc64-gnu@1.81.0': - resolution: {integrity: sha512-TFqLja3uYmVSte6nof9GWrex9Z8WgdZrNiLC6Te5rXGDqXB2y4j/26iFhwosXiAFqDhE9JJVuuCkDKLwptTn1g==} + resolution: {integrity: sha512-TFqLja3uYmVSte6nof9GWrex9Z8WgdZrNiLC6Te5rXGDqXB2y4j/26iFhwosXiAFqDhE9JJVuuCkDKLwptTn1g==, tarball: https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.81.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] '@oxlint/binding-linux-riscv64-gnu@1.81.0': - resolution: {integrity: sha512-UEcySvGS0NOVo7h7n7CYyJL9+6gFAh7Zc/ToDXVScFvzHSTIxtzkMVU30rmQ6+nQ1LF+UdiRDdJajpDu+OylLg==} + resolution: {integrity: sha512-UEcySvGS0NOVo7h7n7CYyJL9+6gFAh7Zc/ToDXVScFvzHSTIxtzkMVU30rmQ6+nQ1LF+UdiRDdJajpDu+OylLg==, tarball: https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.81.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] '@oxlint/binding-linux-riscv64-musl@1.81.0': - resolution: {integrity: sha512-H+diDbhD00+wI1IRP8Kz88x/lat+DgtoBJzoTthS16xkTJGNaEkfb8gzmd1rzc/2uDQQMl7GNl+JFUacVeWxIA==} + resolution: {integrity: sha512-H+diDbhD00+wI1IRP8Kz88x/lat+DgtoBJzoTthS16xkTJGNaEkfb8gzmd1rzc/2uDQQMl7GNl+JFUacVeWxIA==, tarball: https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.81.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] '@oxlint/binding-linux-s390x-gnu@1.81.0': - resolution: {integrity: sha512-8znJ/5TekjOKg1j1Acho4PJMdiAHLtlcXuWEiipOhAMV6rQcXdmDdXCbheyDczN6TjBwiNfjcP81k4AthrKRzw==} + resolution: {integrity: sha512-8znJ/5TekjOKg1j1Acho4PJMdiAHLtlcXuWEiipOhAMV6rQcXdmDdXCbheyDczN6TjBwiNfjcP81k4AthrKRzw==, tarball: https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.81.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] '@oxlint/binding-linux-x64-gnu@1.81.0': - resolution: {integrity: sha512-Q2Wj70yFsvn5QjlmifFzbj4H+kJy53bwqc41o1fzoM7MpLV1NIbhg/LpWXRfC6KOkSAdUx1Wd8VJsdPmhp/HRA==} + resolution: {integrity: sha512-Q2Wj70yFsvn5QjlmifFzbj4H+kJy53bwqc41o1fzoM7MpLV1NIbhg/LpWXRfC6KOkSAdUx1Wd8VJsdPmhp/HRA==, tarball: https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.81.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] '@oxlint/binding-linux-x64-musl@1.81.0': - resolution: {integrity: sha512-cPInHp/ddEe5qkyK2IiyQ8Q3Mp2oLLEhhsGgTK2oZx4L6+llGam1H1yBvJZ7qHfOXj8N3hxBS8sj4tO+gtFlIg==} + resolution: {integrity: sha512-cPInHp/ddEe5qkyK2IiyQ8Q3Mp2oLLEhhsGgTK2oZx4L6+llGam1H1yBvJZ7qHfOXj8N3hxBS8sj4tO+gtFlIg==, tarball: https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.81.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] '@oxlint/binding-openharmony-arm64@1.81.0': - resolution: {integrity: sha512-0CQxSX4ajqm07AHBf5U33qQzXKdd7wtq/oTL/7vpY6RNNuxrRi8W4bqUV1Jyu/vj+9KmxQyDhxfeVX1nQL6kfg==} + resolution: {integrity: sha512-0CQxSX4ajqm07AHBf5U33qQzXKdd7wtq/oTL/7vpY6RNNuxrRi8W4bqUV1Jyu/vj+9KmxQyDhxfeVX1nQL6kfg==, tarball: https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.81.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] '@oxlint/binding-win32-arm64-msvc@1.81.0': - resolution: {integrity: sha512-l0hbeISm9673hVrrQU8j/p2M7YH9Ouoj7p7E/QM55NTrKVLP+P3PF8hLu+OY+x0VtGRW+ggiQKZqmdYps9H+TA==} + resolution: {integrity: sha512-l0hbeISm9673hVrrQU8j/p2M7YH9Ouoj7p7E/QM55NTrKVLP+P3PF8hLu+OY+x0VtGRW+ggiQKZqmdYps9H+TA==, tarball: https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.81.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] '@oxlint/binding-win32-ia32-msvc@1.81.0': - resolution: {integrity: sha512-ksqPP5jbFXcYreEQ7zdJh06rJQBymCTyGRCdaXjfcf2aG4f8KxUWY5wcgYHmaTK+FJ4bPG5sUAdOX+6trnH1JA==} + resolution: {integrity: sha512-ksqPP5jbFXcYreEQ7zdJh06rJQBymCTyGRCdaXjfcf2aG4f8KxUWY5wcgYHmaTK+FJ4bPG5sUAdOX+6trnH1JA==, tarball: https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.81.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] '@oxlint/binding-win32-x64-msvc@1.81.0': - resolution: {integrity: sha512-IZuUCwGw9emG5JtCp+fYGB+Z4OWEoeEcM8R5BA1pYw63/ieYFVdcU2ylxTpHbVHSenZnsYE+ZZ20uHAJszQ4cA==} + resolution: {integrity: sha512-IZuUCwGw9emG5JtCp+fYGB+Z4OWEoeEcM8R5BA1pYw63/ieYFVdcU2ylxTpHbVHSenZnsYE+ZZ20uHAJszQ4cA==, tarball: https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.81.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] '@playwright/test@1.64.1': - resolution: {integrity: sha512-IaYGml/nSOiZWROfPtuIL7neuu9qDOZ3TNvNljUIrhDMxiGTgPjzj/g9p9RxpS2tSepPDRY+UwAaEeiKtUvIAg==} + resolution: {integrity: sha512-IaYGml/nSOiZWROfPtuIL7neuu9qDOZ3TNvNljUIrhDMxiGTgPjzj/g9p9RxpS2tSepPDRY+UwAaEeiKtUvIAg==, tarball: https://registry.npmjs.org/@playwright/test/-/test-1.64.1.tgz} engines: {node: '>=20'} hasBin: true @@ -2174,7 +2174,7 @@ packages: resolution: {integrity: sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==} '@react-grab/cli@0.2.0': - resolution: {integrity: sha512-LVfA+j5cFT0Szd958WRD8W/B8ucCtz4ViiveDKWLqAwBkZFW9VRW1ZvwOUhp/M3SmVPtjDaRkW12GvAuTArP+Q==} + resolution: {integrity: sha512-LVfA+j5cFT0Szd958WRD8W/B8ucCtz4ViiveDKWLqAwBkZFW9VRW1ZvwOUhp/M3SmVPtjDaRkW12GvAuTArP+Q==, tarball: https://registry.npmjs.org/@react-grab/cli/-/cli-0.2.0.tgz} hasBin: true '@reactour/mask@1.2.0': @@ -2198,97 +2198,97 @@ packages: react: 16.x || 17.x || 18.x || 19.x '@rolldown/binding-android-arm-eabi@1.2.15': - resolution: {integrity: sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw==} + resolution: {integrity: sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw==, tarball: https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.15.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] '@rolldown/binding-android-arm64@1.2.15': - resolution: {integrity: sha512-xD7DdZCrbThKuxNg7ooQmvp07ys4J/pnSEMTG3/OdOWl6daC67m/Q4fL78sRHMxTH/N4fGUbYhcPM0MF5WFHkg==} + resolution: {integrity: sha512-xD7DdZCrbThKuxNg7ooQmvp07ys4J/pnSEMTG3/OdOWl6daC67m/Q4fL78sRHMxTH/N4fGUbYhcPM0MF5WFHkg==, tarball: https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.15.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] '@rolldown/binding-darwin-arm64@1.2.15': - resolution: {integrity: sha512-m2WuQ6r0B4OTYxyPpNLtXaMRbRDUbG8YQV2Q3KTdSSK8Th6KHCnC8jVCJt8bvYeSB5WivkkNbc85mKadaSdOxA==} + resolution: {integrity: sha512-m2WuQ6r0B4OTYxyPpNLtXaMRbRDUbG8YQV2Q3KTdSSK8Th6KHCnC8jVCJt8bvYeSB5WivkkNbc85mKadaSdOxA==, tarball: https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.15.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] '@rolldown/binding-darwin-x64@1.2.15': - resolution: {integrity: sha512-5RtnsHM36nodM9WbMy+zip0LGS/G+y5wro5RHrpOlr4ac24+1zSi9kiV2MP1myZeZ/ifEHfulQcWjnAXzc2C2A==} + resolution: {integrity: sha512-5RtnsHM36nodM9WbMy+zip0LGS/G+y5wro5RHrpOlr4ac24+1zSi9kiV2MP1myZeZ/ifEHfulQcWjnAXzc2C2A==, tarball: https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.15.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] '@rolldown/binding-freebsd-x64@1.2.15': - resolution: {integrity: sha512-g3l09pyFqhu+135JKkbkwiKGQmwU5hUMUPJ4VdBADvuqR8d33L6H578rh8m7rtxbdp+VmX/+nzI7nvhSZlzpUQ==} + resolution: {integrity: sha512-g3l09pyFqhu+135JKkbkwiKGQmwU5hUMUPJ4VdBADvuqR8d33L6H578rh8m7rtxbdp+VmX/+nzI7nvhSZlzpUQ==, tarball: https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.15.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] '@rolldown/binding-linux-arm-gnueabihf@1.2.15': - resolution: {integrity: sha512-nIe3VugI97nGQtspPxDqA4sXMUpeiocKIP/CqpY/u/vgbT+alX+cVxu1C/oVBPKg4feNTmYovE91v4DgXvXNwQ==} + resolution: {integrity: sha512-nIe3VugI97nGQtspPxDqA4sXMUpeiocKIP/CqpY/u/vgbT+alX+cVxu1C/oVBPKg4feNTmYovE91v4DgXvXNwQ==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.15.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@rolldown/binding-linux-arm64-gnu@1.2.15': - resolution: {integrity: sha512-iTBjXx+B5QSvgI89lJfmqh4wTh8NabR1YZIIN3hNx4zSG0EVfVqkOvLulNpG4251pbd6/IAmZ/LefVk1Y4rPhw==} + resolution: {integrity: sha512-iTBjXx+B5QSvgI89lJfmqh4wTh8NabR1YZIIN3hNx4zSG0EVfVqkOvLulNpG4251pbd6/IAmZ/LefVk1Y4rPhw==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.15.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] '@rolldown/binding-linux-arm64-musl@1.2.15': - resolution: {integrity: sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ==} + resolution: {integrity: sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.15.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] '@rolldown/binding-linux-ppc64-gnu@1.2.15': - resolution: {integrity: sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw==} + resolution: {integrity: sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.15.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] '@rolldown/binding-linux-s390x-gnu@1.2.15': - resolution: {integrity: sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA==} + resolution: {integrity: sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.15.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] '@rolldown/binding-linux-x64-gnu@1.2.15': - resolution: {integrity: sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw==} + resolution: {integrity: sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.15.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] '@rolldown/binding-linux-x64-musl@1.2.15': - resolution: {integrity: sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ==} + resolution: {integrity: sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.15.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] '@rolldown/binding-openharmony-arm64@1.2.15': - resolution: {integrity: sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w==} + resolution: {integrity: sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w==, tarball: https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.15.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] '@rolldown/binding-win32-arm64-msvc@1.2.15': - resolution: {integrity: sha512-+PgYiS+AH5omy6L45ZLxybasauJaj3M8buoT83D+nzPmCayXlxUV9hydhciCKVyIg0B/v0BtXPDE1uGqNwCPpg==} + resolution: {integrity: sha512-+PgYiS+AH5omy6L45ZLxybasauJaj3M8buoT83D+nzPmCayXlxUV9hydhciCKVyIg0B/v0BtXPDE1uGqNwCPpg==, tarball: https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.15.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] '@rolldown/binding-win32-x64-msvc@1.2.15': - resolution: {integrity: sha512-HFZd2QNQYn1JbIi+Hpd7H+r/lkWxcfIu28nirZ6eCn2LafuvmExMlb/P96Sfx5tf7pPArw39IdhmCF8vmV+NhQ==} + resolution: {integrity: sha512-HFZd2QNQYn1JbIi+Hpd7H+r/lkWxcfIu28nirZ6eCn2LafuvmExMlb/P96Sfx5tf7pPArw39IdhmCF8vmV+NhQ==, tarball: https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.15.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -2323,140 +2323,140 @@ packages: optional: true '@rollup/rollup-android-arm-eabi@4.61.1': - resolution: {integrity: sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==} + resolution: {integrity: sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==, tarball: https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.61.1.tgz} cpu: [arm] os: [android] '@rollup/rollup-android-arm64@4.61.1': - resolution: {integrity: sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==} + resolution: {integrity: sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==, tarball: https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.61.1.tgz} cpu: [arm64] os: [android] '@rollup/rollup-darwin-arm64@4.61.1': - resolution: {integrity: sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==} + resolution: {integrity: sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==, tarball: https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.61.1.tgz} cpu: [arm64] os: [darwin] '@rollup/rollup-darwin-x64@4.61.1': - resolution: {integrity: sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==} + resolution: {integrity: sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==, tarball: https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.61.1.tgz} cpu: [x64] os: [darwin] '@rollup/rollup-freebsd-arm64@4.61.1': - resolution: {integrity: sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==} + resolution: {integrity: sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==, tarball: https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.61.1.tgz} cpu: [arm64] os: [freebsd] '@rollup/rollup-freebsd-x64@4.61.1': - resolution: {integrity: sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==} + resolution: {integrity: sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==, tarball: https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.61.1.tgz} cpu: [x64] os: [freebsd] '@rollup/rollup-linux-arm-gnueabihf@4.61.1': - resolution: {integrity: sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==} + resolution: {integrity: sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.61.1.tgz} cpu: [arm] os: [linux] libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.61.1': - resolution: {integrity: sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==} + resolution: {integrity: sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.61.1.tgz} cpu: [arm] os: [linux] libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.61.1': - resolution: {integrity: sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==} + resolution: {integrity: sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.61.1.tgz} cpu: [arm64] os: [linux] libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.61.1': - resolution: {integrity: sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==} + resolution: {integrity: sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.61.1.tgz} cpu: [arm64] os: [linux] libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.61.1': - resolution: {integrity: sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==} + resolution: {integrity: sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.61.1.tgz} cpu: [loong64] os: [linux] libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.61.1': - resolution: {integrity: sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==} + resolution: {integrity: sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.61.1.tgz} cpu: [loong64] os: [linux] libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.61.1': - resolution: {integrity: sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==} + resolution: {integrity: sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.61.1.tgz} cpu: [ppc64] os: [linux] libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.61.1': - resolution: {integrity: sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==} + resolution: {integrity: sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.61.1.tgz} cpu: [ppc64] os: [linux] libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.61.1': - resolution: {integrity: sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==} + resolution: {integrity: sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.61.1.tgz} cpu: [riscv64] os: [linux] libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.61.1': - resolution: {integrity: sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==} + resolution: {integrity: sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.61.1.tgz} cpu: [riscv64] os: [linux] libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.61.1': - resolution: {integrity: sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==} + resolution: {integrity: sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.61.1.tgz} cpu: [s390x] os: [linux] libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.61.1': - resolution: {integrity: sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==} + resolution: {integrity: sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.61.1.tgz} cpu: [x64] os: [linux] libc: [glibc] '@rollup/rollup-linux-x64-musl@4.61.1': - resolution: {integrity: sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==} + resolution: {integrity: sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.61.1.tgz} cpu: [x64] os: [linux] libc: [musl] '@rollup/rollup-openbsd-x64@4.61.1': - resolution: {integrity: sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==} + resolution: {integrity: sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==, tarball: https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.61.1.tgz} cpu: [x64] os: [openbsd] '@rollup/rollup-openharmony-arm64@4.61.1': - resolution: {integrity: sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==} + resolution: {integrity: sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==, tarball: https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.61.1.tgz} cpu: [arm64] os: [openharmony] '@rollup/rollup-win32-arm64-msvc@4.61.1': - resolution: {integrity: sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==} + resolution: {integrity: sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.61.1.tgz} cpu: [arm64] os: [win32] '@rollup/rollup-win32-ia32-msvc@4.61.1': - resolution: {integrity: sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==} + resolution: {integrity: sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.61.1.tgz} cpu: [ia32] os: [win32] '@rollup/rollup-win32-x64-gnu@4.61.1': - resolution: {integrity: sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==} + resolution: {integrity: sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.61.1.tgz} cpu: [x64] os: [win32] '@rollup/rollup-win32-x64-msvc@4.61.1': - resolution: {integrity: sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==} + resolution: {integrity: sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.61.1.tgz} cpu: [x64] os: [win32] @@ -2466,15 +2466,15 @@ packages: react: '>=16.8.0' '@sentry/conventions@0.16.0': - resolution: {integrity: sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==} + resolution: {integrity: sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==, tarball: https://registry.npmjs.org/@sentry/conventions/-/conventions-0.16.0.tgz} engines: {node: '>=14'} '@sentry/core@10.76.2': - resolution: {integrity: sha512-vndrQalRiwYatTkjlLWvyf69KocXIb9TKJ4A9oiyWguL8PJYtqIRTlRe4OpkpjLrxS18fvRdV0jEm29c/9p3hQ==} + resolution: {integrity: sha512-vndrQalRiwYatTkjlLWvyf69KocXIb9TKJ4A9oiyWguL8PJYtqIRTlRe4OpkpjLrxS18fvRdV0jEm29c/9p3hQ==, tarball: https://registry.npmjs.org/@sentry/core/-/core-10.76.2.tgz} engines: {node: '>=18'} '@sentry/node-core@10.76.2': - resolution: {integrity: sha512-TrgAc/SvU5kQfjw8pz6I8LLIUiblOSf9BPPNkTbS0Dzcqa4yQLVbmsbS3rvGT7ot64m9Vjzn327ZRy63MlI6/w==} + resolution: {integrity: sha512-TrgAc/SvU5kQfjw8pz6I8LLIUiblOSf9BPPNkTbS0Dzcqa4yQLVbmsbS3rvGT7ot64m9Vjzn327ZRy63MlI6/w==, tarball: https://registry.npmjs.org/@sentry/node-core/-/node-core-10.76.2.tgz} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 @@ -2495,11 +2495,11 @@ packages: optional: true '@sentry/node@10.76.2': - resolution: {integrity: sha512-LSwGGE/GceTbkDHRoQQr3lq/dpxxA6RdooBbuGTJ/S6BdqKX2kw2FdV7Se9eJzZJJJ5AxCZum37+ejYS7N2pJg==} + resolution: {integrity: sha512-LSwGGE/GceTbkDHRoQQr3lq/dpxxA6RdooBbuGTJ/S6BdqKX2kw2FdV7Se9eJzZJJJ5AxCZum37+ejYS7N2pJg==, tarball: https://registry.npmjs.org/@sentry/node/-/node-10.76.2.tgz} engines: {node: '>=18'} '@sentry/opentelemetry@10.76.2': - resolution: {integrity: sha512-deqWrKJGaku3nhiZchg34z8W9v6kUwtzoQ4cmEq6CeT8/4X93X4YfeJgoh1dhbswFRpwor4pQ6kj0KttagKtfA==} + resolution: {integrity: sha512-deqWrKJGaku3nhiZchg34z8W9v6kUwtzoQ4cmEq6CeT8/4X93X4YfeJgoh1dhbswFRpwor4pQ6kj0KttagKtfA==, tarball: https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.76.2.tgz} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 @@ -2507,150 +2507,150 @@ packages: '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 '@sentry/server-utils@10.76.2': - resolution: {integrity: sha512-wogj6Trj8fAu+aD7HbpvHEBlyANMOvvO9MP5NFJaiQbprLKugtCdE6rFTxDhofXvRo4nswL9m4Cvysg3k5KcvQ==} + resolution: {integrity: sha512-wogj6Trj8fAu+aD7HbpvHEBlyANMOvvO9MP5NFJaiQbprLKugtCdE6rFTxDhofXvRo4nswL9m4Cvysg3k5KcvQ==, tarball: https://registry.npmjs.org/@sentry/server-utils/-/server-utils-10.76.2.tgz} engines: {node: '>=18'} '@shaderfrog/glsl-parser@7.0.1': - resolution: {integrity: sha512-8mpfsoPeRhesY3pOrzNZBL8uG6N5GVX1EHLBYbd4gzKs+c7vaEIqpTNK5VrffU33qQN4cwpP2v3u4aPPBU32sw==} + resolution: {integrity: sha512-8mpfsoPeRhesY3pOrzNZBL8uG6N5GVX1EHLBYbd4gzKs+c7vaEIqpTNK5VrffU33qQN4cwpP2v3u4aPPBU32sw==, tarball: https://registry.npmjs.org/@shaderfrog/glsl-parser/-/glsl-parser-7.0.1.tgz} engines: {node: '>=16'} '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} '@solid-primitives/event-listener@2.4.6': - resolution: {integrity: sha512-5I0YJcTVYIWoMmgBSROBZGcz+ymhew/pGTg2dHW74BUjFKsV8Li4bOZYl0YAGP4mHw5o4UBd9/BEesqBci3wxw==} + resolution: {integrity: sha512-5I0YJcTVYIWoMmgBSROBZGcz+ymhew/pGTg2dHW74BUjFKsV8Li4bOZYl0YAGP4mHw5o4UBd9/BEesqBci3wxw==, tarball: https://registry.npmjs.org/@solid-primitives/event-listener/-/event-listener-2.4.6.tgz} peerDependencies: solid-js: ^1.6.12 '@solid-primitives/keyed@1.5.3': - resolution: {integrity: sha512-zNadtyYBhJSOjXtogkGHmRxjGdz9KHc8sGGVAGlUABkE8BED2tbIZoxkwSqzOwde8OcUEH0bb5DLZUWIMvyBSA==} + resolution: {integrity: sha512-zNadtyYBhJSOjXtogkGHmRxjGdz9KHc8sGGVAGlUABkE8BED2tbIZoxkwSqzOwde8OcUEH0bb5DLZUWIMvyBSA==, tarball: https://registry.npmjs.org/@solid-primitives/keyed/-/keyed-1.5.3.tgz} peerDependencies: solid-js: ^1.6.12 '@solid-primitives/map@0.4.13': - resolution: {integrity: sha512-B1zyFbsiTQvqPr+cuPCXO72sRuczG9Swncqk5P74NCGw1VE8qa/Ry9GlfI1e/VdeQYHjan+XkbE3rO2GW/qKew==} + resolution: {integrity: sha512-B1zyFbsiTQvqPr+cuPCXO72sRuczG9Swncqk5P74NCGw1VE8qa/Ry9GlfI1e/VdeQYHjan+XkbE3rO2GW/qKew==, tarball: https://registry.npmjs.org/@solid-primitives/map/-/map-0.4.13.tgz} peerDependencies: solid-js: ^1.6.12 '@solid-primitives/media@2.3.6': - resolution: {integrity: sha512-pk49gPOq/UMRUJ+pTSrOfBiR8xJjRYHXIf1iR/jSnyQ/KroU+ZXhkZzavC7hvfp2vJeOTW5k2/HN0r3Q1VJ7Pw==} + resolution: {integrity: sha512-pk49gPOq/UMRUJ+pTSrOfBiR8xJjRYHXIf1iR/jSnyQ/KroU+ZXhkZzavC7hvfp2vJeOTW5k2/HN0r3Q1VJ7Pw==, tarball: https://registry.npmjs.org/@solid-primitives/media/-/media-2.3.6.tgz} peerDependencies: solid-js: ^1.6.12 '@solid-primitives/props@3.2.4': - resolution: {integrity: sha512-MXXdvi2TSB6d+0N6ueA/HP1j/Kh9SEc4WdF1ZDMLwPagxW6pIHHSS9xbRO0RjqSVpNP4j0nxCWT1hx3CkJNhNA==} + resolution: {integrity: sha512-MXXdvi2TSB6d+0N6ueA/HP1j/Kh9SEc4WdF1ZDMLwPagxW6pIHHSS9xbRO0RjqSVpNP4j0nxCWT1hx3CkJNhNA==, tarball: https://registry.npmjs.org/@solid-primitives/props/-/props-3.2.4.tgz} peerDependencies: solid-js: ^1.6.12 '@solid-primitives/refs@1.1.4': - resolution: {integrity: sha512-bLjwIs6ZPu8NQnuw04sU3Zc8qKSpbc0umUU/O4SHf6oWOdO4+dHY8vb1T7C4b/Tg103+9WGr6sEE0+NFlbaB/A==} + resolution: {integrity: sha512-bLjwIs6ZPu8NQnuw04sU3Zc8qKSpbc0umUU/O4SHf6oWOdO4+dHY8vb1T7C4b/Tg103+9WGr6sEE0+NFlbaB/A==, tarball: https://registry.npmjs.org/@solid-primitives/refs/-/refs-1.1.4.tgz} peerDependencies: solid-js: ^1.6.12 '@solid-primitives/resize-observer@2.2.0': - resolution: {integrity: sha512-9Fuu/EWBeGj+atGHRJp70HKhdfalmpjwxY8a32NZixdLNmfCJ45AfhLQNr6uOzETbbiMx4iCKlTrJ8KZCHC2Ww==} + resolution: {integrity: sha512-9Fuu/EWBeGj+atGHRJp70HKhdfalmpjwxY8a32NZixdLNmfCJ45AfhLQNr6uOzETbbiMx4iCKlTrJ8KZCHC2Ww==, tarball: https://registry.npmjs.org/@solid-primitives/resize-observer/-/resize-observer-2.2.0.tgz} peerDependencies: solid-js: ^1.6.12 '@solid-primitives/rootless@1.5.4': - resolution: {integrity: sha512-TOIZa1VUfVJ+9nkCcRajw3U4t9vBOP1HxX1WHNTbXq32mXwlqTvUnC4CRIilohcryBkT9u2ZkhUDSHRTaGp55g==} + resolution: {integrity: sha512-TOIZa1VUfVJ+9nkCcRajw3U4t9vBOP1HxX1WHNTbXq32mXwlqTvUnC4CRIilohcryBkT9u2ZkhUDSHRTaGp55g==, tarball: https://registry.npmjs.org/@solid-primitives/rootless/-/rootless-1.5.4.tgz} peerDependencies: solid-js: ^1.6.12 '@solid-primitives/static-store@0.1.4': - resolution: {integrity: sha512-LgtVaVBtB7EbmS4+M0b8xY5Iq6pUWXBsIC4VgtrFKDGDdyCaDt88sHk0fUlx1Enxm/XZnZyLXJABRoa39RjJqA==} + resolution: {integrity: sha512-LgtVaVBtB7EbmS4+M0b8xY5Iq6pUWXBsIC4VgtrFKDGDdyCaDt88sHk0fUlx1Enxm/XZnZyLXJABRoa39RjJqA==, tarball: https://registry.npmjs.org/@solid-primitives/static-store/-/static-store-0.1.4.tgz} peerDependencies: solid-js: ^1.6.12 '@solid-primitives/storage@1.3.11': - resolution: {integrity: sha512-PpQWR3TaTxHIJFbI9ZssYTM4Aa67g1vJIgps4TPhcXzHqqomrPAIveFC2FG7SDQoi9YQia8FVBjigELziJpfIg==} + resolution: {integrity: sha512-PpQWR3TaTxHIJFbI9ZssYTM4Aa67g1vJIgps4TPhcXzHqqomrPAIveFC2FG7SDQoi9YQia8FVBjigELziJpfIg==, tarball: https://registry.npmjs.org/@solid-primitives/storage/-/storage-1.3.11.tgz} peerDependencies: solid-js: ^1.6.12 '@solid-primitives/transition-group@1.1.2': - resolution: {integrity: sha512-gnHS0OmcdjeoHN9n7Khu8KNrOlRc8a2weETDt2YT6o1zeW/XtUC6Db3Q9pkMU/9cCKdEmN4b0a/41MKAHRhzWA==} + resolution: {integrity: sha512-gnHS0OmcdjeoHN9n7Khu8KNrOlRc8a2weETDt2YT6o1zeW/XtUC6Db3Q9pkMU/9cCKdEmN4b0a/41MKAHRhzWA==, tarball: https://registry.npmjs.org/@solid-primitives/transition-group/-/transition-group-1.1.2.tgz} peerDependencies: solid-js: ^1.6.12 '@solid-primitives/trigger@1.2.4': - resolution: {integrity: sha512-Ju0e+ZOD7hpOp7nptJimvDSZHWFvIvF9iBWMvuwt30smX7c5wmB8Kmc0AdDuv0wOTi06PQ1J5IrP1WJbH2yUBQ==} + resolution: {integrity: sha512-Ju0e+ZOD7hpOp7nptJimvDSZHWFvIvF9iBWMvuwt30smX7c5wmB8Kmc0AdDuv0wOTi06PQ1J5IrP1WJbH2yUBQ==, tarball: https://registry.npmjs.org/@solid-primitives/trigger/-/trigger-1.2.4.tgz} peerDependencies: solid-js: ^1.6.12 '@solid-primitives/utils@6.4.1': - resolution: {integrity: sha512-ISSB5QX1qP2ynrheIpYwc4oKR5Ny4siNuUyf1qZniy+Il+p/PtDB0QK1Dnle8noiHpwRD3gpPdubOC3qI/Zamg==} + resolution: {integrity: sha512-ISSB5QX1qP2ynrheIpYwc4oKR5Ny4siNuUyf1qZniy+Il+p/PtDB0QK1Dnle8noiHpwRD3gpPdubOC3qI/Zamg==, tarball: https://registry.npmjs.org/@solid-primitives/utils/-/utils-6.4.1.tgz} peerDependencies: solid-js: ^1.6.12 '@standard-schema/spec@1.1.0': - resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==, tarball: https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz} '@swc/helpers@0.5.23': - resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} + resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==, tarball: https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz} '@tailwindcss/node@4.3.3': resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==} '@tailwindcss/oxide-android-arm64@4.3.3': - resolution: {integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==} + resolution: {integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz} engines: {node: '>= 20'} cpu: [arm64] os: [android] '@tailwindcss/oxide-darwin-arm64@4.3.3': - resolution: {integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==} + resolution: {integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz} engines: {node: '>= 20'} cpu: [arm64] os: [darwin] '@tailwindcss/oxide-darwin-x64@4.3.3': - resolution: {integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==} + resolution: {integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz} engines: {node: '>= 20'} cpu: [x64] os: [darwin] '@tailwindcss/oxide-freebsd-x64@4.3.3': - resolution: {integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==} + resolution: {integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz} engines: {node: '>= 20'} cpu: [x64] os: [freebsd] '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': - resolution: {integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==} + resolution: {integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz} engines: {node: '>= 20'} cpu: [arm] os: [linux] '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': - resolution: {integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==} + resolution: {integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz} engines: {node: '>= 20'} cpu: [arm64] os: [linux] libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.3.3': - resolution: {integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==} + resolution: {integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz} engines: {node: '>= 20'} cpu: [arm64] os: [linux] libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.3.3': - resolution: {integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==} + resolution: {integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz} engines: {node: '>= 20'} cpu: [x64] os: [linux] libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.3.3': - resolution: {integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==} + resolution: {integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz} engines: {node: '>= 20'} cpu: [x64] os: [linux] libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.3.3': - resolution: {integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==} + resolution: {integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -2662,13 +2662,13 @@ packages: - tslib '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': - resolution: {integrity: sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==} + resolution: {integrity: sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz} engines: {node: '>= 20'} cpu: [arm64] os: [win32] '@tailwindcss/oxide-win32-x64-msvc@4.3.3': - resolution: {integrity: sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==} + resolution: {integrity: sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz} engines: {node: '>= 20'} cpu: [x64] os: [win32] @@ -2682,19 +2682,19 @@ packages: peerDependencies: vite: ^5.2.0 || ^6 || ^7 || ^8 - '@tangent/embed-react@https://codeload.github.com/TangleML/tangent-shell/tar.gz/1998f551d910e229d5108045b552b5549635cd58#path:packages/embed-react': - resolution: {path: packages/embed-react, tarball: https://codeload.github.com/TangleML/tangent-shell/tar.gz/1998f551d910e229d5108045b552b5549635cd58} + '@tangent/embed-react@https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/embed-react': + resolution: {path: packages/embed-react, tarball: https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a} version: 0.1.1 peerDependencies: react: ^19.0.0 react-dom: ^19.0.0 - '@tangent/remote-subagent@https://codeload.github.com/TangleML/tangent-shell/tar.gz/1998f551d910e229d5108045b552b5549635cd58#path:packages/remote-subagent': - resolution: {path: packages/remote-subagent, tarball: https://codeload.github.com/TangleML/tangent-shell/tar.gz/1998f551d910e229d5108045b552b5549635cd58} + '@tangent/remote-subagent@https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/remote-subagent': + resolution: {path: packages/remote-subagent, tarball: https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a} version: 0.0.0 - '@tangent/shared@https://codeload.github.com/TangleML/tangent-shell/tar.gz/1998f551d910e229d5108045b552b5549635cd58#path:packages/shared': - resolution: {path: packages/shared, tarball: https://codeload.github.com/TangleML/tangent-shell/tar.gz/1998f551d910e229d5108045b552b5549635cd58} + '@tangent/shared@https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/shared': + resolution: {path: packages/shared, tarball: https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a} version: 0.0.0 '@tanstack/history@1.162.5': @@ -2702,17 +2702,17 @@ packages: engines: {node: '>=20.19'} '@tanstack/match-sorter-utils@9.2.6': - resolution: {integrity: sha512-DIpUSOhPfQ7u/qfUi/Wgm0wBO56MyBYXGvnlI0NcauDRPqdBUlAZZjW2Dxm92m+k1UWVhKw8YJ4f47+75Aujyw==} + resolution: {integrity: sha512-DIpUSOhPfQ7u/qfUi/Wgm0wBO56MyBYXGvnlI0NcauDRPqdBUlAZZjW2Dxm92m+k1UWVhKw8YJ4f47+75Aujyw==, tarball: https://registry.npmjs.org/@tanstack/match-sorter-utils/-/match-sorter-utils-9.2.6.tgz} engines: {node: '>=20'} '@tanstack/query-core@5.104.5': - resolution: {integrity: sha512-Q94gI8Opd+CbdVNOdj8ZCyV8WqLYitrDq1t96Xc8WZT6lY0r5iRnJLXYDxMex7EEf2v+mXvn4FcjaBKSxXZQ9A==} + resolution: {integrity: sha512-Q94gI8Opd+CbdVNOdj8ZCyV8WqLYitrDq1t96Xc8WZT6lY0r5iRnJLXYDxMex7EEf2v+mXvn4FcjaBKSxXZQ9A==, tarball: https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.104.5.tgz} '@tanstack/query-devtools@5.104.5': - resolution: {integrity: sha512-BLVEw4lFF7aIRsfqGvI50SSR9oN46/EmlygC+3h5TUdI+fL8GnMQialL3H92TbYi7J1b/hYlUGnckBT2uG5h8w==} + resolution: {integrity: sha512-BLVEw4lFF7aIRsfqGvI50SSR9oN46/EmlygC+3h5TUdI+fL8GnMQialL3H92TbYi7J1b/hYlUGnckBT2uG5h8w==, tarball: https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.104.5.tgz} '@tanstack/react-query-devtools@5.104.5': - resolution: {integrity: sha512-2SMhSSYnOGSaPLuSTKLOj5nPoP4sqasOfkpTn5BLe6LIZuHzrpcqrJx0Rm8uUSN5Ob4lPAbh/1oWClnnGaR7Bg==} + resolution: {integrity: sha512-2SMhSSYnOGSaPLuSTKLOj5nPoP4sqasOfkpTn5BLe6LIZuHzrpcqrJx0Rm8uUSN5Ob4lPAbh/1oWClnnGaR7Bg==, tarball: https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.104.5.tgz} peerDependencies: '@tanstack/react-query': ^5.104.5 '@types/react': ^18 || ^19 @@ -2722,7 +2722,7 @@ packages: optional: true '@tanstack/react-query@5.104.5': - resolution: {integrity: sha512-RORgFe052U+93weaNuqg7qHq7vNVNzXN/VrXqD4bbAuQCWUqOl2lV5Ui9flWk5CVvMyL/Bh27QWhzwH0aaOx9g==} + resolution: {integrity: sha512-RORgFe052U+93weaNuqg7qHq7vNVNzXN/VrXqD4bbAuQCWUqOl2lV5Ui9flWk5CVvMyL/Bh27QWhzwH0aaOx9g==, tarball: https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.104.5.tgz} peerDependencies: '@types/react': ^18 || ^19 react: ^18 || ^19 @@ -2750,7 +2750,7 @@ packages: react-dom: '>=18.0.0 || >=19.0.0' '@tanstack/react-store@0.11.2': - resolution: {integrity: sha512-oj5O5DmiMWE+ih+SioX2ayC1kbK7d3fpLx0B75fPJr9nY3zU+hoW010yr64qXsY/9fyiLVE2bn5mGNV3owm8RQ==} + resolution: {integrity: sha512-oj5O5DmiMWE+ih+SioX2ayC1kbK7d3fpLx0B75fPJr9nY3zU+hoW010yr64qXsY/9fyiLVE2bn5mGNV3owm8RQ==, tarball: https://registry.npmjs.org/@tanstack/react-store/-/react-store-0.11.2.tgz} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -2782,7 +2782,7 @@ packages: optional: true '@tanstack/store@0.11.2': - resolution: {integrity: sha512-sJ4mjol8uQsHV0gOJzzjwXfh2Fwm+Sz0+8deqiTm4jGbMdjzNSW+xZCFm0kUa870uhd8yi+DpKnZb5Kc4apa0Q==} + resolution: {integrity: sha512-sJ4mjol8uQsHV0gOJzzjwXfh2Fwm+Sz0+8deqiTm4jGbMdjzNSW+xZCFm0kUa870uhd8yi+DpKnZb5Kc4apa0Q==, tarball: https://registry.npmjs.org/@tanstack/store/-/store-0.11.2.tgz} '@testing-library/dom@10.4.2': resolution: {integrity: sha512-yzr2S9HyAIdhz2/6qHgbs665Q7PKVcDF05vsOlHPxG1mo36gKVesdYVeDLnXgfjJ03CrKRk08knc6+E/9m8v2Q==} @@ -2814,13 +2814,13 @@ packages: optional: true '@testing-library/user-event@14.6.8': - resolution: {integrity: sha512-vgSSqnANMnkcYR/vUNDbwS7aM3OErRLNUG8vCVVlj4oc6cKffUrccX7UAS5AmJQQNZTSibi9z5GvvRZ0aYgUCQ==} + resolution: {integrity: sha512-vgSSqnANMnkcYR/vUNDbwS7aM3OErRLNUG8vCVVlj4oc6cKffUrccX7UAS5AmJQQNZTSibi9z5GvvRZ0aYgUCQ==, tarball: https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.8.tgz} engines: {node: '>=12', npm: '>=6'} peerDependencies: '@testing-library/dom': '>=7.21.4' '@tybys/wasm-util@0.10.4': - resolution: {integrity: sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==} + resolution: {integrity: sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==, tarball: https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.4.tgz} '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} @@ -2856,7 +2856,7 @@ packages: resolution: {integrity: sha512-cMQm7pxu6BxtHyqJ7mQZ2kXWV5SLmugybFdHCBbJ5eHzOo6VhBckEgAT3//rP5FwPHNPeEiq4SmQ5ucBwsOo4Q==} '@types/esrecurse@4.3.1': - resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==, tarball: https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz} '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} @@ -2871,13 +2871,13 @@ packages: resolution: {integrity: sha512-/ZsLuq6BffMgbKMtZyDZ8vwQvTyKhKQ1G2K6VyWCgtHHhfSSXbk4+4JwImZiTjWNXfI2q1ZStAwFFHSkNoTkHA==} '@types/gensync@1.0.5': - resolution: {integrity: sha512-MbsRCT7mTikHwKZ0X+LVUTLRrZZRLipTuXEO9qOYO+zmjMVk81axyClMROf6uoPD9MRVu46bx8zoR0Ad9q3NAg==} + resolution: {integrity: sha512-MbsRCT7mTikHwKZ0X+LVUTLRrZZRLipTuXEO9qOYO+zmjMVk81axyClMROf6uoPD9MRVu46bx8zoR0Ad9q3NAg==, tarball: https://registry.npmjs.org/@types/gensync/-/gensync-1.0.5.tgz} '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} '@types/jsesc@2.5.1': - resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} + resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==, tarball: https://registry.npmjs.org/@types/jsesc/-/jsesc-2.5.1.tgz} '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -2918,10 +2918,10 @@ packages: resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} '@types/ws@8.18.2': - resolution: {integrity: sha512-67MQl+fpWKVTT1NYdnmo3U4sc/xPo/zQBncVnI74qmQa0z/b+1g6iYqNmGCPbxO+zz2aklb08a0oHfegiVd0/w==} + resolution: {integrity: sha512-67MQl+fpWKVTT1NYdnmo3U4sc/xPo/zQBncVnI74qmQa0z/b+1g6iYqNmGCPbxO+zz2aklb08a0oHfegiVd0/w==, tarball: https://registry.npmjs.org/@types/ws/-/ws-8.18.2.tgz} '@typescript-eslint/eslint-plugin@8.72.0': - resolution: {integrity: sha512-aysKgysPX9XOv/dk95UkvUDe++Q3n3dJPG9CEWT3RUccxfkPJ+Qn6nhKPDhZXU6Ofo9kgwGWwKyQCnZIXTgngg==} + resolution: {integrity: sha512-aysKgysPX9XOv/dk95UkvUDe++Q3n3dJPG9CEWT3RUccxfkPJ+Qn6nhKPDhZXU6Ofo9kgwGWwKyQCnZIXTgngg==, tarball: https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.72.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.72.0 @@ -2929,54 +2929,54 @@ packages: typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/parser@8.72.0': - resolution: {integrity: sha512-0O+x3ynciD6zuXb1CKSu165haQV6ey53DKhHHUnujP7U+XlltJG+/keT0Xhd1HXCze+IHf7Tvt39ovyepX/LNw==} + resolution: {integrity: sha512-0O+x3ynciD6zuXb1CKSu165haQV6ey53DKhHHUnujP7U+XlltJG+/keT0Xhd1HXCze+IHf7Tvt39ovyepX/LNw==, tarball: https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.72.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/project-service@8.72.0': - resolution: {integrity: sha512-bzHngBbmIgzj6TsTjVUCntGx6FFLgE7mPjpO0PzHycOaZHXqS43XnrkFIHRS16LLr/xO6FHSXG3GJFo/F4bSBQ==} + resolution: {integrity: sha512-bzHngBbmIgzj6TsTjVUCntGx6FFLgE7mPjpO0PzHycOaZHXqS43XnrkFIHRS16LLr/xO6FHSXG3GJFo/F4bSBQ==, tarball: https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.72.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/scope-manager@8.72.0': - resolution: {integrity: sha512-JewPyJOYcXBSAS7PIgufscHOwZqhTGyBRiQJhhuDln66tMchxvvt7nJ90JKKIoOelmdHA3kAQbm9cEUHc4JSEA==} + resolution: {integrity: sha512-JewPyJOYcXBSAS7PIgufscHOwZqhTGyBRiQJhhuDln66tMchxvvt7nJ90JKKIoOelmdHA3kAQbm9cEUHc4JSEA==, tarball: https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.72.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.72.0': - resolution: {integrity: sha512-0vbxCf7xLVPSjcOdm6eQbgViH8EDJ8xnl1laLT9yoqSz2YmcSfSg5Vdl0JUvZf6rcuJ0ZivJIuMD4edTg8obaA==} + resolution: {integrity: sha512-0vbxCf7xLVPSjcOdm6eQbgViH8EDJ8xnl1laLT9yoqSz2YmcSfSg5Vdl0JUvZf6rcuJ0ZivJIuMD4edTg8obaA==, tarball: https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.72.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/type-utils@8.72.0': - resolution: {integrity: sha512-gEXxmmbSTSOVTC37IYYvremOM31v+UN2W772bJxmVYPhAOPSUxtacyb7wkm1EhkYKU6KzFJsdU44DCV4to0LWw==} + resolution: {integrity: sha512-gEXxmmbSTSOVTC37IYYvremOM31v+UN2W772bJxmVYPhAOPSUxtacyb7wkm1EhkYKU6KzFJsdU44DCV4to0LWw==, tarball: https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.72.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/types@8.72.0': - resolution: {integrity: sha512-Adfoy59kxNEjT5E5fnyrp7GpHYuWs8ONbdohu4ez6IU46qDq8LltG90tLmkucZB2uhYL8mqBjtXqGd8yhfjXqQ==} + resolution: {integrity: sha512-Adfoy59kxNEjT5E5fnyrp7GpHYuWs8ONbdohu4ez6IU46qDq8LltG90tLmkucZB2uhYL8mqBjtXqGd8yhfjXqQ==, tarball: https://registry.npmjs.org/@typescript-eslint/types/-/types-8.72.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@8.72.0': - resolution: {integrity: sha512-gwLfFgWcw15tN2KXDCJvxEOQjPn+P3j/n3eSqkZ5sX4VpW9Cosodr+tWwBZRvJT3TcKx6HnPRGZMCNAiP1Xhug==} + resolution: {integrity: sha512-gwLfFgWcw15tN2KXDCJvxEOQjPn+P3j/n3eSqkZ5sX4VpW9Cosodr+tWwBZRvJT3TcKx6HnPRGZMCNAiP1Xhug==, tarball: https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.72.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/utils@8.72.0': - resolution: {integrity: sha512-0up3oArJZBYnzOaEXKo8DmI43QvMZyPoFEt9LWzRJcyl/RcJjfKKPebknN1VbTgXBf3M/KggUh5uUMdURLnseg==} + resolution: {integrity: sha512-0up3oArJZBYnzOaEXKo8DmI43QvMZyPoFEt9LWzRJcyl/RcJjfKKPebknN1VbTgXBf3M/KggUh5uUMdURLnseg==, tarball: https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.72.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/visitor-keys@8.72.0': - resolution: {integrity: sha512-E2mroviasaWecDzTGiGMtuEguuLVOeRggnrIn93CJuxYJzQBHbyymlT08aVQydbBal5538o+JkBKXh2sRuCyIA==} + resolution: {integrity: sha512-E2mroviasaWecDzTGiGMtuEguuLVOeRggnrIn93CJuxYJzQBHbyymlT08aVQydbBal5538o+JkBKXh2sRuCyIA==, tarball: https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.72.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.1': @@ -2999,7 +2999,7 @@ packages: optional: true '@vitest/coverage-v8@5.0.5': - resolution: {integrity: sha512-4KpkGdIrSqBihx/JyP/e+S+IKB98etg1XDQiLAxksLfcShnwi1VReGeOT9NI43hLHbknosKp/1CCLn9Zgcs8mg==} + resolution: {integrity: sha512-4KpkGdIrSqBihx/JyP/e+S+IKB98etg1XDQiLAxksLfcShnwi1VReGeOT9NI43hLHbknosKp/1CCLn9Zgcs8mg==, tarball: https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-5.0.5.tgz} peerDependencies: '@vitest/browser': 5.0.5 vitest: 5.0.5 @@ -3011,11 +3011,11 @@ packages: resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==} '@vitest/istanbul-lib-coverage@1.0.2': - resolution: {integrity: sha512-9J/JMwOf9AoJhAywhrn7ScKTL38hsWQP/qPG60OtaAFcQ5OXPwKsxZFlbnuCKmZ61m8/lGgHYnFpdyQZUvG/iA==} + resolution: {integrity: sha512-9J/JMwOf9AoJhAywhrn7ScKTL38hsWQP/qPG60OtaAFcQ5OXPwKsxZFlbnuCKmZ61m8/lGgHYnFpdyQZUvG/iA==, tarball: https://registry.npmjs.org/@vitest/istanbul-lib-coverage/-/istanbul-lib-coverage-1.0.2.tgz} engines: {node: '>=22'} '@vitest/istanbul-lib-report@1.0.2': - resolution: {integrity: sha512-gUsfXZJbzPamoIY5TvHFiMMoXESBrUMo+xqaj+rYrWI69+EnvRlYBlP96ZnHPY4vX8kyUpgAnFUCg5wZG/HkDQ==} + resolution: {integrity: sha512-gUsfXZJbzPamoIY5TvHFiMMoXESBrUMo+xqaj+rYrWI69+EnvRlYBlP96ZnHPY4vX8kyUpgAnFUCg5wZG/HkDQ==, tarball: https://registry.npmjs.org/@vitest/istanbul-lib-report/-/istanbul-lib-report-1.0.2.tgz} engines: {node: '>=22'} '@vitest/mocker@4.1.10': @@ -3061,36 +3061,36 @@ packages: resolution: {integrity: sha512-a//eDjez3WZF47aaPTfVUDD0OINOq+WVwBZCRTqG+m0a5euX8EwlJ4jMY1Wxz0Ocj4xOeekJ70V22Pq0ll8RJw==} acorn-jsx-walk@2.0.0: - resolution: {integrity: sha512-uuo6iJj4D4ygkdzd6jPtcxs8vZgDX9YFIkqczGImoypX2fQ4dVImmu3UzA4ynixCIMTrEOWW+95M2HuBaCEOVA==} + resolution: {integrity: sha512-uuo6iJj4D4ygkdzd6jPtcxs8vZgDX9YFIkqczGImoypX2fQ4dVImmu3UzA4ynixCIMTrEOWW+95M2HuBaCEOVA==, tarball: https://registry.npmjs.org/acorn-jsx-walk/-/acorn-jsx-walk-2.0.0.tgz} acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, tarball: https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 acorn-loose@8.5.2: - resolution: {integrity: sha512-PPvV6g8UGMGgjrMu+n/f9E/tCSkNQ2Y97eFvuVdJfG11+xdIeDcLyNdC8SHcrHbRqkfwLASdplyR6B6sKM1U4A==} + resolution: {integrity: sha512-PPvV6g8UGMGgjrMu+n/f9E/tCSkNQ2Y97eFvuVdJfG11+xdIeDcLyNdC8SHcrHbRqkfwLASdplyR6B6sKM1U4A==, tarball: https://registry.npmjs.org/acorn-loose/-/acorn-loose-8.5.2.tgz} engines: {node: '>=0.4.0'} acorn-walk@8.3.5: - resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} + resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==, tarball: https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz} engines: {node: '>=0.4.0'} acorn@8.19.0: - resolution: {integrity: sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==} + resolution: {integrity: sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==, tarball: https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz} engines: {node: '>=0.4.0'} hasBin: true agent-install@0.0.5: - resolution: {integrity: sha512-nHlms9BkP8ZiY79HrwCGiA2DcNaXrAaJrCM/BEqQ7MEsSKyCk+2A76xPGylIfASZSZE0SaU3T0bNSg4rBPIJAQ==} + resolution: {integrity: sha512-nHlms9BkP8ZiY79HrwCGiA2DcNaXrAaJrCM/BEqQ7MEsSKyCk+2A76xPGylIfASZSZE0SaU3T0bNSg4rBPIJAQ==, tarball: https://registry.npmjs.org/agent-install/-/agent-install-0.0.5.tgz} hasBin: true agent-install@0.0.6: - resolution: {integrity: sha512-7NRMZ/ZDz2vHevQTgJsocBFpakB1/Wx5ip19YSJuj4VOXpraWztTerViNtdSyARKZT9e2yVwUUB5JXXCE7mNrA==} + resolution: {integrity: sha512-7NRMZ/ZDz2vHevQTgJsocBFpakB1/Wx5ip19YSJuj4VOXpraWztTerViNtdSyARKZT9e2yVwUUB5JXXCE7mNrA==, tarball: https://registry.npmjs.org/agent-install/-/agent-install-0.0.6.tgz} hasBin: true ajv-formats@3.0.1: - resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==, tarball: https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz} peerDependencies: ajv: ^8.0.0 peerDependenciesMeta: @@ -3101,7 +3101,7 @@ packages: resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} ajv@8.20.0: - resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==, tarball: https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz} ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} @@ -3112,7 +3112,7 @@ packages: engines: {node: '>=8'} ansi-regex@6.4.0: - resolution: {integrity: sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==} + resolution: {integrity: sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==, tarball: https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz} engines: {node: '>=12'} ansi-styles@3.2.1: @@ -3131,7 +3131,7 @@ packages: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} argparse@3.0.3: - resolution: {integrity: sha512-Sn98ftV3KfsO8xfLPHG6oT8y+b0yvbrLgZjgPDXsharNY15tTAjqIsIju4S32nl6/iEUNNE4v+yL2Zhm8ooP3w==} + resolution: {integrity: sha512-Sn98ftV3KfsO8xfLPHG6oT8y+b0yvbrLgZjgPDXsharNY15tTAjqIsIju4S32nl6/iEUNNE4v+yL2Zhm8ooP3w==, tarball: https://registry.npmjs.org/argparse/-/argparse-3.0.3.tgz} aria-hidden@1.2.6: resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} @@ -3193,7 +3193,7 @@ packages: engines: {node: '>=12'} ast-v8-to-istanbul@1.0.7: - resolution: {integrity: sha512-kFL68AG6ajd8fg248zwM9GQrUWEp79gsmjum34OEXjs4yHuUMZfYKwOLW9GMmB4oNvVrj+EAGxsP7ye2UR9UlA==} + resolution: {integrity: sha512-kFL68AG6ajd8fg248zwM9GQrUWEp79gsmjum34OEXjs4yHuUMZfYKwOLW9GMmB4oNvVrj+EAGxsP7ye2UR9UlA==, tarball: https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.7.tgz} async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} @@ -3206,7 +3206,7 @@ packages: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} atomically@2.1.1: - resolution: {integrity: sha512-P4w9o2dqARji6P7MHprklbfiArZAWvo07yW7qs3pdljb3BWr12FIB7W+p0zJiuiVsUpRO0iZn1kFFcpPegg0tQ==} + resolution: {integrity: sha512-P4w9o2dqARji6P7MHprklbfiArZAWvo07yW7qs3pdljb3BWr12FIB7W+p0zJiuiVsUpRO0iZn1kFFcpPegg0tQ==, tarball: https://registry.npmjs.org/atomically/-/atomically-2.1.1.tgz} available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} @@ -3222,7 +3222,7 @@ packages: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} balanced-match@4.0.4: - resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==, tarball: https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz} engines: {node: 18 || 20 || >=22} baseline-browser-mapping@2.11.32: @@ -3231,7 +3231,7 @@ packages: hasBin: true bidi-js@1.1.0: - resolution: {integrity: sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg==} + resolution: {integrity: sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg==, tarball: https://registry.npmjs.org/bidi-js/-/bidi-js-1.1.0.tgz} bippy@0.5.41: resolution: {integrity: sha512-jCP2pXXLhXqPrAN+iSEFZmLI4uUM4fjSqajh0K+TmM062VehfDT3ZJNkrTGyN701Z5XMejs9qAudSqkMGhSMKg==} @@ -3239,7 +3239,7 @@ packages: react: '>=17.0.1' bippy@0.6.1: - resolution: {integrity: sha512-ky4m94Y/KfsddjGkKTsV4uFjZqkJjpOjQ2t5gKPdX6XH1MNxMNX5FrVefsxV4lpjemEmEdwe0e0YbzAMNs3oUQ==} + resolution: {integrity: sha512-ky4m94Y/KfsddjGkKTsV4uFjZqkJjpOjQ2t5gKPdX6XH1MNxMNX5FrVefsxV4lpjemEmEdwe0e0YbzAMNs3oUQ==, tarball: https://registry.npmjs.org/bippy/-/bippy-0.6.1.tgz} peerDependencies: react: '>=17.0.1' @@ -3247,15 +3247,15 @@ packages: resolution: {integrity: sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==} brace-expansion@5.0.12: - resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} + resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==, tarball: https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz} engines: {node: 20 || >=22} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.29.4: - resolution: {integrity: sha512-2TcbbiwXEq+N0kD36tykpCfyyQigUXmuwNQbnPp4Xk4PX9JOqVIKjJlIDjpH7uY6tmRGoAd+apeT1p60T8wsdA==} + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -3325,7 +3325,7 @@ packages: engines: {node: '>=10'} chalk@5.6.2: - resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==, tarball: https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} character-entities-html4@2.1.0: @@ -3345,7 +3345,7 @@ packages: engines: {node: '>= 20.19.0'} cjs-module-lexer@2.3.0: - resolution: {integrity: sha512-lmNyBzi6iYiqyrExTctfmjd8tInx+dHu9PBomTfH5RXS53c27JMFJgzNv5TyAnm3PgGf+MLy0+Ws41/HlVxdKQ==} + resolution: {integrity: sha512-lmNyBzi6iYiqyrExTctfmjd8tInx+dHu9PBomTfH5RXS53c27JMFJgzNv5TyAnm3PgGf+MLy0+Ws41/HlVxdKQ==, tarball: https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.3.0.tgz} class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} @@ -3354,11 +3354,11 @@ packages: resolution: {integrity: sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==} cli-cursor@5.0.0: - resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==, tarball: https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz} engines: {node: '>=18'} cli-spinners@3.4.0: - resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==} + resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==, tarball: https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.4.0.tgz} engines: {node: '>=18.20'} clsx@2.1.1: @@ -3433,7 +3433,7 @@ packages: engines: {'0': node >= 6.0} conf@15.1.0: - resolution: {integrity: sha512-Uy5YN9KEu0WWDaZAVJ5FAmZoaJt9rdK6kH+utItPyGsCqCgaTKkrmZx3zoE0/3q6S3bcp3Ihkk+ZqPxWxFK5og==} + resolution: {integrity: sha512-Uy5YN9KEu0WWDaZAVJ5FAmZoaJt9rdK6kH+utItPyGsCqCgaTKkrmZx3zoE0/3q6S3bcp3Ihkk+ZqPxWxFK5og==, tarball: https://registry.npmjs.org/conf/-/conf-15.1.0.tgz} engines: {node: '>=20'} confbox@0.2.4: @@ -3447,13 +3447,13 @@ packages: engines: {node: '>= 0.6'} convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==, tarball: https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz} cookie-es@3.1.1: - resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==} + resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==, tarball: https://registry.npmjs.org/cookie-es/-/cookie-es-3.1.1.tgz} copy-anything@4.1.5: - resolution: {integrity: sha512-vePst2VLTSqPglW7Q98VwFpGiGpOHTyXIo81MO5VyLecA5zMbZw/P/V1vmwD1jksa2iJpn/YkDrJ3XhTfzUyhQ==} + resolution: {integrity: sha512-vePst2VLTSqPglW7Q98VwFpGiGpOHTyXIo81MO5VyLecA5zMbZw/P/V1vmwD1jksa2iJpn/YkDrJ3XhTfzUyhQ==, tarball: https://registry.npmjs.org/copy-anything/-/copy-anything-4.1.5.tgz} engines: {node: '>=18'} core-util-is@1.0.3: @@ -3464,7 +3464,7 @@ packages: engines: {node: '>= 8'} css-tree@3.2.1: - resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==, tarball: https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} css.escape@1.5.1: @@ -3512,7 +3512,7 @@ packages: engines: {node: '>=12'} data-urls@8.0.0: - resolution: {integrity: sha512-JQUCCK+/CAGbGGx618PlFZMXH81Jtj7t73CHv9dd/zVWvxgGT2zLFRjnKb+Ng46GIwcy+f7f88qd4duqllK+WQ==} + resolution: {integrity: sha512-JQUCCK+/CAGbGGx618PlFZMXH81Jtj7t73CHv9dd/zVWvxgGT2zLFRjnKb+Ng46GIwcy+f7f88qd4duqllK+WQ==, tarball: https://registry.npmjs.org/data-urls/-/data-urls-8.0.0.tgz} engines: {node: ^22.14.0 || >=24.0.0} data-view-buffer@1.0.2: @@ -3531,7 +3531,7 @@ packages: resolution: {integrity: sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==} debounce-fn@6.0.0: - resolution: {integrity: sha512-rBMW+F2TXryBwB54Q0d8drNEI+TfoS9JpNTAoVpukbWEhjXQq4rySFYLaqXMFXwdv61Zb2OHtj5bviSoimqxRQ==} + resolution: {integrity: sha512-rBMW+F2TXryBwB54Q0d8drNEI+TfoS9JpNTAoVpukbWEhjXQq4rySFYLaqXMFXwdv61Zb2OHtj5bviSoimqxRQ==, tarball: https://registry.npmjs.org/debounce-fn/-/debounce-fn-6.0.0.tgz} engines: {node: '>=18'} debug@4.4.3: @@ -3552,7 +3552,7 @@ packages: engines: {node: '>=0.10.0'} decimal.js@10.6.0: - resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==, tarball: https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz} decode-named-character-reference@1.3.0: resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} @@ -3592,7 +3592,7 @@ packages: engines: {node: '>=0.4.0'} dependency-cruiser@18.7.0: - resolution: {integrity: sha512-WexT4OswA6Gy86ms2J7SJ4FJscN43dyS+i5skpbtXlPTFQW4yWbontjFip1TOBAVnc+U+9zdH0a56YTRZX/IGw==} + resolution: {integrity: sha512-WexT4OswA6Gy86ms2J7SJ4FJscN43dyS+i5skpbtXlPTFQW4yWbontjFip1TOBAVnc+U+9zdH0a56YTRZX/IGw==, tarball: https://registry.npmjs.org/dependency-cruiser/-/dependency-cruiser-18.7.0.tgz} engines: {node: ^22||^24||>=26} hasBin: true @@ -3640,7 +3640,7 @@ packages: resolution: {integrity: sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==} dot-prop@10.2.0: - resolution: {integrity: sha512-BTJ9aZYL3vCfZlZOBLy9v8TUqWGQ0pzFnygKwFZt5udj6viBoFIBviKPUoZLDCPn1FoXffv6McQFDenrm5Krfw==} + resolution: {integrity: sha512-BTJ9aZYL3vCfZlZOBLy9v8TUqWGQ0pzFnygKwFZt5udj6viBoFIBviKPUoZLDCPn1FoXffv6McQFDenrm5Krfw==, tarball: https://registry.npmjs.org/dot-prop/-/dot-prop-10.2.0.tgz} engines: {node: '>=20'} dotenv@17.4.2: @@ -3658,7 +3658,7 @@ packages: resolution: {integrity: sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==} empathic@2.1.0: - resolution: {integrity: sha512-AnfC1ATldl49/cvZdLPDjBfrRNwbDO05aibiOtzQu3qtlbJtomNLhF30HEtn/7iBz50dlMECqATo3fG0LrdEgw==} + resolution: {integrity: sha512-AnfC1ATldl49/cvZdLPDjBfrRNwbDO05aibiOtzQu3qtlbJtomNLhF30HEtn/7iBz50dlMECqATo3fG0LrdEgw==, tarball: https://registry.npmjs.org/empathic/-/empathic-2.1.0.tgz} engines: {node: '>=14'} end-of-stream@1.4.5: @@ -3672,15 +3672,15 @@ packages: engines: {node: '>=10.0.0'} enhanced-resolve@5.26.0: - resolution: {integrity: sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==} + resolution: {integrity: sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==, tarball: https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.26.0.tgz} engines: {node: '>=10.13.0'} entities@8.1.0: - resolution: {integrity: sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==} + resolution: {integrity: sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==, tarball: https://registry.npmjs.org/entities/-/entities-8.1.0.tgz} engines: {node: '>=20.19.0'} env-paths@3.0.0: - resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} + resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==, tarball: https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} error-ex@1.3.4: @@ -3708,8 +3708,8 @@ packages: es-module-lexer@2.3.1: resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==} - es-module-lexer@3.0.4: - resolution: {integrity: sha512-BldNY5mwkVu+cQ5Ymn3e6hB9Sx8XHx4ZevWbiUyZbfKxJxYO27Hf0wfzkxIaD5CAJMaGl3YspKELj0ff/7nCMA==} + es-module-lexer@3.0.3: + resolution: {integrity: sha512-yWGe1O/j+almnV4hOYrmZo8u58JdnmXEMneRQAYkWtqSiT86yoMIEKF4rpqEDj52tbYl3xceBD3DQIBwcsC9Pw==, tarball: https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-3.0.3.tgz} es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} @@ -3733,7 +3733,7 @@ packages: hasBin: true escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==, tarball: https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz} engines: {node: '>=6'} escape-string-regexp@1.0.5: @@ -3764,7 +3764,7 @@ packages: eslint: '>=7' eslint-plugin-react-hooks@7.1.1: - resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==} + resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==, tarball: https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz} engines: {node: '>=18'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 @@ -3785,7 +3785,7 @@ packages: engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-scope@9.1.2: - resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==, tarball: https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz} engines: {node: ^20.19.0 || ^22.13.0 || >=24} eslint-visitor-keys@3.4.3: @@ -3797,7 +3797,7 @@ packages: engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@5.0.1: - resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==, tarball: https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz} engines: {node: ^20.19.0 || ^22.13.0 || >=24} eslint@9.39.4: @@ -3841,11 +3841,11 @@ packages: engines: {node: '>=0.10.0'} eventsource-parser@3.1.1: - resolution: {integrity: sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==} + resolution: {integrity: sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==, tarball: https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz} engines: {node: '>=18.0.0'} eventsource@3.0.7: - resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==, tarball: https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz} engines: {node: '>=18.0.0'} expect-type@1.3.0: @@ -3859,7 +3859,7 @@ packages: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} fake-indexeddb@6.2.6: - resolution: {integrity: sha512-ihhmr0KoET51jT8R4YhbUYuLLvr1qqO6INISygtN16i8loR8kAxtAUr4+EKHTSMjL+lggaAn4OC7DRqzrNgnuQ==} + resolution: {integrity: sha512-ihhmr0KoET51jT8R4YhbUYuLLvr1qqO6INISygtN16i8loR8kAxtAUr4+EKHTSMjL+lggaAn4OC7DRqzrNgnuQ==, tarball: https://registry.npmjs.org/fake-indexeddb/-/fake-indexeddb-6.2.6.tgz} engines: {node: '>=18'} fast-deep-equal@3.1.3: @@ -3881,14 +3881,14 @@ packages: fast-safe-stringify@1.2.3: resolution: {integrity: sha512-QJYT/i0QYoiZBQ71ivxdyTqkwKkQ0oxACXHYxH2zYHJEgzi2LsbjgvtzTbLi1SZcF190Db2YP7I7eTsU2egOlw==} - fast-uri@3.1.9: - resolution: {integrity: sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==} + fast-uri@3.1.8: + resolution: {integrity: sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==, tarball: https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz} fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} fd-package-json@2.0.0: - resolution: {integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==} + resolution: {integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==, tarball: https://registry.npmjs.org/fd-package-json/-/fd-package-json-2.0.0.tgz} fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} @@ -3945,7 +3945,7 @@ packages: resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} flru@1.0.2: - resolution: {integrity: sha512-kWyh8ADvHBFz6ua5xYOPnUroZTT/bwWfrCeL0Wj1dzG4/YOmOcfJ99W8dOVyyynJN35rZ9aCOtHChqQovV7yog==} + resolution: {integrity: sha512-kWyh8ADvHBFz6ua5xYOPnUroZTT/bwWfrCeL0Wj1dzG4/YOmOcfJ99W8dOVyyynJN35rZ9aCOtHChqQovV7yog==, tarball: https://registry.npmjs.org/flru/-/flru-1.0.2.tgz} engines: {node: '>=6'} for-each@0.3.5: @@ -3957,7 +3957,7 @@ packages: engines: {node: '>= 6'} formatly@0.11.4: - resolution: {integrity: sha512-TeCC10lP8KCgoX+Fe5lryILIT0/Dy+nP0OSVnsuB5CwXoLuOdqxCYlPFF1inR8S/zTy1ec7TjhS2TeRKWzzImA==} + resolution: {integrity: sha512-TeCC10lP8KCgoX+Fe5lryILIT0/Dy+nP0OSVnsuB5CwXoLuOdqxCYlPFF1inR8S/zTy1ec7TjhS2TeRKWzzImA==, tarball: https://registry.npmjs.org/formatly/-/formatly-0.11.4.tgz} engines: {node: '>=18.14.0'} hasBin: true @@ -3969,7 +3969,7 @@ packages: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, tarball: https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] @@ -3992,11 +3992,11 @@ packages: engines: {node: '>= 0.4'} gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==, tarball: https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz} engines: {node: '>=6.9.0'} get-east-asian-width@1.7.0: - resolution: {integrity: sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==} + resolution: {integrity: sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==, tarball: https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz} engines: {node: '>=18'} get-intrinsic@1.3.0: @@ -4019,7 +4019,7 @@ packages: resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} get-tsconfig@4.14.3: - resolution: {integrity: sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==} + resolution: {integrity: sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==, tarball: https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.3.tgz} gh-pages@6.3.0: resolution: {integrity: sha512-Ot5lU6jK0Eb+sszG8pciXdjMXdBJ5wODvgjR+imihTqsUWF2K6dJ9HST55lgqcs8wWcw6o6wAsUzfcYRhJPXbA==} @@ -4043,7 +4043,7 @@ packages: 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 global-directory@4.0.1: - resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} + resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==, tarball: https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz} engines: {node: '>=18'} globals@14.0.0: @@ -4129,14 +4129,14 @@ packages: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} html-encoding-sniffer@7.0.0: - resolution: {integrity: sha512-UikN5yr7xsCDAq87Or5or0PAlD3HJJOKVzM05az588WnpDJ4Ux7a2A53Qi6gofGg2/EtvF/H4hCi/TXfCW4Y6w==} + resolution: {integrity: sha512-UikN5yr7xsCDAq87Or5or0PAlD3HJJOKVzM05az588WnpDJ4Ux7a2A53Qi6gofGg2/EtvF/H4hCi/TXfCW4Y6w==, tarball: https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-7.0.0.tgz} engines: {node: ^22.13.0 || >=24.0.0} html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} hyparquet@1.31.5: - resolution: {integrity: sha512-KPVfaxW8xOiO2/4fjeiBfFNEXhGVw9tfcb9ha2iNZdJ3J/yBjEwIeAnTyyuhllFFTb9YO9wg0DfRAOnm6KCvBg==} + resolution: {integrity: sha512-KPVfaxW8xOiO2/4fjeiBfFNEXhGVw9tfcb9ha2iNZdJ3J/yBjEwIeAnTyyuhllFFTb9YO9wg0DfRAOnm6KCvBg==, tarball: https://registry.npmjs.org/hyparquet/-/hyparquet-1.31.5.tgz} idn-hostname@15.1.19: resolution: {integrity: sha512-pRGRbFpQWCptOBWw9Gn5DXtPdern5/XtdkcXdoEavjd+sh72jNm2eVI1eeysp64UisXPfaqoQrnLUKpSN4hJWw==} @@ -4147,15 +4147,11 @@ packages: engines: {node: '>= 4'} ignore@7.0.12: - resolution: {integrity: sha512-/8UvqAPU9DGTI9k4mxtf49U37Isfwr8Uts96+SBHIkFxPJnHS0Ew4f00sM4Scd8V8EjM0jUNtVDdv1kPdt35lg==} - engines: {node: '>= 4'} - - ignore@7.0.13: - resolution: {integrity: sha512-2E8Q6rh+1RRSq/YdaGsNfXsEZy9FVqv6BSSrkmZJP7BnaoPnRfKL+YaofPvP+5rzdk144qSm6i4+hvPgUcbV4Q==} + resolution: {integrity: sha512-/8UvqAPU9DGTI9k4mxtf49U37Isfwr8Uts96+SBHIkFxPJnHS0Ew4f00sM4Scd8V8EjM0jUNtVDdv1kPdt35lg==, tarball: https://registry.npmjs.org/ignore/-/ignore-7.0.12.tgz} engines: {node: '>= 4'} ignore@7.0.14: - resolution: {integrity: sha512-wCBiZ77u1tuWde6G/nfOtLaCTYcFDU5yCzARzK10y+rLhzYoL7a7yEemcopcc2Mzow7gZOIjnV2ym1NYmdiJOQ==} + resolution: {integrity: sha512-wCBiZ77u1tuWde6G/nfOtLaCTYcFDU5yCzARzK10y+rLhzYoL7a7yEemcopcc2Mzow7gZOIjnV2ym1NYmdiJOQ==, tarball: https://registry.npmjs.org/ignore/-/ignore-7.0.14.tgz} engines: {node: '>= 4'} immediate@3.0.6: @@ -4166,11 +4162,11 @@ packages: engines: {node: '>=6'} import-in-the-middle@3.5.2: - resolution: {integrity: sha512-WseIA/4o56+GYO3RmlOPvejYi771OFeJEeK/ejROsr9bdjW+E5NXTOD1Di8TL40Qgg3JJX9aa97Hf8iiSVIKZw==} + resolution: {integrity: sha512-WseIA/4o56+GYO3RmlOPvejYi771OFeJEeK/ejROsr9bdjW+E5NXTOD1Di8TL40Qgg3JJX9aa97Hf8iiSVIKZw==, tarball: https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.5.2.tgz} engines: {node: '>=18'} import-meta-resolve@4.2.0: - resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==, tarball: https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz} imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} @@ -4188,7 +4184,7 @@ packages: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} ini@4.1.1: - resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==, tarball: https://registry.npmjs.org/ini/-/ini-4.1.1.tgz} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} inline-style-parser@0.2.7: @@ -4199,7 +4195,7 @@ packages: engines: {node: '>= 0.4'} interpret@3.1.1: - resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} + resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==, tarball: https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz} engines: {node: '>=10.13.0'} is-alphabetical@2.0.1: @@ -4284,11 +4280,11 @@ packages: hasBin: true is-installed-globally@1.0.1: - resolution: {integrity: sha512-li88H4QGRoNMX27jEmY8nR515oJVspAgC/WFMlhHfFV8RJ4QyGpHhXnwAe9gH5RIgKmdYRUmrOhQobcgEjIdSw==} + resolution: {integrity: sha512-li88H4QGRoNMX27jEmY8nR515oJVspAgC/WFMlhHfFV8RJ4QyGpHhXnwAe9gH5RIgKmdYRUmrOhQobcgEjIdSw==, tarball: https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-1.0.1.tgz} engines: {node: '>=18'} is-interactive@2.0.0: - resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==, tarball: https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz} engines: {node: '>=12'} is-map@2.0.3: @@ -4308,7 +4304,7 @@ packages: engines: {node: '>=0.12.0'} is-path-inside@4.0.0: - resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==, tarball: https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz} engines: {node: '>=12'} is-plain-obj@1.1.0: @@ -4320,7 +4316,7 @@ packages: engines: {node: '>=12'} is-potential-custom-element-name@1.0.1: - resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==, tarball: https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz} is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} @@ -4347,7 +4343,7 @@ packages: engines: {node: '>= 0.4'} is-unicode-supported@2.1.0: - resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==, tarball: https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz} engines: {node: '>=18'} is-weakmap@2.0.2: @@ -4373,7 +4369,7 @@ packages: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} isbot@5.2.2: - resolution: {integrity: sha512-iQcBXcd+Rv/pkubRyGh2utW2j1oPG5hZY6TUhVPpqK4G+o3IbxpJNx04hgksjc/N7GK5pEorUxDeg31cFgEk/w==} + resolution: {integrity: sha512-iQcBXcd+Rv/pkubRyGh2utW2j1oPG5hZY6TUhVPpqK4G+o3IbxpJNx04hgksjc/N7GK5pEorUxDeg31cFgEk/w==, tarball: https://registry.npmjs.org/isbot/-/isbot-5.2.2.tgz} engines: {node: '>=18'} iserror@0.0.2: @@ -4391,28 +4387,28 @@ packages: hasBin: true jose@6.2.13: - resolution: {integrity: sha512-7TIg10ZE1x82YediN+lcrPNvGMKge9yFHQD+Qho/b8y2adfugU3AsvvHfxV0fzyFllT6l9xCjf4RWY9g/Z9PJQ==} + resolution: {integrity: sha512-7TIg10ZE1x82YediN+lcrPNvGMKge9yFHQD+Qho/b8y2adfugU3AsvvHfxV0fzyFllT6l9xCjf4RWY9g/Z9PJQ==, tarball: https://registry.npmjs.org/jose/-/jose-6.2.13.tgz} js-tokens@10.0.0: - resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==, tarball: https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} js-yaml@4.2.0: - resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} + resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==, tarball: https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz} hasBin: true js-yaml@4.3.2: - resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} + resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==, tarball: https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz} hasBin: true js-yaml@5.5.0: - resolution: {integrity: sha512-paNr4LFIFPjeeSmwNYdrb8FafLLG/Cr2Af3GjbsHJ5CxIjH4r0c2emlmRdAtpPAieP1gRVzk6buBbVP5gBMGAg==} + resolution: {integrity: sha512-paNr4LFIFPjeeSmwNYdrb8FafLLG/Cr2Af3GjbsHJ5CxIjH4r0c2emlmRdAtpPAieP1gRVzk6buBbVP5gBMGAg==, tarball: https://registry.npmjs.org/js-yaml/-/js-yaml-5.5.0.tgz} hasBin: true jsdom@30.1.3: - resolution: {integrity: sha512-hQMUPvRgN7KxsfIytDQji7bimuJeRQa6Qo5+iuiZQk5CafeRxLIA10/cG5YCBFNpD9RO3oqIiN4CO1Y44kxqXg==} + resolution: {integrity: sha512-hQMUPvRgN7KxsfIytDQji7bimuJeRQa6Qo5+iuiZQk5CafeRxLIA10/cG5YCBFNpD9RO3oqIiN4CO1Y44kxqXg==, tarball: https://registry.npmjs.org/jsdom/-/jsdom-30.1.3.tgz} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} peerDependencies: canvas: ^3.2.3 @@ -4435,21 +4431,21 @@ packages: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==, tarball: https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz} json-schema-typed@8.0.2: - resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} + resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==, tarball: https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz} json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==, tarball: https://registry.npmjs.org/json5/-/json5-2.2.3.tgz} engines: {node: '>=6'} hasBin: true jsonc-parser@3.3.1: - resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==, tarball: https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz} jsonfile@6.2.0: resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} @@ -4469,7 +4465,7 @@ packages: engines: {node: '>=0.10.0'} kleur@3.0.3: - resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==, tarball: https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz} engines: {node: '>=6'} knip@6.43.0: @@ -4485,141 +4481,141 @@ packages: resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==} lightningcss-android-arm64@1.32.0: - resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==, tarball: https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] lightningcss-android-arm64@1.33.0: - resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==, tarball: https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] lightningcss-darwin-arm64@1.32.0: - resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==, tarball: https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] lightningcss-darwin-arm64@1.33.0: - resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==, tarball: https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] lightningcss-darwin-x64@1.32.0: - resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==, tarball: https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] lightningcss-darwin-x64@1.33.0: - resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==, tarball: https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] lightningcss-freebsd-x64@1.32.0: - resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==, tarball: https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] lightningcss-freebsd-x64@1.33.0: - resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==, tarball: https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] lightningcss-linux-arm-gnueabihf@1.32.0: - resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==, tarball: https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] lightningcss-linux-arm-gnueabihf@1.33.0: - resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==, tarball: https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] lightningcss-linux-arm64-gnu@1.32.0: - resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==, tarball: https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] libc: [glibc] lightningcss-linux-arm64-gnu@1.33.0: - resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==, tarball: https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: - resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==, tarball: https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] libc: [musl] lightningcss-linux-arm64-musl@1.33.0: - resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==, tarball: https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] libc: [musl] lightningcss-linux-x64-gnu@1.32.0: - resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==, tarball: https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] libc: [glibc] lightningcss-linux-x64-gnu@1.33.0: - resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==, tarball: https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] libc: [glibc] lightningcss-linux-x64-musl@1.32.0: - resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==, tarball: https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] libc: [musl] lightningcss-linux-x64-musl@1.33.0: - resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==, tarball: https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: - resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==, tarball: https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] lightningcss-win32-arm64-msvc@1.33.0: - resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==, tarball: https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] lightningcss-win32-x64-msvc@1.32.0: - resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==, tarball: https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] lightningcss-win32-x64-msvc@1.33.0: - resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==, tarball: https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] @@ -4653,7 +4649,7 @@ packages: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} log-symbols@7.0.1: - resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} + resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==, tarball: https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz} engines: {node: '>=18'} longest-streak@3.1.0: @@ -4664,14 +4660,14 @@ packages: hasBin: true lru-cache@11.5.3: - resolution: {integrity: sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==} + resolution: {integrity: sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==, tarball: https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz} engines: {node: 20 || >=22} lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} lucide-react@1.61.0: - resolution: {integrity: sha512-v2SQPPPNjnr+BlhL9Spuzmo5yR+hLkftfuE4E3jaxdNPgSH0hJKPcLJBP7yAta87jQNPsSERS59PJjCJ/CorBA==} + resolution: {integrity: sha512-v2SQPPPNjnr+BlhL9Spuzmo5yR+hLkftfuE4E3jaxdNPgSH0hJKPcLJBP7yAta87jQNPsSERS59PJjCJ/CorBA==, tarball: https://registry.npmjs.org/lucide-react/-/lucide-react-1.61.0.tgz} peerDependencies: '@types/react': '*' react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -4687,7 +4683,7 @@ packages: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} magicast@0.5.5: - resolution: {integrity: sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==} + resolution: {integrity: sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==, tarball: https://registry.npmjs.org/magicast/-/magicast-0.5.5.tgz} make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} @@ -4759,7 +4755,7 @@ packages: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} mdn-data@2.27.1: - resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==, tarball: https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz} meow@6.1.1: resolution: {integrity: sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg==} @@ -4866,7 +4862,7 @@ packages: engines: {node: '>= 0.6'} mimic-function@5.0.1: - resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==, tarball: https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz} engines: {node: '>=18'} min-indent@1.0.1: @@ -4874,7 +4870,7 @@ packages: engines: {node: '>=4'} minimatch@10.2.6: - resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==, tarball: https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz} engines: {node: 18 || 20 || >=22} minimatch@3.1.5: @@ -4885,7 +4881,7 @@ packages: engines: {node: '>= 6'} minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==, tarball: https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz} mobx-keystone@2.4.1: resolution: {integrity: sha512-jPK/sL16dvHcnLLr8rnNvku3ez+NIEMfPHmyH+8bs9hAuZoIk4DrPVpcyvrnyh+OUDqhTkkF7eeGPLi5cxzW3g==} @@ -4909,7 +4905,7 @@ packages: resolution: {integrity: sha512-syNcDdX3KT+Jq3je6eGjBhuc24Z68td2VG0zNFqRswaE433D9SNH5VRy/xrGbJsUixfppLLccXhAW9JSf6n+SQ==} module-details-from-path@1.0.4: - resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} + resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==, tarball: https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz} monaco-editor@0.54.0: resolution: {integrity: sha512-hx45SEUoLatgWxHKCmlLJH81xBo0uXP4sRkESUpmDQevfi+e7K1VuiSprK6UpQ8u4zOcKNiH0pMvHvlMWA/4cw==} @@ -4974,7 +4970,7 @@ packages: engines: {node: '>=12.20.0'} obug@3.0.0: - resolution: {integrity: sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==} + resolution: {integrity: sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==, tarball: https://registry.npmjs.org/obug/-/obug-3.0.0.tgz} engines: {node: '>=12.20.0'} ohash@2.0.11: @@ -4984,7 +4980,7 @@ packages: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} onetime@7.0.0: - resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==, tarball: https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz} engines: {node: '>=18'} open@11.0.0: @@ -4992,7 +4988,7 @@ packages: engines: {node: '>=20'} openai@7.43.0: - resolution: {integrity: sha512-fBjKaDFG8AVfkGYoIo/E+xNRlNk7nx2nQ9GoQtXfePzPjg4QcT6nejkhXayJnEwSpFGfthOrAPI2KXj5yAqg9A==} + resolution: {integrity: sha512-fBjKaDFG8AVfkGYoIo/E+xNRlNk7nx2nQ9GoQtXfePzPjg4QcT6nejkhXayJnEwSpFGfthOrAPI2KXj5yAqg9A==, tarball: https://registry.npmjs.org/openai/-/openai-7.43.0.tgz} engines: {node: '>=22.0.0'} peerDependencies: '@aws-sdk/credential-provider-node': '>=3.972.0 <4' @@ -5023,7 +5019,7 @@ packages: engines: {node: '>= 0.8.0'} ora@9.4.1: - resolution: {integrity: sha512-6VlU9MLXbjVQD04AZCMX28hVtA5bUoadvUqO76MUCVA0ilwJbMiHsITRPfyVm6p/BC0Av/BXMujx39WCe1LEqw==} + resolution: {integrity: sha512-6VlU9MLXbjVQD04AZCMX28hVtA5bUoadvUqO76MUCVA0ilwJbMiHsITRPfyVm6p/BC0Av/BXMujx39WCe1LEqw==, tarball: https://registry.npmjs.org/ora/-/ora-9.4.1.tgz} engines: {node: '>=20'} own-keys@1.0.1: @@ -5031,7 +5027,7 @@ packages: engines: {node: '>= 0.4'} oxc-parser@0.148.0: - resolution: {integrity: sha512-syxUKHeUll89RIABQADcI7sikYrwyssvA6gj4phSSIPezKVM8yMaLAiLLSc7fmzVvrwybfFGFbW5zme9sX87rg==} + resolution: {integrity: sha512-syxUKHeUll89RIABQADcI7sikYrwyssvA6gj4phSSIPezKVM8yMaLAiLLSc7fmzVvrwybfFGFbW5zme9sX87rg==, tarball: https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.148.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} oxc-parser@0.155.0: @@ -5039,14 +5035,14 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} oxc-resolver@11.24.2: - resolution: {integrity: sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==} + resolution: {integrity: sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==, tarball: https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.24.2.tgz} oxlint-plugin-react-doctor@0.9.18: - resolution: {integrity: sha512-1mfeb/jzXnduWOZJxrvdJjvKT0pFNKrNFYcPO4ncpFAnfW41M0mLz/23lAFzHb0knNLYayEgIQ5DjI4ZfFe+DQ==} + resolution: {integrity: sha512-1mfeb/jzXnduWOZJxrvdJjvKT0pFNKrNFYcPO4ncpFAnfW41M0mLz/23lAFzHb0knNLYayEgIQ5DjI4ZfFe+DQ==, tarball: https://registry.npmjs.org/oxlint-plugin-react-doctor/-/oxlint-plugin-react-doctor-0.9.18.tgz} engines: {node: ^20.19.0 || >=22.13.0} oxlint@1.81.0: - resolution: {integrity: sha512-HyrJYqeoOCL0iqaLEzGewGT48ZX99P3hxYh8udAF9RGGIghSamkXE4ClUyBpEDNqasamThgmlPbuMOe7SAZmHg==} + resolution: {integrity: sha512-HyrJYqeoOCL0iqaLEzGewGT48ZX99P3hxYh8udAF9RGGIghSamkXE4ClUyBpEDNqasamThgmlPbuMOe7SAZmHg==, tarball: https://registry.npmjs.org/oxlint/-/oxlint-1.81.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -5079,7 +5075,7 @@ packages: engines: {node: '>=6'} package-manager-detector@1.9.0: - resolution: {integrity: sha512-zAMfbta9nPA8AKkA4WdmNAT8R8s6Dk0/xwPsqNnRidMFcLo1YkLuPDTgs2f64XdzS8vbLNnoJ+uUIkgbz7oRHA==} + resolution: {integrity: sha512-zAMfbta9nPA8AKkA4WdmNAT8R8s6Dk0/xwPsqNnRidMFcLo1YkLuPDTgs2f64XdzS8vbLNnoJ+uUIkgbz7oRHA==, tarball: https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.9.0.tgz} papaparse@5.7.0: resolution: {integrity: sha512-qBGxg/7Q3Kl9Wfhrz2Z74UnvnHTXLNG6jmKJFeBvP2+y4lV7So+7SR62+Zd47JvdrCkX+nDcnr0ObPzek/+6RA==} @@ -5096,7 +5092,7 @@ packages: engines: {node: '>=8'} parse5@8.0.1: - resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} + resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==, tarball: https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz} path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} @@ -5127,7 +5123,7 @@ packages: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} picomatch@2.3.2: - resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==, tarball: https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz} engines: {node: '>=8.6'} picomatch@4.0.5: @@ -5146,7 +5142,7 @@ packages: hasBin: true pkce-challenge@5.0.1: - resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==, tarball: https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz} engines: {node: '>=16.20.0'} pkg-dir@4.2.0: @@ -5157,12 +5153,12 @@ packages: resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==} playwright-core@1.64.1: - resolution: {integrity: sha512-BC4s8y6SEVA6Q1rSxZTto8hI1KUI3xXlHGZgvGqP4IqPdI/a6FX9H29IDNz7vKo9l21b6LfYosScPL6M3xxyDQ==} + resolution: {integrity: sha512-BC4s8y6SEVA6Q1rSxZTto8hI1KUI3xXlHGZgvGqP4IqPdI/a6FX9H29IDNz7vKo9l21b6LfYosScPL6M3xxyDQ==, tarball: https://registry.npmjs.org/playwright-core/-/playwright-core-1.64.1.tgz} engines: {node: '>=20'} hasBin: true playwright@1.64.1: - resolution: {integrity: sha512-HCrVhHr5QQuwYoswEUGTb7gGp522yflbv/ST8nF4sFWGuLjQCt17Q/EubpGGSNKYn1tsTCPZxqHhHCfVURu7UA==} + resolution: {integrity: sha512-HCrVhHr5QQuwYoswEUGTb7gGp522yflbv/ST8nF4sFWGuLjQCt17Q/EubpGGSNKYn1tsTCPZxqHhHCfVURu7UA==, tarball: https://registry.npmjs.org/playwright/-/playwright-1.64.1.tgz} engines: {node: '>=20'} hasBin: true @@ -5203,7 +5199,7 @@ packages: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} prompts@2.4.2: - resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==, tarball: https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz} engines: {node: '>= 6'} prop-types@15.8.1: @@ -5256,12 +5252,12 @@ packages: optional: true react-doctor@0.9.18: - resolution: {integrity: sha512-4Ach00KtfnfjTBGgr2PyPgHua85vT/UfPY5sR0bHgRAc/QSu0JInA0GHCL6a7WFGMeGUrxtGnI8oqZinG44BOA==} + resolution: {integrity: sha512-4Ach00KtfnfjTBGgr2PyPgHua85vT/UfPY5sR0bHgRAc/QSu0JInA0GHCL6a7WFGMeGUrxtGnI8oqZinG44BOA==, tarball: https://registry.npmjs.org/react-doctor/-/react-doctor-0.9.18.tgz} engines: {node: ^20.19.0 || >=22.13.0} hasBin: true react-dom@19.3.1: - resolution: {integrity: sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ==} + resolution: {integrity: sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ==, tarball: https://registry.npmjs.org/react-dom/-/react-dom-19.3.1.tgz} peerDependencies: react: ^19.3.1 @@ -5275,7 +5271,7 @@ packages: optional: true react-grab@0.2.0: - resolution: {integrity: sha512-ohhsfXD4qN0j6cMQd56aaVJBPDF3kUdviF/Od1Eak/rEIXQ3svQ4gjkwTfuTZwTBnxHRL16p9JqdlVQO5nUHqA==} + resolution: {integrity: sha512-ohhsfXD4qN0j6cMQd56aaVJBPDF3kUdviF/Od1Eak/rEIXQ3svQ4gjkwTfuTZwTBnxHRL16p9JqdlVQO5nUHqA==, tarball: https://registry.npmjs.org/react-grab/-/react-grab-0.2.0.tgz} hasBin: true peerDependencies: react: '>=17.0.0' @@ -5348,7 +5344,7 @@ packages: react-dom: ^18 || ^19 react@19.3.1: - resolution: {integrity: sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==} + resolution: {integrity: sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==, tarball: https://registry.npmjs.org/react/-/react-19.3.1.tgz} engines: {node: '>=0.10.0'} read-pkg-up@7.0.1: @@ -5371,7 +5367,7 @@ packages: engines: {node: '>= 20.19.0'} rechoir@0.8.0: - resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} + resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==, tarball: https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz} engines: {node: '>= 10.13.0'} redent@3.0.0: @@ -5387,7 +5383,7 @@ packages: engines: {node: '>= 0.4'} regexp-tree@0.1.27: - resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} + resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==, tarball: https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz} hasBin: true regexp.prototype.flags@1.5.4: @@ -5407,14 +5403,14 @@ packages: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} remove-accents@0.5.0: - resolution: {integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==} + resolution: {integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==, tarball: https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz} require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==, tarball: https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz} engines: {node: '>=0.10.0'} require-in-the-middle@8.0.1: - resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==} + resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==, tarball: https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz} engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'} resize-observer-polyfill@1.5.1: @@ -5438,7 +5434,7 @@ packages: hasBin: true restore-cursor@5.1.0: - resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==, tarball: https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz} engines: {node: '>=18'} reusify@1.1.0: @@ -5451,7 +5447,7 @@ packages: hasBin: true rollup@4.61.1: - resolution: {integrity: sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==} + resolution: {integrity: sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==, tarball: https://registry.npmjs.org/rollup/-/rollup-4.61.1.tgz} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -5481,20 +5477,20 @@ packages: engines: {node: '>= 0.4'} safe-regex@2.1.1: - resolution: {integrity: sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==} + resolution: {integrity: sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==, tarball: https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz} saxes@6.0.0: - resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==, tarball: https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz} engines: {node: '>=v12.22.7'} scheduler@0.28.0: - resolution: {integrity: sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==} + resolution: {integrity: sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==, tarball: https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz} seedrandom@3.0.5: resolution: {integrity: sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==} semver@5.7.2: - resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==, tarball: https://registry.npmjs.org/semver/-/semver-5.7.2.tgz} hasBin: true semver@6.3.1: @@ -5507,7 +5503,7 @@ packages: hasBin: true semver@7.8.5: - resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==, tarball: https://registry.npmjs.org/semver/-/semver-7.8.5.tgz} engines: {node: '>=10'} hasBin: true @@ -5571,18 +5567,18 @@ packages: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==, tarball: https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz} engines: {node: '>=14'} sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==, tarball: https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz} slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} smol-toml@1.9.1: - resolution: {integrity: sha512-j7WyPdf0CJoutMzMoPLF/bVXHtKZIT9Rb4eGeEbWw8X2t5oirjPgpL+OpaENZrILYaXqYwnVH+qgUwVbl+pUJQ==} + resolution: {integrity: sha512-j7WyPdf0CJoutMzMoPLF/bVXHtKZIT9Rb4eGeEbWw8X2t5oirjPgpL+OpaENZrILYaXqYwnVH+qgUwVbl+pUJQ==, tarball: https://registry.npmjs.org/smol-toml/-/smol-toml-1.9.1.tgz} engines: {node: '>= 18'} socket.io-client@4.8.4: @@ -5594,20 +5590,20 @@ packages: engines: {node: '>=10.0.0'} solid-js@1.9.17: - resolution: {integrity: sha512-4GAmLOWPfNV8H9Jfn2LSFTpNEG6jiOKPiwd5lnLGQtehHaX6l3TOLzJqQ7mqwB/3k1Yy3zyumFVopmc/tm34ow==} + resolution: {integrity: sha512-4GAmLOWPfNV8H9Jfn2LSFTpNEG6jiOKPiwd5lnLGQtehHaX6l3TOLzJqQ7mqwB/3k1Yy3zyumFVopmc/tm34ow==, tarball: https://registry.npmjs.org/solid-js/-/solid-js-1.9.17.tgz} solid-presence@0.2.0: - resolution: {integrity: sha512-YM92o+jvpzX3XGaD4rLYmq/Kc2ZVh47GSCLEufHBFQQIurvZTs8SoGJxO8BJGNDxBKdcS8F3dYhW1SDXp4BNjA==} + resolution: {integrity: sha512-YM92o+jvpzX3XGaD4rLYmq/Kc2ZVh47GSCLEufHBFQQIurvZTs8SoGJxO8BJGNDxBKdcS8F3dYhW1SDXp4BNjA==, tarball: https://registry.npmjs.org/solid-presence/-/solid-presence-0.2.0.tgz} peerDependencies: solid-js: ^1.8 solid-prevent-scroll@0.1.11: - resolution: {integrity: sha512-2PComVCDHaQN/5t7ogEqUYeHasritZKXZ8Sb/VLkl0Web8o9YhEwOo8LSujJEahvMlYCNyKt0zRJeHldOAeWZg==} + resolution: {integrity: sha512-2PComVCDHaQN/5t7ogEqUYeHasritZKXZ8Sb/VLkl0Web8o9YhEwOo8LSujJEahvMlYCNyKt0zRJeHldOAeWZg==, tarball: https://registry.npmjs.org/solid-prevent-scroll/-/solid-prevent-scroll-0.1.11.tgz} peerDependencies: solid-js: ^1.8 solid-transition-group@0.2.3: - resolution: {integrity: sha512-iB72c9N5Kz9ykRqIXl0lQohOau4t0dhel9kjwFvx81UZJbVwaChMuBuyhiZmK24b8aKEK0w3uFM96ZxzcyZGdg==} + resolution: {integrity: sha512-iB72c9N5Kz9ykRqIXl0lQohOau4t0dhel9kjwFvx81UZJbVwaChMuBuyhiZmK24b8aKEK0w3uFM96ZxzcyZGdg==, tarball: https://registry.npmjs.org/solid-transition-group/-/solid-transition-group-0.2.3.tgz} engines: {node: '>=18.0.0', pnpm: '>=8.6.0'} peerDependencies: solid-js: ^1.6.12 @@ -5654,10 +5650,10 @@ packages: resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} std-env@4.3.0: - resolution: {integrity: sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==} + resolution: {integrity: sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==, tarball: https://registry.npmjs.org/std-env/-/std-env-4.3.0.tgz} stdin-discarder@0.3.2: - resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==} + resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==, tarball: https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.3.2.tgz} engines: {node: '>=18'} stop-iteration-iterator@1.1.0: @@ -5665,7 +5661,7 @@ packages: engines: {node: '>= 0.4'} string-width@8.3.0: - resolution: {integrity: sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==} + resolution: {integrity: sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==, tarball: https://registry.npmjs.org/string-width/-/string-width-8.3.0.tgz} engines: {node: '>=20'} string.prototype.matchall@4.0.12: @@ -5697,11 +5693,11 @@ packages: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} strip-ansi@7.2.0: - resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==, tarball: https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz} engines: {node: '>=12'} strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==, tarball: https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz} engines: {node: '>=4'} strip-indent@3.0.0: @@ -5713,7 +5709,7 @@ packages: engines: {node: '>=8'} strip-json-comments@5.0.3: - resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} + resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==, tarball: https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz} engines: {node: '>=14.16'} strip-outer@1.0.1: @@ -5721,10 +5717,10 @@ packages: engines: {node: '>=0.10.0'} stubborn-fs@2.0.0: - resolution: {integrity: sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==} + resolution: {integrity: sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==, tarball: https://registry.npmjs.org/stubborn-fs/-/stubborn-fs-2.0.0.tgz} stubborn-utils@1.0.2: - resolution: {integrity: sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg==} + resolution: {integrity: sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg==, tarball: https://registry.npmjs.org/stubborn-utils/-/stubborn-utils-1.0.2.tgz} style-to-js@1.1.21: resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} @@ -5733,7 +5729,7 @@ packages: resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} superjson@2.2.6: - resolution: {integrity: sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==} + resolution: {integrity: sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==, tarball: https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz} engines: {node: '>=16'} supports-color@5.5.0: @@ -5753,7 +5749,7 @@ packages: engines: {node: '>=8.0.0'} tagged-tag@1.0.0: - resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==, tarball: https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz} engines: {node: '>=20'} tailwind-merge@3.7.0: @@ -5768,7 +5764,7 @@ packages: resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} tapable@2.3.3: - resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==, tarball: https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz} engines: {node: '>=6'} through2@2.0.5: @@ -5782,7 +5778,7 @@ packages: engines: {node: '>=18'} tinyexec@1.3.1: - resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} + resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==, tarball: https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz} engines: {node: '>=18'} tinyglobby@0.2.17: @@ -5794,14 +5790,14 @@ packages: engines: {node: '>=14.0.0'} tinyrainbow@3.2.0: - resolution: {integrity: sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==} + resolution: {integrity: sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==, tarball: https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.2.0.tgz} engines: {node: '>=14.0.0'} tldts-core@7.4.18: - resolution: {integrity: sha512-KI2PKvCmvQI/xD6ypPqkU0i7oxNXR/O58RLRaHR4svsgVUQ2JE/XC8BtMbG6PSehD27I8sZJRcC1ASnCy6lp3g==} + resolution: {integrity: sha512-KI2PKvCmvQI/xD6ypPqkU0i7oxNXR/O58RLRaHR4svsgVUQ2JE/XC8BtMbG6PSehD27I8sZJRcC1ASnCy6lp3g==, tarball: https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.18.tgz} tldts@7.4.18: - resolution: {integrity: sha512-somxA8xKAUzlAxl0TX/jMSYIbRNFIVGnI96pVGJPeMc5BsHmfVXn6mOJT6U++ELnl04eGfzpfcEuZe8WiASkXg==} + resolution: {integrity: sha512-somxA8xKAUzlAxl0TX/jMSYIbRNFIVGnI96pVGJPeMc5BsHmfVXn6mOJT6U++ELnl04eGfzpfcEuZe8WiASkXg==, tarball: https://registry.npmjs.org/tldts/-/tldts-7.4.18.tgz} hasBin: true to-regex-range@5.0.1: @@ -5809,11 +5805,11 @@ packages: engines: {node: '>=8.0'} tough-cookie@6.0.2: - resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==} + resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==, tarball: https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz} engines: {node: '>=16'} tr46@7.0.0: - resolution: {integrity: sha512-BcDSDW+SIGWVOns1Zm6Yg91q9AEj7eaID0QDY9oIqJD1A8dscNwMcl9Sms2DeuWCNov1X0/oXanHGYQBSLwZsg==} + resolution: {integrity: sha512-BcDSDW+SIGWVOns1Zm6Yg91q9AEj7eaID0QDY9oIqJD1A8dscNwMcl9Sms2DeuWCNov1X0/oXanHGYQBSLwZsg==, tarball: https://registry.npmjs.org/tr46/-/tr46-7.0.0.tgz} engines: {node: ^22.14.0 || >=24.0.0} trim-lines@3.0.1: @@ -5831,7 +5827,7 @@ packages: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} ts-api-utils@2.5.0: - resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==, tarball: https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' @@ -5840,11 +5836,11 @@ packages: resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} tsconfig-paths-webpack-plugin@4.2.0: - resolution: {integrity: sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==} + resolution: {integrity: sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==, tarball: https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz} engines: {node: '>=10.13.0'} tsconfig-paths@4.2.0: - resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==, tarball: https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz} engines: {node: '>=6'} tslib@2.8.1: @@ -5872,7 +5868,7 @@ packages: engines: {node: '>=8'} type-fest@5.10.0: - resolution: {integrity: sha512-NoSdpq/WEiAg5sjmBkmV/hfxv6HJH4NqPNrqjtSO5CwRmpsDfaf4begxW34KdJykH/l1yHtwBWQkCRdoXO8mPA==} + resolution: {integrity: sha512-NoSdpq/WEiAg5sjmBkmV/hfxv6HJH4NqPNrqjtSO5CwRmpsDfaf4begxW34KdJykH/l1yHtwBWQkCRdoXO8mPA==, tarball: https://registry.npmjs.org/type-fest/-/type-fest-5.10.0.tgz} engines: {node: '>=20'} typed-array-buffer@1.0.3: @@ -5895,14 +5891,14 @@ packages: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} typescript-eslint@8.72.0: - resolution: {integrity: sha512-r26Fkc8As+mZvqp7SLsMmNFyYUup/6CjK7/eHBHmhOw6GH6n6YFxHVFrG0jN1c5CMpDzelLPxvgeCOzx7BjN+A==} + resolution: {integrity: sha512-r26Fkc8As+mZvqp7SLsMmNFyYUup/6CjK7/eHBHmhOw6GH6n6YFxHVFrG0jN1c5CMpDzelLPxvgeCOzx7BjN+A==, tarball: https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.72.0.tgz} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==, tarball: https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz} engines: {node: '>=14.17'} hasBin: true @@ -5920,7 +5916,7 @@ packages: engines: {node: '>=8'} uint8array-extras@1.6.0: - resolution: {integrity: sha512-8iAasVS4wUx0gPLjH8Xtz2PeDzTSiy8QiLGu2DT3X5GU+egFEQhpnbtkehbAwjvDcxIERmCYcysiODFmE3Ud0Q==} + resolution: {integrity: sha512-8iAasVS4wUx0gPLjH8Xtz2PeDzTSiy8QiLGu2DT3X5GU+egFEQhpnbtkehbAwjvDcxIERmCYcysiODFmE3Ud0Q==, tarball: https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.6.0.tgz} engines: {node: '>=18'} unbash@6.0.1: @@ -5934,8 +5930,8 @@ packages: undici-types@8.9.0: resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} - undici@8.12.2: - resolution: {integrity: sha512-XtuDWVdqTLr3NAxm2ly9/hU6kUsJOgGh1RASzMBbsw6aWCOaQzHdW+QQDfdERjfJ2V5vL5pRPa4RetO9UfivDg==} + undici@8.12.3: + resolution: {integrity: sha512-+fdndUTwBP86cVjihTp21hZHo7d6oj1c/47XPA3lL8mM0XHs+C4EkcjmgRsFnFk2f/UONGDy9ItBItmXsLOQHw==, tarball: https://registry.npmjs.org/undici/-/undici-8.12.3.tgz} engines: {node: '>=22.19.0'} unified@11.0.5: @@ -5961,11 +5957,11 @@ packages: engines: {node: '>= 10.0.0'} unplugin@3.0.0: - resolution: {integrity: sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg==} + resolution: {integrity: sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg==, tarball: https://registry.npmjs.org/unplugin/-/unplugin-3.0.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} - update-browserslist-db@1.3.4: - resolution: {integrity: sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==} + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==, tarball: https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -6014,7 +6010,7 @@ packages: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} verkit@0.5.0: - resolution: {integrity: sha512-u4J1hr6Y+C/hit34GwL3eIcUf3wngT8TlYUjK7tk0jxydKLAq3sYvifjdTDA6U3KCKRcQCQDGf/srjxfZF3l7g==} + resolution: {integrity: sha512-u4J1hr6Y+C/hit34GwL3eIcUf3wngT8TlYUjK7tk0jxydKLAq3sYvifjdTDA6U3KCKRcQCQDGf/srjxfZF3l7g==, tarball: https://registry.npmjs.org/verkit/-/verkit-0.5.0.tgz} engines: {node: '>=18.12.0'} vfile-message@4.0.3: @@ -6113,35 +6109,35 @@ packages: optional: true w3c-xmlserializer@6.0.0: - resolution: {integrity: sha512-4Nsy8K5Tr6SPDH9jhKJOHf7ChDrc1zufZTVSF7x72hwuEXBqxqk9G6cK+K2NRUtB3iELRJqjXb4JPDMBjMTl2Q==} + resolution: {integrity: sha512-4Nsy8K5Tr6SPDH9jhKJOHf7ChDrc1zufZTVSF7x72hwuEXBqxqk9G6cK+K2NRUtB3iELRJqjXb4JPDMBjMTl2Q==, tarball: https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-6.0.0.tgz} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} walk-up-path@4.0.0: - resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} + resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==, tarball: https://registry.npmjs.org/walk-up-path/-/walk-up-path-4.0.0.tgz} engines: {node: 20 || >=22} watskeburt@6.0.0: - resolution: {integrity: sha512-jfiuDABaxSkC71T6oZ3vCS99roYkSHm/+As+G0Dz8taAHQb+SJBvLEm5RlsgG71XdfAj3rv7eudUBTgwcQUPlQ==} + resolution: {integrity: sha512-jfiuDABaxSkC71T6oZ3vCS99roYkSHm/+As+G0Dz8taAHQb+SJBvLEm5RlsgG71XdfAj3rv7eudUBTgwcQUPlQ==, tarball: https://registry.npmjs.org/watskeburt/-/watskeburt-6.0.0.tgz} engines: {node: ^22.13||^24||>=26} hasBin: true webidl-conversions@8.0.1: - resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} + resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==, tarball: https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz} engines: {node: '>=20'} webpack-virtual-modules@0.6.2: resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} whatwg-mimetype@5.0.0: - resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} + resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==, tarball: https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz} engines: {node: '>=20'} - whatwg-url@17.3.0: - resolution: {integrity: sha512-KwOAJrpf+GTgBvcBQJcP38sMF0DsQKdwtG+TxM2pW44P6E5k0hDbWrDmyuJDIF0Uef0aQq0ZoJs+5oI+k8ddhg==} + whatwg-url@17.2.0: + resolution: {integrity: sha512-IhLIHNcBrzIixC5XXsbr4DxdjajyA5vQ5RwGZMtrUp+GbtE5Nrsge9QKau7jXrC0aTXLYGV0xiW7Tb4YS2aomw==, tarball: https://registry.npmjs.org/whatwg-url/-/whatwg-url-17.2.0.tgz} engines: {node: ^22.14.0 || >=24.0.0} when-exit@2.1.5: - resolution: {integrity: sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==} + resolution: {integrity: sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==, tarball: https://registry.npmjs.org/when-exit/-/when-exit-2.1.5.tgz} which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} @@ -6209,11 +6205,11 @@ packages: engines: {node: '>=20'} xml-name-validator@5.0.0: - resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==, tarball: https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz} engines: {node: '>=18'} xmlchars@2.2.0: - resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==, tarball: https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz} xmlhttprequest-ssl@2.1.2: resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} @@ -6227,7 +6223,7 @@ packages: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} yaml@2.9.1: - resolution: {integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==} + resolution: {integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==, tarball: https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz} engines: {node: '>= 14.6'} hasBin: true @@ -6240,11 +6236,11 @@ packages: engines: {node: '>=10'} yoctocolors@2.2.0: - resolution: {integrity: sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==} + resolution: {integrity: sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==, tarball: https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.2.0.tgz} engines: {node: '>=18'} yoga-layout@3.2.1: - resolution: {integrity: sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==} + resolution: {integrity: sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==, tarball: https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz} zod-validation-error@3.5.4: resolution: {integrity: sha512-+hEiRIiPobgyuFlEojnqjJnhFvg4r/i3cqgcm67eehZf/WBaK3g6cD02YU9mtdVxZjv8CzCA9n/Rhrs3yAAvAw==} @@ -6253,7 +6249,7 @@ packages: zod: ^3.24.4 zod-validation-error@4.0.2: - resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==, tarball: https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz} engines: {node: '>=18.0.0'} peerDependencies: zod: ^3.25.0 || ^4.0.0 @@ -6286,7 +6282,7 @@ snapshots: '@adobe/css-tools@4.5.0': {} - '@asamuzakjp/css-color@7.1.7': + '@asamuzakjp/css-color@7.1.5': dependencies: '@csstools/css-calc': 3.4.3(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2) '@csstools/css-color-parser': 4.2.6(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2) @@ -6321,13 +6317,13 @@ snapshots: '@babel/core@7.29.7': dependencies: '@babel/code-frame': 7.29.7 - '@babel/generator': 7.29.8 + '@babel/generator': 7.29.7 '@babel/helper-compilation-targets': 7.29.7 '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) - '@babel/helpers': 7.29.10 + '@babel/helpers': 7.29.7 '@babel/parser': 7.29.11 '@babel/template': 7.29.7 - '@babel/traverse': 7.29.10 + '@babel/traverse': 7.29.7 '@babel/types': 7.29.11 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 @@ -6357,7 +6353,7 @@ snapshots: obug: 3.0.0 verkit: 0.5.0 - '@babel/generator@7.29.8': + '@babel/generator@7.29.7': dependencies: '@babel/parser': 7.29.11 '@babel/types': 7.29.11 @@ -6386,7 +6382,7 @@ snapshots: dependencies: '@babel/compat-data': 7.29.7 '@babel/helper-validator-option': 7.29.7 - browserslist: 4.29.4 + browserslist: 4.28.2 lru-cache: 5.1.1 semver: 6.3.1 @@ -6394,7 +6390,7 @@ snapshots: dependencies: '@babel/compat-data': 8.0.5 '@babel/helper-validator-option': 8.0.0 - browserslist: 4.29.4 + browserslist: 4.28.2 flru: 1.0.2 verkit: 0.5.0 @@ -6406,7 +6402,7 @@ snapshots: '@babel/helper-optimise-call-expression': 7.29.7 '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 - '@babel/traverse': 7.29.10 + '@babel/traverse': 7.29.7 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -6428,7 +6424,7 @@ snapshots: '@babel/helper-member-expression-to-functions@7.29.7': dependencies: - '@babel/traverse': 7.29.10 + '@babel/traverse': 7.29.7 '@babel/types': 7.29.11 transitivePeerDependencies: - supports-color @@ -6440,7 +6436,7 @@ snapshots: '@babel/helper-module-imports@7.29.7': dependencies: - '@babel/traverse': 7.29.10 + '@babel/traverse': 7.29.7 '@babel/types': 7.29.11 transitivePeerDependencies: - supports-color @@ -6450,7 +6446,7 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-module-imports': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@babel/traverse': 7.29.10 + '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color @@ -6473,7 +6469,7 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-member-expression-to-functions': 7.29.7 '@babel/helper-optimise-call-expression': 7.29.7 - '@babel/traverse': 7.29.10 + '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color @@ -6486,7 +6482,7 @@ snapshots: '@babel/helper-skip-transparent-expression-wrappers@7.29.7': dependencies: - '@babel/traverse': 7.29.10 + '@babel/traverse': 7.29.7 '@babel/types': 7.29.11 transitivePeerDependencies: - supports-color @@ -6514,7 +6510,7 @@ snapshots: '@babel/helper-validator-option@8.0.0': {} - '@babel/helpers@7.29.10': + '@babel/helpers@7.29.7': dependencies: '@babel/template': 7.29.7 '@babel/types': 7.29.11 @@ -6570,10 +6566,10 @@ snapshots: '@babel/parser': 8.0.8 '@babel/types': 8.0.8 - '@babel/traverse@7.29.10': + '@babel/traverse@7.29.7': dependencies: '@babel/code-frame': 7.29.7 - '@babel/generator': 7.29.8 + '@babel/generator': 7.29.7 '@babel/helper-globals': 7.29.7 '@babel/parser': 7.29.11 '@babel/template': 7.29.7 @@ -6986,7 +6982,7 @@ snapshots: '@jridgewell/gen-mapping@0.3.13': dependencies: - '@jridgewell/sourcemap-codec': 1.6.0 + '@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/gen-mapping@0.4.0-beta.0': @@ -7008,7 +7004,7 @@ snapshots: '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.6.0 + '@jridgewell/sourcemap-codec': 1.5.5 '@jsdevtools/ono@7.1.3': {} @@ -7082,11 +7078,11 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 - '@openai/agents-core@0.21.0(undici@8.12.2)(ws@8.22.0)(zod@4.6.7)': + '@openai/agents-core@0.21.0(undici@8.12.3)(ws@8.22.0)(zod@4.6.7)': dependencies: '@standard-schema/spec': 1.1.0 debug: 4.4.3 - openai: 7.43.0(undici@8.12.2)(ws@8.22.0)(zod@4.6.7) + openai: 7.43.0(undici@8.12.3)(ws@8.22.0)(zod@4.6.7) optionalDependencies: '@modelcontextprotocol/client': 2.3.1 zod: 4.6.7 @@ -7099,10 +7095,10 @@ snapshots: - undici - ws - '@openai/agents-openai@0.21.0(undici@8.12.2)(ws@8.22.0)(zod@4.6.7)': + '@openai/agents-openai@0.21.0(undici@8.12.3)(ws@8.22.0)(zod@4.6.7)': dependencies: - '@openai/agents-core': 0.21.0(undici@8.12.2)(ws@8.22.0)(zod@4.6.7) - openai: 7.43.0(undici@8.12.2)(ws@8.22.0)(zod@4.6.7) + '@openai/agents-core': 0.21.0(undici@8.12.3)(ws@8.22.0)(zod@4.6.7) + openai: 7.43.0(undici@8.12.3)(ws@8.22.0)(zod@4.6.7) zod: 4.6.7 transitivePeerDependencies: - '@aws-sdk/credential-provider-node' @@ -7113,9 +7109,9 @@ snapshots: - undici - ws - '@openai/agents-realtime@0.21.0(undici@8.12.2)(zod@4.6.7)': + '@openai/agents-realtime@0.21.0(undici@8.12.3)(zod@4.6.7)': dependencies: - '@openai/agents-core': 0.21.0(undici@8.12.2)(ws@8.22.0)(zod@4.6.7) + '@openai/agents-core': 0.21.0(undici@8.12.3)(ws@8.22.0)(zod@4.6.7) '@types/ws': 8.18.2 ws: 8.22.0 zod: 4.6.7 @@ -7129,12 +7125,12 @@ snapshots: - undici - utf-8-validate - '@openai/agents@0.21.0(undici@8.12.2)(ws@8.22.0)(zod@4.6.7)': + '@openai/agents@0.21.0(undici@8.12.3)(ws@8.22.0)(zod@4.6.7)': dependencies: - '@openai/agents-core': 0.21.0(undici@8.12.2)(ws@8.22.0)(zod@4.6.7) - '@openai/agents-openai': 0.21.0(undici@8.12.2)(ws@8.22.0)(zod@4.6.7) - '@openai/agents-realtime': 0.21.0(undici@8.12.2)(zod@4.6.7) - openai: 7.43.0(undici@8.12.2)(ws@8.22.0)(zod@4.6.7) + '@openai/agents-core': 0.21.0(undici@8.12.3)(ws@8.22.0)(zod@4.6.7) + '@openai/agents-openai': 0.21.0(undici@8.12.3)(ws@8.22.0)(zod@4.6.7) + '@openai/agents-realtime': 0.21.0(undici@8.12.3)(zod@4.6.7) + openai: 7.43.0(undici@8.12.3)(ws@8.22.0)(zod@4.6.7) zod: 4.6.7 transitivePeerDependencies: - '@aws-sdk/credential-provider-node' @@ -7149,43 +7145,43 @@ snapshots: '@opentelemetry/api-logs@0.220.0': dependencies: - '@opentelemetry/api': 1.10.0 + '@opentelemetry/api': 1.9.1 - '@opentelemetry/api@1.10.0': {} + '@opentelemetry/api@1.9.1': {} - '@opentelemetry/core@2.12.0(@opentelemetry/api@1.10.0)': + '@opentelemetry/core@2.12.0(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.10.0 + '@opentelemetry/api': 1.9.1 '@opentelemetry/semantic-conventions': 1.43.0 - '@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.10.0)': + '@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.10.0 + '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.220.0 import-in-the-middle: 3.5.2 require-in-the-middle: 8.0.1 transitivePeerDependencies: - supports-color - '@opentelemetry/resources@2.12.0(@opentelemetry/api@1.10.0)': + '@opentelemetry/resources@2.12.0(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.10.0 - '@opentelemetry/core': 2.12.0(@opentelemetry/api@1.10.0) + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.12.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.43.0 - '@opentelemetry/sdk-trace-base@2.12.0(@opentelemetry/api@1.10.0)': + '@opentelemetry/sdk-trace-base@2.12.0(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.10.0 - '@opentelemetry/core': 2.12.0(@opentelemetry/api@1.10.0) - '@opentelemetry/resources': 2.12.0(@opentelemetry/api@1.10.0) - '@opentelemetry/sdk-trace': 2.12.0(@opentelemetry/api@1.10.0) + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.12.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.12.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace': 2.12.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.43.0 - '@opentelemetry/sdk-trace@2.12.0(@opentelemetry/api@1.10.0)': + '@opentelemetry/sdk-trace@2.12.0(@opentelemetry/api@1.9.1)': dependencies: - '@opentelemetry/api': 1.10.0 - '@opentelemetry/core': 2.12.0(@opentelemetry/api@1.10.0) - '@opentelemetry/resources': 2.12.0(@opentelemetry/api@1.10.0) + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.12.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.12.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.43.0 '@opentelemetry/semantic-conventions@1.43.0': {} @@ -8152,27 +8148,27 @@ snapshots: dependencies: '@sentry/conventions': 0.16.0 - '@sentry/node-core@10.76.2(@opentelemetry/api@1.10.0)(@opentelemetry/core@2.12.0(@opentelemetry/api@1.10.0))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.10.0))(@opentelemetry/sdk-trace-base@2.12.0(@opentelemetry/api@1.10.0))': + '@sentry/node-core@10.76.2(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.12.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.12.0(@opentelemetry/api@1.9.1))': dependencies: '@sentry/conventions': 0.16.0 '@sentry/core': 10.76.2 - '@sentry/opentelemetry': 10.76.2(@opentelemetry/api@1.10.0)(@opentelemetry/core@2.12.0(@opentelemetry/api@1.10.0))(@opentelemetry/sdk-trace-base@2.12.0(@opentelemetry/api@1.10.0)) + '@sentry/opentelemetry': 10.76.2(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.12.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.12.0(@opentelemetry/api@1.9.1)) import-in-the-middle: 3.5.2 optionalDependencies: - '@opentelemetry/api': 1.10.0 - '@opentelemetry/core': 2.12.0(@opentelemetry/api@1.10.0) - '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.10.0) - '@opentelemetry/sdk-trace-base': 2.12.0(@opentelemetry/api@1.10.0) + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.12.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.12.0(@opentelemetry/api@1.9.1) - '@sentry/node@10.76.2(@opentelemetry/core@2.12.0(@opentelemetry/api@1.10.0))': + '@sentry/node@10.76.2(@opentelemetry/core@2.12.0(@opentelemetry/api@1.9.1))': dependencies: - '@opentelemetry/api': 1.10.0 - '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.10.0) - '@opentelemetry/sdk-trace-base': 2.12.0(@opentelemetry/api@1.10.0) + '@opentelemetry/api': 1.9.1 + '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.12.0(@opentelemetry/api@1.9.1) '@sentry/conventions': 0.16.0 '@sentry/core': 10.76.2 - '@sentry/node-core': 10.76.2(@opentelemetry/api@1.10.0)(@opentelemetry/core@2.12.0(@opentelemetry/api@1.10.0))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.10.0))(@opentelemetry/sdk-trace-base@2.12.0(@opentelemetry/api@1.10.0)) - '@sentry/opentelemetry': 10.76.2(@opentelemetry/api@1.10.0)(@opentelemetry/core@2.12.0(@opentelemetry/api@1.10.0))(@opentelemetry/sdk-trace-base@2.12.0(@opentelemetry/api@1.10.0)) + '@sentry/node-core': 10.76.2(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.12.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.12.0(@opentelemetry/api@1.9.1)) + '@sentry/opentelemetry': 10.76.2(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.12.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.12.0(@opentelemetry/api@1.9.1)) '@sentry/server-utils': 10.76.2 import-in-the-middle: 3.5.2 transitivePeerDependencies: @@ -8180,11 +8176,11 @@ snapshots: - '@opentelemetry/exporter-trace-otlp-http' - supports-color - '@sentry/opentelemetry@10.76.2(@opentelemetry/api@1.10.0)(@opentelemetry/core@2.12.0(@opentelemetry/api@1.10.0))(@opentelemetry/sdk-trace-base@2.12.0(@opentelemetry/api@1.10.0))': + '@sentry/opentelemetry@10.76.2(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.12.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.12.0(@opentelemetry/api@1.9.1))': dependencies: - '@opentelemetry/api': 1.10.0 - '@opentelemetry/core': 2.12.0(@opentelemetry/api@1.10.0) - '@opentelemetry/sdk-trace-base': 2.12.0(@opentelemetry/api@1.10.0) + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.12.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.12.0(@opentelemetry/api@1.9.1) '@sentry/conventions': 0.16.0 '@sentry/core': 10.76.2 @@ -8339,21 +8335,21 @@ snapshots: tailwindcss: 4.3.3 vite: 8.3.5(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1) - '@tangent/embed-react@https://codeload.github.com/TangleML/tangent-shell/tar.gz/1998f551d910e229d5108045b552b5549635cd58#path:packages/embed-react(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': + '@tangent/embed-react@https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/embed-react(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: react: 19.3.1 react-dom: 19.3.1(react@19.3.1) - '@tangent/remote-subagent@https://codeload.github.com/TangleML/tangent-shell/tar.gz/1998f551d910e229d5108045b552b5549635cd58#path:packages/remote-subagent': + '@tangent/remote-subagent@https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/remote-subagent': dependencies: - '@tangent/shared': https://codeload.github.com/TangleML/tangent-shell/tar.gz/1998f551d910e229d5108045b552b5549635cd58#path:packages/shared + '@tangent/shared': https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/shared socket.io-client: 4.8.4 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@tangent/shared@https://codeload.github.com/TangleML/tangent-shell/tar.gz/1998f551d910e229d5108045b552b5549635cd58#path:packages/shared': + '@tangent/shared@https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/shared': dependencies: zod: 4.6.7 @@ -8474,7 +8470,7 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 optionalDependencies: - vitest: 4.1.10(@opentelemetry/api@1.10.0)(@types/node@26.6.5)(@vitest/coverage-v8@5.0.5)(jsdom@30.1.3)(vite@8.3.5(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) + vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.6.5)(@vitest/coverage-v8@5.0.5)(jsdom@30.1.3)(vite@8.3.5(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) '@testing-library/react@16.3.3(@testing-library/dom@10.4.2)(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: @@ -8535,7 +8531,7 @@ snapshots: '@types/estree-jsx@1.0.5': dependencies: - '@types/estree': 1.0.9 + '@types/estree': 1.0.8 '@types/estree@1.0.8': {} @@ -8598,7 +8594,7 @@ snapshots: '@typescript-eslint/utils': 8.72.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.72.0 eslint: 9.39.4(jiti@2.7.0) - ignore: 7.0.13 + ignore: 7.0.12 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 @@ -8700,7 +8696,7 @@ snapshots: obug: 3.0.0 std-env: 4.3.0 tinyrainbow: 3.2.0 - vitest: 4.1.10(@opentelemetry/api@1.10.0)(@types/node@26.6.5)(@vitest/coverage-v8@5.0.5)(jsdom@30.1.3)(vite@8.3.5(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) + vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.6.5)(@vitest/coverage-v8@5.0.5)(jsdom@30.1.3)(vite@8.3.5(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) '@vitest/expect@4.1.10': dependencies: @@ -8709,7 +8705,7 @@ snapshots: '@vitest/spy': 4.1.10 '@vitest/utils': 4.1.10 chai: 6.2.2 - tinyrainbow: 3.2.0 + tinyrainbow: 3.1.0 '@vitest/istanbul-lib-coverage@1.0.2': {} @@ -8727,7 +8723,7 @@ snapshots: '@vitest/pretty-format@4.1.10': dependencies: - tinyrainbow: 3.2.0 + tinyrainbow: 3.1.0 '@vitest/runner@4.1.10': dependencies: @@ -8747,7 +8743,7 @@ snapshots: dependencies: '@vitest/pretty-format': 4.1.10 convert-source-map: 2.0.0 - tinyrainbow: 3.2.0 + tinyrainbow: 3.1.0 '@xyflow/react@12.12.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: @@ -8822,7 +8818,7 @@ snapshots: ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.9 + fast-uri: 3.1.8 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -8981,13 +8977,13 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.29.4: + browserslist@4.28.2: dependencies: baseline-browser-mapping: 2.11.32 caniuse-lite: 1.0.30001819 electron-to-chromium: 1.5.464 node-releases: 2.0.60 - update-browserslist-db: 1.3.4(browserslist@4.29.4) + update-browserslist-db: 1.2.3(browserslist@4.28.2) buffer-from@1.1.2: {} @@ -9250,7 +9246,7 @@ snapshots: data-urls@8.0.0: dependencies: whatwg-mimetype: 5.0.0 - whatwg-url: 17.3.0 + whatwg-url: 17.2.0 transitivePeerDependencies: - '@noble/hashes' @@ -9513,7 +9509,7 @@ snapshots: es-module-lexer@2.3.1: {} - es-module-lexer@3.0.4: {} + es-module-lexer@3.0.3: {} es-object-atoms@1.1.1: dependencies: @@ -9748,7 +9744,7 @@ snapshots: fast-safe-stringify@1.2.3: {} - fast-uri@3.1.9: {} + fast-uri@3.1.8: {} fastq@1.20.1: dependencies: @@ -10033,8 +10029,6 @@ snapshots: ignore@7.0.12: {} - ignore@7.0.13: {} - ignore@7.0.14: {} immediate@3.0.6: {} @@ -10047,7 +10041,7 @@ snapshots: import-in-the-middle@3.5.2: dependencies: cjs-module-lexer: 2.3.0 - es-module-lexer: 3.0.4 + es-module-lexer: 3.0.3 module-details-from-path: 1.0.4 import-meta-resolve@4.2.0: {} @@ -10271,7 +10265,7 @@ snapshots: jsdom@30.1.3: dependencies: - '@asamuzakjp/css-color': 7.1.7 + '@asamuzakjp/css-color': 7.1.5 '@asamuzakjp/dom-selector': 10.0.4 '@bramus/specificity': 2.4.3 '@csstools/css-syntax-patches-for-csstree': 1.1.15(css-tree@3.2.1) @@ -10285,11 +10279,11 @@ snapshots: parse5: 8.0.1 saxes: 6.0.0 tough-cookie: 6.0.2 - undici: 8.12.2 + undici: 8.12.3 w3c-xmlserializer: 6.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 5.0.0 - whatwg-url: 17.3.0 + whatwg-url: 17.2.0 xml-name-validator: 5.0.0 transitivePeerDependencies: - '@noble/hashes' @@ -11025,9 +11019,9 @@ snapshots: powershell-utils: 0.1.0 wsl-utils: 0.3.1 - openai@7.43.0(undici@8.12.2)(ws@8.22.0)(zod@4.6.7): + openai@7.43.0(undici@8.12.3)(ws@8.22.0)(zod@4.6.7): optionalDependencies: - undici: 8.12.2 + undici: 8.12.3 ws: 8.22.0 zod: 4.6.7 @@ -11347,10 +11341,10 @@ snapshots: optionalDependencies: '@types/react': 19.3.0 - react-doctor@0.9.18(@opentelemetry/core@2.12.0(@opentelemetry/api@1.10.0))(eslint@9.39.4(jiti@2.7.0))(react@19.3.1): + react-doctor@0.9.18(@opentelemetry/core@2.12.0(@opentelemetry/api@1.9.1))(eslint@9.39.4(jiti@2.7.0))(react@19.3.1): dependencies: '@astrojs/compiler': 4.0.0 - '@sentry/node': 10.76.2(@opentelemetry/core@2.12.0(@opentelemetry/api@1.10.0)) + '@sentry/node': 10.76.2(@opentelemetry/core@2.12.0(@opentelemetry/api@1.9.1)) agent-install: 0.0.5 bippy: 0.6.1(react@19.3.1) conf: 15.1.0 @@ -11440,7 +11434,7 @@ snapshots: optionalDependencies: '@types/react': 19.3.0 - react-scan@0.5.7(@opentelemetry/core@2.12.0(@opentelemetry/api@1.10.0))(esbuild@0.28.2)(eslint@9.39.4(jiti@2.7.0))(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(rollup@4.61.1): + react-scan@0.5.7(@opentelemetry/core@2.12.0(@opentelemetry/api@1.9.1))(esbuild@0.28.2)(eslint@9.39.4(jiti@2.7.0))(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(rollup@4.61.1): dependencies: '@babel/core': 7.29.7 '@babel/types': 7.29.7 @@ -11452,7 +11446,7 @@ snapshots: preact: 10.29.2 prompts: 2.4.2 react: 19.3.1 - react-doctor: 0.9.18(@opentelemetry/core@2.12.0(@opentelemetry/api@1.10.0))(eslint@9.39.4(jiti@2.7.0))(react@19.3.1) + react-doctor: 0.9.18(@opentelemetry/core@2.12.0(@opentelemetry/api@1.9.1))(eslint@9.39.4(jiti@2.7.0))(react@19.3.1) react-dom: 19.3.1(react@19.3.1) react-grab: 0.2.0(react@19.3.1) optionalDependencies: @@ -12169,7 +12163,7 @@ snapshots: undici-types@8.9.0: {} - undici@8.12.2: {} + undici@8.12.3: {} unified@11.0.5: dependencies: @@ -12213,9 +12207,9 @@ snapshots: webpack-virtual-modules: 0.6.2 optional: true - update-browserslist-db@1.3.4(browserslist@4.29.4): + update-browserslist-db@1.2.3(browserslist@4.28.2): dependencies: - browserslist: 4.29.4 + browserslist: 4.28.2 escalade: 3.2.0 picocolors: 1.1.1 @@ -12294,7 +12288,7 @@ snapshots: tsx: 4.23.15 yaml: 2.9.1 - vitest@4.1.10(@opentelemetry/api@1.10.0)(@types/node@26.6.5)(@vitest/coverage-v8@5.0.5)(jsdom@30.1.3)(vite@8.3.5(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)): + vitest@4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.6.5)(@vitest/coverage-v8@5.0.5)(jsdom@30.1.3)(vite@8.3.5(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)): dependencies: '@vitest/expect': 4.1.10 '@vitest/mocker': 4.1.10(vite@8.3.5(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1)) @@ -12317,7 +12311,7 @@ snapshots: vite: 8.3.5(@types/node@26.6.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)(yaml@2.9.1) why-is-node-running: 2.3.0 optionalDependencies: - '@opentelemetry/api': 1.10.0 + '@opentelemetry/api': 1.9.1 '@types/node': 26.6.5 '@vitest/coverage-v8': 5.0.5(vitest@4.1.10) jsdom: 30.1.3 @@ -12339,7 +12333,7 @@ snapshots: whatwg-mimetype@5.0.0: {} - whatwg-url@17.3.0: + whatwg-url@17.2.0: dependencies: '@exodus/bytes': 1.16.0 tr46: 7.0.0 diff --git a/tsconfig.json b/tsconfig.json index bff8cc221a..5ae66792bd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,5 +25,5 @@ "noFallthroughCasesInSwitch": true, "noImplicitAny": true }, - "exclude": ["node_modules", "src/api"] + "exclude": ["node_modules", "src/api", "vscode-extension"] } diff --git a/vite.config.js b/vite.config.js index 58f939c608..d55c869931 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,6 +1,14 @@ import babel from "@rolldown/plugin-babel"; import tailwindcss from "@tailwindcss/vite"; import viteReact from "@vitejs/plugin-react"; +import { spawn } from "child_process"; +import { + copyFileSync, + existsSync, + mkdirSync, + readFileSync, + writeFileSync, +} from "fs"; import { createRequire } from "module"; import path from "path"; import { fileURLToPath } from "url"; @@ -25,6 +33,155 @@ const agentsCoreBrowserShim = path.resolve( "shims/shims-browser.mjs", ); +function isExtensionArtifactCurrent(outputDir, name, vsixFilename, version) { + const manifestPath = path.join(outputDir, "latest.json"); + const requiredFiles = [ + manifestPath, + path.join(outputDir, vsixFilename), + path.join(outputDir, `${name}.vsix`), + ]; + if (requiredFiles.some((file) => !existsSync(file))) return false; + try { + const manifest = JSON.parse(readFileSync(manifestPath, "utf8")); + return manifest.version === version && manifest.filename === vsixFilename; + } catch { + return false; + } +} + +// Compiles the Tangent Local Agent extension to a `.vsix` and publishes it, +// its install scripts, and a version manifest under `public/extension/` so the +// app serves them from its own origin (`/extension/*`). A production build +// (`vite build`) always builds it and fails if it fails. The dev server never +// blocks on it: the build is opt-in there via `BUILD_VSCODE_EXTENSION=true`, in +// which case it runs in the background. Either way the rebuild is skipped when +// the published artifacts already match the extension's declared version. The +// extension's own `package` script shells out to esbuild and `@vscode/vsce`. +function tangleVscodeExtension() { + const extensionDir = path.resolve(__dirname, "vscode-extension"); + const outputDir = path.resolve(__dirname, "public/extension"); + const manifestPath = path.join(extensionDir, "package.json"); + + const buildInDev = process.env.BUILD_VSCODE_EXTENSION === "true"; + let command = "serve"; + let failedVersion; + let building = false; + + const run = (args) => + new Promise((resolve, reject) => { + // `shell: true` so Windows resolves the `pnpm.cmd` shim; async spawn so a + // long build never blocks Vite's event loop. + const child = spawn("pnpm", args, { + cwd: extensionDir, + stdio: "inherit", + shell: true, + }); + child.on("error", reject); + child.on("close", (code) => + code === 0 + ? resolve() + : reject( + new Error(`pnpm ${args.join(" ")} exited with code ${code}`), + ), + ); + }); + + const buildArtifacts = async () => { + const pkg = JSON.parse(readFileSync(manifestPath, "utf8")); + const { name, version } = pkg; + const vsixFilename = `${name}-${version}.vsix`; + + if (isExtensionArtifactCurrent(outputDir, name, vsixFilename, version)) { + return false; + } + // A previous attempt for this exact version already failed; don't re-run the + // install/package toolchain until the declared version changes. + if (failedVersion === version) return false; + + try { + await run(["install", "--frozen-lockfile"]); + await run(["package"]); + } catch (error) { + failedVersion = version; + throw error; + } + + mkdirSync(outputDir, { recursive: true }); + + const builtVsix = path.join(extensionDir, vsixFilename); + copyFileSync(builtVsix, path.join(outputDir, vsixFilename)); + copyFileSync(builtVsix, path.join(outputDir, `${name}.vsix`)); + + for (const script of ["install.sh", "install.ps1"]) { + const src = path.join(extensionDir, script); + if (existsSync(src)) { + copyFileSync(src, path.join(outputDir, script)); + } + } + + writeFileSync( + path.join(outputDir, "latest.json"), + `${JSON.stringify( + { + version, + filename: vsixFilename, + url: `/extension/${vsixFilename}`, + }, + null, + 2, + )}\n`, + ); + + failedVersion = undefined; + return true; + }; + + // A failed extension build must not fail the web app's build or stall its dev + // server, so log and keep going with whatever artifact is already published. + const buildOnce = async (log) => { + if (building) return false; + building = true; + try { + return await buildArtifacts(); + } catch (error) { + log(`[tangle-vscode-extension] build failed: ${error.message}`); + return false; + } finally { + building = false; + } + }; + + return { + name: "tangle-vscode-extension", + configResolved(config) { + command = config.command; + }, + // Only a production build blocks here, and it fails the build on error. In + // dev, `buildStart` is awaited before the server listens, so building here + // would stall `pnpm start`; the background loop in `configureServer` handles + // dev instead. + async buildStart() { + if (command !== "build") return; + await buildArtifacts(); + }, + // A bump to this nested, non-imported manifest is invisible to Vite's dev + // watcher (it only tracks the module graph, config, and publicDir, and + // `server.watcher.add` does not bring it in). Poll the staleness guard + // instead, which is a cheap stat+parse until the declared version changes. + configureServer(server) { + if (!buildInDev) return; + const interval = setInterval(() => { + void buildOnce((message) => server.config.logger.error(message)).then( + (rebuilt) => { + if (rebuilt) server.hot.send({ type: "full-reload" }); + }, + ); + }, 2000); + server.httpServer?.once("close", () => clearInterval(interval)); + }, + }; +} + export default defineConfig(({ mode }) => { if (mode === "test") process.env.TZ = "America/New_York"; const env = loadEnv(mode, process.cwd(), ""); @@ -44,6 +201,7 @@ export default defineConfig(({ mode }) => { return { plugins: [ + ...(mode === "test" ? [] : [tangleVscodeExtension()]), viteReact(), babel({ plugins: [ diff --git a/vscode-extension/.gitignore b/vscode-extension/.gitignore new file mode 100644 index 0000000000..7dfeb92b44 --- /dev/null +++ b/vscode-extension/.gitignore @@ -0,0 +1,3 @@ +*.vsix +node_modules +dist diff --git a/vscode-extension/.vscode/launch.json b/vscode-extension/.vscode/launch.json new file mode 100644 index 0000000000..d5f65810da --- /dev/null +++ b/vscode-extension/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run Tangent Local Agent", + "type": "extensionHost", + "request": "launch", + "args": ["--extensionDevelopmentPath=${workspaceFolder}"], + "outFiles": ["${workspaceFolder}/dist/**/*.cjs"], + "preLaunchTask": "${defaultBuildTask}" + } + ] +} diff --git a/vscode-extension/.vscode/tasks.json b/vscode-extension/.vscode/tasks.json new file mode 100644 index 0000000000..389ef4f359 --- /dev/null +++ b/vscode-extension/.vscode/tasks.json @@ -0,0 +1,12 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "type": "shell", + "command": "pnpm build", + "group": { "kind": "build", "isDefault": true }, + "problemMatcher": [] + } + ] +} diff --git a/vscode-extension/.vscodeignore b/vscode-extension/.vscodeignore new file mode 100644 index 0000000000..b05d5ea614 --- /dev/null +++ b/vscode-extension/.vscodeignore @@ -0,0 +1,10 @@ +** +!dist/extension.cjs +!dist/chat.js +!dist/proxyProvider.ts +!dist/sessionResources.ts +!resources/tangle.svg +!resources/icon.png +!package.json +!README.md +!LICENSE diff --git a/vscode-extension/LICENSE b/vscode-extension/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/vscode-extension/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vscode-extension/README.md b/vscode-extension/README.md new file mode 100644 index 0000000000..66f8b3dfb0 --- /dev/null +++ b/vscode-extension/README.md @@ -0,0 +1,205 @@ +# Tangent Local Agent (VS Code) + +A VS Code extension that connects your editor to a Tangent Shell in both directions: + +- **Prime → your machine.** The extension registers as a remote environment on the Shell's + `/remote-env` namespace. When Prime calls `spawn_subagent` with `environment: "remote"`, the + sub-agent runs here as a local `pi --mode rpc` process in your open workspace folder and streams + its replies back into the session. +- **Your machine → Tangent.** Start a new session or attach to an existing one from VS Code, and + chat with it in an editor panel, the same chat the Tangent Shell shows. +- **Current Tangle project.** The **Tangle** activity bar container follows one project at a time, + with **Runs**, **Resources**, and **Sessions** views. New sessions are recorded on the project and + get this window's local environment and file tools. + +This is an MVP: it authenticates as a dev identity (an unsigned JWT for `tangent.userEmail`) and +isn't published to a marketplace. + +## Prerequisites + +- `pi` on your `PATH` (or set `tangent.piBin`). Local sub-agents use your own pi configuration + unless you set `tangent.piProxyUrl`. +- A Shell that accepts the extension's dev identity on its `/remote-env` namespace. +- A folder open in VS Code. Sub-agents run there with the pi built-in tools Prime grants (`read`, + `write`, `edit`, `bash`, `grep`, `find`, `ls`), always including `read`, `ls`, `grep`, and `find`. + A sub-agent granted none of them gets all seven. + +## Run it + +The extension is a standalone package, separate from the Tangle UI app: it has its own +`package.json`, lockfile, and `pnpm-workspace.yaml`, and the root project's typecheck and knip skip +it. + +```bash +cd vscode-extension +pnpm install +pnpm build +``` + +Then open `vscode-extension` in VS Code and press F5 (**Run Tangent Local Agent**). To install it +into your everyday editor instead, build a `.vsix` and install it: + +```bash +pnpm package +code --install-extension tangent-local-agent-*.vsix +``` + +The web app also serves the current build: run `/extension/install.sh` (or `install.ps1`) from the +deployment to download and install the latest `.vsix`, e.g. +`curl -fsSL /extension/install.sh | sh -s -- `. + +## Connect + +Run **Tangent: Connect Local Agent** (or click the **Tangent** status bar item), then enter the +Shell URL, e.g. `http://localhost:5173`. Pasting a session URL (`…/sessions/`) also attaches to +that session. + +The extension reconnects on startup (`tangent.autoConnect`). A link of the form +`vscode://tangent.tangent-local-agent/connect?url=` (optionally with `apiUrl` and +`projectId`) sets the window up for you. When the link points at a Shell or Tangle API origin you +are not already connected to, the extension asks you to confirm those origins before connecting. The +project name is read from the backend, not taken from the link. + +## Commands + +| Command | What it does | +| ------------------------------- | ---------------------------------------------------------------------------- | +| Tangle: Select Project | Pick the current project. The Runs, Resources, and Sessions views follow it. | +| Tangle: New Session | Create a session in the current project and open its chat. | +| Tangle: Open Project in Tangle | Open the current project in the Tangle web app. | +| Tangle: Set Tangle API URL | Set the Tangle backend URL and an optional bearer token. | +| Tangle: Refresh | Reload the Runs, Resources, and Sessions views. | +| Tangent: Connect Local Agent | Set the Shell URL, then connect. | +| Tangent: Attach to Session | Open any Shell session's chat by URL or id, even one outside the project. | +| Tangent: Disconnect Local Agent | Stop every local sub-agent and disconnect. | +| Tangent: Set Pi Proxy API Key | Store the key local pi uses with `tangent.piProxyUrl`. | + +Opening, renaming, and removing a session are on the session's inline button and context menu in +the **Sessions** view. + +The status bar shows the connection state and how many sub-agents are running. Its menu can stop +any single sub-agent, and each new sub-agent shows a notification with a **Stop** button. + +## Settings + +| Setting | Default | Purpose | +| -------------------------- | ----------------------- | ---------------------------------------------------------------- | +| `tangent.shellUrl` | — | Shell to connect to. | +| `tangent.autoConnect` | `true` | Reconnect on startup. | +| `tangent.defaultBundleId` | — | Bundle for **New Session**. Empty means pick from a list. | +| `tangent.userEmail` | — | Identity this window mints scoped tokens as. Empty uses dev. | +| `tangent.piBin` | `pi` | Path to the pi executable. | +| `tangent.piModel` | — | Model (`provider/id`) for local sub-agents. Empty uses Prime's. | +| `tangent.piProxyUrl` | — | LLM proxy for local pi. Empty uses your own pi configuration. | +| `tangent.tangleApiUrl` | `http://localhost:8000` | Tangle backend for projects, runs, and resources. | +| `tangent.tangleUiUrl` | `http://localhost:3000` | Tangle web app used to open projects and runs. | +| `tangent.updateBaseUrl` | — | Base URL checked for new `.vsix` builds. Empty uses tangleUiUrl. | +| `tangent.autoCheckUpdates` | `true` | Check for a newer build on startup and offer to update. | + +With `tangent.piProxyUrl` set, local pi loads the bundled proxy-provider extension and reads the +key from **Set Pi Proxy API Key**, falling back to `PI_PROXY_API_KEY` in VS Code's environment. + +## Current project + +The **Tangle** activity bar container follows one project, picked with **Select Project** (title bar +of any of its views). The choice is remembered per workspace, and each view shows the project name. +Until a project is picked, the views offer **Select Project** and **Set Tangle API URL** instead. +Projects, runs, and resources come from the Tangle backend REST API (`tangent.tangleApiUrl`). + +- **Runs** lists the project's runs (`GET /api/projects/{id}/runs`), newest first, with the overall + status computed from task counts the same way the web app does. While the view is visible and a + run is still active (running, pending, queued, …), active runs refresh every 10 seconds. Long + lists page in with **Load more runs…**. Click a run to open it in the Tangle web app + (`tangent.tangleUiUrl`). +- **Resources** lists the project's pipelines and documents, most recently updated first. Click one + to open it in the web app: a pipeline opens in the editor, a run reference opens the run, anything + else opens the project page. +- **Sessions** lists the project's agentic sessions: its `agent_session` resources, each pointing at + a Tangent Shell session. **New Session** (the `+` in the title bar) creates a Shell session from an + agent bundle, records it on the project, and opens its chat. **Remove Session from Project** + deletes only the project's resource; the session stays on the Shell. The session open in this + window's chat is marked **local env**; the window's local environment serves every session. + +A run belongs to a project through its `tangleml.com/project/id/` annotation, set when the +run is submitted, so runs submitted from the web app with a project, or from a client that sets the +annotation, show up here. + +## Session chat + +Opening a session attaches this window to it (see [How routing works](#how-routing-works)), so +Prime's remote sub-agents and read-only file tools run here, and opens the session's chat in an +editor panel. The panel renders the Shell's own embeddable chat (`` from +`@tangent/embed-react`), so messages, streaming, attachments, and model choice behave as they do in +the Shell. The panel follows the VS Code light or dark theme, and artifact links open in your +browser. + +The chat runtime, its API calls, and its socket all come from the Shell, through a loopback proxy +the extension starts on `127.0.0.1`. The Shell answers CORS only for an exact list of origins +(`EMBED_ALLOWED_ORIGINS`), and a webview's origin is a fresh `vscode-webview://` for every panel, +so it can't be listed there. The proxy accepts only webview origins and forwards to the Shell +without an `Origin` header. + +Known limits: + +- The panel authenticates with the window's unsigned identity JWT (the `tangent.userEmail` + identity), not a per-user auth token. +- Bundle UI widgets in messages show "This component could not be displayed". The Shell's runtime + starts their Web Worker from its own origin, and browsers refuse cross-origin workers, so this + needs a fix in the Shell (for example, starting the worker from a `blob:` URL). +- Against a Shell running under Vite dev, the page installs no-op React Fast Refresh hooks before + loading the runtime, because Vite's dev modules throw without them. + +## How routing works + +Each VS Code window connects as its own environment, `vscode--`, scoped to the +current project. The Shell treats it as a generic dev host eligible for all of that project's +sessions, so one window serves them all — opening or switching sessions does not change the id or +reconnect the bridge, and running sub-agents are never interrupted. Switching projects reconnects +under a new id. When several windows connect, the most recently connected one serves every session; +pinning a specific window to a specific session is not supported. + +Server-only tools in Prime's allowlist (`message_prime`, `read_room`, …) are dropped, because local +pi doesn't have them. A local sub-agent is never started without file tools: it always gets read +access to the workspace, and the full built-in set when the allowlist names no built-in tools. Its +finished replies still reach Prime through the session. + +## Read-only file tools + +For light lookups, Prime doesn't need a local sub-agent. The extension registers read-only tools that +Prime calls directly through `list_remote_tools` / `call_remote_tool`. No local pi process starts and +no local tokens are spent: + +| Tool | What it returns | +| ------------- | ---------------------------------------------------------------------- | +| `read_file` | A text file's contents, truncated past `maxBytes` (256 KB by default). | +| `list_dir` | A directory's entries. Directory names end with `/`. | +| `find_files` | Paths matching a glob such as `*.ts` or `src/**/*.test.ts`. | +| `search_text` | `path:line: text` for each line matching a string or regex. | + +Paths are relative to the first workspace folder. Anything outside it is refused, including symlinks +that point outside. Binary files are skipped, and so are `.git`, `node_modules`, and build output +folders. + +The window acts as a generic dev host, so its tool calls reach any of the project's sessions. If a +browser embed with a session-scoped token is connected to a session, the embed handles that +session's tool calls instead of VS Code. + +## Develop + +The host logic (pi adapter, file tools, URL parsing) is imported from `@tangent/local-agent-host`, +not vendored here. It, `@tangent/shared`, `@tangent/remote-subagent`, and `@tangent/embed-react` +come from the `TangleML/tangent-shell` GitHub repo, pinned to a commit. + +esbuild produces two bundles: `dist/extension.cjs` for the extension host and `dist/chat.js`, the +React page inside the chat panel (`src/webview/chat.tsx`, typechecked with +`tsconfig.webview.json`). + +```bash +pnpm test # resource kinds, run status, REST client, update manifest, chat config/html, embed proxy +pnpm typecheck +pnpm lint +pnpm watch # rebuild on change while running under F5 +``` + +Not in this MVP: user-scoped login instead of the dev token, editor tools for Prime (diagnostics, +apply edit), `tangle-deploy` integration (submit, validate, logs), and marketplace publishing. diff --git a/vscode-extension/esbuild.mjs b/vscode-extension/esbuild.mjs new file mode 100644 index 0000000000..d6b9a7de00 --- /dev/null +++ b/vscode-extension/esbuild.mjs @@ -0,0 +1,100 @@ +// Bundles the extension into a single CommonJS file the VS Code extension host +// loads, plus the session chat webview as a browser ES module. `@tangent/*` +// packages, socket.io-client, and React are inlined, so the packaged .vsix +// needs no node_modules. +import { copyFile, mkdir } from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +import { build, context } from "esbuild"; + +const extensionDir = path.dirname(fileURLToPath(import.meta.url)); +const distDir = path.join(extensionDir, "dist"); +const watch = process.argv.includes("--watch"); + +// The OktaSSO gate is configured through the repo-root `.env`; values already +// present in the build shell win, so CI can inject them without a file. +try { + process.loadEnvFile(path.join(extensionDir, "..", ".env")); +} catch { + // No `.env` — the gate stays disabled unless the keys are set some other way. +} + +const OKTASSO_ENV_KEYS = [ + "VITE_OKTASSO_ENABLED", + "VITE_OKTASSO_OAUTH_ENDPOINT_BASE", + "VITE_OKTASSO_TOKEN_EXCHANGE_URL", + "VITE_OKTASSO_CLIENT_ID", + "VITE_OKTASSO_TOKEN_COOKIE_NAME", + "VITE_OKTASSO_SCOPES", + "VITE_OKTASSO_REDIRECT_URI", + "VITE_OKTASSO_CALLBACK_PORT", + "VITE_OKTASSO_REFRESH_AFTER_MS", +]; + +const oktassoDefine = Object.fromEntries( + OKTASSO_ENV_KEYS.map((key) => [ + `process.env.${key}`, + JSON.stringify(process.env[key] ?? ""), + ]), +); + +/** @type {import("esbuild").BuildOptions} */ +const extensionOptions = { + entryPoints: [path.join(extensionDir, "src", "extension.ts")], + bundle: true, + platform: "node", + format: "cjs", + target: "node20", + outfile: path.join(distDir, "extension.cjs"), + sourcemap: true, + define: oktassoDefine, + // `vscode` is provided by the extension host; the ws native add-ons are + // optional and loaded behind a try/catch. + external: ["vscode", "bufferutil", "utf-8-validate"], +}; + +// ESM, so the embed runtime's dynamic `import()` of the Shell's module works. +/** @type {import("esbuild").BuildOptions} */ +const webviewOptions = { + entryPoints: [path.join(extensionDir, "src", "webview", "chat.tsx")], + bundle: true, + platform: "browser", + format: "esm", + target: "es2022", + jsx: "automatic", + outfile: path.join(distDir, "chat.js"), + sourcemap: true, + minify: !watch, + define: { "process.env.NODE_ENV": JSON.stringify("production") }, +}; + +// pi loads these extensions from disk at spawn time, so they ship unbundled, +// sourced from @tangent/local-agent-host so there is a single copy. +async function copyPiExtensions() { + await mkdir(distDir, { recursive: true }); + await Promise.all( + ["proxyProvider.ts", "sessionResources.ts"].map((file) => + copyFile( + fileURLToPath( + import.meta.resolve(`@tangent/local-agent-host/resources/${file}`), + ), + path.join(distDir, file), + ), + ), + ); +} + +await copyPiExtensions(); +if (watch) { + const contexts = await Promise.all( + [extensionOptions, webviewOptions].map((options) => context(options)), + ); + await Promise.all(contexts.map((ctx) => ctx.watch())); + console.log("[build:vscode-extension] watching"); +} else { + await Promise.all([build(extensionOptions), build(webviewOptions)]); + console.log( + "[build:vscode-extension] bundled to dist/extension.cjs and dist/chat.js", + ); +} diff --git a/vscode-extension/eslint.config.js b/vscode-extension/eslint.config.js new file mode 100644 index 0000000000..0bb41b3994 --- /dev/null +++ b/vscode-extension/eslint.config.js @@ -0,0 +1,30 @@ +import js from "@eslint/js"; +import simpleImportSort from "eslint-plugin-simple-import-sort"; +import globals from "globals"; +import tseslint from "typescript-eslint"; + +export default tseslint.config( + { ignores: ["node_modules", "dist", "resources", "*.vsix"] }, + js.configs.recommended, + ...tseslint.configs.recommended, + { + languageOptions: { globals: globals.node }, + plugins: { "simple-import-sort": simpleImportSort }, + rules: { + "simple-import-sort/imports": "error", + "simple-import-sort/exports": "error", + }, + }, + { + files: ["src/webview/chat.tsx"], + languageOptions: { globals: globals.browser }, + }, + { + files: ["**/*.ts", "**/*.tsx"], + rules: { + complexity: ["error", 6], + "max-depth": ["warn", 3], + "max-lines-per-function": ["warn", 60], + }, + }, +); diff --git a/vscode-extension/install.ps1 b/vscode-extension/install.ps1 new file mode 100644 index 0000000000..c82353b8d2 --- /dev/null +++ b/vscode-extension/install.ps1 @@ -0,0 +1,43 @@ +# Installs the Tangent Local Agent VS Code / Cursor extension from a Tangle +# deployment: resolves the latest build, downloads the .vsix, and installs it +# with the cursor (or code) CLI. +# +# $env:TANGENT_BASE_URL = "https://app.example.com" +# iwr "$env:TANGENT_BASE_URL/extension/install.ps1" -UseBasicParsing | iex +param([string]$BaseUrl = $env:TANGENT_BASE_URL) +$ErrorActionPreference = "Stop" + +if (-not $BaseUrl) { + Write-Error "Usage: install.ps1 (or set `$env:TANGENT_BASE_URL)" + exit 1 +} +$BaseUrl = $BaseUrl.TrimEnd('/') + +$cli = $null +foreach ($candidate in @("cursor", "code")) { + if (Get-Command $candidate -ErrorAction SilentlyContinue) { $cli = $candidate; break } +} +if (-not $cli) { + Write-Error "Neither 'cursor' nor 'code' is on your PATH." + exit 1 +} + +$manifest = Invoke-RestMethod "$BaseUrl/extension/latest.json" +$relUrl = $manifest.url +if (-not $relUrl) { + Write-Error "Could not read the extension URL from $BaseUrl/extension/latest.json" + exit 1 +} +$vsixUrl = if ($relUrl -match '^https?://') { $relUrl } else { "$BaseUrl$relUrl" } + +$tmp = Join-Path ([System.IO.Path]::GetTempPath()) ("tangent-local-agent-" + [guid]::NewGuid().ToString() + ".vsix") +try { + Write-Host "Downloading Tangent Local Agent $($manifest.version)..." + Invoke-WebRequest $vsixUrl -OutFile $tmp -UseBasicParsing + Write-Host "Installing into $cli..." + & $cli --install-extension $tmp --force + Write-Host "Done. Reload or restart $cli to activate the extension." +} +finally { + if (Test-Path $tmp) { Remove-Item $tmp -Force } +} diff --git a/vscode-extension/install.sh b/vscode-extension/install.sh new file mode 100755 index 0000000000..4bd18ca52a --- /dev/null +++ b/vscode-extension/install.sh @@ -0,0 +1,62 @@ +#!/bin/sh +# Installs the Tangent Local Agent VS Code / Cursor extension from a Tangle +# deployment: resolves the latest build, downloads the .vsix, and installs it +# with the cursor (or code) CLI. +# +# curl -fsSL /extension/install.sh | sh -s -- +set -eu + +BASE="${1:-${TANGENT_BASE_URL:-}}" +if [ -z "$BASE" ]; then + echo "Usage: install.sh " >&2 + echo " curl -fsSL https://app.example.com/extension/install.sh | sh -s -- https://app.example.com" >&2 + exit 1 +fi +BASE="${BASE%/}" + +if command -v cursor >/dev/null 2>&1; then + CLI=cursor +elif command -v code >/dev/null 2>&1; then + CLI=code +else + echo "Neither 'cursor' nor 'code' is on your PATH." >&2 + echo "In the editor, run 'Shell Command: Install ... command in PATH' from the command palette, then re-run this." >&2 + exit 1 +fi + +fetch() { + if command -v curl >/dev/null 2>&1; then + if [ -n "${2:-}" ]; then curl -fsSL "$1" -o "$2"; else curl -fsSL "$1"; fi + elif command -v wget >/dev/null 2>&1; then + if [ -n "${2:-}" ]; then wget -qO "$2" "$1"; else wget -qO- "$1"; fi + else + echo "Need curl or wget to download." >&2 + exit 1 + fi +} + +manifest=$(fetch "$BASE/extension/latest.json") +rel_url=$(printf '%s' "$manifest" | sed -n 's/.*"url"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p') +version=$(printf '%s' "$manifest" | sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p') +if [ -z "$rel_url" ]; then + echo "Could not read the extension URL from $BASE/extension/latest.json" >&2 + exit 1 +fi + +case "$rel_url" in + http://* | https://*) vsix_url="$rel_url" ;; + /*) vsix_url="$BASE$rel_url" ;; + *) vsix_url="$BASE/$rel_url" ;; +esac + +tmpdir=$(mktemp -d 2>/dev/null || mktemp -d -t tangent) +tmp="$tmpdir/tangent-local-agent.vsix" +trap 'rm -rf "$tmpdir"' EXIT INT TERM + +echo "Downloading Tangent Local Agent ${version:-latest}..." +fetch "$vsix_url" "$tmp" + +echo "Installing into $CLI..." +"$CLI" --install-extension "$tmp" --force + +echo "Done. Reload or restart $CLI to activate the extension." diff --git a/vscode-extension/package.json b/vscode-extension/package.json new file mode 100644 index 0000000000..6b0659be39 --- /dev/null +++ b/vscode-extension/package.json @@ -0,0 +1,346 @@ +{ + "name": "tangent-local-agent", + "displayName": "Tangent Local Agent", + "description": "Browse a Tangle project's runs, resources, and agentic sessions from VS Code, chat with sessions in an editor panel, and host Tangent sub-agents locally with pi.", + "version": "0.0.27", + "private": true, + "publisher": "tangent", + "license": "Apache-2.0", + "repository": "github:tangleml/tangle-ui", + "icon": "resources/icon.png", + "type": "module", + "main": "./dist/extension.cjs", + "packageManager": "pnpm@10.28.0", + "engines": { + "vscode": "^1.90.0" + }, + "categories": [ + "AI" + ], + "activationEvents": [ + "onStartupFinished", + "onUri" + ], + "contributes": { + "viewsContainers": { + "activitybar": [ + { + "id": "tangle", + "title": "Tangle", + "icon": "resources/tangle.svg" + } + ] + }, + "views": { + "tangle": [ + { + "id": "tangle.runs", + "name": "Runs" + }, + { + "id": "tangle.resources", + "name": "Resources" + }, + { + "id": "tangle.sessions", + "name": "Sessions" + } + ] + }, + "viewsWelcome": [ + { + "view": "tangle.runs", + "contents": "Select a Tangle project to see its runs.\n[Select Project](command:tangle.selectProject)\n[Set Tangle API URL](command:tangle.setApiUrl)", + "when": "!tangle.hasProject" + }, + { + "view": "tangle.resources", + "contents": "Select a Tangle project to see its resources.\n[Select Project](command:tangle.selectProject)\n[Set Tangle API URL](command:tangle.setApiUrl)", + "when": "!tangle.hasProject" + }, + { + "view": "tangle.sessions", + "contents": "Select a Tangle project to see its agentic sessions.\n[Select Project](command:tangle.selectProject)\n[Set Tangle API URL](command:tangle.setApiUrl)", + "when": "!tangle.hasProject" + } + ], + "menus": { + "view/title": [ + { + "command": "tangle.newSession", + "when": "view == tangle.sessions", + "group": "navigation@1" + }, + { + "command": "tangle.selectProject", + "when": "view =~ /^tangle\\.(runs|resources|sessions)$/", + "group": "navigation@2" + }, + { + "command": "tangle.refresh", + "when": "view =~ /^tangle\\.(runs|resources|sessions)$/", + "group": "navigation@3" + }, + { + "command": "tangle.openProject", + "when": "view =~ /^tangle\\.(runs|resources|sessions)$/ && tangle.hasProject", + "group": "1_project" + }, + { + "command": "tangle.setApiUrl", + "when": "view =~ /^tangle\\.(runs|resources|sessions)$/", + "group": "2_settings" + } + ], + "view/item/context": [ + { + "command": "tangle.openRun", + "when": "view == tangle.runs && viewItem == tangleRun", + "group": "inline" + }, + { + "command": "tangle.openResource", + "when": "view == tangle.resources && viewItem == tangleResource", + "group": "inline" + }, + { + "command": "tangle.openSession", + "when": "view == tangle.sessions && viewItem == tangleSession", + "group": "inline" + }, + { + "command": "tangle.renameSession", + "when": "view == tangle.sessions && viewItem == tangleSession", + "group": "1_edit" + }, + { + "command": "tangle.detachSession", + "when": "view == tangle.sessions && viewItem == tangleSession", + "group": "2_remove" + } + ], + "commandPalette": [ + { + "command": "tangle.openRun", + "when": "false" + }, + { + "command": "tangle.openResource", + "when": "false" + }, + { + "command": "tangle.openSession", + "when": "false" + }, + { + "command": "tangle.renameSession", + "when": "false" + }, + { + "command": "tangle.detachSession", + "when": "false" + }, + { + "command": "tangle.loadMoreRuns", + "when": "false" + } + ] + }, + "commands": [ + { + "command": "tangle.selectProject", + "title": "Select Project", + "category": "Tangle", + "icon": "$(folder-library)" + }, + { + "command": "tangle.refresh", + "title": "Refresh", + "category": "Tangle", + "icon": "$(refresh)" + }, + { + "command": "tangle.setApiUrl", + "title": "Set Tangle API URL", + "category": "Tangle" + }, + { + "command": "tangle.openProject", + "title": "Open Project in Tangle", + "category": "Tangle", + "icon": "$(link-external)" + }, + { + "command": "tangle.openRun", + "title": "Open Run in Tangle", + "category": "Tangle", + "icon": "$(link-external)" + }, + { + "command": "tangle.openResource", + "title": "Open Resource in Tangle", + "category": "Tangle", + "icon": "$(link-external)" + }, + { + "command": "tangle.loadMoreRuns", + "title": "Load More Runs", + "category": "Tangle" + }, + { + "command": "tangle.newSession", + "title": "New Session", + "category": "Tangle", + "icon": "$(add)" + }, + { + "command": "tangle.openSession", + "title": "Open Session Chat", + "category": "Tangle", + "icon": "$(comment-discussion)" + }, + { + "command": "tangle.renameSession", + "title": "Rename Session", + "category": "Tangle" + }, + { + "command": "tangle.detachSession", + "title": "Remove Session from Project", + "category": "Tangle" + }, + { + "command": "tangent.connect", + "title": "Connect Local Agent", + "category": "Tangent" + }, + { + "command": "tangent.disconnect", + "title": "Disconnect Local Agent", + "category": "Tangent" + }, + { + "command": "tangent.attachSession", + "title": "Attach to Session", + "category": "Tangent" + }, + { + "command": "tangent.setPiProxyApiKey", + "title": "Set Pi Proxy API Key", + "category": "Tangent" + }, + { + "command": "tangent.showMenu", + "title": "Show Menu", + "category": "Tangent" + }, + { + "command": "tangent.checkForUpdates", + "title": "Check for Updates", + "category": "Tangent" + } + ], + "configuration": { + "title": "Tangent Local Agent", + "properties": { + "tangent.shellUrl": { + "type": "string", + "default": "", + "description": "Base URL of the Tangent Shell, e.g. http://localhost:5173." + }, + "tangent.autoConnect": { + "type": "boolean", + "default": true, + "description": "Reconnect to the saved Shell URL when VS Code starts." + }, + "tangent.defaultBundleId": { + "type": "string", + "default": "", + "description": "Agent bundle used by \"New Session\". Leave empty to pick from a list." + }, + "tangent.userEmail": { + "type": "string", + "default": "", + "description": "Email this window mints scoped remote-env tokens as. Must match the identity you chat as, so Tangent routes a session's tools and sub-agents to this window. Leave empty for the local dev identity." + }, + "tangent.piBin": { + "type": "string", + "default": "pi", + "description": "Path to the pi executable." + }, + "tangent.piModel": { + "type": "string", + "default": "", + "description": "Model (provider/id) for local sub-agents. Leave empty to use the model Prime asked for." + }, + "tangent.piProxyUrl": { + "type": "string", + "default": "", + "description": "LLM proxy base URL for local pi. Leave empty to use your own pi configuration." + }, + "tangent.tangleApiUrl": { + "type": "string", + "default": "http://localhost:8000", + "description": "Base URL of the Tangle backend the Runs, Resources, and Sessions views read from." + }, + "tangent.tangleUiUrl": { + "type": "string", + "default": "http://localhost:3000", + "description": "Base URL of the Tangle web app, used to open projects and runs in the browser." + }, + "tangent.updateBaseUrl": { + "type": "string", + "default": "", + "description": "Base URL this extension checks for new .vsix builds (reads /extension/latest.json). Leave empty to use tangent.tangleUiUrl." + }, + "tangent.autoCheckUpdates": { + "type": "boolean", + "default": true, + "description": "Check for a newer extension build on startup and offer to update." + } + } + } + }, + "scripts": { + "build": "node esbuild.mjs", + "watch": "node esbuild.mjs --watch", + "lint": "eslint .", + "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.webview.json", + "test": "tsx --test \"src/**/*.test.ts\"", + "format": "prettier --write .", + "package": "node esbuild.mjs && vsce package --no-dependencies" + }, + "dependencies": { + "@tangent/embed-react": "github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/embed-react", + "@tangent/local-agent-host": "github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/local-agent-host", + "@tangent/remote-subagent": "github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/remote-subagent", + "@tangent/shared": "github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/shared", + "react": "^19.3.1", + "react-dom": "^19.3.1", + "socket.io-client": "^4.8.3", + "zod": "^4.6.7" + }, + "devDependencies": { + "@eslint/js": "^9.39.2", + "@types/node": "^26.1.1", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", + "@types/vscode": "~1.90.0", + "@vscode/vsce": "^4.0.0", + "esbuild": "^0.28.2", + "eslint": "^9.39.2", + "eslint-plugin-simple-import-sort": "^14.0.0", + "globals": "^17.7.0", + "prettier": "^3.9.6", + "tsx": "^4.23.1", + "typescript": "^5.9.3", + "typescript-eslint": "^8.65.0" + }, + "pnpm": { + "overrides": { + "@tangent/local-agent-host": "github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/local-agent-host", + "@tangent/remote-subagent": "github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/remote-subagent", + "@tangent/shared": "github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/shared" + } + } +} diff --git a/vscode-extension/pnpm-lock.yaml b/vscode-extension/pnpm-lock.yaml new file mode 100644 index 0000000000..cf86fcc957 --- /dev/null +++ b/vscode-extension/pnpm-lock.yaml @@ -0,0 +1,2558 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +overrides: + '@tangent/local-agent-host': github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/local-agent-host + '@tangent/remote-subagent': github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/remote-subagent + '@tangent/shared': github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/shared + +importers: + + .: + dependencies: + '@tangent/embed-react': + specifier: github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/embed-react + version: https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/embed-react(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + '@tangent/local-agent-host': + specifier: github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/local-agent-host + version: https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/local-agent-host + '@tangent/remote-subagent': + specifier: github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/remote-subagent + version: https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/remote-subagent + '@tangent/shared': + specifier: github:TangleML/tangent-shell#b0c6ac565872440b779f12e7787f8065d9af867a&path:packages/shared + version: https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/shared + react: + specifier: ^19.3.1 + version: 19.3.1 + react-dom: + specifier: ^19.3.1 + version: 19.3.1(react@19.3.1) + socket.io-client: + specifier: ^4.8.3 + version: 4.8.4 + zod: + specifier: ^4.6.7 + version: 4.6.7 + devDependencies: + '@eslint/js': + specifier: ^9.39.2 + version: 9.39.5 + '@types/node': + specifier: ^26.1.1 + version: 26.6.5 + '@types/react': + specifier: ^19.3.0 + version: 19.3.0 + '@types/react-dom': + specifier: ^19.3.0 + version: 19.3.0(@types/react@19.3.0) + '@types/vscode': + specifier: ~1.90.0 + version: 1.90.0 + '@vscode/vsce': + specifier: ^4.0.0 + version: 4.0.0 + esbuild: + specifier: ^0.28.2 + version: 0.28.2 + eslint: + specifier: ^9.39.2 + version: 9.39.5 + eslint-plugin-simple-import-sort: + specifier: ^14.0.0 + version: 14.0.0(eslint@9.39.5) + globals: + specifier: ^17.7.0 + version: 17.13.0 + prettier: + specifier: ^3.9.6 + version: 3.9.10 + tsx: + specifier: ^4.23.1 + version: 4.23.15 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + typescript-eslint: + specifier: ^8.65.0 + version: 8.71.2(eslint@9.39.5)(typescript@5.9.3) + +packages: + + '@azure/abort-controller@2.2.0': + resolution: {integrity: sha512-fNAjWnA/nZ2jz31kxR/AqRaUT8ewHBw/WuBIosK0moMy1C9e5ValbDfFdIxJzVOOYaYkV/b2F1S4H/aHiqfVQg==, tarball: https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.2.0.tgz} + engines: {node: '>=22.0.0'} + + '@azure/core-auth@1.11.0': + resolution: {integrity: sha512-IUZydyTUkDnYdstOW9pFOOUQlBjAepK5teihDE3x6yxsPJs/hsAaaYpeGxdxrgtOiJbBKSjKW7MDk7AEhb4LRg==, tarball: https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.11.0.tgz} + engines: {node: '>=22.0.0'} + + '@azure/core-client@1.11.1': + resolution: {integrity: sha512-2QygG2F76ZpMP2eMztiJvAiFMu71M9rDeU7vO/QKg5Css7MgM4frUOslFjhVjRhbGaCNPtz/S8M6y46/fFKVuQ==, tarball: https://registry.npmjs.org/@azure/core-client/-/core-client-1.11.1.tgz} + engines: {node: '>=22.0.0'} + + '@azure/core-process@1.0.0': + resolution: {integrity: sha512-/shnJ+ooO8WPxDhPEeI/2oRQuubn16gZ6CvlbpWbEswZfzwI9tI/sMAHmF3x1LuQ9yZYXfLW3TjzGMLEC5blKg==, tarball: https://registry.npmjs.org/@azure/core-process/-/core-process-1.0.0.tgz} + engines: {node: '>=22.0.0'} + + '@azure/core-rest-pipeline@1.25.0': + resolution: {integrity: sha512-bMs8ekJLjX8wPV+9IPBges1SLPyuDtE9g5gLDWOpxzKcoOFQnpLGkbcT1tdw3FaAmDS1gnPmMmJ6y/T5B96kIA==, tarball: https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.25.0.tgz} + engines: {node: '>=22.0.0'} + + '@azure/core-tracing@1.4.0': + resolution: {integrity: sha512-eGwxD0AtncrxeBM4tG8R55Pc3rdX1hNW2WibJAgYpCVA6E93mvvVH+LcssoVjOBrSKWS55yEIHsk0X8ctHmfOQ==, tarball: https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.4.0.tgz} + engines: {node: '>=22.0.0'} + + '@azure/core-util@1.14.0': + resolution: {integrity: sha512-9n2pWK61veAuN0V20t9lOuoV4CFMdyAZ1ygZzvBGk/pBBJRib/PjL9PLXa/aI2CcPpyHfqVsxxqLCYl6uZlfDw==, tarball: https://registry.npmjs.org/@azure/core-util/-/core-util-1.14.0.tgz} + engines: {node: '>=22.0.0'} + + '@azure/identity@4.14.0': + resolution: {integrity: sha512-8utCLdTZn14CiAe0HW4JbtzUMsc0mdDwnjUvStFMrR4IxQP8S2eL9zVAdplpfORVJGYmidZElpeXRYjJQ4UyUw==, tarball: https://registry.npmjs.org/@azure/identity/-/identity-4.14.0.tgz} + engines: {node: '>=22.0.0'} + + '@azure/logger@1.4.0': + resolution: {integrity: sha512-rbAE25KUfjU/s3XHUdJgceoCP5dEOpMx85J04kF+QMdta73XkuG9JGHHinch+XIoKpBdqljin+KqURpJriSzLA==, tarball: https://registry.npmjs.org/@azure/logger/-/logger-1.4.0.tgz} + engines: {node: '>=22.0.0'} + + '@azure/msal-browser@5.26.0': + resolution: {integrity: sha512-GYKRquzDZmZggCdStWy2bNJKnPui1upssSuznJgP+/h6Mb0ldkUkaGvaY6uSGGmZOm0CGiBQ3b5BiGy43BpXSg==, tarball: https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-5.26.0.tgz} + engines: {node: '>=0.8.0'} + + '@azure/msal-common@16.14.1': + resolution: {integrity: sha512-Or6xhPNyi4zHW25158yxBoyxuCqNSPa5YBVqfF1J5Ks4MJWBo/USXdp05DQIPu1Zli00YZu6t0+h6KvHJealxQ==, tarball: https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.14.1.tgz} + engines: {node: '>=0.8.0'} + + '@azure/msal-common@16.14.3': + resolution: {integrity: sha512-tz3GvMrR9V/plb2lDdW8qaO8CPpfZevdExBKjltzzbAtyhK0eB2UIvji1aU+gkyHj3fxQ2ie+LNHyMXdmo8wOg==, tarball: https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.14.3.tgz} + engines: {node: '>=0.8.0'} + + '@azure/msal-node@6.0.1': + resolution: {integrity: sha512-ixSO1Y/kCVRthRs+hSx/5qkwaunX1/RAePhlMN0wIpIQ4WEZ6AREGGnGd1AP0qspHVsAwzWQKGubpjh50JyJIQ==, tarball: https://registry.npmjs.org/@azure/msal-node/-/msal-node-6.0.1.tgz} + engines: {node: '>=20'} + + '@esbuild/aix-ppc64@0.28.2': + resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==, tarball: https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.28.2': + resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==, tarball: https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.28.2': + resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==, tarball: https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.28.2': + resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==, tarball: https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.28.2': + resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==, tarball: https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.2': + resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==, tarball: https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.28.2': + resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==, tarball: https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.2': + resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==, tarball: https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.28.2': + resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==, tarball: https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.28.2': + resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==, tarball: https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.28.2': + resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==, tarball: https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.28.2': + resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==, tarball: https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.28.2': + resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==, tarball: https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.28.2': + resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==, tarball: https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.2': + resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==, tarball: https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.28.2': + resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==, tarball: https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.28.2': + resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==, tarball: https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.28.2': + resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==, tarball: https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.2': + resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==, tarball: https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.28.2': + resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==, tarball: https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.2': + resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==, tarball: https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.28.2': + resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==, tarball: https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.28.2': + resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==, tarball: https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.28.2': + resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==, tarball: https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.28.2': + resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==, tarball: https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.28.2': + resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==, tarball: https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.10.1': + resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==, tarball: https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==, tarball: https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==, tarball: https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==, tarball: https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==, tarball: https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.7': + resolution: {integrity: sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw==, tarball: https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.7.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.5': + resolution: {integrity: sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==, tarball: https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==, tarball: https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==, tarball: https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==, tarball: https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==, tarball: https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==, tarball: https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==, tarball: https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==, tarball: https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz} + engines: {node: '>=18.18'} + + '@napi-rs/keyring-darwin-arm64@1.3.0': + resolution: {integrity: sha512-pl76hJvdYUBn6I24bXiOBMA9nbDapo3I5B+f3OorjDU4dUMSypXeKbOVehJe8fhgTiH24flMyTS3aAIy43xegQ==, tarball: https://registry.npmjs.org/@napi-rs/keyring-darwin-arm64/-/keyring-darwin-arm64-1.3.0.tgz} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@napi-rs/keyring-darwin-x64@1.3.0': + resolution: {integrity: sha512-YcJtEV5LA3cvA4z3BurgxH5IhTsW1JfIvcAAcqcecwk06Si9F9NqkxbZVIfDwQ8oRHgaBmT3zZJnLAotCrVahw==, tarball: https://registry.npmjs.org/@napi-rs/keyring-darwin-x64/-/keyring-darwin-x64-1.3.0.tgz} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@napi-rs/keyring-freebsd-x64@1.3.0': + resolution: {integrity: sha512-vlLf31TGhfRAaxLDBhg8b89ss0HHD/lyNmL5F3UjSaz5CUXElsJmKYq9fqA/B+cZKUEUcLHHGhF0I/CqcFdaVw==, tarball: https://registry.npmjs.org/@napi-rs/keyring-freebsd-x64/-/keyring-freebsd-x64-1.3.0.tgz} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@napi-rs/keyring-linux-arm-gnueabihf@1.3.0': + resolution: {integrity: sha512-KiWdMMu/Inz/bHHIAGrnF7r54FZDYXuHO6UFF/rhIrshUsxbMG1Rl9lEymNtqqsVo927G0VYcb02FzWQ3iBQRQ==, tarball: https://registry.npmjs.org/@napi-rs/keyring-linux-arm-gnueabihf/-/keyring-linux-arm-gnueabihf-1.3.0.tgz} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@napi-rs/keyring-linux-arm64-gnu@1.3.0': + resolution: {integrity: sha512-eyKGpY40lm9Jvs1aD294XRH4y7+TlJM0YVAryZeXA6TX0mb4gMkxVXwSQv7MCwgah7raeUd0dKUb4BPAYIgcMg==, tarball: https://registry.npmjs.org/@napi-rs/keyring-linux-arm64-gnu/-/keyring-linux-arm64-gnu-1.3.0.tgz} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@napi-rs/keyring-linux-arm64-musl@1.3.0': + resolution: {integrity: sha512-iIK6JWHXAJqDrEyLY3TmswwloVyt2vj+04TZnew+uSJ9gnDO8EwRbp3/iw3LpWaXiDO7VomGO6y8I0Id8uBZSw==, tarball: https://registry.npmjs.org/@napi-rs/keyring-linux-arm64-musl/-/keyring-linux-arm64-musl-1.3.0.tgz} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@napi-rs/keyring-linux-riscv64-gnu@1.3.0': + resolution: {integrity: sha512-/PGqrwn6EwgtK6vccASSXJRfOSP4vN1F4ASsIQ+7MdrK6hNvAJ1FZPrIuD5gGGdxezo3F++To2Wq7DbuGIeuNQ==, tarball: https://registry.npmjs.org/@napi-rs/keyring-linux-riscv64-gnu/-/keyring-linux-riscv64-gnu-1.3.0.tgz} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@napi-rs/keyring-linux-x64-gnu@1.3.0': + resolution: {integrity: sha512-2PDK1WKWTu9lBGq9VvNEkSlQD3O7YwVpmnyN2M3cy4v7NJ/8gDMd9GXv3G+FVXN13uhp4gnnPBS+ScefmEeD2A==, tarball: https://registry.npmjs.org/@napi-rs/keyring-linux-x64-gnu/-/keyring-linux-x64-gnu-1.3.0.tgz} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@napi-rs/keyring-linux-x64-musl@1.3.0': + resolution: {integrity: sha512-oJ2HkX8YUo46QBkn0pG+HuIKQNqr523q6vBobCn+P95s4C4K6/kLBqHY/1bg5J4ap31DzsznhnFKcfBNBsjCnw==, tarball: https://registry.npmjs.org/@napi-rs/keyring-linux-x64-musl/-/keyring-linux-x64-musl-1.3.0.tgz} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@napi-rs/keyring-win32-arm64-msvc@1.3.0': + resolution: {integrity: sha512-tOd3c/uAaeoE4ycVlmAdSvygz0Zt3zdca6Y7gokBeIbaRDWpjDIUOpU3MvML59XAaqyuKGsVVu0F/DZb1lHPmw==, tarball: https://registry.npmjs.org/@napi-rs/keyring-win32-arm64-msvc/-/keyring-win32-arm64-msvc-1.3.0.tgz} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@napi-rs/keyring-win32-ia32-msvc@1.3.0': + resolution: {integrity: sha512-sPSqeAFZMGqP1R++M2JTza7GQJJ/TpCo6JU6Vcd4jnebvOaEDs9b7eipakU1PJdSvhpC2yXMCNRk9gXfrhuwHQ==, tarball: https://registry.npmjs.org/@napi-rs/keyring-win32-ia32-msvc/-/keyring-win32-ia32-msvc-1.3.0.tgz} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@napi-rs/keyring-win32-x64-msvc@1.3.0': + resolution: {integrity: sha512-4DnCWXwDc0HRKwyRlG5y0VhKZW2tNRQfKKfyj6IX/KWfDNyq9hn4n+GL1auyDcOO/v8PwnhmYo2+rOOqCkvvOg==, tarball: https://registry.npmjs.org/@napi-rs/keyring-win32-x64-msvc/-/keyring-win32-x64-msvc-1.3.0.tgz} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@napi-rs/keyring@1.3.0': + resolution: {integrity: sha512-WrOw/bcXm0f9qHkumlT1QlArXSTWqaY9sunsDpOk+yCCorCKMxvWT/a3xko4EYHVdeZoh00yI2TydXn6eyICDA==, tarball: https://registry.npmjs.org/@napi-rs/keyring/-/keyring-1.3.0.tgz} + engines: {node: '>= 10'} + + '@secretlint/core@10.2.2': + resolution: {integrity: sha512-6rdwBwLP9+TO3rRjMVW1tX+lQeo5gBbxl1I5F8nh8bgGtKwdlCMhMKsBWzWg1ostxx/tIG7OjZI0/BxsP8bUgw==, tarball: https://registry.npmjs.org/@secretlint/core/-/core-10.2.2.tgz} + engines: {node: '>=20.0.0'} + + '@secretlint/profiler@10.2.2': + resolution: {integrity: sha512-qm9rWfkh/o8OvzMIfY8a5bCmgIniSpltbVlUVl983zDG1bUuQNd1/5lUEeWx5o/WJ99bXxS7yNI4/KIXfHexig==, tarball: https://registry.npmjs.org/@secretlint/profiler/-/profiler-10.2.2.tgz} + + '@secretlint/secretlint-rule-no-dotenv@10.2.2': + resolution: {integrity: sha512-KJRbIShA9DVc5Va3yArtJ6QDzGjg3PRa1uYp9As4RsyKtKSSZjI64jVca57FZ8gbuk4em0/0Jq+uy6485wxIdg==, tarball: https://registry.npmjs.org/@secretlint/secretlint-rule-no-dotenv/-/secretlint-rule-no-dotenv-10.2.2.tgz} + engines: {node: '>=20.0.0'} + + '@secretlint/secretlint-rule-preset-recommend@10.2.2': + resolution: {integrity: sha512-K3jPqjva8bQndDKJqctnGfwuAxU2n9XNCPtbXVI5JvC7FnQiNg/yWlQPbMUlBXtBoBGFYp08A94m6fvtc9v+zA==, tarball: https://registry.npmjs.org/@secretlint/secretlint-rule-preset-recommend/-/secretlint-rule-preset-recommend-10.2.2.tgz} + engines: {node: '>=20.0.0'} + + '@secretlint/source-creator@10.2.2': + resolution: {integrity: sha512-h6I87xJfwfUTgQ7irWq7UTdq/Bm1RuQ/fYhA3dtTIAop5BwSFmZyrchph4WcoEvbN460BWKmk4RYSvPElIIvxw==, tarball: https://registry.npmjs.org/@secretlint/source-creator/-/source-creator-10.2.2.tgz} + engines: {node: '>=20.0.0'} + + '@secretlint/types@10.2.2': + resolution: {integrity: sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==, tarball: https://registry.npmjs.org/@secretlint/types/-/types-10.2.2.tgz} + engines: {node: '>=20.0.0'} + + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==, tarball: https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz} + + '@tangent/embed-react@https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/embed-react': + resolution: {path: packages/embed-react, tarball: https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a} + version: 0.1.1 + peerDependencies: + react: ^19.0.0 + react-dom: ^19.0.0 + + '@tangent/local-agent-host@https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/local-agent-host': + resolution: {path: packages/local-agent-host, tarball: https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a} + version: 0.0.0 + + '@tangent/remote-subagent@https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/remote-subagent': + resolution: {path: packages/remote-subagent, tarball: https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a} + version: 0.0.0 + + '@tangent/shared@https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/shared': + resolution: {path: packages/shared, tarball: https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a} + version: 0.0.0 + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==, tarball: https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==, tarball: https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz} + + '@types/node@26.6.5': + resolution: {integrity: sha512-uMzsv9stDdofIZBx4UGh4Z9ZgXnGC9seRDscilBcTHAeOFk67MZg/k9bWVfO+bk4qZmG+l52hRnLwUiH2LnJTg==, tarball: https://registry.npmjs.org/@types/node/-/node-26.6.5.tgz} + + '@types/react-dom@19.3.0': + resolution: {integrity: sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==, tarball: https://registry.npmjs.org/@types/react-dom/-/react-dom-19.3.0.tgz} + peerDependencies: + '@types/react': ^19.3.0 + + '@types/react@19.3.0': + resolution: {integrity: sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==, tarball: https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz} + + '@types/vscode@1.90.0': + resolution: {integrity: sha512-oT+ZJL7qHS9Z8bs0+WKf/kQ27qWYR3trsXpq46YDjFqBsMLG4ygGGjPaJ2tyrH0wJzjOEmDyg9PDJBBhWg9pkQ==, tarball: https://registry.npmjs.org/@types/vscode/-/vscode-1.90.0.tgz} + + '@typescript-eslint/eslint-plugin@8.71.2': + resolution: {integrity: sha512-K72/pvT7qpm8q3k8G5u93NCSN1RcLxX5h7Gw0bQpbMXviBHeY6Nx1WEwmk9wP+eKTSQ8ecxvcgt/ZX/rB938xQ==, tarball: https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.71.2.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.71.2 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.71.2': + resolution: {integrity: sha512-aTtmZjp/x3gsGHkDs04/1I8kNogBJkpQaeGaN0PfiG43NzDEmW2n+OP5im9wfsvyX0yP1UZWBrAGtDBfH9jEVw==, tarball: https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.71.2.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.71.2': + resolution: {integrity: sha512-Pd68GsLVh5ZXlcEccFPdhSOvtHZ/2WU0XsA8zmXOS3F158lVlcTLhfQ6qvtL1RzbMie0Kp8xtbVwAg4nejKPHw==, tarball: https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.71.2.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.71.2': + resolution: {integrity: sha512-YF4wgg5iUX5Obywoac27B56FhozMNFhdJSbh11H5GBAwmBtfyRhmgTQqW9tRNNmHDvsZ2CX6UGzoz+YfYn2Leg==, tarball: https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.71.2.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.71.2': + resolution: {integrity: sha512-jz49TAWokm/7yWD9/o837nYax9DyALxIPOImdImvVzo6canBCZYzin8zJyqNR5k1j9K1DE0bVvr0XYB+99uAdg==, tarball: https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.71.2.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.71.2': + resolution: {integrity: sha512-cC8UaalCViJkb3rH/xLIvidEwoFRtMZMUqDjjJMGEKaTFwYRHYBcoczlEGHuvkHEO8svRDrgZ0yQgOArFmwang==, tarball: https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.71.2.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.71.2': + resolution: {integrity: sha512-y5Ukl3oVp7e/VGxqIoEXoeb/Roj71PAJ4WG/N/RGj+eeDvKRLM15kGr9MijLbN0i03S52zxu31Z98E5gWChJKw==, tarball: https://registry.npmjs.org/@typescript-eslint/types/-/types-8.71.2.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.71.2': + resolution: {integrity: sha512-DgNOEsl4pdYzwSL+N/Cl33tX1Hlw3FvId73LnKXfq7jnrF2viOEn7X+P7RBkkSWCVm92gnWd28GIC5vJuLREeA==, tarball: https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.71.2.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.71.2': + resolution: {integrity: sha512-RyKa1G1M9rKHDhrk+HJtiF1MvLD5Ux2JW3TSkzw+02pRALdpJb+QzG1Y5em+LvCgzGwgg8H7mBRFKljNz7ImPw==, tarball: https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.71.2.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.71.2': + resolution: {integrity: sha512-zozlN0UA32bSG2zkKAgQyRYSbEISVoyU6vvkKYo2aSny74D8Jh4e+POkJ6Lk0dlezZi5x7TtD+88Xb06IB8nhg==, tarball: https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.71.2.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typespec/ts-http-runtime@0.3.9': + resolution: {integrity: sha512-edSdeAqkdxBVzA1yL1LrLCml1YjyCVvPMtMqJpbF+6K609tHe8V6sQUzFQSGcYNhcuhOceZtjvN32+mpIth30A==, tarball: https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.9.tgz} + engines: {node: '>=22.0.0'} + + '@vscode/vsce-sign-alpine-arm64@2.0.6': + resolution: {integrity: sha512-wKkJBsvKF+f0GfsUuGT0tSW0kZL87QggEiqNqK6/8hvqsXvpx8OsTEc3mnE1kejkh5r+qUyQ7PtF8jZYN0mo8Q==, tarball: https://registry.npmjs.org/@vscode/vsce-sign-alpine-arm64/-/vsce-sign-alpine-arm64-2.0.6.tgz} + cpu: [arm64] + os: [alpine] + + '@vscode/vsce-sign-alpine-x64@2.0.6': + resolution: {integrity: sha512-YoAGlmdK39vKi9jA18i4ufBbd95OqGJxRvF3n6ZbCyziwy3O+JgOpIUPxv5tjeO6gQfx29qBivQ8ZZTUF2Ba0w==, tarball: https://registry.npmjs.org/@vscode/vsce-sign-alpine-x64/-/vsce-sign-alpine-x64-2.0.6.tgz} + cpu: [x64] + os: [alpine] + + '@vscode/vsce-sign-darwin-arm64@2.0.6': + resolution: {integrity: sha512-5HMHaJRIQuozm/XQIiJiA0W9uhdblwwl2ZNDSSAeXGO9YhB9MH5C4KIHOmvyjUnKy4UCuiP43VKpIxW1VWP4tQ==, tarball: https://registry.npmjs.org/@vscode/vsce-sign-darwin-arm64/-/vsce-sign-darwin-arm64-2.0.6.tgz} + cpu: [arm64] + os: [darwin] + + '@vscode/vsce-sign-darwin-x64@2.0.6': + resolution: {integrity: sha512-25GsUbTAiNfHSuRItoQafXOIpxlYj+IXb4/qarrXu7kmbH94jlm5sdWSCKrrREs8+GsXF1b+l3OB7VJy5jsykw==, tarball: https://registry.npmjs.org/@vscode/vsce-sign-darwin-x64/-/vsce-sign-darwin-x64-2.0.6.tgz} + cpu: [x64] + os: [darwin] + + '@vscode/vsce-sign-linux-arm64@2.0.6': + resolution: {integrity: sha512-cfb1qK7lygtMa4NUl2582nP7aliLYuDEVpAbXJMkDq1qE+olIw/es+C8j1LJwvcRq1I2yWGtSn3EkDp9Dq5FdA==, tarball: https://registry.npmjs.org/@vscode/vsce-sign-linux-arm64/-/vsce-sign-linux-arm64-2.0.6.tgz} + cpu: [arm64] + os: [linux] + + '@vscode/vsce-sign-linux-arm@2.0.6': + resolution: {integrity: sha512-UndEc2Xlq4HsuMPnwu7420uqceXjs4yb5W8E2/UkaHBB9OWCwMd3/bRe/1eLe3D8kPpxzcaeTyXiK3RdzS/1CA==, tarball: https://registry.npmjs.org/@vscode/vsce-sign-linux-arm/-/vsce-sign-linux-arm-2.0.6.tgz} + cpu: [arm] + os: [linux] + + '@vscode/vsce-sign-linux-x64@2.0.6': + resolution: {integrity: sha512-/olerl1A4sOqdP+hjvJ1sbQjKN07Y3DVnxO4gnbn/ahtQvFrdhUi0G1VsZXDNjfqmXw57DmPi5ASnj/8PGZhAA==, tarball: https://registry.npmjs.org/@vscode/vsce-sign-linux-x64/-/vsce-sign-linux-x64-2.0.6.tgz} + cpu: [x64] + os: [linux] + + '@vscode/vsce-sign-win32-arm64@2.0.6': + resolution: {integrity: sha512-ivM/MiGIY0PJNZBoGtlRBM/xDpwbdlCWomUWuLmIxbi1Cxe/1nooYrEQoaHD8ojVRgzdQEUzMsRbyF5cJJgYOg==, tarball: https://registry.npmjs.org/@vscode/vsce-sign-win32-arm64/-/vsce-sign-win32-arm64-2.0.6.tgz} + cpu: [arm64] + os: [win32] + + '@vscode/vsce-sign-win32-x64@2.0.6': + resolution: {integrity: sha512-mgth9Kvze+u8CruYMmhHw6Zgy3GRX2S+Ed5oSokDEK5vPEwGGKnmuXua9tmFhomeAnhgJnL4DCna3TiNuGrBTQ==, tarball: https://registry.npmjs.org/@vscode/vsce-sign-win32-x64/-/vsce-sign-win32-x64-2.0.6.tgz} + cpu: [x64] + os: [win32] + + '@vscode/vsce-sign@2.1.0': + resolution: {integrity: sha512-9AQrqazrBgTgRSuwleLVXUrIUphY02/SFCh2TKYoLV/xifJAdblhdmEmw5gUrYSPQ3sRwNs9iyCMD14sATEE6g==, tarball: https://registry.npmjs.org/@vscode/vsce-sign/-/vsce-sign-2.1.0.tgz} + + '@vscode/vsce@4.0.0': + resolution: {integrity: sha512-NImwuLaenMmb5D5Jer9/lzi/F9ZQUBOp8Azhj/BVYcTFgixv8KehFXqEUDjQlD2tAiw2E6dDGyjTuAB//di60A==, tarball: https://registry.npmjs.org/@vscode/vsce/-/vsce-4.0.0.tgz} + engines: {node: '>= 22'} + hasBin: true + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, tarball: https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.19.0: + resolution: {integrity: sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==, tarball: https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==, tarball: https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz} + engines: {node: '>= 14'} + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==, tarball: https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==, tarball: https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==, tarball: https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz} + + azure-devops-node-api@12.5.0: + resolution: {integrity: sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==, tarball: https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-12.5.0.tgz} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, tarball: https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==, tarball: https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz} + engines: {node: 18 || 20 || >=22} + + binaryextensions@6.11.0: + resolution: {integrity: sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw==, tarball: https://registry.npmjs.org/binaryextensions/-/binaryextensions-6.11.0.tgz} + engines: {node: '>=4'} + + boundary@2.0.0: + resolution: {integrity: sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA==, tarball: https://registry.npmjs.org/boundary/-/boundary-2.0.0.tgz} + + brace-expansion@1.1.21: + resolution: {integrity: sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==, tarball: https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz} + + brace-expansion@5.0.12: + resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==, tarball: https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz} + engines: {node: 20 || >=22} + + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==, tarball: https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz} + + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==, tarball: https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz} + + bundle-name@4.1.1: + resolution: {integrity: sha512-DdH81/zPLVS11EUgWq3tEu/xn+EzljlMYooDNdzWEnFha3R3NBMpMV1UqYIpjYHV/SgpFKMIX1Oh7o06SjM/oA==, tarball: https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.1.tgz} + engines: {node: '>=18'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==, tarball: https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==, tarball: https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==, tarball: https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz} + engines: {node: '>=6'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==, tarball: https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz} + engines: {node: '>=10'} + + cockatiel@3.2.1: + resolution: {integrity: sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==, tarball: https://registry.npmjs.org/cockatiel/-/cockatiel-3.2.1.tgz} + engines: {node: '>=16'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==, tarball: https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, tarball: https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==, tarball: https://registry.npmjs.org/commander/-/commander-12.1.0.tgz} + engines: {node: '>=18'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==, tarball: https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==, tarball: https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz} + engines: {node: '>= 8'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==, tarball: https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==, tarball: https://registry.npmjs.org/debug/-/debug-4.4.3.tgz} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==, tarball: https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz} + + default-browser-id@5.0.1: + resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==, tarball: https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz} + engines: {node: '>=18'} + + default-browser@5.5.1: + resolution: {integrity: sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==, tarball: https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz} + engines: {node: '>=18'} + + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==, tarball: https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==, tarball: https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz} + engines: {node: '>= 0.4'} + + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==, tarball: https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz} + + editions@6.22.0: + resolution: {integrity: sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ==, tarball: https://registry.npmjs.org/editions/-/editions-6.22.0.tgz} + engines: {ecmascript: '>= es5', node: '>=4'} + + engine.io-client@6.6.7: + resolution: {integrity: sha512-GyAvmPA7gnRCpDQbYACAy79Lg4ewevrmaPzySZz0OVvgyZc3gEfLLqPTYk8wVBScjOEpM9ZO4KryyFpVItjGEA==, tarball: https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.7.tgz} + + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==, tarball: https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz} + engines: {node: '>=10.0.0'} + + entities@8.1.0: + resolution: {integrity: sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==, tarball: https://registry.npmjs.org/entities/-/entities-8.1.0.tgz} + engines: {node: '>=20.19.0'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==, tarball: https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==, tarball: https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==, tarball: https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz} + engines: {node: '>= 0.4'} + + esbuild@0.28.2: + resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==, tarball: https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz} + engines: {node: '>=18'} + hasBin: true + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==, tarball: https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz} + engines: {node: '>=10'} + + eslint-plugin-simple-import-sort@14.0.0: + resolution: {integrity: sha512-NUJO0+XFCkk+o5EsAJruTgnfMEpeWrPWeJS15UVF60GgXmqz1BJ9/3hzlvG7lkL8Bubzos5cCLptThbFfPnSMQ==, tarball: https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-14.0.0.tgz} + peerDependencies: + eslint: '>=5.0.0' + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==, tarball: https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==, tarball: https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==, tarball: https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==, tarball: https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.5: + resolution: {integrity: sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==, tarball: https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==, tarball: https://registry.npmjs.org/espree/-/espree-10.4.0.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==, tarball: https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==, tarball: https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==, tarball: https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==, tarball: https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, tarball: https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==, tarball: https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==, tarball: https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==, tarball: https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==, tarball: https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz} + engines: {node: '>=16.0.0'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==, tarball: https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==, tarball: https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz} + engines: {node: '>=16'} + + flatted@3.4.4: + resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==, tarball: https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, tarball: https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==, tarball: https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==, tarball: https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==, tarball: https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz} + engines: {node: '>= 0.4'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==, tarball: https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==, tarball: https://registry.npmjs.org/globals/-/globals-14.0.0.tgz} + engines: {node: '>=18'} + + globals@17.13.0: + resolution: {integrity: sha512-RwMTC61u7hrG3ZJMkZQOK4JLJrKS8QtoTii63EmWvFXHqycY9FObBJQCbsLxSO8kjKhWE5kV1GC+Vb1g3RI0Zg==, tarball: https://registry.npmjs.org/globals/-/globals-17.13.0.tgz} + engines: {node: '>=18'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==, tarball: https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==, tarball: https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==, tarball: https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz} + engines: {node: '>=8'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==, tarball: https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz} + engines: {node: '>= 0.4'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==, tarball: https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz} + engines: {node: '>= 0.4'} + + hosted-git-info@4.1.0: + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==, tarball: https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz} + engines: {node: '>=10'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==, tarball: https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==, tarball: https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz} + engines: {node: '>= 14'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==, tarball: https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz} + engines: {node: '>= 4'} + + ignore@7.0.12: + resolution: {integrity: sha512-/8UvqAPU9DGTI9k4mxtf49U37Isfwr8Uts96+SBHIkFxPJnHS0Ew4f00sM4Scd8V8EjM0jUNtVDdv1kPdt35lg==, tarball: https://registry.npmjs.org/ignore/-/ignore-7.0.12.tgz} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==, tarball: https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==, tarball: https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz} + engines: {node: '>=0.8.19'} + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==, tarball: https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==, tarball: https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, tarball: https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz} + engines: {node: '>=0.10.0'} + + is-in-ssh@1.0.0: + resolution: {integrity: sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==, tarball: https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz} + engines: {node: '>=20'} + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==, tarball: https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz} + engines: {node: '>=14.16'} + hasBin: true + + is-wsl@3.1.1: + resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==, tarball: https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz} + engines: {node: '>=16'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, tarball: https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz} + + istextorbinary@9.5.0: + resolution: {integrity: sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw==, tarball: https://registry.npmjs.org/istextorbinary/-/istextorbinary-9.5.0.tgz} + engines: {node: '>=4'} + + js-yaml@4.3.2: + resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==, tarball: https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==, tarball: https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==, tarball: https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==, tarball: https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz} + + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==, tarball: https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz} + + jsonwebtoken@9.0.3: + resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==, tarball: https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz} + engines: {node: '>=12', npm: '>=6'} + + jwa@2.0.1: + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==, tarball: https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz} + + jws@4.0.1: + resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==, tarball: https://registry.npmjs.org/jws/-/jws-4.0.1.tgz} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==, tarball: https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==, tarball: https://registry.npmjs.org/levn/-/levn-0.4.1.tgz} + engines: {node: '>= 0.8.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==, tarball: https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz} + engines: {node: '>=10'} + + lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==, tarball: https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==, tarball: https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz} + + lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==, tarball: https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz} + + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==, tarball: https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==, tarball: https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==, tarball: https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==, tarball: https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz} + + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==, tarball: https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==, tarball: https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz} + engines: {node: '>=10'} + + marked@18.1.1: + resolution: {integrity: sha512-zEGgrtUG8crk+TrMacJSVABU4DYDDkS8uqgU3ohrn/2fW9C2AEfUrO3m6IhpIpQpl7YcVpiXXC9cSMWDANT3dg==, tarball: https://registry.npmjs.org/marked/-/marked-18.1.1.tgz} + engines: {node: '>= 20'} + hasBin: true + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==, tarball: https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz} + engines: {node: '>= 0.4'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==, tarball: https://registry.npmjs.org/mime/-/mime-1.6.0.tgz} + engines: {node: '>=4'} + hasBin: true + + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==, tarball: https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==, tarball: https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, tarball: https://registry.npmjs.org/ms/-/ms-2.1.3.tgz} + + mute-stream@0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==, tarball: https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, tarball: https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==, tarball: https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz} + engines: {node: '>= 0.4'} + + open@11.0.4: + resolution: {integrity: sha512-++Zlftm0kVLPmzC06t6epuWmcRMDbI4z5P3NNX979WA/k23+NtSOynEGzsVfZwguKw2mi5umVgnBlJQMwRz4Pg==, tarball: https://registry.npmjs.org/open/-/open-11.0.4.tgz} + engines: {node: '>=20'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==, tarball: https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==, tarball: https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==, tarball: https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==, tarball: https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz} + engines: {node: '>=6'} + + parse5@8.0.1: + resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==, tarball: https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, tarball: https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, tarball: https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz} + engines: {node: '>=8'} + + pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==, tarball: https://registry.npmjs.org/pend/-/pend-1.2.0.tgz} + + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==, tarball: https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz} + engines: {node: '>=12'} + + powershell-utils@0.2.1: + resolution: {integrity: sha512-C+y9x90UElAddDZmV4qOx9W53B61PO7cIqWz2dQsWlwswuq4mr8NEwytdGKboYbQlGZ3awrkTeNvcZiZNHnQ8A==, tarball: https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.2.1.tgz} + engines: {node: '>=20'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==, tarball: https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz} + engines: {node: '>= 0.8.0'} + + prettier@3.9.10: + resolution: {integrity: sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA==, tarball: https://registry.npmjs.org/prettier/-/prettier-3.9.10.tgz} + engines: {node: '>=14'} + hasBin: true + + proper-lockfile@4.1.2: + resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==, tarball: https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, tarball: https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz} + engines: {node: '>=6'} + + qs@6.16.1: + resolution: {integrity: sha512-5Px6J7rfHBAGUhNQ3es4YzcOdBBw0SDcVtThJml4RdnKw4tKs2epA0kPZ+K8X+WiHktT93RHVcQ/3ym5aTCgtg==, tarball: https://registry.npmjs.org/qs/-/qs-6.16.1.tgz} + engines: {node: '>=0.6'} + + react-dom@19.3.1: + resolution: {integrity: sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ==, tarball: https://registry.npmjs.org/react-dom/-/react-dom-19.3.1.tgz} + peerDependencies: + react: ^19.3.1 + + react@19.3.1: + resolution: {integrity: sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==, tarball: https://registry.npmjs.org/react/-/react-19.3.1.tgz} + engines: {node: '>=0.10.0'} + + read@1.0.7: + resolution: {integrity: sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==, tarball: https://registry.npmjs.org/read/-/read-1.0.7.tgz} + engines: {node: '>=0.8'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==, tarball: https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz} + engines: {node: '>=4'} + + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==, tarball: https://registry.npmjs.org/retry/-/retry-0.12.0.tgz} + engines: {node: '>= 4'} + + run-applescript@7.1.0: + resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==, tarball: https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz} + engines: {node: '>=18'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==, tarball: https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz} + + sax@1.6.1: + resolution: {integrity: sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==, tarball: https://registry.npmjs.org/sax/-/sax-1.6.1.tgz} + engines: {node: '>=11.0.0'} + + scheduler@0.28.0: + resolution: {integrity: sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==, tarball: https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz} + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==, tarball: https://registry.npmjs.org/semver/-/semver-7.8.5.tgz} + engines: {node: '>=10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, tarball: https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, tarball: https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz} + engines: {node: '>=8'} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==, tarball: https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==, tarball: https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==, tarball: https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz} + engines: {node: '>= 0.4'} + + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==, tarball: https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==, tarball: https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz} + + socket.io-client@4.8.4: + resolution: {integrity: sha512-7wjKAvVXYOJsbmUgkWbnhPD4bFpkqrUR0++XOjpGFmqDqsqV1CI93VTraeExLxpjxEqrWgFoSRZu1ihjLWfkAA==, tarball: https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.4.tgz} + engines: {node: '>=10.0.0'} + + socket.io-parser@4.2.7: + resolution: {integrity: sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==, tarball: https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.7.tgz} + engines: {node: '>=10.0.0'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==, tarball: https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz} + engines: {node: '>=8'} + + structured-source@4.0.0: + resolution: {integrity: sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==, tarball: https://registry.npmjs.org/structured-source/-/structured-source-4.0.0.tgz} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==, tarball: https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz} + engines: {node: '>=8'} + + textextensions@6.11.0: + resolution: {integrity: sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ==, tarball: https://registry.npmjs.org/textextensions/-/textextensions-6.11.0.tgz} + engines: {node: '>=4'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==, tarball: https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz} + engines: {node: '>=12.0.0'} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==, tarball: https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==, tarball: https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz} + + tsx@4.23.15: + resolution: {integrity: sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==, tarball: https://registry.npmjs.org/tsx/-/tsx-4.23.15.tgz} + engines: {node: '>=18.0.0'} + hasBin: true + + tunnel@0.0.6: + resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==, tarball: https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz} + engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==, tarball: https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz} + engines: {node: '>= 0.8.0'} + + typed-rest-client@1.8.11: + resolution: {integrity: sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==, tarball: https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz} + + typescript-eslint@8.71.2: + resolution: {integrity: sha512-Rz4effbMy2g9wnvOJZG6SbVavX313Lap1vNEbk1RUochzickvzP8P4HB+bkNg5e50eVz1hQGjh9K1omI1xB4Bw==, tarball: https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.71.2.tgz} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==, tarball: https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz} + engines: {node: '>=14.17'} + hasBin: true + + underscore@1.13.8: + resolution: {integrity: sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==, tarball: https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz} + + undici-types@8.9.0: + resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==, tarball: https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==, tarball: https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz} + + url-join@4.0.1: + resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==, tarball: https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz} + + version-range@4.15.0: + resolution: {integrity: sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==, tarball: https://registry.npmjs.org/version-range/-/version-range-4.15.0.tgz} + engines: {node: '>=4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, tarball: https://registry.npmjs.org/which/-/which-2.0.2.tgz} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==, tarball: https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz} + engines: {node: '>=0.10.0'} + + ws@8.21.3: + resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==, tarball: https://registry.npmjs.org/ws/-/ws-8.21.3.tgz} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + wsl-utils@1.0.1: + resolution: {integrity: sha512-c16PKLIRxBYpR3jSWsdS7SFp0COm5SOQdiFjY818dYFHJbMGuLBszpF07LBWmOS1uzrHa9VGdynsEgoeKy0oOQ==, tarball: https://registry.npmjs.org/wsl-utils/-/wsl-utils-1.0.1.tgz} + engines: {node: '>=20'} + + xml2js@0.5.0: + resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==, tarball: https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz} + engines: {node: '>=4.0.0'} + + xmlbuilder@11.0.1: + resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==, tarball: https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz} + engines: {node: '>=4.0'} + + xmlhttprequest-ssl@2.1.2: + resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==, tarball: https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz} + engines: {node: '>=0.4.0'} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==, tarball: https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz} + + yauzl@3.4.0: + resolution: {integrity: sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==, tarball: https://registry.npmjs.org/yauzl/-/yauzl-3.4.0.tgz} + engines: {node: '>=12'} + + yazl@2.5.1: + resolution: {integrity: sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==, tarball: https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==, tarball: https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz} + engines: {node: '>=10'} + + zod@4.6.7: + resolution: {integrity: sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==, tarball: https://registry.npmjs.org/zod/-/zod-4.6.7.tgz} + +snapshots: + + '@azure/abort-controller@2.2.0': + dependencies: + tslib: 2.8.1 + + '@azure/core-auth@1.11.0': + dependencies: + '@azure/abort-controller': 2.2.0 + '@azure/core-util': 1.14.0 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@azure/core-client@1.11.1': + dependencies: + '@azure/abort-controller': 2.2.0 + '@azure/core-auth': 1.11.0 + '@azure/core-rest-pipeline': 1.25.0 + '@azure/core-tracing': 1.4.0 + '@azure/core-util': 1.14.0 + '@azure/logger': 1.4.0 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@azure/core-process@1.0.0': {} + + '@azure/core-rest-pipeline@1.25.0': + dependencies: + '@azure/abort-controller': 2.2.0 + '@azure/core-auth': 1.11.0 + '@azure/core-tracing': 1.4.0 + '@azure/core-util': 1.14.0 + '@azure/logger': 1.4.0 + '@typespec/ts-http-runtime': 0.3.9 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@azure/core-tracing@1.4.0': + dependencies: + tslib: 2.8.1 + + '@azure/core-util@1.14.0': + dependencies: + '@azure/abort-controller': 2.2.0 + '@typespec/ts-http-runtime': 0.3.9 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@azure/identity@4.14.0': + dependencies: + '@azure/abort-controller': 2.2.0 + '@azure/core-auth': 1.11.0 + '@azure/core-client': 1.11.1 + '@azure/core-process': 1.0.0 + '@azure/core-rest-pipeline': 1.25.0 + '@azure/core-tracing': 1.4.0 + '@azure/core-util': 1.14.0 + '@azure/logger': 1.4.0 + '@azure/msal-browser': 5.26.0 + '@azure/msal-node': 6.0.1 + open: 11.0.4 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@azure/logger@1.4.0': + dependencies: + '@typespec/ts-http-runtime': 0.3.9 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@azure/msal-browser@5.26.0': + dependencies: + '@azure/msal-common': 16.14.3 + + '@azure/msal-common@16.14.1': {} + + '@azure/msal-common@16.14.3': {} + + '@azure/msal-node@6.0.1': + dependencies: + '@azure/msal-common': 16.14.1 + jsonwebtoken: 9.0.3 + + '@esbuild/aix-ppc64@0.28.2': + optional: true + + '@esbuild/android-arm64@0.28.2': + optional: true + + '@esbuild/android-arm@0.28.2': + optional: true + + '@esbuild/android-x64@0.28.2': + optional: true + + '@esbuild/darwin-arm64@0.28.2': + optional: true + + '@esbuild/darwin-x64@0.28.2': + optional: true + + '@esbuild/freebsd-arm64@0.28.2': + optional: true + + '@esbuild/freebsd-x64@0.28.2': + optional: true + + '@esbuild/linux-arm64@0.28.2': + optional: true + + '@esbuild/linux-arm@0.28.2': + optional: true + + '@esbuild/linux-ia32@0.28.2': + optional: true + + '@esbuild/linux-loong64@0.28.2': + optional: true + + '@esbuild/linux-mips64el@0.28.2': + optional: true + + '@esbuild/linux-ppc64@0.28.2': + optional: true + + '@esbuild/linux-riscv64@0.28.2': + optional: true + + '@esbuild/linux-s390x@0.28.2': + optional: true + + '@esbuild/linux-x64@0.28.2': + optional: true + + '@esbuild/netbsd-arm64@0.28.2': + optional: true + + '@esbuild/netbsd-x64@0.28.2': + optional: true + + '@esbuild/openbsd-arm64@0.28.2': + optional: true + + '@esbuild/openbsd-x64@0.28.2': + optional: true + + '@esbuild/openharmony-arm64@0.28.2': + optional: true + + '@esbuild/sunos-x64@0.28.2': + optional: true + + '@esbuild/win32-arm64@0.28.2': + optional: true + + '@esbuild/win32-ia32@0.28.2': + optional: true + + '@esbuild/win32-x64@0.28.2': + optional: true + + '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5)': + dependencies: + eslint: 9.39.5 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.2': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.7': + dependencies: + ajv: 6.15.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.3.2 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.5': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@napi-rs/keyring-darwin-arm64@1.3.0': + optional: true + + '@napi-rs/keyring-darwin-x64@1.3.0': + optional: true + + '@napi-rs/keyring-freebsd-x64@1.3.0': + optional: true + + '@napi-rs/keyring-linux-arm-gnueabihf@1.3.0': + optional: true + + '@napi-rs/keyring-linux-arm64-gnu@1.3.0': + optional: true + + '@napi-rs/keyring-linux-arm64-musl@1.3.0': + optional: true + + '@napi-rs/keyring-linux-riscv64-gnu@1.3.0': + optional: true + + '@napi-rs/keyring-linux-x64-gnu@1.3.0': + optional: true + + '@napi-rs/keyring-linux-x64-musl@1.3.0': + optional: true + + '@napi-rs/keyring-win32-arm64-msvc@1.3.0': + optional: true + + '@napi-rs/keyring-win32-ia32-msvc@1.3.0': + optional: true + + '@napi-rs/keyring-win32-x64-msvc@1.3.0': + optional: true + + '@napi-rs/keyring@1.3.0': + optionalDependencies: + '@napi-rs/keyring-darwin-arm64': 1.3.0 + '@napi-rs/keyring-darwin-x64': 1.3.0 + '@napi-rs/keyring-freebsd-x64': 1.3.0 + '@napi-rs/keyring-linux-arm-gnueabihf': 1.3.0 + '@napi-rs/keyring-linux-arm64-gnu': 1.3.0 + '@napi-rs/keyring-linux-arm64-musl': 1.3.0 + '@napi-rs/keyring-linux-riscv64-gnu': 1.3.0 + '@napi-rs/keyring-linux-x64-gnu': 1.3.0 + '@napi-rs/keyring-linux-x64-musl': 1.3.0 + '@napi-rs/keyring-win32-arm64-msvc': 1.3.0 + '@napi-rs/keyring-win32-ia32-msvc': 1.3.0 + '@napi-rs/keyring-win32-x64-msvc': 1.3.0 + + '@secretlint/core@10.2.2': + dependencies: + '@secretlint/profiler': 10.2.2 + '@secretlint/types': 10.2.2 + debug: 4.4.3 + structured-source: 4.0.0 + transitivePeerDependencies: + - supports-color + + '@secretlint/profiler@10.2.2': {} + + '@secretlint/secretlint-rule-no-dotenv@10.2.2': + dependencies: + '@secretlint/types': 10.2.2 + + '@secretlint/secretlint-rule-preset-recommend@10.2.2': {} + + '@secretlint/source-creator@10.2.2': + dependencies: + '@secretlint/types': 10.2.2 + istextorbinary: 9.5.0 + + '@secretlint/types@10.2.2': {} + + '@socket.io/component-emitter@3.1.2': {} + + '@tangent/embed-react@https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/embed-react(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': + dependencies: + react: 19.3.1 + react-dom: 19.3.1(react@19.3.1) + + '@tangent/local-agent-host@https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/local-agent-host': + dependencies: + '@tangent/remote-subagent': https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/remote-subagent + '@tangent/shared': https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/shared + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@tangent/remote-subagent@https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/remote-subagent': + dependencies: + '@tangent/shared': https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/shared + socket.io-client: 4.8.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@tangent/shared@https://codeload.github.com/TangleML/tangent-shell/tar.gz/b0c6ac565872440b779f12e7787f8065d9af867a#path:packages/shared': + dependencies: + zod: 4.6.7 + + '@types/estree@1.0.9': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@26.6.5': + dependencies: + undici-types: 8.9.0 + + '@types/react-dom@19.3.0(@types/react@19.3.0)': + dependencies: + '@types/react': 19.3.0 + + '@types/react@19.3.0': + dependencies: + csstype: 3.2.3 + + '@types/vscode@1.90.0': {} + + '@typescript-eslint/eslint-plugin@8.71.2(@typescript-eslint/parser@8.71.2(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.71.2(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.71.2 + '@typescript-eslint/type-utils': 8.71.2(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/utils': 8.71.2(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.71.2 + eslint: 9.39.5 + ignore: 7.0.12 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.71.2(eslint@9.39.5)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.71.2 + '@typescript-eslint/types': 8.71.2 + '@typescript-eslint/typescript-estree': 8.71.2(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.71.2 + debug: 4.4.3 + eslint: 9.39.5 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.71.2(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.71.2(typescript@5.9.3) + '@typescript-eslint/types': 8.71.2 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.71.2': + dependencies: + '@typescript-eslint/types': 8.71.2 + '@typescript-eslint/visitor-keys': 8.71.2 + + '@typescript-eslint/tsconfig-utils@8.71.2(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.71.2(eslint@9.39.5)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.71.2 + '@typescript-eslint/typescript-estree': 8.71.2(typescript@5.9.3) + '@typescript-eslint/utils': 8.71.2(eslint@9.39.5)(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.5 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.71.2': {} + + '@typescript-eslint/typescript-estree@8.71.2(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.71.2(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.71.2(typescript@5.9.3) + '@typescript-eslint/types': 8.71.2 + '@typescript-eslint/visitor-keys': 8.71.2 + debug: 4.4.3 + minimatch: 10.2.6 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.71.2(eslint@9.39.5)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5) + '@typescript-eslint/scope-manager': 8.71.2 + '@typescript-eslint/types': 8.71.2 + '@typescript-eslint/typescript-estree': 8.71.2(typescript@5.9.3) + eslint: 9.39.5 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.71.2': + dependencies: + '@typescript-eslint/types': 8.71.2 + eslint-visitor-keys: 5.0.1 + + '@typespec/ts-http-runtime@0.3.9': + dependencies: + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@vscode/vsce-sign-alpine-arm64@2.0.6': + optional: true + + '@vscode/vsce-sign-alpine-x64@2.0.6': + optional: true + + '@vscode/vsce-sign-darwin-arm64@2.0.6': + optional: true + + '@vscode/vsce-sign-darwin-x64@2.0.6': + optional: true + + '@vscode/vsce-sign-linux-arm64@2.0.6': + optional: true + + '@vscode/vsce-sign-linux-arm@2.0.6': + optional: true + + '@vscode/vsce-sign-linux-x64@2.0.6': + optional: true + + '@vscode/vsce-sign-win32-arm64@2.0.6': + optional: true + + '@vscode/vsce-sign-win32-x64@2.0.6': + optional: true + + '@vscode/vsce-sign@2.1.0': + optionalDependencies: + '@vscode/vsce-sign-alpine-arm64': 2.0.6 + '@vscode/vsce-sign-alpine-x64': 2.0.6 + '@vscode/vsce-sign-darwin-arm64': 2.0.6 + '@vscode/vsce-sign-darwin-x64': 2.0.6 + '@vscode/vsce-sign-linux-arm': 2.0.6 + '@vscode/vsce-sign-linux-arm64': 2.0.6 + '@vscode/vsce-sign-linux-x64': 2.0.6 + '@vscode/vsce-sign-win32-arm64': 2.0.6 + '@vscode/vsce-sign-win32-x64': 2.0.6 + + '@vscode/vsce@4.0.0': + dependencies: + '@azure/identity': 4.14.0 + '@napi-rs/keyring': 1.3.0 + '@secretlint/core': 10.2.2 + '@secretlint/secretlint-rule-no-dotenv': 10.2.2 + '@secretlint/secretlint-rule-preset-recommend': 10.2.2 + '@secretlint/source-creator': 10.2.2 + '@secretlint/types': 10.2.2 + '@vscode/vsce-sign': 2.1.0 + azure-devops-node-api: 12.5.0 + cockatiel: 3.2.1 + commander: 12.1.0 + hosted-git-info: 4.1.0 + jsonc-parser: 3.3.1 + marked: 18.1.1 + mime: 1.6.0 + minimatch: 10.2.6 + parse5: 8.0.1 + proper-lockfile: 4.1.2 + read: 1.0.7 + semver: 7.8.5 + tinyglobby: 0.2.17 + typed-rest-client: 1.8.11 + url-join: 4.0.1 + xml2js: 0.5.0 + yauzl: 3.4.0 + yazl: 2.5.1 + transitivePeerDependencies: + - supports-color + + acorn-jsx@5.3.2(acorn@8.19.0): + dependencies: + acorn: 8.19.0 + + acorn@8.19.0: {} + + agent-base@7.1.4: {} + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + azure-devops-node-api@12.5.0: + dependencies: + tunnel: 0.0.6 + typed-rest-client: 1.8.11 + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + binaryextensions@6.11.0: + dependencies: + editions: 6.22.0 + + boundary@2.0.0: {} + + brace-expansion@1.1.21: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@5.0.12: + dependencies: + balanced-match: 4.0.4 + + buffer-crc32@0.2.13: {} + + buffer-equal-constant-time@1.0.1: {} + + bundle-name@4.1.1: + dependencies: + run-applescript: 7.1.0 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + cockatiel@3.2.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + commander@12.1.0: {} + + concat-map@0.0.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.2.3: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + default-browser-id@5.0.1: {} + + default-browser@5.5.1: + dependencies: + bundle-name: 4.1.1 + default-browser-id: 5.0.1 + + define-lazy-prop@3.0.0: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + + editions@6.22.0: + dependencies: + version-range: 4.15.0 + + engine.io-client@6.6.7: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3 + engine.io-parser: 5.2.3 + ws: 8.21.3 + xmlhttprequest-ssl: 2.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + engine.io-parser@5.2.3: {} + + entities@8.1.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + esbuild@0.28.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.2 + '@esbuild/android-arm': 0.28.2 + '@esbuild/android-arm64': 0.28.2 + '@esbuild/android-x64': 0.28.2 + '@esbuild/darwin-arm64': 0.28.2 + '@esbuild/darwin-x64': 0.28.2 + '@esbuild/freebsd-arm64': 0.28.2 + '@esbuild/freebsd-x64': 0.28.2 + '@esbuild/linux-arm': 0.28.2 + '@esbuild/linux-arm64': 0.28.2 + '@esbuild/linux-ia32': 0.28.2 + '@esbuild/linux-loong64': 0.28.2 + '@esbuild/linux-mips64el': 0.28.2 + '@esbuild/linux-ppc64': 0.28.2 + '@esbuild/linux-riscv64': 0.28.2 + '@esbuild/linux-s390x': 0.28.2 + '@esbuild/linux-x64': 0.28.2 + '@esbuild/netbsd-arm64': 0.28.2 + '@esbuild/netbsd-x64': 0.28.2 + '@esbuild/openbsd-arm64': 0.28.2 + '@esbuild/openbsd-x64': 0.28.2 + '@esbuild/openharmony-arm64': 0.28.2 + '@esbuild/sunos-x64': 0.28.2 + '@esbuild/win32-arm64': 0.28.2 + '@esbuild/win32-ia32': 0.28.2 + '@esbuild/win32-x64': 0.28.2 + + escape-string-regexp@4.0.0: {} + + eslint-plugin-simple-import-sort@14.0.0(eslint@9.39.5): + dependencies: + eslint: 9.39.5 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.5: + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.7 + '@eslint/js': 9.39.5 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.19.0 + acorn-jsx: 5.3.2(acorn@8.19.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.4 + keyv: 4.5.4 + + flatted@3.4.4: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@17.13.0: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + hosted-git-info@4.1.0: + dependencies: + lru-cache: 6.0.0 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + ignore@5.3.2: {} + + ignore@7.0.12: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + is-docker@3.0.0: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-in-ssh@1.0.0: {} + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-wsl@3.1.1: + dependencies: + is-inside-container: 1.0.0 + + isexe@2.0.0: {} + + istextorbinary@9.5.0: + dependencies: + binaryextensions: 6.11.0 + editions: 6.22.0 + textextensions: 6.11.0 + + js-yaml@4.3.2: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + jsonc-parser@3.3.1: {} + + jsonwebtoken@9.0.3: + dependencies: + jws: 4.0.1 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 7.8.5 + + jwa@2.0.1: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@4.0.1: + dependencies: + jwa: 2.0.1 + safe-buffer: 5.2.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.includes@4.3.0: {} + + lodash.isboolean@3.0.3: {} + + lodash.isinteger@4.0.4: {} + + lodash.isnumber@3.0.3: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isstring@4.0.1: {} + + lodash.merge@4.6.2: {} + + lodash.once@4.1.1: {} + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + marked@18.1.1: {} + + math-intrinsics@1.1.0: {} + + mime@1.6.0: {} + + minimatch@10.2.6: + dependencies: + brace-expansion: 5.0.12 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.21 + + ms@2.1.3: {} + + mute-stream@0.0.8: {} + + natural-compare@1.4.0: {} + + object-inspect@1.13.4: {} + + open@11.0.4: + 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.1 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse5@8.0.1: + dependencies: + entities: 8.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + pend@1.2.0: {} + + picomatch@4.0.7: {} + + powershell-utils@0.2.1: {} + + prelude-ls@1.2.1: {} + + prettier@3.9.10: {} + + proper-lockfile@4.1.2: + dependencies: + graceful-fs: 4.2.11 + retry: 0.12.0 + signal-exit: 3.0.7 + + punycode@2.3.1: {} + + qs@6.16.1: + dependencies: + es-define-property: 1.0.1 + side-channel: 1.1.1 + + react-dom@19.3.1(react@19.3.1): + dependencies: + react: 19.3.1 + scheduler: 0.28.0 + + react@19.3.1: {} + + read@1.0.7: + dependencies: + mute-stream: 0.0.8 + + resolve-from@4.0.0: {} + + retry@0.12.0: {} + + run-applescript@7.1.0: {} + + safe-buffer@5.2.1: {} + + sax@1.6.1: {} + + scheduler@0.28.0: {} + + semver@7.8.5: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@3.0.7: {} + + socket.io-client@4.8.4: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3 + engine.io-client: 6.6.7 + socket.io-parser: 4.2.7 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.7: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + strip-json-comments@3.1.1: {} + + structured-source@4.0.0: + dependencies: + boundary: 2.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + textextensions@6.11.0: + dependencies: + editions: 6.22.0 + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + tslib@2.8.1: {} + + tsx@4.23.15: + dependencies: + esbuild: 0.28.2 + optionalDependencies: + fsevents: 2.3.3 + + tunnel@0.0.6: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typed-rest-client@1.8.11: + dependencies: + qs: 6.16.1 + tunnel: 0.0.6 + underscore: 1.13.8 + + typescript-eslint@8.71.2(eslint@9.39.5)(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.71.2(@typescript-eslint/parser@8.71.2(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/parser': 8.71.2(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.71.2(typescript@5.9.3) + '@typescript-eslint/utils': 8.71.2(eslint@9.39.5)(typescript@5.9.3) + eslint: 9.39.5 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + typescript@5.9.3: {} + + underscore@1.13.8: {} + + undici-types@8.9.0: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + url-join@4.0.1: {} + + version-range@4.15.0: {} + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + ws@8.21.3: {} + + wsl-utils@1.0.1: + dependencies: + is-wsl: 3.1.1 + powershell-utils: 0.2.1 + + xml2js@0.5.0: + dependencies: + sax: 1.6.1 + xmlbuilder: 11.0.1 + + xmlbuilder@11.0.1: {} + + xmlhttprequest-ssl@2.1.2: {} + + yallist@4.0.0: {} + + yauzl@3.4.0: + dependencies: + pend: 1.2.0 + + yazl@2.5.1: + dependencies: + buffer-crc32: 0.2.13 + + yocto-queue@0.1.0: {} + + zod@4.6.7: {} diff --git a/vscode-extension/pnpm-workspace.yaml b/vscode-extension/pnpm-workspace.yaml new file mode 100644 index 0000000000..a1c031fc33 --- /dev/null +++ b/vscode-extension/pnpm-workspace.yaml @@ -0,0 +1 @@ +minimumReleaseAge: 0 diff --git a/vscode-extension/resources/icon.png b/vscode-extension/resources/icon.png new file mode 100644 index 0000000000..e6d45091ba Binary files /dev/null and b/vscode-extension/resources/icon.png differ diff --git a/vscode-extension/resources/tangle.svg b/vscode-extension/resources/tangle.svg new file mode 100644 index 0000000000..e7516f7a0a --- /dev/null +++ b/vscode-extension/resources/tangle.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/vscode-extension/src/auth/oktaSsoAuth.ts b/vscode-extension/src/auth/oktaSsoAuth.ts new file mode 100644 index 0000000000..9a5e6c93d0 --- /dev/null +++ b/vscode-extension/src/auth/oktaSsoAuth.ts @@ -0,0 +1,387 @@ +import crypto from "node:crypto"; +import http from "node:http"; + +import * as vscode from "vscode"; +import { number, object, optional, string } from "zod/mini"; + +import { showError } from "../prompts.ts"; +import { + getOktaSsoConfig, + isOktaSsoEnabled, + type OktaSsoConfig, +} from "./oktaSsoConfig.ts"; +import { emailFromSessionCookie } from "./oktaSsoIdentity.ts"; + +const SECRET_PREFIX = "tangent.oktaSso"; +const CALLBACK_TIMEOUT_MS = 120_000; + +const accessTokenSchema = object({ access_token: optional(string()) }); +const cachedCookieSchema = object({ cookie: string(), createdAt: number() }); + +export interface GetCookieOptions { + forceRefresh?: boolean; +} + +/** Resolves the OktaSSO cookie header for one audience, or `undefined` when off. */ +export type CookieProvider = ( + options?: GetCookieOptions, +) => Promise; + +interface CachedCookie { + cookie: string; + createdAt: number; +} + +/** + * Mints and caches the SSO session cookie the real-environment API gate expects, + * following the Okta PKCE flow. One token is kept per audience (the API host the + * cookie authenticates against), so the Tangle backend and the Shell each get + * their own. Tokens live in VS Code SecretStorage. A read is silent by default + * and returns `undefined` when the cache is cold or stale, so background callers + * (the scoped-connection refresh, auto-connect, REST retries) never open the + * browser; a silent miss instead surfaces a single "Sign In" notification per + * audience. The browser opens only when the user accepts that notification, with + * concurrent callers awaiting the same flow. + */ +export class OktaSsoAuthProvider { + private readonly secrets: vscode.SecretStorage; + private readonly config: OktaSsoConfig; + private readonly inFlight = new Map>(); + private readonly signInPrompts = new Set(); + private flowChain: Promise = Promise.resolve(); + + constructor(secrets: vscode.SecretStorage, config = getOktaSsoConfig()) { + this.secrets = secrets; + this.config = config; + } + + /** A provider bound to one audience, suitable to hand an API client. */ + cookieProviderFor(audienceUrl: string): CookieProvider { + return (options) => this.getCookieHeader(audienceUrl, options); + } + + /** The real user's email from the session JWT, reusing the cached cookie. */ + async getAuthenticatedEmail( + audienceUrl: string, + ): Promise { + const cookie = await this.getCookieHeader(audienceUrl); + return cookie + ? emailFromSessionCookie(cookie, this.config.cookieName) + : undefined; + } + + async getCookieHeader( + audienceUrl: string, + options: GetCookieOptions = {}, + ): Promise { + if (!isOktaSsoEnabled(this.config)) return undefined; + + const key = this.secretKey(audienceUrl); + const cached = options.forceRefresh ? undefined : await this.readCache(key); + if (cached) return cached; + this.promptSignIn(key, audienceUrl); + return undefined; + } + + /** + * Offers one browser sign-in per audience when a silent read came up empty, + * so a stale session surfaces a notification the user can act on instead of a + * browser tab opening on its own. Clients recover on their next cycle once the + * flow warms the cache (REST on the next call, the socket on reconnect). + */ + private promptSignIn(key: string, audienceUrl: string): void { + if (this.signInPrompts.has(key)) return; + this.signInPrompts.add(key); + void vscode.window + .showWarningMessage( + `Sign in to Tangent to reach ${new URL(audienceUrl).host}.`, + "Sign In", + ) + .then((choice) => + choice === "Sign In" ? this.runFlow(key, audienceUrl) : undefined, + ) + .then( + () => this.signInPrompts.delete(key), + (err: unknown) => { + this.signInPrompts.delete(key); + showError(err); + }, + ); + } + + /** Dedupes concurrent flows for one audience so only one browser opens. */ + private runFlow(key: string, audienceUrl: string): Promise { + const existing = this.inFlight.get(key); + if (existing) return existing; + const flow = this.enqueue(() => this.authorize(audienceUrl)) + .then(async (cookie) => { + await this.writeCache(key, cookie); + return cookie; + }) + .finally(() => this.inFlight.delete(key)); + this.inFlight.set(key, flow); + return flow; + } + + /** + * Runs browser flows one at a time across all audiences: each opens a loopback + * server on the same callback port, so overlapping flows would collide. + */ + private enqueue(task: () => Promise): Promise { + const run = this.flowChain.then(task, task); + this.flowChain = run.then( + () => undefined, + () => undefined, + ); + return run; + } + + private secretKey(audienceUrl: string): string { + return `${SECRET_PREFIX}.${new URL(audienceUrl).host}`; + } + + private async readCache(key: string): Promise { + const raw = await this.secrets.get(key); + if (!raw) return undefined; + const parsed = cachedCookieSchema.safeParse(safeJsonParse(raw)); + if (!parsed.success) return undefined; + const ageMs = Date.now() - parsed.data.createdAt; + if (ageMs >= this.config.refreshAfterMs) return undefined; + return parsed.data.cookie || undefined; + } + + private async writeCache(key: string, cookie: string): Promise { + const payload: CachedCookie = { cookie, createdAt: Date.now() }; + await this.secrets.store(key, JSON.stringify(payload)); + } + + private async authorize(audienceUrl: string): Promise { + const { verifier, challenge } = generatePkcePair(); + const code = await this.getAuthorizationCode(challenge); + const accessToken = await this.exchangeCodeForAccessToken(code, verifier); + const sessionToken = await this.exchangeForSessionToken( + accessToken, + audienceUrl, + ); + return `${this.config.cookieName}=${sessionToken}`; + } + + private async getAuthorizationCode(challenge: string): Promise { + const state = crypto.randomUUID(); + const codePromise = waitForAuthorizationCode( + this.config.callbackPort, + state, + ); + + const url = new URL("authorize", this.config.oauthEndpointBase); + url.searchParams.set("response_type", "code"); + url.searchParams.set("client_id", this.config.clientId); + url.searchParams.set("callback_url", this.config.redirectUri); + url.searchParams.set("scope", this.config.scopes); + url.searchParams.set("code_challenge", challenge); + url.searchParams.set("code_challenge_method", "S256"); + url.searchParams.set("state", state); + url.searchParams.set("redirect_uri", this.config.redirectUri); + + await vscode.env.openExternal(vscode.Uri.parse(url.toString())); + return codePromise; + } + + private async exchangeCodeForAccessToken( + code: string, + verifier: string, + ): Promise { + const body = new URLSearchParams({ + grant_type: "authorization_code", + client_id: this.config.clientId, + code, + code_verifier: verifier, + redirect_uri: this.config.redirectUri, + }); + + const res = await fetch(new URL("token", this.config.oauthEndpointBase), { + method: "POST", + headers: { "content-type": "application/x-www-form-urlencoded" }, + body, + }); + if (!res.ok) { + throw new Error( + `OktaSSO: token exchange failed (${res.status}): ${await safeText(res)}`, + ); + } + const json = accessTokenSchema.parse(await res.json()); + if (!json.access_token) { + throw new Error("OktaSSO: token response did not include access_token."); + } + return json.access_token; + } + + private async exchangeForSessionToken( + accessToken: string, + audienceUrl: string, + ): Promise { + const url = new URL(this.config.tokenExchangeUrl); + url.searchParams.set("audience", audienceUrl); + + const res = await fetch(url, { + method: "GET", + headers: { + authorization: `Bearer ${accessToken}`, + accept: "application/json", + }, + }); + if (!res.ok) { + throw new Error( + `OktaSSO: session exchange failed (${res.status}): ${await safeText(res)}`, + ); + } + const json = accessTokenSchema.parse(await res.json()); + if (!json.access_token) { + throw new Error( + "OktaSSO: session response did not include access_token.", + ); + } + return json.access_token; + } +} + +function generatePkcePair(): { verifier: string; challenge: string } { + const verifier = base64UrlNoPad(crypto.randomBytes(64)); + const challenge = base64UrlNoPad( + crypto.createHash("sha256").update(verifier).digest(), + ); + return { verifier, challenge }; +} + +function base64UrlNoPad(buf: Buffer): string { + return buf + .toString("base64") + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/=+$/g, ""); +} + +interface AuthCallback { + resolve: (code: string) => void; + reject: (err: Error) => void; + done: () => void; +} + +/** + * A callback whose `state` does not match is a stray or forged request: it is + * answered but left to time out, so it cannot abort the real sign-in. Only a + * matching callback resolves (with the code) or rejects (with the IdP error). + */ +function handleAuthCallback( + req: http.IncomingMessage, + res: http.ServerResponse, + expectedState: string, + callback: AuthCallback, +): void { + const requestUrl = new URL(req.url ?? "/", "http://localhost"); + if (!requestUrl.pathname.startsWith("/auth")) { + res.statusCode = 404; + res.end(); + return; + } + if (requestUrl.searchParams.get("state") !== expectedState) { + res.statusCode = 400; + res.end("Unexpected state."); + return; + } + resolveAuthCallback(requestUrl.searchParams, res, callback); +} + +function resolveAuthCallback( + params: URLSearchParams, + res: http.ServerResponse, + callback: AuthCallback, +): void { + const error = params.get("error"); + if (error) { + const detail = params.get("error_description") ?? error; + res.statusCode = 400; + res.end(detail); + callback.reject(new Error(`OktaSSO: authorization failed: ${detail}.`)); + callback.done(); + return; + } + const code = params.get("code"); + if (!code) { + res.statusCode = 400; + res.end("Missing ?code"); + return; + } + res.statusCode = 200; + res.setHeader("Content-Type", "text/html; charset=utf-8"); + res.end(SUCCESS_PAGE); + callback.resolve(code); + callback.done(); +} + +function waitForAuthorizationCode( + port: number, + expectedState: string, +): Promise { + return new Promise((resolve, reject) => { + const server = http.createServer((req, res) => + handleAuthCallback(req, res, expectedState, { + resolve, + reject, + done: () => setImmediate(() => server.close()), + }), + ); + + server.on("error", (err) => + reject( + new Error( + `OktaSSO: local callback server failed on port ${port}: ${stringifyError(err)}`, + ), + ), + ); + + const timer = setTimeout(() => { + server.close(); + reject( + new Error( + `OktaSSO: timed out after ${CALLBACK_TIMEOUT_MS / 1000}s waiting for the browser callback.`, + ), + ); + }, CALLBACK_TIMEOUT_MS); + timer.unref(); + server.on("close", () => clearTimeout(timer)); + + server.listen(port, "127.0.0.1"); + }); +} + +async function safeText(res: Response): Promise { + try { + return (await res.text()).slice(0, 500); + } catch { + return ""; + } +} + +function stringifyError(err: unknown): string { + return err instanceof Error ? err.message : String(err); +} + +function safeJsonParse(raw: string): unknown { + try { + return JSON.parse(raw); + } catch { + return undefined; + } +} + +const SUCCESS_PAGE = ` + +Auth successful + +

+ Auth successful! This window will close shortly. +

+ + +`; diff --git a/vscode-extension/src/auth/oktaSsoConfig.test.ts b/vscode-extension/src/auth/oktaSsoConfig.test.ts new file mode 100644 index 0000000000..54160e4e8e --- /dev/null +++ b/vscode-extension/src/auth/oktaSsoConfig.test.ts @@ -0,0 +1,56 @@ +import assert from "node:assert/strict"; +import { afterEach, test } from "node:test"; + +import { getOktaSsoConfig, isOktaSsoEnabled } from "./oktaSsoConfig.ts"; + +const KEYS = [ + "VITE_OKTASSO_ENABLED", + "VITE_OKTASSO_OAUTH_ENDPOINT_BASE", + "VITE_OKTASSO_TOKEN_EXCHANGE_URL", + "VITE_OKTASSO_CLIENT_ID", + "VITE_OKTASSO_TOKEN_COOKIE_NAME", + "VITE_OKTASSO_SCOPES", + "VITE_OKTASSO_REDIRECT_URI", + "VITE_OKTASSO_CALLBACK_PORT", + "VITE_OKTASSO_REFRESH_AFTER_MS", +]; + +function clearEnv(): void { + for (const key of KEYS) delete process.env[key]; +} + +function configureRequired(): void { + process.env.VITE_OKTASSO_OAUTH_ENDPOINT_BASE = "https://okta.example/oauth2/"; + process.env.VITE_OKTASSO_TOKEN_EXCHANGE_URL = "https://sso.example/token"; + process.env.VITE_OKTASSO_CLIENT_ID = "client-id"; + process.env.VITE_OKTASSO_TOKEN_COOKIE_NAME = "SSO_TOKEN"; +} + +afterEach(clearEnv); + +test("stays disabled when the required values are missing", () => { + clearEnv(); + assert.equal(isOktaSsoEnabled(), false); +}); + +test("turns on once the four required values are present", () => { + clearEnv(); + configureRequired(); + assert.equal(isOktaSsoEnabled(), true); +}); + +test("the kill switch disables the gate even when configured", () => { + clearEnv(); + configureRequired(); + process.env.VITE_OKTASSO_ENABLED = "false"; + assert.equal(isOktaSsoEnabled(), false); +}); + +test("falls back to the documented defaults for optional settings", () => { + clearEnv(); + const config = getOktaSsoConfig(); + assert.equal(config.scopes, "profile email openid"); + assert.equal(config.redirectUri, "http://localhost:3001/auth"); + assert.equal(config.callbackPort, 3001); + assert.equal(config.refreshAfterMs, 3_300_000); +}); diff --git a/vscode-extension/src/auth/oktaSsoConfig.ts b/vscode-extension/src/auth/oktaSsoConfig.ts new file mode 100644 index 0000000000..ff9c994770 --- /dev/null +++ b/vscode-extension/src/auth/oktaSsoConfig.ts @@ -0,0 +1,66 @@ +const DEFAULT_SCOPES = "profile email openid"; +const DEFAULT_REDIRECT_URI = "http://localhost:3001/auth"; +const DEFAULT_CALLBACK_PORT = 3001; +const DEFAULT_REFRESH_AFTER_MS = 3_300_000; + +export interface OktaSsoConfig { + oauthEndpointBase: string; + tokenExchangeUrl: string; + clientId: string; + cookieName: string; + scopes: string; + redirectUri: string; + callbackPort: number; + refreshAfterMs: number; +} + +function trimmed(raw: string | undefined): string { + return (raw ?? "").trim(); +} + +function numberValue(raw: string | undefined, fallback: number): number { + const parsed = Number(trimmed(raw)); + return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback; +} + +/** + * Reads each key as a static `process.env.VITE_OKTASSO_*` so esbuild's `define` + * inlines the build-time value — a dynamic `process.env[key]` would be left + * untouched and read as `undefined` in the packaged extension. + */ +export function getOktaSsoConfig(): OktaSsoConfig { + return { + oauthEndpointBase: trimmed(process.env.VITE_OKTASSO_OAUTH_ENDPOINT_BASE), + tokenExchangeUrl: trimmed(process.env.VITE_OKTASSO_TOKEN_EXCHANGE_URL), + clientId: trimmed(process.env.VITE_OKTASSO_CLIENT_ID), + cookieName: trimmed(process.env.VITE_OKTASSO_TOKEN_COOKIE_NAME), + scopes: trimmed(process.env.VITE_OKTASSO_SCOPES) || DEFAULT_SCOPES, + redirectUri: + trimmed(process.env.VITE_OKTASSO_REDIRECT_URI) || DEFAULT_REDIRECT_URI, + callbackPort: numberValue( + process.env.VITE_OKTASSO_CALLBACK_PORT, + DEFAULT_CALLBACK_PORT, + ), + refreshAfterMs: numberValue( + process.env.VITE_OKTASSO_REFRESH_AFTER_MS, + DEFAULT_REFRESH_AFTER_MS, + ), + }; +} + +/** + * The gate is on only when it is configured: an explicit `VITE_OKTASSO_ENABLED=false` + * is a kill switch, and the four proprietary values must all be present. OSS + * checkouts leave them blank, so the extension behaves exactly as before. + */ +export function isOktaSsoEnabled( + config: OktaSsoConfig = getOktaSsoConfig(), +): boolean { + if (trimmed(process.env.VITE_OKTASSO_ENABLED) === "false") return false; + return Boolean( + config.oauthEndpointBase && + config.tokenExchangeUrl && + config.clientId && + config.cookieName, + ); +} diff --git a/vscode-extension/src/auth/oktaSsoIdentity.test.ts b/vscode-extension/src/auth/oktaSsoIdentity.test.ts new file mode 100644 index 0000000000..a666ee0e10 --- /dev/null +++ b/vscode-extension/src/auth/oktaSsoIdentity.test.ts @@ -0,0 +1,58 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; + +import { emailFromSessionCookie } from "./oktaSsoIdentity.ts"; + +const COOKIE_NAME = "MINERVA_TOKEN"; + +function jwt(claims: Record): string { + const header = Buffer.from( + JSON.stringify({ alg: "none", typ: "JWT" }), + ).toString("base64url"); + const payload = Buffer.from(JSON.stringify(claims)).toString("base64url"); + return `${header}.${payload}.`; +} + +function cookie(value: string): string { + return `${COOKIE_NAME}=${value}`; +} + +test("reads the email claim from the session JWT", () => { + const header = cookie(jwt({ email: "real.user@corp.example" })); + assert.equal( + emailFromSessionCookie(header, COOKIE_NAME), + "real.user@corp.example", + ); +}); + +test("finds the cookie among several", () => { + const header = `other=x; ${cookie(jwt({ email: "a@b.com" }))}; last=y`; + assert.equal(emailFromSessionCookie(header, COOKIE_NAME), "a@b.com"); +}); + +test("returns undefined when the JWT omits email", () => { + const header = cookie(jwt({ sub: "123" })); + assert.equal(emailFromSessionCookie(header, COOKIE_NAME), undefined); +}); + +test("returns undefined when email is an empty string", () => { + const header = cookie(jwt({ email: "" })); + assert.equal(emailFromSessionCookie(header, COOKIE_NAME), undefined); +}); + +test("returns undefined for an opaque, non-JWT cookie value", () => { + assert.equal( + emailFromSessionCookie(cookie("opaque-session-token"), COOKIE_NAME), + undefined, + ); +}); + +test("returns undefined when the cookie name is absent", () => { + const header = cookie(jwt({ email: "a@b.com" })); + assert.equal(emailFromSessionCookie(header, "OTHER_COOKIE"), undefined); +}); + +test("returns undefined when the JWT payload is malformed", () => { + const header = cookie("header.not-base64-json!!.signature"); + assert.equal(emailFromSessionCookie(header, COOKIE_NAME), undefined); +}); diff --git a/vscode-extension/src/auth/oktaSsoIdentity.ts b/vscode-extension/src/auth/oktaSsoIdentity.ts new file mode 100644 index 0000000000..206978ab61 --- /dev/null +++ b/vscode-extension/src/auth/oktaSsoIdentity.ts @@ -0,0 +1,45 @@ +import { object, optional, string } from "zod/mini"; + +const claimsSchema = object({ email: optional(string()) }); + +function cookieValue( + cookieHeader: string, + cookieName: string, +): string | undefined { + for (const pair of cookieHeader.split(";")) { + const index = pair.indexOf("="); + if (index === -1) continue; + if (pair.slice(0, index).trim() === cookieName) { + return pair.slice(index + 1).trim(); + } + } + return undefined; +} + +function decodeJwtClaims(jwt: string): unknown { + const parts = jwt.split("."); + if (parts.length !== 3) return undefined; + try { + return JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8")); + } catch { + return undefined; + } +} + +/** + * Reads the `email` claim from the OktaSSO session JWT carried in a cookie. The + * Shell resolves the prompting person from this same claim, so minting with it + * makes a session's spawns route to the real user instead of the dev fallback. + * The signature is not verified — the Shell owns verification; we only read the + * identity the gate already minted. + */ +export function emailFromSessionCookie( + cookieHeader: string, + cookieName: string, +): string | undefined { + const token = cookieValue(cookieHeader, cookieName); + if (!token) return undefined; + const parsed = claimsSchema.safeParse(decodeJwtClaims(token)); + if (!parsed.success) return undefined; + return parsed.data.email || undefined; +} diff --git a/vscode-extension/src/auth/remoteEnvTokenMint.test.ts b/vscode-extension/src/auth/remoteEnvTokenMint.test.ts new file mode 100644 index 0000000000..a5475fde8f --- /dev/null +++ b/vscode-extension/src/auth/remoteEnvTokenMint.test.ts @@ -0,0 +1,96 @@ +import assert from "node:assert/strict"; +import { afterEach, test } from "node:test"; + +import { cookieAwareMint } from "./remoteEnvTokenMint.ts"; + +interface RecordedRequest { + url: string; + headers: Record; + body: unknown; +} + +const originalFetch = globalThis.fetch; + +afterEach(() => { + globalThis.fetch = originalFetch; +}); + +function loginPage(): Response { + return new Response("login", { + status: 200, + headers: { "content-type": "text/html" }, + }); +} + +function tokenJson(): Response { + return new Response( + JSON.stringify({ + token: "scoped-token", + environmentId: "env-1", + expiresAt: "2026-10-23T00:00:00Z", + }), + { status: 200, headers: { "content-type": "application/json" } }, + ); +} + +test("refreshes the cookie and retries when the gate returns a login page", async () => { + const requests: RecordedRequest[] = []; + const forceRefresh: boolean[] = []; + let call = 0; + globalThis.fetch = async (input, init) => { + requests.push({ + url: String(input), + headers: Object.fromEntries(new Headers(init?.headers).entries()), + body: typeof init?.body === "string" ? JSON.parse(init.body) : undefined, + }); + call += 1; + return call === 1 ? loginPage() : tokenJson(); + }; + const cookieProvider = async (options?: { forceRefresh?: boolean }) => { + forceRefresh.push(Boolean(options?.forceRefresh)); + return options?.forceRefresh + ? "MINERVA_TOKEN=fresh" + : "MINERVA_TOKEN=stale"; + }; + + const token = await cookieAwareMint(cookieProvider)({ + shellUrl: "http://shell.test/", + selector: "projectId:p1", + email: "dev@localhost", + }); + + assert.deepEqual(token, { + token: "scoped-token", + environmentId: "env-1", + expiresAtMs: Date.parse("2026-10-23T00:00:00Z"), + }); + assert.equal(requests.length, 2); + assert.equal(requests[0].url, "http://shell.test/api/embed/remote-env-token"); + assert.equal(requests[0].headers.cookie, "MINERVA_TOKEN=stale"); + assert.equal(requests[1].headers.cookie, "MINERVA_TOKEN=fresh"); + assert.deepEqual(requests[1].body, { selector: "projectId:p1" }); + assert.deepEqual(forceRefresh, [false, true]); +}); + +test("passes the cookie on the first try when the gate accepts it", async () => { + const requests: RecordedRequest[] = []; + globalThis.fetch = async (input, init) => { + requests.push({ + url: String(input), + headers: Object.fromEntries(new Headers(init?.headers).entries()), + body: undefined, + }); + return tokenJson(); + }; + + const token = await cookieAwareMint(async () => "MINERVA_TOKEN=ok")({ + shellUrl: "http://shell.test", + selector: "projectId:p1", + email: "dev@localhost", + environmentId: "env-pinned", + }); + + assert.equal(token.environmentId, "env-1"); + assert.equal(requests.length, 1); + assert.equal(requests[0].headers.cookie, "MINERVA_TOKEN=ok"); +}); diff --git a/vscode-extension/src/auth/remoteEnvTokenMint.ts b/vscode-extension/src/auth/remoteEnvTokenMint.ts new file mode 100644 index 0000000000..448e7afa83 --- /dev/null +++ b/vscode-extension/src/auth/remoteEnvTokenMint.ts @@ -0,0 +1,90 @@ +import { + createUnsignedJwt, + type mintRemoteEnvToken, + type MintRemoteEnvTokenParams, + type ScopedEnvToken, +} from "@tangent/local-agent-host/remoteEnvToken.ts"; +import type { + RemoteEnvTokenRequest, + RemoteEnvTokenResponse, +} from "@tangent/shared/contracts.ts"; + +import type { CookieProvider } from "./oktaSsoAuth.ts"; + +const REMOTE_ENV_TOKEN_PATH = "/api/embed/remote-env-token"; + +/** + * Mints the Shell's `/remote-env` token with the OktaSSO cookie attached. The + * stock {@link mintRemoteEnvToken} sends no cookie, so behind the SSO gate the + * request lands on the HTML login page and JSON parsing fails. We send the + * minted cookie, and when the gate still rejects us (a 401 or a login page in + * place of JSON) we refresh the cookie once and retry — the first attempt with + * no cached token is what opens the browser flow. + */ +export function cookieAwareMint( + cookieProvider: CookieProvider, +): typeof mintRemoteEnvToken { + return async (params) => { + const cookie = await cookieProvider(); + const first = await requestToken(params, cookie); + if (isAuthenticatedJson(first)) return parseToken(first); + + const refreshed = await cookieProvider({ forceRefresh: true }); + const second = await requestToken(params, refreshed); + return parseToken(second); + }; +} + +function requestToken( + params: MintRemoteEnvTokenParams, + cookie: string | undefined, +): Promise { + const url = `${params.shellUrl.replace(/\/+$/, "")}${REMOTE_ENV_TOKEN_PATH}`; + const body: RemoteEnvTokenRequest = { + selector: params.selector, + ...(params.environmentId ? { environmentId: params.environmentId } : {}), + }; + const headers: Record = { + "Content-Type": "application/json", + Authorization: `Bearer ${createUnsignedJwt({ email: params.email })}`, + }; + if (cookie) headers.Cookie = cookie; + return fetch(url, { method: "POST", headers, body: JSON.stringify(body) }); +} + +function isAuthenticatedJson(response: Response): boolean { + return ( + response.ok && + (response.headers.get("content-type") ?? "").includes("application/json") + ); +} + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null; +} + +function isResponseBody(value: unknown): value is RemoteEnvTokenResponse { + return ( + isRecord(value) && + typeof value.token === "string" && + typeof value.environmentId === "string" + ); +} + +async function parseToken(response: Response): Promise { + if (!response.ok) { + throw new Error( + `Failed to mint remote-env token (${response.status} ${response.statusText}).`, + ); + } + const parsed: unknown = await response.json(); + if (!isResponseBody(parsed)) { + throw new Error("Remote-env token response was malformed."); + } + const expiresAtMs = Date.parse(parsed.expiresAt); + return { + token: parsed.token, + environmentId: parsed.environmentId, + ...(Number.isNaN(expiresAtMs) ? {} : { expiresAtMs }), + }; +} diff --git a/vscode-extension/src/autoUpdate.ts b/vscode-extension/src/autoUpdate.ts new file mode 100644 index 0000000000..ace5088a55 --- /dev/null +++ b/vscode-extension/src/autoUpdate.ts @@ -0,0 +1,171 @@ +import { mkdtemp, rm } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import path from "node:path"; + +import * as vscode from "vscode"; + +import type { ExtensionConfig } from "./config.ts"; +import { errorMessage } from "./prompts.ts"; +import { + type ExtensionManifest, + isExtensionManifest, + isNewerVersion, + isTrustedUpdateUrl, + manifestUrl, + resolveVsixUrl, +} from "./updateManifest.ts"; + +const SKIPPED_VERSION_KEY = "tangent.skippedUpdateVersion"; +const STARTUP_CHECK_DELAY_MS = 5000; + +function currentVersion(context: vscode.ExtensionContext): string { + const version: unknown = context.extension.packageJSON?.version; + return typeof version === "string" ? version : "0.0.0"; +} + +async function fetchManifest(baseUrl: string): Promise { + const url = manifestUrl(baseUrl); + const response = await fetch(url); + if (!response.ok) { + throw new Error( + `The update server returned ${response.status} for ${url}.`, + ); + } + const data: unknown = await response.json().catch(() => undefined); + if (!isExtensionManifest(data)) { + throw new Error(`The update manifest at ${url} was not readable.`); + } + return data; +} + +async function downloadVsix(url: string): Promise { + const response = await fetch(url); + if (!response.ok) { + throw new Error( + `Could not download the extension: ${response.status} ${response.statusText}`, + ); + } + const bytes = new Uint8Array(await response.arrayBuffer()); + const dir = await mkdtemp(path.join(tmpdir(), "tangent-local-agent-")); + const file = vscode.Uri.file(path.join(dir, "tangent-local-agent.vsix")); + await vscode.workspace.fs.writeFile(file, bytes); + return file; +} + +async function installUpdate( + manifest: ExtensionManifest, + baseUrl: string, +): Promise { + const vsixUrl = resolveVsixUrl(baseUrl, manifest.url); + if (!isTrustedUpdateUrl(vsixUrl)) { + throw new Error( + "Refusing to install an update over an insecure connection. Set tangent.updateBaseUrl to an https:// URL.", + ); + } + const vsixUri = await downloadVsix(vsixUrl); + try { + await vscode.commands.executeCommand( + "workbench.extensions.installExtension", + vsixUri, + ); + } finally { + await rm(path.dirname(vsixUri.fsPath), { recursive: true, force: true }); + } + + const reload = await vscode.window.showInformationMessage( + `Tangent Local Agent ${manifest.version} installed.`, + "Reload Window", + ); + if (reload === "Reload Window") { + await vscode.commands.executeCommand("workbench.action.reloadWindow"); + } +} + +async function loadManifest( + baseUrl: string, + interactive: boolean, +): Promise { + try { + return await fetchManifest(baseUrl); + } catch (err) { + if (interactive) { + vscode.window.showErrorMessage( + `Could not check for updates: ${errorMessage(err)}`, + ); + } + return null; + } +} + +function isSkipped( + context: vscode.ExtensionContext, + interactive: boolean, + version: string, +): boolean { + return ( + !interactive && + context.globalState.get(SKIPPED_VERSION_KEY) === version + ); +} + +async function offerUpdate( + context: vscode.ExtensionContext, + config: ExtensionConfig, + manifest: ExtensionManifest, + current: string, +): Promise { + const choice = await vscode.window.showInformationMessage( + `Tangent Local Agent ${manifest.version} is available (you have ${current}).`, + "Update", + "Skip This Version", + ); + if (choice === "Update") { + try { + await installUpdate(manifest, config.updateBaseUrl); + } catch (err) { + vscode.window.showErrorMessage(`Update failed: ${errorMessage(err)}`); + } + } else if (choice === "Skip This Version") { + await context.globalState.update(SKIPPED_VERSION_KEY, manifest.version); + } +} + +function notifyUpToDate(interactive: boolean, current: string): void { + if (interactive) { + vscode.window.showInformationMessage( + `Tangent Local Agent is up to date (${current}).`, + ); + } +} + +export async function checkForUpdate( + context: vscode.ExtensionContext, + config: ExtensionConfig, + options: { interactive?: boolean } = {}, +): Promise { + const interactive = options.interactive ?? false; + const current = currentVersion(context); + const manifest = await loadManifest(config.updateBaseUrl, interactive); + + if (!manifest) return; + + if (!isNewerVersion(manifest.version, current)) { + notifyUpToDate(interactive, current); + return; + } + + if (isSkipped(context, interactive, manifest.version)) return; + + await offerUpdate(context, config, manifest, current); +} + +export function scheduleUpdateCheck( + context: vscode.ExtensionContext, + config: ExtensionConfig, +): void { + if (!config.autoCheckUpdates) return; + const timer = setTimeout(() => { + void checkForUpdate(context, config); + }, STARTUP_CHECK_DELAY_MS); + context.subscriptions.push(new vscode.Disposable(() => clearTimeout(timer))); +} diff --git a/vscode-extension/src/config.ts b/vscode-extension/src/config.ts new file mode 100644 index 0000000000..035c9cb9b8 --- /dev/null +++ b/vscode-extension/src/config.ts @@ -0,0 +1,175 @@ +import { randomUUID } from "node:crypto"; + +import type { PiLaunchOptions } from "@tangent/local-agent-host/piAdapter.ts"; +import { DEFAULT_USER } from "@tangent/shared/contracts.ts"; +import * as vscode from "vscode"; + +const SECTION = "tangent"; +const PROXY_KEY_SECRET = "tangent.piProxyApiKey"; +const TANGLE_API_TOKEN_SECRET = "tangent.tangleApiToken"; +const DEFAULT_TANGLE_API_URL = "http://localhost:8000"; +const DEFAULT_TANGLE_UI_URL = "http://localhost:3000"; +const HOST_ID_KEY = "tangent.hostId"; +const ACTIVE_SESSION_KEY = "tangent.activeSessionId"; +const CURRENT_PROJECT_KEY = "tangle.currentProject"; + +export interface CurrentProject { + id: string; + name: string; +} + +function stringSetting(key: string): string | undefined { + const value = vscode.workspace + .getConfiguration(SECTION) + .get(key) + ?.trim(); + return value || undefined; +} + +/** + * Settings, secrets, and per-workspace identity for the extension. The host id + * and active session live in workspace state, so each VS Code window is its own + * stable remote environment rather than several windows fighting over one id. + */ +export class ExtensionConfig { + private readonly context: vscode.ExtensionContext; + + constructor(context: vscode.ExtensionContext) { + this.context = context; + } + + get shellUrl(): string | undefined { + return stringSetting("shellUrl"); + } + + async setShellUrl(url: string): Promise { + await vscode.workspace + .getConfiguration(SECTION) + .update("shellUrl", url, vscode.ConfigurationTarget.Global); + } + + get autoConnect(): boolean { + return ( + vscode.workspace.getConfiguration(SECTION).get("autoConnect") ?? + true + ); + } + + get defaultBundleId(): string | undefined { + return stringSetting("defaultBundleId"); + } + + /** + * The identity this window mints scoped `/remote-env` tokens as. The Shell + * routes a session's spawns and tool calls to the prompting person's hosts, + * so this must match the email you chat as; defaults to the dev identity. + */ + get userEmail(): string { + return stringSetting("userEmail") ?? DEFAULT_USER.email; + } + + async setPiProxyApiKey(key: string): Promise { + await this.context.secrets.store(PROXY_KEY_SECRET, key); + } + + get tangleApiUrl(): string { + return stringSetting("tangleApiUrl") ?? DEFAULT_TANGLE_API_URL; + } + + async setTangleApiUrl(url: string): Promise { + await vscode.workspace + .getConfiguration(SECTION) + .update("tangleApiUrl", url, vscode.ConfigurationTarget.Global); + } + + get tangleUiUrl(): string { + return (stringSetting("tangleUiUrl") ?? DEFAULT_TANGLE_UI_URL).replace( + /\/+$/, + "", + ); + } + + get updateBaseUrl(): string { + return (stringSetting("updateBaseUrl") ?? this.tangleUiUrl).replace( + /\/+$/, + "", + ); + } + + get autoCheckUpdates(): boolean { + return ( + vscode.workspace + .getConfiguration(SECTION) + .get("autoCheckUpdates") ?? true + ); + } + + async tangleApiToken(): Promise { + return this.context.secrets.get(TANGLE_API_TOKEN_SECRET); + } + + async setTangleApiToken(token: string): Promise { + await this.context.secrets.store(TANGLE_API_TOKEN_SECRET, token); + } + + get activeSessionId(): string | undefined { + return this.context.workspaceState.get(ACTIVE_SESSION_KEY); + } + + async setActiveSessionId(sessionId: string): Promise { + await this.context.workspaceState.update(ACTIVE_SESSION_KEY, sessionId); + } + + get currentProject(): CurrentProject | undefined { + return this.context.workspaceState.get(CURRENT_PROJECT_KEY); + } + + async setCurrentProject(project: CurrentProject): Promise { + await this.context.workspaceState.update(CURRENT_PROJECT_KEY, project); + } + + /** + * The id this window asks to keep across token refreshes, so a refresh reuses + * the same scoped host instead of connecting as a new environment. The Shell + * honors the pin when it is free, so the minted id is authoritative. The + * project is part of the id so switching projects connects as a distinct + * environment — the gateway drops the old one cleanly rather than overwriting + * its selector in place. + */ + async environmentId(projectId: string): Promise { + return `vscode-${await this.hostId()}-${projectId}`; + } + + /** How local pi sub-agents are launched, resolved fresh for each spawn. */ + async piLaunchOptions(): Promise { + const proxyUrl = stringSetting("piProxyUrl"); + const proxyApiKey = + (await this.context.secrets.get(PROXY_KEY_SECRET)) ?? + process.env.PI_PROXY_API_KEY; + return { + bin: stringSetting("piBin") ?? "pi", + cwd: vscode.workspace.workspaceFolders?.[0]?.uri.fsPath, + model: stringSetting("piModel"), + proxyUrl, + proxyApiKey: proxyUrl ? proxyApiKey : undefined, + proxyExtensionPath: vscode.Uri.joinPath( + this.context.extensionUri, + "dist", + "proxyProvider.ts", + ).fsPath, + sessionResourceExtensionPath: vscode.Uri.joinPath( + this.context.extensionUri, + "dist", + "sessionResources.ts", + ).fsPath, + }; + } + + private async hostId(): Promise { + const existing = this.context.workspaceState.get(HOST_ID_KEY); + if (existing) return existing; + const created = randomUUID(); + await this.context.workspaceState.update(HOST_ID_KEY, created); + return created; + } +} diff --git a/vscode-extension/src/controller.ts b/vscode-extension/src/controller.ts new file mode 100644 index 0000000000..5389fd4a1a --- /dev/null +++ b/vscode-extension/src/controller.ts @@ -0,0 +1,465 @@ +import { + type HostedAgent, + LocalAgentBridge, +} from "@tangent/local-agent-host/bridge.ts"; +import { createFileTools } from "@tangent/local-agent-host/remoteFileTools.ts"; +import { startScopedConnection } from "@tangent/local-agent-host/scopedConnection.ts"; +import { + normalizeShellUrl, + sessionIdFromUrl, +} from "@tangent/local-agent-host/shellUrl.ts"; +import { connectRemoteEnvironment } from "@tangent/remote-subagent"; +import { + io, + type ManagerOptions, + type Socket, + type SocketOptions, +} from "socket.io-client"; +import * as vscode from "vscode"; + +import type { OktaSsoAuthProvider } from "./auth/oktaSsoAuth.ts"; +import { cookieAwareMint } from "./auth/remoteEnvTokenMint.ts"; +import type { ExtensionConfig } from "./config.ts"; +import { EarlyMessageBuffer } from "./earlyMessageBuffer.ts"; +import { projectSelector } from "./projectSelector.ts"; +import { askProxyApiKey, askShellUrl, errorMessage } from "./prompts.ts"; +import { StatusView } from "./statusView.ts"; +import { TangentClient } from "./tangentClient.ts"; + +interface MenuAction extends vscode.QuickPickItem { + run(): unknown; +} + +function runCommand(command: string): () => Thenable { + return () => vscode.commands.executeCommand(command); +} + +function windowDescription( + appName: string, + windowName: string | undefined, + workspacePath: string | undefined, +): string { + const parts = [appName]; + if (windowName) parts.push(`— ${windowName}`); + if (workspacePath) parts.push(`in ${workspacePath}`); + return parts.join(" "); +} + +/** + * Socket.IO treats its URL as an origin and takes the path separately, so a + * Shell mounted under a prefix must move that prefix into `socketPath`; + * otherwise the `/remote-env` transport drops it and hits the host root. + */ +function shellSocketConfig(shellUrl: string): { + socketUrl: string; + socketPath: string; +} { + const url = new URL(shellUrl); + const prefix = url.pathname.replace(/\/+$/, ""); + return { socketUrl: url.origin, socketPath: `${prefix}/socket.io` }; +} + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null; +} + +function describeDetail(value: unknown): string { + if (typeof value === "number" || typeof value === "string") { + return String(value); + } + if (value instanceof Error) return value.message; + try { + return JSON.stringify(value); + } catch { + return String(value); + } +} + +/** + * socket.io reports a failed handshake as the opaque `websocket error`; the + * cause lives on `description` (an HTTP status or the underlying error) and + * `context`. Surfacing both turns a dead-end log into one that names a login + * redirect, a 401, or a wrong transport path. + */ +function connectErrorDetail(error: unknown): string { + if (!isRecord(error)) return String(error); + const parts: string[] = []; + if (typeof error.message === "string") parts.push(error.message); + if (error.description !== undefined) { + parts.push(`description=${describeDetail(error.description)}`); + } + if (error.context !== undefined) { + parts.push(`context=${describeDetail(error.context)}`); + } + return parts.join(" "); +} + +/** Host identity this window advertises so a viewer knows which editor is connected. */ +function windowMetadata(): Record { + const appName = vscode.env.appName; + const windowName = vscode.workspace.name; + const workspacePath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath; + const metadata: Record = { + editor: appName, + capabilities: + "runs local pi sub-agents that edit files + run the terminal in this workspace", + description: windowDescription(appName, windowName, workspacePath), + }; + if (windowName) metadata.windowName = windowName; + if (workspacePath) metadata.workspacePath = workspacePath; + return metadata; +} + +/** + * Owns the live connection to the Shell: the `/remote-env` bridge that hosts + * local pi sub-agents and file tools, and the session REST client. The bridge is + * scoped to one project at a time, so every session in that project can reach it; + * a different window can serve a different project. The status bar reflects its + * state. + */ +export class TangentController implements vscode.Disposable { + private readonly config: ExtensionConfig; + private readonly logChannel = vscode.window.createOutputChannel( + "Tangent Local Agent", + ); + private readonly status = new StatusView(); + private readonly attached = new vscode.EventEmitter(); + readonly onDidAttach = this.attached.event; + private bridge: LocalAgentBridge | undefined; + private client: TangentClient | undefined; + private connectedShellUrl: string | undefined; + private stopConnection: (() => void) | undefined; + private socketCookie: string | undefined; + private connectGeneration = 0; + private readonly oktaSso: OktaSsoAuthProvider | undefined; + private readonly earlyMessages = new EarlyMessageBuffer((line) => + this.log(line), + ); + + constructor(config: ExtensionConfig, oktaSso?: OktaSsoAuthProvider) { + this.config = config; + this.oktaSso = oktaSso; + this.renderStatus(); + } + + get shellUrl(): string | undefined { + return this.connectedShellUrl; + } + + log(line: string): void { + this.logChannel.appendLine(line); + } + + autoConnect(): void { + const url = this.config.shellUrl; + if (!this.config.autoConnect || !url) return; + this.start(url); + } + + async connect(prefillUrl?: string): Promise { + const input = await askShellUrl( + prefillUrl ?? this.config.shellUrl ?? "http://localhost:5173", + ); + if (!input) return; + await this.finishConnect(input); + } + + /** Connects to a deep link's Shell URL, already confirmed by the caller. */ + async connectFromLink(url: string): Promise { + await this.finishConnect(url); + } + + private async finishConnect(input: string): Promise { + const url = normalizeShellUrl(input); + await this.config.setShellUrl(url); + const sessionId = sessionIdFromUrl(input); + if (sessionId) await this.config.setActiveSessionId(sessionId); + this.start(url); + } + + disconnect(): void { + this.teardownBridge(); + this.client = undefined; + this.connectedShellUrl = undefined; + this.renderStatus(); + } + + private teardownBridge(): void { + this.connectGeneration++; + this.stopConnection?.(); + this.stopConnection = undefined; + this.bridge?.dispose(); + this.bridge = undefined; + this.earlyMessages.reset(); + } + + async requireClient(): Promise { + if (!this.client) await this.connect(); + return this.client; + } + + /** + * Marks `sessionId` as this window's active session for the chat panel, status + * bar, and Sessions tree. The remote-env bridge is project-scoped, so it is + * left as is: one connection already serves every session in the project. + */ + async attach(sessionId: string): Promise { + await this.config.setActiveSessionId(sessionId); + this.renderStatus(); + this.attached.fire(sessionId); + } + + /** + * Rebinds the remote-env host to the current project, reconnecting the bridge + * with a token minted for the new project's selector. Called when the window's + * project changes; a window with no project tears the connection down. + */ + reconnectForCurrentProject(): void { + if (!this.connectedShellUrl) return; + const project = this.config.currentProject; + if (!project) { + this.teardownBridge(); + this.renderStatus(); + return; + } + void this.connectProject(project.id); + } + + async setPiProxyApiKey(): Promise { + const key = await askProxyApiKey(); + if (key) await this.config.setPiProxyApiKey(key); + } + + async showMenu(): Promise { + const picked = await vscode.window.showQuickPick(this.menuActions(), { + title: "Tangent Local Agent", + }); + await picked?.run(); + } + + dispose(): void { + this.disconnect(); + this.status.dispose(); + this.attached.dispose(); + this.logChannel.dispose(); + } + + private start(url: string): void { + this.disconnect(); + this.connectedShellUrl = url; + this.client = new TangentClient(url, this.oktaSso?.cookieProviderFor(url)); + const project = this.config.currentProject; + if (project) void this.connectProject(project.id); + this.renderStatus(); + } + + /** + * (Re)connects the bridge scoped to `projectId`: a fresh bridge whose token the + * gateway routes to every session tagged for the project, kept alive by + * re-minting before the token expires. Replaces any previous project's + * connection. + */ + private async connectProject(projectId: string): Promise { + const url = this.connectedShellUrl; + if (!url) return; + const generation = ++this.connectGeneration; + const initialEnvironmentId = await this.config.environmentId(projectId); + const email = await this.resolveUserEmail(url); + if (!this.isCurrentConnection(generation, url)) return; + + this.stopConnection?.(); + this.bridge?.dispose(); + const bridge = this.createBridge(url, projectId); + this.bridge = bridge; + const onError = (message: string) => { + this.log(`remote-env token: ${message}`); + this.renderStatus(); + }; + this.stopConnection = startScopedConnection({ + shellUrl: url, + selector: projectSelector(projectId), + email, + initialEnvironmentId, + ...this.scopedMint(url), + connect: (token, environmentId) => + void this.connectBridge( + { bridge, generation, onError }, + url, + token, + environmentId, + ), + onError, + }); + this.renderStatus(); + } + + /** + * A connect attempt that outlived a disconnect or a project switch must not + * revive a bridge the user has already torn down: every teardown bumps the + * generation, so an attempt whose captured generation no longer matches bails. + */ + private isCurrentConnection(generation: number, url: string): boolean { + return ( + this.connectGeneration === generation && this.connectedShellUrl === url + ); + } + + /** + * Refreshes the SSO cookie the socket handshake carries, then (re)connects the + * bridge. Node's socket.io does not send cookies on its own, so without this + * the `/remote-env` WebSocket upgrade reaches the SSO gate unauthenticated and + * fails as an opaque `websocket error`, even though the REST and mint paths — + * which set `Cookie` themselves — succeed. + */ + private async connectBridge( + attempt: { + bridge: LocalAgentBridge; + generation: number; + onError: (message: string) => void; + }, + url: string, + token: string, + environmentId: string, + ): Promise { + const { bridge, generation, onError } = attempt; + try { + this.socketCookie = this.oktaSso + ? await this.oktaSso.cookieProviderFor(url)() + : undefined; + } catch (err) { + onError(`socket cookie: ${errorMessage(err)}`); + return; + } + if (this.connectGeneration !== generation || this.bridge !== bridge) return; + bridge.connect(token, environmentId); + } + + /** + * The identity this window mints as: the real OktaSSO user when the gate is + * on, otherwise the configured dev identity. + */ + async resolveUserEmail(audienceUrl: string): Promise { + const okta = this.oktaSso + ? await this.oktaSso.getAuthenticatedEmail(audienceUrl) + : undefined; + return okta ?? this.config.userEmail; + } + + /** The SSO-cookie mint for this Shell, or nothing when the gate is off. */ + private scopedMint(url: string): { + mint?: ReturnType; + } { + const cookieProvider = this.oktaSso?.cookieProviderFor(url); + return cookieProvider ? { mint: cookieAwareMint(cookieProvider) } : {}; + } + + private createBridge(url: string, projectId: string): LocalAgentBridge { + const metadata = windowMetadata(); + const { socketUrl, socketPath } = shellSocketConfig(url); + return new LocalAgentBridge( + { + url: socketUrl, + selector: projectSelector(projectId), + tools: createFileTools( + () => vscode.workspace.workspaceFolders?.[0]?.uri.fsPath, + ), + piLaunchOptions: () => this.config.piLaunchOptions(), + log: (line) => this.log(line), + onChange: () => this.renderStatus(), + onSpawned: (agent) => void this.notifySpawned(agent), + }, + // The bridge's connect factory is the only seam to advertise this window's + // identity, pin the mounted-prefix transport path, and carry the SSO cookie + // that Node's socket.io does not send on the WebSocket handshake. + (options) => this.openRemoteEnv(options, metadata, socketPath), + ); + } + + private openRemoteEnv( + options: Parameters[0], + metadata: Record, + socketPath: string, + ): ReturnType { + const client = connectRemoteEnvironment( + { + ...options, + handlers: this.earlyMessages.wrap(options.handlers), + socketPath, + metadata, + }, + (uri, opts) => io(uri, this.withCookie(opts)), + ); + this.watchSocket(client.socket); + return client; + } + + private withCookie( + opts: Partial, + ): Partial { + if (!this.socketCookie) return opts; + return { + ...opts, + extraHeaders: { ...opts.extraHeaders, Cookie: this.socketCookie }, + }; + } + + private watchSocket(socket: Socket): void { + socket.on("connect_error", (error: Error) => { + const transport = socket.io.engine?.transport?.name ?? "unknown"; + const cookie = this.socketCookie ? "yes" : "no"; + this.log( + `remote-env connect_error (transport=${transport}, cookie=${cookie}): ${connectErrorDetail(error)}`, + ); + }); + } + + private async notifySpawned(agent: HostedAgent): Promise { + this.earlyMessages.markReady(agent.agentId); + const choice = await vscode.window.showInformationMessage( + `Tangent started local sub-agent "${agent.name}".`, + "Stop", + "Show Log", + ); + if (choice === "Stop") this.bridge?.stopAgent(agent.agentId); + if (choice === "Show Log") this.logChannel.show(true); + } + + private menuActions(): MenuAction[] { + if (!this.bridge) { + return [ + { label: "$(plug) Connect Local Agent", run: () => this.connect() }, + { label: "$(gear) Settings", run: () => this.openSettings() }, + ]; + } + const agents = this.bridge.listAgents().map((agent) => ({ + label: `$(debug-stop) Stop "${agent.name}"`, + run: () => this.bridge?.stopAgent(agent.agentId), + })); + return [ + { label: "$(add) New Session", run: runCommand("tangle.newSession") }, + { + label: "$(link) Attach to Session", + run: runCommand("tangent.attachSession"), + }, + ...agents, + { label: "$(output) Show Log", run: () => this.logChannel.show() }, + { label: "$(debug-disconnect) Disconnect", run: () => this.disconnect() }, + ]; + } + + private openSettings(): Thenable { + return vscode.commands.executeCommand( + "workbench.action.openSettings", + "@ext:tangent.tangent-local-agent", + ); + } + + private renderStatus(): void { + this.status.render({ + bridge: this.bridge, + shellUrl: this.connectedShellUrl, + project: this.config.currentProject?.name, + sessionId: this.config.activeSessionId, + }); + } +} diff --git a/vscode-extension/src/currentProject.ts b/vscode-extension/src/currentProject.ts new file mode 100644 index 0000000000..56233b4f52 --- /dev/null +++ b/vscode-extension/src/currentProject.ts @@ -0,0 +1,44 @@ +import * as vscode from "vscode"; + +import type { CurrentProject, ExtensionConfig } from "./config.ts"; + +const HAS_PROJECT_CONTEXT = "tangle.hasProject"; + +/** + * The project the Tangle views are scoped to. Persisted per workspace so each + * window can follow a different project. + */ +export class CurrentProjectState implements vscode.Disposable { + private readonly config: ExtensionConfig; + private readonly changed = new vscode.EventEmitter< + CurrentProject | undefined + >(); + readonly onDidChange = this.changed.event; + + constructor(config: ExtensionConfig) { + this.config = config; + void this.syncContextKey(); + } + + get project(): CurrentProject | undefined { + return this.config.currentProject; + } + + async select(project: CurrentProject): Promise { + await this.config.setCurrentProject(project); + await this.syncContextKey(); + this.changed.fire(project); + } + + dispose(): void { + this.changed.dispose(); + } + + private async syncContextKey(): Promise { + await vscode.commands.executeCommand( + "setContext", + HAS_PROJECT_CONTEXT, + Boolean(this.project), + ); + } +} diff --git a/vscode-extension/src/earlyMessageBuffer.test.ts b/vscode-extension/src/earlyMessageBuffer.test.ts new file mode 100644 index 0000000000..b725647f5b --- /dev/null +++ b/vscode-extension/src/earlyMessageBuffer.test.ts @@ -0,0 +1,124 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; + +import type { + RemoteKillCommand, + RemoteMessageCommand, +} from "@tangent/remote-subagent"; + +import { EarlyMessageBuffer } from "./earlyMessageBuffer.ts"; + +function message(agentId: string, text: string): RemoteMessageCommand { + return { sessionId: "s1", agentId, text, delivery: "auto" }; +} + +function kill(agentId: string): RemoteKillCommand { + return { sessionId: "s1", agentId, completed: false }; +} + +interface Harness { + buffer: EarlyMessageBuffer; + delivered: RemoteMessageCommand[]; + killed: RemoteKillCommand[]; + onMessage: (command: RemoteMessageCommand) => void; + onKill: (command: RemoteKillCommand) => void; + advance: (ms: number) => void; +} + +function harness( + options: { ttlMs?: number; maxMessages?: number } = {}, +): Harness { + const delivered: RemoteMessageCommand[] = []; + const killed: RemoteKillCommand[] = []; + let clock = 0; + const buffer = new EarlyMessageBuffer(() => {}, { + ...options, + now: () => clock, + }); + const wrapped = buffer.wrap({ + onSpawn: () => {}, + onMessage: (command) => { + delivered.push(command); + }, + onKill: (command) => { + killed.push(command); + }, + }); + const onMessage = wrapped.onMessage; + const onKill = wrapped.onKill; + if (!onMessage || !onKill) { + throw new Error("wrap must return onMessage and onKill handlers"); + } + return { + buffer, + delivered, + killed, + onMessage: (command) => void onMessage(command), + onKill: (command) => void onKill(command), + advance: (ms) => { + clock += ms; + }, + }; +} + +test("buffers a message for a not-yet-ready agent instead of delivering it", () => { + const h = harness(); + h.onMessage(message("a1", "hello")); + assert.deepEqual(h.delivered, []); +}); + +test("flushes buffered messages in arrival order once the agent is ready", () => { + const h = harness(); + h.onMessage(message("a1", "first")); + h.onMessage(message("a1", "second")); + h.buffer.markReady("a1"); + assert.deepEqual( + h.delivered.map((command) => command.text), + ["first", "second"], + ); +}); + +test("delivers straight through after the agent is ready", () => { + const h = harness(); + h.buffer.markReady("a1"); + h.onMessage(message("a1", "live")); + assert.deepEqual( + h.delivered.map((command) => command.text), + ["live"], + ); +}); + +test("discards buffered messages after the TTL with no spawn", () => { + const h = harness({ ttlMs: 1000 }); + h.onMessage(message("ghost", "orphan")); + h.advance(1001); + h.buffer.sweep(); + h.buffer.markReady("ghost"); + assert.deepEqual(h.delivered, []); +}); + +test("drops the oldest message when the global cap is exceeded", () => { + const h = harness({ maxMessages: 2 }); + h.onMessage(message("a1", "1")); + h.onMessage(message("a2", "2")); + h.onMessage(message("a3", "3")); + h.buffer.markReady("a1"); + h.buffer.markReady("a2"); + h.buffer.markReady("a3"); + assert.deepEqual( + h.delivered.map((command) => command.text), + ["2", "3"], + ); +}); + +test("onKill clears the agent's buffer and still forwards the kill", () => { + const h = harness(); + h.onMessage(message("a1", "stale")); + h.onKill(kill("a1")); + h.buffer.markReady("a1"); + assert.deepEqual(h.delivered, []); + assert.deepEqual( + h.killed.map((command) => command.agentId), + ["a1"], + ); +}); diff --git a/vscode-extension/src/earlyMessageBuffer.ts b/vscode-extension/src/earlyMessageBuffer.ts new file mode 100644 index 0000000000..e74808b5b2 --- /dev/null +++ b/vscode-extension/src/earlyMessageBuffer.ts @@ -0,0 +1,154 @@ +import type { + RemoteEnvironmentHandlers, + RemoteKillCommand, + RemoteMessageCommand, +} from "@tangent/remote-subagent"; + +const DEFAULT_TTL_MS = 5 * 60_000; +const DEFAULT_MAX_MESSAGES = 10; +const DEFAULT_SWEEP_INTERVAL_MS = 30_000; + +type MessageHandler = (command: RemoteMessageCommand) => void | Promise; +type KillHandler = (command: RemoteKillCommand) => void | Promise; + +interface BufferedMessage { + agentId: string; + command: RemoteMessageCommand; + enqueuedAt: number; +} + +export interface EarlyMessageBufferOptions { + ttlMs?: number; + maxMessages?: number; + sweepIntervalMs?: number; + now?: () => number; +} + +/** + * Holds messages addressed to a local sub-agent that is spawning but not yet + * live. The Shell can deliver the first `message` before the bridge has + * constructed the `PiAgent`, in which case the bridge would log + * `message for unknown sub-agent` and drop it. This buffers such messages, + * keyed by the server-assigned agentId, and replays them in arrival order once + * `markReady` reports the agent is running. Unclaimed ids are bounded by a TTL + * and a global cap so a never-spawned id cannot grow the buffer without bound. + */ +export class EarlyMessageBuffer { + private readonly log: (line: string) => void; + private readonly ttlMs: number; + private readonly maxMessages: number; + private readonly sweepIntervalMs: number; + private readonly now: () => number; + private readonly ready = new Set(); + private buffer: BufferedMessage[] = []; + private deliver: MessageHandler | undefined; + private sweepTimer: ReturnType | undefined; + + constructor( + log: (line: string) => void, + options: EarlyMessageBufferOptions = {}, + ) { + this.log = log; + this.ttlMs = options.ttlMs ?? DEFAULT_TTL_MS; + this.maxMessages = options.maxMessages ?? DEFAULT_MAX_MESSAGES; + this.sweepIntervalMs = options.sweepIntervalMs ?? DEFAULT_SWEEP_INTERVAL_MS; + this.now = options.now ?? (() => Date.now()); + } + + /** + * Wraps the bridge's handlers so inbound messages route through the buffer. + * Re-called on every (re)connect to track the live socket's `onMessage` while + * the buffered state persists across reconnects. + */ + wrap( + handlers: Partial | undefined, + ): Partial { + const delegate = handlers ?? {}; + this.deliver = delegate.onMessage; + this.startTimer(); + return { + ...delegate, + onMessage: (command) => this.onMessage(command), + onKill: (command) => this.onKill(delegate.onKill, command), + }; + } + + markReady(agentId: string): void { + this.ready.add(agentId); + const pending = this.buffer.filter((entry) => entry.agentId === agentId); + if (pending.length === 0) return; + this.buffer = this.buffer.filter((entry) => entry.agentId !== agentId); + this.log( + `flushing ${pending.length} buffered message(s) to sub-agent ${agentId}`, + ); + for (const entry of pending) void this.deliver?.(entry.command); + } + + sweep(): void { + const cutoff = this.now() - this.ttlMs; + const expired = this.buffer.filter((entry) => entry.enqueuedAt < cutoff); + if (expired.length === 0) return; + this.buffer = this.buffer.filter((entry) => entry.enqueuedAt >= cutoff); + for (const agentId of new Set(expired.map((entry) => entry.agentId))) { + this.log( + `discarded buffered message(s) for sub-agent ${agentId} after no spawn within ${this.ttlMs}ms`, + ); + } + } + + reset(): void { + this.ready.clear(); + this.buffer = []; + this.deliver = undefined; + this.stopTimer(); + } + + private onMessage(command: RemoteMessageCommand): void { + if (this.ready.has(command.agentId)) { + void this.deliver?.(command); + return; + } + this.enqueue(command); + } + + private onKill( + delegate: KillHandler | undefined, + command: RemoteKillCommand, + ): void { + this.ready.delete(command.agentId); + this.buffer = this.buffer.filter( + (entry) => entry.agentId !== command.agentId, + ); + void delegate?.(command); + } + + private enqueue(command: RemoteMessageCommand): void { + this.sweep(); + this.buffer.push({ + agentId: command.agentId, + command, + enqueuedAt: this.now(), + }); + if (this.buffer.length > this.maxMessages) { + const dropped = this.buffer.shift(); + if (dropped) { + this.log( + `early-message buffer full; dropped oldest message for sub-agent ${dropped.agentId}`, + ); + } + } + this.log(`buffered message for pending sub-agent ${command.agentId}`); + } + + private startTimer(): void { + if (this.sweepTimer) return; + this.sweepTimer = setInterval(() => this.sweep(), this.sweepIntervalMs); + this.sweepTimer.unref?.(); + } + + private stopTimer(): void { + if (!this.sweepTimer) return; + clearInterval(this.sweepTimer); + this.sweepTimer = undefined; + } +} diff --git a/vscode-extension/src/extension.ts b/vscode-extension/src/extension.ts new file mode 100644 index 0000000000..df033d4cd5 --- /dev/null +++ b/vscode-extension/src/extension.ts @@ -0,0 +1,283 @@ +import { normalizeShellUrl } from "@tangent/local-agent-host/shellUrl.ts"; +import * as vscode from "vscode"; + +import { OktaSsoAuthProvider } from "./auth/oktaSsoAuth.ts"; +import { isOktaSsoEnabled } from "./auth/oktaSsoConfig.ts"; +import { checkForUpdate, scheduleUpdateCheck } from "./autoUpdate.ts"; +import { ExtensionConfig } from "./config.ts"; +import { TangentController } from "./controller.ts"; +import { CurrentProjectState } from "./currentProject.ts"; +import { pickProject } from "./projectPicker.ts"; +import { ProjectSessions } from "./projectSessions.ts"; +import { createProjectViews, type ProjectViews } from "./projectViews.ts"; +import { + askTangleApiToken, + askTangleApiUrl, + confirmConnectLink, + showError, +} from "./prompts.ts"; +import { resourceWebPath } from "./resourceKinds.ts"; +import type { ResourceNode } from "./resourcesTree.ts"; +import type { RunNode } from "./runsTree.ts"; +import type { SessionNode } from "./sessionsTree.ts"; +import { normalizeBaseUrl, TangleRestClient } from "./tangleRestClient.ts"; +import { SessionChatPanels } from "./webview/chatPanel.ts"; + +type CommandMap = Record unknown>; + +interface Services { + config: ExtensionConfig; + currentProject: CurrentProjectState; + views: ProjectViews; + sessions: ProjectSessions; + createTangleClient(): Promise; +} + +function originOf(url: string): string { + return new URL(url).origin; +} + +/** The link's target origin, or `undefined` when it matches `current` or is absent. */ +function changedOrigin( + current: string | undefined, + incoming: string | null, + normalize: (value: string) => string, +): string | undefined { + if (!incoming) return undefined; + const origin = originOf(normalize(incoming)); + return origin === current ? undefined : origin; +} + +async function confirmLinkIfNeeded( + config: ExtensionConfig, + apiUrl: string | null, + url: string | null, +): Promise { + const apiOrigin = changedOrigin( + originOf(config.tangleApiUrl), + apiUrl, + normalizeBaseUrl, + ); + const savedShellOrigin = config.shellUrl + ? originOf(config.shellUrl) + : undefined; + const shellOrigin = changedOrigin(savedShellOrigin, url, normalizeShellUrl); + if (!apiOrigin && !shellOrigin) return true; + return confirmConnectLink(shellOrigin, apiOrigin); +} + +async function selectLinkProject( + services: Services, + query: URLSearchParams, +): Promise { + const projectId = query.get("projectId"); + if (!projectId) return; + const client = await services.createTangleClient(); + const project = await client.getProject(projectId); + await services.currentProject.select({ + id: project.id, + name: project.name, + }); +} + +/** + * Handles `vscode://tangent.tangent-local-agent/connect?url=` with + * optional `apiUrl` and `projectId`. A link that points at a Shell or Tangle + * API origin the window is not already connected to must be confirmed first, so + * a web page cannot silently repoint the editor at an attacker's Shell (whose + * local sub-agents run in the workspace) or leak the saved Tangle API token to + * an attacker's API origin. The project name is read from the backend rather + * than trusted from the link. + */ +async function applyConnectLink( + services: Services, + controller: TangentController, + query: URLSearchParams, +): Promise { + const apiUrl = query.get("apiUrl"); + const url = query.get("url"); + if (!(await confirmLinkIfNeeded(services.config, apiUrl, url))) return; + if (apiUrl) await services.config.setTangleApiUrl(normalizeBaseUrl(apiUrl)); + await selectLinkProject(services, query); + if (url) await controller.connectFromLink(url); +} + +function uriHandler( + services: Services, + controller: TangentController, +): vscode.UriHandler { + return { + handleUri(uri) { + if (uri.path !== "/connect") return; + const query = new URLSearchParams(uri.query); + applyConnectLink(services, controller, query).catch(showError); + }, + }; +} + +function openInTangleUi( + config: ExtensionConfig, + path: string, +): Thenable { + return vscode.env.openExternal( + vscode.Uri.parse(`${config.tangleUiUrl}${path}`), + ); +} + +async function selectProject(services: Services): Promise { + try { + const project = await pickProject( + await services.createTangleClient(), + services.currentProject.project?.id, + ); + if (project) await services.currentProject.select(project); + } catch (err) { + showError(err); + } +} + +async function openCurrentProject(services: Services): Promise { + const project = services.currentProject.project; + if (!project) return selectProject(services); + return openInTangleUi( + services.config, + `/projects/${encodeURIComponent(project.id)}`, + ); +} + +async function setTangleApiUrl(services: Services): Promise { + const { config } = services; + const input = await askTangleApiUrl(config.tangleApiUrl); + if (!input) return; + const token = await askTangleApiToken(await config.tangleApiToken()); + await config.setTangleApiUrl(normalizeBaseUrl(input)); + if (token) await config.setTangleApiToken(token); + services.views.refreshAll(); +} + +/** The cached name goes stale when the project is renamed elsewhere. */ +async function refreshProjectName(services: Services): Promise { + const project = services.currentProject.project; + if (!project) return; + try { + const client = await services.createTangleClient(); + const latest = await client.getProject(project.id); + if (latest.name === project.name) return; + await services.currentProject.select({ id: latest.id, name: latest.name }); + } catch { + // The views report an unreachable backend or a deleted project. + } +} + +function projectCommands(services: Services): CommandMap { + const { config, views, sessions } = services; + return { + "tangle.selectProject": () => selectProject(services), + "tangle.refresh": () => views.refreshAll(), + "tangle.setApiUrl": () => setTangleApiUrl(services), + "tangle.openProject": () => openCurrentProject(services), + "tangle.loadMoreRuns": () => views.runs.loadMore().catch(showError), + "tangle.openRun": (node: RunNode) => + openInTangleUi(config, `/runs/${encodeURIComponent(node.run.id)}`), + "tangle.openResource": (node: ResourceNode) => + openInTangleUi(config, resourceWebPath(node.projectId, node.resource)), + "tangle.newSession": () => sessions.newSession(), + "tangle.openSession": (node: SessionNode) => sessions.openSession(node), + "tangle.renameSession": (node: SessionNode) => sessions.renameSession(node), + "tangle.detachSession": (node: SessionNode) => sessions.detachSession(node), + }; +} + +function tangentCommands( + controller: TangentController, + sessions: ProjectSessions, +): CommandMap { + return { + "tangent.connect": () => controller.connect(), + "tangent.disconnect": () => controller.disconnect(), + "tangent.attachSession": () => sessions.attachSession(), + "tangent.setPiProxyApiKey": () => controller.setPiProxyApiKey(), + "tangent.showMenu": () => controller.showMenu(), + }; +} + +function registerCommands(commands: CommandMap): vscode.Disposable[] { + return Object.entries(commands).map(([id, run]) => + vscode.commands.registerCommand(id, run), + ); +} + +function tangleClientFactory( + config: ExtensionConfig, + oktaSso: OktaSsoAuthProvider | undefined, +): () => Promise { + return async () => + new TangleRestClient( + config.tangleApiUrl, + await config.tangleApiToken(), + oktaSso?.cookieProviderFor(config.tangleApiUrl), + ); +} + +export function activate(context: vscode.ExtensionContext): void { + const config = new ExtensionConfig(context); + const oktaSso = isOktaSsoEnabled() + ? new OktaSsoAuthProvider(context.secrets) + : undefined; + const controller = new TangentController(config, oktaSso); + const currentProject = new CurrentProjectState(config); + const createTangleClient = tangleClientFactory(config, oktaSso); + const views = createProjectViews({ + currentProject, + createClient: createTangleClient, + activeSessionId: () => config.activeSessionId, + }); + const chats = new SessionChatPanels( + context.extensionUri, + (line) => controller.log(line), + oktaSso + ? (url, options) => oktaSso.getCookieHeader(url, options) + : undefined, + ); + const sessions = new ProjectSessions({ + config, + controller, + currentProject, + createTangleClient, + chats, + sessionsTree: views.sessions, + }); + const services: Services = { + config, + currentProject, + views, + sessions, + createTangleClient, + }; + + context.subscriptions.push( + controller, + currentProject, + views, + chats, + vscode.window.registerUriHandler(uriHandler(services, controller)), + controller.onDidAttach(() => views.sessions.showActiveSessionChanged()), + currentProject.onDidChange(() => controller.reconnectForCurrentProject()), + vscode.workspace.onDidChangeConfiguration((event) => { + if (event.affectsConfiguration("tangent.tangleApiUrl")) { + views.refreshAll(); + } + }), + ...registerCommands({ + ...projectCommands(services), + ...tangentCommands(controller, sessions), + "tangent.checkForUpdates": () => + checkForUpdate(context, config, { interactive: true }), + }), + ); + controller.autoConnect(); + void refreshProjectName(services); + scheduleUpdateCheck(context, config); +} + +export function deactivate(): void {} diff --git a/vscode-extension/src/projectPicker.ts b/vscode-extension/src/projectPicker.ts new file mode 100644 index 0000000000..1cceb9bfbe --- /dev/null +++ b/vscode-extension/src/projectPicker.ts @@ -0,0 +1,48 @@ +import * as vscode from "vscode"; + +import type { CurrentProject } from "./config.ts"; +import type { ProjectSummary, TangleRestClient } from "./tangleRestClient.ts"; +import { pluralize } from "./treeNodes.ts"; + +interface ProjectPickItem extends vscode.QuickPickItem { + project: ProjectSummary; +} + +function resourceSummary(project: ProjectSummary): string { + return Object.entries(project.resourceCounts) + .filter(([, count]) => count > 0) + .map(([entity, count]) => pluralize(count, entity.replace(/_/g, " "))) + .join(", "); +} + +function toPickItem( + project: ProjectSummary, + currentId: string | undefined, +): ProjectPickItem { + return { + label: project.name, + description: + project.id === currentId ? "current" : resourceSummary(project), + detail: project.description ?? undefined, + project, + }; +} + +export async function pickProject( + client: TangleRestClient, + currentId: string | undefined, +): Promise { + const items = client + .listAllProjects() + .then((projects) => + projects.map((project) => toPickItem(project, currentId)), + ); + const picked = await vscode.window.showQuickPick(items, { + title: "Select Tangle project", + placeHolder: "The project the Runs, Resources, and Sessions views show", + matchOnDescription: true, + matchOnDetail: true, + }); + if (!picked) return undefined; + return { id: picked.project.id, name: picked.project.name }; +} diff --git a/vscode-extension/src/projectSelector.ts b/vscode-extension/src/projectSelector.ts new file mode 100644 index 0000000000..85f19c4f64 --- /dev/null +++ b/vscode-extension/src/projectSelector.ts @@ -0,0 +1,7 @@ +/** + * The selector a project-scoped remote-env host mints its token with, matching + * the `projectId:` tag the web app puts on every session in the project. + */ +export function projectSelector(projectId: string): string { + return `projectId:${projectId}`; +} diff --git a/vscode-extension/src/projectSessions.ts b/vscode-extension/src/projectSessions.ts new file mode 100644 index 0000000000..8476b7bc9e --- /dev/null +++ b/vscode-extension/src/projectSessions.ts @@ -0,0 +1,206 @@ +import { createUnsignedJwt } from "@tangent/local-agent-host/remoteEnvToken.ts"; +import { parseSessionRef } from "@tangent/local-agent-host/shellUrl.ts"; +import * as vscode from "vscode"; + +import type { CurrentProject, ExtensionConfig } from "./config.ts"; +import type { TangentController } from "./controller.ts"; +import type { CurrentProjectState } from "./currentProject.ts"; +import { projectSelector } from "./projectSelector.ts"; +import { + askNewSessionName, + askSessionName, + askSessionRef, + confirmDetachSession, + pickBundle, + showError, +} from "./prompts.ts"; +import type { SessionNode, SessionsTreeProvider } from "./sessionsTree.ts"; +import type { TangentClient } from "./tangentClient.ts"; +import type { TangleRestClient } from "./tangleRestClient.ts"; +import type { ChatSession, SessionChatPanels } from "./webview/chatPanel.ts"; + +export interface ProjectSessionsDeps { + config: ExtensionConfig; + controller: TangentController; + currentProject: CurrentProjectState; + createTangleClient(): Promise; + chats: SessionChatPanels; + sessionsTree: SessionsTreeProvider; +} + +interface SessionRequest { + bundleId: string; + name: string | undefined; +} + +/** + * Agentic sessions of the current project. A session is created on the Shell, + * recorded on the project as an `agent_session` resource, and opened in a chat + * panel. The window's bridge is scoped to the whole project, so every session + * reaches the local env and file tools. + */ +export class ProjectSessions { + private readonly deps: ProjectSessionsDeps; + + constructor(deps: ProjectSessionsDeps) { + this.deps = deps; + } + + async newSession(): Promise { + const project = await this.requireProject(); + if (!project) return; + const client = await this.deps.controller.requireClient(); + if (!client) return; + const request = await this.askSessionRequest(client); + if (!request) return; + try { + const session = await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: "Starting Tangent session…", + }, + () => this.startSession(client, project, request), + ); + await this.openChat(session); + } catch (err) { + showError(err); + } + } + + async openSession(node: SessionNode): Promise { + try { + const client = await this.deps.controller.requireClient(); + if (client) { + await this.ensureProjectTag( + client, + node.session.sessionId, + node.projectId, + ); + } + await this.openChat({ + sessionId: node.session.sessionId, + name: node.session.name, + }); + } catch (err) { + showError(err); + } + } + + /** + * Adds the project selector to the session without discarding its other tags, + * which a session attached to several projects relies on. Updating with only + * the selector would drop the rest. + */ + private async ensureProjectTag( + client: TangentClient, + sessionId: string, + projectId: string, + ): Promise { + const selector = projectSelector(projectId); + const session = await client.getSession(sessionId); + const tags = session.tags ?? []; + if (tags.includes(selector)) return; + await client.updateSessionTags(sessionId, [...tags, selector]); + } + + /** Opens any Shell session by URL or id, whether or not it is in a project. */ + async attachSession(): Promise { + const client = await this.deps.controller.requireClient(); + if (!client) return; + const input = await askSessionRef(this.deps.config.activeSessionId); + if (!input) return; + try { + const session = await client.getSession(parseSessionRef(input)); + await this.openChat({ sessionId: session.id, name: session.name }); + } catch (err) { + showError(err); + } + } + + async renameSession(node: SessionNode): Promise { + const name = (await askNewSessionName(node.session.name))?.trim(); + if (!name || name === node.session.name) return; + try { + const tangle = await this.deps.createTangleClient(); + await tangle.renameResource( + node.projectId, + node.session.resourceId, + name, + ); + this.deps.chats.rename(node.session.sessionId, name); + this.deps.sessionsTree.refresh(); + } catch (err) { + showError(err); + } + } + + async detachSession(node: SessionNode): Promise { + if (!(await confirmDetachSession(node.session.name))) return; + try { + const tangle = await this.deps.createTangleClient(); + await tangle.deleteResource(node.projectId, node.session.resourceId); + this.deps.chats.close(node.session.sessionId); + this.deps.sessionsTree.refresh(); + } catch (err) { + showError(err); + } + } + + private async requireProject(): Promise { + if (!this.deps.currentProject.project) { + await vscode.commands.executeCommand("tangle.selectProject"); + } + return this.deps.currentProject.project; + } + + private async chooseBundle( + client: TangentClient, + ): Promise { + try { + return await pickBundle(await client.listBundles()); + } catch (err) { + showError(err); + return undefined; + } + } + + private async askSessionRequest( + client: TangentClient, + ): Promise { + const bundleId = + this.deps.config.defaultBundleId ?? (await this.chooseBundle(client)); + if (!bundleId) return undefined; + const name = await askSessionName(); + if (name === undefined) return undefined; + return { bundleId, name: name.trim() || undefined }; + } + + private async startSession( + client: TangentClient, + project: CurrentProject, + { bundleId, name }: SessionRequest, + ): Promise { + const session = await client.createSession(bundleId, name, [ + projectSelector(project.id), + ]); + const tangle = await this.deps.createTangleClient(); + await tangle.createResource(project.id, { + entity: "agent_session", + entityId: session.id, + name: session.name, + }); + this.deps.sessionsTree.refresh(); + return { sessionId: session.id, name: session.name }; + } + + private async openChat(session: ChatSession): Promise { + const client = await this.deps.controller.requireClient(); + const shellUrl = this.deps.controller.shellUrl; + if (!client || !shellUrl) return; + await this.deps.controller.attach(session.sessionId); + const authToken = createUnsignedJwt({ + email: await this.deps.controller.resolveUserEmail(shellUrl), + }); + await this.deps.chats.open(shellUrl, session, authToken); + } +} diff --git a/vscode-extension/src/projectViews.ts b/vscode-extension/src/projectViews.ts new file mode 100644 index 0000000000..97a43811fd --- /dev/null +++ b/vscode-extension/src/projectViews.ts @@ -0,0 +1,84 @@ +import * as vscode from "vscode"; + +import type { CurrentProjectState } from "./currentProject.ts"; +import { ResourcesTreeProvider } from "./resourcesTree.ts"; +import { RunsTreeProvider } from "./runsTree.ts"; +import { SessionsTreeProvider } from "./sessionsTree.ts"; +import type { TangleRestClient } from "./tangleRestClient.ts"; + +export interface ProjectViewsDeps { + currentProject: CurrentProjectState; + createClient(): Promise; + activeSessionId(): string | undefined; +} + +export interface ProjectViews extends vscode.Disposable { + runs: RunsTreeProvider; + resources: ResourcesTreeProvider; + sessions: SessionsTreeProvider; + refreshAll(): void; +} + +/** + * The Runs, Resources, and Sessions views of the Tangle container, all scoped + * to the current project, whose name they show as their description. + */ +export function createProjectViews(deps: ProjectViewsDeps): ProjectViews { + const treeDeps = { + currentProject: () => deps.currentProject.project, + createClient: deps.createClient, + }; + const runs = new RunsTreeProvider(treeDeps); + const resources = new ResourcesTreeProvider(treeDeps); + const sessions = new SessionsTreeProvider({ + ...treeDeps, + activeSessionId: deps.activeSessionId, + }); + const runsView = vscode.window.createTreeView("tangle.runs", { + treeDataProvider: runs, + }); + const treeViews = [ + runsView, + vscode.window.createTreeView("tangle.resources", { + treeDataProvider: resources, + }), + vscode.window.createTreeView("tangle.sessions", { + treeDataProvider: sessions, + }), + ]; + + const describe = () => { + for (const view of treeViews) { + view.description = deps.currentProject.project?.name; + } + }; + const refreshAll = () => { + runs.refresh(); + resources.refresh(); + sessions.refresh(); + }; + describe(); + runs.setVisible(runsView.visible); + + const disposables: vscode.Disposable[] = [ + runs, + resources, + sessions, + ...treeViews, + runsView.onDidChangeVisibility((event) => runs.setVisible(event.visible)), + deps.currentProject.onDidChange(() => { + describe(); + refreshAll(); + }), + ]; + + return { + runs, + resources, + sessions, + refreshAll, + dispose: () => { + for (const disposable of disposables) disposable.dispose(); + }, + }; +} diff --git a/vscode-extension/src/prompts.ts b/vscode-extension/src/prompts.ts new file mode 100644 index 0000000000..7b5beb79fa --- /dev/null +++ b/vscode-extension/src/prompts.ts @@ -0,0 +1,154 @@ +import { normalizeShellUrl } from "@tangent/local-agent-host/shellUrl.ts"; +import * as vscode from "vscode"; + +import type { TangentBundle } from "./tangentClient.ts"; +import { normalizeBaseUrl } from "./tangleRestClient.ts"; + +export function errorMessage(err: unknown): string { + return err instanceof Error ? err.message : String(err); +} + +export function showError(err: unknown): void { + void vscode.window.showErrorMessage(errorMessage(err)); +} + +export async function confirmConnectLink( + shellOrigin: string | undefined, + apiOrigin: string | undefined, +): Promise { + const targets = [ + shellOrigin ? `Shell: ${shellOrigin}` : "", + apiOrigin ? `Tangle API: ${apiOrigin}` : "", + ].filter(Boolean); + const choice = await vscode.window.showWarningMessage( + "Connect this editor to a Tangent Shell from a link?", + { + modal: true, + detail: `A link wants this window to connect to:\n${targets.join( + "\n", + )}\n\nLocal sub-agents from this Shell can edit files and run the terminal in this workspace, and the saved Tangle API token is sent to the API host.`, + }, + "Connect", + ); + return choice === "Connect"; +} + +export function askShellUrl(value: string): Thenable { + return vscode.window.showInputBox({ + title: "Tangent Shell URL", + prompt: "The Shell to connect to. A session URL also attaches to it.", + value, + ignoreFocusOut: true, + validateInput: (input) => { + try { + normalizeShellUrl(input); + return undefined; + } catch (err) { + return errorMessage(err); + } + }, + }); +} + +export function askSessionRef( + value: string | undefined, +): Thenable { + return vscode.window.showInputBox({ + title: "Attach to Tangent session", + prompt: "Paste a session URL or id.", + value, + ignoreFocusOut: true, + }); +} + +export function askSessionName(): Thenable { + return vscode.window.showInputBox({ + title: "Session name (optional)", + ignoreFocusOut: true, + }); +} + +export function askNewSessionName(value: string): Thenable { + return vscode.window.showInputBox({ + title: "Rename session", + value, + ignoreFocusOut: true, + validateInput: (input) => + input.trim() ? undefined : "The name cannot be empty.", + }); +} + +export async function confirmDetachSession(name: string): Promise { + const choice = await vscode.window.showWarningMessage( + `Remove "${name}" from this project?`, + { + modal: true, + detail: "The session itself stays on the Tangent Shell.", + }, + "Remove", + ); + return choice === "Remove"; +} + +export function askProxyApiKey(): Thenable { + return vscode.window.showInputBox({ + title: "Pi proxy API key", + prompt: "Used by local pi sub-agents when tangent.piProxyUrl is set.", + password: true, + ignoreFocusOut: true, + }); +} + +export function askTangleApiUrl(value: string): Thenable { + return vscode.window.showInputBox({ + title: "Tangle API URL", + prompt: "Base URL of the Tangle backend, e.g. http://localhost:8000.", + value, + ignoreFocusOut: true, + validateInput: (input) => { + try { + normalizeBaseUrl(input); + return undefined; + } catch (err) { + return errorMessage(err); + } + }, + }); +} + +/** Asks for the backend token; an empty answer keeps `existing`. */ +export async function askTangleApiToken( + existing: string | undefined, +): Promise { + const input = await vscode.window.showInputBox({ + title: "Tangle API token (optional)", + prompt: existing + ? "Bearer token for the Tangle backend. Leave empty to keep the saved one." + : "Bearer token for the Tangle backend. Leave empty if it needs none.", + password: true, + ignoreFocusOut: true, + }); + if (input === undefined) return undefined; + return input.trim() || existing; +} + +export async function pickBundle( + bundles: TangentBundle[], +): Promise { + if (bundles.length === 0) { + void vscode.window.showWarningMessage( + "The Shell has no agent bundles to start a session from.", + ); + return undefined; + } + const picked = await vscode.window.showQuickPick( + bundles.map((bundle) => ({ + label: bundle.name, + description: bundle.version, + detail: bundle.description, + id: bundle.id, + })), + { title: "Start a session from which agent bundle?" }, + ); + return picked?.id; +} diff --git a/vscode-extension/src/resourceKinds.test.ts b/vscode-extension/src/resourceKinds.test.ts new file mode 100644 index 0000000000..2949e5d329 --- /dev/null +++ b/vscode-extension/src/resourceKinds.test.ts @@ -0,0 +1,67 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; + +import { resourceKind, resourceWebPath } from "./resourceKinds.ts"; + +test("resourceKind prefers the data type over the entity", () => { + assert.equal( + resourceKind({ entity: "document", metadata: { type: "pipeline_run" } }) + .label, + "Run", + ); + assert.equal( + resourceKind({ entity: "document", metadata: null }).label, + "Document", + ); + assert.equal( + resourceKind({ entity: "pipeline", metadata: { type: "unheard_of" } }) + .label, + "Pipeline", + ); + assert.equal( + resourceKind({ entity: "new", metadata: null }).label, + "Resource", + ); +}); + +test("resourceWebPath opens runs and named pipelines, else the project", () => { + assert.equal( + resourceWebPath("p1", { + entity: "document", + metadata: { type: "pipeline_run", identity: "run://id/r 1" }, + }), + "/runs/r%201", + ); + assert.equal( + resourceWebPath("p1", { + entity: "document", + metadata: { type: "local_pipeline", identity: "pipeline://name/My flow" }, + }), + "/editor/My%20flow", + ); + assert.equal( + resourceWebPath("p1", { + entity: "document", + metadata: { + type: "local_pipeline", + identity: "pipeline://id/abc", + fallbackName: "Old name", + }, + }), + "/editor/Old%20name", + ); + assert.equal( + resourceWebPath("p1", { + entity: "document", + metadata: { type: "pipeline", identity: "pipeline://remote/rp 1" }, + }), + "/editor/pipeline/rp%201", + ); + assert.equal( + resourceWebPath("p/1", { + entity: "document", + metadata: { type: "document" }, + }), + "/projects/p%2F1", + ); +}); diff --git a/vscode-extension/src/resourceKinds.ts b/vscode-extension/src/resourceKinds.ts new file mode 100644 index 0000000000..c2cd202805 --- /dev/null +++ b/vscode-extension/src/resourceKinds.ts @@ -0,0 +1,70 @@ +import type { ProjectResource } from "./tangleRestClient.ts"; + +export interface ResourceKind { + label: string; + icon: string; +} + +type ResourceRow = Pick; + +const PIPELINE: ResourceKind = { label: "Pipeline", icon: "type-hierarchy" }; + +// Must match the `data.type` values the web app writes in +// src/services/projects/resourceDescriptor.ts; the row's entity is the fallback. +const KINDS: Record = { + pipeline: PIPELINE, + local_pipeline: PIPELINE, + pipeline_run: { label: "Run", icon: "play-circle" }, + instructions: { label: "Instructions", icon: "book" }, + document: { label: "Document", icon: "file-text" }, + agent_session: { label: "Session", icon: "comment-discussion" }, +}; + +const UNKNOWN_KIND: ResourceKind = { label: "Resource", icon: "file" }; + +const WORKAREA_TARGET = /^(run|pipeline):\/\/(id|name|remote)\/(.+)$/; + +function metadataString( + resource: ResourceRow, + key: string, +): string | undefined { + const value = resource.metadata?.[key]; + return typeof value === "string" && value !== "" ? value : undefined; +} + +export function resourceKind(resource: ResourceRow): ResourceKind { + const type = metadataString(resource, "type"); + return (type && KINDS[type]) || KINDS[resource.entity] || UNKNOWN_KIND; +} + +/** + * Where the web app shows a resource: the run or pipeline its row points at, + * otherwise the project page that lists it. + */ +export function resourceWebPath( + projectId: string, + resource: ResourceRow, +): string { + const projectPath = `/projects/${encodeURIComponent(projectId)}`; + const match = WORKAREA_TARGET.exec( + metadataString(resource, "identity") ?? "", + ); + if (!match) return projectPath; + const [, type, key, value] = match; + if (type === "run") return `/runs/${encodeURIComponent(value)}`; + return pipelineWebPath(resource, key, value, projectPath); +} + +function pipelineWebPath( + resource: ResourceRow, + key: string, + value: string, + projectPath: string, +): string { + if (key === "remote") { + return `/editor/pipeline/${encodeURIComponent(value)}`; + } + const name = + key === "name" ? value : metadataString(resource, "fallbackName"); + return name ? `/editor/${encodeURIComponent(name)}` : projectPath; +} diff --git a/vscode-extension/src/resourcesTree.ts b/vscode-extension/src/resourcesTree.ts new file mode 100644 index 0000000000..014f3c4123 --- /dev/null +++ b/vscode-extension/src/resourcesTree.ts @@ -0,0 +1,62 @@ +import * as vscode from "vscode"; + +import { resourceKind } from "./resourceKinds.ts"; +import type { ProjectResource } from "./tangleRestClient.ts"; +import { type MessageNode, messageNode, ProjectTree } from "./treeNodes.ts"; + +const UNNAMED_RESOURCE = "Untitled resource"; + +export interface ResourceNode { + kind: "resource"; + projectId: string; + resource: ProjectResource; +} + +function resourceItem(node: ResourceNode): vscode.TreeItem { + const { resource } = node; + const kind = resourceKind(resource); + const item = new vscode.TreeItem( + resource.name ?? UNNAMED_RESOURCE, + vscode.TreeItemCollapsibleState.None, + ); + item.id = `resource:${resource.id}`; + item.description = kind.label; + item.tooltip = [ + `${kind.label} · updated ${resource.updatedAt.toLocaleString()}`, + resource.createdBy ? `Added by ${resource.createdBy}` : "", + ] + .filter(Boolean) + .join("\n"); + item.iconPath = new vscode.ThemeIcon(kind.icon); + item.contextValue = "tangleResource"; + item.command = { + command: "tangle.openResource", + title: "Open Resource", + arguments: [node], + }; + return item; +} + +/** + * The current project's pipelines and documents. Sessions are resources too, + * but they get their own view. + */ +export class ResourcesTreeProvider extends ProjectTree { + protected async loadRoot( + projectId: string, + ): Promise<(ResourceNode | MessageNode)[]> { + const client = await this.deps.createClient(); + const resources = await client.listAllResources(projectId, [ + "pipeline", + "document", + ]); + if (resources.length === 0) return [messageNode("No resources yet")]; + return resources + .toSorted((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()) + .map((resource) => ({ kind: "resource", projectId, resource })); + } + + protected itemFor(node: ResourceNode): vscode.TreeItem { + return resourceItem(node); + } +} diff --git a/vscode-extension/src/runStatus.test.ts b/vscode-extension/src/runStatus.test.ts new file mode 100644 index 0000000000..98e296848f --- /dev/null +++ b/vscode-extension/src/runStatus.test.ts @@ -0,0 +1,37 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; + +import { + isRunActive, + overallRunStatus, + runStatusIcon, + runStatusLabel, +} from "./runStatus.ts"; + +test("only statuses that can still change count as active", () => { + assert.equal(isRunActive("RUNNING"), true); + assert.equal(isRunActive("QUEUED"), true); + assert.equal(isRunActive("SUCCEEDED"), false); + assert.equal(isRunActive("FAILED"), false); + assert.equal(isRunActive(undefined), false); +}); + +test("the most severe status with tasks wins", () => { + assert.equal(overallRunStatus({ SUCCEEDED: 5, FAILED: 1 }), "FAILED"); + assert.equal(overallRunStatus({ SUCCEEDED: 5, RUNNING: 2 }), "RUNNING"); + assert.equal(overallRunStatus({ SUCCEEDED: 5, FAILED: 0 }), "SUCCEEDED"); +}); + +test("unknown statuses fall back to any status with tasks", () => { + assert.equal(overallRunStatus({ NEW_STATUS: 1 }), "NEW_STATUS"); + assert.equal(overallRunStatus({}), undefined); + assert.equal(overallRunStatus(null), undefined); +}); + +test("labels and icons cover known and unknown statuses", () => { + assert.equal(runStatusLabel("WAITING_FOR_UPSTREAM"), "Waiting for upstream"); + assert.equal(runStatusLabel("NEW_STATUS"), "NEW_STATUS"); + assert.equal(runStatusLabel(undefined), "Unknown"); + assert.equal(runStatusIcon("SUCCEEDED").id, "pass"); + assert.equal(runStatusIcon(undefined).id, "circle-large-outline"); +}); diff --git a/vscode-extension/src/runStatus.ts b/vscode-extension/src/runStatus.ts new file mode 100644 index 0000000000..85402541bf --- /dev/null +++ b/vscode-extension/src/runStatus.ts @@ -0,0 +1,87 @@ +import type { ColoredIcon } from "./treeNodes.ts"; + +// Must match EXECUTION_STATUS_PRIORITY in the web app's +// src/utils/executionStatus.ts, so a run shows the same status in both places. +const STATUS_PRIORITY = [ + "SYSTEM_ERROR", + "FAILED", + "INVALID", + "CANCELLING", + "CANCELLED", + "RUNNING", + "PENDING", + "WAITING_FOR_UPSTREAM", + "QUEUED", + "UNINITIALIZED", + "SKIPPED", + "SUCCEEDED", +] as const; + +const STATUS_LABELS: Record = { + CANCELLED: "Cancelled", + CANCELLING: "Cancelling", + FAILED: "Failed", + INVALID: "Invalid", + PENDING: "Pending", + QUEUED: "Queued", + RUNNING: "Running", + SKIPPED: "Skipped", + SUCCEEDED: "Succeeded", + SYSTEM_ERROR: "System error", + UNINITIALIZED: "Uninitialized", + WAITING_FOR_UPSTREAM: "Waiting for upstream", +}; + +const FAILED_ICON = { id: "error", color: "testing.iconFailed" }; +const WAITING_ICON = { id: "clock" }; +const STOPPED_ICON = { id: "circle-slash" }; + +const STATUS_ICONS: Record = { + SUCCEEDED: { id: "pass", color: "testing.iconPassed" }, + FAILED: FAILED_ICON, + SYSTEM_ERROR: FAILED_ICON, + INVALID: { id: "warning", color: "problemsWarningIcon.foreground" }, + RUNNING: { id: "sync~spin" }, + PENDING: WAITING_ICON, + QUEUED: WAITING_ICON, + WAITING_FOR_UPSTREAM: WAITING_ICON, + CANCELLING: STOPPED_ICON, + CANCELLED: STOPPED_ICON, + SKIPPED: { id: "debug-step-over" }, + UNINITIALIZED: { id: "circle-outline" }, +}; + +const ACTIVE_STATUSES = new Set([ + "RUNNING", + "PENDING", + "QUEUED", + "WAITING_FOR_UPSTREAM", + "CANCELLING", + "UNINITIALIZED", +]); + +/** Whether a run can still change status, so it is worth polling. */ +export function isRunActive(status: string | undefined): boolean { + return status !== undefined && ACTIVE_STATUSES.has(status); +} + +/** The status that best summarizes a run, from its per-status task counts. */ +export function overallRunStatus( + stats: Record | null | undefined, +): string | undefined { + if (!stats) return undefined; + const prioritized = STATUS_PRIORITY.find( + (status) => (stats[status] ?? 0) > 0, + ); + if (prioritized) return prioritized; + return Object.entries(stats).find(([, count]) => count > 0)?.[0]; +} + +export function runStatusLabel(status: string | undefined): string { + if (!status) return "Unknown"; + return STATUS_LABELS[status] ?? status; +} + +export function runStatusIcon(status: string | undefined): ColoredIcon { + return (status && STATUS_ICONS[status]) || { id: "circle-large-outline" }; +} diff --git a/vscode-extension/src/runsTree.ts b/vscode-extension/src/runsTree.ts new file mode 100644 index 0000000000..16991bbabd --- /dev/null +++ b/vscode-extension/src/runsTree.ts @@ -0,0 +1,191 @@ +import * as vscode from "vscode"; + +import { + isRunActive, + overallRunStatus, + runStatusIcon, + runStatusLabel, +} from "./runStatus.ts"; +import type { ProjectRun, TangleRestClient } from "./tangleRestClient.ts"; +import { + type MessageNode, + messageNode, + ProjectTree, + themeIcon, +} from "./treeNodes.ts"; + +const RUN_POLL_INTERVAL_MS = 10_000; +const UNNAMED_PIPELINE = "Unnamed pipeline"; + +export interface RunNode { + kind: "run"; + run: ProjectRun; + status: string | undefined; +} + +interface LoadMoreNode { + kind: "loadMore"; +} + +type RunsTreeNode = RunNode | LoadMoreNode; + +interface LoadedRuns { + projectId: string; + runs: RunNode[]; + nextPageToken: string | null; +} + +function runDescription({ run, status }: RunNode): string { + const parts = [runStatusLabel(status)]; + if (run.createdAt) parts.push(run.createdAt.toLocaleString()); + return parts.join(" · "); +} + +function runItem(node: RunNode): vscode.TreeItem { + const { run } = node; + const item = new vscode.TreeItem( + run.pipelineName ?? UNNAMED_PIPELINE, + vscode.TreeItemCollapsibleState.None, + ); + item.id = `run:${run.id}`; + item.description = runDescription(node); + item.tooltip = [ + `Run ${run.id}`, + run.createdBy ? `Started by ${run.createdBy}` : "", + ] + .filter(Boolean) + .join("\n"); + item.iconPath = themeIcon(runStatusIcon(node.status)); + item.contextValue = "tangleRun"; + item.command = { + command: "tangle.openRun", + title: "Open Run", + arguments: [node], + }; + return item; +} + +function loadMoreItem(): vscode.TreeItem { + const item = new vscode.TreeItem( + "Load more runs…", + vscode.TreeItemCollapsibleState.None, + ); + item.iconPath = new vscode.ThemeIcon("ellipsis"); + item.command = { command: "tangle.loadMoreRuns", title: "Load More Runs" }; + return item; +} + +async function withLatestStatus( + client: TangleRestClient, + node: RunNode, +): Promise { + try { + const stats = await client.getRunStats(node.run.id); + return { ...node, status: overallRunStatus(stats) }; + } catch { + return node; + } +} + +/** + * The current project's runs, newest first. While the view is visible, runs + * that can still change status are re-polled so the list reflects live state. + */ +export class RunsTreeProvider extends ProjectTree { + private loaded: LoadedRuns | undefined; + private pollTimer: ReturnType | undefined; + private visible = false; + + setVisible(visible: boolean): void { + this.visible = visible; + if (visible) this.schedulePoll(); + } + + async loadMore(): Promise { + const loaded = this.loaded; + if (!loaded?.nextPageToken) return; + const page = await this.fetchRuns(loaded.projectId, loaded.nextPageToken); + if (this.loaded !== loaded) return; + this.loaded = { + ...loaded, + runs: [...loaded.runs, ...page.runs], + nextPageToken: page.nextPageToken, + }; + this.rerender(); + } + + override dispose(): void { + this.stopPolling(); + super.dispose(); + } + + protected override reset(): void { + this.loaded = undefined; + this.stopPolling(); + } + + protected async loadRoot( + projectId: string, + ): Promise<(RunsTreeNode | MessageNode)[]> { + if (this.loaded?.projectId !== projectId) { + const page = await this.fetchRuns(projectId); + this.loaded = { projectId, ...page }; + } + this.schedulePoll(); + if (this.loaded.runs.length === 0) return [messageNode("No runs yet")]; + const loadMore: LoadMoreNode[] = this.loaded.nextPageToken + ? [{ kind: "loadMore" }] + : []; + return [...this.loaded.runs, ...loadMore]; + } + + protected itemFor(node: RunsTreeNode): vscode.TreeItem { + return node.kind === "run" ? runItem(node) : loadMoreItem(); + } + + private async fetchRuns( + projectId: string, + pageToken?: string, + ): Promise> { + const client = await this.deps.createClient(); + const page = await client.listProjectRuns(projectId, pageToken); + const runs = await Promise.all( + page.items.map((run) => + withLatestStatus(client, { kind: "run", run, status: undefined }), + ), + ); + return { runs, nextPageToken: page.nextPageToken }; + } + + private hasActiveRuns(): boolean { + return this.loaded?.runs.some((node) => isRunActive(node.status)) ?? false; + } + + private schedulePoll(): void { + if (this.pollTimer || !this.visible || !this.hasActiveRuns()) return; + this.pollTimer = setTimeout(() => { + this.pollTimer = undefined; + // A failed poll stops polling; the next refresh shows the error. + this.pollActiveRuns().catch(() => undefined); + }, RUN_POLL_INTERVAL_MS); + } + + private stopPolling(): void { + clearTimeout(this.pollTimer); + this.pollTimer = undefined; + } + + private async pollActiveRuns(): Promise { + const loaded = this.loaded; + if (!loaded || !this.visible) return; + const client = await this.deps.createClient(); + const runs = await Promise.all( + loaded.runs.map((node) => + isRunActive(node.status) ? withLatestStatus(client, node) : node, + ), + ); + if (this.loaded !== loaded) return; + this.loaded = { ...loaded, runs }; + this.rerender(); + } +} diff --git a/vscode-extension/src/sessionsTree.ts b/vscode-extension/src/sessionsTree.ts new file mode 100644 index 0000000000..78cdd5c479 --- /dev/null +++ b/vscode-extension/src/sessionsTree.ts @@ -0,0 +1,110 @@ +import * as vscode from "vscode"; + +import { + type MessageNode, + messageNode, + ProjectTree, + type ProjectTreeDeps, +} from "./treeNodes.ts"; + +const UNNAMED_SESSION = "Untitled session"; + +const START_SESSION_NODE: MessageNode = { + ...messageNode("No sessions yet. Start one…", "add"), + command: { command: "tangle.newSession", title: "New Session" }, +}; + +export interface ProjectSession { + resourceId: string; + sessionId: string; + name: string; + createdAt: Date; +} + +export interface SessionNode { + kind: "session"; + projectId: string; + session: ProjectSession; +} + +interface SessionsTreeDeps extends ProjectTreeDeps { + activeSessionId(): string | undefined; +} + +function sessionItem(node: SessionNode, isActive: boolean): vscode.TreeItem { + const { session } = node; + const item = new vscode.TreeItem( + session.name, + vscode.TreeItemCollapsibleState.None, + ); + const created = session.createdAt.toLocaleString(); + item.id = `session:${session.resourceId}`; + item.description = isActive ? `local env · ${created}` : created; + item.tooltip = [ + `Session ${session.sessionId}`, + isActive ? "Local sub-agents and file tools run in this window." : "", + ] + .filter(Boolean) + .join("\n"); + item.iconPath = new vscode.ThemeIcon( + isActive ? "comment-discussion" : "comment", + ); + item.contextValue = "tangleSession"; + item.command = { + command: "tangle.openSession", + title: "Open Session", + arguments: [node], + }; + return item; +} + +/** + * The current project's agentic sessions: `agent_session` resources whose + * entity id is a Tangent Shell session id. + */ +export class SessionsTreeProvider extends ProjectTree { + private readonly activeSessionId: () => string | undefined; + + constructor(deps: SessionsTreeDeps) { + super(deps); + this.activeSessionId = deps.activeSessionId; + } + + /** Moves the "local env" marker without refetching the sessions. */ + showActiveSessionChanged(): void { + this.rerender(); + } + + protected async loadRoot( + projectId: string, + ): Promise<(SessionNode | MessageNode)[]> { + const client = await this.deps.createClient(); + const resources = await client.listAllResources(projectId, [ + "agent_session", + ]); + const sessions = resources.flatMap((resource): SessionNode[] => + resource.entityId + ? [ + { + kind: "session", + projectId, + session: { + resourceId: resource.id, + sessionId: resource.entityId, + name: resource.name ?? UNNAMED_SESSION, + createdAt: resource.createdAt, + }, + }, + ] + : [], + ); + if (sessions.length === 0) return [START_SESSION_NODE]; + return sessions.toSorted( + (a, b) => b.session.createdAt.getTime() - a.session.createdAt.getTime(), + ); + } + + protected itemFor(node: SessionNode): vscode.TreeItem { + return sessionItem(node, node.session.sessionId === this.activeSessionId()); + } +} diff --git a/vscode-extension/src/statusView.ts b/vscode-extension/src/statusView.ts new file mode 100644 index 0000000000..b0e3c86801 --- /dev/null +++ b/vscode-extension/src/statusView.ts @@ -0,0 +1,59 @@ +import type { LocalAgentBridge } from "@tangent/local-agent-host/bridge.ts"; +import * as vscode from "vscode"; + +export interface StatusSnapshot { + bridge: LocalAgentBridge | undefined; + shellUrl: string | undefined; + project: string | undefined; + sessionId: string | undefined; +} + +function statusText(bridge: LocalAgentBridge | undefined): string { + if (!bridge || bridge.state === "disconnected") { + return "$(debug-disconnect) Tangent"; + } + if (bridge.state === "connecting") return "$(sync~spin) Tangent"; + const count = bridge.listAgents().length; + return count > 0 + ? `$(radio-tower) Tangent · ${count}` + : "$(radio-tower) Tangent"; +} + +function statusTooltip({ + bridge, + shellUrl, + project, + sessionId, +}: StatusSnapshot): string { + if (!bridge) return "Not connected — click to connect."; + return [ + `Shell: ${shellUrl}`, + project ? `Project: ${project}` : "", + bridge.environmentId ? `Environment: ${bridge.environmentId}` : "", + sessionId ? `Session: ${sessionId}` : "", + bridge.lastError ? `Last error: ${bridge.lastError}` : "", + ] + .filter(Boolean) + .join("\n"); +} + +/** The status bar item: connection state and live sub-agent count; click for the menu. */ +export class StatusView implements vscode.Disposable { + private readonly item = vscode.window.createStatusBarItem( + vscode.StatusBarAlignment.Left, + ); + + constructor() { + this.item.command = "tangent.showMenu"; + this.item.show(); + } + + render(snapshot: StatusSnapshot): void { + this.item.text = statusText(snapshot.bridge); + this.item.tooltip = statusTooltip(snapshot); + } + + dispose(): void { + this.item.dispose(); + } +} diff --git a/vscode-extension/src/tangentClient.test.ts b/vscode-extension/src/tangentClient.test.ts new file mode 100644 index 0000000000..ec36e7f111 --- /dev/null +++ b/vscode-extension/src/tangentClient.test.ts @@ -0,0 +1,48 @@ +import assert from "node:assert/strict"; +import { afterEach, test } from "node:test"; + +import { TangentClient } from "./tangentClient.ts"; + +interface RecordedRequest { + url: string; + headers: Record; +} + +const originalFetch = globalThis.fetch; + +afterEach(() => { + globalThis.fetch = originalFetch; +}); + +test("attaches the OktaSSO cookie and retries once after a 401", async () => { + const requests: RecordedRequest[] = []; + const forceRefresh: boolean[] = []; + let call = 0; + globalThis.fetch = async (input, init) => { + requests.push({ + url: String(input), + headers: Object.fromEntries(new Headers(init?.headers).entries()), + }); + call += 1; + if (call === 1) return new Response("unauthorized", { status: 401 }); + return new Response( + JSON.stringify({ session: { id: "s1", name: "Debug" } }), + { status: 200 }, + ); + }; + const cookieProvider = async (options?: { forceRefresh?: boolean }) => { + forceRefresh.push(Boolean(options?.forceRefresh)); + return options?.forceRefresh ? "SSO_TOKEN=fresh" : "SSO_TOKEN=stale"; + }; + + const session = await new TangentClient( + "http://shell.test", + cookieProvider, + ).getSession("s1"); + + assert.deepEqual(session, { id: "s1", name: "Debug" }); + assert.equal(requests.length, 2); + assert.equal(requests[0].headers.cookie, "SSO_TOKEN=stale"); + assert.equal(requests[1].headers.cookie, "SSO_TOKEN=fresh"); + assert.deepEqual(forceRefresh, [false, true]); +}); diff --git a/vscode-extension/src/tangentClient.ts b/vscode-extension/src/tangentClient.ts new file mode 100644 index 0000000000..c2db2c6811 --- /dev/null +++ b/vscode-extension/src/tangentClient.ts @@ -0,0 +1,121 @@ +import { array, object, optional, string } from "zod/mini"; + +import type { CookieProvider } from "./auth/oktaSsoAuth.ts"; + +const sessionSchema = object({ + id: string(), + name: string(), + tags: optional(array(string())), +}); +const sessionEnvelope = object({ session: sessionSchema }); +const bundleSchema = object({ + id: string(), + name: string(), + version: string(), + description: optional(string()), +}); +const bundlesEnvelope = object({ bundles: array(bundleSchema) }); + +export interface TangentSession { + id: string; + name: string; + tags?: string[]; +} + +export interface TangentBundle { + id: string; + name: string; + version: string; + description?: string; +} + +/** + * The Shell's session REST API. Chat itself runs in the session chat panel, + * whose embedded Tangent UI owns the chat socket. + */ +export class TangentClient { + private readonly baseUrl: string; + private readonly cookieProvider: CookieProvider | undefined; + + constructor(baseUrl: string, cookieProvider?: CookieProvider) { + this.baseUrl = baseUrl; + this.cookieProvider = cookieProvider; + } + + async listBundles(): Promise { + const body = bundlesEnvelope.parse( + await this.requestJson("GET", "/api/agent-bundles"), + ); + return body.bundles; + } + + async createSession( + bundleId: string, + name?: string, + tags?: string[], + ): Promise { + const body = sessionEnvelope.parse( + await this.requestJson("POST", "/api/sessions", { + bundleId, + ...(name ? { name } : {}), + ...(tags ? { tags } : {}), + }), + ); + return body.session; + } + + async updateSessionTags(sessionId: string, tags: string[]): Promise { + await this.requestJson( + "PATCH", + `/api/sessions/${encodeURIComponent(sessionId)}`, + { tags }, + ); + } + + async getSession(sessionId: string): Promise { + const body = sessionEnvelope.parse( + await this.requestJson( + "GET", + `/api/sessions/${encodeURIComponent(sessionId)}`, + ), + ); + return body.session; + } + + private async requestJson( + method: "GET" | "POST" | "PATCH", + path: string, + body?: unknown, + ): Promise { + let response = await this.send(method, path, body, await this.cookie()); + if (response.status === 401 && this.cookieProvider) { + const refreshed = await this.cookieProvider({ forceRefresh: true }); + response = await this.send(method, path, body, refreshed); + } + if (!response.ok) { + const text = await response.text(); + throw new Error(`${method} ${path} failed (${response.status}): ${text}`); + } + return response.json(); + } + + private cookie(): Promise { + return this.cookieProvider?.() ?? Promise.resolve(undefined); + } + + private send( + method: "GET" | "POST" | "PATCH", + path: string, + body: unknown, + cookie: string | undefined, + ): Promise { + const headers: Record = {}; + if (body) headers["Content-Type"] = "application/json"; + if (cookie) headers.Cookie = cookie; + return fetch(`${this.baseUrl}${path}`, { + method, + headers, + body: body ? JSON.stringify(body) : undefined, + }); + } +} diff --git a/vscode-extension/src/tangleRestClient.test.ts b/vscode-extension/src/tangleRestClient.test.ts new file mode 100644 index 0000000000..4172674958 --- /dev/null +++ b/vscode-extension/src/tangleRestClient.test.ts @@ -0,0 +1,301 @@ +import assert from "node:assert/strict"; +import { afterEach, test } from "node:test"; + +import { + collectPages, + normalizeBaseUrl, + TangleRestClient, +} from "./tangleRestClient.ts"; + +interface RecordedRequest { + url: URL; + method: string; + headers: Record; + body: unknown; +} + +const originalFetch = globalThis.fetch; + +const sessionResource = { + id: "res1", + project_id: "p1", + entity: "agent_session", + name: "Debug churn", + entity_id: "s1", + data: null, + created_by: "dev@localhost", + created_at: "2026-10-01T10:00:00Z", + updated_at: "2026-10-02T10:00:00Z", +}; + +function recordRequest(input: unknown, init?: RequestInit): RecordedRequest { + return { + url: new URL(String(input)), + method: init?.method ?? "GET", + headers: Object.fromEntries(new Headers(init?.headers).entries()), + body: typeof init?.body === "string" ? JSON.parse(init.body) : undefined, + }; +} + +function mockFetch(body: unknown, status = 200): RecordedRequest[] { + const requests: RecordedRequest[] = []; + globalThis.fetch = async (input, init) => { + requests.push(recordRequest(input, init)); + const payload = status === 204 ? null : JSON.stringify(body); + return new Response(payload, { status }); + }; + return requests; +} + +afterEach(() => { + globalThis.fetch = originalFetch; +}); + +test("listProjects maps the page and sends the page size and token", async () => { + const requests = mockFetch({ + projects: [ + { + id: "p1", + workspace_id: "w1", + name: "Churn model", + description: null, + created_by: "dev@localhost", + updated_at: "2026-09-28T18:50:48Z", + resource_counts: { pipeline: 2 }, + }, + ], + total_count: 1, + next_page_token: "next", + }); + + const page = await new TangleRestClient("http://api.test/").listProjects( + "tok", + ); + + assert.equal(requests[0].url.pathname, "/api/projects/"); + assert.equal(requests[0].url.searchParams.get("page_token"), "tok"); + assert.equal(requests[0].url.searchParams.get("page_size"), "50"); + assert.equal(page.nextPageToken, "next"); + assert.deepEqual(page.items[0], { + id: "p1", + name: "Churn model", + description: null, + createdBy: "dev@localhost", + updatedAt: new Date("2026-09-28T18:50:48Z"), + resourceCounts: { pipeline: 2 }, + }); +}); + +test("listProjectRuns maps runs and tolerates missing fields", async () => { + const requests = mockFetch({ + runs: [ + { + id: "r1", + root_execution_id: "e1", + created_by: null, + created_at: null, + pipeline_name: null, + }, + ], + }); + + const page = await new TangleRestClient("http://api.test").listProjectRuns( + "p/1", + ); + + assert.equal(requests[0].url.pathname, "/api/projects/p%2F1/runs"); + assert.equal(page.nextPageToken, null); + assert.deepEqual(page.items, [ + { + id: "r1", + rootExecutionId: "e1", + pipelineName: null, + createdBy: null, + createdAt: null, + }, + ]); +}); + +test("getRunStats asks for execution stats and sends the bearer token", async () => { + const requests = mockFetch({ + id: "r1", + execution_status_stats: { SUCCEEDED: 3, FAILED: 1 }, + }); + + const stats = await new TangleRestClient( + "http://api.test", + "secret", + ).getRunStats("r1"); + + assert.equal(requests[0].url.pathname, "/api/pipeline_runs/r1"); + assert.equal( + requests[0].url.searchParams.get("include_execution_stats"), + "true", + ); + assert.equal(requests[0].headers.authorization, "Bearer secret"); + assert.deepEqual(stats, { SUCCEEDED: 3, FAILED: 1 }); +}); + +test("getProject maps a single project", async () => { + const requests = mockFetch({ + id: "p 1", + name: "Churn model", + resource_counts: null, + }); + + const project = await new TangleRestClient("http://api.test").getProject( + "p 1", + ); + + assert.equal(requests[0].url.pathname, "/api/projects/p%201"); + assert.equal(project.name, "Churn model"); + assert.deepEqual(project.resourceCounts, {}); +}); + +test("listResources repeats the entity filter and maps rows", async () => { + const requests = mockFetch({ + resources: [sessionResource], + total_count: 1, + next_page_token: null, + }); + + const page = await new TangleRestClient("http://api.test").listResources( + "p1", + { entity: ["pipeline", "document"] }, + ); + + assert.equal(requests[0].url.pathname, "/api/projects/p1/resources/"); + assert.deepEqual(requests[0].url.searchParams.getAll("entity"), [ + "pipeline", + "document", + ]); + assert.equal(page.nextPageToken, null); + assert.deepEqual(page.items[0], { + id: "res1", + entity: "agent_session", + name: "Debug churn", + entityId: "s1", + metadata: null, + createdBy: "dev@localhost", + createdAt: new Date("2026-10-01T10:00:00Z"), + updatedAt: new Date("2026-10-02T10:00:00Z"), + }); +}); + +test("createResource posts a session reference as JSON", async () => { + const requests = mockFetch(sessionResource, 201); + + const resource = await new TangleRestClient( + "http://api.test", + "secret", + ).createResource("p1", { + entity: "agent_session", + entityId: "s1", + name: "Debug churn", + }); + + assert.equal(requests[0].method, "POST"); + assert.equal(requests[0].headers["content-type"], "application/json"); + assert.equal(requests[0].headers.authorization, "Bearer secret"); + assert.deepEqual(requests[0].body, { + entity: "agent_session", + entity_id: "s1", + name: "Debug churn", + }); + assert.equal(resource.entityId, "s1"); +}); + +test("renameResource patches only the name", async () => { + const requests = mockFetch({ ...sessionResource, name: "Renamed" }); + + const resource = await new TangleRestClient("http://api.test").renameResource( + "p1", + "res1", + "Renamed", + ); + + assert.equal(requests[0].method, "PATCH"); + assert.equal(requests[0].url.pathname, "/api/projects/p1/resources/res1"); + assert.deepEqual(requests[0].body, { name: "Renamed" }); + assert.equal(resource.name, "Renamed"); +}); + +test("deleteResource accepts an empty 204", async () => { + const requests = mockFetch(null, 204); + + await new TangleRestClient("http://api.test").deleteResource("p1", "res1"); + + assert.equal(requests[0].method, "DELETE"); + assert.equal(requests[0].url.pathname, "/api/projects/p1/resources/res1"); +}); + +test("collectPages follows tokens and stops at the page cap", async () => { + const tokens: (string | undefined)[] = []; + const items = await collectPages(async (pageToken) => { + tokens.push(pageToken); + return { items: [tokens.length], nextPageToken: `t${tokens.length}` }; + }, 3); + + assert.deepEqual(items, [1, 2, 3]); + assert.deepEqual(tokens, [undefined, "t1", "t2"]); +}); + +test("failed responses surface the status and body", async () => { + mockFetch({ detail: "Not found" }, 404); + + await assert.rejects( + new TangleRestClient("http://api.test").getRunStats("missing"), + /GET \/api\/pipeline_runs\/missing failed \(404\)/, + ); +}); + +test("network failures name the API it could not reach", async () => { + globalThis.fetch = async () => { + throw new TypeError("fetch failed"); + }; + + await assert.rejects( + new TangleRestClient("http://api.test/").listProjects(), + /Cannot reach the Tangle API at http:\/\/api\.test/, + ); +}); + +test("attaches the OktaSSO cookie and retries once after a 401", async () => { + const requests: RecordedRequest[] = []; + const forceRefresh: boolean[] = []; + let call = 0; + globalThis.fetch = async (input, init) => { + requests.push(recordRequest(input, init)); + call += 1; + if (call === 1) return new Response("unauthorized", { status: 401 }); + return new Response( + JSON.stringify({ id: "r1", execution_status_stats: { SUCCEEDED: 1 } }), + { status: 200 }, + ); + }; + const cookieProvider = async (options?: { forceRefresh?: boolean }) => { + forceRefresh.push(Boolean(options?.forceRefresh)); + return options?.forceRefresh ? "SSO_TOKEN=fresh" : "SSO_TOKEN=stale"; + }; + + const stats = await new TangleRestClient( + "http://api.test", + undefined, + cookieProvider, + ).getRunStats("r1"); + + assert.deepEqual(stats, { SUCCEEDED: 1 }); + assert.equal(requests.length, 2); + assert.equal(requests[0].headers.cookie, "SSO_TOKEN=stale"); + assert.equal(requests[1].headers.cookie, "SSO_TOKEN=fresh"); + assert.deepEqual(forceRefresh, [false, true]); +}); + +test("normalizeBaseUrl drops trailing slashes and rejects other schemes", () => { + assert.equal( + normalizeBaseUrl(" http://localhost:8000/ "), + "http://localhost:8000", + ); + assert.throws(() => normalizeBaseUrl("ftp://host"), /http/); + assert.throws(() => normalizeBaseUrl("not a url")); +}); diff --git a/vscode-extension/src/tangleRestClient.ts b/vscode-extension/src/tangleRestClient.ts new file mode 100644 index 0000000000..34aacb960f --- /dev/null +++ b/vscode-extension/src/tangleRestClient.ts @@ -0,0 +1,390 @@ +import { + array, + type infer as Infer, + nullish, + number, + object, + record, + string, + unknown, + type ZodMiniType, +} from "zod/mini"; + +import type { CookieProvider } from "./auth/oktaSsoAuth.ts"; + +const PROJECTS_PAGE_SIZE = 50; +const RESOURCES_PAGE_SIZE = 100; +const MAX_PAGES = 10; + +const optionalString = nullish(string()); +const optionalCounts = nullish(record(string(), number())); + +const projectDto = object({ + id: string(), + name: string(), + description: optionalString, + created_by: optionalString, + updated_at: optionalString, + resource_counts: optionalCounts, +}); + +const projectListDto = object({ + projects: array(projectDto), + next_page_token: optionalString, +}); + +const projectRunDto = object({ + id: string(), + root_execution_id: string(), + pipeline_name: optionalString, + created_by: optionalString, + created_at: optionalString, +}); + +const projectRunListDto = object({ + runs: array(projectRunDto), + next_page_token: optionalString, +}); + +const runStatsDto = object({ + execution_status_stats: optionalCounts, +}); + +const resourceDto = object({ + id: string(), + entity: string(), + name: optionalString, + entity_id: optionalString, + data: nullish(record(string(), unknown())), + created_by: optionalString, + created_at: string(), + updated_at: string(), +}); + +const resourceListDto = object({ + resources: array(resourceDto), + next_page_token: optionalString, +}); + +export type ResourceEntity = "pipeline" | "agent_session" | "document"; + +export interface ProjectSummary { + id: string; + name: string; + description: string | null; + createdBy: string | null; + updatedAt: Date | null; + resourceCounts: Record; +} + +export interface ProjectRun { + id: string; + rootExecutionId: string; + pipelineName: string | null; + createdBy: string | null; + createdAt: Date | null; +} + +export interface ProjectResource { + id: string; + entity: string; + name: string | null; + entityId: string | null; + metadata: Record | null; + createdBy: string | null; + createdAt: Date; + updatedAt: Date; +} + +export interface CreateResourceInput { + entity: ResourceEntity; + entityId?: string; + name?: string; +} + +export interface Page { + items: T[]; + nextPageToken: string | null; +} + +type QueryValue = string | number | boolean | string[] | undefined; +type Query = Record; +type Method = "GET" | "POST" | "PATCH" | "DELETE"; + +interface RequestOptions { + query?: Query; + body?: unknown; +} + +function toDate(value: string | null | undefined): Date | null { + return value ? new Date(value) : null; +} + +function mapProject(dto: Infer): ProjectSummary { + return { + id: dto.id, + name: dto.name, + description: dto.description ?? null, + createdBy: dto.created_by ?? null, + updatedAt: toDate(dto.updated_at), + resourceCounts: dto.resource_counts ?? {}, + }; +} + +function mapProjectRun(dto: Infer): ProjectRun { + return { + id: dto.id, + rootExecutionId: dto.root_execution_id, + pipelineName: dto.pipeline_name ?? null, + createdBy: dto.created_by ?? null, + createdAt: toDate(dto.created_at), + }; +} + +function mapResource(dto: Infer): ProjectResource { + return { + id: dto.id, + entity: dto.entity, + name: dto.name ?? null, + entityId: dto.entity_id ?? null, + metadata: dto.data ?? null, + createdBy: dto.created_by ?? null, + createdAt: new Date(dto.created_at), + updatedAt: new Date(dto.updated_at), + }; +} + +function appendQuery(url: URL, query: Query): void { + for (const [key, value] of Object.entries(query)) { + if (value === undefined) continue; + const values = Array.isArray(value) ? value : [String(value)]; + for (const item of values) url.searchParams.append(key, item); + } +} + +/** Follows page tokens until the last page, capped so a runaway list stops. */ +export async function collectPages( + fetchPage: (pageToken?: string) => Promise>, + maxPages = MAX_PAGES, +): Promise { + const items: T[] = []; + let pageToken: string | undefined; + for (let page = 0; page < maxPages; page++) { + const result = await fetchPage(pageToken); + items.push(...result.items); + if (!result.nextPageToken) break; + pageToken = result.nextPageToken; + } + return items; +} + +export function normalizeBaseUrl(input: string): string { + const url = new URL(input.trim()); + if (url.protocol !== "http:" && url.protocol !== "https:") { + throw new Error("Use an http:// or https:// URL."); + } + return url.toString().replace(/\/+$/, ""); +} + +/** + * Talks to the Tangle backend REST API the web app uses. The web app's + * generated client depends on its React providers, so the few endpoints the + * extension needs are called directly here. + */ +export class TangleRestClient { + private readonly baseUrl: string; + private readonly token: string | undefined; + private readonly cookieProvider: CookieProvider | undefined; + + constructor( + baseUrl: string, + token?: string, + cookieProvider?: CookieProvider, + ) { + this.baseUrl = normalizeBaseUrl(baseUrl); + this.token = token; + this.cookieProvider = cookieProvider; + } + + async listProjects(pageToken?: string): Promise> { + const body = await this.request("GET", "/api/projects/", projectListDto, { + query: { page_size: PROJECTS_PAGE_SIZE, page_token: pageToken }, + }); + return { + items: body.projects.map(mapProject), + nextPageToken: body.next_page_token ?? null, + }; + } + + listAllProjects(): Promise { + return collectPages((pageToken) => this.listProjects(pageToken)); + } + + async getProject(projectId: string): Promise { + const body = await this.request( + "GET", + `/api/projects/${encodeURIComponent(projectId)}`, + projectDto, + ); + return mapProject(body); + } + + async listProjectRuns( + projectId: string, + pageToken?: string, + ): Promise> { + const body = await this.request( + "GET", + `/api/projects/${encodeURIComponent(projectId)}/runs`, + projectRunListDto, + { query: { page_token: pageToken } }, + ); + return { + items: body.runs.map(mapProjectRun), + nextPageToken: body.next_page_token ?? null, + }; + } + + /** Per-status task counts for a run, or `null` when the backend has none. */ + async getRunStats(runId: string): Promise | null> { + const body = await this.request( + "GET", + `/api/pipeline_runs/${encodeURIComponent(runId)}`, + runStatsDto, + { query: { include_execution_stats: true } }, + ); + return body.execution_status_stats ?? null; + } + + async listResources( + projectId: string, + options: { entity?: ResourceEntity[]; pageToken?: string } = {}, + ): Promise> { + const body = await this.request( + "GET", + this.resourcesPath(projectId), + resourceListDto, + { + query: { + entity: options.entity, + page_size: RESOURCES_PAGE_SIZE, + page_token: options.pageToken, + }, + }, + ); + return { + items: body.resources.map(mapResource), + nextPageToken: body.next_page_token ?? null, + }; + } + + listAllResources( + projectId: string, + entity?: ResourceEntity[], + ): Promise { + return collectPages((pageToken) => + this.listResources(projectId, { entity, pageToken }), + ); + } + + async createResource( + projectId: string, + input: CreateResourceInput, + ): Promise { + const body = await this.request( + "POST", + this.resourcesPath(projectId), + resourceDto, + { + body: { + entity: input.entity, + entity_id: input.entityId ?? null, + name: input.name ?? null, + }, + }, + ); + return mapResource(body); + } + + async renameResource( + projectId: string, + resourceId: string, + name: string, + ): Promise { + const body = await this.request( + "PATCH", + this.resourcePath(projectId, resourceId), + resourceDto, + { body: { name } }, + ); + return mapResource(body); + } + + async deleteResource(projectId: string, resourceId: string): Promise { + await this.send("DELETE", this.resourcePath(projectId, resourceId)); + } + + private resourcesPath(projectId: string): string { + return `/api/projects/${encodeURIComponent(projectId)}/resources/`; + } + + private resourcePath(projectId: string, resourceId: string): string { + return `${this.resourcesPath(projectId)}${encodeURIComponent(resourceId)}`; + } + + private async request( + method: Method, + path: string, + schema: ZodMiniType, + options: RequestOptions = {}, + ): Promise { + const response = await this.send(method, path, options); + return schema.parse(await response.json()); + } + + private async send( + method: Method, + path: string, + { query = {}, body }: RequestOptions = {}, + ): Promise { + const url = new URL(`${this.baseUrl}${path}`); + appendQuery(url, query); + let response = await this.fetch(url, method, body, await this.cookie()); + if (response.status === 401 && this.cookieProvider) { + const refreshed = await this.cookieProvider({ forceRefresh: true }); + response = await this.fetch(url, method, body, refreshed); + } + if (!response.ok) { + const text = await response.text(); + throw new Error(`${method} ${path} failed (${response.status}): ${text}`); + } + return response; + } + + private cookie(): Promise { + return this.cookieProvider?.() ?? Promise.resolve(undefined); + } + + private async fetch( + url: URL, + method: Method, + body: unknown, + cookie: string | undefined, + ): Promise { + const headers: Record = {}; + if (this.token) headers.Authorization = `Bearer ${this.token}`; + if (cookie) headers.Cookie = cookie; + if (body !== undefined) headers["Content-Type"] = "application/json"; + try { + return await fetch(url, { + method, + headers, + body: body === undefined ? undefined : JSON.stringify(body), + }); + } catch (err) { + throw new Error(`Cannot reach the Tangle API at ${this.baseUrl}`, { + cause: err, + }); + } + } +} diff --git a/vscode-extension/src/treeNodes.ts b/vscode-extension/src/treeNodes.ts new file mode 100644 index 0000000000..6e2292461a --- /dev/null +++ b/vscode-extension/src/treeNodes.ts @@ -0,0 +1,115 @@ +import * as vscode from "vscode"; + +import type { CurrentProject } from "./config.ts"; +import { errorMessage } from "./prompts.ts"; +import type { TangleRestClient } from "./tangleRestClient.ts"; + +export interface MessageNode { + kind: "message"; + label: string; + icon: string; + command?: vscode.Command; +} + +export interface ColoredIcon { + id: string; + color?: string; +} + +export interface ProjectTreeDeps { + currentProject(): CurrentProject | undefined; + createClient(): Promise; +} + +export function pluralize(count: number, noun: string): string { + return `${count} ${noun}${count === 1 ? "" : "s"}`; +} + +export function themeIcon({ id, color }: ColoredIcon): vscode.ThemeIcon { + return new vscode.ThemeIcon( + id, + color ? new vscode.ThemeColor(color) : undefined, + ); +} + +export function messageNode(label: string, icon = "info"): MessageNode { + return { kind: "message", label, icon }; +} + +function errorNode(err: unknown): MessageNode { + return { + kind: "message", + label: errorMessage(err), + icon: "error", + command: { command: "tangle.setApiUrl", title: "Set Tangle API URL" }, + }; +} + +function isMessageNode(node: { kind: string }): node is MessageNode { + return node.kind === "message"; +} + +function messageItem(node: MessageNode): vscode.TreeItem { + const item = new vscode.TreeItem( + node.label, + vscode.TreeItemCollapsibleState.None, + ); + item.iconPath = new vscode.ThemeIcon(node.icon); + item.command = node.command; + return item; +} + +/** + * A flat list scoped to the current project. Without a project it is empty, so + * the view's welcome content offers the project picker instead. + */ +export abstract class ProjectTree + implements vscode.TreeDataProvider, vscode.Disposable +{ + protected readonly deps: ProjectTreeDeps; + private readonly changed = new vscode.EventEmitter(); + readonly onDidChangeTreeData = this.changed.event; + + constructor(deps: ProjectTreeDeps) { + this.deps = deps; + } + + refresh(): void { + this.reset(); + this.changed.fire(); + } + + getTreeItem(node: TNode | MessageNode): vscode.TreeItem { + if (isMessageNode(node)) return messageItem(node); + return this.itemFor(node); + } + + async getChildren( + node?: TNode | MessageNode, + ): Promise<(TNode | MessageNode)[]> { + const project = this.deps.currentProject(); + if (node || !project) return []; + try { + return await this.loadRoot(project.id); + } catch (err) { + return [errorNode(err)]; + } + } + + dispose(): void { + this.changed.dispose(); + } + + /** Re-renders from what is already loaded, without fetching. */ + protected rerender(): void { + this.changed.fire(); + } + + protected reset(): void {} + + protected abstract loadRoot( + projectId: string, + ): Promise<(TNode | MessageNode)[]>; + + protected abstract itemFor(node: TNode): vscode.TreeItem; +} diff --git a/vscode-extension/src/updateManifest.test.ts b/vscode-extension/src/updateManifest.test.ts new file mode 100644 index 0000000000..232994cdcd --- /dev/null +++ b/vscode-extension/src/updateManifest.test.ts @@ -0,0 +1,71 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; + +import { + isExtensionManifest, + isNewerVersion, + isTrustedUpdateUrl, + manifestUrl, + resolveVsixUrl, +} from "./updateManifest.ts"; + +test("isNewerVersion compares dotted versions component-wise", () => { + assert.equal(isNewerVersion("0.0.6", "0.0.5"), true); + assert.equal(isNewerVersion("0.1.0", "0.0.9"), true); + assert.equal(isNewerVersion("1.0.0", "0.9.9"), true); + assert.equal(isNewerVersion("0.0.5", "0.0.5"), false); + assert.equal(isNewerVersion("0.0.4", "0.0.5"), false); + assert.equal(isNewerVersion("0.0.5", "0.0.5.1"), false); +}); + +test("resolveVsixUrl joins relative urls and keeps same-origin absolute ones", () => { + assert.equal( + resolveVsixUrl("https://app.example.com", "/extension/x.vsix"), + "https://app.example.com/extension/x.vsix", + ); + assert.equal( + resolveVsixUrl("https://app.example.com/", "extension/x.vsix"), + "https://app.example.com/extension/x.vsix", + ); + assert.equal( + resolveVsixUrl( + "https://app.example.com", + "https://app.example.com/extension/x.vsix", + ), + "https://app.example.com/extension/x.vsix", + ); +}); + +test("resolveVsixUrl strips a cross-origin absolute url to the deployment origin", () => { + assert.equal( + resolveVsixUrl( + "https://app.example.com", + "https://evil.example.com/x.vsix", + ), + "https://app.example.com/x.vsix", + ); +}); + +test("isTrustedUpdateUrl allows https and loopback but refuses plain http", () => { + assert.equal(isTrustedUpdateUrl("https://app.example.com/x.vsix"), true); + assert.equal(isTrustedUpdateUrl("http://localhost:3000/x.vsix"), true); + assert.equal(isTrustedUpdateUrl("http://127.0.0.1:3000/x.vsix"), true); + assert.equal(isTrustedUpdateUrl("http://app.example.com/x.vsix"), false); +}); + +test("manifestUrl points at /extension/latest.json without a double slash", () => { + assert.equal( + manifestUrl("https://app.example.com/"), + "https://app.example.com/extension/latest.json", + ); +}); + +test("isExtensionManifest guards shape", () => { + assert.equal( + isExtensionManifest({ version: "0.0.6", filename: "x.vsix", url: "/x" }), + true, + ); + assert.equal(isExtensionManifest({ version: "0.0.6" }), false); + assert.equal(isExtensionManifest(null), false); + assert.equal(isExtensionManifest("0.0.6"), false); +}); diff --git a/vscode-extension/src/updateManifest.ts b/vscode-extension/src/updateManifest.ts new file mode 100644 index 0000000000..7eb8fd3ea7 --- /dev/null +++ b/vscode-extension/src/updateManifest.ts @@ -0,0 +1,75 @@ +export interface ExtensionManifest { + version: string; + filename: string; + url: string; +} + +function hasStringProp( + value: object, + key: K, +): value is { [P in K]: string } { + return ( + key in value && typeof (value as { [P in K]: unknown })[key] === "string" + ); +} + +export function isExtensionManifest( + value: unknown, +): value is ExtensionManifest { + return ( + typeof value === "object" && + value !== null && + hasStringProp(value, "version") && + hasStringProp(value, "filename") && + hasStringProp(value, "url") + ); +} + +/** True when `candidate` is a strictly higher dotted version than `current`. */ +export function isNewerVersion(candidate: string, current: string): boolean { + const parts = (value: string) => + value.split(".").map((part) => Number.parseInt(part, 10) || 0); + const a = parts(candidate); + const b = parts(current); + const length = Math.max(a.length, b.length); + for (let i = 0; i < length; i++) { + const diff = (a[i] ?? 0) - (b[i] ?? 0); + if (diff !== 0) return diff > 0; + } + return false; +} + +/** + * Resolves the manifest's `url` against the deployment base to an absolute URL. + * A cross-origin absolute `url` is reduced to its path on the deployment's own + * origin, so a tampered manifest cannot redirect the installer to another host. + * This pins the origin only; it does not make a plain-http origin safe, which + * {@link isTrustedUpdateUrl} guards separately. + */ +export function resolveVsixUrl(baseUrl: string, manifestUrl: string): string { + const origin = baseUrl.replace(/\/+$/, ""); + if (/^https?:\/\//.test(manifestUrl)) { + const absolute = new URL(manifestUrl); + if (absolute.origin === new URL(baseUrl).origin) return manifestUrl; + return `${origin}${absolute.pathname}${absolute.search}`; + } + return manifestUrl.startsWith("/") + ? `${origin}${manifestUrl}` + : `${origin}/${manifestUrl}`; +} + +const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "::1", "[::1]"]); + +/** + * A plain-http update origin lets an on-path attacker serve a malicious `.vsix` + * from the deployment's own host, so auto-install is allowed only over https or + * a loopback host (local development). + */ +export function isTrustedUpdateUrl(url: string): boolean { + const parsed = new URL(url); + return parsed.protocol === "https:" || LOOPBACK_HOSTS.has(parsed.hostname); +} + +export function manifestUrl(baseUrl: string): string { + return `${baseUrl.replace(/\/+$/, "")}/extension/latest.json`; +} diff --git a/vscode-extension/src/webview/chat.tsx b/vscode-extension/src/webview/chat.tsx new file mode 100644 index 0000000000..f84e3aff6c --- /dev/null +++ b/vscode-extension/src/webview/chat.tsx @@ -0,0 +1,129 @@ +import { Chat, TangentProvider } from "@tangent/embed-react"; +import { type ReactNode, useEffect, useState } from "react"; +import { createRoot } from "react-dom/client"; + +import { + CHAT_CONFIG_ELEMENT_ID, + type ChatWebviewConfig, + type ColorScheme, + embedEndpoints, + isChatWebviewConfig, + isThemeMessage, + type WebviewToHostMessage, +} from "./chatConfig.ts"; + +interface VsCodeApi { + postMessage(message: WebviewToHostMessage): void; +} + +declare function acquireVsCodeApi(): VsCodeApi; + +declare global { + interface Window { + $RefreshReg$?: () => void; + $RefreshSig$?: () => (type: T) => T; + } +} + +type RuntimeStatus = "loading" | "ready" | "unreachable"; + +const vscodeApi = acquireVsCodeApi(); + +/** + * A Shell running under the Vite dev server ships React Fast Refresh code that + * throws unless the host page installed `@vitejs/plugin-react`'s preamble. Host + * pages that are Vite dev apps themselves happen to provide it; this panel is + * not one. A production Shell never reads these globals. + */ +function installViteDevPreamble(): void { + window.$RefreshReg$ ??= () => undefined; + window.$RefreshSig$ ??= () => (type) => type; +} + +function readConfig(): ChatWebviewConfig { + const element = document.getElementById(CHAT_CONFIG_ELEMENT_ID); + const config: unknown = JSON.parse(element?.textContent ?? "null"); + if (!isChatWebviewConfig(config)) { + throw new Error("The chat panel has no valid configuration."); + } + return config; +} + +function useColorScheme(initial: ColorScheme): ColorScheme { + const [colorScheme, setColorScheme] = useState(initial); + useEffect(() => { + const onMessage = (event: MessageEvent) => { + if (isThemeMessage(event.data)) setColorScheme(event.data.colorScheme); + }; + window.addEventListener("message", onMessage); + return () => window.removeEventListener("message", onMessage); + }, []); + return colorScheme; +} + +/** + * `TangentProvider` never reports a failed runtime import, so a missing Shell + * would leave the panel blank. Importing the same URL here surfaces the + * failure; module imports are cached per URL, so it is fetched once. + */ +function useRuntimeStatus(channelUrl: string): RuntimeStatus { + const [status, setStatus] = useState("loading"); + useEffect(() => { + let active = true; + import(/* @vite-ignore */ channelUrl).then( + () => active && setStatus("ready"), + () => active && setStatus("unreachable"), + ); + return () => { + active = false; + }; + }, [channelUrl]); + return status; +} + +function Notice({ children }: { children: ReactNode }) { + return

{children}

; +} + +function SessionChat({ config }: { config: ChatWebviewConfig }) { + const colorScheme = useColorScheme(config.colorScheme); + const { channelUrl, socketUrl, socketPath } = embedEndpoints( + config.embedBaseUrl, + ); + const runtime = useRuntimeStatus(channelUrl); + + if (runtime === "loading") return Loading chat…; + if (runtime === "unreachable") { + return ( + + Cannot load the Tangent chat from the Shell. Check that the Shell is + running, then reopen this session. + + ); + } + + return ( + config.authToken} + socketUrl={socketUrl} + socketPath={socketPath} + colorScheme={colorScheme} + > + + vscodeApi.postMessage({ type: "openArtifact", url, title }) + } + onError={(message) => vscodeApi.postMessage({ type: "error", message })} + /> + + ); +} + +installViteDevPreamble(); +const root = document.getElementById("root"); +if (!root) throw new Error("The chat panel is missing its root element."); +createRoot(root).render(); diff --git a/vscode-extension/src/webview/chatConfig.test.ts b/vscode-extension/src/webview/chatConfig.test.ts new file mode 100644 index 0000000000..1c188c1978 --- /dev/null +++ b/vscode-extension/src/webview/chatConfig.test.ts @@ -0,0 +1,45 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; + +import { + embedEndpoints, + isChatWebviewConfig, + isThemeMessage, +} from "./chatConfig.ts"; + +test("embed endpoints for a Shell at the origin root", () => { + assert.deepEqual(embedEndpoints("http://127.0.0.1:4000"), { + channelUrl: "http://127.0.0.1:4000/embed/v1/tangent-elements.js", + socketUrl: "http://127.0.0.1:4000", + socketPath: "/socket.io", + }); +}); + +test("a Shell path prefix moves into the socket path", () => { + assert.deepEqual(embedEndpoints("http://127.0.0.1:4000/tangent/"), { + channelUrl: "http://127.0.0.1:4000/tangent/embed/v1/tangent-elements.js", + socketUrl: "http://127.0.0.1:4000", + socketPath: "/tangent/socket.io", + }); +}); + +test("chat config guard requires every field", () => { + const config = { + embedBaseUrl: "http://127.0.0.1:4000", + sessionId: "s1", + colorScheme: "dark", + authToken: "header.payload.", + }; + assert.equal(isChatWebviewConfig(config), true); + assert.equal(isChatWebviewConfig({ ...config, colorScheme: "blue" }), false); + assert.equal(isChatWebviewConfig({ ...config, sessionId: 1 }), false); + assert.equal(isChatWebviewConfig({ ...config, authToken: 1 }), false); + assert.equal(isChatWebviewConfig(null), false); +}); + +test("theme message guard", () => { + assert.equal(isThemeMessage({ type: "theme", colorScheme: "light" }), true); + assert.equal(isThemeMessage({ type: "theme" }), false); + assert.equal(isThemeMessage({ type: "error", colorScheme: "dark" }), false); + assert.equal(isThemeMessage("theme"), false); +}); diff --git a/vscode-extension/src/webview/chatConfig.ts b/vscode-extension/src/webview/chatConfig.ts new file mode 100644 index 0000000000..87d9f1f4ad --- /dev/null +++ b/vscode-extension/src/webview/chatConfig.ts @@ -0,0 +1,73 @@ +import * as z from "zod/mini"; + +export type ColorScheme = "light" | "dark"; + +export interface ChatWebviewConfig { + embedBaseUrl: string; + sessionId: string; + colorScheme: ColorScheme; + authToken: string; +} + +export interface EmbedEndpoints { + channelUrl: string; + socketUrl: string; + socketPath: string; +} + +export interface ThemeMessage { + type: "theme"; + colorScheme: ColorScheme; +} + +export const webviewToHostMessage = z.union([ + z.object({ + type: z.literal("openArtifact"), + url: z.string(), + title: z.string(), + }), + z.object({ type: z.literal("error"), message: z.string() }), +]); + +export type WebviewToHostMessage = z.infer; + +export const CHAT_CONFIG_ELEMENT_ID = "tangent-chat-config"; + +/** + * Socket.IO treats its URL as an origin and takes the path separately, so a + * Shell mounted under a prefix moves that prefix into `socketPath`. + */ +export function embedEndpoints(embedBaseUrl: string): EmbedEndpoints { + const url = new URL(embedBaseUrl); + const prefix = url.pathname.replace(/\/+$/, ""); + return { + channelUrl: `${url.origin}${prefix}/embed/v1/tangent-elements.js`, + socketUrl: url.origin, + socketPath: `${prefix}/socket.io`, + }; +} + +function isColorScheme(value: unknown): value is ColorScheme { + return value === "light" || value === "dark"; +} + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null; +} + +export function isChatWebviewConfig( + value: unknown, +): value is ChatWebviewConfig { + if (!isRecord(value)) return false; + return ( + typeof value.embedBaseUrl === "string" && + typeof value.sessionId === "string" && + isColorScheme(value.colorScheme) && + typeof value.authToken === "string" + ); +} + +export function isThemeMessage(value: unknown): value is ThemeMessage { + if (!isRecord(value)) return false; + return value.type === "theme" && isColorScheme(value.colorScheme); +} diff --git a/vscode-extension/src/webview/chatHtml.test.ts b/vscode-extension/src/webview/chatHtml.test.ts new file mode 100644 index 0000000000..95378e7fc5 --- /dev/null +++ b/vscode-extension/src/webview/chatHtml.test.ts @@ -0,0 +1,68 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; + +import { chatContentSecurityPolicy, chatPageHtml } from "./chatHtml.ts"; + +const PAGE = { + cspSource: "https://webview.test", + scriptUri: "https://webview.test/dist/chat.js", + nonce: "abc123", + embedOrigin: "http://127.0.0.1:4000", + config: { + embedBaseUrl: "http://127.0.0.1:4000", + sessionId: "s1", + colorScheme: "dark" as const, + authToken: "header.payload.", + }, +}; + +function directives(csp: string): Map { + return new Map( + csp.split("; ").map((directive) => { + const [name = "", ...sources] = directive.split(" "); + return [name, sources.join(" ")]; + }), + ); +} + +test("the CSP limits scripts to the nonce, the webview, and the embed origin", () => { + const csp = directives(chatContentSecurityPolicy(PAGE)); + + assert.equal(csp.get("default-src"), "'none'"); + assert.equal( + csp.get("script-src"), + "'nonce-abc123' https://webview.test http://127.0.0.1:4000", + ); + assert.equal( + csp.get("connect-src"), + "http://127.0.0.1:4000 ws://127.0.0.1:4000", + ); + assert.equal(csp.get("worker-src"), "http://127.0.0.1:4000 blob:"); +}); + +test("an https embed origin allows a secure socket", () => { + const csp = directives( + chatContentSecurityPolicy({ ...PAGE, embedOrigin: "https://shell.test" }), + ); + assert.equal(csp.get("connect-src"), "https://shell.test wss://shell.test"); +}); + +test("the page loads the bundle with the nonce and embeds the config", () => { + const html = chatPageHtml(PAGE); + + assert.match( + html, + /` inside a string value from closing the JSON block. */ +function inertJson(value: unknown): string { + return JSON.stringify(value).replace(/): string { + const socketOrigin = embedOrigin.replace(/^http/, "ws"); + return [ + "default-src 'none'", + `script-src 'nonce-${nonce}' ${cspSource} ${embedOrigin}`, + `style-src ${cspSource} ${embedOrigin} 'unsafe-inline'`, + `connect-src ${embedOrigin} ${socketOrigin}`, + `img-src ${cspSource} ${embedOrigin} data: blob: https:`, + `font-src ${cspSource} ${embedOrigin} data:`, + `worker-src ${embedOrigin} blob:`, + `frame-src ${embedOrigin} blob:`, + ].join("; "); +} + +export function chatPageHtml(options: ChatPageOptions): string { + const csp = chatContentSecurityPolicy(options); + return ` + + + + + + + + +
+ + + +`; +} diff --git a/vscode-extension/src/webview/chatPanel.ts b/vscode-extension/src/webview/chatPanel.ts new file mode 100644 index 0000000000..89ae8563a2 --- /dev/null +++ b/vscode-extension/src/webview/chatPanel.ts @@ -0,0 +1,180 @@ +import { randomBytes } from "node:crypto"; + +import * as vscode from "vscode"; + +import { + type ColorScheme, + type ThemeMessage, + webviewToHostMessage, +} from "./chatConfig.ts"; +import { chatPageHtml } from "./chatHtml.ts"; +import { EmbedProxy, type ProxyCookieProvider } from "./embedProxy.ts"; + +const VIEW_TYPE = "tangent.sessionChat"; + +export interface ChatSession { + sessionId: string; + name: string; +} + +function colorSchemeFor(theme: vscode.ColorTheme): ColorScheme { + const light = + theme.kind === vscode.ColorThemeKind.Light || + theme.kind === vscode.ColorThemeKind.HighContrastLight; + return light ? "light" : "dark"; +} + +function isWebUrl(url: string): boolean { + return /^https?:\/\//.test(url); +} + +export type CookieHeaderFor = ( + shellUrl: string, + options?: { forceRefresh?: boolean }, +) => Promise; + +/** + * One chat panel per Tangent session, each rendering the embedded Tangent chat. + * The embed talks to the Shell through a loopback {@link EmbedProxy}, started + * once per Shell URL and shared by its panels. + */ +export class SessionChatPanels implements vscode.Disposable { + private readonly extensionUri: vscode.Uri; + private readonly log: (line: string) => void; + private readonly cookieHeaderFor: CookieHeaderFor | undefined; + private readonly panels = new Map(); + private readonly proxies = new Map>(); + private readonly themeListener: vscode.Disposable; + + constructor( + extensionUri: vscode.Uri, + log: (line: string) => void, + cookieHeaderFor?: CookieHeaderFor, + ) { + this.extensionUri = extensionUri; + this.log = log; + this.cookieHeaderFor = cookieHeaderFor; + this.themeListener = vscode.window.onDidChangeActiveColorTheme((theme) => + this.broadcastTheme(colorSchemeFor(theme)), + ); + } + + async open( + shellUrl: string, + session: ChatSession, + authToken: string, + ): Promise { + const existing = this.panels.get(session.sessionId); + if (existing) { + existing.reveal(); + return; + } + const proxy = await this.proxyFor(shellUrl); + const panel = this.createPanel(session, proxy, authToken); + this.panels.set(session.sessionId, panel); + panel.onDidDispose(() => this.panels.delete(session.sessionId)); + } + + rename(sessionId: string, name: string): void { + const panel = this.panels.get(sessionId); + if (panel) panel.title = name; + } + + close(sessionId: string): void { + this.panels.get(sessionId)?.dispose(); + } + + dispose(): void { + for (const panel of this.panels.values()) panel.dispose(); + this.themeListener.dispose(); + for (const proxy of this.proxies.values()) { + void proxy.then((started) => started.dispose()).catch(() => undefined); + } + this.proxies.clear(); + } + + private proxyFor(shellUrl: string): Promise { + const existing = this.proxies.get(shellUrl); + if (existing) return existing; + const started = this.startProxy(shellUrl); + started.catch(() => this.proxies.delete(shellUrl)); + this.proxies.set(shellUrl, started); + return started; + } + + private async startProxy(shellUrl: string): Promise { + return EmbedProxy.start(shellUrl, { + log: this.log, + cookieProvider: this.cookieProviderFor(shellUrl), + }); + } + + private cookieProviderFor(shellUrl: string): ProxyCookieProvider | undefined { + const cookieHeaderFor = this.cookieHeaderFor; + if (!cookieHeaderFor) return undefined; + return (options) => cookieHeaderFor(shellUrl, options); + } + + private createPanel( + session: ChatSession, + proxy: EmbedProxy, + authToken: string, + ): vscode.WebviewPanel { + const distUri = vscode.Uri.joinPath(this.extensionUri, "dist"); + const panel = vscode.window.createWebviewPanel( + VIEW_TYPE, + session.name, + vscode.ViewColumn.Active, + { + enableScripts: true, + retainContextWhenHidden: true, + localResourceRoots: [distUri], + portMapping: [ + { webviewPort: proxy.port, extensionHostPort: proxy.port }, + ], + }, + ); + panel.iconPath = vscode.Uri.joinPath( + this.extensionUri, + "resources", + "tangle.svg", + ); + const { webview } = panel; + webview.html = chatPageHtml({ + cspSource: webview.cspSource, + scriptUri: webview + .asWebviewUri(vscode.Uri.joinPath(distUri, "chat.js")) + .toString(), + nonce: randomBytes(16).toString("base64"), + embedOrigin: proxy.origin, + config: { + embedBaseUrl: proxy.baseUrl, + sessionId: session.sessionId, + colorScheme: colorSchemeFor(vscode.window.activeColorTheme), + authToken, + }, + }); + webview.onDidReceiveMessage((message) => this.onMessage(proxy, message)); + return panel; + } + + private onMessage(proxy: EmbedProxy, message: unknown): void { + const parsed = webviewToHostMessage.safeParse(message); + if (!parsed.success) return; + const data = parsed.data; + if (data.type === "error") { + this.log(`chat: ${data.message}`); + void vscode.window.showErrorMessage(data.message); + return; + } + const url = proxy.toTargetUrl(data.url); + if (isWebUrl(url)) void vscode.env.openExternal(vscode.Uri.parse(url)); + } + + private broadcastTheme(colorScheme: ColorScheme): void { + const message: ThemeMessage = { type: "theme", colorScheme }; + for (const panel of this.panels.values()) { + void panel.webview.postMessage(message); + } + } +} diff --git a/vscode-extension/src/webview/embedProxy.test.ts b/vscode-extension/src/webview/embedProxy.test.ts new file mode 100644 index 0000000000..2ef6c4e1c2 --- /dev/null +++ b/vscode-extension/src/webview/embedProxy.test.ts @@ -0,0 +1,303 @@ +import assert from "node:assert/strict"; +import * as http from "node:http"; +import * as net from "node:net"; +import { after, before, test } from "node:test"; + +import { EmbedProxy, isWebviewOrigin } from "./embedProxy.ts"; + +const WEBVIEW_ORIGIN = "vscode-webview://panel-id"; + +interface SeenRequest { + method: string | undefined; + url: string | undefined; + headers: http.IncomingHttpHeaders; +} + +interface ProxyResponse { + status: number; + headers: http.IncomingHttpHeaders; + body: string; +} + +const seen: SeenRequest[] = []; +let upstream: http.Server; +let upstreamHost: string; +let proxy: EmbedProxy; + +function portOf(server: http.Server): number { + const address = server.address(); + if (!address || typeof address !== "object") { + throw new Error("Test server is not listening."); + } + return address.port; +} + +function startUpstream(): Promise { + const server = http.createServer((req, res) => { + seen.push({ method: req.method, url: req.url, headers: req.headers }); + res.writeHead(200, { + "content-type": "text/plain", + "access-control-allow-origin": "http://localhost:5173", + }); + res.end(`upstream ${req.url}`); + }); + server.on("upgrade", (_req, socket) => { + socket.write( + "HTTP/1.1 101 Switching Protocols\r\nupgrade: websocket\r\nconnection: Upgrade\r\n\r\n", + ); + socket.on("data", (chunk: Buffer) => socket.write(`echo:${chunk}`)); + }); + return new Promise((resolve) => + server.listen(0, "127.0.0.1", () => resolve(server)), + ); +} + +function send( + path: string, + options: { method?: string; headers?: http.OutgoingHttpHeaders } = {}, +): Promise { + return new Promise((resolve, reject) => { + const req = http.request( + `${proxy.origin}${path}`, + { method: options.method ?? "GET", headers: options.headers }, + (res) => { + let body = ""; + res.setEncoding("utf8"); + res.on("data", (chunk: string) => (body += chunk)); + res.on("end", () => + resolve({ status: res.statusCode ?? 0, headers: res.headers, body }), + ); + }, + ); + req.on("error", reject); + req.end(); + }); +} + +function readUntil(socket: net.Socket, marker: string): Promise { + return new Promise((resolve, reject) => { + let received = ""; + socket.on("data", (chunk: Buffer) => { + received += chunk.toString(); + if (received.includes(marker)) resolve(received); + }); + socket.on("error", reject); + }); +} + +before(async () => { + upstream = await startUpstream(); + upstreamHost = `127.0.0.1:${portOf(upstream)}`; + proxy = await EmbedProxy.start(`http://${upstreamHost}/tangent/`); +}); + +after(() => { + proxy.dispose(); + upstream.close(); +}); + +test("the proxy keeps the Shell's path prefix in its base URL", () => { + assert.match(proxy.origin, /^http:\/\/127\.0\.0\.1:\d+$/); + assert.equal(proxy.baseUrl, `${proxy.origin}/tangent`); +}); + +test("webview requests reach the Shell as same-origin calls", async () => { + seen.length = 0; + const response = await send("/tangent/api/sessions/s1", { + headers: { origin: WEBVIEW_ORIGIN, referer: "vscode-webview://panel-id/" }, + }); + + assert.equal(response.status, 200); + assert.equal(response.body, "upstream /tangent/api/sessions/s1"); + assert.equal(response.headers["access-control-allow-origin"], WEBVIEW_ORIGIN); + assert.equal(response.headers.vary, "Origin"); + assert.equal(seen[0]?.headers.host, upstreamHost); + assert.equal(seen[0]?.headers.origin, undefined); + assert.equal(seen[0]?.headers.referer, undefined); +}); + +test("the SSO cookie rides along, appended to the webview's own cookies", async () => { + seen.length = 0; + const cookieProxy = await EmbedProxy.start( + `http://${upstreamHost}/tangent/`, + { + cookieProvider: () => Promise.resolve("SSO_TOKEN=abc"), + }, + ); + + await new Promise((resolve, reject) => { + http + .request( + `${cookieProxy.origin}/tangent/api/sessions`, + { headers: { origin: WEBVIEW_ORIGIN, cookie: "existing=1" } }, + (res) => { + res.resume(); + res.on("end", resolve); + }, + ) + .on("error", reject) + .end(); + }); + + assert.equal(seen[0]?.headers.cookie, "existing=1; SSO_TOKEN=abc"); + cookieProxy.dispose(); +}); + +test("a 401 refreshes the cookie once and replays the request", async () => { + const calls: Array = []; + const gated = http.createServer((req, res) => { + calls.push(req.headers.cookie); + if (req.headers.cookie === "SSO=stale") { + res.writeHead(401).end("expired"); + return; + } + res.writeHead(200).end("ok"); + }); + await new Promise((resolve) => gated.listen(0, "127.0.0.1", resolve)); + let refreshed = false; + const retryProxy = await EmbedProxy.start( + `http://127.0.0.1:${portOf(gated)}/`, + { + cookieProvider: ({ forceRefresh } = {}) => { + if (forceRefresh) refreshed = true; + return Promise.resolve(refreshed ? "SSO=fresh" : "SSO=stale"); + }, + }, + ); + + const status = await new Promise((resolve, reject) => { + http + .request( + `${retryProxy.origin}/api/sessions`, + { headers: { origin: WEBVIEW_ORIGIN } }, + (res) => { + res.resume(); + res.on("end", () => resolve(res.statusCode ?? 0)); + }, + ) + .on("error", reject) + .end(); + }); + + assert.equal(status, 200); + assert.deepEqual(calls, ["SSO=stale", "SSO=fresh"]); + retryProxy.dispose(); + await new Promise((resolve) => gated.close(resolve)); +}); + +test("callers without an origin get no CORS headers", async () => { + const response = await send("/tangent/embed/v1/tangent-elements.js"); + + assert.equal(response.status, 200); + assert.equal(response.headers["access-control-allow-origin"], undefined); +}); + +test("preflights from a webview are answered without the Shell", async () => { + seen.length = 0; + const response = await send("/tangent/api/sessions", { + method: "OPTIONS", + headers: { origin: WEBVIEW_ORIGIN }, + }); + + assert.equal(response.status, 204); + assert.equal(response.headers["access-control-allow-origin"], WEBVIEW_ORIGIN); + assert.match( + String(response.headers["access-control-allow-methods"]), + /PATCH/, + ); + assert.equal(seen.length, 0); +}); + +test("other origins are refused before reaching the Shell", async () => { + seen.length = 0; + const response = await send("/tangent/api/sessions", { + headers: { origin: "https://evil.example" }, + }); + + assert.equal(response.status, 403); + assert.equal(seen.length, 0); +}); + +test("a foreign Host header is refused (DNS rebinding)", async () => { + seen.length = 0; + const response = await send("/tangent/api/sessions", { + headers: { origin: WEBVIEW_ORIGIN, host: "attacker.example" }, + }); + + assert.equal(response.status, 403); + assert.equal(seen.length, 0); +}); + +test("WebSocket upgrades are piped through to the Shell", async () => { + const socket = net.connect(proxy.port, "127.0.0.1"); + const handshake = readUntil(socket, "\r\n\r\n"); + socket.write( + [ + "GET /tangent/socket.io/?EIO=4&transport=websocket HTTP/1.1", + `host: ${proxy.origin.replace("http://", "")}`, + `origin: ${WEBVIEW_ORIGIN}`, + "upgrade: websocket", + "connection: Upgrade", + "", + "", + ].join("\r\n"), + ); + + assert.match(await handshake, /^HTTP\/1\.1 101/); + const echo = readUntil(socket, "echo:ping"); + socket.write("ping"); + assert.match(await echo, /echo:ping/); + socket.destroy(); +}); + +test("upgrades from other origins are refused", async () => { + const socket = net.connect(proxy.port, "127.0.0.1"); + const reply = readUntil(socket, "\r\n\r\n"); + socket.write( + "GET /tangent/socket.io/ HTTP/1.1\r\norigin: https://evil.example\r\nupgrade: websocket\r\nconnection: Upgrade\r\n\r\n", + ); + + assert.match(await reply, /^HTTP\/1\.1 403/); + socket.destroy(); +}); + +test("an unreachable Shell answers 502", async () => { + const deadServer = http.createServer(); + await new Promise((resolve) => + deadServer.listen(0, "127.0.0.1", resolve), + ); + const port = portOf(deadServer); + await new Promise((resolve) => deadServer.close(resolve)); + const deadProxy = await EmbedProxy.start(`http://127.0.0.1:${port}`); + + const response = await new Promise((resolve, reject) => { + http + .get(`${deadProxy.origin}/api/sessions`, (res) => + resolve(res.statusCode ?? 0), + ) + .on("error", reject); + }); + + assert.equal(response, 502); + deadProxy.dispose(); +}); + +test("artifact URLs map from the proxy back onto the Shell", () => { + assert.equal( + proxy.toTargetUrl(`${proxy.origin}/tangent/sessions/s1?tab=files#a`), + `http://${upstreamHost}/tangent/sessions/s1?tab=files#a`, + ); + assert.equal( + proxy.toTargetUrl("files/report.html"), + `http://${upstreamHost}/tangent/files/report.html`, + ); + assert.equal( + proxy.toTargetUrl("https://docs.example/page"), + "https://docs.example/page", + ); +}); + +test("only VS Code webview origins are allowed by default", () => { + assert.equal(isWebviewOrigin(WEBVIEW_ORIGIN), true); + assert.equal(isWebviewOrigin("http://localhost:5173"), false); +}); diff --git a/vscode-extension/src/webview/embedProxy.ts b/vscode-extension/src/webview/embedProxy.ts new file mode 100644 index 0000000000..fde8859b1b --- /dev/null +++ b/vscode-extension/src/webview/embedProxy.ts @@ -0,0 +1,371 @@ +import * as http from "node:http"; +import * as https from "node:https"; +import * as net from "node:net"; +import type { Duplex } from "node:stream"; +import * as tls from "node:tls"; + +const LOOPBACK_HOST = "127.0.0.1"; +const WEBVIEW_ORIGIN_PREFIX = "vscode-webview://"; +// The body is buffered in full so a 401 can be replayed, so it must be bounded. +const MAX_BUFFERED_BODY_BYTES = 32 * 1024 * 1024; +const CORS_ALLOW_HEADERS = { + "access-control-allow-methods": "GET,POST,PATCH,PUT,DELETE,OPTIONS", + "access-control-allow-headers": "Authorization, Content-Type", +}; + +export type OriginPolicy = (origin: string) => boolean; + +export type ProxyCookieProvider = (options?: { + forceRefresh?: boolean; +}) => Promise; + +export interface EmbedProxyOptions { + allowOrigin?: OriginPolicy; + log?: (line: string) => void; + cookieProvider?: ProxyCookieProvider; +} + +export function isWebviewOrigin(origin: string): boolean { + return origin.startsWith(WEBVIEW_ORIGIN_PREFIX); +} + +function corsHeaders(origin: string | undefined): http.OutgoingHttpHeaders { + if (!origin) return {}; + return { + "access-control-allow-origin": origin, + vary: "Origin", + ...CORS_ALLOW_HEADERS, + }; +} + +function mergeCookie( + existing: string | string[] | undefined, + extra: string, +): string { + const base = Array.isArray(existing) ? existing.join("; ") : existing; + return base ? `${base}; ${extra}` : extra; +} + +function errorText(err: unknown): string { + return err instanceof Error ? err.message : String(err); +} + +class BodyTooLargeError extends Error {} + +function collectBody( + req: http.IncomingMessage, + maxBytes: number, +): Promise { + return new Promise((resolve, reject) => { + const chunks: Buffer[] = []; + let size = 0; + req.on("data", (chunk: Buffer) => { + size += chunk.length; + if (size > maxBytes) { + req.destroy(); + reject(new BodyTooLargeError(`Request body exceeds ${maxBytes} bytes.`)); + return; + } + chunks.push(chunk); + }); + req.on("end", () => resolve(Buffer.concat(chunks))); + req.on("error", reject); + }); +} + +/** + * Drops the browser's `Origin` so the Shell treats the call as same-origin; + * the proxy has already applied its own origin policy. When the SSO gate is on, + * the minted session cookie rides along so the embed clears the API gate too. + */ +function upstreamHeaders( + headers: http.IncomingHttpHeaders, + target: URL, + cookieHeader?: string, +): http.OutgoingHttpHeaders { + const forwarded: http.OutgoingHttpHeaders = { ...headers, host: target.host }; + delete forwarded.origin; + delete forwarded.referer; + if (cookieHeader) { + forwarded.cookie = mergeCookie(forwarded.cookie, cookieHeader); + } + return forwarded; +} + +function withoutCorsHeaders( + headers: http.IncomingHttpHeaders, +): http.OutgoingHttpHeaders { + return Object.fromEntries( + Object.entries(headers).filter( + ([name]) => !name.startsWith("access-control-"), + ), + ); +} + +function headerLines(headers: http.OutgoingHttpHeaders): string[] { + return Object.entries(headers).flatMap(([name, value]) => { + if (value === undefined) return []; + const values = Array.isArray(value) ? value : [String(value)]; + return values.map((item) => `${name}: ${item}`); + }); +} + +export function upgradeRequestHead( + req: http.IncomingMessage, + target: URL, + cookieHeader?: string, +): string { + return [ + `${req.method} ${req.url} HTTP/1.1`, + ...headerLines(upstreamHeaders(req.headers, target, cookieHeader)), + "", + "", + ].join("\r\n"); +} + +function listen(server: http.Server): Promise { + return new Promise((resolve, reject) => { + server.once("error", reject); + server.listen(0, LOOPBACK_HOST, () => { + const address = server.address(); + if (address && typeof address === "object") resolve(address.port); + else reject(new Error("The embed proxy did not get a port.")); + }); + }); +} + +/** + * Forwards a webview's HTTP and WebSocket traffic to the Tangent Shell over + * loopback. The Shell answers CORS only for an exact allowlist of origins, and + * a webview's origin is a fresh `vscode-webview://` per panel, so it can + * never be listed there. + */ +export class EmbedProxy { + readonly origin: string; + readonly baseUrl: string; + readonly port: number; + private readonly server: http.Server; + private readonly target: URL; + private readonly allowOrigin: OriginPolicy; + private readonly log: (line: string) => void; + private readonly cookieProvider: ProxyCookieProvider | undefined; + private readonly sockets = new Set(); + + private constructor( + server: http.Server, + port: number, + target: URL, + options: EmbedProxyOptions, + ) { + this.server = server; + this.port = port; + this.target = target; + this.allowOrigin = options.allowOrigin ?? isWebviewOrigin; + this.log = options.log ?? (() => undefined); + this.cookieProvider = options.cookieProvider; + this.origin = `http://${LOOPBACK_HOST}:${port}`; + this.baseUrl = `${this.origin}${target.pathname.replace(/\/+$/, "")}`; + server.on("request", (req, res) => this.handleRequest(req, res)); + server.on("upgrade", (req, socket, head) => + this.handleUpgrade(req, socket, head), + ); + server.on("connection", (socket) => this.track(socket)); + } + + static async start( + targetUrl: string, + options: EmbedProxyOptions = {}, + ): Promise { + const server = http.createServer(); + const port = await listen(server); + return new EmbedProxy(server, port, new URL(targetUrl), options); + } + + /** Maps a URL the embed produced against the proxy back onto the Shell. */ + toTargetUrl(url: string): string { + const resolved = new URL(url, `${this.baseUrl}/`); + if (resolved.origin !== this.origin) return resolved.toString(); + const path = `${resolved.pathname}${resolved.search}${resolved.hash}`; + return new URL(path, this.target.origin).toString(); + } + + dispose(): void { + for (const socket of this.sockets) socket.destroy(); + this.sockets.clear(); + this.server.close(); + } + + private track(socket: Duplex): void { + this.sockets.add(socket); + socket.once("close", () => this.sockets.delete(socket)); + } + + /** `undefined` for a non-browser caller, `false` for a refused origin. */ + private checkOrigin(origin: string | undefined): string | undefined | false { + if (origin === undefined) return undefined; + return this.allowOrigin(origin) ? origin : false; + } + + /** + * A request whose `Host` is not this proxy's loopback address did not come + * through the webview's port mapping. Rejecting it is the standard defense + * against DNS rebinding, which otherwise lets a remote page reach the Shell + * with the attached session cookie through a request that carries no `Origin`. + */ + private hostAllowed(req: http.IncomingMessage): boolean { + return req.headers.host === `${LOOPBACK_HOST}:${this.port}`; + } + + private handleRequest( + req: http.IncomingMessage, + res: http.ServerResponse, + ): void { + if (!this.hostAllowed(req)) { + res.writeHead(403).end("Host not allowed."); + return; + } + const origin = this.checkOrigin(req.headers.origin); + if (origin === false) { + res.writeHead(403).end("Origin not allowed."); + return; + } + const cors = corsHeaders(origin); + if (req.method === "OPTIONS" && origin) { + res.writeHead(204, cors).end(); + return; + } + void this.forwardRequest(req, res, cors); + } + + private resolveCookie(forceRefresh: boolean): Promise { + if (!this.cookieProvider) return Promise.resolve(undefined); + return this.cookieProvider({ forceRefresh }).catch(() => undefined); + } + + /** + * The webview's SSO cookie is resolved per request, not once at startup, so a + * refreshed session is picked up without reloading the window. When the gate + * answers 401 the cookie is refreshed once and the request is replayed with + * the buffered body, the same single-retry contract the REST clients use. + */ + private async forwardRequest( + req: http.IncomingMessage, + res: http.ServerResponse, + cors: http.OutgoingHttpHeaders, + ): Promise { + try { + const body = await collectBody(req, MAX_BUFFERED_BODY_BYTES); + let response = await this.sendUpstream(req, body, false); + if (response.statusCode === 401 && this.cookieProvider) { + response.resume(); + response = await this.sendUpstream(req, body, true); + } + res.writeHead(response.statusCode ?? 502, { + ...withoutCorsHeaders(response.headers), + ...cors, + }); + response.pipe(res); + } catch (err) { + this.respondRequestError(req, res, cors, err); + } + } + + private respondRequestError( + req: http.IncomingMessage, + res: http.ServerResponse, + cors: http.OutgoingHttpHeaders, + err: unknown, + ): void { + if (err instanceof BodyTooLargeError) { + if (!res.headersSent) res.writeHead(413, cors); + res.end("Request body too large."); + return; + } + this.log(`embed proxy: ${req.method} ${req.url} failed: ${errorText(err)}`); + if (!res.headersSent) res.writeHead(502, cors); + res.end(`Cannot reach the Tangent Shell at ${this.target.origin}.`); + } + + private async sendUpstream( + req: http.IncomingMessage, + body: Buffer, + forceRefresh: boolean, + ): Promise { + const cookie = await this.resolveCookie(forceRefresh); + return new Promise((resolve, reject) => { + const upstream = this.transport().request( + new URL(req.url ?? "/", this.target.origin), + { + method: req.method, + headers: upstreamHeaders(req.headers, this.target, cookie), + }, + resolve, + ); + upstream.on("error", reject); + upstream.end(body); + }); + } + + private handleUpgrade( + req: http.IncomingMessage, + socket: Duplex, + head: Buffer, + ): void { + if (!this.hostAllowed(req)) { + socket.end("HTTP/1.1 403 Forbidden\r\n\r\n"); + return; + } + if (this.checkOrigin(req.headers.origin) === false) { + socket.end("HTTP/1.1 403 Forbidden\r\n\r\n"); + return; + } + const close = () => socket.destroy(); + this.resolveCookie(false) + .then((cookie) => this.forwardUpgrade(req, socket, head, cookie)) + .catch((err: unknown) => { + this.log(`embed proxy: upgrade ${req.url} failed: ${errorText(err)}`); + close(); + }); + } + + private forwardUpgrade( + req: http.IncomingMessage, + socket: Duplex, + head: Buffer, + cookie: string | undefined, + ): void { + const upstream = this.connectUpstream(); + const close = () => { + socket.destroy(); + upstream.destroy(); + }; + upstream.once(this.isSecure() ? "secureConnect" : "connect", () => { + upstream.write(upgradeRequestHead(req, this.target, cookie)); + if (head.length > 0) upstream.write(head); + upstream.pipe(socket).pipe(upstream); + }); + upstream.on("error", (err) => { + this.log(`embed proxy: upgrade ${req.url} failed: ${err.message}`); + close(); + }); + upstream.on("close", close); + socket.on("error", close); + socket.on("close", close); + } + + private isSecure(): boolean { + return this.target.protocol === "https:"; + } + + private transport(): typeof http | typeof https { + return this.isSecure() ? https : http; + } + + private connectUpstream(): net.Socket { + const host = this.target.hostname.replace(/^\[|\]$/g, ""); + const port = Number(this.target.port) || (this.isSecure() ? 443 : 80); + if (!this.isSecure()) return net.connect({ host, port }); + const servername = net.isIP(host) ? undefined : host; + return tls.connect({ host, port, servername }); + } +} diff --git a/vscode-extension/tsconfig.json b/vscode-extension/tsconfig.json new file mode 100644 index 0000000000..cea90c431c --- /dev/null +++ b/vscode-extension/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "moduleResolution": "bundler", + "types": ["node", "vscode"], + "skipLibCheck": true, + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"], + "exclude": ["src/webview/chat.tsx"] +} diff --git a/vscode-extension/tsconfig.webview.json b/vscode-extension/tsconfig.webview.json new file mode 100644 index 0000000000..8516f52be2 --- /dev/null +++ b/vscode-extension/tsconfig.webview.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "lib": ["ES2023", "DOM", "DOM.Iterable"], + "types": [], + "jsx": "react-jsx" + }, + "include": ["src/webview/chat.tsx"], + "exclude": [] +}