Compare commits

...
Author SHA1 Message Date
abeatrix 3756d583dd fix(llms): report prompt-only input token usage totals
Subtract cache read and write tokens from reported input token counts in
runtime usage events and run results to avoid double counting cached tokens.

Also add reasoningTokens to AgentUsageEvent so usage metadata can represent
reasoning token counts when providers return them.
2026-06-30 13:59:50 -07:00
John Choi dbe15202e1 fix(cli): update ClinePass tests for forced-enabled behavior (#11990)
#11986 (Forcefully enable ClinePass on the CLI) hardcoded isClinePassEnabled: true in session-runtime.ts, provider-catalog.ts, and main.ts and removed the ext-cline-pass feature-flag check, but left the corresponding tests asserting the old flag-driven / disabled behavior. They fail on main (and every branch that merges it).

- session-runtime.test.ts: expect getLastUsedProviderSettings called with isClinePassEnabled: true.

- provider-catalog.test.ts: drop the obsolete getBooleanFlagEnabled('ext-cline-pass') assertion (source no longer reads the flag) and its now-unused mock; keep the isClinePassEnabled: true expectation.

- main.test.ts: the ClinePass flag is no longer read during startup, so getBooleanFlagEnabled is never called. Re-target the 'seed identity before flags' ordering assertion at refreshCliFeatureFlagsInBackground (which is still invoked after seeding), and wire that mock through featureFlagMocks.
2026-06-29 22:35:52 +02:00
BeeandJohn Choi 8d102db392 chore: remove console logs from compaction test (#11983)
Follow up on #11894, this PR removes the console logging code from the compaction unit test.

Co-authored-by: John Choi <97497948+johnwschoi@users.noreply.github.com>
2026-06-29 13:22:05 -07:00
MaxandMax Paulus 🥪 3dfd5dc31c fix(cli): recover missing interactive sessions on message reads (#11984)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-29 13:21:27 -07:00
John Choi 43ce9f3694 fix(vscode): exclude vitest-owned tests from the mocha integration compile (#11981)
The vscode test (Extension Integration Tests) job is red on main: the updateAutoApprovalSettings suite (added in #11929) is vitest-native but was being collected and run by the Mocha @vscode/test-cli runner, where vitest-only matchers (toHaveBeenCalledWith / toHaveBeenCalledOnce / not.toHaveBeenCalled) are not registered, failing with 'is not a function'.

build-tests.js already excludes bun:test-owned tests from the Node-based out/ tree (single source of truth for the runner split). Extend that same mechanism to also exclude vitest-owned tests (files importing from 'vitest'), so neither bun nor vitest suites are ever compiled into the mocha out/ tree. Verified locally: detection catches the state suite (123 non-mocha test files total) while preserving the existing 60 bun __tests__ exclusions. No coverage lost — these suites run under test:vitest / bun.
2026-06-29 12:51:52 -07:00
Tomás Barreiro f1c73fb48b Remove unused imports (#11988) 2026-06-29 12:39:17 -07:00
Tomás Barreiro abaa8383c4 Forcefully enable ClinePass on the CLI (#11986) 2026-06-29 21:31:51 +02:00
Renee Huang 3a5e372d73 docs: document ClinePass API usage (#11980)
* docs: add MiniMax M3, Qwen3.7 Max, Qwen3.7 Plus models to ClinePass page

* more updates

* explicitly direct to personal org

* making the clinepass page more detailed

* updates to cline provider wording

* docs: document ClinePass API usage

* chore: discard McpHub change from PR

* docs: simplify ClinePass model slug table

* updates

* nit
2026-06-29 10:59:02 -07:00
Saoud Rizwan cf3a59f0e2 chore(cli): release v3.0.34 2026-06-29 09:59:28 -07:00
Tomás BarreiroandSaoud Rizwan b3aee68ca5 Merge both options and remove credits link (#11973)
* Merge both options and remove credits link

* Remove import

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-29 09:48:57 -07:00
Tomás BarreiroandSaoud Rizwan cd8fd29063 Improve the ClinePass step wording (#11974)
* Improve the ClinePass step wording

* Use a blacklist instead

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-29 09:21:27 -07:00
Saoud Rizwan 7777d61311 fix(cli): suppress ClinePass notice after onboarding (#11975) 2026-06-29 09:20:35 -07:00
Renee Huang 64fc3f372e docs: add MiniMax M3, Qwen3.7 Max, Qwen3.7 Plus models to ClinePass page (#11849)
* docs: add MiniMax M3, Qwen3.7 Max, Qwen3.7 Plus models to ClinePass page

* more updates

* explicitly direct to personal org

* making the clinepass page more detailed

* updates to cline provider wording
2026-06-29 07:28:38 -07:00
Saoud Rizwan 4175677e71 chore(cli): release v3.0.33 2026-06-28 23:45:56 -07:00
Saoud Rizwan 4934450947 fix(cli): show ClinePass subscription URL fallback (#11961)
* fix(cli): show ClinePass subscription URL fallback

* fix(cli): move ClinePass URL fallback below options
2026-06-28 23:33:58 -07:00
Saoud Rizwan b0a2d8a223 fix(cli): hide ClinePass promo for ClinePass users (#11963)
* fix(cli): hide ClinePass promo for ClinePass users

* fix(cli): expand ClinePass subscription card

* fix(cli): tune ClinePass subscription card height
2026-06-28 23:33:19 -07:00
Saoud Rizwan d9f1d862a5 fix(cli): use adaptive plan accent for ClinePass prompts (#11962) 2026-06-28 23:12:30 -07:00
Saoud Rizwan f8d73f3811 chore(cli): release v3.0.32 2026-06-28 21:43:18 -07:00
Saoud Rizwan 9aac8340dc chore(sdk): regenerate bun.lock for v0.0.54
Align resolved workspace versions in bun.lock with the v0.0.54 package
bumps. bun pm pack substitutes workspace:* deps using the version
recorded in bun.lock, so a stale lock made packed inter-package deps
resolve to 0.0.53, failing check-publish and the node smoke test (which
then pulled the old published @cline/shared from npm).
2026-06-28 21:24:41 -07:00
Saoud Rizwan 242b5ebff6 chore(sdk): release v0.0.54 2026-06-28 20:54:47 -07:00
Tomás Barreiro 7ca41fdb7d Improve ClinePass onboarding UX (#11959)
* Prevent ClinePass onboarding flicker

* Make the clinepass step scrollable and remove details
2026-06-28 20:49:27 -07:00
Saoud Rizwan 000918989f fix(cli): make ClinePass subscription screen selectable (#11957) 2026-06-28 20:12:04 -07:00
Saoud Rizwan 9560b6d625 fix(cli): use ClinePass as one word consistently (#11956) 2026-06-28 18:41:26 -07:00
Saoud Rizwan c7304097a7 fix(cli): update ClinePass provider UI copy (#11953)
* fix(cli): update ClinePass provider UI copy

* fix(llms): rename Cline provider display name

* fix(cli): separate ClinePass billing links
2026-06-28 17:57:11 -07:00
Tomás BarreiroandSaoud Rizwan 674a6022ee Add an intermediate step before going to ClinePass model selection (#11947)
* Add an intermediate step before going to model selection

* fix type issues

* use allSettled

* fix(cli): serialize ClinePass subscription checks

* fix(cli): handle missing ClinePass plan as unsubscribed

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-28 16:13:25 -07:00
Saoud Rizwan dbf0775384 fix(llms): keep error detail extraction for Error instances (#11949)
PR #11928 added an `instanceof Error` branch to extractErrorMessage to
preserve transport-error wrappers (e.g. "fetch failed: SocketError: ...
(UND_ERR_SOCKET)"), but that branch regressed two cases:

- Generic SDK wrappers like "No output generated. Check the stream for
  errors." were prepended to the real cause instead of being dropped.
- Errors carrying structured detail on responseBody/detail/error fields
  surfaced the bland top-level .message ("Bad Request") instead of the
  detail ("Instructions are required").

The Error branch now drops known generic wrappers in favor of the cause
and extracts structured detail from the error's own fields, while keeping
the transport-wrapper concat behavior #11928 intended.
2026-06-28 15:27:46 -07:00
Saoud Rizwan e130b45eb0 feat(cli): promote Cline Pass in startup notice (#11948) 2026-06-28 13:14:27 -07:00
6f4dbae86f Improve the ClinePass onboarding experience on the CLI [ENG-2236] (#11946)
* Improve the ClinePass onboarding experience on the CLI

* Update apps/cli/src/tui/views/onboarding/screens.tsx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* address comments

* style(cli): format ClinePass onboarding warning

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-28 10:53:27 -07:00
Saoud Rizwan c7de31ae24 ci(vscode): gate legacy publish on test job (#11936) 2026-06-27 19:33:37 -07:00
Saoud Rizwan 45dddb9a4e ci(vscode): add legacy extension publish workflow (#11935) 2026-06-27 19:17:46 -07:00
BeeandSaoud Rizwan e32caee96b fix: basic compaction token budgeting (#11894)
* Improve compaction token budgeting

Use MessageWithMetadata.metrics input/output token counts when estimating message size for compaction, falling back to the existing chars/3 heuristic only when metrics are unavailable. This makes trigger decisions and post-compaction accounting use provider-reported token usage instead of relying only on serialized character estimates.

When an explicit compaction maxInputTokens budget is configured and the model exposes maxTokens, reserve half of the model output budget before triggering compaction. This gives the next provider request room for completion tokens and reduces edge cases where the local context estimate passes but the provider rejects the prompt as exceeding its limit.

Keep explicit reserveTokens and thresholdRatio overrides intact, and add regression coverage for metric-based token estimation, fallback estimation, and output-token-aware trigger budgeting.

* new target tokens and trigger tokens value

* fixes

* use imports and add unit test

* resolveMaxInputTokens

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-27 16:04:56 -07:00
Saoud Rizwan 8f6dae0ac0 fix(vscode): sync auto-approve task settings (#11929) 2026-06-27 15:13:14 -07:00
Saoud Rizwan 263b58f8c3 fix(llms): preserve fetch error cause details (#11928) 2026-06-27 14:51:56 -07:00
Saoud Rizwan b193a81ac9 fix: prevent API key field clearing on settings load (#11925)
* fix: prevent API key field clearing on settings load

* fix: cancel API key save on mask hydration
2026-06-27 14:32:14 -07:00
John Choi 92806c60ca fix(agents): derive messageModelInfo in the provider/model runtime path (#11903)
The standalone AgentRuntime({ providerId, modelId }) constructor built the gateway model in resolveRuntimeConfig and returned { ...rest, model } without deriving messageModelInfo. The prebuilt-model path preserves it, and core sessions populate it via buildMessageModelInfo, but standalone SDK callers lost it -- so assistant-message modelInfo and model-tagged telemetry (reasoning tokens, action-follow-through) emitted without provider/model dimensions.

Derive messageModelInfo as { id: modelId, provider: providerId } in that path (family omitted; it is optional and unavailable here). An explicit caller-provided messageModelInfo still wins. Adds provider-form tests covering both the derived and explicit-override cases.
2026-06-26 18:23:54 -07:00
Saoud Rizwan 3a05171e30 fix(sdk): preserve failed run error messages (#11904) 2026-06-26 18:04:53 -07:00
Saoud Rizwan a6e315a4a6 chore(cli): release v3.0.31 2026-06-26 17:37:36 -07:00
78 changed files with 2776 additions and 604 deletions
@@ -0,0 +1,294 @@
name: ext-vscode-publish-legacy
# Publishes the legacy (pre-SDK-migration) VS Code extension from the
# `legacy-extension` branch. This branch holds the npm-based 3.89.x codebase,
# rolled forward under a 4.0.x version so existing 4.0.0 users still receive
# the update. The main `ext-vscode-publish-stable.yml` workflow (bun-based)
# stays the path for releasing main once the SDK migration is solid.
#
# This workflow lives on and is dispatched from `main` (so it satisfies the
# default-branch dispatch requirement), but it checks out and builds the
# `legacy-extension` branch.
on:
workflow_dispatch:
inputs:
release-type:
description: "Choose release type (release or pre-release)"
required: true
default: "release"
type: choice
options:
- pre-release
- release
branch:
description: "Branch holding the legacy extension code"
required: true
default: "legacy-extension"
type: string
permissions:
contents: write
packages: write
checks: write
pull-requests: write
concurrency:
group: ext-vscode-publish-legacy-${{ github.event.inputs.branch }}
cancel-in-progress: false
jobs:
# Gate the publish on the legacy branch's own npm-based test suite. We can't
# reuse ./.github/workflows/ext-vscode-test.yml here — on main that's the
# bun-based suite and it would test main, not the legacy branch — so the
# essential quality + test steps are inlined against the checked-out legacy
# branch.
test:
name: Test Legacy Extension
runs-on: ubuntu-latest
defaults:
run:
working-directory: apps/vscode
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.branch }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'npm'
cache-dependency-path: |
apps/vscode/package-lock.json
apps/vscode/webview-ui/package-lock.json
- name: Install extension dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode ci
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install webview-ui dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode/webview-ui ci
- name: Run Quality Checks (lint + typecheck)
run: npm run ci:check-all
- name: Build Tests and Extension
id: build_step
run: npm run ci:build
- name: Unit Tests
if: ${{ !cancelled() && steps.build_step.outcome == 'success' }}
run: npm run test:unit
- name: Extension Integration Tests
if: ${{ !cancelled() && steps.build_step.outcome == 'success' }}
run: xvfb-run -a npm run test:coverage
- name: Webview Tests
if: ${{ !cancelled() && steps.build_step.outcome == 'success' }}
run: |
cd webview-ui
npm run test:coverage
publish:
needs: test
name: Publish Legacy Extension
runs-on: ubuntu-latest
environment: publish
defaults:
run:
working-directory: apps/vscode
steps:
# Check out the legacy branch (NOT main). fetch-depth: 0 + tags so we
# can create/push the release tag and compute the previous tag.
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.branch }}
fetch-depth: 0
fetch-tags: true
lfs: true
- name: Resolve Release Tag
id: resolve_tag
working-directory: ${{ github.workspace }}
env:
BRANCH: ${{ github.event.inputs.branch }}
run: |
# Tag is derived from the package version on the legacy branch.
VERSION=$(node -p "require('./apps/vscode/package.json').version")
TAG="v$VERSION"
if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.]+)?$ ]]; then
echo "Error: derived tag '$TAG' does not match vX.Y.Z"
exit 1
fi
TAG_REF="refs/tags/$TAG"
HEAD_SHA=$(git rev-parse HEAD)
if git show-ref --verify --quiet "$TAG_REF"; then
TAG_SHA=$(git rev-list -n 1 "$TAG_REF^{commit}")
if [[ "$TAG_SHA" != "$HEAD_SHA" ]]; then
echo "Error: tag '$TAG' already exists at $TAG_SHA, not at branch head ($HEAD_SHA)"
exit 1
fi
echo "Tag '$TAG' already exists at branch head. Continuing."
else
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag "$TAG" "$HEAD_SHA"
git push origin "$TAG_REF"
echo "Created and pushed tag '$TAG' from $BRANCH head $HEAD_SHA."
fi
echo "tag=$TAG" >> $GITHUB_OUTPUT
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install extension dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode install --include=optional
- name: Install webview-ui dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode/webview-ui install --include=optional
- name: Install Publishing Tools
run: npm install -g @vscode/vsce ovsx
- name: Get Version
id: get_version
run: |
VERSION=$(node -p "require('./package.json').version")
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Verify Tag Matches Package Version
run: |
TAG="${{ steps.resolve_tag.outputs.tag }}"
VERSION="v${{ steps.get_version.outputs.version }}"
if [[ "$TAG" != "$VERSION" ]]; then
echo "Error: tag '$TAG' does not match package version '$VERSION'"
exit 1
fi
echo "Tag and package version match: $TAG"
- name: Verify Changelog Entry
working-directory: ${{ github.workspace }}
run: |
EXPECTED_HEADING="## [${{ steps.get_version.outputs.version }}]"
FIRST_HEADING=$(grep -m 1 '^## \[' CHANGELOG.md || true)
if [[ "$FIRST_HEADING" != "$EXPECTED_HEADING" ]]; then
echo "Error: CHANGELOG.md must start with '$EXPECTED_HEADING' before publishing."
echo "Current first release heading: ${FIRST_HEADING:-<none>}"
exit 1
fi
echo "Found changelog entry for ${{ steps.get_version.outputs.version }}"
- name: Verify Marketplace Tokens
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
OVSX_PAT: ${{ secrets.OVSX_PAT }}
run: |
if [[ -z "$VSCE_PAT" ]]; then
echo "Error: VSCE_PAT is required to publish the stable VS Code extension."
exit 1
fi
if [[ -z "$OVSX_PAT" ]]; then
echo "Error: OVSX_PAT is required to publish the stable Open VSX extension."
exit 1
fi
echo "Marketplace publish tokens are configured."
- name: Package and Publish Extension
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
OVSX_PAT: ${{ secrets.OVSX_PAT }}
CLINE_ENVIRONMENT: production
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
# OpenTelemetry production defaults (can be overridden at runtime)
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
OTEL_LOGS_EXPORTER: otlp
OTEL_METRICS_EXPORTER: otlp
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
RELEASE_TYPE: ${{ github.event.inputs.release-type }}
run: |
# Swap README.marketplace.md into README.md so both the GitHub
# release artifact (vsce package below) and the marketplace
# publish (npm run publish:marketplace below, which swaps
# internally as an idempotent no-op) ship the same README.
node scripts/marketplace-readme.mjs swap-in
trap 'node scripts/marketplace-readme.mjs restore' EXIT
# Required to generate the .vsix
vsce package --allow-package-secrets sendgrid --out "cline-${{ steps.get_version.outputs.version }}.vsix"
if [ "$RELEASE_TYPE" = "pre-release" ]; then
npm run publish:marketplace:prerelease
echo "Successfully published pre-release version ${{ steps.get_version.outputs.version }} to VS Code Marketplace and Open VSX Registry"
else
npm run publish:marketplace
echo "Successfully published release version ${{ steps.get_version.outputs.version }} to VS Code Marketplace and Open VSX Registry"
fi
- name: Get Previous Tag
id: prev_tag
working-directory: ${{ github.workspace }}
run: |
CURRENT_TAG="${{ steps.resolve_tag.outputs.tag }}"
PREV_TAG=$(git describe --tags --abbrev=0 "$CURRENT_TAG^" 2>/dev/null || echo "")
echo "prev_tag=$PREV_TAG" >> $GITHUB_OUTPUT
- name: Get Changelog Entry
id: changelog
working-directory: ${{ github.workspace }}
run: |
# Get content between first ## [ and second ## [
CONTENT=$(awk '/^## \[/{if(found) exit; found=1; next} found{print}' CHANGELOG.md)
echo "content<<EOF" >> $GITHUB_OUTPUT
echo "$CONTENT" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.resolve_tag.outputs.tag }}
files: "apps/vscode/*.vsix"
body: |
${{ steps.changelog.outputs.content }}
**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.prev_tag.outputs.prev_tag }}...${{ steps.resolve_tag.outputs.tag }}
prerelease: ${{ github.event.inputs.release-type == 'pre-release' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Post release to Slack
uses: slackapi/slack-github-action@v3.0.1
with:
method: chat.postMessage
token: ${{ secrets.SLACK_RELEASE_BOT_TOKEN }}
payload: |
channel: "C0APVKGGZFC"
text: "Cline ${{ steps.resolve_tag.outputs.tag }} (legacy)"
blocks:
- type: "section"
text:
type: "mrkdwn"
text: "*Cline ${{ steps.resolve_tag.outputs.tag }} (legacy)*"
- type: "section"
text:
type: "mrkdwn"
text: ${{ toJSON(steps.changelog.outputs.content) }}
- type: "context"
elements:
- type: "mrkdwn"
text: "Full Changelog: https://github.com/${{ github.repository }}/compare/${{ steps.prev_tag.outputs.prev_tag }}...${{ steps.resolve_tag.outputs.tag }}"
+31
View File
@@ -1,5 +1,36 @@
# Cline CLI Changelog
## 3.0.34
- Fixed the ClinePass upgrade notice appearing immediately after completing onboarding.
- Improved the wording of the ClinePass onboarding step.
- Streamlined the Cline provider picker by merging the subscription and usage/billing options into one and removing the credits link.
## 3.0.33
- Show a ClinePass subscription URL as a fallback during onboarding so you can still subscribe if the subscription screen can't open automatically
- Hide the ClinePass promo for users who already have a ClinePass subscription
- Use an adaptive plan accent color for ClinePass prompts so they fit the active theme
## 3.0.32
- Improved the ClinePass onboarding experience
- Added an intermediate step before going to ClinePass model selection
- Made the ClinePass subscription screen selectable
- Promoted ClinePass in the startup notice
- Used "ClinePass" as one word consistently and refined the provider UI copy
- More accurate context compaction and clearer error messages (from SDK v0.0.54)
## 3.0.31
- Show when request cost is covered by your Cline subscription
- Prompt to switch to ClinePass when you run out of credits, and list ClinePass features in the not-subscribed message
- Added an option to open the subscription page from the ClinePass options
- Added marketplace uninstall support and surfaced plugin-bundled skills
- Require quoted prompts for one-shot mode
- Capped MCP tool names at 64 characters for OpenAI-compatible providers
- Updated coupon code
## 3.0.30
- Added a token count to the status bar, shown alongside cost
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@cline/cli",
"displayName": "cline",
"version": "3.0.30",
"version": "3.0.34",
"description": "Autonomous coding agent CLI - capable of creating/editing files, running commands, using the browser, and more",
"type": "module",
"publishConfig": {
@@ -101,7 +101,7 @@ describe("buildConnectorStartRequest", () => {
expect(request.apiKey).toBe("env-openrouter-key");
expect(request.model).toBe("anthropic/claude-sonnet-4.6");
expect(mockGetLastUsedProviderSettings).toHaveBeenCalledWith({
isClinePassEnabled: false,
isClinePassEnabled: true,
});
});
+1 -3
View File
@@ -16,7 +16,6 @@ import {
import type { CliLoggerAdapter } from "../logging/adapter";
import { resolveSystemPrompt } from "../runtime/prompt";
import { resolveCliSessionMetadata } from "../utils/enterprise";
import { getCliFeatureFlagsService } from "../utils/feature-flags";
import { resolveWorkspaceRoot } from "../utils/helpers";
import {
parseLocalRowMetadata,
@@ -64,8 +63,7 @@ export async function buildConnectorStartRequest(input: {
const providerSettingsManager = new ProviderSettingsManager();
const lastUsedProviderSettings =
providerSettingsManager.getLastUsedProviderSettings({
isClinePassEnabled:
getCliFeatureFlagsService().getBooleanFlagEnabled("ext-cline-pass"),
isClinePassEnabled: true,
});
const provider = normalizeProviderId(
input.options.provider?.trim() ||
+37 -16
View File
@@ -1,7 +1,10 @@
// @jsxImportSource @opentui/react
import type { ChoiceContext } from "@opentui-ui/dialog";
import { useDialogKeyboard } from "@opentui-ui/dialog/react";
import open from "open";
import { useCallback, useMemo, useState } from "react";
import { palette } from "../tui/palette";
import { getCliSubscriptionUrl } from "../utils/cline-pass-errors";
import type { CliMigrationNotice } from "./notice";
export function MigrationNoticeContent(
@@ -10,10 +13,29 @@ export function MigrationNoticeContent(
},
) {
const { dialogId, notice, resolve } = props;
const subscriptionUrl = useMemo(() => getCliSubscriptionUrl(), []);
const [status, setStatus] = useState<string | undefined>();
const openSubscriptionPage = useCallback(() => {
setStatus("Opening ClinePass in your browser...");
void open(subscriptionUrl, { wait: false })
.then(() => {
setStatus("Opened ClinePass in your browser.");
})
.catch(() => {
setStatus(
"Could not open the browser automatically. Use the URL below.",
);
});
}, [subscriptionUrl]);
useDialogKeyboard((key) => {
if (key.name === "escape") {
resolve(true);
return;
}
if (key.name === "return" || key.name === "enter") {
openSubscriptionPage();
}
}, dialogId);
@@ -22,25 +44,24 @@ export function MigrationNoticeContent(
<text fg={palette.act}>{notice.title}</text>
<box flexDirection="column">
<text selectable>
We rebuilt the CLI from the ground up using the new Cline SDK. Learn
more:{" "}
<a href="https://github.com/cline/cline">
<span fg={palette.act}>https://github.com/cline/cline</span>
</a>
ClinePass is a $9.99/month subscription plan to get access to the
latest open-weight coding models with enough quota for day-to-day
work, at a much lower cost than paying API costs directly.
</text>
<text selectable>
Running{" "}
<span fg="#98c379" bg="#1f2937">
{" cline "}
</span>{" "}
now opens the terminal UI. To open Kanban, use /quit and run{" "}
<span fg="#98c379" bg="#1f2937">
{" cline kanban "}
</span>{" "}
in your terminal
<text selectable>Try it now with a limited-time promo for $1.99.</text>
</box>
<box flexDirection="row">
<text fg={palette.act} selectable>
<a href={subscriptionUrl}>{subscriptionUrl}</a>
</text>
</box>
<text fg={palette.muted}>Press Esc to close</text>
<box flexDirection="row">
<box paddingX={1} backgroundColor={palette.act}>
<text fg={palette.textOnSelection}>Open ClinePass</text>
</box>
</box>
{status && <text fg={palette.muted}>{status}</text>}
<text fg={palette.muted}>Press Enter to open, Esc to close</text>
</box>
);
}
+71 -9
View File
@@ -1,11 +1,18 @@
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
import {
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { dirname, join } from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import {
getClineCliMigrationNotice,
markClineCliMigrationNoticeShown,
resolveCliNoticeStatePath,
shouldSuppressClineCliMigrationNoticeForActiveProvider,
} from "./notice";
const tempDirs: string[] = [];
@@ -26,8 +33,25 @@ describe("migration notice", () => {
it("returns the notice for a fresh data dir", () => {
const dataDir = createTempDataDir();
expect(getClineCliMigrationNotice(dataDir)?.title).toBe(
"Welcome to the new Cline CLI",
expect(getClineCliMigrationNotice(dataDir)?.title).toBe("Try ClinePass");
});
it("shows when only the old Kanban notice was marked as shown", () => {
const dataDir = createTempDataDir();
const noticePath = resolveCliNoticeStatePath(dataDir);
mkdirSync(dirname(noticePath), { recursive: true, mode: 0o700 });
writeFileSync(
noticePath,
`${JSON.stringify(
{ shown: { "cline-cli-tui-default": true } },
null,
2,
)}\n`,
"utf8",
);
expect(getClineCliMigrationNotice(dataDir)?.id).toBe(
"cline-cli-cline-pass-intro",
);
});
@@ -46,7 +70,7 @@ describe("migration notice", () => {
expect(
getClineCliMigrationNotice(dataDir, {
CLINE_FORCE_MIGRATION_NOTICE: "1",
CLINE_FORCE_CLINE_PASS_NOTICE: "1",
}),
).toBeDefined();
});
@@ -56,18 +80,56 @@ describe("migration notice", () => {
expect(
getClineCliMigrationNotice(dataDir, {
CLINE_DISABLE_MIGRATION_NOTICE: "1",
CLINE_DISABLE_CLINE_PASS_NOTICE: "1",
}),
).toBeUndefined();
});
it("does not show when ClinePass is already the active provider", () => {
const dataDir = createTempDataDir();
expect(
getClineCliMigrationNotice(
dataDir,
{},
{ activeProviderId: "cline-pass" },
),
).toBeUndefined();
});
it("suppresses the active ClinePass provider even when the provider id has surrounding whitespace", () => {
expect(
shouldSuppressClineCliMigrationNoticeForActiveProvider(" cline-pass "),
).toBe(true);
});
it("does not suppress the active ClinePass provider when forced", () => {
expect(
shouldSuppressClineCliMigrationNoticeForActiveProvider("cline-pass", {
CLINE_FORCE_CLINE_PASS_NOTICE: "1",
}),
).toBe(false);
});
it("shows for the active ClinePass provider when forced", () => {
const dataDir = createTempDataDir();
expect(
getClineCliMigrationNotice(
dataDir,
{ CLINE_FORCE_CLINE_PASS_NOTICE: "1" },
{ activeProviderId: "cline-pass" },
),
).toBeDefined();
});
it("shows when forced even if disabled through the environment", () => {
const dataDir = createTempDataDir();
expect(
getClineCliMigrationNotice(dataDir, {
CLINE_DISABLE_MIGRATION_NOTICE: "1",
CLINE_FORCE_MIGRATION_NOTICE: "1",
CLINE_DISABLE_CLINE_PASS_NOTICE: "1",
CLINE_FORCE_CLINE_PASS_NOTICE: "1",
}),
).toBeDefined();
});
@@ -78,7 +140,7 @@ describe("migration notice", () => {
markClineCliMigrationNoticeShown(dataDir);
const rawState = readFileSync(resolveCliNoticeStatePath(dataDir), "utf8");
expect(rawState).toContain("cline-cli-tui-default");
expect(rawState).toContain("cline-cli-cline-pass-intro");
expect(getClineCliMigrationNotice(dataDir)).toBeUndefined();
});
});
+31 -5
View File
@@ -2,15 +2,19 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { resolveClineDataDir } from "@cline/shared/storage";
const NOTICE_ID = "cline-cli-tui-default";
const FORCE_NOTICE_ENV = "CLINE_FORCE_MIGRATION_NOTICE";
const DISABLE_NOTICE_ENV = "CLINE_DISABLE_MIGRATION_NOTICE";
const NOTICE_ID = "cline-cli-cline-pass-intro";
const FORCE_NOTICE_ENV = "CLINE_FORCE_CLINE_PASS_NOTICE";
const DISABLE_NOTICE_ENV = "CLINE_DISABLE_CLINE_PASS_NOTICE";
export interface CliMigrationNotice {
id: string;
title: string;
}
export interface CliMigrationNoticeOptions {
activeProviderId?: string;
}
interface CliNoticeState {
shown: Record<string, boolean>;
}
@@ -49,6 +53,19 @@ function readNoticeState(filePath: string): CliNoticeState {
return { shown };
}
function isForceNoticeEnabled(env: NodeJS.ProcessEnv): boolean {
return env[FORCE_NOTICE_ENV]?.trim() === "1";
}
export function shouldSuppressClineCliMigrationNoticeForActiveProvider(
activeProviderId: string | undefined,
env: NodeJS.ProcessEnv = process.env,
): boolean {
return (
activeProviderId?.trim() === "cline-pass" && !isForceNoticeEnabled(env)
);
}
export function resolveCliNoticeStatePath(
dataDir = resolveClineDataDir(),
): string {
@@ -58,20 +75,29 @@ export function resolveCliNoticeStatePath(
export function getClineCliMigrationNotice(
dataDir = resolveClineDataDir(),
env: NodeJS.ProcessEnv = process.env,
options: CliMigrationNoticeOptions = {},
): CliMigrationNotice | undefined {
const noticePath = resolveCliNoticeStatePath(dataDir);
const noticeState = readNoticeState(noticePath);
const forceNotice = env[FORCE_NOTICE_ENV]?.trim() === "1";
const forceNotice = isForceNoticeEnabled(env);
const disableNotice = env[DISABLE_NOTICE_ENV]?.trim() === "1";
if (disableNotice && !forceNotice) {
return undefined;
}
if (
shouldSuppressClineCliMigrationNoticeForActiveProvider(
options.activeProviderId,
env,
)
) {
return undefined;
}
if (noticeState.shown[NOTICE_ID] && !forceNotice) {
return undefined;
}
return {
id: NOTICE_ID,
title: "Welcome to the new Cline CLI",
title: "Try ClinePass",
};
}
+60 -10
View File
@@ -1,6 +1,9 @@
import { fstatSync } from "node:fs";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { CliMigrationNotice } from "./kanban-migration/notice";
import type {
CliMigrationNotice,
CliMigrationNoticeOptions,
} from "./kanban-migration/notice";
/** Real `fstatSync`: used when tests stub only stdin (fd 0); throwing for every fd breaks imports and session I/O. */
const fsActual = vi.hoisted(() => ({
@@ -59,9 +62,13 @@ const dashboardMocks = vi.hoisted(() => ({
runDashboardCommand: vi.fn(),
}));
const migrationNoticeMocks = vi.hoisted(() => ({
getClineCliMigrationNotice: vi.fn<() => CliMigrationNotice | undefined>(
() => undefined,
),
getClineCliMigrationNotice: vi.fn<
(
dataDir?: string,
env?: NodeJS.ProcessEnv,
options?: CliMigrationNoticeOptions,
) => CliMigrationNotice | undefined
>(() => undefined),
markClineCliMigrationNoticeShown: vi.fn(),
}));
const updateMocks = vi.hoisted(() => ({
@@ -115,6 +122,7 @@ const telemetryMocks = vi.hoisted(() => ({
const featureFlagMocks = vi.hoisted(() => ({
getBooleanFlagEnabled: vi.fn(() => false),
setCliFeatureFlagsAccountContext: vi.fn(),
refreshCliFeatureFlagsInBackground: vi.fn(),
}));
function forcePromptModeInput() {
@@ -179,7 +187,8 @@ vi.mock("./utils/feature-flags", () => ({
getCliFeatureFlagsService: () => ({
getBooleanFlagEnabled: featureFlagMocks.getBooleanFlagEnabled,
}),
refreshCliFeatureFlagsInBackground: vi.fn(),
refreshCliFeatureFlagsInBackground:
featureFlagMocks.refreshCliFeatureFlagsInBackground,
setCliFeatureFlagsAccountContext:
featureFlagMocks.setCliFeatureFlagsAccountContext,
}));
@@ -258,6 +267,7 @@ describe("runCli lightweight command dispatch", () => {
featureFlagMocks.getBooleanFlagEnabled.mockReset();
featureFlagMocks.getBooleanFlagEnabled.mockReturnValue(false);
featureFlagMocks.setCliFeatureFlagsAccountContext.mockReset();
featureFlagMocks.refreshCliFeatureFlagsInBackground.mockReset();
kanbanMocks.launchKanban.mockReset();
kanbanMocks.launchKanban.mockResolvedValue(0);
dashboardMocks.runDashboardCommand.mockReset();
@@ -630,8 +640,8 @@ describe("runCli lightweight command dispatch", () => {
it("passes the migration notice marker into interactive mode", async () => {
const notice = {
id: "cline-cli-tui-default",
title: "Welcome to the new Cline CLI",
id: "cline-cli-cline-pass-intro",
title: "Try ClinePass",
};
migrationNoticeMocks.getClineCliMigrationNotice.mockReturnValue(notice);
Object.defineProperty(process.stdout, "isTTY", {
@@ -662,6 +672,37 @@ describe("runCli lightweight command dispatch", () => {
).toHaveBeenCalledTimes(1);
});
it("passes the active ClinePass provider into the migration notice gate", async () => {
providerSettingsMocks.getLastUsedProviderSettings.mockReturnValue({
provider: "cline-pass",
model: "cline-pass/test-model",
});
Object.defineProperty(process.stdout, "isTTY", {
value: true,
configurable: true,
});
process.argv = ["bun", "src/index.ts"];
const { runCli } = await import("./main");
await expect(runCli()).resolves.toBeUndefined();
expect(
migrationNoticeMocks.getClineCliMigrationNotice,
).toHaveBeenCalledWith(undefined, process.env, {
activeProviderId: "cline-pass",
});
expect(runtimeMocks.runInteractive).toHaveBeenCalledWith(
expect.objectContaining({
providerId: "cline-pass",
}),
expect.anything(),
undefined,
expect.objectContaining({
initialNotice: undefined,
}),
);
});
it("does not start OAuth before onboarding in interactive mode", async () => {
authMocks.isOAuthProvider.mockReturnValue(true);
authMocks.normalizeProviderId.mockReturnValue("cline");
@@ -942,7 +983,7 @@ describe("runCli lightweight command dispatch", () => {
);
});
it("seeds feature flag identity from persisted Cline account id before checking flags", async () => {
it("seeds feature flag identity from persisted Cline account id before refreshing flags", async () => {
const clineSettings = {
provider: "cline",
model: "anthropic/claude-sonnet-4.6",
@@ -961,11 +1002,14 @@ describe("runCli lightweight command dispatch", () => {
expect(
featureFlagMocks.setCliFeatureFlagsAccountContext,
).toHaveBeenCalledWith({ id: "acct-startup" });
// The account identity must be seeded before flags are refreshed/used so
// the background refresh resolves flags for the correct account.
expect(
featureFlagMocks.setCliFeatureFlagsAccountContext.mock
.invocationCallOrder[0],
).toBeLessThan(
featureFlagMocks.getBooleanFlagEnabled.mock.invocationCallOrder[0],
featureFlagMocks.refreshCliFeatureFlagsInBackground.mock
.invocationCallOrder[0],
);
});
@@ -1297,7 +1341,13 @@ describe("runCli lightweight command dispatch", () => {
runtimeMocks.runAgent.mockClear();
forcePromptModeInput();
process.argv = ["bun", "src/index.ts", "--compaction", "basic", "say hello"];
process.argv = [
"bun",
"src/index.ts",
"--compaction",
"basic",
"say hello",
];
const { runCli } = await import("./main");
+4 -4
View File
@@ -20,7 +20,6 @@ import {
CLI_COMPACTION_MODE_EXPECTED_TEXT,
} from "./utils/compaction-mode";
import {
getCliFeatureFlagsService,
refreshCliFeatureFlagsInBackground,
setCliFeatureFlagsAccountContext,
} from "./utils/feature-flags";
@@ -956,8 +955,7 @@ export async function runCli(): Promise<void> {
refreshCliFeatureFlagsInBackground();
const lastUsedProviderSettings =
providerSettingsManager.getLastUsedProviderSettings({
isClinePassEnabled:
getCliFeatureFlagsService().getBooleanFlagEnabled("ext-cline-pass"),
isClinePassEnabled: true,
});
const provider = normalizeProviderId(
args.provider?.trim() || lastUsedProviderSettings?.provider || "cline",
@@ -1182,7 +1180,9 @@ export async function runCli(): Promise<void> {
if (!launchConfigView && process.stdin.isTTY && process.stdout.isTTY) {
const { getClineCliMigrationNotice, markClineCliMigrationNoticeShown } =
await import("./kanban-migration/notice");
initialNotice = getClineCliMigrationNotice();
initialNotice = getClineCliMigrationNotice(undefined, process.env, {
activeProviderId: provider,
});
if (initialNotice) {
markInitialNoticeShown = () => {
markClineCliMigrationNoticeShown();
@@ -365,6 +365,48 @@ describe("createInteractiveSessionRuntime", () => {
expect(runtime.getActiveSessionId()).toBe("session-2");
});
it("recovers empty read-driven restarts when the active interactive session disappeared", async () => {
const manager = makeManager();
manager.readMessages.mockRejectedValueOnce(
new SessionNotFoundError("session-1"),
);
const runtime = makeRuntime(manager);
await runtime.ensureReady();
await runtime.restartWithCurrentMessages();
expect(manager.readMessages).toHaveBeenCalledWith("session-1");
expect(manager.start).toHaveBeenCalledTimes(2);
expect(manager.start).toHaveBeenNthCalledWith(
2,
expect.objectContaining({
initialMessages: [],
}),
);
expect(runtime.getActiveSessionId()).toBe("session-2");
});
it("does not restart with stale messages when another operation changes the active session during a read", async () => {
const manager = makeManager();
let runtime!: ReturnType<typeof makeRuntime>;
manager.readMessages.mockImplementationOnce(async () => {
await runtime.restartEmpty();
return [
{
role: "user" as const,
content: [{ type: "text" as const, text: "stale" }],
},
];
});
runtime = makeRuntime(manager);
await runtime.ensureReady();
await runtime.restartWithCurrentMessages();
expect(manager.start).toHaveBeenCalledTimes(2);
expect(runtime.getActiveSessionId()).toBe("session-2");
});
it("waits for missing-session recovery before cleanup disposes the manager", async () => {
const manager = makeManager();
const recoveryRead = deferred<Message[]>();
@@ -49,6 +49,13 @@ type CurrentTurnResult = Awaited<ReturnType<CliCore["send"]>>;
type AskQuestionRef = {
current: ((question: string, options: string[]) => Promise<string>) | null;
};
type CurrentMessagesRead =
| { messages: Message[]; status: "read" }
| { messages: Message[]; status: "recovered" }
| { messages: Message[]; status: "stale" };
type MissingSessionRecovery = {
messages: Message[];
};
type ToolPolicyResolver = (
toolName: string,
) => NonNullable<Config["toolPolicies"]>[string];
@@ -103,7 +110,9 @@ export function createInteractiveSessionRuntime(input: {
let shutdownRequested = false;
let activeSessionId = "";
let abortRequested = false;
let missingSessionRecoveryPromise: Promise<void> | undefined;
let missingSessionRecoveryPromise:
| Promise<MissingSessionRecovery>
| undefined;
// A reset can happen while an earlier manager.start() is still in flight.
// Bump this before resets and restarts so stale starts cannot become active.
let sessionStartGeneration = 0;
@@ -275,14 +284,34 @@ export function createInteractiveSessionRuntime(input: {
return await startupPromise;
};
const readCurrentMessages = async (): Promise<Message[]> => {
if (!sessionManager || !activeSessionId) {
return [];
const readCurrentMessages = async (): Promise<CurrentMessagesRead> => {
const manager = sessionManager;
const sessionId = activeSessionId;
if (!manager || !sessionId) {
return { messages: [], status: "read" };
}
try {
const messages = (await manager.readMessages(sessionId)) ?? [];
return {
messages,
status: activeSessionId === sessionId ? "read" : "stale",
};
} catch (error) {
if (
abortRequested ||
shutdownRequested ||
!isSessionNotFoundError(error)
) {
throw error;
}
const recovery = await recoverMissingActiveSession(error);
return { messages: recovery.messages, status: "recovered" };
}
return (await sessionManager.readMessages(activeSessionId)) ?? [];
};
const recoverMissingActiveSession = async (error: unknown): Promise<void> => {
const recoverMissingActiveSession = async (
error: unknown,
): Promise<MissingSessionRecovery> => {
if (missingSessionRecoveryPromise) {
return await missingSessionRecoveryPromise;
}
@@ -290,7 +319,7 @@ export function createInteractiveSessionRuntime(input: {
const manager = sessionManager;
const missingSessionId = activeSessionId;
if (!manager || !missingSessionId || shutdownRequested) {
return;
return { messages: [] };
}
const messages = await manager
.readMessages(missingSessionId)
@@ -307,6 +336,7 @@ export function createInteractiveSessionRuntime(input: {
startupError = undefined;
clearActiveSession();
await startFreshSession(messages);
return { messages };
})().finally(() => {
missingSessionRecoveryPromise = undefined;
});
@@ -361,7 +391,13 @@ export function createInteractiveSessionRuntime(input: {
};
const restartWithCurrentMessages = async (): Promise<void> => {
const messages = await readCurrentMessages();
const { messages, status } = await readCurrentMessages();
if (status !== "read") {
// If reading recovered a missing hub session, the current messages are
// already in the replacement session. If the read is stale, another async
// operation changed the active session while this read was in flight.
return;
}
await restartWithMessages(messages);
};
@@ -510,7 +546,13 @@ export function createInteractiveSessionRuntime(input: {
if (!sessionManager) {
return { messagesBefore: 0, messagesAfter: 0, compacted: false };
}
const messages = await readCurrentMessages();
const { messages, status } = await readCurrentMessages();
if (status === "stale" || (status === "recovered" && !activeSessionId)) {
return { messagesBefore: 0, messagesAfter: 0, compacted: false };
}
// If reading messages recovered the session, `messages` are the same messages
// used to seed the replacement session, so it is safe to compact the current
// active session with them.
const messagesBefore = messages.length;
if (messagesBefore === 0) {
return { messagesBefore: 0, messagesAfter: 0, compacted: false };
@@ -551,7 +593,10 @@ export function createInteractiveSessionRuntime(input: {
return undefined;
}
const checkpointHistory = readSessionCheckpointHistory(sessionRecord);
const messages = await readCurrentMessages();
const { messages, status } = await readCurrentMessages();
if (status !== "read") {
return undefined;
}
return { messages, checkpointHistory };
};
+9 -1
View File
@@ -362,7 +362,15 @@ export async function runAgent(
type: "run_result",
finishReason: result.finishReason,
iterations: result.iterations,
usage,
usage: usage && {
...usage,
inputTokens: Math.max(
0,
usage.inputTokens -
(usage.cacheReadTokens ?? 0) -
(usage.cacheWriteTokens ?? 0),
),
},
...(aggregateUsage ? { aggregateUsage } : {}),
durationMs: result.durationMs,
text: result.text,
+1 -1
View File
@@ -389,7 +389,7 @@ export async function runInteractive(
? async () => {
try {
await sessionRuntime.ensureReady();
const messages = await sessionRuntime.readCurrentMessages();
const { messages } = await sessionRuntime.readCurrentMessages();
const usage = await sessionRuntime.getAccumulatedUsage({
inputTokens: 0,
outputTokens: 0,
+1 -1
View File
@@ -124,7 +124,7 @@ export function clineEnv(
}),
CLINE_SESSION_DATA_DIR: path.join(dataDir, "sessions"),
CLINE_TEAM_DATA_DIR: path.join(dataDir, "teams"),
CLINE_DISABLE_MIGRATION_NOTICE: "1",
CLINE_DISABLE_CLINE_PASS_NOTICE: "1",
NO_UPDATE_NOTIFIER: "1",
CLINE_NO_AUTO_UPDATE: "1",
...extra,
+7
View File
@@ -13,6 +13,7 @@ const coreMocks = vi.hoisted(() => {
fetchBalance: vi.fn(),
fetchOrganizationBalance: vi.fn(),
fetchAvailableSubscriptionPlans: vi.fn(),
fetchCurrentUserPlan: vi.fn(),
serviceOptions,
};
});
@@ -45,6 +46,9 @@ vi.mock("@cline/core", async (importOriginal) => {
}) {
return coreMocks.fetchAvailableSubscriptionPlans(input);
}
fetchCurrentUserPlan() {
return coreMocks.fetchCurrentUserPlan();
}
},
ProviderSettingsManager: class {
getProviderSettings(providerId: string) {
@@ -107,6 +111,7 @@ describe("createClineAccountService", () => {
coreMocks.fetchBalance.mockReset();
coreMocks.fetchOrganizationBalance.mockReset();
coreMocks.fetchAvailableSubscriptionPlans.mockReset();
coreMocks.fetchCurrentUserPlan.mockReset();
coreMocks.serviceOptions.length = 0;
telemetryMocks.identifyTelemetryAccount.mockReset();
});
@@ -204,6 +209,7 @@ describe("loadClineAccountSnapshot", () => {
coreMocks.fetchBalance.mockReset();
coreMocks.fetchOrganizationBalance.mockReset();
coreMocks.fetchAvailableSubscriptionPlans.mockReset();
coreMocks.fetchCurrentUserPlan.mockReset();
coreMocks.serviceOptions.length = 0;
telemetryMocks.identifyTelemetryAccount.mockReset();
});
@@ -268,6 +274,7 @@ describe("loadIndividualSubscriptionPlans", () => {
coreMocks.fetchBalance.mockReset();
coreMocks.fetchOrganizationBalance.mockReset();
coreMocks.fetchAvailableSubscriptionPlans.mockReset();
coreMocks.fetchCurrentUserPlan.mockReset();
coreMocks.serviceOptions.length = 0;
telemetryMocks.identifyTelemetryAccount.mockReset();
});
+45 -1
View File
@@ -3,6 +3,7 @@ import {
type ClineAccountOrganization,
type ClineAccountOrganizationBalance,
type ClineSubscriptionPlan,
type UserCurrentPlan,
ClineAccountService,
type ClineAccountUser,
formatProviderOAuthApiKey,
@@ -125,8 +126,9 @@ export async function createClineAccountService(input: {
config: ClineAccountConfig;
clineApiBaseUrl?: string;
clineProviderSettings?: ProviderSettings;
providerSettingsManager?: ProviderSettingsManager;
}): Promise<ClineAccountService | undefined> {
const manager = new ProviderSettingsManager();
const manager = input.providerSettingsManager ?? new ProviderSettingsManager();
const settings =
manager.getProviderSettings("cline") ?? input.clineProviderSettings;
const apiBaseUrl = resolveAccountApiBaseUrl({
@@ -216,6 +218,48 @@ export async function loadIndividualSubscriptionPlans(input: {
return service.fetchAvailableSubscriptionPlans({ type: "individual" });
}
export async function loadCurrentUserPlan(input: {
config: ClineAccountConfig;
clineApiBaseUrl?: string;
clineProviderSettings?: ProviderSettings;
}): Promise<UserCurrentPlan | undefined> {
const service = await createClineAccountService(input);
if (!service) {
throw new Error("No Cline account auth token found");
}
return service.fetchCurrentUserPlan();
}
export async function loadCurrentUserPlanFromProviderSettings(input: {
providerSettingsManager: ProviderSettingsManager;
clineApiBaseUrl?: string;
}): Promise<UserCurrentPlan | undefined> {
const service = await createClineAccountService({
config: { apiKey: "", logger: undefined, providerId: "cline" },
clineApiBaseUrl: input.clineApiBaseUrl,
providerSettingsManager: input.providerSettingsManager,
});
if (!service) {
throw new Error("No Cline account auth token found");
}
return service.fetchCurrentUserPlan();
}
export async function loadIndividualSubscriptionPlansFromProviderSettings(input: {
providerSettingsManager: ProviderSettingsManager;
clineApiBaseUrl?: string;
}): Promise<ClineSubscriptionPlan[]> {
const service = await createClineAccountService({
config: { apiKey: "", logger: undefined, providerId: "cline" },
clineApiBaseUrl: input.clineApiBaseUrl,
providerSettingsManager: input.providerSettingsManager,
});
if (!service) {
throw new Error("No Cline account auth token found");
}
return service.fetchAvailableSubscriptionPlans({ type: "individual" });
}
async function onChangeToClinePass(config: ClineAccountConfig) {
try {
await switchClineAccount({
+39 -31
View File
@@ -6,10 +6,10 @@ import "opentui-spinner/react";
import {
getClineOrgIndividualInferenceSubscriptionMessage,
getCliSubscriptionUrl,
getIndividualPlanFeatures,
isClineOrgIndividualInferenceSubscriptionErrorMessage,
isClinePassSubscriptionError,
} from "../../utils/cline-pass-errors";
import { getCliFeatureFlagsService } from "../../utils/feature-flags";
import {
CLINE_CREDITS_DASHBOARD_URL,
isClineAccountCreditsErrorMessage,
@@ -17,6 +17,7 @@ import {
import { useTerminalBackground } from "../hooks/use-terminal-background";
import {
getDefaultForeground,
getModeAccent,
getModeInputBackground,
palette,
type TerminalTheme,
@@ -37,12 +38,6 @@ import {
} from "../utils/tool-parsing";
import { ToolOutput } from "./tool-output";
function getIndividualPlanFeatures(plans: ClineSubscriptionPlan[]): string[] {
const planWithFeatures = plans.find((plan) => plan.interval === "Monthly");
return planWithFeatures?.features?.included ?? [];
}
function trimLeading(text: string): string {
return text.replace(/^\n+/, "");
}
@@ -274,14 +269,8 @@ function ToolCallView(props: {
);
}
const CLINE_PASS_ENABLED_OUT_OF_CREDITS_MESSAGE =
"You have run out of Cline credits. Add credits in the dashboard or purchase and switch to ClinePass to continue.";
const OUT_OF_CREDITS_MESSAGE =
"You have run out of Cline credits. Add credits in the dashboard to continue.";
function ClineCreditsErrorView(props: { defaultFg?: string }) {
const isClinePassEnabled =
getCliFeatureFlagsService().getBooleanFlagEnabled("ext-cline-pass");
function ClineCreditsClinePassErrorView(props: { defaultFg?: string }) {
const subscriptionUrl = getCliSubscriptionUrl();
return (
<box flexDirection="row">
<text fg="red" content="* " />
@@ -297,30 +286,46 @@ function ClineCreditsErrorView(props: { defaultFg?: string }) {
fg={props.defaultFg}
selectable
content={
isClinePassEnabled
? CLINE_PASS_ENABLED_OUT_OF_CREDITS_MESSAGE
: OUT_OF_CREDITS_MESSAGE
"You have run out of Cline credits. Add credits in the dashboard or purchase and switch to ClinePass to continue."
}
/>
<box flexDirection="row">
<text fg="gray">Dashboard: </text>
<text fg="gray">Purchase Credits: </text>
<text fg="cyan" selectable>
<a href={CLINE_CREDITS_DASHBOARD_URL}>
{CLINE_CREDITS_DASHBOARD_URL}
</a>
</text>
</box>
<box flexDirection="row">
<text fg="gray">Purchase ClinePass: </text>
<text fg="cyan" selectable>
<a href={subscriptionUrl}>{subscriptionUrl}</a>
</text>
</box>
<box flexDirection="row">
<text fg="gray">Switch to ClinePass: </text>
<text fg="gray">
type /settings in CLI and switch provider to ClinePass
</text>
</box>
</box>
</box>
);
}
function ClineCreditsErrorView(props: { defaultFg?: string }) {
return <ClineCreditsClinePassErrorView defaultFg={props.defaultFg} />;
}
function ClinePassSubscriptionErrorView(props: {
defaultFg?: string;
loadIndividualSubscriptionPlans?: () => Promise<ClineSubscriptionPlan[]>;
terminalTheme: TerminalTheme;
}) {
const subscriptionUrl = getCliSubscriptionUrl();
const [planFeatures, setPlanFeatures] = useState<string[]>([]);
const planAccent = getModeAccent("plan", props.terminalTheme);
useEffect(() => {
if (!props.loadIndividualSubscriptionPlans) {
@@ -345,15 +350,15 @@ function ClinePassSubscriptionErrorView(props: {
return (
<box flexDirection="row">
<text fg="yellow" content="* " />
<text fg={planAccent} content="* " />
<box
flexDirection="column"
border
borderStyle="rounded"
borderColor="yellow"
borderColor={planAccent}
paddingX={1}
>
<text fg="yellow">ClinePass subscription required</text>
<text fg={planAccent}>ClinePass subscription required</text>
<text
fg={props.defaultFg}
selectable
@@ -363,12 +368,10 @@ function ClinePassSubscriptionErrorView(props: {
<box flexDirection="column" marginTop={1}>
<text fg={props.defaultFg}>ClinePass includes:</text>
{planFeatures.map((feature) => (
<box key={feature} flexDirection="row">
<text fg="green" content="✓ " />
<text fg={props.defaultFg} selectable>
{feature}
</text>
</box>
<text key={feature} fg={props.defaultFg} selectable>
<span fg="green"> </span>
<span>{feature}</span>
</text>
))}
</box>
)}
@@ -391,18 +394,21 @@ function ClinePassSubscriptionErrorView(props: {
function ClineOrgIndividualInferenceSubscriptionErrorView(props: {
defaultFg?: string;
terminalTheme: TerminalTheme;
}) {
const planAccent = getModeAccent("plan", props.terminalTheme);
return (
<box flexDirection="row">
<text fg="yellow" content="* " />
<text fg={planAccent} content="* " />
<box
flexDirection="column"
border
borderStyle="rounded"
borderColor="yellow"
borderColor={planAccent}
paddingX={1}
>
<text fg="yellow">Personal ClinePass required</text>
<text fg={planAccent}>Personal ClinePass required</text>
<text
fg={props.defaultFg}
selectable
@@ -516,6 +522,7 @@ export function ChatEntryView(props: {
return (
<ClineOrgIndividualInferenceSubscriptionErrorView
defaultFg={defaultFg}
terminalTheme={terminalTheme}
/>
);
}
@@ -526,6 +533,7 @@ export function ChatEntryView(props: {
loadIndividualSubscriptionPlans={
props.loadIndividualSubscriptionPlans
}
terminalTheme={terminalTheme}
/>
);
}
@@ -0,0 +1,13 @@
const CLINE_PASS_SUBSCRIPTION_PATH = "/dashboard/subscription";
const DEFAULT_APP_BASE_URL = "https://app.cline.bot";
export function buildClinePassSubscriptionPageUrl(
appBaseUrl: string | undefined,
): string {
const url = new URL(
CLINE_PASS_SUBSCRIPTION_PATH,
appBaseUrl || DEFAULT_APP_BASE_URL,
);
url.searchParams.set("personal", "true");
return url.toString();
}
@@ -0,0 +1,18 @@
import { describe, expect, it } from "vitest";
import { buildClinePassSubscriptionPageUrl } from "./provider-picker-helpers";
describe("buildClinePassSubscriptionPageUrl", () => {
it("opens the personal subscription page on production by default", () => {
expect(buildClinePassSubscriptionPageUrl(undefined)).toBe(
"https://app.cline.bot/dashboard/subscription?personal=true",
);
});
it("keeps the configured app base URL", () => {
expect(
buildClinePassSubscriptionPageUrl("https://staging-app.cline.bot"),
).toBe(
"https://staging-app.cline.bot/dashboard/subscription?personal=true",
);
});
});
@@ -37,6 +37,7 @@ import {
getSearchableListRowsWindow,
type SearchableItem,
} from "../searchable-list";
import { buildClinePassSubscriptionPageUrl } from "./provider-picker-helpers";
interface ProviderItem {
id: string;
@@ -251,7 +252,8 @@ export function ProviderPickerContent(
export type ExistingProviderAction =
| "use_existing"
| "reconfigure"
| "open_subscription";
| "open_subscription_page"
| "open_usage_billing";
export interface ExistingProviderOption {
value: ExistingProviderAction;
@@ -259,18 +261,6 @@ export interface ExistingProviderOption {
onSelect?: () => Promise<void> | void;
}
const CLINE_PASS_SUBSCRIPTION_PATH = "/dashboard/subscription";
const DEFAULT_APP_BASE_URL = "https://app.cline.bot";
export function buildClinePassSubscriptionPageUrl(
appBaseUrl: string | undefined,
): string {
return new URL(
CLINE_PASS_SUBSCRIPTION_PATH,
appBaseUrl || DEFAULT_APP_BASE_URL,
).toString();
}
export function UseExistingOrReconfigureContent(
props: ChoiceContext<ExistingProviderOption> & {
providerName: string;
@@ -340,28 +330,34 @@ export function UseExistingOrReconfigureContent(
);
}
export function ClinePassSubscriptionContent(
function ClinePassBrowserPageContent(
props: ChoiceContext<boolean> & {
providerName: string;
pageLabel: string;
url: string;
openedStatus: string;
},
) {
const { resolve, dismiss, dialogId, providerName } = props;
const subscriptionUrl = useMemo(
() =>
buildClinePassSubscriptionPageUrl(getClineEnvironmentConfig().appBaseUrl),
[],
);
const {
resolve,
dismiss,
dialogId,
providerName,
pageLabel,
url,
openedStatus,
} = props;
const [status, setStatus] = useState("Opening browser...");
useEffect(() => {
void open(subscriptionUrl, { wait: false })
void open(url, { wait: false })
.then(() => {
setStatus("Opened subscription page in your browser.");
setStatus(openedStatus);
})
.catch(() => {
setStatus("Could not open browser automatically. Open the URL below.");
});
}, [subscriptionUrl]);
}, [url, openedStatus]);
useDialogKeyboard((key) => {
if (key.name === "escape") {
@@ -381,9 +377,9 @@ export function ClinePassSubscriptionContent(
<text>{status}</text>
<text fg="gray">Subscription page:</text>
<text fg="gray">{pageLabel}:</text>
<text fg="cyan" selectable>
<a href={subscriptionUrl}>{subscriptionUrl}</a>
<a href={url}>{url}</a>
</text>
<text fg="gray">
@@ -393,6 +389,27 @@ export function ClinePassSubscriptionContent(
);
}
export function ClinePassSubscriptionContent(
props: ChoiceContext<boolean> & {
providerName: string;
},
) {
const subscriptionUrl = useMemo(
() =>
buildClinePassSubscriptionPageUrl(getClineEnvironmentConfig().appBaseUrl),
[],
);
return (
<ClinePassBrowserPageContent
{...props}
pageLabel="Subscription page"
url={subscriptionUrl}
openedStatus="Opened subscription page in your browser."
/>
);
}
const DEFAULT_FIELD_LABELS: Partial<Record<ProviderConfigFieldKey, string>> = {
apiKey: "API key",
baseUrl: "Base URL",
+28 -1
View File
@@ -3,6 +3,7 @@ import {
createContextBar,
formatStatusBarUsageText,
resolveContextBarFilledForeground,
resolveModelDisplayName,
} from "./status-bar";
vi.mock("@opentui/react", () => ({
@@ -67,6 +68,32 @@ describe("formatStatusBarUsageText", () => {
totalCost: 0.123,
providerId: "cline-pass",
}),
).toBe("(12,345 tokens) $0.00 (included with your subscription)");
).toBe("(12,345 tokens) $0.00 (included with subscription)");
});
});
describe("resolveModelDisplayName", () => {
it("keeps ClinePass visible when model ids have provider prefixes", () => {
expect(
resolveModelDisplayName({
providerId: "cline-pass",
modelId: "zai/glm-5.2",
knownModels: {
"zai/glm-5.2": { name: "GLM 5.2" },
},
}),
).toBe("ClinePass/glm-5.2");
});
it("uses the friendly model name for non-ClinePass providers", () => {
expect(
resolveModelDisplayName({
providerId: "cline",
modelId: "zai/glm-5.2",
knownModels: {
"zai/glm-5.2": { name: "GLM 5.2" },
},
}),
).toBe("GLM 5.2");
});
});
+9 -4
View File
@@ -51,7 +51,7 @@ function formatCost(cost: number): string {
function formatCostText(providerId: string, totalCost: number): string {
if (shouldShowCliUsageCoveredBySubscription(providerId)) {
return "$0.00 (included with your subscription)";
return "$0.00 (included with subscription)";
}
if (!shouldShowCliUsageCost(providerId)) {
@@ -94,17 +94,22 @@ function lookupModelInfo(
}
export function resolveModelDisplayName(config: {
providerId?: string;
modelId: string;
knownModels?: Record<string, unknown>;
thinking?: boolean;
reasoningEffort?: string;
}): string {
const info = lookupModelInfo(config.modelId, config.knownModels);
const name = info?.name ?? config.modelId.split("/").pop() ?? config.modelId;
const modelIdTail = config.modelId.split("/").pop() ?? config.modelId;
const displayName =
config.providerId === "cline-pass"
? `ClinePass/${modelIdTail}`
: (info?.name ?? modelIdTail);
if (config.thinking && config.reasoningEffort) {
return `${name} (${config.reasoningEffort})`;
return `${displayName} (${config.reasoningEffort})`;
}
return name;
return displayName;
}
export function resolveModelMaxInputTokens(config: {
@@ -91,8 +91,8 @@ function providerToExistingProviderOptions(input: {
return [
{
value: "open_subscription",
label: "Open ClinePass subscription page",
value: "open_subscription_page",
label: "Manage subscription & see usage",
onSelect: async () => {
await input.dialog.choice<boolean>({
style: { maxHeight: input.termHeight - 2 },
+9 -1
View File
@@ -10,6 +10,7 @@ import {
useDialogState,
} from "@opentui-ui/dialog/react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { shouldSuppressClineCliMigrationNoticeForActiveProvider } from "../kanban-migration/notice";
import { MigrationNoticeContent } from "../kanban-migration/notice-dialog";
import type { RepoStatus } from "../utils/repo-status";
import { readRepoStatus } from "../utils/repo-status";
@@ -541,10 +542,17 @@ function App(props: TuiProps) {
const notice = props.initialNotice;
const onInitialNoticeShown = props.onInitialNoticeShown;
const currentProviderId = props.config.providerId;
useEffect(() => {
if (!notice) return;
if (initialNoticeShownRef.current) return;
if (appView !== "home") return;
if (
shouldSuppressClineCliMigrationNoticeForActiveProvider(currentProviderId)
) {
initialNoticeShownRef.current = true;
return;
}
initialNoticeShownRef.current = true;
const timeout = setTimeout(() => {
@@ -560,7 +568,7 @@ function App(props: TuiProps) {
});
}, 0);
return () => clearTimeout(timeout);
}, [appView, dialog, notice, onInitialNoticeShown]);
}, [appView, currentProviderId, dialog, notice, onInitialNoticeShown]);
const {
appendEntry: appendSessionEntry,
+150 -7
View File
@@ -10,16 +10,24 @@ import {
saveLocalProviderSettings,
} from "@cline/core";
import { isClineProvider } from "@cline/shared";
import open from "open";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
getCliSubscriptionUrl,
getIndividualPlanFeatures,
} from "../../../utils/cline-pass-errors";
import {
type CodexCliStatus,
checkCodexCliInstalled,
isOpenAICodexCliProvider,
} from "../../../utils/codex-cli";
import { getCliFeatureFlagsService } from "../../../utils/feature-flags";
import { getPersistedProviderApiKey } from "../../../utils/provider-auth";
import { listLocalProviders } from "../../../utils/provider-catalog";
import { getCliTelemetryService } from "../../../utils/telemetry";
import {
loadCurrentUserPlanFromProviderSettings,
loadIndividualSubscriptionPlansFromProviderSettings,
} from "../../cline-account";
import {
buildClineModelEntries,
type ClineModelPickerEntry,
@@ -48,6 +56,8 @@ import {
import { FIELD_ORDER } from "./fields";
import { useOnboardingKeyboard } from "./keyboard";
import {
CLINE_PASS_SUBSCRIPTION_OPTIONS,
type ClinePassSubscriptionStatus,
getMainMenuOptions,
type ModelEntry,
type OnboardingResult,
@@ -78,8 +88,7 @@ export function useOnboardingController(props: OnboardingControllerProps) {
const menuOptions = useMemo(
() =>
getMainMenuOptions({
isClinePassEnabled:
getCliFeatureFlagsService().getBooleanFlagEnabled("ext-cline-pass"),
isClinePassEnabled: true,
}),
[],
);
@@ -150,6 +159,19 @@ export function useOnboardingController(props: OnboardingControllerProps) {
const [modelsDefaultId, setModelsDefaultId] = useState("");
const [customModelId, setCustomModelId] = useState("");
const [customModelError, setCustomModelError] = useState("");
const [clinePassSubscriptionStatus, setClinePassSubscriptionStatus] =
useState<ClinePassSubscriptionStatus>("loading");
const [clinePassSubscriptionError, setClinePassSubscriptionError] =
useState("");
const [clinePassCurrentPlanName, setClinePassCurrentPlanName] = useState("");
const [clinePassPlanFeatures, setClinePassPlanFeatures] = useState<string[]>(
[],
);
const [clinePassSubscriptionSelected, setClinePassSubscriptionSelected] =
useState(0);
const [clinePassSubscriptionOpenStatus, setClinePassSubscriptionOpenStatus] =
useState("");
const clinePassSubscriptionUrl = useMemo(() => getCliSubscriptionUrl(), []);
const modelItems: SearchableItem[] = useMemo(
() =>
@@ -265,6 +287,62 @@ export function useOnboardingController(props: OnboardingControllerProps) {
[providerSettingsManager],
);
const refreshClinePassSubscriptionStatus = useCallback(() => {
setClinePassSubscriptionStatus("loading");
setClinePassSubscriptionError("");
setClinePassCurrentPlanName("");
setClinePassSubscriptionOpenStatus("");
loadCurrentUserPlanFromProviderSettings({ providerSettingsManager })
.then(
(value) => ({ status: "fulfilled" as const, value }),
(reason) => ({ status: "rejected" as const, reason }),
)
.then((currentPlanResult) =>
loadIndividualSubscriptionPlansFromProviderSettings({
providerSettingsManager,
})
.then(
(value) => ({ status: "fulfilled" as const, value }),
(reason) => ({ status: "rejected" as const, reason }),
)
.then((availablePlansResult) => ({
availablePlansResult,
currentPlanResult,
})),
)
.then(({ currentPlanResult, availablePlansResult }) => {
if (availablePlansResult.status === "fulfilled") {
setClinePassPlanFeatures(
getIndividualPlanFeatures(availablePlansResult.value),
);
}
if (currentPlanResult.status === "rejected") {
const error = currentPlanResult.reason;
const message =
error instanceof Error ? error.message : String(error);
if (message.trim().toLowerCase() === "no plan found for user") {
setClinePassSubscriptionStatus("unsubscribed");
return;
}
setClinePassSubscriptionError(message);
setClinePassSubscriptionStatus("error");
return;
}
const plan = currentPlanResult.value?.plan;
if (plan) {
setClinePassCurrentPlanName(
plan.displayName || plan.name || plan.id || "ClinePass",
);
setClinePassSubscriptionStatus("subscribed");
} else {
setClinePassSubscriptionStatus("unsubscribed");
}
});
}, [providerSettingsManager]);
const transitionToModelPicker = useCallback(
(providerId: string) => {
setActiveProviderId(providerId);
@@ -288,6 +366,27 @@ export function useOnboardingController(props: OnboardingControllerProps) {
[providers, loadModelsForProvider, providerSettingsManager],
);
const transitionToClinePassSubscription = useCallback(() => {
setActiveProviderId("cline-pass");
const provider = providers.find((p) => p.id === "cline-pass");
setActiveProviderName(provider?.name ?? "ClinePass");
setModelsDefaultId(provider?.defaultModelId ?? "");
setClinePassSubscriptionSelected(0);
setStep("cline_pass_subscription");
refreshClinePassSubscriptionStatus();
}, [providers, refreshClinePassSubscriptionStatus]);
const handleAuthComplete = useCallback(
(providerId: OnboardingOAuthProviderId) => {
if (providerId === "cline-pass") {
transitionToClinePassSubscription();
return;
}
transitionToModelPicker(providerId);
},
[transitionToClinePassSubscription, transitionToModelPicker],
);
const resetAuth = useCallback(() => {
setAuthStatus("");
setAuthUrl("");
@@ -313,11 +412,11 @@ export function useOnboardingController(props: OnboardingControllerProps) {
setVerifyUrl: setDeviceVerifyUrl,
setStatus: setDeviceStatus,
setError: setDeviceError,
onComplete: transitionToModelPicker,
onComplete: handleAuthComplete,
telemetry: getCliTelemetryService(),
});
},
[providerSettingsManager, transitionToModelPicker],
[providerSettingsManager, handleAuthComplete],
);
const startOAuthFlow = useCallback(
@@ -339,18 +438,46 @@ export function useOnboardingController(props: OnboardingControllerProps) {
setStatus: setAuthStatus,
setAuthUrl,
setError: setAuthError,
onComplete: transitionToModelPicker,
onComplete: handleAuthComplete,
telemetry: getCliTelemetryService(),
});
},
[
providerSettingsManager,
resetAuth,
transitionToModelPicker,
handleAuthComplete,
startDeviceCodeFlow,
],
);
const continueFromClinePassSubscription = useCallback(() => {
transitionToModelPicker("cline-pass");
}, [transitionToModelPicker]);
const openClinePassSubscriptionPage = useCallback(() => {
setClinePassSubscriptionOpenStatus("Opening subscription page...");
void open(clinePassSubscriptionUrl, { wait: false })
.then(() => {
setClinePassSubscriptionOpenStatus(
"Opened subscription page in your browser.",
);
})
.catch(() => {
setClinePassSubscriptionOpenStatus(
`Could not open browser automatically. Open ${clinePassSubscriptionUrl}`,
);
});
}, [clinePassSubscriptionUrl]);
useEffect(() => {
if (
step === "cline_pass_subscription" &&
clinePassSubscriptionStatus === "subscribed"
) {
transitionToModelPicker("cline-pass");
}
}, [step, clinePassSubscriptionStatus, transitionToModelPicker]);
const refreshCodexCliStatus = useCallback(() => {
setCodexCliStatus(undefined);
setCodexCliChecking(true);
@@ -632,6 +759,9 @@ export function useOnboardingController(props: OnboardingControllerProps) {
modelList,
clineEntries,
clineModelSelected,
clinePassSubscriptionStatus,
clinePassSubscriptionOptions: CLINE_PASS_SUBSCRIPTION_OPTIONS,
clinePassSubscriptionSelected,
thinkingSelected,
setStep,
setMenuSelected,
@@ -648,7 +778,11 @@ export function useOnboardingController(props: OnboardingControllerProps) {
setDeviceError,
setDeviceStatus,
setClineModelSelected,
setClinePassSubscriptionSelected,
setThinkingSelected,
continueFromClinePassSubscription,
refreshClinePassSubscriptionStatus,
openClinePassSubscriptionPage,
abortOAuth: () => {
authAbortRef.current = true;
},
@@ -670,6 +804,7 @@ export function useOnboardingController(props: OnboardingControllerProps) {
return {
activeProviderName,
activeProviderId,
authError,
authStatus,
authUrl,
@@ -682,6 +817,14 @@ export function useOnboardingController(props: OnboardingControllerProps) {
clineEntries,
clineKnownModels,
clineModelSelected,
clinePassCurrentPlanName,
clinePassPlanFeatures,
clinePassSubscriptionError,
clinePassSubscriptionOpenStatus,
clinePassSubscriptionOptions: CLINE_PASS_SUBSCRIPTION_OPTIONS,
clinePassSubscriptionSelected,
clinePassSubscriptionStatus,
clinePassSubscriptionUrl,
deviceError,
deviceStatus,
deviceUserCode,
@@ -9,6 +9,8 @@ import {
} from "./auth";
import { FIELD_ORDER } from "./fields";
import {
type ClinePassSubscriptionOption,
type ClinePassSubscriptionStatus,
type MenuOption,
type OnboardingStep,
THINKING_LEVELS,
@@ -26,6 +28,9 @@ export function useOnboardingKeyboard(input: {
modelList: SearchableListState;
clineEntries: ClineModelPickerEntry[];
clineModelSelected: number;
clinePassSubscriptionStatus: ClinePassSubscriptionStatus;
clinePassSubscriptionOptions: ClinePassSubscriptionOption[];
clinePassSubscriptionSelected: number;
thinkingSelected: number;
setStep: (step: OnboardingStep) => void;
setMenuSelected: Dispatch<SetStateAction<number>>;
@@ -38,7 +43,11 @@ export function useOnboardingKeyboard(input: {
setDeviceError: (value: string) => void;
setDeviceStatus: (value: string) => void;
setClineModelSelected: Dispatch<SetStateAction<number>>;
setClinePassSubscriptionSelected: Dispatch<SetStateAction<number>>;
setThinkingSelected: Dispatch<SetStateAction<number>>;
continueFromClinePassSubscription: () => void;
refreshClinePassSubscriptionStatus: () => void;
openClinePassSubscriptionPage: () => void;
abortOAuth: () => void;
abortDeviceCode: () => void;
resetAuth: () => void;
@@ -93,6 +102,11 @@ export function useOnboardingKeyboard(input: {
input.setStep("byo_provider");
return;
}
if (input.step === "cline_pass_subscription") {
input.setStep("menu");
input.setMenuSelected(0);
return;
}
if (input.step === "cline_model") {
input.setStep("menu");
input.setMenuSelected(0);
@@ -135,6 +149,43 @@ export function useOnboardingKeyboard(input: {
if (input.step === "device_code") return;
if (input.step === "cline_pass_subscription") {
const total = input.clinePassSubscriptionOptions.length;
if (total === 0) return;
if (key.name === "up" || (key.ctrl && key.name === "p")) {
input.setClinePassSubscriptionSelected((s) =>
s <= 0 ? total - 1 : s - 1,
);
return;
}
if (key.name === "down" || (key.ctrl && key.name === "n")) {
input.setClinePassSubscriptionSelected((s) =>
s >= total - 1 ? 0 : s + 1,
);
return;
}
if (key.name === "return" || key.name === "enter") {
const option =
input.clinePassSubscriptionOptions[
Math.min(input.clinePassSubscriptionSelected, total - 1)
];
if (!option) return;
if (option.value === "subscribe") {
input.openClinePassSubscriptionPage();
} else if (option.value === "refresh") {
if (input.clinePassSubscriptionStatus !== "loading") {
input.refreshClinePassSubscriptionStatus();
}
} else if (option.value === "skip") {
input.continueFromClinePassSubscription();
} else if (option.value === "back") {
input.setStep("menu");
input.setMenuSelected(0);
}
}
return;
}
if (input.step === "menu") {
if (key.name === "up") {
input.setMenuSelected((s) =>
@@ -8,6 +8,7 @@ export type OnboardingStep =
| "byo_provider"
| "byo_apikey"
| "codex_cli_setup"
| "cline_pass_subscription"
| "cline_model"
| "model_picker"
| "custom_model_id"
@@ -36,6 +37,17 @@ export interface MenuOption {
icon: string;
}
export type ClinePassSubscriptionAction =
| "subscribe"
| "refresh"
| "skip"
| "back";
export interface ClinePassSubscriptionOption {
value: ClinePassSubscriptionAction;
label: string;
}
export const MAIN_MENU: MenuOption[] = [
{
label: "Sign in with Cline",
@@ -71,6 +83,25 @@ export function getMainMenuOptions(options?: {
);
}
export const CLINE_PASS_SUBSCRIPTION_OPTIONS: ClinePassSubscriptionOption[] = [
{
value: "subscribe",
label: "Subscribe to ClinePass",
},
{
value: "refresh",
label: "Re-check subscription status",
},
{
value: "skip",
label: "Skip for now",
},
{
value: "back",
label: "Go back",
},
];
export interface OnboardingResult {
providerId: string;
modelId: string;
@@ -96,6 +127,12 @@ export interface ModelEntry {
supportsReasoning: boolean;
}
export type ClinePassSubscriptionStatus =
| "loading"
| "subscribed"
| "unsubscribed"
| "error";
export interface ProviderCatalogItem {
id: string;
name: string;
+209 -3
View File
@@ -1,5 +1,7 @@
import "opentui-spinner/react";
import type { ScrollBoxRenderable } from "@opentui/core";
import type { ReactNode } from "react";
import { useEffect, useRef } from "react";
import {
CODEX_CLI_INSTALL_URL,
type CodexCliStatus,
@@ -17,10 +19,18 @@ import {
TrackedRobot,
type useMouseTracker,
} from "../../components/tracked-robot";
import { useTerminalBackground } from "../../hooks/use-terminal-background";
import { getDefaultForeground, palette } from "../../palette";
import {
useTerminalBackground,
useTerminalTheme,
} from "../../hooks/use-terminal-background";
import { getDefaultForeground, getModeAccent, palette } from "../../palette";
import { FIELD_ORDER } from "./fields";
import { type MenuOption, THINKING_LEVELS } from "./model";
import {
type ClinePassSubscriptionOption,
type ClinePassSubscriptionStatus,
type MenuOption,
THINKING_LEVELS,
} from "./model";
type MouseTrackerState = ReturnType<typeof useMouseTracker>;
@@ -29,6 +39,10 @@ function useDefaultFg(): string | undefined {
return getDefaultForeground(terminalBg);
}
function getClinePassSubscriptionOptionId(index: number): string {
return `cline-pass-subscription-option-${index}`;
}
interface OnboardingFrameProps {
children: ReactNode;
compact: boolean;
@@ -468,6 +482,198 @@ export function OnboardingClineModelScreen(props: {
);
}
export function OnboardingClinePassSubscriptionScreen(props: {
compact: boolean;
contentWidth: number;
currentPlanName: string;
error: string;
mouse: MouseTrackerState;
openStatus: string;
options: ClinePassSubscriptionOption[];
planFeatures: string[];
selected: number;
status: ClinePassSubscriptionStatus;
subscriptionUrl: string;
}) {
const defaultFg = useDefaultFg();
const terminalTheme = useTerminalTheme();
const planAccent = getModeAccent("plan", terminalTheme);
const scrollRef = useRef<ScrollBoxRenderable | null>(null);
const isLoading = props.status === "loading";
const isSubscribed = props.status === "subscribed";
const isError = props.status === "error";
const bodyHeight = props.compact ? 17 : 19;
useEffect(() => {
if (isSubscribed) {
return;
}
const scrollSelectedOptionIntoView = () => {
scrollRef.current?.scrollChildIntoView(
getClinePassSubscriptionOptionId(props.selected),
);
};
scrollSelectedOptionIntoView();
queueMicrotask(scrollSelectedOptionIntoView);
const timeout = setTimeout(scrollSelectedOptionIntoView, 0);
return () => clearTimeout(timeout);
}, [isSubscribed, props.selected]);
return (
<OnboardingFrame
compact={props.compact}
contentWidth={props.contentWidth}
mouse={props.mouse}
>
<box
flexDirection="column"
border
borderStyle="rounded"
borderColor={isSubscribed ? palette.success : planAccent}
paddingX={1}
paddingY={1}
height={bodyHeight}
overflow="hidden"
>
<scrollbox
ref={scrollRef}
width="100%"
height="100%"
scrollY
scrollX={false}
viewportOptions={{ overflow: "hidden" }}
contentOptions={{ flexDirection: "column" }}
>
<box flexDirection="column" width="100%" flexShrink={0}>
<text
fg={isSubscribed ? palette.success : planAccent}
flexShrink={0}
>
{isSubscribed
? "ClinePass subscription active"
: "ClinePass subscription required"}
</text>
{isLoading ? (
<box flexDirection="row" gap={1} flexShrink={0}>
<spinner name="dots" color="gray" />
<text fg="gray">Checking your ClinePass subscription...</text>
</box>
) : isSubscribed ? (
<text fg={defaultFg} selectable flexShrink={0}>
Current plan: {props.currentPlanName || "ClinePass"}
</text>
) : isError ? (
<text
fg={defaultFg}
selectable
flexShrink={0}
content="Could not verify your ClinePass subscription. Re-check before choosing a ClinePass model."
/>
) : (
<text
fg={defaultFg}
selectable
flexShrink={0}
content="No access to ClinePass subscription models yet. Subscribe to ClinePass, the low cost open weights model coding plan."
/>
)}
{props.status === "error" &&
props.error &&
props.error !== "no plan found for user" && (
<text fg="red" selectable flexShrink={0}>
{props.error}
</text>
)}
{!isSubscribed && props.planFeatures.length > 0 && (
<box flexDirection="column" marginTop={1} flexShrink={0}>
{props.planFeatures.map((feature) => {
if (
feature === "Low cost subscription pricing" ||
feature === "Generous limits and reliable access" ||
feature === "Built for as many programmers as possible"
) {
return null;
}
return (
<text
key={feature}
fg={defaultFg}
selectable
flexShrink={0}
>
<span fg="green"> </span>
<span>{feature}</span>
</text>
);
})}
</box>
)}
{!isSubscribed && (
<box flexDirection="column" marginTop={1} flexShrink={0}>
{props.options.map((option, i) => {
const isSel = i === props.selected;
return (
<box
id={getClinePassSubscriptionOptionId(i)}
key={option.value}
paddingX={1}
flexDirection="row"
gap={1}
backgroundColor={isSel ? palette.selection : undefined}
height={1}
flexShrink={0}
overflow="hidden"
>
<text
fg={isSel ? palette.textOnSelection : "gray"}
flexShrink={0}
>
{isSel ? "\u276f" : " "}
</text>
<text
fg={isSel ? palette.textOnSelection : defaultFg}
flexShrink={0}
>
{option.label}
</text>
</box>
);
})}
</box>
)}
{props.openStatus && (
<text fg="gray" selectable flexShrink={0}>
{props.openStatus}
</text>
)}
{!isSubscribed && (
<box flexDirection="column" marginTop={1} flexShrink={0}>
<text fg="gray" flexShrink={0}>
If the browser button does not work:
</text>
<text fg={palette.act} selectable flexShrink={0}>
<a href={props.subscriptionUrl}>{props.subscriptionUrl}</a>
</text>
</box>
)}
</box>
</scrollbox>
</box>
<text fg="gray" paddingX={1}>
<em>/ navigate, Enter to select, Esc to go back, Ctrl+C to exit</em>
</text>
</OnboardingFrame>
);
}
export function OnboardingModelPickerScreen(props: {
activeProviderName: string;
compact: boolean;
@@ -6,6 +6,7 @@ import { useOnboardingController } from "./controller";
import { getOAuthProviderLabel, type OnboardingResult } from "./model";
import {
OnboardingClineModelScreen,
OnboardingClinePassSubscriptionScreen,
OnboardingCodexCliScreen,
OnboardingCustomModelIdScreen,
OnboardingDeviceCodeScreen,
@@ -121,6 +122,24 @@ export function OnboardingView(props: OnboardingViewProps) {
);
}
if (state.step === "cline_pass_subscription") {
return (
<OnboardingClinePassSubscriptionScreen
compact={compact}
contentWidth={contentWidth}
currentPlanName={state.clinePassCurrentPlanName}
error={state.clinePassSubscriptionError}
mouse={mouse}
openStatus={state.clinePassSubscriptionOpenStatus}
options={state.clinePassSubscriptionOptions}
planFeatures={state.clinePassPlanFeatures}
selected={state.clinePassSubscriptionSelected}
status={state.clinePassSubscriptionStatus}
subscriptionUrl={state.clinePassSubscriptionUrl}
/>
);
}
if (state.step === "model_picker") {
return (
<OnboardingModelPickerScreen
+9
View File
@@ -1,4 +1,5 @@
import {
type ClineSubscriptionPlan,
getClineOrgIndividualInferenceSubscriptionMessage,
isClineNotSubscribedError,
isClineNotSubscribedMessage,
@@ -21,6 +22,14 @@ export function getCliNotSubscribedMessage(): string {
return `No access to ClinePass subscription models yet. Subscribe to ClinePass, the low cost open weights model coding plan: ${getCliSubscriptionUrl()}`;
}
export function getIndividualPlanFeatures(
plans: ClineSubscriptionPlan[],
): string[] {
const planWithFeatures = plans.find((plan) => plan.interval === "Monthly");
return planWithFeatures?.features?.included ?? [];
}
function isFormattedClinePassSubscriptionMessage(message: string): boolean {
const normalized = message.trim().toLowerCase();
return (
+1 -9
View File
@@ -2,7 +2,6 @@ import { describe, expect, it, vi } from "vitest";
const mocks = vi.hoisted(() => ({
listLocalProviders: vi.fn(async () => ({ providers: [], settingsPath: "" })),
getBooleanFlagEnabled: vi.fn(() => true),
}));
vi.mock("@cline/core", async (importOriginal) => {
@@ -13,20 +12,13 @@ vi.mock("@cline/core", async (importOriginal) => {
};
});
vi.mock("./feature-flags", () => ({
getCliFeatureFlagsService: () => ({
getBooleanFlagEnabled: mocks.getBooleanFlagEnabled,
}),
}));
describe("listLocalProviders", () => {
it("passes the ClinePass feature flag into the SDK provider list", async () => {
it("enables ClinePass when listing the SDK provider list", async () => {
const { listLocalProviders } = await import("./provider-catalog");
const manager = {} as never;
await listLocalProviders(manager);
expect(mocks.getBooleanFlagEnabled).toHaveBeenCalledWith("ext-cline-pass");
expect(mocks.listLocalProviders).toHaveBeenCalledWith(manager, {
isClinePassEnabled: true,
});
+1 -3
View File
@@ -2,13 +2,11 @@ import {
listLocalProviders as internalListLocalProviders,
type ProviderSettingsManager,
} from "@cline/core";
import { getCliFeatureFlagsService } from "./feature-flags";
export async function listLocalProviders(
manager: ProviderSettingsManager,
): ReturnType<typeof internalListLocalProviders> {
return await internalListLocalProviders(manager, {
isClinePassEnabled:
getCliFeatureFlagsService().getBooleanFlagEnabled("ext-cline-pass"),
isClinePassEnabled: true,
});
}
+14 -11
View File
@@ -61,33 +61,36 @@ async function main() {
fs.rmSync(path.join(__dirname, "..", "out", "src"), { recursive: true, force: true })
fs.rmSync(path.join(__dirname, "..", "out", "packages"), { recursive: true, force: true })
// Single source of truth for the bun-vs-integration test split: any *.test.ts that
// imports from "bun:test" is owned by the bun runner (scripts/run-bun-unit-tests.ts)
// and must NOT be compiled into the Node-based @vscode/test-cli `out/` tree (Node
// cannot load the `bun:test` builtin, and these files use bun-only APIs like
// `mock.module` / 3-arg `it`). Generate a tsconfig that excludes them so the
// Single source of truth for the test-runner split: any *.test.ts that imports
// from "bun:test" is owned by the bun runner (scripts/run-bun-unit-tests.ts),
// and any that imports from "vitest" is owned by the vitest runner (test:vitest,
// see vitest.config.ts). Neither may be compiled into the Node-based
// @vscode/test-cli `out/` tree: Node cannot load `bun:test`, and vitest suites
// use vitest-only APIs/matchers (e.g. `toHaveBeenCalledWith`) that the mocha
// runner does not provide. Generate a tsconfig that excludes them so the
// integration compile only ever sees mocha-owned tests.
const projectRoot = path.join(__dirname, "..")
const bunTestImport = /from\s+["']bun:test["']/
function collectBunTestFiles(dir, acc) {
const nonMochaTestImport =
/from\s+["'](?:bun:test|vitest(?:\/[^"']*)?|@vitest\/[^"']*)["']/
function collectNonMochaTestFiles(dir, acc) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
if (entry.name === "node_modules") continue
const full = path.join(dir, entry.name)
if (entry.isDirectory()) {
collectBunTestFiles(full, acc)
collectNonMochaTestFiles(full, acc)
} else if (entry.isFile() && entry.name.endsWith(".test.ts")) {
if (bunTestImport.test(fs.readFileSync(full, "utf-8"))) {
if (nonMochaTestImport.test(fs.readFileSync(full, "utf-8"))) {
acc.push(path.relative(projectRoot, full).split(path.sep).join("/"))
}
}
}
return acc
}
const bunOwnedTests = collectBunTestFiles(path.join(projectRoot, "src"), [])
const nonMochaOwnedTests = collectNonMochaTestFiles(path.join(projectRoot, "src"), [])
// tsconfig.test.json is JSONC (contains comments); parse with json5 (a project dep).
const JSON5 = require("json5")
const baseTestConfig = JSON5.parse(fs.readFileSync(path.join(projectRoot, "tsconfig.test.json"), "utf-8"))
baseTestConfig.exclude = [...(baseTestConfig.exclude ?? []), ...bunOwnedTests]
baseTestConfig.exclude = [...(baseTestConfig.exclude ?? []), ...nonMochaOwnedTests]
const generatedConfigPath = path.join(projectRoot, "tsconfig.test.generated.json")
fs.writeFileSync(generatedConfigPath, JSON.stringify(baseTestConfig, null, "\t"))
@@ -0,0 +1,107 @@
import { DEFAULT_AUTO_APPROVAL_SETTINGS } from "@shared/AutoApprovalSettings"
import { AutoApprovalSettingsRequest } from "@shared/proto/cline/state"
import { describe, expect, it, vi } from "vitest"
import type { Controller } from ".."
import { updateAutoApprovalSettings } from "./updateAutoApprovalSettings"
function makeController(currentSettings = DEFAULT_AUTO_APPROVAL_SETTINGS, taskId?: string) {
const controller = {
task: taskId ? { taskId } : undefined,
getStateToPostToWebview: vi.fn(async () => ({
autoApprovalSettings: currentSettings,
})),
postStateToWebview: vi.fn(async () => undefined),
stateManager: {
setGlobalState: vi.fn(),
setTaskSettings: vi.fn(),
},
}
return controller as unknown as Controller & {
getStateToPostToWebview: ReturnType<typeof vi.fn>
postStateToWebview: ReturnType<typeof vi.fn>
stateManager: {
setGlobalState: ReturnType<typeof vi.fn>
setTaskSettings: ReturnType<typeof vi.fn>
}
}
}
describe("updateAutoApprovalSettings", () => {
it("updates the active task override when auto-approval settings change", async () => {
const currentSettings = {
...DEFAULT_AUTO_APPROVAL_SETTINGS,
version: 1,
actions: {
...DEFAULT_AUTO_APPROVAL_SETTINGS.actions,
editFiles: false,
},
}
const controller = makeController(currentSettings, "task-1")
await updateAutoApprovalSettings(
controller,
AutoApprovalSettingsRequest.create({
version: 2,
actions: {
editFiles: true,
},
}),
)
const expectedSettings = {
...currentSettings,
version: 2,
actions: {
...currentSettings.actions,
editFiles: true,
},
}
expect(controller.stateManager.setGlobalState).toHaveBeenCalledWith("autoApprovalSettings", expectedSettings)
expect(controller.stateManager.setTaskSettings).toHaveBeenCalledWith("task-1", "autoApprovalSettings", expectedSettings)
expect(controller.postStateToWebview).toHaveBeenCalledOnce()
})
it("does not create a task override when no task is active", async () => {
const controller = makeController(DEFAULT_AUTO_APPROVAL_SETTINGS)
await updateAutoApprovalSettings(
controller,
AutoApprovalSettingsRequest.create({
version: DEFAULT_AUTO_APPROVAL_SETTINGS.version + 1,
actions: {
readFiles: false,
},
}),
)
expect(controller.stateManager.setGlobalState).toHaveBeenCalledOnce()
expect(controller.stateManager.setTaskSettings).not.toHaveBeenCalled()
expect(controller.postStateToWebview).toHaveBeenCalledOnce()
})
it("ignores stale auto-approval settings versions", async () => {
const controller = makeController(
{
...DEFAULT_AUTO_APPROVAL_SETTINGS,
version: 3,
},
"task-1",
)
await updateAutoApprovalSettings(
controller,
AutoApprovalSettingsRequest.create({
version: 3,
actions: {
readFiles: false,
},
}),
)
expect(controller.stateManager.setGlobalState).not.toHaveBeenCalled()
expect(controller.stateManager.setTaskSettings).not.toHaveBeenCalled()
expect(controller.postStateToWebview).not.toHaveBeenCalled()
})
})
@@ -29,6 +29,9 @@ export async function updateAutoApprovalSettings(controller: Controller, request
}
controller.stateManager.setGlobalState("autoApprovalSettings", settings)
if (controller.task?.taskId) {
controller.stateManager.setTaskSettings(controller.task.taskId, "autoApprovalSettings", settings)
}
await controller.postStateToWebview()
}
+1
View File
@@ -11,6 +11,7 @@ export default defineConfig({
"src/shared/vsCodeSelectorUtils.test.ts",
"src/shared/proto-conversions/models/**/*.test.ts",
"src/core/storage/remote-config/**/*.test.ts",
"src/core/controller/state/**/*.test.ts",
"src/core/controller/slash/**/*.test.ts",
"src/services/mcp/__tests__/settingsLock.test.ts",
"src/shared/model-catalog/provider-helpers.test.ts",
@@ -29,6 +29,7 @@ export const ApiKeyField = ({
}: ApiKeyFieldProps) => {
const [localValue, setLocalValue] = useState(initialValue)
const isFocusedRef = useRef(false)
const hasPendingUserEditRef = useRef(false)
const prevInitialValueRef = useRef(initialValue)
useEffect(() => {
@@ -42,12 +43,18 @@ export const ApiKeyField = ({
// Do not replace their in-progress input with the new mask, or subsequent saves only
// persist the suffix typed after that rerender.
if (!isFocusedRef.current) {
hasPendingUserEditRef.current = false
setLocalValue(initialValue)
}
}, [initialValue])
useDebounceEffect(
() => {
if (!hasPendingUserEditRef.current) {
return
}
hasPendingUserEditRef.current = false
onChange(localValue)
},
100,
@@ -63,7 +70,10 @@ export const ApiKeyField = ({
onFocus={() => {
isFocusedRef.current = true
}}
onInput={(e: any) => setLocalValue(e.target.value)}
onInput={(e) => {
hasPendingUserEditRef.current = true
setLocalValue((e.target as HTMLInputElement | null)?.value ?? "")
}}
placeholder={placeholder}
required={true}
style={{ width: "100%" }}
@@ -1,3 +1,4 @@
import type { ProviderConfigResponse } from "@shared/proto/cline/models"
import { ApiFormat } from "@shared/proto/cline/models"
import { fireEvent, render, screen, waitFor } from "@testing-library/react"
import type { ChangeEventHandler, ReactNode } from "react"
@@ -6,6 +7,8 @@ import { useProviderConfig } from "@/hooks/useProviderConfig"
import { useProviderModels } from "@/hooks/useProviderModels"
import { GenericProviderSettings } from "./GenericProviderSettings"
const providerConfig = (config: Partial<ProviderConfigResponse>): ProviderConfigResponse => config as ProviderConfigResponse
vi.mock("@/hooks/useProviderModels", () => ({
useProviderModels: vi.fn(),
}))
@@ -147,7 +150,11 @@ describe("GenericProviderSettings", () => {
refresh: vi.fn(),
fingerprint: "fingerprint",
})
vi.mocked(useProviderConfig).mockReturnValue({ config: { apiKeyLength: 12 } as any, write, commitSelection: vi.fn() })
vi.mocked(useProviderConfig).mockReturnValue({
config: providerConfig({ apiKeyLength: 12 }),
write,
commitSelection: vi.fn(),
})
render(
<GenericProviderSettings
@@ -168,6 +175,158 @@ describe("GenericProviderSettings", () => {
await waitFor(() => expect(write).toHaveBeenCalledWith({ apiKey: "new-secret" }))
})
it("does not clear API keys while provider config is loading", async () => {
const write = vi.fn(async () => undefined)
vi.mocked(useProviderModels).mockReturnValue({
models: {},
defaultModelId: "",
isLoading: false,
isStale: false,
error: undefined,
refresh: vi.fn(),
fingerprint: "fingerprint",
})
vi.mocked(useProviderConfig).mockReturnValue({ config: undefined, write, commitSelection: vi.fn() })
render(
<GenericProviderSettings
allowsCustomIds={false}
currentMode="act"
providerId="deepseek"
providerName="DeepSeek"
showModelOptions={false}
/>,
)
await new Promise((resolve) => setTimeout(resolve, 150))
expect(write).not.toHaveBeenCalled()
})
it("does not save when provider config hydrates a saved API key", async () => {
const write = vi.fn(async () => undefined)
vi.mocked(useProviderModels).mockReturnValue({
models: {},
defaultModelId: "",
isLoading: false,
isStale: false,
error: undefined,
refresh: vi.fn(),
fingerprint: "fingerprint",
})
vi.mocked(useProviderConfig).mockReturnValue({ config: undefined, write, commitSelection: vi.fn() })
const { rerender } = render(
<GenericProviderSettings
allowsCustomIds={false}
currentMode="act"
providerId="deepseek"
providerName="DeepSeek"
showModelOptions={false}
/>,
)
await new Promise((resolve) => setTimeout(resolve, 150))
expect(write).not.toHaveBeenCalled()
vi.mocked(useProviderConfig).mockReturnValue({
config: providerConfig({ apiKeyLength: 8 }),
write,
commitSelection: vi.fn(),
})
rerender(
<GenericProviderSettings
allowsCustomIds={false}
currentMode="act"
providerId="deepseek"
providerName="DeepSeek"
showModelOptions={false}
/>,
)
expect(screen.getByDisplayValue("••••••••")).toBeInTheDocument()
await new Promise((resolve) => setTimeout(resolve, 150))
expect(write).not.toHaveBeenCalled()
})
it("does not write a mask if config hydrates after a blurred edit", async () => {
const write = vi.fn(async () => undefined)
vi.mocked(useProviderModels).mockReturnValue({
models: {},
defaultModelId: "",
isLoading: false,
isStale: false,
error: undefined,
refresh: vi.fn(),
fingerprint: "fingerprint",
})
vi.mocked(useProviderConfig).mockReturnValue({ config: undefined, write, commitSelection: vi.fn() })
const { rerender } = render(
<GenericProviderSettings
allowsCustomIds={false}
currentMode="act"
providerId="deepseek"
providerName="DeepSeek"
showModelOptions={false}
/>,
)
const apiKeyInput = screen.getByPlaceholderText("Enter API Key...")
fireEvent.input(apiKeyInput, { target: { value: "partial-key" } })
fireEvent.blur(apiKeyInput)
vi.mocked(useProviderConfig).mockReturnValue({
config: providerConfig({ apiKeyLength: 8 }),
write,
commitSelection: vi.fn(),
})
rerender(
<GenericProviderSettings
allowsCustomIds={false}
currentMode="act"
providerId="deepseek"
providerName="DeepSeek"
showModelOptions={false}
/>,
)
await new Promise((resolve) => setTimeout(resolve, 150))
expect(write).not.toHaveBeenCalledWith({ apiKey: "••••••••" })
expect(write).not.toHaveBeenCalled()
})
it("still allows users to clear a saved API key", async () => {
const write = vi.fn(async () => undefined)
vi.mocked(useProviderModels).mockReturnValue({
models: {},
defaultModelId: "",
isLoading: false,
isStale: false,
error: undefined,
refresh: vi.fn(),
fingerprint: "fingerprint",
})
vi.mocked(useProviderConfig).mockReturnValue({
config: providerConfig({ apiKeyLength: 8 }),
write,
commitSelection: vi.fn(),
})
render(
<GenericProviderSettings
allowsCustomIds={false}
currentMode="act"
providerId="deepseek"
providerName="DeepSeek"
showModelOptions={false}
/>,
)
fireEvent.input(screen.getByDisplayValue("••••••••"), { target: { value: "" } })
await waitFor(() => expect(write).toHaveBeenCalledWith({ apiKey: "" }))
})
it("does not persist mask characters when editing a saved API key", async () => {
const write = vi.fn(async () => undefined)
vi.mocked(useProviderModels).mockReturnValue({
@@ -179,7 +338,11 @@ describe("GenericProviderSettings", () => {
refresh: vi.fn(),
fingerprint: "fingerprint",
})
vi.mocked(useProviderConfig).mockReturnValue({ config: { apiKeyLength: 7 } as any, write, commitSelection: vi.fn() })
vi.mocked(useProviderConfig).mockReturnValue({
config: providerConfig({ apiKeyLength: 7 }),
write,
commitSelection: vi.fn(),
})
render(
<GenericProviderSettings
@@ -208,7 +371,11 @@ describe("GenericProviderSettings", () => {
refresh: vi.fn(),
fingerprint: "fingerprint",
})
vi.mocked(useProviderConfig).mockReturnValue({ config: { apiKeyLength: 0 } as any, write, commitSelection: vi.fn() })
vi.mocked(useProviderConfig).mockReturnValue({
config: providerConfig({ apiKeyLength: 0 }),
write,
commitSelection: vi.fn(),
})
const { rerender } = render(
<GenericProviderSettings
@@ -226,7 +393,11 @@ describe("GenericProviderSettings", () => {
await waitFor(() => expect(write).toHaveBeenCalledWith({ apiKey: "max" }))
vi.mocked(useProviderConfig).mockReturnValue({ config: { apiKeyLength: 3 } as any, write, commitSelection: vi.fn() })
vi.mocked(useProviderConfig).mockReturnValue({
config: providerConfig({ apiKeyLength: 3 }),
write,
commitSelection: vi.fn(),
})
rerender(
<GenericProviderSettings
allowsCustomIds={false}
@@ -264,7 +435,7 @@ describe("GenericProviderSettings", () => {
fingerprint: "fingerprint",
})
vi.mocked(useProviderConfig).mockReturnValue({
config: { baseUrl: "https://custom.example", apiKeyLength: 0 } as any,
config: providerConfig({ baseUrl: "https://custom.example", apiKeyLength: 0 }),
write,
commitSelection: vi.fn(),
})
+139 -189
View File
@@ -19,7 +19,7 @@
},
"apps/cli": {
"name": "@cline/cli",
"version": "3.0.30",
"version": "3.0.31",
"bin": {
"cline": "src/index.ts",
},
@@ -616,7 +616,7 @@
},
"sdk/packages/agents": {
"name": "@cline/agents",
"version": "0.0.53",
"version": "0.0.54",
"dependencies": {
"@cline/llms": "workspace:*",
"@cline/shared": "workspace:*",
@@ -625,7 +625,7 @@
},
"sdk/packages/core": {
"name": "@cline/core",
"version": "0.0.53",
"version": "0.0.54",
"dependencies": {
"@cline/agents": "workspace:*",
"@cline/llms": "workspace:*",
@@ -663,7 +663,7 @@
},
"sdk/packages/llms": {
"name": "@cline/llms",
"version": "0.0.53",
"version": "0.0.54",
"dependencies": {
"@ai-sdk/amazon-bedrock": "^4.0.89",
"@ai-sdk/anthropic": "^3.0.68",
@@ -697,14 +697,14 @@
},
"sdk/packages/sdk": {
"name": "@cline/sdk",
"version": "0.0.53",
"version": "0.0.54",
"dependencies": {
"@cline/core": "workspace:*",
},
},
"sdk/packages/shared": {
"name": "@cline/shared",
"version": "0.0.53",
"version": "0.0.54",
"dependencies": {
"aws4fetch": "^1.0.20",
"jsonrepair": "^3.13.2",
@@ -769,23 +769,23 @@
"@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="],
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.3.183", "", { "optionalDependencies": { "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.183", "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.183", "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.183", "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.183", "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.183", "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.183", "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.183", "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.183" }, "peerDependencies": { "@anthropic-ai/sdk": ">=0.93.0", "@modelcontextprotocol/sdk": "^1.29.0", "zod": "^4.0.0" } }, "sha512-rAKws76RKdFu+DK5q9wYN0AGLRdPrw0vGd+TMzSFynFdxwUAehYc9vUKJoK27j2xRoHFdNt+JDsih5BzsUzi+Q=="],
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.3.185", "", { "optionalDependencies": { "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.185", "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.185", "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.185", "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.185", "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.185", "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.185", "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.185", "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.185" }, "peerDependencies": { "@anthropic-ai/sdk": ">=0.93.0", "@modelcontextprotocol/sdk": "^1.29.0", "zod": "^4.0.0" } }, "sha512-UDDd0cInvOufmR88btR/Ursnh71sb5scoutNlRS2L0LBdkt7JEqYJ0jt7DnOYHEVhMx8AAcg0eYgvXztcWUnFA=="],
"@anthropic-ai/claude-agent-sdk-darwin-arm64": ["@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.183", "", { "os": "darwin", "cpu": "arm64" }, "sha512-o/+sxwKgXuw6RG5cERWjvcvL1CDSPe/TaXMhax+dq+V4lDOI5iTqg3y5Wfb6dL3xlWoTA2OhWowDQllKbE04LQ=="],
"@anthropic-ai/claude-agent-sdk-darwin-arm64": ["@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.185", "", { "os": "darwin", "cpu": "arm64" }, "sha512-P2Svxx1IFrS9aw4ylJBtcoJrc/OAOVnDs+o05x4TV7HoHUQUcKZ9XhWOGnadyVT0KLyoktgjyjnaK7Zdtc0Ldg=="],
"@anthropic-ai/claude-agent-sdk-darwin-x64": ["@anthropic-ai/claude-agent-sdk-darwin-x64@0.3.183", "", { "os": "darwin", "cpu": "x64" }, "sha512-V7Cf8JeD5EPf4MPomFUlEblCIQI0wg+aWdOSqvfMsDmCBEHljd52CQ3a7W263oVt6I7QUfRTpX2KNvdma56rDA=="],
"@anthropic-ai/claude-agent-sdk-darwin-x64": ["@anthropic-ai/claude-agent-sdk-darwin-x64@0.3.185", "", { "os": "darwin", "cpu": "x64" }, "sha512-H69m2hIJawzSkCDNO/CPLQCYvDGdlbZdzGTgGF8/IQuB4O81sv8WSAh6TBXwtFXOgEi4HtrUYlxiFDmTHt7hMA=="],
"@anthropic-ai/claude-agent-sdk-linux-arm64": ["@anthropic-ai/claude-agent-sdk-linux-arm64@0.3.183", "", { "os": "linux", "cpu": "arm64" }, "sha512-qqnSf85D3uBH7kRHLGOMhr7aQrREUZ4hUlCztrkJe/twUT4bmJsO86zamnAf7vOYeCul/sHYwBMmXtOfI+cHZg=="],
"@anthropic-ai/claude-agent-sdk-linux-arm64": ["@anthropic-ai/claude-agent-sdk-linux-arm64@0.3.185", "", { "os": "linux", "cpu": "arm64" }, "sha512-KtXMCoprGYTSPb/A0/kjrO+PpDJAFbHhDd8rjqA4izU51OYjTDkahsGCcSTFM2jA8krOhPqzx/SNVuAVWmexwA=="],
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": ["@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.3.183", "", { "os": "linux", "cpu": "arm64" }, "sha512-ua6XaiCcYYKfd0CY9cjoyYnTQWI2GocpbP7Gwvmr+9taW/lTpQGVQH3FrF2VFKXVSt7AaVE6Z7WN9oPxcxl3VA=="],
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": ["@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.3.185", "", { "os": "linux", "cpu": "arm64" }, "sha512-1GRpKYrg5foomW+qLrxm/k2R/5PEU7RAuEdX65HV6lFMJR3HsGj8qfBq4KCJHTS3r4YdVrFLvPQ/mUqbY6klIA=="],
"@anthropic-ai/claude-agent-sdk-linux-x64": ["@anthropic-ai/claude-agent-sdk-linux-x64@0.3.183", "", { "os": "linux", "cpu": "x64" }, "sha512-GvfNp9XsKWFvr0EH3NgX2pqzHPYhFeqhOmVc4A+2e+GdxBMYUEa0ylfPLJItahQU9FkTJAw2oQNnzTHPPm1GxA=="],
"@anthropic-ai/claude-agent-sdk-linux-x64": ["@anthropic-ai/claude-agent-sdk-linux-x64@0.3.185", "", { "os": "linux", "cpu": "x64" }, "sha512-8U5wu6dNcyzRkwoae0Gu5ZHs6lRSu8CXqd9yrilX3fHR9kKICrm76bLu5q2auuSc/8dx6VL5ZXqSzFKIMp//zw=="],
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": ["@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.3.183", "", { "os": "linux", "cpu": "x64" }, "sha512-wp+p+4xeZOW+h4InMKR7nIdGlgYftXyR+ErOVz7i2tMRcKuYMoc5jRIYjdJkuETMSr5nsRj0rDeO5gG7M6MGZQ=="],
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": ["@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.3.185", "", { "os": "linux", "cpu": "x64" }, "sha512-YAky0Oez+YYrlcas/xDiaCm9AUX0z1YwM+pnk+CFxEr/ICmc7MbiFsnEIXFLPHaox9bE5iOzw4LS4b/c1tIJRg=="],
"@anthropic-ai/claude-agent-sdk-win32-arm64": ["@anthropic-ai/claude-agent-sdk-win32-arm64@0.3.183", "", { "os": "win32", "cpu": "arm64" }, "sha512-cGyMC9YDsrCQ4av74dMVa2liOb5oLFX4+1SxwJGRCW7mBVBazcM6rg7ifWbd9XfkDhNPKOIfyiloipyMfoaE4g=="],
"@anthropic-ai/claude-agent-sdk-win32-arm64": ["@anthropic-ai/claude-agent-sdk-win32-arm64@0.3.185", "", { "os": "win32", "cpu": "arm64" }, "sha512-pJEOaCLQQQWSyBeseR/GAn4eEp4WtMDP/o5yPMuuAsNWeoJXM7cY4j/N3KBE6GZofgnYdWEXILF2uw3SjbtINA=="],
"@anthropic-ai/claude-agent-sdk-win32-x64": ["@anthropic-ai/claude-agent-sdk-win32-x64@0.3.183", "", { "os": "win32", "cpu": "x64" }, "sha512-h/XzbrSmXGroTk/FYKR6J4/8G9vDb1HUUUeNXeBGqGW1kppIiWPJKLRzjtSe0brVjADOKOT6tE5IHK0mV/1gBw=="],
"@anthropic-ai/claude-agent-sdk-win32-x64": ["@anthropic-ai/claude-agent-sdk-win32-x64@0.3.185", "", { "os": "win32", "cpu": "x64" }, "sha512-/Kf9XneSuIkSHNwrxOVY0XT7RovAEcx5nDASKzku4994cZz54otU5mT2jT6dwevDLEStfgts5x/A4z0AePwVTQ=="],
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.37.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-tHjX2YbkUBwEgg0JZU3EFSSAQPoK4qQR/NFYa8Vtzd5UAyXzZksCw2In69Rml4R/TyHPBfRYaLK35XiOe33pjw=="],
@@ -1051,9 +1051,9 @@
"@discordjs/ws": ["@discordjs/ws@1.2.3", "", { "dependencies": { "@discordjs/collection": "^2.1.0", "@discordjs/rest": "^2.5.1", "@discordjs/util": "^1.1.0", "@sapphire/async-queue": "^1.5.2", "@types/ws": "^8.5.10", "@vladfrangu/async_event_emitter": "^2.2.4", "discord-api-types": "^0.38.1", "tslib": "^2.6.2", "ws": "^8.17.0" } }, "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw=="],
"@dotenvx/dotenvx": ["@dotenvx/dotenvx@1.74.3", "", { "dependencies": { "commander": "^11.1.0", "conf": "^10.2.0", "dotenv": "^17.2.1", "eciesjs": "^0.4.10", "enquirer": "^2.4.1", "env-paths": "^2.2.1", "execa": "^5.1.1", "fdir": "^6.2.0", "ignore": "^5.3.0", "object-treeify": "1.1.33", "open": "^8.4.2", "picomatch": "^4.0.4", "systeminformation": "^5.22.11", "undici": "^7.11.0", "which": "^4.0.0", "yocto-spinner": "^1.1.0" }, "bin": { "dotenvx": "src/cli/dotenvx.js" } }, "sha512-+VjTIiOCApjB0K4a41+SkN18gTetmhU9UN2JD8LHeNUqo/38FmtgvdtWsW/WWN1dyYRGS7XKeAAb/ruy7x1tRw=="],
"@dotenvx/dotenvx": ["@dotenvx/dotenvx@1.75.1", "", { "dependencies": { "@dotenvx/primitives": "^0.8.0", "commander": "^11.1.0", "conf": "^10.2.0", "dotenv": "^17.2.1", "enquirer": "^2.4.1", "env-paths": "^2.2.1", "execa": "^5.1.1", "fdir": "^6.2.0", "ignore": "^5.3.0", "object-treeify": "1.1.33", "open": "^8.4.2", "picomatch": "^4.0.4", "systeminformation": "^5.22.11", "undici": "^7.11.0", "which": "^4.0.0", "yocto-spinner": "^1.1.0" }, "bin": { "dotenvx": "src/cli/dotenvx.js" } }, "sha512-/BITOC9dmS/edY2zQwZNicQ059O6RKabtQfyEafV0nGtfYRNHYy1DIPiYVcov40+tob9hfmBnbR963dS+EQ1DQ=="],
"@ecies/ciphers": ["@ecies/ciphers@0.2.6", "", { "peerDependencies": { "@noble/ciphers": "^1.0.0" } }, "sha512-patgsRPKGkhhoBjETV4XxD0En4ui5fbX0hzayqI3M8tvNMGUoUvmyYAIWwlxBc1KX5cturfqByYdj5bYGRpN9g=="],
"@dotenvx/primitives": ["@dotenvx/primitives@0.8.0", "", {}, "sha512-VYJy0uhFm9zTJ1TxBaW/pA8bjbOM/OttaNMwZ1RHG4JKyRG7DhSdiqD1ipQoAyoD22olUtxbP78W9xY3Wd11bg=="],
"@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="],
@@ -1643,12 +1643,6 @@
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.2.6", "", { "os": "win32", "cpu": "x64" }, "sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA=="],
"@noble/ciphers": ["@noble/ciphers@1.3.0", "", {}, "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw=="],
"@noble/curves": ["@noble/curves@1.9.7", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw=="],
"@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
"@nodable/entities": ["@nodable/entities@2.2.0", "", {}, "sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
@@ -1671,7 +1665,7 @@
"@openai/codex-win32-x64": ["@openai/codex@0.130.0-win32-x64", "", { "os": "win32", "cpu": "x64" }, "sha512-FzMznm7fr5/nbjZgOujZ9Y9AbdGm7ji1FOoWiY3U+srqauvZaTgn6o6aCheSL7kuymu7nTLOO/cAyWV6NuesqQ=="],
"@opencode-ai/sdk": ["@opencode-ai/sdk@1.17.8", "", { "dependencies": { "cross-spawn": "7.0.6" } }, "sha512-6MKmsj2ujZyL44jy+12dpwWYDYKPS9fUr+0wVQxaIlPYQ/eAt8T8T3QrybplJ5ZtHfZUX+esXZ02x2UYYm7oEw=="],
"@opencode-ai/sdk": ["@opencode-ai/sdk@1.17.9", "", { "dependencies": { "cross-spawn": "7.0.6" } }, "sha512-MHmXEpGPHkg14v1p+cUlIOUxd6DQdSElfau9nqY7tcDI0x5r4Y8D0dKXcyAh0Gc73ptaGW67Vg84nkcV6O27Pw=="],
"@opentelemetry/api": ["@opentelemetry/api@1.9.1", "", {}, "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q=="],
@@ -1809,7 +1803,7 @@
"@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="],
"@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="],
"@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="],
@@ -1831,7 +1825,7 @@
"@radix-ui/react-hover-card": ["@radix-ui/react-hover-card@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg=="],
"@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="],
"@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
"@radix-ui/react-label": ["@radix-ui/react-label@2.1.8", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A=="],
@@ -1853,7 +1847,7 @@
"@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="],
"@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.6", "", { "dependencies": { "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wetd0QI77DbvrPpTAvH1SqOxsYF2wZe5TNxqwOd5Ty4XDpV3dpV0s8K/1MGMJBeY5o7lg8ub5VIt1Ub+yVen6g=="],
"@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="],
"@radix-ui/react-progress": ["@radix-ui/react-progress@1.1.8", "", { "dependencies": { "@radix-ui/react-context": "1.1.3", "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-+gISHcSPUJ7ktBy9RnTqbdKW78bcGke3t6taawyZ71pio1JewwGSJizycs7rLhGTvMJYCQB1DBK4KQsxs7U8dA=="],
@@ -2949,7 +2943,7 @@
"character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="],
"chardet": ["chardet@2.1.1", "", {}, "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ=="],
"chardet": ["chardet@2.2.0", "", {}, "sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA=="],
"chat": ["chat@4.31.0", "", { "dependencies": { "@workflow/serde": "4.1.0-beta.2", "mdast-util-to-string": "^4.0.0", "remark-gfm": "^4.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "remend": "^1.2.1", "unified": "^11.0.5" }, "peerDependencies": { "ai": "^6.0.182", "zod": "^3.0.0 || ^4.0.0" }, "optionalPeers": ["ai", "zod"] }, "sha512-F6oJq9JUraLFkITS96/NKgwZwBfZX8aOwOj5qMs5NNwnOGHrSXZ5+osK+EA4HjzfyUEDfFTNiOSmyzgtFLpuWg=="],
@@ -3263,8 +3257,6 @@
"ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
"eciesjs": ["eciesjs@0.4.18", "", { "dependencies": { "@ecies/ciphers": "^0.2.5", "@noble/ciphers": "^1.3.0", "@noble/curves": "^1.9.7", "@noble/hashes": "^1.8.0" } }, "sha512-wG99Zcfcys9fZux7Cft8BAX/YrOJLJSZ3jyYPfhZHqN2E+Ffx+QXBDsv3gubEgPtV6dTzJMSQUwk1H98/t/0wQ=="],
"editions": ["editions@6.22.0", "", { "dependencies": { "version-range": "^4.15.0" } }, "sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ=="],
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
@@ -3289,7 +3281,7 @@
"end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="],
"enhanced-resolve": ["enhanced-resolve@5.24.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-SkE2t82KlkkxQRVMVLAGKxLfORGQfrkx5dkj+vlgXRVNEdPc4eZcR+J/Fvj8C+yKSFH5L0q3NFlyufOVQnCcYQ=="],
"enhanced-resolve": ["enhanced-resolve@5.21.6", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ=="],
"enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="],
@@ -3313,7 +3305,7 @@
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
"es-toolkit": ["es-toolkit@1.47.1", "", {}, "sha512-5RAqEwf4P4E17p+W75KLOWw/nOvKZzSQpxM32IpI2KZLaVonjTrZ0Ai5ghMaVI9eKC2p8eoQgcBdkEDgzFk6+Q=="],
"es-toolkit": ["es-toolkit@1.48.1", "", {}, "sha512-wfnXlwd5I75eXRtdD2vuEs50xHHESECDsGD7yiQnfFVNoa5522NwXEbmgo98LfiukSQHs+mBM7/YG3qKJB9/mQ=="],
"esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="],
@@ -4159,7 +4151,7 @@
"nano-css": ["nano-css@5.6.2", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15", "css-tree": "^1.1.2", "csstype": "^3.1.2", "fastest-stable-stringify": "^2.0.2", "inline-style-prefixer": "^7.0.1", "rtl-css-js": "^1.16.1", "stacktrace-js": "^2.0.2", "stylis": "^4.3.0" }, "peerDependencies": { "react": "*", "react-dom": "*" } }, "sha512-+6bHaC8dSDGALM1HJjOHVXpuastdu2xFoZlC77Jh4cg+33Zcgm+Gxd+1xsnpZK14eyHObSp82+ll5y3SX75liw=="],
"nanoid": ["nanoid@5.1.14", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-5c8l8kVzqpnDPaicbEop/fV0Q1w16FmbWtVhMqugTozAwYdlIQojWH5a/M7UfziFmGdQRrUdV+EPzc9Xng3VAQ=="],
"nanoid": ["nanoid@5.1.15", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-kBg3RpGtIe+RpTbyXwoI6pk5yD7KUiI3sygUqgeBMRst42KmhB4RZC7eiO9Wa1HIpaCCtpE2DJ6OI4Wi5ebwFw=="],
"napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="],
@@ -4317,7 +4309,7 @@
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
"path-expression-matcher": ["path-expression-matcher@1.5.0", "", {}, "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ=="],
"path-expression-matcher": ["path-expression-matcher@1.6.0", "", {}, "sha512-e5y7RCLHKjemsgQ4eqGJtPyr10ILz25HO7flzxhTV8bgvd5yHx98DGtCAtbVW9f2TqnYI/gEVZd+vz7snrdPTw=="],
"path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="],
@@ -4465,7 +4457,7 @@
"react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="],
"react-hook-form": ["react-hook-form@7.79.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-mhYp/MTmXvzYX6AJcJVko0rktoIhhmRnEouObj4wF5i/tCttgJvnp1+9wRkpITZjDTqpo4IOSJqu0dBlPlV/Lw=="],
"react-hook-form": ["react-hook-form@7.80.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-4P+fk6oXsxY+6xSj7Euhc2sumQD8zQqCuVHoJwoyp9EchP+IUW9OESB7uHFJOKsIBQ4MQqYE84INJFqUCYNoOg=="],
"react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
@@ -4611,7 +4603,7 @@
"rw": ["rw@1.3.3", "", {}, "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="],
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
"safe-stable-stringify": ["safe-stable-stringify@2.5.0", "", {}, "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="],
@@ -4799,7 +4791,7 @@
"styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="],
"stylis": ["stylis@4.3.6", "", {}, "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ=="],
"stylis": ["stylis@4.4.0", "", {}, "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA=="],
"supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
@@ -4811,9 +4803,9 @@
"symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="],
"systeminformation": ["systeminformation@5.31.7", "", { "os": "!aix", "bin": { "systeminformation": "lib/cli.js" } }, "sha512-/8NC53e5nP9nmhn42/ncdOkyJnOoue/Vy+tJOyUGd1Yv66G069wK4rrziwhrqDETgk78CudTQupw5z19S5uoZw=="],
"systeminformation": ["systeminformation@5.31.8", "", { "os": "!aix", "bin": { "systeminformation": "lib/cli.js" } }, "sha512-/HGurKdgjYGbfwCPSokoX4WAlasVxuj1SnTukLiIYHPkjbDbN1PMATj8mkHpqkVzUF34y5ZyCY1TMKP32AI9rA=="],
"tabbable": ["tabbable@6.4.0", "", {}, "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg=="],
"tabbable": ["tabbable@6.5.0", "", {}, "sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA=="],
"table": ["table@6.9.0", "", { "dependencies": { "ajv": "^8.0.1", "lodash.truncate": "^4.4.2", "slice-ansi": "^4.0.0", "string-width": "^4.2.3", "strip-ansi": "^6.0.1" } }, "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A=="],
@@ -5257,8 +5249,6 @@
"@heroui/use-is-mobile/@react-aria/ssr": ["@react-aria/ssr@3.9.10", "", { "dependencies": { "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ=="],
"@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="],
"@jest/types/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="],
"@jest/types/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
@@ -5461,46 +5451,30 @@
"@radix-ui/react-accessible-icon/@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.6", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.6" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-jCE0WljWifTI4niIMCll06kGpsJTAPiZVU9H4WR1N6qW7At9ystHbN7dDB+we2xH535roFHj7qKS+RGj0FMDWQ=="],
"@radix-ui/react-accordion/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-accordion/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
"@radix-ui/react-accordion/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-accordion/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
"@radix-ui/react-alert-dialog/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-alert-dialog/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-alert-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
"@radix-ui/react-aspect-ratio/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="],
"@radix-ui/react-arrow/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.6", "", { "dependencies": { "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wetd0QI77DbvrPpTAvH1SqOxsYF2wZe5TNxqwOd5Ty4XDpV3dpV0s8K/1MGMJBeY5o7lg8ub5VIt1Ub+yVen6g=="],
"@radix-ui/react-avatar/@radix-ui/react-context": ["@radix-ui/react-context@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw=="],
"@radix-ui/react-avatar/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="],
"@radix-ui/react-avatar/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-checkbox/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-checkbox/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-checkbox/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
"@radix-ui/react-collapsible/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-collapsible/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
"@radix-ui/react-collapsible/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-collapsible/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
"@radix-ui/react-collapsible/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-collection/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-collection/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
@@ -5509,68 +5483,48 @@
"@radix-ui/react-context-menu/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
"@radix-ui/react-dialog/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-dialog/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
"@radix-ui/react-dialog/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
"@radix-ui/react-dialog/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
"@radix-ui/react-dismissable-layer/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-dismissable-layer/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-dismissable-layer/@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="],
"@radix-ui/react-dropdown-menu/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-dropdown-menu/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
"@radix-ui/react-dropdown-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-dropdown-menu/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
"@radix-ui/react-focus-scope/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-focus-scope/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-form/@radix-ui/primitive": ["@radix-ui/primitive@1.1.4", "", {}, "sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ=="],
"@radix-ui/react-form/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="],
"@radix-ui/react-form/@radix-ui/react-context": ["@radix-ui/react-context@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg=="],
"@radix-ui/react-form/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="],
"@radix-ui/react-form/@radix-ui/react-label": ["@radix-ui/react-label@2.1.10", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.6" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ib0zvq2ZsAqKm5tRnqGJn3vOxSgIts5ToxsXT0q1S/GfLD1Zj7UOEnkw8u2w6sRmn47djpQWuSU1DCL1R29/yw=="],
"@radix-ui/react-hover-card/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-form/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.6", "", { "dependencies": { "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wetd0QI77DbvrPpTAvH1SqOxsYF2wZe5TNxqwOd5Ty4XDpV3dpV0s8K/1MGMJBeY5o7lg8ub5VIt1Ub+yVen6g=="],
"@radix-ui/react-hover-card/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-hover-card/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
"@radix-ui/react-label/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="],
"@radix-ui/react-menu/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-menu/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
"@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-menu/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
"@radix-ui/react-menubar/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-menubar/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
"@radix-ui/react-menubar/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-menubar/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
"@radix-ui/react-navigation-menu/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-navigation-menu/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
"@radix-ui/react-navigation-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-navigation-menu/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
@@ -5583,24 +5537,30 @@
"@radix-ui/react-one-time-password-field/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.10", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-primitive": "2.1.6", "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-IVVz4EvBcKjrzKgof714qDnz/SzQAkLA2Emh5edlHbgcE6fNd3Un6CJLlaYcnm8N4JmAtzQgse4dOKxcD2yc9g=="],
"@radix-ui/react-one-time-password-field/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="],
"@radix-ui/react-one-time-password-field/@radix-ui/react-context": ["@radix-ui/react-context@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg=="],
"@radix-ui/react-one-time-password-field/@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA=="],
"@radix-ui/react-one-time-password-field/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.6", "", { "dependencies": { "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wetd0QI77DbvrPpTAvH1SqOxsYF2wZe5TNxqwOd5Ty4XDpV3dpV0s8K/1MGMJBeY5o7lg8ub5VIt1Ub+yVen6g=="],
"@radix-ui/react-one-time-password-field/@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-collection": "1.1.10", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-primitive": "2.1.6", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9gkwneI0guf8JDmrFxPjJF6Ozzgioyw+/lonYNCwefS9ZHA05er0BVHiXr+LbWGHxUfczvMY6G1oiZZi1VzjRw=="],
"@radix-ui/react-one-time-password-field/@radix-ui/react-use-is-hydrated": ["@radix-ui/react-use-is-hydrated@0.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-qwOiz4Tjo8CNnrOLAYUMXeZwDzXgXpvK4TKQPmWLECM9XoWvA6+0Z2/7Ag3A4ivjS4ovbLJPbskkxioFyBhr8A=="],
"@radix-ui/react-password-toggle-field/@radix-ui/primitive": ["@radix-ui/primitive@1.1.4", "", {}, "sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ=="],
"@radix-ui/react-password-toggle-field/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="],
"@radix-ui/react-password-toggle-field/@radix-ui/react-context": ["@radix-ui/react-context@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg=="],
"@radix-ui/react-password-toggle-field/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="],
"@radix-ui/react-password-toggle-field/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.6", "", { "dependencies": { "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wetd0QI77DbvrPpTAvH1SqOxsYF2wZe5TNxqwOd5Ty4XDpV3dpV0s8K/1MGMJBeY5o7lg8ub5VIt1Ub+yVen6g=="],
"@radix-ui/react-password-toggle-field/@radix-ui/react-use-is-hydrated": ["@radix-ui/react-use-is-hydrated@0.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-qwOiz4Tjo8CNnrOLAYUMXeZwDzXgXpvK4TKQPmWLECM9XoWvA6+0Z2/7Ag3A4ivjS4ovbLJPbskkxioFyBhr8A=="],
"@radix-ui/react-popover/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-popover/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
"@radix-ui/react-popover/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-popover/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
@@ -5609,8 +5569,6 @@
"@radix-ui/react-popper/@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="],
"@radix-ui/react-popper/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-popper/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-popper/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
@@ -5619,40 +5577,22 @@
"@radix-ui/react-portal/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-presence/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA=="],
"@radix-ui/react-progress/@radix-ui/react-context": ["@radix-ui/react-context@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw=="],
"@radix-ui/react-progress/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="],
"@radix-ui/react-radio-group/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-radio-group/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-radio-group/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
"@radix-ui/react-roving-focus/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-roving-focus/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
"@radix-ui/react-roving-focus/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-roving-focus/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
"@radix-ui/react-scroll-area/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-scroll-area/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-scroll-area/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-select/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-select/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
"@radix-ui/react-select/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-select/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
@@ -5661,32 +5601,20 @@
"@radix-ui/react-select/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-separator/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="],
"@radix-ui/react-slider/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-slider/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-slider/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
"@radix-ui/react-slider/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-switch/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-switch/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-switch/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
"@radix-ui/react-tabs/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
"@radix-ui/react-tabs/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-tabs/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
"@radix-ui/react-toast/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-toast/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-toast/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
@@ -5707,16 +5635,14 @@
"@radix-ui/react-toolbar/@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA=="],
"@radix-ui/react-toolbar/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.6", "", { "dependencies": { "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wetd0QI77DbvrPpTAvH1SqOxsYF2wZe5TNxqwOd5Ty4XDpV3dpV0s8K/1MGMJBeY5o7lg8ub5VIt1Ub+yVen6g=="],
"@radix-ui/react-toolbar/@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-collection": "1.1.10", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-primitive": "2.1.6", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9gkwneI0guf8JDmrFxPjJF6Ozzgioyw+/lonYNCwefS9ZHA05er0BVHiXr+LbWGHxUfczvMY6G1oiZZi1VzjRw=="],
"@radix-ui/react-toolbar/@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.10", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.6" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Y6K6jLQCVfCnTL2MEtGxDLffkhNfEfHsEg3Wa8JU+IWdn3EWbLXd3OuOfQRN7p/W/cUce1WyTk3QeuAoDBzN9g=="],
"@radix-ui/react-toolbar/@radix-ui/react-toggle-group": ["@radix-ui/react-toggle-group@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-primitive": "2.1.6", "@radix-ui/react-roving-focus": "1.1.13", "@radix-ui/react-toggle": "1.1.12", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Xb9PLtlvU66F36LiKba6dFswu6V2mDkgidO4fNSbQHQwmZ9ObxMIO17MN/LJ4aWJecVuSVLAHPZjyeMzJrgeiA=="],
"@radix-ui/react-tooltip/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-tooltip/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
"@radix-ui/react-tooltip/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
"@radix-ui/react-tooltip/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
@@ -5791,13 +5717,11 @@
"@streamdown/code/shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="],
"@tailwindcss/node/enhanced-resolve": ["enhanced-resolve@5.21.6", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.5", "", { "dependencies": { "@tybys/wasm-util": "^0.10.2" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q=="],
@@ -5907,8 +5831,6 @@
"cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
"cmdk/@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.17", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-dismissable-layer": "1.1.13", "@radix-ui/react-focus-guards": "1.1.4", "@radix-ui/react-focus-scope": "1.1.10", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-portal": "1.1.12", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.6", "@radix-ui/react-slot": "1.3.0", "@radix-ui/react-use-controllable-state": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TDTYmpdq8dI2+Xgvgj9AJ8Ghqq+Eph/TRVEdaFQPDItIY+6QSkU7MJMeevw1568Yw/2Ijz8BTphPSP2XejKphw=="],
"compress-commons/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
"conf/ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="],
@@ -6029,6 +5951,8 @@
"jszip/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
"jwa/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
"keytar/node-addon-api": ["node-addon-api@4.3.0", "", {}, "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ=="],
@@ -6103,7 +6027,7 @@
"playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="],
"postcss/nanoid": ["nanoid@3.3.13", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-sPdqC6ByMVVGvF1ynvvMo0/o+oD1VX7DaHhijt1bFgjvBkHBib4t49GoNDhf2NDta4oeUNlaGbSt5K7qjZ955Q=="],
"postcss/nanoid": ["nanoid@3.3.15", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA=="],
"pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
@@ -6133,6 +6057,8 @@
"radix-ui/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.10", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-primitive": "2.1.6", "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-IVVz4EvBcKjrzKgof714qDnz/SzQAkLA2Emh5edlHbgcE6fNd3Un6CJLlaYcnm8N4JmAtzQgse4dOKxcD2yc9g=="],
"radix-ui/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="],
"radix-ui/@radix-ui/react-context": ["@radix-ui/react-context@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg=="],
"radix-ui/@radix-ui/react-context-menu": ["@radix-ui/react-context-menu@2.3.1", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-menu": "2.1.18", "@radix-ui/react-primitive": "2.1.6", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-XbrxS68W5dyiE4fAb96yvJwSVU5x66B20A99sD5Mk3xSWK/LqeOnx6TZnim1KieMjXS/CTFq8reOAjWxas2G8Q=="],
@@ -6167,6 +6093,8 @@
"radix-ui/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.6", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ=="],
"radix-ui/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.6", "", { "dependencies": { "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wetd0QI77DbvrPpTAvH1SqOxsYF2wZe5TNxqwOd5Ty4XDpV3dpV0s8K/1MGMJBeY5o7lg8ub5VIt1Ub+yVen6g=="],
"radix-ui/@radix-ui/react-progress": ["@radix-ui/react-progress@1.1.10", "", { "dependencies": { "@radix-ui/react-context": "1.1.4", "@radix-ui/react-primitive": "2.1.6" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-JYzEg60lk79PwKM27WZyKd7PW8O4OM5jOaFfRPfOyeXmMw7tLJh5kSj+CEjVTehszuwml/AdCzPGMXBTGf4BBw=="],
"radix-ui/@radix-ui/react-radio-group": ["@radix-ui/react-radio-group@1.4.1", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.6", "@radix-ui/react-roving-focus": "1.1.13", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-previous": "1.1.2", "@radix-ui/react-use-size": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/SSxZdKEo2Eo29FFRKd06EfFDYp8HryKg0WYg7QLXaydPzl52YfSvCH2a3QDBRdtcuwACroJT8UVjQVgOJ7P9A=="],
@@ -6277,10 +6205,14 @@
"string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"string_decoder/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"strip-literal/js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="],
"styled-components/stylis": ["stylis@4.3.6", "", {}, "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ=="],
"supports-hyperlinks/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
"table/ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="],
@@ -6303,6 +6235,8 @@
"unzipper/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
"vaul/@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.17", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-dismissable-layer": "1.1.13", "@radix-ui/react-focus-guards": "1.1.4", "@radix-ui/react-focus-scope": "1.1.10", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-portal": "1.1.12", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.6", "@radix-ui/react-slot": "1.3.0", "@radix-ui/react-use-controllable-state": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TDTYmpdq8dI2+Xgvgj9AJ8Ghqq+Eph/TRVEdaFQPDItIY+6QSkU7MJMeevw1568Yw/2Ijz8BTphPSP2XejKphw=="],
"verror/core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="],
"vite-node/es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="],
@@ -6311,7 +6245,7 @@
"webview/@types/node": ["@types/node@24.13.2", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA=="],
"webview-ui/@types/node": ["@types/node@22.19.21", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-VMeFBSCKQKmm2swI2kW51SFusDqekC6q9trBCvJ/JliDchFSuoYYKN7yVNjPthP1HKZcx3U1gI/wTcEBjEFKTA=="],
"webview-ui/@types/node": ["@types/node@22.20.0", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-QWlFW2wf3nTjC13/DqRnBpR4ZO36VJH/JVBkA/vcnmbTBNQIlnObqyqZE1tUR7+Ni23Lda8R1BxMfbXRpCUx5g=="],
"webview-ui/@types/react": ["@types/react@18.3.31", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" } }, "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw=="],
@@ -6513,7 +6447,7 @@
"@opentelemetry/sdk-node/@opentelemetry/sdk-trace-node/@opentelemetry/propagator-jaeger": ["@opentelemetry/propagator-jaeger@1.29.0", "", { "dependencies": { "@opentelemetry/core": "1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-EXIEYmFgybnFMijVgqx1mq/diWwSQcd0JWVksytAVQEnAiaDvP45WuncEVQkFIAC0gVxa2+Xr8wL5pF5jCVKbg=="],
"@radix-ui/react-accordion/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-accessible-icon/@radix-ui/react-visually-hidden/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.6", "", { "dependencies": { "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wetd0QI77DbvrPpTAvH1SqOxsYF2wZe5TNxqwOd5Ty4XDpV3dpV0s8K/1MGMJBeY5o7lg8ub5VIt1Ub+yVen6g=="],
"@radix-ui/react-accordion/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
@@ -6521,6 +6455,8 @@
"@radix-ui/react-accordion/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-arrow/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA=="],
"@radix-ui/react-checkbox/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
"@radix-ui/react-checkbox/@radix-ui/react-use-controllable-state/@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="],
@@ -6537,16 +6473,12 @@
"@radix-ui/react-context-menu/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-dialog/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-dialog/@radix-ui/react-use-controllable-state/@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="],
"@radix-ui/react-dialog/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-dismissable-layer/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
"@radix-ui/react-dropdown-menu/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-dropdown-menu/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
"@radix-ui/react-dropdown-menu/@radix-ui/react-use-controllable-state/@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="],
@@ -6555,16 +6487,14 @@
"@radix-ui/react-focus-scope/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
"@radix-ui/react-form/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA=="],
"@radix-ui/react-hover-card/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
"@radix-ui/react-hover-card/@radix-ui/react-use-controllable-state/@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="],
"@radix-ui/react-hover-card/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-menu/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-menubar/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-menubar/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
"@radix-ui/react-menubar/@radix-ui/react-use-controllable-state/@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="],
@@ -6577,9 +6507,13 @@
"@radix-ui/react-one-time-password-field/@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA=="],
"@radix-ui/react-one-time-password-field/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA=="],
"@radix-ui/react-one-time-password-field/@radix-ui/react-roving-focus/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="],
"@radix-ui/react-one-time-password-field/@radix-ui/react-roving-focus/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw=="],
"@radix-ui/react-popover/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-password-toggle-field/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA=="],
"@radix-ui/react-popover/@radix-ui/react-use-controllable-state/@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="],
@@ -6595,8 +6529,6 @@
"@radix-ui/react-radio-group/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-roving-focus/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-roving-focus/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
"@radix-ui/react-roving-focus/@radix-ui/react-use-controllable-state/@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="],
@@ -6617,8 +6549,6 @@
"@radix-ui/react-switch/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-tabs/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-tabs/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
"@radix-ui/react-tabs/@radix-ui/react-use-controllable-state/@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="],
@@ -6641,14 +6571,18 @@
"@radix-ui/react-toggle/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-toolbar/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA=="],
"@radix-ui/react-toolbar/@radix-ui/react-roving-focus/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.10", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-primitive": "2.1.6", "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-IVVz4EvBcKjrzKgof714qDnz/SzQAkLA2Emh5edlHbgcE6fNd3Un6CJLlaYcnm8N4JmAtzQgse4dOKxcD2yc9g=="],
"@radix-ui/react-toolbar/@radix-ui/react-roving-focus/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="],
"@radix-ui/react-toolbar/@radix-ui/react-roving-focus/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="],
"@radix-ui/react-toolbar/@radix-ui/react-roving-focus/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw=="],
"@radix-ui/react-toolbar/@radix-ui/react-toggle-group/@radix-ui/react-toggle": ["@radix-ui/react-toggle@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-primitive": "2.1.6", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-AsAVsYNZIlRBsci7BhE+QyQeKd1h6TffJYt+lF0QQkd5OpQ3klfIByPsCb4G0h/Fq6PJwh1FYNluzBFYzhk4+w=="],
"@radix-ui/react-tooltip/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
"@radix-ui/react-tooltip/@radix-ui/react-use-controllable-state/@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="],
"@radix-ui/react-tooltip/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
@@ -6837,22 +6771,6 @@
"cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"cmdk/@radix-ui/react-dialog/@radix-ui/primitive": ["@radix-ui/primitive@1.1.4", "", {}, "sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ=="],
"cmdk/@radix-ui/react-dialog/@radix-ui/react-context": ["@radix-ui/react-context@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg=="],
"cmdk/@radix-ui/react-dialog/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.6", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-escape-keydown": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-2v+zNAWWe0ySxgC0D0yeXMPQ23xZVgXZTerTz+JKlmdRj6gfTqmCcR29jb6d290DezXPGgruHWDX/vYUebtErg=="],
"cmdk/@radix-ui/react-dialog/@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q=="],
"cmdk/@radix-ui/react-dialog/@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.10", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.6", "@radix-ui/react-use-callback-ref": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fas/lXQqhVvqwAb64s5RFeHiHYElZ6SUQbZaNd6EkfhP/Al7wTIQ9WIR4QVX475tlu5yFCEdDcJH6/UwsZjMWw=="],
"cmdk/@radix-ui/react-dialog/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.12", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.6", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m309havGzsjLHHaIX50G5PlvRs3xkgPCsGk/5PTvYm8D5q33yG0J7w/712PTOhid7NTaFETtnSXjngHQavvhVw=="],
"cmdk/@radix-ui/react-dialog/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.6", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ=="],
"cmdk/@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA=="],
"conf/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
"cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
@@ -6863,8 +6781,6 @@
"d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="],
"duplexer2/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
"duplexer2/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
"enquirer/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
@@ -6915,12 +6831,8 @@
"jest-util/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
"jszip/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
"jszip/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
"lazystream/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
"lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
"log-symbols/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
@@ -6941,7 +6853,7 @@
"mocha/minimatch/brace-expansion": ["brace-expansion@2.1.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA=="],
"next/postcss/nanoid": ["nanoid@3.3.13", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-sPdqC6ByMVVGvF1ynvvMo0/o+oD1VX7DaHhijt1bFgjvBkHBib4t49GoNDhf2NDta4oeUNlaGbSt5K7qjZ955Q=="],
"next/postcss/nanoid": ["nanoid@3.3.15", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA=="],
"normalize-package-data/hosted-git-info/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
@@ -6949,20 +6861,40 @@
"pkg-up/find-up/locate-path": ["locate-path@3.0.0", "", { "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="],
"radix-ui/@radix-ui/react-accordion/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="],
"radix-ui/@radix-ui/react-checkbox/@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw=="],
"radix-ui/@radix-ui/react-collapsible/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="],
"radix-ui/@radix-ui/react-dialog/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="],
"radix-ui/@radix-ui/react-dropdown-menu/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="],
"radix-ui/@radix-ui/react-menu/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="],
"radix-ui/@radix-ui/react-menubar/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="],
"radix-ui/@radix-ui/react-navigation-menu/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="],
"radix-ui/@radix-ui/react-navigation-menu/@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw=="],
"radix-ui/@radix-ui/react-popover/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="],
"radix-ui/@radix-ui/react-popper/@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.2", "", { "dependencies": { "@radix-ui/rect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw=="],
"radix-ui/@radix-ui/react-popper/@radix-ui/rect": ["@radix-ui/rect@1.1.2", "", {}, "sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA=="],
"radix-ui/@radix-ui/react-radio-group/@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw=="],
"radix-ui/@radix-ui/react-roving-focus/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="],
"radix-ui/@radix-ui/react-scroll-area/@radix-ui/number": ["@radix-ui/number@1.1.2", "", {}, "sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig=="],
"radix-ui/@radix-ui/react-select/@radix-ui/number": ["@radix-ui/number@1.1.2", "", {}, "sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig=="],
"radix-ui/@radix-ui/react-select/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="],
"radix-ui/@radix-ui/react-select/@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw=="],
"radix-ui/@radix-ui/react-slider/@radix-ui/number": ["@radix-ui/number@1.1.2", "", {}, "sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig=="],
@@ -6971,6 +6903,10 @@
"radix-ui/@radix-ui/react-switch/@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw=="],
"radix-ui/@radix-ui/react-tabs/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="],
"radix-ui/@radix-ui/react-tooltip/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="],
"react-remark/remark-parse/mdast-util-from-markdown": ["mdast-util-from-markdown@0.8.5", "", { "dependencies": { "@types/mdast": "^3.0.0", "mdast-util-to-string": "^2.0.0", "micromark": "~2.11.0", "parse-entities": "^2.0.0", "unist-util-stringify-position": "^2.0.0" } }, "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ=="],
"react-remark/remark-rehype/mdast-util-to-hast": ["mdast-util-to-hast@10.2.0", "", { "dependencies": { "@types/mdast": "^3.0.0", "@types/unist": "^2.0.0", "mdast-util-definitions": "^4.0.0", "mdurl": "^1.0.0", "unist-builder": "^2.0.0", "unist-util-generated": "^1.0.0", "unist-util-position": "^3.0.0", "unist-util-visit": "^2.0.0" } }, "sha512-JoPBfJ3gBnHZ18icCwHR50orC9kNH81tiR1gs01D8Q5YpV6adHNO9nKNuFBCJQ941/32PT1a63UF/DitmS3amQ=="],
@@ -7065,10 +7001,30 @@
"test-exclude/minimatch/brace-expansion": ["brace-expansion@5.0.6", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g=="],
"unzipper/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
"unzipper/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
"vaul/@radix-ui/react-dialog/@radix-ui/primitive": ["@radix-ui/primitive@1.1.4", "", {}, "sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ=="],
"vaul/@radix-ui/react-dialog/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="],
"vaul/@radix-ui/react-dialog/@radix-ui/react-context": ["@radix-ui/react-context@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg=="],
"vaul/@radix-ui/react-dialog/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.6", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-escape-keydown": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-2v+zNAWWe0ySxgC0D0yeXMPQ23xZVgXZTerTz+JKlmdRj6gfTqmCcR29jb6d290DezXPGgruHWDX/vYUebtErg=="],
"vaul/@radix-ui/react-dialog/@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q=="],
"vaul/@radix-ui/react-dialog/@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.10", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.6", "@radix-ui/react-use-callback-ref": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fas/lXQqhVvqwAb64s5RFeHiHYElZ6SUQbZaNd6EkfhP/Al7wTIQ9WIR4QVX475tlu5yFCEdDcJH6/UwsZjMWw=="],
"vaul/@radix-ui/react-dialog/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="],
"vaul/@radix-ui/react-dialog/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.12", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.6", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m309havGzsjLHHaIX50G5PlvRs3xkgPCsGk/5PTvYm8D5q33yG0J7w/712PTOhid7NTaFETtnSXjngHQavvhVw=="],
"vaul/@radix-ui/react-dialog/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.6", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ=="],
"vaul/@radix-ui/react-dialog/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.6", "", { "dependencies": { "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wetd0QI77DbvrPpTAvH1SqOxsYF2wZe5TNxqwOd5Ty4XDpV3dpV0s8K/1MGMJBeY5o7lg8ub5VIt1Ub+yVen6g=="],
"vaul/@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA=="],
"webview-ui/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
"webview-ui/@vitejs/plugin-react-swc/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="],
@@ -7171,20 +7127,14 @@
"@opentelemetry/otlp-grpc-exporter-base/@opentelemetry/otlp-transformer/@opentelemetry/sdk-trace-base/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="],
"@radix-ui/react-context-menu/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-accessible-icon/@radix-ui/react-visually-hidden/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA=="],
"@radix-ui/react-portal/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-arrow/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="],
"@radix-ui/react-tabs/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-toggle-group/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-toggle/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@radix-ui/react-toolbar/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="],
"@radix-ui/react-toolbar/@radix-ui/react-roving-focus/@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA=="],
"@radix-ui/react-visually-hidden/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
"@so-ric/colorspace/color/color-convert/color-name": ["color-name@2.1.0", "", {}, "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg=="],
"@so-ric/colorspace/color/color-string/color-name": ["color-name@2.1.0", "", {}, "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg=="],
@@ -7251,10 +7201,6 @@
"claude-dev/@opentelemetry/sdk-trace-node/@opentelemetry/sdk-trace-base/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="],
"cmdk/@radix-ui/react-dialog/@radix-ui/react-dismissable-layer/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw=="],
"cmdk/@radix-ui/react-dialog/@radix-ui/react-focus-scope/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw=="],
"exceljs/archiver/archiver-utils/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
"exceljs/archiver/archiver-utils/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
@@ -7331,6 +7277,10 @@
"test-exclude/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
"vaul/@radix-ui/react-dialog/@radix-ui/react-dismissable-layer/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw=="],
"vaul/@radix-ui/react-dialog/@radix-ui/react-focus-scope/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw=="],
"webview-ui/vitest/@vitest/utils/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="],
"webview-ui/vitest/chai/check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="],
@@ -7359,6 +7309,8 @@
"@microsoft/tui-test/jest-diff/pretty-format/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.10", "", {}, "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA=="],
"@radix-ui/react-accessible-icon/@radix-ui/react-visually-hidden/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="],
"@storybook/react-vite/find-up/locate-path/p-locate/p-limit": ["p-limit@4.0.0", "", { "dependencies": { "yocto-queue": "^1.0.0" } }, "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ=="],
"@vscode/test-cli/glob/jackspeak/@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
@@ -7387,8 +7339,6 @@
"claude-dev/@opentelemetry/exporter-metrics-otlp-http/@opentelemetry/otlp-transformer/@opentelemetry/sdk-trace-base/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="],
"exceljs/archiver/archiver-utils/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
"exceljs/archiver/archiver-utils/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
"exceljs/archiver/zip-stream/archiver-utils/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
Binary file not shown.

After

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

+2 -2
View File
@@ -15,10 +15,10 @@ Cline is an AI coding agent that lives in your editor and your terminal. It can
Choose the model access path that fits your workflow:
<CardGroup cols={3}>
<Card title="Cline Provider" icon="bolt" href="/getting-started/cline-provider">
<Card title="Cline (usage-billing)" icon="bolt" href="/getting-started/cline-provider">
Fastest setup path with one sign-in, built-in billing, and free model options.
</Card>
<Card title="ClinePass (beta)" icon="credit-card" href="/getting-started/clinepass">
<Card title="ClinePass" icon="credit-card" href="/getting-started/clinepass">
Flat $9.99/month subscription for selected open coding models with 2-5x API rate limits.
</Card>
<Card title="Bring Your Own Key" icon="key" href="/getting-started/authorizing-with-cline#byok-cloud--local">
+2 -2
View File
@@ -92,12 +92,12 @@ You pay for AI usage when using cloud providers like Anthropic, OpenAI, OpenRout
| Provider Type | Billing Model |
|--------------|---------------|
| **Cline Provider** | Pay-per-use with credits you purchase |
| **ClinePass (beta)** | Flat monthly subscription for selected open coding models, with usage measured against your ClinePass (beta) quota |
| **ClinePass** | Flat monthly subscription for selected open coding models, with usage measured against your ClinePass quota |
| **Direct API keys** | Billed by your provider (Anthropic, OpenAI, etc.) |
| **OpenRouter/Requesty** | Aggregated billing across multiple models |
| **Local models** | Free (you provide the hardware) |
If you want predictable monthly spend instead of per-request charges, [ClinePass (beta)](/getting-started/clinepass) provides selected open coding models for $9.99/month. If you're using your own API keys, check your provider's pricing page for current rates. Prices change frequently and vary by model.
If you want predictable monthly spend instead of per-request charges, [ClinePass](/getting-started/clinepass) provides selected open coding models for $9.99/month. If you're using your own API keys, check your provider's pricing page for current rates. Prices change frequently and vary by model.
### Free Options
@@ -5,8 +5,8 @@ description: "Authenticate with Cline and choose your first AI model"
Cline connects to AI models through a **provider**. You have three common paths:
- **Cline Provider** (recommended): sign in with Google/GitHub/email, no API key setup.
- **ClinePass (beta)**: a $9.99/month subscription provider with access to selected open coding models and 2-5x API rate limits.
- **Cline (usage-billing)** (recommended): sign in with Google/GitHub/email, no API key setup.
- **ClinePass**: a $9.99/month subscription provider with access to selected open coding models and 2-5x API rate limits.
- **Bring Your Own Key (BYOK)**: use your own provider credentials (cloud or local runtimes).
## Menu
@@ -26,8 +26,8 @@ Cline connects to AI models through a **provider**. You have three common paths:
</Step>
<Step title="Authenticate">
- **Cline Provider:** Click **Sign In** and complete OAuth.
- **ClinePass (beta):** Select **ClinePass (beta)**, click **Sign In**, and subscribe if prompted.
- **Cline (usage-billing):** Click **Sign In** and complete OAuth.
- **ClinePass:** Select **ClinePass**, click **Sign In**, and subscribe if prompted.
- **BYOK cloud provider:** Paste your API key into the **API Key** field.
- **Local runtime (Ollama/LM Studio):** no key needed; ensure runtime is running.
</Step>
@@ -39,7 +39,7 @@ Cline connects to AI models through a **provider**. You have three common paths:
## Provider Options
### Cline Provider
### Cline (usage-billing)
- One sign-in, no key management
- Built-in billing and free model options
@@ -47,13 +47,13 @@ Cline connects to AI models through a **provider**. You have three common paths:
Add credits in Cline settings or at [app.cline.bot/dashboard](https://app.cline.bot/dashboard).
### ClinePass (beta)
### ClinePass
- Flat **$9.99/month** subscription
- 2-5x API rate limits for longer agent sessions
- Curated open coding models including GLM, Kimi, DeepSeek, and MiMo
Subscribe from the [ClinePass (beta) dashboard](https://app.cline.bot/dashboard/subscription), then select **ClinePass (beta)** as your provider in Cline. See the [ClinePass (beta) guide](/getting-started/clinepass) for included models and reference pricing.
Subscribe from the [ClinePass dashboard](https://app.cline.bot/dashboard/subscription?personal=true), then select **ClinePass** as your provider in Cline. See the [ClinePass guide](/getting-started/clinepass) for included models and reference pricing.
### BYOK (cloud + local)
+9 -9
View File
@@ -1,24 +1,24 @@
---
title: "Cline Provider"
description: "Use the Cline Provider for pay-as-you-go model access with built-in authentication and Cline credits."
title: "Cline (usage-billing)"
description: "Use Cline (usage-billing) for pay-as-you-go model access with built-in authentication and Cline credits."
---
The **Cline Provider** is the simplest way to get started with pay-as-you-go model access in Cline.
Cline (usage-billing) is the simplest way to get started with pay-as-you-go model access in Cline.
Instead of managing separate API keys across multiple vendors, you sign in once, add **Cline credits**, and select from available models directly in Cline.
If you want a flat monthly subscription for selected open coding models with higher rate limits, use [ClinePass (beta)](/getting-started/clinepass) instead. ClinePass (beta) is a separate provider in Cline.
If you want a flat monthly subscription for selected open coding models with higher rate limits, use [ClinePass](/getting-started/clinepass) instead. ClinePass is a separate provider in Cline.
## Why use Cline Provider
## Why use Cline (usage-billing)
- **Fastest setup**: no manual API key copy/paste
- **Fastest setup**: no manual API key copy/paste; access to 100+ models supported by Cline
- **One account**: sign in once with Google, GitHub, or email
- **Pay as you go**: add Cline credits and use one balance across supported models
- **Free options**: look for models tagged **FREE** in the selector
## How it works
The Cline Provider is the pay-as-you-go provider in Cline. Add Cline credits, then select **Cline** wherever you configure your provider.
Cline (usage-billing) is the pay-as-you-go provider in Cline. Add Cline credits, then select **Cline** wherever you configure your provider.
- **IDE Extension**: Go to IDE extension settings, set **API Provider** to **Cline**, and sign in.
- **CLI**: Go to `/settings` and select **Cline** as your provider.
@@ -26,13 +26,13 @@ The Cline Provider is the pay-as-you-go provider in Cline. Add Cline credits, th
## Credits and usage
- Add **Cline credits** from your [Cline dashboard](https://app.cline.bot/dashboard)
- Credits are used on a pay-as-you-go basis when you select paid models through the Cline Provider
- Credits are used on a pay-as-you-go basis when you select paid models through Cline (usage-billing)
- View usage in Cline Settings → **View Usage**
- Switch organizations in Cline Settings → **Switch Organization**
## Related
- [Authorization](/getting-started/authorizing-with-cline)
- [ClinePass (beta)](/getting-started/clinepass)
- [ClinePass](/getting-started/clinepass)
- [Local models](/running-models-locally/overview)
- [Provider setup guides](/provider-config/openrouter)
+67 -31
View File
@@ -1,20 +1,32 @@
---
title: "ClinePass (beta)"
title: "ClinePass"
description: "A low-cost monthly subscription that gives you reliable access to popular open coding models with 2-5x API rate limits."
---
<Warning>
ClinePass is currently under invite-only private preview.
</Warning>
ClinePass is a low-cost monthly subscription — **$9.99/month** — that gives you reliable access to popular open coding models with **2-5x the API rate limits** of standard access.
It's completely optional. You don't need ClinePass to use Cline, and you can always use any other provider alongside it.
<Card title="Subscribe to ClinePass" icon="credit-card" href="https://app.cline.bot/dashboard/subscription">
<Card title="Subscribe to ClinePass" icon="credit-card" href="https://app.cline.bot/dashboard/subscription?personal=true">
Get started for $9.99/month and unlock 2-5x API rate limits.
</Card>
## How it works
ClinePass is a separate provider in Cline. After subscribing, select **ClinePass** wherever you configure your provider.
- **IDE Extension**: Go to IDE extension settings, set **API Provider** to **ClinePass**, and sign in.
<Frame>
<img src="/assets/clinepass/clinepass-ide-extension.png" alt="Cline IDE extension settings with ClinePass selected as the API Provider" />
</Frame>
- **CLI**: Go to `/settings` and select **ClinePass** as your provider.
<Frame>
<img src="/assets/clinepass/clinepass-cli.png" alt="Cline CLI settings showing ClinePass selected as the provider" />
</Frame>
## Why ClinePass
Open models have gotten really good. They now reach performance close to proprietary models for coding tasks, and because many providers can serve them competitively, they're usually far cheaper.
@@ -27,15 +39,8 @@ ClinePass solves this by:
- Offering **2-5x the API rate limits** so you can run long, complex agent tasks without interruption
- Providing stable access through Cline's infrastructure
## How it works
ClinePass is a separate provider in Cline. After subscribing, select **ClinePass** wherever you configure your provider.
- **IDE Extension**: Go to IDE extension settings, set **API Provider** to **ClinePass**, and sign in.
- **CLI**: Go to `/settings` and select **ClinePass** as your provider.
<Note>
ClinePass is a separate provider from the Cline Provider. You can use both independently — subscribe to ClinePass for 2-5x API rate limits, or use the Cline Provider for pay-as-you-go access.
ClinePass is a separate provider from Cline (usage-billing). You can use both independently — subscribe to ClinePass for 2-5x API rate limits, or use Cline (usage-billing) for pay-as-you-go access.
</Note>
## Models
@@ -43,16 +48,43 @@ ClinePass is a separate provider from the Cline Provider. You can use both indep
ClinePass includes the following models, tested and benchmarked for coding agent use:
| Model | Model ID |
|-------|----------|
| GLM-5.2 | `glm-5.2` |
| Kimi K2.7 Code | `kimi-k2.7-code` |
| Kimi K2.6 | `kimi-k2.6` |
| DeepSeek V4 Pro | `deepseek-v4-pro` |
| DeepSeek V4 Flash | `deepseek-v4-flash` |
| MiMo-V2.5 | `mimo-v2.5` |
| MiMo-V2.5-Pro | `mimo-v2.5-pro` |
|-------|------------|
| GLM-5.2 | `cline-pass/glm-5.2` |
| Kimi K2.7 Code | `cline-pass/kimi-k2.7-code` |
| Kimi K2.6 | `cline-pass/kimi-k2.6` |
| DeepSeek V4 Pro | `cline-pass/deepseek-v4-pro` |
| DeepSeek V4 Flash | `cline-pass/deepseek-v4-flash` |
| MiMo-V2.5 | `cline-pass/mimo-v2.5` |
| MiMo-V2.5-Pro | `cline-pass/mimo-v2.5-pro` |
| MiniMax M3 | `cline-pass/minimax-m3` |
| Qwen3.7 Max | `cline-pass/qwen3.7-max` |
| Qwen3.7 Plus | `cline-pass/qwen3.7-plus` |
### Reference pricing
## Using ClinePass outside of Cline
You can use ClinePass models from your own scripts, apps, or automation through the Cline API. The API uses the same OpenAI-compatible Chat Completions format as the rest of Cline's API.
To get started, create an API key from **Settings > API Keys** in [app.cline.bot](https://app.cline.bot). For the complete walkthrough, see the [Cline API Getting Started guide](/api/getting-started).
Use the full ClinePass model slug in the `model` field:
```bash
export CLINE_API_KEY="your_api_key_here"
curl -X POST https://api.cline.bot/api/v1/chat/completions \
-H "Authorization: Bearer $CLINE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "cline-pass/qwen3.7-max",
"messages": [
{"role": "user", "content": "Write a TypeScript function that validates an email address."}
],
"stream": false
}'
```
## Reference pricing
ClinePass is a flat monthly subscription, so you are not charged the individual API prices below. These reference prices show the underlying per-1M-token rates for each model and can help you understand how usage is measured against your ClinePass quota (2-5x quota compares to paying standard api rate).
@@ -65,19 +97,23 @@ ClinePass is a flat monthly subscription, so you are not charged the individual
| DeepSeek V4 Flash | $0.14 | $0.28 | $0.0028 | - |
| MiMo-V2.5 | $0.14 | $0.28 | $0.0028 | - |
| MiMo-V2.5-Pro | $1.74 | $3.48 | $0.0145 | - |
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - |
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 |
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 |
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 |
## Goals
## Usage
We created ClinePass to:
ClinePass measures usage against three limits:
- Make AI coding accessible to more people with a low-cost subscription
- Provide reliable access to the best open coding models
- Curate models that are tested and benchmarked for coding agent use
- Remove rate-limit friction with **2-5x API rate limits**
- Have no lock-in — you can use any other provider with Cline as well
- **5-hour rolling window** — your usage within rolling 5-hour period
- **Weekly** — your usage over the calendar week
- **Monthly** — your usage over the calendar month
To check your current usage, visit your [Cline dashboard](https://app.cline.bot/dashboard/subscription?personal=true).
## Related
- [Cline Provider](/getting-started/cline-provider)
- [Cline (usage-billing)](/getting-started/cline-provider)
- [Authorization](/getting-started/authorizing-with-cline)
- [Local models](/running-models-locally/overview)
+2 -2
View File
@@ -30,7 +30,7 @@ Use this if you want Cline inside your editor UI.
Use the Cline activity bar icon, or run `Cline: Open In New Tab` from Command Palette.
</Step>
<Step title="Authorize with Cline">
After installing the extension, complete provider setup in Cline settings. Use the Cline Provider for pay-as-you-go access, ClinePass (beta) for a flat monthly subscription, or bring your own provider key.
After installing the extension, complete provider setup in Cline settings. Use the Cline Provider for pay-as-you-go access, ClinePass for a flat monthly subscription, or bring your own provider key.
[Authorize with Cline](/getting-started/authorizing-with-cline)
</Step>
@@ -53,7 +53,7 @@ Use this if you want Cline inside your editor UI.
**View** → **Tool Windows** → **Cline**.
</Step>
<Step title="Authorize with Cline">
After installing the extension, complete provider setup in Cline settings. Use the Cline Provider for pay-as-you-go access, ClinePass (beta) for a flat monthly subscription, or bring your own provider key.
After installing the extension, complete provider setup in Cline settings. Use the Cline Provider for pay-as-you-go access, ClinePass for a flat monthly subscription, or bring your own provider key.
[Authorize with Cline](/getting-started/authorizing-with-cline)
</Step>
+1 -1
View File
@@ -7,7 +7,7 @@ description: "Use Cline CLI for interactive terminal sessions and automated head
## Prerequisites
- Cline CLI installed (install via `npm i -g cline`)
- Provider authenticated (`cline auth`) — use the Cline Provider, ClinePass (beta), or your own provider key ([Authorization Guide](/getting-started/authorizing-with-cline#cli-setup))
- Provider authenticated (`cline auth`) — use the Cline Provider, ClinePass, or your own provider key ([Authorization Guide](/getting-started/authorizing-with-cline#cli-setup))
## Quick Start
+8
View File
@@ -1,5 +1,13 @@
# Cline SDK Changelog
## 0.0.54
- Improve basic compaction token budgeting so context compaction is more accurate
- Preserve error detail and fetch error cause information so failures surface clearer messages
- Preserve failed run error messages instead of dropping them
- Derive model info in the provider/model runtime path for more reliable provider/model handling
- Add ClinePass subscription support to the account service
## 0.0.53
- Show when request cost is covered by the user's Cline subscription
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@cline/agents",
"version": "0.0.53",
"version": "0.0.54",
"repository": {
"type": "git",
"url": "https://github.com/cline/cline",
@@ -234,4 +234,56 @@ describe("AgentRuntime (provider-form config + Agent alias)", () => {
await agent.run("again");
expect(received).toHaveLength(countAfterRun);
});
it("derives messageModelInfo from providerId/modelId so assistant messages carry model tags", async () => {
const model = new ScriptedModel([
() => [
{ type: "text-delta", text: "hi" },
{ type: "finish", reason: "stop" },
],
]);
createAgentModel.mockReturnValue(model);
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
});
const result = await agent.run("hello");
const assistant = result.messages.find((m) => m.role === "assistant");
expect(assistant?.modelInfo).toEqual({
id: "claude-sonnet-4-6",
provider: "anthropic",
});
});
it("prefers an explicit messageModelInfo over the derived provider/model", async () => {
const model = new ScriptedModel([
() => [
{ type: "text-delta", text: "hi" },
{ type: "finish", reason: "stop" },
],
]);
createAgentModel.mockReturnValue(model);
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
messageModelInfo: {
id: "explicit-model",
provider: "explicit-provider",
family: "explicit-family",
},
});
const result = await agent.run("hello");
const assistant = result.messages.find((m) => m.role === "assistant");
expect(assistant?.modelInfo).toEqual({
id: "explicit-model",
provider: "explicit-provider",
family: "explicit-family",
});
});
});
+8 -1
View File
@@ -105,7 +105,14 @@ function resolveRuntimeConfig(
telemetry: rest.telemetry,
});
const model = gateway.createAgentModel({ providerId, modelId });
return { ...rest, model };
// The prebuilt-model path preserves a caller-provided messageModelInfo;
// mirror that here so the provider/model constructor also tags assistant
// messages with modelInfo. An explicit caller-provided value still wins.
const messageModelInfo = rest.messageModelInfo ?? {
id: modelId,
provider: providerId,
};
return { ...rest, model, messageModelInfo };
}
function resolveToolPolicy(
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@cline/core",
"description": "Cline Core SDK for Node Runtime",
"version": "0.0.53",
"version": "0.0.54",
"repository": {
"type": "git",
"url": "https://github.com/cline/cline",
@@ -153,6 +153,40 @@ describe("ClineAccountService", () => {
expect(plans).toEqual(plansPayload);
});
it("fetches the current user's subscription plan", async () => {
const planPayload = {
plan: {
id: "plan-1",
displayName: "ClinePass Monthly",
interval: "Monthly",
},
planHistoryId: "history-1",
subscriptionId: "sub-1",
userId: "user-1",
};
const fetchImpl = vi.fn(async (input: unknown, init?: RequestInit) => {
expect(String(input)).toBe(
"https://api.cline.bot/api/v1/users/me/plan",
);
expect(init?.headers).toMatchObject({
Authorization: "Bearer workos:token-123",
});
return new Response(
JSON.stringify({ success: true, data: planPayload }),
{ status: 200, headers: { "Content-Type": "application/json" } },
);
});
const service = new ClineAccountService({
apiBaseUrl: "https://api.cline.bot",
getAuthToken: async () => "workos:token-123",
fetchImpl: fetchImpl as unknown as typeof fetch,
});
const plan = await service.fetchCurrentUserPlan();
expect(plan).toEqual(planPayload);
});
it("fetches remote config with fallback org selected", async () => {
const remoteConfigPayload = {
organizationId: "org-fallback",
@@ -9,6 +9,7 @@ import type {
ClineOrganization,
ClineSubscriptionPlan,
FeaturebaseTokenResponse,
UserCurrentPlan,
UserRemoteConfigResponse,
} from "./types";
@@ -156,6 +157,10 @@ export class ClineAccountService {
);
}
public async fetchCurrentUserPlan(): Promise<UserCurrentPlan | undefined> {
return this.request<UserCurrentPlan | undefined>("/api/v1/users/me/plan");
}
public async fetchOrganization(
organizationId: string,
): Promise<ClineOrganization> {
@@ -334,9 +339,7 @@ export class ClineAccountService {
if (!envelope.success) {
throw new Error(envelope.error || "Cline account request failed");
}
if (envelope.data !== undefined) {
return envelope.data;
}
return envelope.data as T;
}
}
+1
View File
@@ -20,6 +20,7 @@ export type {
ClineOrganization,
ClineSubscriptionPlan,
FeaturebaseTokenResponse,
UserCurrentPlan,
UserRemoteConfigOrganization,
UserRemoteConfigResponse,
} from "./types";
+12
View File
@@ -97,6 +97,18 @@ export interface ClineSubscriptionPlan {
[key: string]: unknown;
}
export interface UserCurrentPlan {
cancelAt?: string;
canceledAt?: string;
currentPeriodEnd?: string;
currentPeriodStart?: string;
plan?: ClineSubscriptionPlan | null;
planHistoryId?: string;
subscriptionId?: string;
userId?: string;
[key: string]: unknown;
}
export interface ClineAccountOrganizationUsageTransaction {
aiInferenceProviderName: string;
aiModelName: string;
@@ -1,9 +1,14 @@
import type { BasicLogger, MessageWithMetadata } from "@cline/shared";
import {
type BasicLogger,
CHARS_PER_TOKEN,
type MessageWithMetadata,
} from "@cline/shared";
import type {
CoreCompactionContext,
CoreCompactionResult,
} from "../../types/config";
import {
DEFAULT_TARGET_RATIO,
type EstimateMessageTokens,
findFirstUserMessageIndex,
findLastAssistantIndex,
@@ -11,7 +16,6 @@ import {
isCompactionSummaryMessage,
isTurnStartMessage,
MIN_TRUNCATED_MESSAGE_TOKENS,
truncateText,
truncateToolResultContentForCompaction,
} from "./compaction-shared";
@@ -19,11 +23,17 @@ interface BasicCompactionCandidate {
index: number;
message: MessageWithMetadata;
estimatedTokens: number;
toolPairIds: string[];
isFirstUser: boolean;
isLastUser: boolean;
isLastAssistant: boolean;
}
interface BasicCompactionCandidateSet {
candidates: BasicCompactionCandidate[];
totalTokens: number;
}
function sanitizeMessageForBasic(
message: MessageWithMetadata,
): MessageWithMetadata | undefined {
@@ -67,15 +77,24 @@ function getTotalTokens(
);
}
function truncateMessageText(text: string, limit: number): string {
if (text.length <= limit) {
return text;
}
const suffix = "\n...";
const sliceLength = Math.max(1, limit - suffix.length);
return `${text.slice(0, sliceLength)}${suffix}`;
}
function truncateMessageToTokens(
message: MessageWithMetadata,
maxTokens: number,
): MessageWithMetadata {
const safeMaxTokens = Math.max(1, maxTokens);
const targetChars = Math.max(16, safeMaxTokens * 4);
const safeMaxTokens = Number.isFinite(maxTokens) ? Math.max(1, maxTokens) : 1;
const targetChars = Math.max(16, safeMaxTokens * CHARS_PER_TOKEN);
if (typeof message.content === "string") {
const truncated = truncateText(message.content, targetChars).trim();
const truncated = truncateMessageText(message.content, targetChars).trim();
return { ...message, content: truncated || "..." };
}
@@ -85,7 +104,7 @@ function truncateMessageToTokens(
if (block.type !== "text" || remaining <= 0) {
return block;
}
const truncated = truncateText(block.text, remaining).trim();
const truncated = truncateMessageText(block.text, remaining).trim();
remaining -= truncated.length;
return { ...block, text: truncated || "..." };
});
@@ -95,26 +114,30 @@ function truncateMessageToTokens(
function buildBasicCandidates(
messages: MessageWithMetadata[],
estimateMessageTokens: EstimateMessageTokens,
): BasicCompactionCandidate[] {
): BasicCompactionCandidateSet {
const firstUserIndex = findFirstUserMessageIndex(messages);
const lastUserIndex = findLastTurnStartIndex(messages);
const lastAssistantIndex = findLastAssistantIndex(messages);
const candidates: BasicCompactionCandidate[] = [];
let totalTokens = 0;
for (let index = 0; index < messages.length; index += 1) {
const sanitized = sanitizeMessageForBasic(messages[index]);
if (!sanitized) {
continue;
}
const estimatedTokens = estimateMessageTokens(sanitized);
totalTokens += estimatedTokens;
candidates.push({
index,
message: sanitized,
estimatedTokens: estimateMessageTokens(sanitized),
estimatedTokens,
toolPairIds: collectToolPairIds(sanitized),
isFirstUser: index === firstUserIndex,
isLastUser: index === lastUserIndex,
isLastAssistant: index === lastAssistantIndex,
});
}
return candidates;
return { candidates, totalTokens };
}
function updateCandidate(
@@ -122,98 +145,102 @@ function updateCandidate(
index: number,
message: MessageWithMetadata,
estimateMessageTokens: EstimateMessageTokens,
): void {
): number {
const candidate = candidates[index];
const previousTokens = candidate.estimatedTokens;
candidate.message = message;
candidate.estimatedTokens = estimateMessageTokens(message);
candidate.toolPairIds = collectToolPairIds(message);
return candidate.estimatedTokens - previousTokens;
}
function collectToolUseIds(message: MessageWithMetadata): Set<string> {
const ids = new Set<string>();
function collectToolPairIds(message: MessageWithMetadata): string[] {
if (!Array.isArray(message.content)) {
return ids;
return [];
}
const ids: string[] = [];
for (const block of message.content) {
if (block.type === "tool_use") {
ids.add(block.id);
ids.push(block.id);
} else if (block.type === "tool_result") {
ids.push(block.tool_use_id);
}
}
return ids;
}
function collectToolResultIds(message: MessageWithMetadata): Set<string> {
const ids = new Set<string>();
if (!Array.isArray(message.content)) {
return ids;
}
for (const block of message.content) {
if (block.type === "tool_result") {
ids.add(block.tool_use_id);
}
}
return ids;
}
function collectToolPairIds(candidate: BasicCompactionCandidate): Set<string> {
return new Set([
...collectToolUseIds(candidate.message),
...collectToolResultIds(candidate.message),
]);
}
function buildToolPairCandidateIndex(
candidates: BasicCompactionCandidate[],
): Map<string, Set<number>> {
const indexByToolUseId = new Map<string, Set<number>>();
for (let index = 0; index < candidates.length; index += 1) {
for (const id of collectToolPairIds(candidates[index])) {
): Map<string, Set<BasicCompactionCandidate>> {
const indexByToolUseId = new Map<string, Set<BasicCompactionCandidate>>();
for (const candidate of candidates) {
for (const id of candidate.toolPairIds) {
const existing = indexByToolUseId.get(id);
if (existing) {
existing.add(index);
existing.add(candidate);
} else {
indexByToolUseId.set(id, new Set([index]));
indexByToolUseId.set(id, new Set([candidate]));
}
}
}
return indexByToolUseId;
}
function collectAtomicRemovalIndexes(
candidates: BasicCompactionCandidate[],
startIndex: number,
): Set<number> {
const pairIndex = buildToolPairCandidateIndex(candidates);
const removalIndexes = new Set<number>();
const queue = [startIndex];
function collectAtomicRemovalCandidates(
activeCandidates: Set<BasicCompactionCandidate>,
pairIndex: Map<string, Set<BasicCompactionCandidate>>,
startCandidate: BasicCompactionCandidate,
): Set<BasicCompactionCandidate> {
const removalCandidates = new Set<BasicCompactionCandidate>();
const queue = [startCandidate];
let queueIndex = 0;
while (queue.length > 0) {
const index = queue.shift();
if (index === undefined || removalIndexes.has(index)) {
while (queueIndex < queue.length) {
const candidate = queue[queueIndex];
queueIndex += 1;
if (
candidate === undefined ||
!activeCandidates.has(candidate) ||
removalCandidates.has(candidate)
) {
continue;
}
removalIndexes.add(index);
for (const id of collectToolPairIds(candidates[index])) {
for (const linkedIndex of pairIndex.get(id) ?? []) {
if (!removalIndexes.has(linkedIndex)) {
queue.push(linkedIndex);
removalCandidates.add(candidate);
for (const id of candidate.toolPairIds) {
for (const linkedCandidate of pairIndex.get(id) ?? []) {
if (!removalCandidates.has(linkedCandidate)) {
queue.push(linkedCandidate);
}
}
}
}
return removalIndexes;
return removalCandidates;
}
function removeCandidateSetInPlace(
candidates: BasicCompactionCandidate[],
removalCandidates: Set<BasicCompactionCandidate>,
): void {
let writeIndex = 0;
for (const candidate of candidates) {
if (removalCandidates.has(candidate)) {
continue;
}
candidates[writeIndex] = candidate;
writeIndex += 1;
}
candidates.length = writeIndex;
}
function removeCandidatesByPredicate(
candidates: BasicCompactionCandidate[],
predicate: (candidate: BasicCompactionCandidate) => boolean,
targetTokens: number,
estimateMessageTokens: EstimateMessageTokens,
): void {
let totalTokens = getTotalTokens(
candidates.map((candidate) => candidate.message),
estimateMessageTokens,
);
totalTokens: number,
): number {
const activeCandidates = new Set(candidates);
const pairIndex = buildToolPairCandidateIndex(candidates);
for (
let index = 0;
index < candidates.length && totalTokens > targetTokens;
@@ -222,30 +249,34 @@ function removeCandidatesByPredicate(
index += 1;
continue;
}
const removalIndexes = collectAtomicRemovalIndexes(candidates, index);
totalTokens -= Array.from(removalIndexes).reduce(
(total, removalIndex) => total + candidates[removalIndex].estimatedTokens,
0,
const removalCandidates = collectAtomicRemovalCandidates(
activeCandidates,
pairIndex,
candidates[index],
);
for (const removalIndex of Array.from(removalIndexes).sort(
(left, right) => right - left,
)) {
candidates.splice(removalIndex, 1);
if (removalCandidates.size === 0) {
index += 1;
continue;
}
let removedTokens = 0;
for (const candidate of removalCandidates) {
removedTokens += candidate.estimatedTokens;
activeCandidates.delete(candidate);
}
totalTokens -= removedTokens;
removeCandidateSetInPlace(candidates, removalCandidates);
}
return totalTokens;
}
function trimCandidatesToBudget(
candidates: BasicCompactionCandidate[],
targetTokens: number,
totalTokens: number,
estimateMessageTokens: EstimateMessageTokens,
): void {
let totalTokens = getTotalTokens(
candidates.map((candidate) => candidate.message),
estimateMessageTokens,
);
): number {
if (totalTokens <= targetTokens) {
return;
return totalTokens;
}
for (
@@ -264,40 +295,44 @@ function trimCandidatesToBudget(
if (desiredTokens >= candidate.estimatedTokens) {
continue;
}
updateCandidate(
totalTokens += updateCandidate(
candidates,
index,
truncateMessageToTokens(candidate.message, desiredTokens),
estimateMessageTokens,
);
totalTokens = getTotalTokens(
candidates.map((item) => item.message),
estimateMessageTokens,
);
}
if (totalTokens <= targetTokens) {
return;
return totalTokens;
}
const firstUserIndex = candidates.findIndex(
(candidate) => candidate.isFirstUser,
);
if (firstUserIndex >= 0) {
const desiredTokens = Math.max(
1,
candidates[firstUserIndex].estimatedTokens - (totalTokens - targetTokens),
);
updateCandidate(
candidates,
firstUserIndex,
truncateMessageToTokens(
candidates[firstUserIndex].message,
desiredTokens,
),
estimateMessageTokens,
);
while (totalTokens > targetTokens) {
const candidate = candidates[firstUserIndex];
const desiredTokens = Math.max(
1,
candidate.estimatedTokens - (totalTokens - targetTokens),
);
if (desiredTokens >= candidate.estimatedTokens) {
break;
}
const previousTokens = candidate.estimatedTokens;
totalTokens += updateCandidate(
candidates,
firstUserIndex,
truncateMessageToTokens(candidate.message, desiredTokens),
estimateMessageTokens,
);
if (candidate.estimatedTokens >= previousTokens) {
break;
}
}
}
return totalTokens;
}
function haveMessagesChanged(
@@ -329,9 +364,13 @@ export function runBasicCompaction(options: {
estimateMessageTokens: EstimateMessageTokens;
logger?: BasicLogger;
}): CoreCompactionResult | undefined {
const targetTokens = Math.max(
const totalTargetTokens = Math.max(
1,
Math.min(options.context.triggerTokens, options.context.maxInputTokens),
Math.min(
options.context.targetTokens ??
Math.floor(options.context.triggerTokens * DEFAULT_TARGET_RATIO),
options.context.maxInputTokens,
),
);
const { compactable, protectedTail } = splitLatestTurn(
options.context.messages,
@@ -339,50 +378,59 @@ export function runBasicCompaction(options: {
if (compactable.length === 0) {
return undefined;
}
const candidates = buildBasicCandidates(
const protectedTailTokens = getTotalTokens(
protectedTail,
options.estimateMessageTokens,
);
const targetTokens = Math.max(1, totalTargetTokens - protectedTailTokens);
const candidateSet = buildBasicCandidates(
compactable,
options.estimateMessageTokens,
);
const { candidates } = candidateSet;
if (candidates.length === 0) {
return undefined;
}
const beforeCompactableTokens = candidateSet.totalTokens;
let totalTokens = beforeCompactableTokens;
removeCandidatesByPredicate(
totalTokens = removeCandidatesByPredicate(
candidates,
(candidate) =>
candidate.message.role === "assistant" && !candidate.isLastAssistant,
targetTokens,
options.estimateMessageTokens,
totalTokens,
);
removeCandidatesByPredicate(
totalTokens = removeCandidatesByPredicate(
candidates,
(candidate) =>
candidate.message.role === "user" &&
!candidate.isFirstUser &&
!candidate.isLastUser,
targetTokens,
options.estimateMessageTokens,
totalTokens,
);
removeCandidatesByPredicate(
totalTokens = removeCandidatesByPredicate(
candidates,
(candidate) =>
candidate.message.role === "assistant" && candidate.isLastAssistant,
targetTokens,
options.estimateMessageTokens,
totalTokens,
);
removeCandidatesByPredicate(
totalTokens = removeCandidatesByPredicate(
candidates,
(candidate) =>
candidate.message.role === "user" &&
candidate.isLastUser &&
!candidate.isFirstUser,
targetTokens,
options.estimateMessageTokens,
totalTokens,
);
trimCandidatesToBudget(
totalTokens = trimCandidatesToBudget(
candidates,
targetTokens,
totalTokens,
options.estimateMessageTokens,
);
@@ -394,24 +442,15 @@ export function runBasicCompaction(options: {
return undefined;
}
const beforeTokens = getTotalTokens(
[
...compactable.map((m) => sanitizeMessageForBasic(m) ?? m),
...protectedTail,
],
options.estimateMessageTokens,
);
const afterTokens = getTotalTokens(
nextMessages,
options.estimateMessageTokens,
);
const beforeTokens = beforeCompactableTokens + protectedTailTokens;
const afterTokens = totalTokens + protectedTailTokens;
options.logger?.debug("Performed basic compaction", {
messagesBefore: options.context.messages.length,
messagesAfter: nextMessages.length,
messagesRemoved: options.context.messages.length - nextMessages.length,
tokensBefore: beforeTokens,
tokensAfter: afterTokens,
targetTokens,
targetTokens: totalTargetTokens,
maxInputTokens: options.context.maxInputTokens,
});
@@ -1,7 +1,11 @@
import type { ToolResultContent } from "@cline/llms";
import { estimateTokens, type MessageWithMetadata } from "@cline/shared";
import {
CHARS_PER_TOKEN,
estimateTokens,
type MessageWithMetadata,
} from "@cline/shared";
export { estimateTokens };
export { CHARS_PER_TOKEN, estimateTokens };
import type {
CoreCompactionContext,
@@ -11,6 +15,7 @@ import type { ProviderConfig } from "../../types/provider-settings";
export const DEFAULT_MAX_INPUT_TOKENS = 200_000;
export const DEFAULT_THRESHOLD_RATIO = 0.9;
export const DEFAULT_TARGET_RATIO = 0.7;
export const DEFAULT_RESERVE_TOKENS = 16_384;
export const DEFAULT_PRESERVE_RECENT_TOKENS = 20_000;
export const DEFAULT_SUMMARY_MAX_OUTPUT_TOKENS = 1_024;
@@ -1,4 +1,5 @@
import type * as LlmsProviders from "@cline/llms";
import type { MessageWithMetadata } from "@cline/shared";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { CoreCompactionContext } from "../../types/config";
import { runBasicCompaction } from "./basic-compaction";
@@ -34,6 +35,39 @@ function totalJsonTokens(messages: LlmsProviders.Message[]): number {
);
}
describe("createTokenEstimator", () => {
it("does not treat assistant request metrics as per-message token counts", () => {
const estimateMessageTokens = createTokenEstimator();
const message: MessageWithMetadata = {
role: "assistant",
content: "short",
metrics: {
inputTokens: 12,
outputTokens: 7,
},
};
expect(estimateMessageTokens(message)).toBe(
Math.ceil(JSON.stringify(message).length / 3),
);
});
it("falls back to serialized character estimation when metrics are incomplete", () => {
const estimateMessageTokens = createTokenEstimator();
const message: MessageWithMetadata = {
role: "assistant",
content: "short",
metrics: {
inputTokens: 12,
},
};
expect(estimateMessageTokens(message)).toBe(
Math.ceil(JSON.stringify(message).length / 3),
);
});
});
function runForcedBasicCompaction(
messages: LlmsProviders.Message[],
targetTokens: number,
@@ -1076,6 +1110,254 @@ describe("createContextCompactionPrepareTurn", () => {
]);
});
it("does not subtract model max output tokens from an explicit input budget", async () => {
const compact = vi.fn((_context: CoreCompactionContext) => ({
messages: [
{ role: "user" as const, content: "Compacted by input budget" },
],
}));
const prepareTurn = createContextCompactionPrepareTurn({
providerId: "openai-codex",
modelId: "gpt-5.4-mini",
providerConfig: {
providerId: "openai-codex",
modelId: "gpt-5.4-mini",
} as LlmsProviders.ProviderConfig,
compaction: { enabled: true, maxInputTokens: 200_000, compact },
logger: undefined,
});
const messages: MessageWithMetadata[] = [
{
role: "user",
content: "large prompt ".repeat(20_000),
},
];
const result = await prepareTurn?.({
agentId: "agent-1",
conversationId: "conv-1",
parentAgentId: null,
iteration: 1,
abortSignal: new AbortController().signal,
systemPrompt: "You are helpful.",
tools: [],
messages,
apiMessages: messages,
model: {
id: "gpt-5.4-mini",
provider: "openai-codex",
info: {
id: "gpt-5.4-mini",
maxInputTokens: 200_000,
maxTokens: 128_000,
},
},
});
expect(compact).not.toHaveBeenCalled();
expect(result).toBeUndefined();
});
it("targets basic compaction below the model output-reserved input budget", async () => {
const compact = vi.fn((_context: CoreCompactionContext) => ({
messages: [
{ role: "user" as const, content: "Compacted by target budget" },
],
}));
const prepareTurn = createContextCompactionPrepareTurn({
providerId: "openai-codex",
modelId: "gpt-5.5",
providerConfig: {
providerId: "openai-codex",
modelId: "gpt-5.5",
} as LlmsProviders.ProviderConfig,
compaction: { enabled: true, strategy: "basic", compact },
logger: undefined,
});
const messages: MessageWithMetadata[] = [
{
role: "user",
content: "large prompt ".repeat(70_000),
},
];
await prepareTurn?.({
agentId: "agent-1",
conversationId: "conv-1",
parentAgentId: null,
iteration: 1,
abortSignal: new AbortController().signal,
systemPrompt: "You are helpful.",
tools: [],
messages,
apiMessages: messages,
model: {
id: "gpt-5.5",
provider: "openai-codex",
info: {
id: "gpt-5.5",
maxInputTokens: 272_000,
maxTokens: 128_000,
},
},
});
expect(compact).toHaveBeenCalledTimes(1);
const context = compact.mock.calls[0]?.[0];
expect(context?.triggerTokens).toBe(244_800);
expect(context?.targetTokens).toBe(100_800);
});
it("derives input budget by reserving model max output tokens from context window", async () => {
const compact = vi.fn((_context: CoreCompactionContext) => ({
messages: [
{ role: "user" as const, content: "Compacted by derived input budget" },
],
}));
const prepareTurn = createContextCompactionPrepareTurn({
providerId: "openai-codex",
modelId: "gpt-5.5",
providerConfig: {
providerId: "openai-codex",
modelId: "gpt-5.5",
} as LlmsProviders.ProviderConfig,
compaction: { enabled: true, compact },
logger: undefined,
});
const messages: MessageWithMetadata[] = [
{
role: "user",
content: "large prompt ".repeat(60_000),
},
];
const result = await prepareTurn?.({
agentId: "agent-1",
conversationId: "conv-1",
parentAgentId: null,
iteration: 1,
abortSignal: new AbortController().signal,
systemPrompt: "You are helpful.",
tools: [],
messages,
apiMessages: messages,
model: {
id: "gpt-5.5",
provider: "openai-codex",
info: {
id: "gpt-5.5",
contextWindow: 400_000,
maxTokens: 128_000,
},
},
});
expect(compact).toHaveBeenCalledTimes(1);
const context = compact.mock.calls[0]?.[0];
expect(context?.maxInputTokens).toBe(272_000);
expect(context?.triggerTokens).toBe(244_800);
expect(context?.thresholdRatio).toBe(0.9);
expect(result?.messages).toEqual([
{ role: "user", content: "Compacted by derived input budget" },
]);
});
it("uses the lower split input budget when it is below context-derived input budget", async () => {
const compact = vi.fn((_context: CoreCompactionContext) => ({
messages: [
{ role: "user" as const, content: "Compacted by split input" },
],
}));
const prepareTurn = createContextCompactionPrepareTurn({
providerId: "openai-codex",
modelId: "gpt-5.5",
providerConfig: {
providerId: "openai-codex",
modelId: "gpt-5.5",
} as LlmsProviders.ProviderConfig,
compaction: { enabled: true, compact },
logger: undefined,
});
const messages: MessageWithMetadata[] = [
{
role: "user",
content: "large prompt ".repeat(60_000),
},
];
const result = await prepareTurn?.({
agentId: "agent-1",
conversationId: "conv-1",
parentAgentId: null,
iteration: 1,
abortSignal: new AbortController().signal,
systemPrompt: "You are helpful.",
tools: [],
messages,
apiMessages: messages,
model: {
id: "gpt-5.5",
provider: "openai-codex",
info: {
id: "gpt-5.5",
contextWindow: 400_000,
maxInputTokens: 200_000,
maxTokens: 128_000,
},
},
});
expect(compact).toHaveBeenCalledTimes(1);
const context = compact.mock.calls[0]?.[0];
expect(context?.maxInputTokens).toBe(200_000);
expect(context?.triggerTokens).toBe(180_000);
expect(context?.thresholdRatio).toBe(0.9);
expect(result?.messages).toEqual([
{ role: "user", content: "Compacted by split input" },
]);
});
it("falls back to the default trigger when model max output cannot derive a lower input budget", async () => {
const compact = vi.fn((_context: CoreCompactionContext) => ({
messages: [{ role: "user" as const, content: "Compacted by fallback" }],
}));
const prepareTurn = createContextCompactionPrepareTurn({
providerId: "openai-codex",
modelId: "large-output-model",
providerConfig: {
providerId: "openai-codex",
modelId: "large-output-model",
} as LlmsProviders.ProviderConfig,
compaction: { enabled: true, maxInputTokens: 200_000, compact },
logger: undefined,
});
const result = await prepareTurn?.({
agentId: "agent-1",
conversationId: "conv-1",
parentAgentId: null,
iteration: 1,
abortSignal: new AbortController().signal,
systemPrompt: "You are helpful.",
tools: [],
messages: [{ role: "user", content: "small prompt" }],
apiMessages: [{ role: "user", content: "small prompt" }],
model: {
id: "large-output-model",
provider: "openai-codex",
info: {
id: "large-output-model",
maxInputTokens: 200_000,
contextWindow: 200_000,
maxTokens: 200_000,
},
},
});
expect(compact).not.toHaveBeenCalled();
expect(result).toBeUndefined();
});
it("triggers compaction from provider-sized tool result payloads", async () => {
const compact = vi.fn((_context: CoreCompactionContext) => ({
messages: [
@@ -1726,4 +2008,68 @@ describe("createContextCompactionPrepareTurn", () => {
"manual",
);
});
it("does not immediately re-trigger basic compaction on the next turn after accounting for the protected tail", async () => {
const prepareTurn = createContextCompactionPrepareTurn({
providerId: "openai-codex",
modelId: "mock-model",
providerConfig: {
providerId: "openai-codex",
modelId: "mock-model",
} as LlmsProviders.ProviderConfig,
compaction: { enabled: true, strategy: "basic", thresholdRatio: 0.5 },
logger: undefined,
});
const estimateMessageTokens = createTokenEstimator();
const model = {
id: "mock-model",
provider: "openai-codex",
info: { id: "mock-model", maxInputTokens: 300 },
};
const messages: LlmsProviders.Message[] = [
{ role: "user", content: "old user context ".repeat(80) },
{ role: "assistant", content: "old assistant context ".repeat(80) },
{ role: "user", content: "current request" },
];
const triggerTokens = 150;
const firstResult = await prepareTurn?.({
agentId: "agent-1",
conversationId: "conv-1",
parentAgentId: null,
iteration: 1,
abortSignal: new AbortController().signal,
systemPrompt: "You are helpful.",
tools: [],
messages,
apiMessages: messages,
model,
});
expect(firstResult?.messages).toBeDefined();
const firstAfterTokens = firstResult?.messages.reduce(
(total, message) => total + estimateMessageTokens(message),
0,
);
expect(firstAfterTokens).toBeLessThanOrEqual(triggerTokens);
const nextTurnMessages: LlmsProviders.Message[] = [
...(firstResult?.messages ?? []),
{ role: "assistant", content: "short answer" },
{ role: "user", content: "next request" },
];
const secondResult = await prepareTurn?.({
agentId: "agent-1",
conversationId: "conv-1",
parentAgentId: null,
iteration: 2,
abortSignal: new AbortController().signal,
systemPrompt: "You are helpful.",
tools: [],
messages: nextTurnMessages,
apiMessages: nextTurnMessages,
model,
});
expect(secondResult).toBeUndefined();
});
});
@@ -18,6 +18,7 @@ import {
DEFAULT_MAX_INPUT_TOKENS,
DEFAULT_PRESERVE_RECENT_TOKENS,
DEFAULT_RESERVE_TOKENS,
DEFAULT_TARGET_RATIO,
DEFAULT_THRESHOLD_RATIO,
} from "./compaction-shared";
@@ -76,6 +77,37 @@ function safeJsonSize(value: unknown): number {
}
}
function isPositiveFiniteNumber(value: unknown): value is number {
return typeof value === "number" && Number.isFinite(value) && value > 0;
}
function resolveMaxInputTokens(input: {
configMaxInputTokens?: number;
modelMaxInputTokens?: number;
contextWindow?: number;
modelMaxTokens?: number;
}): number {
const candidates: number[] = [];
if (isPositiveFiniteNumber(input.configMaxInputTokens)) {
candidates.push(input.configMaxInputTokens);
}
if (isPositiveFiniteNumber(input.modelMaxInputTokens)) {
candidates.push(input.modelMaxInputTokens);
}
if (isPositiveFiniteNumber(input.contextWindow)) {
candidates.push(input.contextWindow);
if (
isPositiveFiniteNumber(input.modelMaxTokens) &&
input.modelMaxTokens < input.contextWindow
) {
candidates.push(input.contextWindow - input.modelMaxTokens);
}
}
return candidates.length > 0
? Math.min(...candidates)
: DEFAULT_MAX_INPUT_TOKENS;
}
function summarizeToolResults(messages: CoreCompactionContext["messages"]): {
toolResultCount: number;
toolResultSerializedChars: number;
@@ -157,28 +189,28 @@ function resolveTriggerState(input: {
};
}
if (typeof input.config.thresholdRatio !== "number") {
const triggerTokens = Math.max(
0,
Math.min(
input.maxInputTokens - DEFAULT_RESERVE_TOKENS,
input.maxInputTokens * DEFAULT_THRESHOLD_RATIO,
),
);
if (typeof input.config.thresholdRatio === "number") {
const thresholdRatio = input.config.thresholdRatio;
const triggerTokens = input.maxInputTokens * thresholdRatio;
return {
shouldCompact: input.inputTokens > triggerTokens,
triggerTokens,
thresholdRatio:
input.maxInputTokens > 0 ? triggerTokens / input.maxInputTokens : 0,
thresholdRatio,
};
}
const thresholdRatio = input.config.thresholdRatio ?? DEFAULT_THRESHOLD_RATIO;
const triggerTokens = input.maxInputTokens * thresholdRatio;
const triggerTokens = Math.max(
0,
Math.min(
input.maxInputTokens - DEFAULT_RESERVE_TOKENS,
input.maxInputTokens * DEFAULT_THRESHOLD_RATIO,
),
);
return {
shouldCompact: input.inputTokens > triggerTokens,
triggerTokens,
thresholdRatio,
thresholdRatio:
input.maxInputTokens > 0 ? triggerTokens / input.maxInputTokens : 0,
};
}
@@ -209,6 +241,26 @@ function resolveManualTargetState(input: {
};
}
function resolveBasicTargetTokens(input: {
maxInputTokens: number;
modelMaxTokens?: number;
triggerTokens: number;
}): number {
const targetBaseTokens =
typeof input.modelMaxTokens === "number" &&
Number.isFinite(input.modelMaxTokens) &&
input.modelMaxTokens < input.maxInputTokens
? input.maxInputTokens - input.modelMaxTokens
: input.triggerTokens;
return Math.max(
1,
Math.min(
Math.floor(targetBaseTokens * DEFAULT_TARGET_RATIO),
input.maxInputTokens,
),
);
}
/**
* Build the `prepareTurn` callback used by the agent runtime to compact the
* transcript before each model request.
@@ -264,23 +316,18 @@ export function createContextCompactionPrepareTurn(
(total: number, message) => total + estimateMessageTokens(message),
0,
);
const maxInputTokens =
userCompaction?.maxInputTokens ??
context.model.info?.maxInputTokens ??
context.model.info?.contextWindow ??
DEFAULT_MAX_INPUT_TOKENS;
if (
typeof maxInputTokens !== "number" ||
!Number.isFinite(maxInputTokens) ||
maxInputTokens <= 0
) {
return undefined;
}
const maxInputTokens = resolveMaxInputTokens({
configMaxInputTokens: userCompaction?.maxInputTokens,
modelMaxInputTokens: context.model.info?.maxInputTokens,
contextWindow: context.model.info?.contextWindow,
modelMaxTokens: context.model.info?.maxTokens,
});
const triggerState = resolveTriggerState({
inputTokens,
maxInputTokens,
config: {
maxInputTokens: userCompaction?.maxInputTokens,
reserveTokens: userCompaction?.reserveTokens,
thresholdRatio: userCompaction?.thresholdRatio,
},
@@ -304,28 +351,37 @@ export function createContextCompactionPrepareTurn(
if (mode === "auto" && !triggerState.shouldCompact) {
return undefined;
}
const targetState =
mode === "manual"
? resolveManualTargetState({
inputTokens,
maxInputTokens,
const targetState =
mode === "manual"
? resolveManualTargetState({
inputTokens,
maxInputTokens,
autoTriggerTokens: triggerState.triggerTokens,
manualTargetRatio: options.manualTargetRatio,
})
: triggerState;
: triggerState;
const targetTokens =
mode === "auto"
? resolveBasicTargetTokens({
maxInputTokens,
modelMaxTokens: context.model.info?.maxTokens,
triggerTokens: targetState.triggerTokens,
})
: undefined;
const compactionContext = {
agentId: context.agentId,
conversationId: context.conversationId,
const compactionContext = {
agentId: context.agentId,
conversationId: context.conversationId,
parentAgentId: context.parentAgentId,
iteration: context.iteration,
messages: context.messages,
model: context.model,
maxInputTokens,
triggerTokens: targetState.triggerTokens,
thresholdRatio: targetState.thresholdRatio,
utilizationRatio: maxInputTokens > 0 ? inputTokens / maxInputTokens : 0,
};
model: context.model,
maxInputTokens,
triggerTokens: targetState.triggerTokens,
targetTokens,
thresholdRatio: targetState.thresholdRatio,
utilizationRatio: maxInputTokens > 0 ? inputTokens / maxInputTokens : 0,
};
const statusReason =
mode === "manual" ? "manual_compaction" : "auto_compaction";
+1
View File
@@ -130,6 +130,7 @@ export {
RpcClineAccountService,
type UserRemoteConfigOrganization,
type UserRemoteConfigResponse,
type UserCurrentPlan,
} from "./account";
export {
createOAuthClientCallbacks,
@@ -336,17 +336,27 @@ export class RuntimeEventAdapter {
cacheWriteTokens: next.cacheWriteTokens,
totalCost: next.totalCost,
};
// Emit prompt-only inputTokens (excluding cache reads/writes) so consumers
// can explicitly add cache_read themselves, consistent with other agents.
const promptOnlyDeltaInput = Math.max(
0,
deltaInput - Math.max(0, deltaCacheRead) - Math.max(0, deltaCacheWrite),
);
const totalPromptOnlyInput = Math.max(
0,
next.inputTokens - next.cacheReadTokens - next.cacheWriteTokens,
);
return [
{
type: "usage",
inputTokens: Math.max(0, deltaInput),
inputTokens: promptOnlyDeltaInput,
outputTokens: Math.max(0, deltaOutput),
cacheReadTokens:
deltaCacheRead === 0 ? undefined : Math.max(0, deltaCacheRead),
cacheWriteTokens:
deltaCacheWrite === 0 ? undefined : Math.max(0, deltaCacheWrite),
cost: deltaCost === 0 ? undefined : deltaCost,
totalInputTokens: next.inputTokens,
totalInputTokens: totalPromptOnlyInput,
totalOutputTokens: next.outputTokens,
totalCacheReadTokens:
next.cacheReadTokens === 0 ? undefined : next.cacheReadTokens,
@@ -1435,6 +1435,72 @@ describe("SessionRuntime real AgentRuntime smoke", () => {
"user",
]);
});
it("uses the error message instead of replaying prior assistant text after a tool-call stream failure", async () => {
const scriptedModel: AgentModel = {
async stream(request) {
const turn = request.messages.filter(
(message) => message.role === "assistant",
).length;
return (async function* () {
if (turn === 0) {
yield {
type: "text-delta" as const,
text: "I am going to make one more cleanup.",
};
yield {
type: "tool-call-delta" as const,
toolCallId: "call_cleanup",
toolName: "cleanup",
inputText: JSON.stringify({ ok: true }),
};
yield { type: "finish" as const, reason: "tool-calls" as const };
return;
}
yield {
type: "finish" as const,
reason: "error" as const,
error: "upstream failed after tool result",
};
})();
},
};
const session = new SessionRuntime(
makeAgentConfig({
providerId: "cline",
modelId: "openai/gpt-5.5",
apiKey: "test-key",
tools: [
{
name: "cleanup",
description: "cleanup",
inputSchema: { type: "object" },
execute: async () => ({ ok: true }),
},
],
}),
{
createAgentRuntimeImpl: (config) =>
createAgentRuntime({ ...config, model: scriptedModel }),
},
);
const result = await session.run("first");
expect(result.finishReason).toBe("error");
expect(result.text).toBe("upstream failed after tool result");
expect(result.text).not.toContain("one more cleanup");
expect(session.getMessages().map((message) => message.role)).toEqual([
"user",
"assistant",
"user",
]);
const lastContent = session.getMessages().at(-1)?.content[0];
expect(typeof lastContent === "object" ? lastContent.type : undefined).toBe(
"tool_result",
);
});
});
// ---------------------------------------------------------------------------
@@ -1267,8 +1267,8 @@ export class SessionRuntime {
? "error"
: deriveFinishReason(runResult);
const text =
runResult?.outputText ||
(runResult?.status === "failed" ? runResult.error?.message : undefined) ||
runResult?.outputText ||
"";
const usage: LegacyAgentUsage = runResult
? {
+1
View File
@@ -64,6 +64,7 @@ export interface CoreCompactionContext {
};
maxInputTokens: number;
triggerTokens: number;
targetTokens?: number;
thresholdRatio: number;
utilizationRatio: number;
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@cline/llms",
"version": "0.0.53",
"version": "0.0.54",
"description": "Config-driven SDK for selecting, extending, and instantiating LLM providers and models",
"repository": {
"type": "git",
@@ -99,6 +99,7 @@ describe("cline-pass builtin spec", () => {
describe("built-in provider metadata", () => {
it("marks popular providers with a provider capability and rank", async () => {
await expect(getProvider("cline")).resolves.toMatchObject({
name: "Cline Usage-Billing",
capabilities: expect.arrayContaining(["popular"]),
metadata: { popularRank: 1 },
});
+1 -1
View File
@@ -540,7 +540,7 @@ async function handleClineResponseError(
const cline = createClineLikeSpec({
id: "cline",
name: "Cline",
name: "Cline Usage-Billing",
popular: 1,
modelsFactory: buildClineModels,
defaultModelId: CLINE_DEFAULT_MODEL_ID,
@@ -34,6 +34,17 @@ describe("extractErrorMessage", () => {
);
});
it("preserves native transport error wrappers and cause metadata", () => {
const socketError = Object.assign(new Error("other side closed"), {
name: "SocketError",
code: "UND_ERR_SOCKET",
});
expect(
extractErrorMessage(new TypeError("fetch failed", { cause: socketError })),
).toBe("fetch failed: SocketError: other side closed (UND_ERR_SOCKET)");
});
it("prefers nested stream error details over generic wrapper messages", () => {
expect(
extractErrorMessage({
+75 -18
View File
@@ -1,25 +1,21 @@
export function extractErrorMessage(error: unknown): string {
const extractStructuredMessage = (value: unknown): string | undefined => {
if (!value) {
return undefined;
}
if (typeof value === "string") {
try {
return extractStructuredMessage(JSON.parse(value));
} catch {
return value.trim() || undefined;
}
}
if (typeof value !== "object") {
return undefined;
}
// Generic SDK wrappers carry no signal of their own — when present we prefer
// the underlying cause/detail (e.g. AI SDK's AI_NoOutputGeneratedError).
const GENERIC_WRAPPER_MESSAGES = new Set([
"no output generated. check the stream for errors.",
]);
const isGenericWrapperMessage = (message: string): boolean =>
GENERIC_WRAPPER_MESSAGES.has(message.trim().toLowerCase());
// Pulls a human-readable message out of structured provider fields
// (error/detail/errors/responseBody) without falling back to a top-level
// `message`. Shared by the Error and plain-object branches.
const extractStructuredDetail = (value: object): string | undefined => {
const payload = value as {
error?: { message?: string } | string;
errors?: unknown;
detail?: string;
message?: string;
responseBody?: unknown;
cause?: unknown;
};
if (typeof payload.error === "string" && payload.error.trim()) {
return payload.error;
@@ -36,8 +32,8 @@ export function extractErrorMessage(error: unknown): string {
return payload.detail;
}
if (Array.isArray(payload.errors)) {
for (const error of payload.errors) {
const nested = extractStructuredMessage(error);
for (const nestedError of payload.errors) {
const nested = extractStructuredMessage(nestedError);
if (nested) {
return nested;
}
@@ -49,6 +45,67 @@ export function extractErrorMessage(error: unknown): string {
return nested;
}
}
return undefined;
};
const extractStructuredMessage = (value: unknown): string | undefined => {
if (!value) {
return undefined;
}
if (typeof value === "string") {
try {
return extractStructuredMessage(JSON.parse(value));
} catch {
return value.trim() || undefined;
}
}
if (typeof value !== "object") {
return undefined;
}
if (value instanceof Error) {
const message = value.message.trim();
const detailMessage = extractStructuredDetail(value);
const cause = (value as { cause?: unknown }).cause;
const causeMessage = extractStructuredMessage(cause);
// Generic wrappers (e.g. "No output generated...") only matter as a
// fallback — surface the underlying detail/cause instead.
if (message && isGenericWrapperMessage(message)) {
return detailMessage ?? causeMessage ?? undefined;
}
// Structured provider detail attached directly to the error
// (responseBody/detail/error fields) is more useful than the bland
// top-level Error message.
if (detailMessage && detailMessage !== message) {
return detailMessage;
}
// Otherwise preserve the wrapper message alongside its cause, e.g.
// "fetch failed: SocketError: other side closed (UND_ERR_SOCKET)".
if (causeMessage && message && causeMessage !== message) {
const causeName =
cause instanceof Error && cause.name && cause.name !== "Error"
? `${cause.name}: `
: "";
const causeCode =
cause && typeof cause === "object" && "code" in cause
? (cause as { code?: unknown }).code
: undefined;
const codeSuffix =
typeof causeCode === "string" && causeCode.trim()
? ` (${causeCode})`
: "";
return `${message}: ${causeName}${causeMessage}${codeSuffix}`;
}
return causeMessage ?? (message || undefined);
}
const detail = extractStructuredDetail(value);
if (detail) {
return detail;
}
const payload = value as { cause?: unknown; message?: string };
if ("cause" in payload && payload.cause !== value) {
const nested = extractStructuredMessage(payload.cause);
if (nested) {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@cline/sdk",
"description": "Cline SDK - user-facing alias for @cline/core",
"version": "0.0.53",
"version": "0.0.54",
"repository": {
"type": "git",
"url": "https://github.com/cline/cline",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@cline/shared",
"version": "0.0.53",
"version": "0.0.54",
"description": "Shared utilities, types, and schemas for Cline packages",
"repository": {
"type": "git",
+2
View File
@@ -140,6 +140,8 @@ export interface AgentUsageEvent extends AgentEventMetadata {
cacheReadTokens?: number;
/** Tokens written to cache */
cacheWriteTokens?: number;
/** Number of tokens used for reasoning */
reasoningTokens?: number;
/** Cost for this turn */
cost?: number;
+1 -1
View File
@@ -194,7 +194,7 @@ export {
resolveReasoningEffortRatio,
} from "./llms/reasoning-effort";
export { DEFAULT_REQUEST_HEADERS, serializeAbortReason } from "./llms/requests";
export { estimateTokens } from "./llms/tokens";
export { CHARS_PER_TOKEN, estimateTokens } from "./llms/tokens";
export type {
ToolApprovalRequest,
ToolApprovalResult,
+1 -1
View File
@@ -208,7 +208,7 @@ export {
resolveReasoningEffortRatio,
} from "./llms/reasoning-effort";
export { DEFAULT_REQUEST_HEADERS, serializeAbortReason } from "./llms/requests";
export { estimateTokens } from "./llms/tokens";
export { CHARS_PER_TOKEN, estimateTokens } from "./llms/tokens";
export type {
ToolApprovalRequest,
ToolApprovalResult,
+1 -1
View File
@@ -5,7 +5,7 @@
* rather than after.
*/
const CHARS_PER_TOKEN = 3;
export const CHARS_PER_TOKEN = 3;
export function estimateTokens(chars: number): number {
return Math.max(1, Math.ceil(chars / CHARS_PER_TOKEN));