Compare commits

..

1 Commits

Author SHA1 Message Date
abeatrix 43ddc8c846 fix(test): increase hook test timeouts for Windows in taskcancel tests
Add WINDOWS_HOOK_TEST_TIMEOUT_MS (15s) to taskcancel hook tests to
account for the slower PowerShell bridge that spawns a child Node
process on Windows CI. The double-process startup is variable and can
exceed Mocha's default 2s timeout, causing flaky failures.

Mirrors the approach already used in taskresume, hook-factory, and
user-prompt-submit tests.
2026-06-01 20:36:24 -07:00
1178 changed files with 5955 additions and 19165 deletions
+13 -9
View File
@@ -140,10 +140,12 @@ Adding a new key to global state requires updates in multiple places. Missing an
Required steps:
1. Type definition in `src/shared/storage/state-keys.ts` - Add to `GlobalState` or `Settings` interface
2. Add any default value or transform in `src/shared/storage/state-keys.ts` if the key needs one
3. Read and write the value through `StateManager` (`setGlobalState()` / `getGlobalStateKey()`) after initialization
2. Read from globalState in `src/core/storage/utils/state-helpers.ts`:
- Add `const myKey = context.globalState.get<GlobalStateAndSettings["myKey"]>("myKey")` in `readGlobalStateFromDisk()`
- Add to the return object: `myKey: myKey ?? defaultValue,`
3. StateManager handles read/write via `setGlobalState()`/`getGlobalStateKey()` after initialization
Persistent state is file-backed through `StateManager`; do not add new runtime reads or writes against VS Code `ExtensionContext` storage. That storage is only a legacy migration source.
Common mistake: Adding only the return value without the `context.globalState.get()` call. This compiles but the value is always `undefined` on load.
Settings plumbing gotcha: if a key is user-toggleable from settings, wire both controller update paths:
- `src/core/controller/state/updateSettings.ts` for webview `updateSetting(...)`
@@ -157,20 +159,22 @@ Webview toggle gotcha: settings changes must also round-trip back in state paylo
If this round-trip wiring is missing, the backend value can update but the toggle in webview appears stuck or reverts.
## StateManager Cache vs Direct globalState Access
StateManager uses an in-memory cache populated during `StateManager.initialize()` from file-backed storage. For most state, use `controller.stateManager.setGlobalState()`/`getGlobalStateKey()`.
StateManager uses an in-memory cache populated during `StateManager.initialize(context)` in `common.ts`. For most state, use `controller.stateManager.setGlobalState()`/`getGlobalStateKey()`.
Exception: host migration code may read legacy VS Code storage before file-backed storage is initialized.
Exception: State needed immediately at extension startup (before cache is ready)
Example pattern:
When Window A sets state and immediately opens Window B, the new window's StateManager cache is populated from `context.globalState` during initialization. If you need to read state in Window B right at startup (e.g., in `common.ts` during `initialize()`), read directly from `context.globalState.get()` instead of StateManager's cache.
Example pattern (see `lastShownAnnouncementId` and `worktreeAutoOpenPath`):
```typescript
// Writing (normal pattern)
controller.stateManager.setGlobalState("myKey", value)
// Reading after initialization
const value = controller.stateManager.getGlobalStateKey("myKey")
// Reading at startup in common.ts (bypass cache)
const value = context.globalState.get<string>("myKey")
```
Use `context.globalState` only in VS Code migration code that copies legacy ExtensionContext values into the shared file-backed stores.
This is only needed for cross-window state read during the brief startup window before StateManager cache is fully usable. Normal state access after initialization should use StateManager.
## ChatRow Cancelled/Interrupted States
When a ChatRow displays a loading/in-progress state (spinner), you must handle what happens when the task is cancelled. This is non-obvious because cancellation doesn't update the message content—you have to infer it from context.
+3 -15
View File
@@ -7,10 +7,10 @@ body:
value: |
**Important:** All bug reports must be reproducible using Claude Sonnet 4.5. Cline uses complex prompts so less capable models may not work as expected.
- type: dropdown
id: cline-surface
id: plugin-type
attributes:
label: Cline Surface
description: Which Cline surface are you reporting a bug for?
label: Plugin Type
description: Which plugin are you reporting a bug for?
options:
- VSCode Extension
- JetBrains Plugin
@@ -59,18 +59,6 @@ body:
placeholder: 'e.g., cline:anthropic/claude-sonnet-4.5, gemini:gemini-2.5-pro-exp-03-25'
validations:
required: false
- type: textarea
id: ide-diagnostics
attributes:
label: IDE / CLI Diagnostics
description: |
Paste the "About" diagnostics for your Cline surface. This captures the IDE build, runtime, and host details we need.
- VSCode Extension: open `Help → About` (Windows/Linux) or `Code → About Visual Studio Code` (macOS), then copy the info.
- JetBrains Plugin: open `Help → About` (Windows/Linux) or `<IDE name> → About` (macOS), then click `Copy` to grab build, runtime, OS, memory, and cores.
- CLI: there is no About dialog. Run `cline --version` and paste the output.
placeholder: Paste the copied About info or `cline --version` output here.
validations:
required: false
- type: textarea
id: system-info
attributes:
+1 -1
View File
@@ -44,7 +44,7 @@ For Responses API providers: add to `isNextGenModelProvider()` in `src/utils/mod
Modular: `components/` (shared) + `variants/` (model-specific) + `templates/` (`{{PLACEHOLDER}}`). Variants override components via `componentOverrides` in `config.ts` or custom `template.ts`. XS variant is heavily condensed inline. Always regenerate snapshots after changes.
## Global State Keys (silent failure risk)
Adding a key requires updating the typed storage definitions in `src/shared/storage/state-keys.ts`; runtime reads and writes should go through `StateManager`, not VS Code `ExtensionContext` storage. Persistent state is file-backed so it works across VS Code, CLI, and JetBrains hosts.
Adding a key requires: type in `src/shared/storage/state-keys.ts`, read via `context.globalState.get()` in `src/core/storage/utils/state-helpers.ts` `readGlobalStateFromDisk()`, and add to return object. Missing the `.get()` call compiles fine but value is always `undefined`.
## Slash Commands (3 places)
- `src/core/slash-commands/index.ts` — definitions.
+14 -14
View File
@@ -102,15 +102,15 @@ jobs:
fi
VERSION="${TAG#cli-v}"
PACKAGE_VERSION=$(node -p "require('./apps/cli/package.json').version")
PACKAGE_VERSION=$(node -p "require('./sdk/apps/cli/package.json').version")
if [ "$PACKAGE_VERSION" != "$VERSION" ]; then
echo "apps/cli/package.json version ${PACKAGE_VERSION} does not match ${TAG}"
echo "sdk/apps/cli/package.json version ${PACKAGE_VERSION} does not match ${TAG}"
exit 1
fi
if ! printf "%s\n" "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$'; then
echo "apps/cli/package.json has invalid version: ${VERSION}"
echo "sdk/apps/cli/package.json has invalid version: ${VERSION}"
exit 1
fi
@@ -147,7 +147,7 @@ jobs:
- name: Build platform binaries
run: bun script/build.ts --install-native-variants --skip-sdk-build
working-directory: apps/cli
working-directory: sdk/apps/cli
env:
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
@@ -172,7 +172,7 @@ jobs:
)
for package_name in "${EXPECTED[@]}"; do
dir="apps/cli/dist/${package_name#@cline/}"
dir="sdk/apps/cli/dist/${package_name#@cline/}"
if [ ! -f "$dir/package.json" ]; then
echo "Missing package manifest: $dir/package.json"
exit 1
@@ -194,7 +194,7 @@ jobs:
env:
NPM_CONFIG_PROVENANCE: "true"
run: bun script/publish-npm.ts --tag latest
working-directory: apps/cli
working-directory: sdk/apps/cli
- name: Get Previous CLI Tag
id: prev_tag
@@ -207,8 +207,8 @@ jobs:
- name: Get Changelog Entry
id: changelog
run: |
# Grab content between the first "## " header and the next one in apps/cli/CHANGELOG.md
CONTENT=$(awk '/^## [0-9]/{if(found) exit; found=1; next} found{print}' apps/cli/CHANGELOG.md)
# Grab content between the first "## " header and the next one in sdk/apps/cli/CHANGELOG.md
CONTENT=$(awk '/^## [0-9]/{if(found) exit; found=1; next} found{print}' sdk/apps/cli/CHANGELOG.md)
echo "content<<EOF" >> $GITHUB_OUTPUT
echo "$CONTENT" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
@@ -349,7 +349,7 @@ jobs:
if: steps.check_commits.outputs.skip != 'true'
id: version
run: |
BASE_VERSION=$(node -p "require('./apps/cli/package.json').version")
BASE_VERSION=$(node -p "require('./sdk/apps/cli/package.json').version")
TIMESTAMP=$(date +%s)
VERSION="${BASE_VERSION}-nightly.${TIMESTAMP}"
@@ -365,17 +365,17 @@ jobs:
run: |
node -e '
const fs = require("node:fs");
const path = "apps/cli/package.json";
const path = "sdk/apps/cli/package.json";
const pkg = JSON.parse(fs.readFileSync(path, "utf8"));
pkg.version = process.env.VERSION;
fs.writeFileSync(path, `${JSON.stringify(pkg, null, "\t")}\n`);
'
cat apps/cli/package.json | grep '"version"'
cat sdk/apps/cli/package.json | grep '"version"'
- name: Build platform binaries
if: steps.check_commits.outputs.skip != 'true'
run: bun script/build.ts --install-native-variants --skip-sdk-build
working-directory: apps/cli
working-directory: sdk/apps/cli
env:
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
@@ -401,7 +401,7 @@ jobs:
)
for package_name in "${EXPECTED[@]}"; do
dir="apps/cli/dist/${package_name#@cline/}"
dir="sdk/apps/cli/dist/${package_name#@cline/}"
if [ ! -f "$dir/package.json" ]; then
echo "Missing package manifest: $dir/package.json"
exit 1
@@ -424,7 +424,7 @@ jobs:
env:
NPM_CONFIG_PROVENANCE: "true"
run: bun script/publish-npm.ts --tag nightly
working-directory: apps/cli
working-directory: sdk/apps/cli
- name: Summary
if: steps.check_commits.outputs.skip != 'true'
@@ -31,9 +31,6 @@ jobs:
if: github.repository == 'cline/cline' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dpc/sdk-migration-simpler-login')
runs-on: ubuntu-latest
environment: PublishNightly
defaults:
run:
working-directory: apps/vscode
steps:
- name: Checkout selected branch
@@ -57,13 +54,11 @@ jobs:
# Newer LTS (Node 24 / npm 11) can make `npm list` fail with ELSPROBLEMS during vsce packaging.
node-version: 22
- name: Install extension dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode ci --include=optional
- name: Install root dependencies
run: npm ci --include=optional
- name: Install webview-ui dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode/webview-ui ci --include=optional
run: cd webview-ui && npm ci --include=optional
- name: Install Publishing Tools
run: npm install -g @vscode/vsce ovsx
@@ -114,13 +114,11 @@ jobs:
with:
node-version: 22
- name: Install extension dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode install --include=optional
- name: Install root dependencies
run: npm install --include=optional
- name: Install webview-ui dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode/webview-ui install --include=optional
run: cd webview-ui && npm install --include=optional
- name: Install Publishing Tools
run: npm install -g @vscode/vsce ovsx
+3 -5
View File
@@ -128,15 +128,13 @@ jobs:
restore-keys: |
playwright-browsers-${{ runner.os }}-
- name: Install extension dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode ci
- name: Install root dependencies
run: npm ci
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install webview-ui dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode/webview-ui ci
run: cd webview-ui && npm ci
- name: Install vsce
run: npm install -g @vscode/vsce
+10 -18
View File
@@ -91,15 +91,13 @@ jobs:
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
- name: Install root dependencies
run: npm ci
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install webview-ui dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode/webview-ui ci
run: cd webview-ui && npm ci
- name: Run Quality Checks (Parallel)
run: npm run ci:check-all
@@ -132,19 +130,16 @@ jobs:
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
- name: Install root dependencies
run: npm ci
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install webview-ui dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode/webview-ui ci
run: cd webview-ui && npm ci
- name: Set up NPM on Windows
if: runner.os == 'Windows'
working-directory: ${{ github.workspace }}
run: |
npm config set script-shell "C:\\Program Files\\Git\\bin\\bash.exe"
@@ -234,15 +229,13 @@ jobs:
apps/vscode/webview-ui/package-lock.json
apps/vscode/testing-platform/package-lock.json
- name: Install extension dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode ci
- name: Install root dependencies
run: npm ci
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install webview-ui dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode/webview-ui ci
run: cd webview-ui && npm ci
- name: Download ripgrep binaries
run: npm run download-ripgrep
@@ -251,8 +244,7 @@ jobs:
run: npm run compile-standalone
- name: Install testing platform dependencies
working-directory: ${{ github.workspace }}
run: npm --prefix apps/vscode/testing-platform ci
run: cd testing-platform && npm ci
- name: Running testing platform integration spec tests
timeout-minutes: 7
+3 -3
View File
@@ -17,7 +17,7 @@ jobs:
const labels = context.payload.issue.labels.map(l => l.name);
// Check if JetBrains Plugin is selected
if (body.match(/###\s*Cline Surface\s*\n+JetBrains Plugin/i)) {
if (body.match(/###\s*Plugin Type\s*\n+JetBrains Plugin/i)) {
if (!labels.includes('JetBrains')) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
@@ -29,7 +29,7 @@ jobs:
}
// Check if VSCode Extension is selected
if (body.match(/###\s*Cline Surface\s*\n+VSCode Extension/i)) {
if (body.match(/###\s*Plugin Type\s*\n+VSCode Extension/i)) {
if (!labels.includes('VS Code')) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
@@ -41,7 +41,7 @@ jobs:
}
// Check if CLI is selected
if (body.match(/###\s*Cline Surface\s*\n+CLI/i)) {
if (body.match(/###\s*Plugin Type\s*\n+CLI/i)) {
if (!labels.includes('CLI')) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
-11
View File
@@ -61,17 +61,6 @@ tests/**/cache
# Should never be committed: only exists if a publish aborts mid-swap.
.README.github.bak
# Tauri generated code
apps/*/src-tauri/gen
apps/*/src-tauri/bin
apps/examples/*/src-tauri/gen
apps/examples/*/src-tauri/bin
# Tauri UI test snapshots
apps/*/src/tests/.tui-test
apps/*/src/tests/tui-traces
apps/vscode/webview-ui/src/**/*.js
apps/vscode/webview-ui/src/**/*.js.map
# SDK Session files / User data
.cline/data
-49
View File
@@ -1,54 +1,5 @@
# Changelog
## [3.89.0]
### Added
- Add Claude Fable 5 model support.
### Fixed
- Fix MiniMax M3 thinking controls across gateways.
### Changed
- Clean up the Codex model list.
## [3.88.1]
### Added
- Add a debug section in settings for Cline testers.
### Fixed
- Include the walkthrough markdown files in the VS Code extension package so the first-run walkthrough steps load correctly.
## [3.88.0]
### Added
- Add the latest Fireworks AI serverless models and update the default Fireworks model to Kimi K2.6.
### Fixed
- Fix MCP server delete/add flows so settings writes do not cause the MCP server list to be emptied by the file watcher.
- Remove stale Fireworks AI models and correct Fireworks model metadata and cache pricing.
### Changed
- Always use the upstream Cline recommended models endpoint instead of gating it behind a feature flag.
## [3.87.0]
### Added
- Add MiniMax M3 model support.
### Fixed
- Update VS Code extension dependencies to resolve security issues in `@xmldom/xmldom`, `basic-ftp`, `axios`, `undici`, and other direct/transitive packages.
## [3.86.2]
### Fixed
+3 -7
View File
@@ -51,7 +51,7 @@ for CI/CD and scripting.
npm i -g cline
```
<a href="./apps/cli/README.md">Learn more</a>
<a href="./sdk/apps/cli/README.md">Learn more</a>
<br><br>
</td>
@@ -129,7 +129,7 @@ npm install @cline/sdk
| Product | Description | Location | CHANGELOG |
|---------|------------|--------------|--------------|
| **SDK** | Node.js programmatic agent API and extension exports. | [`sdk/`](https://github.com/cline/cline/tree/main/sdk) | [CHANGELOG.md](https://github.com/cline/cline/blob/main/sdk/CHANGELOG.md) |
| **CLI** | Terminal UI, headless mode, shell commands, and CLI-specific flows. | [`apps/cli/`](https://github.com/cline/cline/tree/main/apps/cli) | [CHANGELOG.md](https://github.com/cline/cline/blob/main/apps/cli/CHANGELOG.md) |
| **CLI** | Terminal UI, headless mode, shell commands, and CLI-specific flows. | [`sdk/apps/cli/`](https://github.com/cline/cline/tree/main/sdk/apps/cli) | [CHANGELOG.md](https://github.com/cline/cline/blob/main/sdk/apps/cli/CHANGELOG.md) |
| **VS Code Extension** | The Marketplace extension and extension host integration. | [`/`](https://github.com/cline/cline/tree/main) (WIP migrating) | [CHANGELOG.md](https://github.com/cline/cline/blob/main/CHANGELOG.md) |
| **JetBrains Plugin** | JetBrains-hosted client that talks to the shared agent core. | Currently we are not open-sourcing JetBrains plugins | - |
| **Kanban** | Web-based multi-agent task board. | [`cline/kanban`](https://github.com/cline/kanban) | [CHANGELOG.md](https://github.com/cline/kanban/blob/main/CHANGELOG.md) |
@@ -212,12 +212,8 @@ cline schedule create "PR summary" \
Chat with your agent from any messaging platform: Telegram, Slack, Discord, Google Chat, WhatsApp, and Linear. Each conversation thread maps to an agent session with full context. Set up access control to restrict who can interact with your agent.
```bash
# Connect to Telegram
cline connect telegram -k $BOT_TOKEN
# Connect to Slack through webhook
cline connect slack --bot-token $SLACK_TOKEN --signing-secret $SECRET --base-url $URL
# Connect to Slack using socket mode
cline connect slack --bot-token $SLACK_TOKEN --app-token $SLACK_APP_TOKEN
cline connect slack --token $SLACK_TOKEN --signing-secret $SECRET --base-url $URL
```
## Headless CLI for CI/CD
-14
View File
@@ -1,14 +0,0 @@
{
"$schema": "https://biomejs.dev/schemas/2.4.5/schema.json",
"root": false,
"extends": [
"../sdk/biome.json"
],
"linter": {
"rules": {
"a11y": {
"noStaticElementInteractions": "warn"
}
}
}
}
-210
View File
@@ -1,210 +0,0 @@
import { existsSync, readFileSync } from "node:fs";
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { setHomeDir } from "@cline/shared/storage";
import { afterEach, describe, expect, it } from "vitest";
import {
installAgentProfile,
parseAgentSource,
planAgentPluginInstalls,
uninstallAgentProfile,
} from "./agent";
const PROFILE = `---
name: reviewer
description: Reviews code
plugins:
- branch-protector
- name: my-tool
install: https://example.com/my-tool.ts
---
You are a meticulous reviewer.`;
describe("agent command", () => {
const envSnapshot = { HOME: process.env.HOME };
afterEach(() => {
process.env.HOME = envSnapshot.HOME;
setHomeDir(envSnapshot.HOME ?? "~");
});
async function setUpHome(): Promise<{ root: string; home: string }> {
// Home is nested under a fixture root so the plugin display-name
// package.json walk never escapes into the shared temp directory.
const root = await mkdtemp(join(tmpdir(), "cli-agent-cmd-"));
const home = join(root, "home");
await mkdir(home, { recursive: true });
process.env.HOME = home;
setHomeDir(home);
return { root, home };
}
describe("parseAgentSource", () => {
it("parses local paths, official slugs, and remote URLs", () => {
expect(parseAgentSource("./reviewer.yml")).toEqual({
type: "local",
path: "./reviewer.yml",
});
expect(parseAgentSource("~/agents/reviewer.yaml")).toEqual({
type: "local",
path: "~/agents/reviewer.yaml",
});
expect(parseAgentSource("reviewer")).toEqual({
type: "official",
slug: "reviewer",
});
expect(parseAgentSource("code-reviewer")).toEqual({
type: "official",
slug: "code-reviewer",
});
expect(
parseAgentSource("https://example.com/profiles/reviewer.yml"),
).toEqual({
type: "remote",
url: "https://example.com/profiles/reviewer.yml",
filename: "reviewer.yml",
});
});
it("rewrites GitHub blob URLs to raw URLs", () => {
expect(
parseAgentSource(
"https://github.com/cline/agents/blob/main/agents/reviewer.yml",
),
).toEqual({
type: "remote",
url: "https://raw.githubusercontent.com/cline/agents/main/agents/reviewer.yml",
filename: "reviewer.yml",
});
});
it("rejects non-yaml GitHub file URLs and http URLs", () => {
expect(() =>
parseAgentSource(
"https://github.com/cline/agents/blob/main/agents/reviewer.md",
),
).toThrow(/must be \.yml or \.yaml/);
expect(() => parseAgentSource("http://example.com/reviewer.yml")).toThrow(
/must use https/,
);
});
});
describe("installAgentProfile", () => {
it("validates and writes the profile under the global agents dir", async () => {
const { root, home } = await setUpHome();
try {
const { config, installPath } = installAgentProfile({
content: PROFILE,
source: "./reviewer.yml",
});
expect(config.name).toBe("reviewer");
expect(installPath).toBe(
join(home, ".cline", "agents", "reviewer.yml"),
);
expect(readFileSync(installPath, "utf8")).toBe(PROFILE);
} finally {
await rm(root, { recursive: true, force: true });
}
});
it("rejects invalid profiles before writing anything", async () => {
const { root, home } = await setUpHome();
try {
expect(() =>
installAgentProfile({
content: "not a profile",
source: "./broken.yml",
}),
).toThrow(/Invalid agent profile from \.\/broken\.yml/);
expect(existsSync(join(home, ".cline", "agents"))).toBe(false);
} finally {
await rm(root, { recursive: true, force: true });
}
});
it("refuses to replace an existing profile without force", async () => {
const { root } = await setUpHome();
try {
installAgentProfile({ content: PROFILE, source: "a" });
expect(() =>
installAgentProfile({ content: PROFILE, source: "a" }),
).toThrow(/already installed/);
expect(() =>
installAgentProfile({ content: PROFILE, source: "a", force: true }),
).not.toThrow();
} finally {
await rm(root, { recursive: true, force: true });
}
});
});
describe("planAgentPluginInstalls", () => {
it("classifies listed plugins as installed, installable, or manual", async () => {
const { root, home } = await setUpHome();
try {
const userPlugins = join(home, ".cline", "plugins");
await mkdir(userPlugins, { recursive: true });
await writeFile(
join(userPlugins, "branch-protector.ts"),
"export default {}",
"utf8",
);
const plan = planAgentPluginInstalls([
{ name: "Branch-Protector" },
{ name: "my-tool", install: "https://example.com/my-tool.ts" },
{ name: "mystery-plugin" },
]);
expect(plan.alreadyInstalled).toEqual([{ name: "Branch-Protector" }]);
expect(plan.installable).toEqual([
{ name: "my-tool", install: "https://example.com/my-tool.ts" },
]);
expect(plan.manual).toEqual([{ name: "mystery-plugin" }]);
} finally {
await rm(root, { recursive: true, force: true });
}
});
it("returns an empty plan when the profile lists no plugins", async () => {
const { root } = await setUpHome();
try {
expect(planAgentPluginInstalls(undefined)).toEqual({
alreadyInstalled: [],
installable: [],
manual: [],
});
} finally {
await rm(root, { recursive: true, force: true });
}
});
});
describe("uninstallAgentProfile", () => {
it("removes a profile by frontmatter name or file name", async () => {
const { root } = await setUpHome();
try {
installAgentProfile({ content: PROFILE, source: "a" });
const result = uninstallAgentProfile("Reviewer");
expect(result.name).toBe("reviewer");
expect(existsSync(result.installPath)).toBe(false);
} finally {
await rm(root, { recursive: true, force: true });
}
});
it("lists available profiles when the name does not match", async () => {
const { root } = await setUpHome();
try {
installAgentProfile({ content: PROFILE, source: "a" });
expect(() => uninstallAgentProfile("nope")).toThrow(
/available: reviewer/,
);
} finally {
await rm(root, { recursive: true, force: true });
}
});
});
});
-520
View File
@@ -1,520 +0,0 @@
import {
existsSync,
mkdirSync,
readdirSync,
readFileSync,
rmSync,
writeFileSync,
} from "node:fs";
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { basename, extname, join, resolve } from "node:path";
import * as p from "@clack/prompts";
import {
type ConfiguredAgentConfig,
type ConfiguredAgentPluginRef,
discoverPluginModulePaths,
loadConfiguredAgentConfigs,
parseConfiguredAgentConfig,
resolvePluginConfigSearchPaths,
} from "@cline/core";
import {
getPluginDisplayName,
resolveAgentsConfigDirPath,
} from "@cline/shared/storage";
import { resolveWorkspaceRoot } from "../utils/helpers";
import {
downloadRemoteFile,
isLocalPathLike,
isOfficialRegistrySlug,
normalizeRemoteSingleFileUrl,
resolveHomePath,
runCommand,
sanitizeSegment,
} from "./install-utils";
import { installPlugin } from "./plugin";
export interface AgentCommandIo {
writeln: (text?: string) => void;
writeErr: (text: string) => void;
}
export interface AgentInstallOptions {
source: string;
force?: boolean;
/** Install profile-declared plugins without asking. */
yes?: boolean;
json?: boolean;
cwd?: string;
officialAgentsRepo?: string;
io?: AgentCommandIo;
}
export interface AgentInstallResult {
source: string;
name: string;
installPath: string;
/** Plugin names by outcome, one consistent shape across categories. */
plugins: {
alreadyInstalled: string[];
installed: string[];
failed: string[];
skipped: string[];
manual: string[];
};
}
export type ParsedAgentSource =
| { type: "official"; slug: string }
| { type: "remote"; url: string; filename: string }
| { type: "local"; path: string };
export const OFFICIAL_AGENTS_REPO = "https://github.com/cline/agents.git";
const AGENTS_REPO_DIRECTORY_NAME = "agents";
const REMOTE_AGENT_FETCH_TIMEOUT_MS = 30_000;
const REMOTE_AGENT_MAX_BYTES = 1024 * 1024;
const AGENT_SOURCE_KIND = "agent profile";
function isAgentConfigFilename(filename: string): boolean {
const extension = extname(filename).toLowerCase();
return extension === ".yml" || extension === ".yaml";
}
export function parseAgentSource(source: string): ParsedAgentSource {
const trimmed = source.trim();
if (!trimmed) {
throw new Error("agent install requires a source");
}
if (isLocalPathLike(trimmed)) {
return { type: "local", path: source };
}
const remote = normalizeRemoteSingleFileUrl(trimmed, {
isExpectedFile: isAgentConfigFilename,
kind: AGENT_SOURCE_KIND,
extensionsLabel: ".yml or .yaml",
fallbackFilename: "agent.yml",
});
if (remote) {
return { type: "remote", ...remote };
}
if (isOfficialRegistrySlug(trimmed)) {
return { type: "official", slug: trimmed };
}
return { type: "local", path: source };
}
async function fetchOfficialAgentProfile(
slug: string,
officialAgentsRepo: string,
): Promise<string> {
const stagingRoot = await mkdtemp(join(tmpdir(), "cline-agent-install-"));
try {
await runCommand("git", [
"clone",
"--filter=blob:none",
"--depth",
"1",
"--",
officialAgentsRepo,
stagingRoot,
]);
for (const extension of [".yml", ".yaml"]) {
const candidate = join(
stagingRoot,
AGENTS_REPO_DIRECTORY_NAME,
`${slug}${extension}`,
);
if (existsSync(candidate)) {
return readFileSync(candidate, "utf8");
}
}
throw new Error(
`Official Cline agent "${slug}" was not found at ${AGENTS_REPO_DIRECTORY_NAME}/${slug}.yml in ${officialAgentsRepo}`,
);
} finally {
await rm(stagingRoot, { recursive: true, force: true });
}
}
async function fetchAgentProfileContent(
parsed: ParsedAgentSource,
cwd: string,
officialAgentsRepo: string,
): Promise<string> {
if (parsed.type === "official") {
return fetchOfficialAgentProfile(parsed.slug, officialAgentsRepo);
}
if (parsed.type === "remote") {
const body = await downloadRemoteFile(parsed.url, {
timeoutMs: REMOTE_AGENT_FETCH_TIMEOUT_MS,
maxBytes: REMOTE_AGENT_MAX_BYTES,
kind: AGENT_SOURCE_KIND,
});
return body.toString("utf8");
}
const absolutePath = resolve(cwd, resolveHomePath(parsed.path));
if (!existsSync(absolutePath)) {
throw new Error(`Agent profile path does not exist: ${absolutePath}`);
}
if (!isAgentConfigFilename(absolutePath)) {
throw new Error(`Agent profile must be .yml or .yaml: ${absolutePath}`);
}
return readFileSync(absolutePath, "utf8");
}
export interface AgentPluginInstallPlan {
/** Listed plugins already installed (matched by display name). */
alreadyInstalled: ConfiguredAgentPluginRef[];
/** Listed plugins with an install source, not installed yet. */
installable: ConfiguredAgentPluginRef[];
/** Listed plugins with no install source and no local match. */
manual: ConfiguredAgentPluginRef[];
}
export function planAgentPluginInstalls(
plugins: ConfiguredAgentPluginRef[] | undefined,
): AgentPluginInstallPlan {
const plan: AgentPluginInstallPlan = {
alreadyInstalled: [],
installable: [],
manual: [],
};
if (!plugins?.length) {
return plan;
}
const installedNames = new Set<string>();
// Global plugin directories only: the profile installs globally, so a
// workspace-local plugin cannot satisfy its dependencies.
for (const directory of resolvePluginConfigSearchPaths(undefined)) {
let pluginPaths: string[] = [];
try {
pluginPaths = discoverPluginModulePaths(directory);
} catch {
// Best effort: skip unreadable plugin roots.
}
for (const pluginPath of pluginPaths) {
try {
installedNames.add(getPluginDisplayName(pluginPath).toLowerCase());
} catch {
// Best effort: one unreadable plugin should not hide the rest.
}
}
}
for (const ref of plugins) {
if (installedNames.has(ref.name.toLowerCase())) {
plan.alreadyInstalled.push(ref);
} else if (ref.install) {
plan.installable.push(ref);
} else {
plan.manual.push(ref);
}
}
return plan;
}
export function installAgentProfile(options: {
content: string;
source: string;
force?: boolean;
}): { config: ConfiguredAgentConfig; installPath: string } {
let config: ConfiguredAgentConfig;
try {
config = parseConfiguredAgentConfig(options.content);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw new Error(`Invalid agent profile from ${options.source}: ${message}`);
}
const agentsDir = resolveAgentsConfigDirPath();
const installPath = join(
agentsDir,
`${sanitizeSegment(config.name.toLowerCase(), "agent")}.yml`,
);
if (existsSync(installPath) && options.force !== true) {
throw new Error(
`Agent profile is already installed at ${installPath}. Use --force to replace it.`,
);
}
mkdirSync(agentsDir, { recursive: true });
writeFileSync(installPath, options.content, "utf8");
return { config, installPath };
}
function formatPluginRef(ref: ConfiguredAgentPluginRef): string {
return ref.install && ref.install !== ref.name
? `${ref.name} (${ref.install})`
: ref.name;
}
async function installPluginDependencies(input: {
refs: ConfiguredAgentPluginRef[];
wizard: boolean;
io?: AgentCommandIo;
}): Promise<{ installed: string[]; failed: string[] }> {
const installed: string[] = [];
const failed: string[] = [];
for (const ref of input.refs) {
const source = ref.install ?? ref.name;
const spinner = input.wizard ? p.spinner() : undefined;
spinner?.start(`Installing plugin ${ref.name}`);
try {
const result = await installPlugin({ source });
spinner?.stop(`Installed plugin ${ref.name}`);
if (!input.wizard) {
input.io?.writeln(
`Installed plugin ${ref.name} at ${result.installPath}`,
);
}
installed.push(ref.name);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
spinner?.stop(`Failed to install plugin ${ref.name}: ${message}`);
if (!input.wizard) {
input.io?.writeErr(`Failed to install plugin ${ref.name}: ${message}`);
}
failed.push(ref.name);
}
}
return { installed, failed };
}
export async function runAgentInstallCommand(
options: AgentInstallOptions,
): Promise<number> {
const json = options.json === true;
const wizard = !json && process.stdout.isTTY === true;
const cwd = options.cwd?.trim() ? resolve(options.cwd) : process.cwd();
const officialAgentsRepo =
options.officialAgentsRepo?.trim() || OFFICIAL_AGENTS_REPO;
try {
if (wizard) {
p.intro("cline agent install");
}
const parsed = parseAgentSource(options.source);
const content = await fetchAgentProfileContent(
parsed,
cwd,
officialAgentsRepo,
);
const { config, installPath } = installAgentProfile({
content,
source: options.source.trim(),
force: options.force,
});
if (wizard) {
p.log.success(`Installed agent profile "${config.name}"`);
p.log.info(`Path: ${installPath}`);
} else if (!json) {
options.io?.writeln(`Installed agent profile "${config.name}"`);
options.io?.writeln(` Path: ${installPath}`);
}
const plan = planAgentPluginInstalls(config.plugins);
const reportLine = (text: string) => {
if (wizard) {
p.log.info(text);
} else if (!json) {
options.io?.writeln(text);
}
};
for (const ref of plan.alreadyInstalled) {
reportLine(`Plugin ${ref.name} is already installed`);
}
for (const ref of plan.manual) {
reportLine(
`Profile references plugin ${ref.name} with no install source; install it manually with: cline plugin install <source>`,
);
}
let installed: string[] = [];
let failed: string[] = [];
let skipped: string[] = [];
if (plan.installable.length > 0) {
// Profile-declared plugin installs run arbitrary code; never install
// them without an explicit confirmation or --yes.
let confirmed = options.yes === true;
if (!confirmed && wizard) {
const lines = plan.installable.map(formatPluginRef).join("\n");
p.note(lines, "This agent profile wants to install plugins");
const answer = await p.confirm({
message: `Install ${plan.installable.length} plugin${plan.installable.length === 1 ? "" : "s"}?`,
});
if (p.isCancel(answer)) {
p.cancel(
`Cancelled. The agent profile is installed at ${installPath}; install its plugins later with cline plugin install.`,
);
return 1;
}
confirmed = answer === true;
}
if (confirmed) {
const result = await installPluginDependencies({
refs: plan.installable,
wizard,
io: options.io,
});
installed = result.installed;
failed = result.failed;
} else {
skipped = plan.installable.map((ref) => ref.name);
const sources = plan.installable
.map((ref) => `cline plugin install ${ref.install ?? ref.name}`)
.join("; ");
reportLine(`Skipped plugin installs. Run manually: ${sources}`);
}
}
if (wizard) {
p.outro(
failed.length > 0
? "Done with errors"
: `Agent "${config.name}" is ready. Switch to it with /agents or --agent ${config.name}.`,
);
}
if (json) {
const result: AgentInstallResult = {
source: options.source.trim(),
name: config.name,
installPath,
plugins: {
alreadyInstalled: plan.alreadyInstalled.map((ref) => ref.name),
installed,
failed,
skipped,
manual: plan.manual.map((ref) => ref.name),
},
};
process.stdout.write(JSON.stringify(result));
}
return failed.length > 0 ? 1 : 0;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
if (wizard) {
p.cancel(message);
} else {
options.io?.writeErr(message);
}
return 1;
}
}
export interface AgentUninstallResult {
name: string;
installPath: string;
}
export function uninstallAgentProfile(name: string): AgentUninstallResult {
const trimmed = name.trim();
if (!trimmed) {
throw new Error("agent uninstall requires a profile name");
}
const agentsDir = resolveAgentsConfigDirPath();
const normalized = trimmed.toLowerCase();
const available: string[] = [];
let entries: string[];
try {
entries = readdirSync(agentsDir);
} catch {
entries = [];
}
for (const entry of entries) {
if (!isAgentConfigFilename(entry)) {
continue;
}
const filePath = join(agentsDir, entry);
let profileName = basename(entry, extname(entry));
try {
profileName = parseConfiguredAgentConfig(
readFileSync(filePath, "utf8"),
).name;
} catch {
// Unparseable file: fall back to matching the filename.
}
available.push(profileName);
if (
profileName.trim().toLowerCase() === normalized ||
basename(entry, extname(entry)).toLowerCase() === normalized
) {
rmSync(filePath);
return { name: profileName, installPath: filePath };
}
}
throw new Error(
available.length > 0
? `Agent profile "${trimmed}" was not found in ${agentsDir} (available: ${available.join(", ")})`
: `Agent profile "${trimmed}" was not found (no agent profiles in ${agentsDir})`,
);
}
export async function runAgentUninstallCommand(options: {
name: string;
json?: boolean;
io?: AgentCommandIo;
}): Promise<number> {
try {
const result = uninstallAgentProfile(options.name);
if (options.json) {
process.stdout.write(JSON.stringify(result));
return 0;
}
options.io?.writeln(`Uninstalled agent profile "${result.name}"`);
options.io?.writeln(` Removed: ${result.installPath}`);
return 0;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
options.io?.writeErr(message);
return 1;
}
}
export async function runAgentListCommand(options: {
cwd?: string;
json?: boolean;
io?: AgentCommandIo;
}): Promise<number> {
const workspaceRoot = resolveWorkspaceRoot(
options.cwd?.trim() ? resolve(options.cwd) : process.cwd(),
);
const { configs, errors } = loadConfiguredAgentConfigs({ workspaceRoot });
if (options.json) {
process.stdout.write(
JSON.stringify({
agents: configs.map((config) => ({
name: config.name,
description: config.description,
path: config.path,
plugins: config.plugins,
})),
errors: errors.map((error) => ({
path: error.path,
message: error.error.message,
})),
}),
);
return 0;
}
if (configs.length === 0 && errors.length === 0) {
options.io?.writeln(
"No agent profiles found. Install one with: cline agent install <source>",
);
return 0;
}
for (const config of configs) {
options.io?.writeln(`${config.name} ${config.description}`);
if (config.path) {
options.io?.writeln(` path: ${config.path}`);
}
if (config.plugins?.length) {
options.io?.writeln(
` plugins: ${config.plugins.map((plugin) => plugin.name).join(", ")}`,
);
}
}
for (const error of errors) {
options.io?.writeErr(
`failed to load ${error.path}: ${error.error.message}`,
);
}
return errors.length > 0 ? 1 : 0;
}
-280
View File
@@ -1,280 +0,0 @@
import { spawn } from "node:child_process";
import { createHash } from "node:crypto";
import { homedir } from "node:os";
import { basename, join } from "node:path";
/**
* Generic helpers shared by the single-source install commands
* (`cline plugin install`, `cline agent install`).
*/
export function resolveHomePath(value: string): string {
if (value === "~") {
return homedir();
}
if (value.startsWith("~/")) {
return join(homedir(), value.slice(2));
}
return value;
}
export function hashSource(source: string): string {
return createHash("sha256").update(source).digest("hex").slice(0, 12);
}
export function sanitizeSegment(value: string, fallback = "plugin"): string {
const sanitized = value
.replace(/^@/, "")
.replace(/[^a-zA-Z0-9._-]+/g, "-")
.replace(/^-+|-+$/g, "")
.slice(0, 80);
return sanitized || fallback;
}
export function isOfficialRegistrySlug(source: string): boolean {
return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(source.trim());
}
export function isLocalPathLike(source: string): boolean {
return (
source.startsWith(".") ||
source.startsWith("/") ||
source === "~" ||
source.startsWith("~/") ||
/^[A-Za-z]:[\\/]|^\\\\/.test(source)
);
}
export async function runCommand(
command: string,
args: string[],
options: { cwd?: string } = {},
): Promise<void> {
await new Promise<void>((resolvePromise, reject) => {
const child = spawn(command, args, {
cwd: options.cwd,
stdio: ["ignore", "ignore", "pipe"],
env: process.env,
// Prevent a console window from flashing on Windows.
windowsHide: true,
});
let stderr = "";
child.stderr.on("data", (chunk) => {
stderr += String(chunk);
});
child.on("error", reject);
child.on("close", (code) => {
if (code === 0) {
resolvePromise();
return;
}
const details = stderr.trim();
reject(
new Error(
`${command} ${args.join(" ")} failed with exit code ${code}${details ? `: ${details}` : ""}`,
),
);
});
});
}
function decodePathSegment(value: string): string {
try {
return decodeURIComponent(value);
} catch {
return value;
}
}
function filenameFromUrlPath(pathname: string, fallback: string): string {
const filename = basename(decodePathSegment(pathname));
return filename || fallback;
}
function isGitHubFilePath(pathname: string): boolean {
const parts = pathname.split("/").filter(Boolean);
return parts.length >= 5 && (parts[2] === "blob" || parts[2] === "raw");
}
export interface NormalizeRemoteSingleFileUrlOptions {
/** Whether the URL's filename has an expected extension for this kind. */
isExpectedFile: (filename: string) => boolean;
/** Short noun for error messages, e.g. "plugin" or "agent profile". */
kind: string;
/** Human label of accepted extensions, e.g. ".js or .ts". */
extensionsLabel: string;
/** Fallback filename when the URL path has none. */
fallbackFilename: string;
}
/**
* Validates an https single-file URL, rewriting GitHub blob/raw page URLs to
* raw.githubusercontent.com. Returns null when the source is not a candidate
* file URL for this kind; throws when it is but violates a constraint.
*/
export function normalizeRemoteSingleFileUrl(
source: string,
options: NormalizeRemoteSingleFileUrlOptions,
): { url: string; filename: string } | null {
if (!/^https?:\/\//i.test(source)) {
return null;
}
let parsed: URL;
try {
parsed = new URL(source);
} catch {
return null;
}
const host = parsed.hostname.toLowerCase();
const filename = filenameFromUrlPath(
parsed.pathname,
options.fallbackFilename,
);
const isExpectedFile = options.isExpectedFile(filename);
const isGitHubFile =
(host === "github.com" || host === "www.github.com") &&
isGitHubFilePath(parsed.pathname);
if (parsed.protocol !== "https:") {
if (
isGitHubFile ||
host === "raw.githubusercontent.com" ||
isExpectedFile
) {
throw new Error(
`Remote ${options.kind} file URLs must use https: ${source}`,
);
}
return null;
}
if (host === "github.com" || host === "www.github.com") {
const parts = parsed.pathname.split("/").filter(Boolean);
if (!isGitHubFile) {
return null;
}
if (!isExpectedFile) {
throw new Error(
`Remote ${options.kind} file must be ${options.extensionsLabel}: ${source}`,
);
}
const rawParts = [parts[0], parts[1], ...parts.slice(3)];
return {
url: `https://raw.githubusercontent.com/${rawParts.join("/")}`,
filename,
};
}
if (host === "raw.githubusercontent.com") {
if (!isExpectedFile) {
throw new Error(
`Remote ${options.kind} file must be ${options.extensionsLabel}: ${source}`,
);
}
return { url: parsed.toString(), filename };
}
if (!isExpectedFile) {
return null;
}
return { url: parsed.toString(), filename };
}
export interface DownloadRemoteFileOptions {
timeoutMs: number;
maxBytes: number;
/** Short noun for error messages, e.g. "plugin" or "agent profile". */
kind: string;
}
function sizeLimitError(
url: string,
options: DownloadRemoteFileOptions,
): Error {
return new Error(
`Remote ${options.kind} file from ${url} exceeds the ${options.maxBytes} byte limit`,
);
}
function getContentLength(response: Response): number | undefined {
const raw = response.headers.get("content-length");
if (!raw) {
return undefined;
}
const value = Number(raw);
if (!Number.isFinite(value) || value < 0) {
return undefined;
}
return value;
}
async function readRemoteBody(
response: Response,
url: string,
options: DownloadRemoteFileOptions,
): Promise<Buffer> {
const contentLength = getContentLength(response);
if (contentLength !== undefined && contentLength > options.maxBytes) {
throw sizeLimitError(url, options);
}
if (!response.body) {
const body = Buffer.from(await response.text(), "utf8");
if (body.byteLength > options.maxBytes) {
throw sizeLimitError(url, options);
}
return body;
}
const reader = response.body.getReader();
const chunks: Buffer[] = [];
let received = 0;
try {
while (true) {
const { done, value } = await reader.read();
if (done) {
break;
}
const chunk = Buffer.from(value);
received += chunk.byteLength;
if (received > options.maxBytes) {
await reader.cancel().catch(() => undefined);
throw sizeLimitError(url, options);
}
chunks.push(chunk);
}
return Buffer.concat(chunks, received);
} finally {
reader.releaseLock();
}
}
export async function downloadRemoteFile(
url: string,
options: DownloadRemoteFileOptions,
): Promise<Buffer> {
const controller = new AbortController();
const timeout = setTimeout(() => {
controller.abort();
}, options.timeoutMs);
try {
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) {
const suffix = response.statusText ? ` ${response.statusText}` : "";
throw new Error(
`Failed to download ${options.kind} file from ${url}: ${response.status}${suffix}`,
);
}
return await readRemoteBody(response, url, options);
} catch (error) {
if (error instanceof Error && error.name === "AbortError") {
throw new Error(
`Timed out downloading ${options.kind} file from ${url} after ${options.timeoutMs}ms`,
);
}
throw error;
} finally {
clearTimeout(timeout);
}
}
-255
View File
@@ -1,255 +0,0 @@
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
autoUpdateOnStartup,
checkForUpdates,
getInstallationInfo,
PackageManager,
resolveCliHubOwnerContext,
withMinimumReleaseAgeBypass,
} from "./update";
const originalArgv = [...process.argv];
const originalBuildEnv = process.env.CLINE_BUILD_ENV;
const originalDataDir = process.env.CLINE_DATA_DIR;
const originalHubDiscoveryPath = process.env.CLINE_HUB_DISCOVERY_PATH;
const originalWrapperPath = process.env.CLINE_WRAPPER_PATH;
const originalGlobalSettingsPath = process.env.CLINE_GLOBAL_SETTINGS_PATH;
const originalIsDev = process.env.IS_DEV;
const originalNoAutoUpdate = process.env.CLINE_NO_AUTO_UPDATE;
const tempDirs: string[] = [];
function createFile(path: string): string {
mkdirSync(dirname(path), { recursive: true });
writeFileSync(path, "");
return path;
}
function createTempFile(pathSuffix: string): string {
const root = mkdtempSync(join(tmpdir(), "cline-update-test-"));
tempDirs.push(root);
return createFile(join(root, pathSuffix));
}
describe("getInstallationInfo", () => {
afterEach(() => {
process.argv = [...originalArgv];
if (originalBuildEnv === undefined) {
delete process.env.CLINE_BUILD_ENV;
} else {
process.env.CLINE_BUILD_ENV = originalBuildEnv;
}
if (originalDataDir === undefined) {
delete process.env.CLINE_DATA_DIR;
} else {
process.env.CLINE_DATA_DIR = originalDataDir;
}
if (originalHubDiscoveryPath === undefined) {
delete process.env.CLINE_HUB_DISCOVERY_PATH;
} else {
process.env.CLINE_HUB_DISCOVERY_PATH = originalHubDiscoveryPath;
}
if (originalWrapperPath === undefined) {
delete process.env.CLINE_WRAPPER_PATH;
} else {
process.env.CLINE_WRAPPER_PATH = originalWrapperPath;
}
if (originalGlobalSettingsPath === undefined) {
delete process.env.CLINE_GLOBAL_SETTINGS_PATH;
} else {
process.env.CLINE_GLOBAL_SETTINGS_PATH = originalGlobalSettingsPath;
}
if (originalIsDev === undefined) {
delete process.env.IS_DEV;
} else {
process.env.IS_DEV = originalIsDev;
}
if (originalNoAutoUpdate === undefined) {
delete process.env.CLINE_NO_AUTO_UPDATE;
} else {
process.env.CLINE_NO_AUTO_UPDATE = originalNoAutoUpdate;
}
vi.restoreAllMocks();
for (const dir of tempDirs.splice(0)) {
rmSync(dir, { recursive: true, force: true });
}
});
it("detects npm installs from the wrapper path passed to the compiled binary", () => {
const wrapperPath = createTempFile("lib/node_modules/cline/bin/cline");
process.env.CLINE_WRAPPER_PATH = wrapperPath;
process.argv = ["bun", "/$bunfs/root/cline", "update", "--verbose"];
expect(getInstallationInfo("1.2.3")).toEqual({
packageManager: PackageManager.NPM,
packageName: "cline",
updateCommand: "npm update -g cline --tag latest",
});
});
it("uses the nightly tag when the current CLI version is nightly", () => {
const wrapperPath = createTempFile("lib/node_modules/cline/bin/cline");
process.env.CLINE_WRAPPER_PATH = wrapperPath;
process.argv = ["bun", "/$bunfs/root/cline", "update", "--verbose"];
expect(getInstallationInfo("1.2.3-nightly.456")).toEqual({
packageManager: PackageManager.NPM,
packageName: "cline",
updateCommand: "npm update -g cline --tag nightly",
});
});
it("falls back to unknown when only Bun's virtual compiled path is available", () => {
delete process.env.CLINE_WRAPPER_PATH;
process.argv = ["bun", "/$bunfs/root/cline", "update", "--verbose"];
expect(getInstallationInfo("1.2.3")).toEqual({
packageManager: PackageManager.UNKNOWN,
packageName: "cline",
});
});
});
describe("auto update settings", () => {
afterEach(() => {
process.argv = [...originalArgv];
if (originalBuildEnv === undefined) {
delete process.env.CLINE_BUILD_ENV;
} else {
process.env.CLINE_BUILD_ENV = originalBuildEnv;
}
if (originalDataDir === undefined) {
delete process.env.CLINE_DATA_DIR;
} else {
process.env.CLINE_DATA_DIR = originalDataDir;
}
if (originalHubDiscoveryPath === undefined) {
delete process.env.CLINE_HUB_DISCOVERY_PATH;
} else {
process.env.CLINE_HUB_DISCOVERY_PATH = originalHubDiscoveryPath;
}
if (originalWrapperPath === undefined) {
delete process.env.CLINE_WRAPPER_PATH;
} else {
process.env.CLINE_WRAPPER_PATH = originalWrapperPath;
}
if (originalGlobalSettingsPath === undefined) {
delete process.env.CLINE_GLOBAL_SETTINGS_PATH;
} else {
process.env.CLINE_GLOBAL_SETTINGS_PATH = originalGlobalSettingsPath;
}
if (originalIsDev === undefined) {
delete process.env.IS_DEV;
} else {
process.env.IS_DEV = originalIsDev;
}
if (originalNoAutoUpdate === undefined) {
delete process.env.CLINE_NO_AUTO_UPDATE;
} else {
process.env.CLINE_NO_AUTO_UPDATE = originalNoAutoUpdate;
}
vi.restoreAllMocks();
for (const dir of tempDirs.splice(0)) {
rmSync(dir, { recursive: true, force: true });
}
});
it("skips startup auto update when disabled globally", () => {
const settingsPath = createTempFile("data/global-settings.json");
writeFileSync(settingsPath, JSON.stringify({ autoUpdateEnabled: false }));
process.env.CLINE_GLOBAL_SETTINGS_PATH = settingsPath;
delete process.env.IS_DEV;
delete process.env.CLINE_NO_AUTO_UPDATE;
const fetchSpy = vi
.spyOn(globalThis, "fetch")
.mockRejectedValue(new Error("should not fetch"));
autoUpdateOnStartup();
expect(fetchSpy).not.toHaveBeenCalled();
});
it("still lets manual update checks run when startup auto update is disabled", async () => {
const settingsPath = createTempFile("data/global-settings.json");
writeFileSync(settingsPath, JSON.stringify({ autoUpdateEnabled: false }));
process.env.CLINE_GLOBAL_SETTINGS_PATH = settingsPath;
delete process.env.CLINE_NO_AUTO_UPDATE;
const fetchSpy = vi.spyOn(globalThis, "fetch").mockResolvedValue({
ok: true,
json: async () => ({ version: "0.0.0" }),
} as Response);
await checkForUpdates({ includeKanban: false });
expect(fetchSpy).toHaveBeenCalled();
});
});
describe("hub restart owner selection", () => {
afterEach(() => {
if (originalBuildEnv === undefined) {
delete process.env.CLINE_BUILD_ENV;
} else {
process.env.CLINE_BUILD_ENV = originalBuildEnv;
}
if (originalDataDir === undefined) {
delete process.env.CLINE_DATA_DIR;
} else {
process.env.CLINE_DATA_DIR = originalDataDir;
}
if (originalHubDiscoveryPath === undefined) {
delete process.env.CLINE_HUB_DISCOVERY_PATH;
} else {
process.env.CLINE_HUB_DISCOVERY_PATH = originalHubDiscoveryPath;
}
});
it("uses the shared hub owner outside production builds", () => {
process.env.CLINE_BUILD_ENV = "development";
process.env.CLINE_DATA_DIR = "/tmp/cline-update-test-data";
delete process.env.CLINE_HUB_DISCOVERY_PATH;
const owner = resolveCliHubOwnerContext();
expect(owner.discoveryPath).toContain("/locks/hub/owners/");
expect(owner.discoveryPath).not.toBe(
"/tmp/cline-update-test-data/locks/hub/production.json",
);
});
});
describe("withMinimumReleaseAgeBypass", () => {
it("adds the package-manager-specific cooldown bypass", () => {
expect(
withMinimumReleaseAgeBypass(
"npm update -g cline --tag latest",
PackageManager.NPM,
).command,
).toBe("npm update -g cline --tag latest --min-release-age=0");
expect(
withMinimumReleaseAgeBypass("bun add -g cline@latest", PackageManager.BUN)
.command,
).toBe("bun add -g cline@latest --minimum-release-age=0");
expect(
withMinimumReleaseAgeBypass(
"yarn global add cline@latest",
PackageManager.YARN,
).command,
).toBe("yarn global add cline@latest");
expect(
withMinimumReleaseAgeBypass(
"yarn global add cline@latest",
PackageManager.YARN,
).env?.YARN_NPM_MINIMAL_AGE_GATE,
).toBe("0");
expect(
withMinimumReleaseAgeBypass(
"pnpm add -g cline@latest",
PackageManager.PNPM,
).env?.pnpm_config_minimum_release_age,
).toBe("0");
});
});
@@ -1,171 +0,0 @@
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { setHomeDir } from "@cline/shared/storage";
import { afterEach, describe, expect, it } from "vitest";
import { resolveAgentProfileDisabledPluginPaths } from "./agent-profile-plugins";
describe("resolveAgentProfileDisabledPluginPaths", () => {
const envSnapshot = {
HOME: process.env.HOME,
CLINE_GLOBAL_SETTINGS_PATH: process.env.CLINE_GLOBAL_SETTINGS_PATH,
};
afterEach(() => {
process.env.HOME = envSnapshot.HOME;
process.env.CLINE_GLOBAL_SETTINGS_PATH =
envSnapshot.CLINE_GLOBAL_SETTINGS_PATH;
setHomeDir(envSnapshot.HOME ?? "~");
});
async function setUpFixture(): Promise<{
root: string;
home: string;
workspace: string;
listedPlugin: string;
unlistedPlugin: string;
alwaysEnabledPlugin: string;
}> {
// Nested under a fixture root so the display-name package.json walk
// never escapes into the shared temp directory.
const root = await mkdtemp(join(tmpdir(), "cli-profile-plugins-"));
const home = join(root, "home");
const workspace = join(root, "workspace");
await mkdir(home, { recursive: true });
await mkdir(workspace, { recursive: true });
process.env.HOME = home;
setHomeDir(home);
process.env.CLINE_GLOBAL_SETTINGS_PATH = join(home, "global-settings.json");
const workspacePlugins = join(workspace, ".cline", "plugins");
const userPlugins = join(home, ".cline", "plugins");
await mkdir(workspacePlugins, { recursive: true });
await mkdir(userPlugins, { recursive: true });
const listedPlugin = join(workspacePlugins, "listed-plugin.js");
const unlistedPlugin = join(workspacePlugins, "unlisted-plugin.js");
const alwaysEnabledPlugin = join(userPlugins, "always-on.js");
await writeFile(listedPlugin, "export default {}", "utf8");
await writeFile(unlistedPlugin, "export default {}", "utf8");
await writeFile(alwaysEnabledPlugin, "export default {}", "utf8");
return {
root,
home,
workspace,
listedPlugin,
unlistedPlugin,
alwaysEnabledPlugin,
};
}
it("returns undefined when the profile has no plugins field", async () => {
const fixture = await setUpFixture();
try {
expect(
resolveAgentProfileDisabledPluginPaths(undefined, fixture.workspace),
).toBeUndefined();
expect(
resolveAgentProfileDisabledPluginPaths({}, fixture.workspace),
).toBeUndefined();
} finally {
await rm(fixture.root, { recursive: true, force: true });
}
});
it("disables installed plugins not listed in the profile", async () => {
const fixture = await setUpFixture();
try {
const disabled = resolveAgentProfileDisabledPluginPaths(
{ plugins: ["Listed-Plugin"] },
fixture.workspace,
);
expect(disabled).toContain(fixture.unlistedPlugin);
expect(disabled).toContain(fixture.alwaysEnabledPlugin);
expect(disabled).not.toContain(fixture.listedPlugin);
} finally {
await rm(fixture.root, { recursive: true, force: true });
}
});
it("exempts always-enabled plugins from profile disabling", async () => {
const fixture = await setUpFixture();
try {
await writeFile(
process.env.CLINE_GLOBAL_SETTINGS_PATH ?? "",
JSON.stringify({
alwaysEnabledPlugins: [fixture.alwaysEnabledPlugin],
}),
"utf8",
);
const disabled = resolveAgentProfileDisabledPluginPaths(
{ plugins: ["listed-plugin"] },
fixture.workspace,
);
expect(disabled).toContain(fixture.unlistedPlugin);
expect(disabled).not.toContain(fixture.alwaysEnabledPlugin);
expect(disabled).not.toContain(fixture.listedPlugin);
} finally {
await rm(fixture.root, { recursive: true, force: true });
}
});
it("disables everything but always-enabled plugins for an empty list", async () => {
const fixture = await setUpFixture();
try {
await writeFile(
process.env.CLINE_GLOBAL_SETTINGS_PATH ?? "",
JSON.stringify({
alwaysEnabledPlugins: [fixture.alwaysEnabledPlugin],
}),
"utf8",
);
const disabled = resolveAgentProfileDisabledPluginPaths(
{ plugins: [] },
fixture.workspace,
);
expect(disabled).toContain(fixture.listedPlugin);
expect(disabled).toContain(fixture.unlistedPlugin);
expect(disabled).not.toContain(fixture.alwaysEnabledPlugin);
} finally {
await rm(fixture.root, { recursive: true, force: true });
}
});
it("matches names resolved from an install wrapper package.json", async () => {
const fixture = await setUpFixture();
try {
const installRoot = join(
fixture.home,
".cline",
"plugins",
"_installed",
"registry",
"branch-protector-abc123",
);
const packageRoot = join(installRoot, "package");
await mkdir(packageRoot, { recursive: true });
await writeFile(
join(installRoot, "package.json"),
JSON.stringify({
name: "branch-protector",
private: true,
cline: { plugins: [{ paths: ["./package/index.ts"] }] },
}),
"utf8",
);
const wrappedEntry = join(packageRoot, "index.ts");
await writeFile(wrappedEntry, "export default {}", "utf8");
const disabled = resolveAgentProfileDisabledPluginPaths(
{ plugins: ["branch-protector"] },
fixture.workspace,
);
expect(disabled).not.toContain(wrappedEntry);
expect(disabled).toContain(fixture.listedPlugin);
} finally {
await rm(fixture.root, { recursive: true, force: true });
}
});
});
@@ -1,57 +0,0 @@
import {
discoverPluginModulePaths,
resolveAlwaysEnabledPluginPaths,
resolvePluginConfigSearchPaths,
} from "@cline/core";
import { getPluginDisplayName } from "@cline/shared/storage";
import type { ActiveAgentProfile } from "../utils/types";
/**
* Computes the session-scoped plugin disable list for an agent profile's
* plugins restriction: every installed plugin whose display name is not in
* the profile's list and is not marked always-enabled in global settings.
* Returns undefined when the profile has no plugins field (no restriction).
* Names listed in the profile that match no installed plugin are silently
* ignored. Recomputed on every session (re)start so plugin installs and
* always-enabled toggles apply on the next restart.
*/
export function resolveAgentProfileDisabledPluginPaths(
profile: Pick<ActiveAgentProfile, "plugins"> | undefined,
workspaceRoot: string | undefined,
): string[] | undefined {
const pluginNames = profile?.plugins;
if (!pluginNames) {
return undefined;
}
const allowedNames = new Set(
pluginNames.map((name) => name.trim().toLowerCase()).filter(Boolean),
);
const alwaysEnabled = resolveAlwaysEnabledPluginPaths();
const disabled = new Set<string>();
for (const directory of resolvePluginConfigSearchPaths(workspaceRoot)) {
let pluginPaths: string[];
try {
pluginPaths = discoverPluginModulePaths(directory);
} catch {
continue;
}
for (const pluginPath of pluginPaths) {
if (alwaysEnabled.has(pluginPath)) {
continue;
}
let displayName: string;
try {
displayName = getPluginDisplayName(pluginPath);
} catch {
// Unresolvable name cannot match the allowlist; disable it.
disabled.add(pluginPath);
continue;
}
if (allowedNames.has(displayName.toLowerCase())) {
continue;
}
disabled.add(pluginPath);
}
}
return [...disabled];
}
@@ -1,110 +0,0 @@
import { basename } from "node:path";
import type { ChoiceContext } from "@opentui-ui/dialog";
import { useDialogKeyboard } from "@opentui-ui/dialog/react";
import { useMemo } from "react";
import {
type SearchableItem,
SearchableList,
useSearchableList,
} from "../searchable-list";
/** Sentinel resolved when the user picks the default Cline agent. */
export const DEFAULT_AGENT_ACTION = "__default_agent__";
export interface AgentProfileOption {
name: string;
description?: string;
systemPrompt: string;
plugins?: string[];
source: "workspace" | "global";
}
export interface AgentProfileLoadError {
path: string;
message: string;
}
export function AgentSelectorContent(
props: ChoiceContext<string> & {
currentAgentName: string | null;
agents: AgentProfileOption[];
loadErrors: AgentProfileLoadError[];
},
) {
const { resolve, dismiss, dialogId, currentAgentName, agents, loadErrors } =
props;
const items: SearchableItem[] = useMemo(() => {
const normalizedCurrent = currentAgentName?.trim().toLowerCase() ?? null;
const defaultItem: SearchableItem = {
key: DEFAULT_AGENT_ACTION,
label: "Cline (default)",
detail: "Standard Cline agent",
section: "Agents",
rightLabel: normalizedCurrent === null ? "(current)" : undefined,
};
const profileItems = agents.map((agent) => ({
key: agent.name.toLowerCase(),
label: agent.name,
detail: agent.description,
section:
agent.source === "workspace" ? "Workspace agents" : "Global agents",
rightLabel:
normalizedCurrent === agent.name.trim().toLowerCase()
? "(current)"
: undefined,
}));
return [defaultItem, ...profileItems];
}, [agents, currentAgentName]);
const list = useSearchableList(items);
useDialogKeyboard((key) => {
if (key.name === "escape") {
dismiss();
return;
}
if (key.name === "return") {
const item = list.selectedItem;
if (item) resolve(item.key);
return;
}
if (key.name === "up") {
list.moveUp();
return;
}
if (key.name === "down") {
list.moveDown();
}
}, dialogId);
return (
<box flexDirection="column" gap={1}>
<text>Select Agent</text>
<SearchableList
items={list.filtered}
selected={list.safeSelected}
placeholder="Search agents..."
onSearchChange={list.setSearch}
onItemSelect={(item) => resolve(item.key)}
emptyText="No agents match"
detailPosition="below"
/>
{loadErrors.length > 0 && (
<box flexDirection="column">
{loadErrors.map((error) => (
<text key={error.path} fg="red">
{basename(error.path)}: {error.message}
</text>
))}
</box>
)}
<text fg="gray">
Type to search, / navigate, Enter to select, Esc to go back
</text>
</box>
);
}
@@ -1,121 +0,0 @@
import { sep } from "node:path";
import { loadConfiguredAgentConfigs } from "@cline/core";
import type { ChoiceContext } from "@opentui-ui/dialog";
import type { DialogActions } from "@opentui-ui/dialog/react";
import { useCallback } from "react";
import type { Config } from "../../utils/types";
import {
type AgentProfileLoadError,
type AgentProfileOption,
AgentSelectorContent,
DEFAULT_AGENT_ACTION,
} from "../components/dialogs/agent-selector";
import { withLoadingDialog } from "../components/dialogs/loading-dialog";
import { useSession } from "../contexts/session-context";
import type { TuiProps } from "../types";
function loadAgentProfileEntries(config: Config): {
agents: AgentProfileOption[];
loadErrors: AgentProfileLoadError[];
} {
const workspaceRoot = config.workspaceRoot?.trim() || config.cwd;
const { configs, errors } = loadConfiguredAgentConfigs({ workspaceRoot });
return {
agents: configs.map((profile) => ({
name: profile.name,
description: profile.description,
systemPrompt: profile.systemPrompt,
plugins: profile.plugins?.map((plugin) => plugin.name),
source:
workspaceRoot && profile.path?.startsWith(`${workspaceRoot}${sep}`)
? ("workspace" as const)
: ("global" as const),
})),
loadErrors: errors.map((error) => ({
path: error.path,
message: error.error.message,
})),
};
}
export function useAgentSelector(opts: {
dialog: DialogActions;
config: Config;
termHeight: number;
onAgentProfileChange: TuiProps["onAgentProfileChange"];
refocusTextarea: () => void;
}): () => Promise<void> {
const { dialog, config, termHeight, onAgentProfileChange, refocusTextarea } =
opts;
const session = useSession();
const openAgentSelector = useCallback(async () => {
// Applying a profile restarts the session in place, so refuse while a
// turn is running instead of yanking the live stream out from under it.
if (session.isRunning) {
session.appendEntry({
kind: "status",
text: "Finish or abort the current task before switching agents.",
});
refocusTextarea();
return;
}
const { agents, loadErrors } = loadAgentProfileEntries(config);
const currentAgentName = config.agentProfile?.name ?? null;
const selectedKey = await dialog.choice<string>({
style: { maxHeight: termHeight - 2 },
content: (ctx: ChoiceContext<string>) => (
<AgentSelectorContent
{...ctx}
currentAgentName={currentAgentName}
agents={agents}
loadErrors={loadErrors}
/>
),
});
if (!selectedKey) {
refocusTextarea();
return;
}
if (selectedKey === DEFAULT_AGENT_ACTION) {
if (config.agentProfile) {
await withLoadingDialog(dialog, "Applying agent...", async () => {
await onAgentProfileChange(null);
});
session.setActiveAgentName(null);
}
refocusTextarea();
return;
}
const profile = agents.find(
(agent) => agent.name.toLowerCase() === selectedKey,
);
if (!profile) {
refocusTextarea();
return;
}
if (profile.name !== currentAgentName) {
await withLoadingDialog(dialog, "Applying agent...", async () => {
await onAgentProfileChange({
name: profile.name,
systemPrompt: profile.systemPrompt,
plugins: profile.plugins,
});
});
session.setActiveAgentName(profile.name);
}
refocusTextarea();
}, [
dialog,
config,
termHeight,
onAgentProfileChange,
refocusTextarea,
session,
]);
return openAgentSelector;
}
@@ -1,81 +0,0 @@
import { describe, expect, it } from "vitest";
import { PLATFORMS, shouldIncludeField } from "./platforms";
describe("connect wizard platform security fields", () => {
it("does not ask Telegram users to re-enter the bot username", () => {
const telegram = PLATFORMS.find((platform) => platform.id === "telegram");
expect(telegram?.fields.map((field) => field.label)).toEqual(["Bot token"]);
expect(telegram?.fields.map((field) => field.flag)).toEqual(["-k"]);
});
it("rejects unsafe Telegram and Slack access restriction identifiers", () => {
const telegram = PLATFORMS.find((platform) => platform.id === "telegram");
const slack = PLATFORMS.find((platform) => platform.id === "slack");
const telegramUser = telegram?.security?.fields.find(
(field) => field.key === "userId",
);
const slackTeam = slack?.security?.fields.find(
(field) => field.key === "teamId",
);
const slackUser = slack?.security?.fields.find(
(field) => field.key === "userId",
);
expect(telegramUser?.validate?.("123456")).toBeUndefined();
expect(telegramUser?.validate?.("123; rm -rf /")).toContain("digits");
expect(slackTeam?.validate?.("T01ABC123")).toBeUndefined();
expect(slackTeam?.validate?.("T01;bad")).toContain("Slack workspace");
expect(slackUser?.validate?.("U01ABC123")).toBeUndefined();
expect(slackUser?.validate?.("U01$(bad)")).toContain("Slack member");
});
it("uses the Telegram allowed user ID flag for wizard security", () => {
const telegram = PLATFORMS.find((platform) => platform.id === "telegram");
const args = telegram?.security?.buildArgs({
userId: "123456",
});
expect(args).toEqual(["--allowed-user-id", "123456"]);
});
it("builds an exact-match Slack authorization hook", () => {
const slack = PLATFORMS.find((platform) => platform.id === "slack");
const args = slack?.security?.buildArgs({
teamId: "T01ABC123",
userId: "U01ABC123",
});
expect(args).toEqual([
"--hook-command",
`jq -r ".payload.actor.participantKey" | grep -qx "slack:team:T01ABC123:user:U01ABC123" && echo '{"action":"allow"}' || echo '{"action":"deny"}'`,
]);
});
it("asks Slack users for mode-specific setup fields", () => {
const slack = PLATFORMS.find((platform) => platform.id === "slack");
const fields = slack?.fields ?? [];
const webhookValues = { "--base-url": "https://example.test" };
const socketValues = { "--base-url": "" };
expect(fields.map((field) => field.flag)).toEqual([
"--bot-token",
"--base-url",
"--signing-secret",
"--app-token",
]);
expect(
fields
.filter((field) => shouldIncludeField(field, webhookValues))
.map((field) => field.flag),
).toEqual(["--bot-token", "--base-url", "--signing-secret"]);
expect(
fields
.filter((field) => shouldIncludeField(field, socketValues))
.map((field) => field.flag),
).toEqual(["--bot-token", "--base-url", "--app-token"]);
});
});
-21
View File
@@ -1,21 +0,0 @@
{
"extends": "../../sdk/packages/tsconfig.base.json",
"compilerOptions": {
"noEmit": true,
"types": ["bun", "node"],
"paths": {
"@cline/core": ["../../sdk/packages/core/src/index.ts"],
"@cline/core/*": [
"../../sdk/packages/core/src/*",
"../../sdk/packages/core/src/*/index.ts"
],
"@cline/shared": ["../../sdk/packages/shared/src/index.ts"],
"@cline/shared/*": [
"../../sdk/packages/shared/src/*",
"../../sdk/packages/shared/src/*/index.ts"
]
}
},
"include": ["src/**/*.ts"],
"exclude": ["src/webview/**"]
}
@@ -1,23 +0,0 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"paths": {
"@/*": ["./*"],
"@cline/agents": ["../../../sdk/packages/agents/src/index.ts"],
"@cline/core": ["../../../sdk/packages/core/src/index.ts"],
"@cline/core/hub": ["../../../sdk/packages/core/src/hub/index.ts"],
"@cline/llms": ["../../../sdk/packages/llms/src/index.ts"],
"@cline/shared": ["../../../sdk/packages/shared/src/index.ts"],
"@cline/shared/storage": [
"../../../sdk/packages/shared/src/storage/index.ts"
],
"@cline/shared/db": ["../../../sdk/packages/shared/src/db/index.ts"],
"@cline/shared/*": [
"../../../sdk/packages/shared/src/*",
"../../../sdk/packages/shared/src/*/index.ts"
]
}
},
"include": ["sidecar/**/*.ts", "scripts/**/*.ts", "global.d.ts", "bun.mts"],
"exclude": ["node_modules", "webview"]
}
@@ -1,28 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"paths": {
"@/*": ["./*"],
"@cline/agents": ["../../../../sdk/packages/agents/src/index.ts"],
"@cline/core": ["../../../../sdk/packages/core/src/index.ts"],
"@cline/llms": ["../../../../sdk/packages/llms/src/index.ts"],
"@cline/shared": ["../../../../sdk/packages/shared/src/index.ts"],
"@cline/shared/storage": [
"../../../../sdk/packages/shared/src/storage/index.ts"
],
"@cline/shared/db": ["../../../../sdk/packages/shared/src/db/index.ts"],
"@cline/shared/*": [
"../../../../sdk/packages/shared/src/*",
"../../../../sdk/packages/shared/src/*/index.ts"
]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts"
],
"exclude": ["node_modules"]
}
-35
View File
@@ -1,35 +0,0 @@
{
"extends": "../../../sdk/packages/tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"module": "ESNext",
"moduleResolution": "Bundler",
"target": "ESNext",
"paths": {
"@cline/agents": ["../../../sdk/packages/agents/src/index.ts"],
"@cline/agents/*": [
"../../../sdk/packages/agents/src/*",
"../../../sdk/packages/agents/src/*/index.ts"
],
"@cline/core": ["../../../sdk/packages/core/src/index.ts"],
"@cline/core/hub/daemon-entry": [
"../../../sdk/packages/core/src/hub/daemon/entry.ts"
],
"@cline/core/*": [
"../../../sdk/packages/core/src/*",
"../../../sdk/packages/core/src/*/index.ts"
],
"@cline/llms": ["../../../sdk/packages/llms/src/index.ts"],
"@cline/shared": ["../../../sdk/packages/shared/src/index.ts"],
"@cline/shared/storage": [
"../../../sdk/packages/shared/src/storage/index.ts"
],
"@cline/shared/db": ["../../../sdk/packages/shared/src/db/index.ts"],
"@cline/shared/*": [
"../../../sdk/packages/shared/src/*",
"../../../sdk/packages/shared/src/*/index.ts"
]
}
},
"include": ["sidecar/**/*.ts"]
}
-31
View File
@@ -1,31 +0,0 @@
{
"compilerOptions": {
"paths": {
"@cline/agents": ["../sdk/packages/agents/src/index.ts"],
"@cline/agents/*": [
"../sdk/packages/agents/src/*",
"../sdk/packages/agents/src/*/index.ts"
],
"@cline/cline-hub": ["./cline-hub/src/server.ts"],
"@cline/core": ["../sdk/packages/core/src/index.ts"],
"@cline/core/hub/daemon-entry": [
"../sdk/packages/core/src/hub/daemon/entry.ts"
],
"@cline/core/*": [
"../sdk/packages/core/src/*",
"../sdk/packages/core/src/*/index.ts"
],
"@cline/core/telemetry": [
"../sdk/packages/core/src/services/telemetry/index.ts"
],
"@cline/llms": ["../sdk/packages/llms/src/index.ts"],
"@cline/shared": ["../sdk/packages/shared/src/index.ts"],
"@cline/shared/storage": ["../sdk/packages/shared/src/storage/index.ts"],
"@cline/shared/db": ["../sdk/packages/shared/src/db/index.ts"],
"@cline/shared/*": [
"../sdk/packages/shared/src/*",
"../sdk/packages/shared/src/*/index.ts"
]
}
}
}
-201
View File
@@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2026 Cline Bot Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+14 -7
View File
@@ -50,8 +50,8 @@
"useEnumInitializers": "off",
"useSelfClosingElements": "info",
"useSingleVarDeclarator": "off",
"useNumberNamespace": "off",
"noInferrableTypes": "off",
"useNumberNamespace": "info",
"noInferrableTypes": "info",
"useTemplate": "info",
"noUselessElse": "info"
},
@@ -122,7 +122,6 @@
"!!**/out",
"!!**/evals",
"!!**/playwright",
"!!**/.vscode-test",
"!!**/test-results",
"!!**/node_modules",
"!!**/webview-ui/build",
@@ -131,7 +130,9 @@
"!!**/tests/specs"
]
},
"plugins": ["src/dev/grit/process-env.grit"],
"plugins": [
"src/dev/grit/process-env.grit"
],
"overrides": [
{
"includes": [
@@ -146,11 +147,15 @@
"!!src/integrations/terminal/**",
"!!src/core/controller/ui/openWalkthrough.ts"
],
"plugins": ["src/dev/grit/vscode-api.grit"]
"plugins": [
"src/dev/grit/vscode-api.grit"
]
},
{
// Do not use console logging directly, use the Logger service instead.
"plugins": ["src/dev/grit/console-log.grit"],
"plugins": [
"src/dev/grit/console-log.grit"
],
"includes": [
"**",
"!!**/esbuild.*",
@@ -183,7 +188,9 @@
"!!src/core/storage/utils/state-helpers.ts",
"!!src/extension.ts"
],
"plugins": ["src/dev/grit/use-cache-service.grit"]
"plugins": [
"src/dev/grit/use-cache-service.grit"
]
}
]
}
+12 -5
View File
@@ -1,13 +1,16 @@
{
"name": "claude-dev",
"version": "3.89.0",
"version": "3.86.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "claude-dev",
"version": "3.89.0",
"version": "3.86.2",
"license": "Apache-2.0",
"workspaces": [
"."
],
"dependencies": {
"@anthropic-ai/sdk": "^0.37.0",
"@anthropic-ai/vertex-sdk": "^0.6.4",
@@ -9379,6 +9382,10 @@
"integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==",
"license": "MIT"
},
"node_modules/claude-dev": {
"resolved": "",
"link": true
},
"node_modules/clean-stack": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
@@ -18543,9 +18550,9 @@
}
},
"node_modules/shell-quote": {
"version": "1.8.4",
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz",
"integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==",
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
"integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
+10 -7
View File
@@ -2,8 +2,11 @@
"name": "claude-dev",
"displayName": "Cline",
"description": "Autonomous coding agent right in your IDE, capable of creating/editing files, running commands, using the browser, and more with your permission every step of the way.",
"version": "3.89.0",
"version": "3.86.2",
"icon": "assets/icons/icon.png",
"workspaces": [
"."
],
"engines": {
"vscode": "^1.84.0"
},
@@ -389,18 +392,18 @@
"protos": "node scripts/build-proto.mjs",
"protos-python": "node scripts/build-python-proto.mjs",
"download-ripgrep": "node scripts/download-ripgrep.mjs",
"postprotos": "biome format --config-path ./biome.jsonc src/shared/proto src/core/controller src/hosts/ webview-ui/src/services src/generated --write --no-errors-on-unmatched",
"postprotos": "biome format src/shared/proto src/core/controller src/hosts/ webview-ui/src/services src/generated --write --no-errors-on-unmatched",
"clean:build": "rimraf dist dist-standalone webview-ui/build src/generated out/",
"clean:deps": "rimraf node_modules webview-ui/node_modules",
"clean:all": "npm run clean:build && npm run clean:deps",
"compile-tests": "node ./scripts/build-tests.js",
"watch-tests": "tsc -p . -w --outDir out",
"check-types": "npm run protos && tsc --noEmit && cd webview-ui && tsc --noEmit",
"lint": "biome lint --config-path ./biome.jsonc --no-errors-on-unmatched --files-ignore-unknown=true --diagnostic-level=error && npm run lint:proto",
"check-types": "npm run protos && npx tsc --noEmit && cd webview-ui && npx tsc --noEmit",
"lint": "biome lint --no-errors-on-unmatched --files-ignore-unknown=true --diagnostic-level=error && npm run lint:proto",
"lint:proto": "bash ./scripts/proto-lint.sh",
"format": "biome format --config-path ./biome.jsonc --changed --since main --no-errors-on-unmatched --files-ignore-unknown=true --diagnostic-level=error",
"format:fix": "biome check --config-path ./biome.jsonc --changed --since main --no-errors-on-unmatched --files-ignore-unknown=true --write",
"fix:all": "biome check --config-path ./biome.jsonc --no-errors-on-unmatched --files-ignore-unknown=true --write --diagnostic-level=error --unsafe",
"format": "biome format --changed --no-errors-on-unmatched --files-ignore-unknown=true --diagnostic-level=error",
"format:fix": "biome check --changed --no-errors-on-unmatched --files-ignore-unknown=true --write",
"fix:all": "biome check --no-errors-on-unmatched --files-ignore-unknown=true --write --diagnostic-level=error --unsafe",
"ci:check-all": "npx npm-run-all -p check-types lint format",
"ci:build": "npm run protos && npm run build:webview && node esbuild.mjs && npm run compile-tests",
"pretest": "npm run compile && npm run compile-tests && npm run compile-standalone && npm run lint",
@@ -87,30 +87,6 @@ describe("AnthropicHandler", () => {
result.id.should.equal("claude-opus-4-8:1m")
result.info.should.deepEqual(anthropicModels["claude-opus-4-8:1m"])
})
it("should return the Fable 5 model when configured", () => {
const handler = new AnthropicHandler({
apiKey: "test-api-key",
apiModelId: "claude-fable-5",
})
const result = handler.getModel()
result.id.should.equal("claude-fable-5")
result.info.should.deepEqual(anthropicModels["claude-fable-5"])
})
it("should return the Fable 5 1m model when configured", () => {
const handler = new AnthropicHandler({
apiKey: "test-api-key",
apiModelId: "claude-fable-5:1m",
})
const result = handler.getModel()
result.id.should.equal("claude-fable-5:1m")
result.info.should.deepEqual(anthropicModels["claude-fable-5:1m"])
})
})
describe("createMessage", () => {
@@ -215,11 +215,6 @@ describe("AwsBedrockHandler", () => {
bedrockModels["anthropic.claude-opus-4-8:1m"].supportsGlobalEndpoint.should.equal(true)
})
it("should mark Bedrock Fable 5 variants as global-endpoint capable", () => {
bedrockModels["anthropic.claude-fable-5"].supportsGlobalEndpoint.should.equal(true)
bedrockModels["anthropic.claude-fable-5:1m"].supportsGlobalEndpoint.should.equal(true)
})
it("should include Vertex Opus 4.7 variants in the derived global model list", () => {
vertexModels["claude-opus-4-7"].supportsGlobalEndpoint.should.equal(true)
vertexModels["claude-opus-4-7:1m"].supportsGlobalEndpoint.should.equal(true)
@@ -233,13 +228,6 @@ describe("AwsBedrockHandler", () => {
vertexGlobalModels.should.have.property("claude-opus-4-8")
vertexGlobalModels.should.have.property("claude-opus-4-8:1m")
})
it("should include Vertex Fable 5 variants in the derived global model list", () => {
vertexModels["claude-fable-5"].supportsGlobalEndpoint.should.equal(true)
vertexModels["claude-fable-5:1m"].supportsGlobalEndpoint.should.equal(true)
vertexGlobalModels.should.have.property("claude-fable-5")
vertexGlobalModels.should.have.property("claude-fable-5:1m")
})
})
const mockOptions: AwsBedrockHandlerOptions = {
@@ -416,26 +416,6 @@ describe("ClaudeCodeHandler", () => {
model.info.contextWindow.should.equal(1_000_000)
})
it("should support Fable 5 model id", () => {
const handler = new ClaudeCodeHandler({
apiModelId: "claude-fable-5",
})
const model = handler.getModel()
model.id.should.equal("claude-fable-5")
model.info.contextWindow.should.equal(200_000)
})
it("should support Fable 5 1m model id", () => {
const handler = new ClaudeCodeHandler({
apiModelId: "claude-fable-5[1m]",
})
const model = handler.getModel()
model.id.should.equal("claude-fable-5[1m]")
model.info.contextWindow.should.equal(1_000_000)
})
it("should support Opus 1m alias model id", () => {
const handler = new ClaudeCodeHandler({
apiModelId: "opus[1m]",
+9 -10
View File
@@ -81,12 +81,11 @@ export class DeepSeekHandler implements ApiHandler {
const client = this.ensureClient()
const model = this.getModel()
const isDeepSeekReasonerModel = model.id.includes("deepseek-reasoner")
const isDeepSeekThinkingModel =
isDeepSeekReasonerModel || model.id === "deepseek-v4-flash" || model.id === "deepseek-v4-pro"
model.id.includes("deepseek-reasoner") || model.id === "deepseek-v4-flash" || model.id === "deepseek-v4-pro"
const convertedMessages = convertToOpenAiMessages(messages)
const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = isDeepSeekReasonerModel
const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = isDeepSeekThinkingModel
? [{ role: "system", content: systemPrompt }, ...addReasoningContent(convertedMessages, messages)]
: [{ role: "system", content: systemPrompt }, ...convertedMessages]
@@ -105,13 +104,6 @@ export class DeepSeekHandler implements ApiHandler {
for await (const chunk of stream) {
const delta = chunk.choices?.[0]?.delta
if (delta && "reasoning_content" in delta && delta.reasoning_content) {
yield {
type: "reasoning",
reasoning: (delta.reasoning_content as string | undefined) || "",
}
}
if (delta?.content) {
yield {
type: "text",
@@ -123,6 +115,13 @@ export class DeepSeekHandler implements ApiHandler {
yield* toolCallProcessor.processToolCallDeltas(delta.tool_calls)
}
if (delta && "reasoning_content" in delta && delta.reasoning_content) {
yield {
type: "reasoning",
reasoning: (delta.reasoning_content as string | undefined) || "",
}
}
if (chunk.usage) {
yield* this.yieldUsage(model.info, chunk.usage)
}
@@ -3,7 +3,6 @@ import {
CLAUDE_SONNET_1M_SUFFIX,
ModelInfo,
OPENROUTER_PROVIDER_PREFERENCES,
openRouterClaudeFable51mModelId,
openRouterClaudeOpus461mModelId,
openRouterClaudeOpus471mModelId,
openRouterClaudeOpus481mModelId,
@@ -64,8 +63,7 @@ export async function createOpenRouterStream(
model.id === openRouterClaudeSonnet461mModelId ||
model.id === openRouterClaudeOpus461mModelId ||
model.id === openRouterClaudeOpus471mModelId ||
model.id === openRouterClaudeOpus481mModelId ||
model.id === openRouterClaudeFable51mModelId
model.id === openRouterClaudeOpus481mModelId
if (isClaude1m) {
// remove the custom :1m suffix, to create the model id openrouter API expects
model.id = model.id.slice(0, -CLAUDE_SONNET_1M_SUFFIX.length)
@@ -2,7 +2,6 @@ import { Anthropic } from "@anthropic-ai/sdk"
import {
CLAUDE_SONNET_1M_SUFFIX,
ModelInfo,
openRouterClaudeFable51mModelId,
openRouterClaudeOpus461mModelId,
openRouterClaudeOpus471mModelId,
openRouterClaudeOpus481mModelId,
@@ -40,8 +39,7 @@ export async function createVercelAIGatewayStream(
model.id === openRouterClaudeSonnet461mModelId ||
model.id === openRouterClaudeOpus461mModelId ||
model.id === openRouterClaudeOpus471mModelId ||
model.id === openRouterClaudeOpus481mModelId ||
model.id === openRouterClaudeFable51mModelId
model.id === openRouterClaudeOpus481mModelId
if (isClaude1m) {
// remove the custom :1m suffix, to create the model id the API expects
model.id = model.id.slice(0, -CLAUDE_SONNET_1M_SUFFIX.length)
@@ -1,12 +1,10 @@
import * as disk from "@core/storage/disk"
import { openRouterClaudeFable51mModelId } from "@shared/api"
import axios from "axios"
import { expect } from "chai"
import fs from "fs/promises"
import { afterEach, beforeEach, describe, it } from "mocha"
import sinon from "sinon"
import { ClineEnv, Environment } from "@/config"
import type { Controller } from "@/core/controller"
import { StateManager } from "@/core/storage/StateManager"
import { getFeatureFlagsService } from "@/services/feature-flags"
import { FeatureFlag } from "@/shared/services/feature-flags/feature-flags"
@@ -39,7 +37,7 @@ describe("refreshClineModels", () => {
sandbox.stub(StateManager, "get").returns({
getModelsCache: () => null,
setModelsCache: () => {},
} as unknown as StateManager)
} as any)
sandbox.stub(disk, "ensureCacheDirectoryExists").resolves("/tmp")
sandbox.stub(fs, "writeFile").resolves()
sandbox.stub(axios, "get").resolves({
@@ -69,70 +67,11 @@ describe("refreshClineModels", () => {
},
})
const models = await refreshClineModels({} as Controller)
const models = await refreshClineModels({} as any)
const qwen37 = models["qwen/qwen3.7-max"]
expect(qwen37.supportsPromptCache).to.equal(true)
expect(qwen37.cacheReadsPrice).to.equal(0.25)
expect(qwen37.cacheWritesPrice).to.equal(undefined)
})
it("adds Claude Fable 5 context variants to the Cline model list", async () => {
sandbox.stub(getFeatureFlagsService(), "getBooleanFlagEnabled").callsFake((flag) => {
return flag === FeatureFlag.EXTENSION_CLINE_MODELS_ENDPOINT
})
sandbox.stub(ClineEnv, "config").returns({
environment: Environment.production,
appBaseUrl: "https://app.cline-mock.bot",
apiBaseUrl: "https://api.cline-mock.bot",
mcpBaseUrl: "https://api.cline-mock.bot/v1/mcp",
})
sandbox.stub(StateManager, "get").returns({
getModelsCache: () => null,
setModelsCache: () => {},
} as unknown as StateManager)
sandbox.stub(disk, "ensureCacheDirectoryExists").resolves("/tmp")
sandbox.stub(fs, "writeFile").resolves()
sandbox.stub(axios, "get").resolves({
data: {
data: [
{
id: "anthropic/claude-fable-5",
name: "Claude Fable 5",
description: "Fetched description",
context_length: 1_000_000,
top_provider: {
max_completion_tokens: 128_000,
context_length: 1_000_000,
is_moderated: false,
},
architecture: {
modality: ["text", "image"],
},
pricing: {
prompt: "0.00001",
completion: "0.00005",
input_cache_read: "0.000001",
input_cache_write: "0.0000125",
},
supported_parameters: ["include_reasoning", "reasoning"],
},
],
},
})
const models = await refreshClineModels({} as Controller)
const fable = models["anthropic/claude-fable-5"]
const fable1m = models[openRouterClaudeFable51mModelId]
expect(fable.contextWindow).to.equal(200_000)
expect(fable.maxTokens).to.equal(128_000)
expect(fable.supportsPromptCache).to.equal(true)
expect(fable.inputPrice).to.equal(10)
expect(fable.outputPrice).to.equal(50)
expect(fable.cacheWritesPrice).to.equal(12.5)
expect(fable.cacheReadsPrice).to.equal(1)
expect(fable1m.contextWindow).to.equal(1_000_000)
expect(fable1m.tiers).to.not.equal(undefined)
})
})
@@ -5,6 +5,9 @@ import fs from "fs/promises"
import { afterEach, beforeEach, describe, it } from "mocha"
import sinon from "sinon"
import { ClineEnv, Environment } from "@/config"
import { getFeatureFlagsService } from "@/services/feature-flags"
import { CLINE_RECOMMENDED_MODELS_FALLBACK } from "@/shared/cline/recommended-models"
import { FeatureFlag } from "@/shared/services/feature-flags/feature-flags"
import { Logger } from "@/shared/services/Logger"
import { refreshClineRecommendedModels, resetClineRecommendedModelsCacheForTests } from "../refreshClineRecommendedModels"
@@ -23,7 +26,20 @@ describe("refreshClineRecommendedModels", () => {
sandbox.restore()
})
it("fetches from upstream", async () => {
it("returns hardcoded models and skips upstream fetch when rollout flag is off", async () => {
sandbox.stub(getFeatureFlagsService(), "getBooleanFlagEnabled").returns(false)
const axiosGetStub = sandbox.stub(axios, "get")
const result = await refreshClineRecommendedModels()
expect(result).to.deep.equal(CLINE_RECOMMENDED_MODELS_FALLBACK)
expect(axiosGetStub.called).to.equal(false)
})
it("fetches from upstream when rollout flag is on", async () => {
sandbox.stub(getFeatureFlagsService(), "getBooleanFlagEnabled").callsFake((flag) => {
return flag === FeatureFlag.CLINE_RECOMMENDED_MODELS_UPSTREAM
})
sandbox.stub(ClineEnv, "config").returns({
environment: Environment.production,
appBaseUrl: "https://app.cline-mock.bot",
@@ -62,7 +78,10 @@ describe("refreshClineRecommendedModels", () => {
})
})
it("uses the in-memory cache after upstream cache is populated", async () => {
it("uses hardcoded models when rollout flag is turned off after upstream cache is populated", async () => {
const flagStub = sandbox.stub(getFeatureFlagsService(), "getBooleanFlagEnabled")
flagStub.onFirstCall().returns(true)
flagStub.onSecondCall().returns(false)
sandbox.stub(ClineEnv, "config").returns({
environment: Environment.production,
appBaseUrl: "https://app.cline-mock.bot",
@@ -82,6 +101,7 @@ describe("refreshClineRecommendedModels", () => {
const secondResult = await refreshClineRecommendedModels()
expect(axiosGetStub.calledOnce).to.equal(true)
expect(secondResult).to.deep.equal(firstResult)
expect(firstResult).to.not.deep.equal(CLINE_RECOMMENDED_MODELS_FALLBACK)
expect(secondResult).to.deep.equal(CLINE_RECOMMENDED_MODELS_FALLBACK)
})
})
@@ -11,10 +11,8 @@ import { StateManager } from "@/core/storage/StateManager"
import { featureFlagsService } from "@/services/feature-flags"
import {
ANTHROPIC_MAX_THINKING_BUDGET,
CLAUDE_FABLE_1M_TIERS,
CLAUDE_OPUS_1M_TIERS,
CLAUDE_SONNET_1M_TIERS,
openRouterClaudeFable51mModelId,
openRouterClaudeOpus461mModelId,
openRouterClaudeOpus471mModelId,
openRouterClaudeOpus481mModelId,
@@ -80,7 +78,7 @@ interface ClineRawModelInfo {
input_cache_write?: string
} | null
supports_global_endpoint?: boolean | null
tiers?: ModelInfo["tiers"] | null
tiers?: any[] | null
supported_parameters?: ClineSupportedParams[] | null
}
@@ -140,7 +138,7 @@ async function fetchAndCacheClineModels(): Promise<Record<string, ModelInfo>> {
let models: Record<string, ModelInfo> = {}
try {
const rawModels = await fetchRawClineModels()
const parsePrice = (price: unknown) => {
const parsePrice = (price: any) => {
if (price === undefined || price === null || price === "") {
return undefined
}
@@ -206,14 +204,6 @@ async function fetchAndCacheClineModels(): Promise<Record<string, ModelInfo>> {
modelInfo.cacheWritesPrice = 6.25
modelInfo.cacheReadsPrice = 0.5
break
case "anthropic/claude-fable-5":
modelInfo.contextWindow = 200_000
modelInfo.supportsPromptCache = true
modelInfo.inputPrice = 10
modelInfo.outputPrice = 50
modelInfo.cacheWritesPrice = 12.5
modelInfo.cacheReadsPrice = 1
break
case "anthropic/claude-opus-4.5":
modelInfo.supportsPromptCache = true
modelInfo.cacheWritesPrice = 6.25
@@ -309,12 +299,6 @@ async function fetchAndCacheClineModels(): Promise<Record<string, ModelInfo>> {
models[openRouterClaudeOpus481mModelId] = claudeOpus1mModelInfo
}
}
if (rawModel.id === "anthropic/claude-fable-5") {
const claudeFable1mModelInfo = cloneDeep(modelInfo)
claudeFable1mModelInfo.contextWindow = 1_000_000
claudeFable1mModelInfo.tiers = CLAUDE_FABLE_1M_TIERS
models[openRouterClaudeFable51mModelId] = claudeFable1mModelInfo
}
}
if (Object.keys(models).length === 0) {
throw new Error("No Cline models returned from API")
@@ -3,7 +3,10 @@ import axios from "axios"
import fs from "fs/promises"
import path from "path"
import { ClineEnv } from "@/config"
import { featureFlagsService } from "@/services/feature-flags"
import { CLINE_RECOMMENDED_MODELS_FALLBACK } from "@/shared/cline/recommended-models"
import { getAxiosSettings } from "@/shared/net"
import { FeatureFlag } from "@/shared/services/feature-flags/feature-flags"
import { Logger } from "@/shared/services/Logger"
export interface ClineRecommendedModelData {
@@ -23,6 +26,14 @@ const RECOMMENDED_MODELS_CACHE_TTL_MS = 60 * 60 * 1000
let pendingRefresh: Promise<ClineRecommendedModelsData> | null = null
let inMemoryCache: { data: ClineRecommendedModelsData; timestamp: number } | null = null
function getHardcodedRecommendedModels(): ClineRecommendedModelsData {
return CLINE_RECOMMENDED_MODELS_FALLBACK
}
function useUpstreamRecommendedModels(): boolean {
return featureFlagsService.getBooleanFlagEnabled(FeatureFlag.CLINE_RECOMMENDED_MODELS_UPSTREAM)
}
function normalizeRecommendedModel(raw: unknown): ClineRecommendedModelData | null {
if (!raw || typeof raw !== "object") {
return null
@@ -69,6 +80,10 @@ function normalizeRecommendedModelsResponse(raw: unknown): ClineRecommendedModel
}
export async function refreshClineRecommendedModels(): Promise<ClineRecommendedModelsData> {
if (!useUpstreamRecommendedModels()) {
return getHardcodedRecommendedModels()
}
if (inMemoryCache && Date.now() - inMemoryCache.timestamp <= RECOMMENDED_MODELS_CACHE_TTL_MS) {
return inMemoryCache.data
}
@@ -8,10 +8,8 @@ import path from "path"
import { StateManager } from "@/core/storage/StateManager"
import {
ANTHROPIC_MAX_THINKING_BUDGET,
CLAUDE_FABLE_1M_TIERS,
CLAUDE_OPUS_1M_TIERS,
CLAUDE_SONNET_1M_TIERS,
openRouterClaudeFable51mModelId,
openRouterClaudeOpus461mModelId,
openRouterClaudeOpus471mModelId,
openRouterClaudeOpus481mModelId,
@@ -181,14 +179,6 @@ async function fetchAndCacheModels(controller: Controller): Promise<Record<strin
modelInfo.cacheWritesPrice = 6.25
modelInfo.cacheReadsPrice = 0.5
break
case "anthropic/claude-fable-5":
modelInfo.contextWindow = 200_000 // restrict to 200k, 1m variant created below
modelInfo.supportsPromptCache = true
modelInfo.inputPrice = 10
modelInfo.outputPrice = 50
modelInfo.cacheWritesPrice = 12.5
modelInfo.cacheReadsPrice = 1
break
case "anthropic/claude-opus-4.5":
modelInfo.supportsPromptCache = true
modelInfo.cacheWritesPrice = 6.25
@@ -332,12 +322,6 @@ async function fetchAndCacheModels(controller: Controller): Promise<Record<strin
models[openRouterClaudeOpus481mModelId] = claudeOpus1mModelInfo
}
}
if (rawModel.id === "anthropic/claude-fable-5") {
const claudeFable1mModelInfo = cloneDeep(modelInfo)
claudeFable1mModelInfo.contextWindow = 1_000_000
claudeFable1mModelInfo.tiers = CLAUDE_FABLE_1M_TIERS
models[openRouterClaudeFable51mModelId] = claudeFable1mModelInfo
}
}
// Save models and cache them in memory
await fs.writeFile(openRouterModelsFilePath, JSON.stringify(models))
@@ -12,6 +12,12 @@ describe("TaskCancel Hook", () => {
let getEnv: () => { tempDir: string }
let hookTestEnv: HookTestEnv
// On Windows, hooks execute via a PowerShell bridge that spawns a child
// Node process. That double-process startup is slow and variable on CI and
// can easily exceed Mocha's default 2 s timeout, so spawning tests opt into a
// larger timeout. Mirrors taskresume/hook-factory/user-prompt-submit tests.
const WINDOWS_HOOK_TEST_TIMEOUT_MS = 15000
const writeHookScript = async (hookPath: string, nodeScript: string): Promise<void> => {
await writeHookScriptForPlatform(hookPath, nodeScript)
}
@@ -29,7 +35,11 @@ describe("TaskCancel Hook", () => {
})
describe("Hook Input Format", () => {
it("should receive task metadata with completionStatus", async () => {
it("should receive task metadata with completionStatus", async function () {
if (process.platform === "win32") {
this.timeout(WINDOWS_HOOK_TEST_TIMEOUT_MS)
}
const hookPath = path.join(tempDir, ".clinerules", "hooks", "TaskCancel")
const hookScript = `#!/usr/bin/env node
const input = JSON.parse(require('fs').readFileSync(0, 'utf-8'));
@@ -61,7 +71,11 @@ console.log(JSON.stringify({
// Note: contextModification is ignored for TaskCancel hooks
})
it("should handle 'abandoned' completion status", async () => {
it("should handle 'abandoned' completion status", async function () {
if (process.platform === "win32") {
this.timeout(WINDOWS_HOOK_TEST_TIMEOUT_MS)
}
const hookPath = path.join(tempDir, ".clinerules", "hooks", "TaskCancel")
const hookScript = `#!/usr/bin/env node
const input = JSON.parse(require('fs').readFileSync(0, 'utf-8'));
@@ -96,7 +110,11 @@ console.log(JSON.stringify({
// Note: contextModification is ignored for TaskCancel hooks
})
it("should receive all common hook input fields", async () => {
it("should receive all common hook input fields", async function () {
if (process.platform === "win32") {
this.timeout(WINDOWS_HOOK_TEST_TIMEOUT_MS)
}
const hookPath = path.join(tempDir, ".clinerules", "hooks", "TaskCancel")
const hookScript = `#!/usr/bin/env node
const input = JSON.parse(require('fs').readFileSync(0, 'utf-8'));
@@ -136,7 +154,11 @@ console.log(JSON.stringify({
})
describe("Fire-and-Forget Behavior", () => {
it("should ignore contextModification regardless of content", async () => {
it("should ignore contextModification regardless of content", async function () {
if (process.platform === "win32") {
this.timeout(WINDOWS_HOOK_TEST_TIMEOUT_MS)
}
const hookPath = path.join(tempDir, ".clinerules", "hooks", "TaskCancel")
const hookScript = `#!/usr/bin/env node
console.log(JSON.stringify({
@@ -192,7 +214,11 @@ console.log(JSON.stringify({
// The contextModification value is different but behavior is identical (fire-and-forget)
})
it("should succeed regardless of hook return value", async () => {
it("should succeed regardless of hook return value", async function () {
if (process.platform === "win32") {
this.timeout(WINDOWS_HOOK_TEST_TIMEOUT_MS)
}
const hookPath = path.join(tempDir, ".clinerules", "hooks", "TaskCancel")
const hookScript = `#!/usr/bin/env node
// Note: contextModification is ignored for TaskCancel hooks
@@ -222,7 +248,11 @@ console.log(JSON.stringify({
result.cancel.should.be.false()
})
it("should return error message when hook returns cancel: true", async () => {
it("should return error message when hook returns cancel: true", async function () {
if (process.platform === "win32") {
this.timeout(WINDOWS_HOOK_TEST_TIMEOUT_MS)
}
const hookPath = path.join(tempDir, ".clinerules", "hooks", "TaskCancel")
const hookScript = `#!/usr/bin/env node
console.log(JSON.stringify({
@@ -254,7 +284,11 @@ console.log(JSON.stringify({
result.errorMessage?.should.equal("Hook tried to block cancellation")
})
it("should execute without errors for cleanup purposes", async () => {
it("should execute without errors for cleanup purposes", async function () {
if (process.platform === "win32") {
this.timeout(WINDOWS_HOOK_TEST_TIMEOUT_MS)
}
const hookPath = path.join(tempDir, ".clinerules", "hooks", "TaskCancel")
const hookScript = `#!/usr/bin/env node
const input = JSON.parse(require('fs').readFileSync(0, 'utf-8'));
@@ -288,7 +322,11 @@ console.log(JSON.stringify({
})
describe("Error Handling", () => {
it("should surface hook errors to the user", async () => {
it("should surface hook errors to the user", async function () {
if (process.platform === "win32") {
this.timeout(WINDOWS_HOOK_TEST_TIMEOUT_MS)
}
const hookPath = path.join(tempDir, ".clinerules", "hooks", "TaskCancel")
const hookScript = `#!/usr/bin/env node
console.error("Hook execution error");
@@ -317,7 +355,11 @@ process.exit(1);`
}
})
it("should handle malformed JSON output from hook", async () => {
it("should handle malformed JSON output from hook", async function () {
if (process.platform === "win32") {
this.timeout(WINDOWS_HOOK_TEST_TIMEOUT_MS)
}
const hookPath = path.join(tempDir, ".clinerules", "hooks", "TaskCancel")
const hookScript = `#!/usr/bin/env node
console.log("not valid json")`
@@ -359,7 +401,11 @@ console.log("not valid json")`
stubHookDirs(sandbox, [globalHooksDir, workspaceHooksDir])
})
it("should execute both global and workspace TaskCancel hooks", async () => {
it("should execute both global and workspace TaskCancel hooks", async function () {
if (process.platform === "win32") {
this.timeout(WINDOWS_HOOK_TEST_TIMEOUT_MS)
}
// Create global hook
const globalHookPath = path.join(globalHooksDir, "TaskCancel")
const globalHookScript = `#!/usr/bin/env node
@@ -399,7 +445,11 @@ console.log(JSON.stringify({
// Both hooks executed successfully
})
it("should execute both hooks with different completion statuses", async () => {
it("should execute both hooks with different completion statuses", async function () {
if (process.platform === "win32") {
this.timeout(WINDOWS_HOOK_TEST_TIMEOUT_MS)
}
const globalHookPath = path.join(globalHooksDir, "TaskCancel")
const globalHookScript = `#!/usr/bin/env node
const input = JSON.parse(require('fs').readFileSync(0, 'utf-8'));
@@ -441,7 +491,11 @@ console.log(JSON.stringify({
})
describe("No Hook Behavior", () => {
it("should succeed when no hook exists", async () => {
it("should succeed when no hook exists", async function () {
if (process.platform === "win32") {
this.timeout(WINDOWS_HOOK_TEST_TIMEOUT_MS)
}
const factory = new HookFactory()
const runner = await factory.create("TaskCancel")
@@ -461,7 +515,11 @@ console.log(JSON.stringify({
})
describe("Fixture-Based Tests", () => {
it("should handle cancel: true with no error message", async () => {
it("should handle cancel: true with no error message", async function () {
if (process.platform === "win32") {
this.timeout(WINDOWS_HOOK_TEST_TIMEOUT_MS)
}
await loadFixture("hooks/taskcancel/false-no-error", getEnv().tempDir)
const factory = new HookFactory()
@@ -484,7 +542,11 @@ console.log(JSON.stringify({
// Cancellation still proceeds (fire-and-forget)
})
it("should handle cancel: true with error message", async () => {
it("should handle cancel: true with error message", async function () {
if (process.platform === "win32") {
this.timeout(WINDOWS_HOOK_TEST_TIMEOUT_MS)
}
await loadFixture("hooks/taskcancel/false-with-error", getEnv().tempDir)
const factory = new HookFactory()
@@ -507,7 +569,11 @@ console.log(JSON.stringify({
// Cancellation still proceeds (fire-and-forget)
})
it("should handle cancel: false with no error message", async () => {
it("should handle cancel: false with no error message", async function () {
if (process.platform === "win32") {
this.timeout(WINDOWS_HOOK_TEST_TIMEOUT_MS)
}
await loadFixture("hooks/taskcancel/true-no-error", getEnv().tempDir)
const factory = new HookFactory()
@@ -529,7 +595,11 @@ console.log(JSON.stringify({
// Normal success case - no errors to surface
})
it("should handle cancel: false with error message", async () => {
it("should handle cancel: false with error message", async function () {
if (process.platform === "win32") {
this.timeout(WINDOWS_HOOK_TEST_TIMEOUT_MS)
}
await loadFixture("hooks/taskcancel/true-with-error", getEnv().tempDir)
const factory = new HookFactory()
@@ -553,7 +623,11 @@ console.log(JSON.stringify({
// Cancellation still proceeds (fire-and-forget)
})
it("should handle hook that exits with non-zero status code", async () => {
it("should handle hook that exits with non-zero status code", async function () {
if (process.platform === "win32") {
this.timeout(WINDOWS_HOOK_TEST_TIMEOUT_MS)
}
await loadFixture("hooks/taskcancel/error", getEnv().tempDir)
const factory = new HookFactory()
-14
View File
@@ -1421,8 +1421,6 @@ export class McpHub {
}
public async addRemoteServer(serverName: string, serverUrl: string, transportType = "streamableHttp"): Promise<McpServer[]> {
// Set flag to prevent file watcher from triggering during our update
this.isUpdatingClineSettings = true
try {
const settings = await this.readAndValidateMcpSettingsFile()
if (!settings) {
@@ -1470,11 +1468,6 @@ export class McpHub {
} catch (error) {
Logger.error("Failed to add remote MCP server:", error)
throw error
} finally {
// Clear flag after a delay to ensure file watcher event has been processed
setTimeout(() => {
this.isUpdatingClineSettings = false
}, 300)
}
}
@@ -1484,8 +1477,6 @@ export class McpHub {
* @returns Array of remaining MCP servers
*/
public async deleteServerRPC(serverName: string): Promise<McpServer[]> {
// Set flag to prevent file watcher from triggering during our update
this.isUpdatingClineSettings = true
try {
// Clear OAuth data BEFORE removing from config (while we still have the connection/URL)
await this.clearOAuthForConnection(serverName)
@@ -1513,11 +1504,6 @@ export class McpHub {
} catch (error) {
Logger.error(`Failed to delete MCP server: ${error instanceof Error ? error.message : String(error)}`)
throw error
} finally {
// Clear flag after a delay to ensure file watcher event has been processed
setTimeout(() => {
this.isUpdatingClineSettings = false
}, 300)
}
}
@@ -1,133 +0,0 @@
import { afterEach, beforeEach, describe, it } from "mocha"
import "should"
import * as diskModule from "@core/storage/disk"
import fs from "fs/promises"
import os from "os"
import path from "path"
import sinon from "sinon"
import { McpHub } from "../McpHub"
// Regression tests for McpHub.deleteServerRPC(): deleting one server must not
// empty the list. Tests bypass the constructor's watcher via
// Object.create(McpHub.prototype), matching McpHub.callTool.test.ts.
type FakeConnection = {
server: { name: string; config: string; status: string; disabled: boolean }
client: Record<string, unknown>
transport: Record<string, unknown>
}
function makeConnection(name: string): FakeConnection {
return {
server: {
name,
config: JSON.stringify({ type: "stdio", command: "test", timeout: 60 }),
status: "connected",
disabled: false,
},
client: {},
transport: {},
}
}
describe("McpHub.deleteServerRPC", () => {
let sandbox: sinon.SinonSandbox
let tempDir: string
let settingsPath: string
let hub: McpHub
const writeSettings = async (mcpServers: Record<string, unknown>) => {
await fs.writeFile(settingsPath, JSON.stringify({ mcpServers }, null, 2))
}
beforeEach(async () => {
sandbox = sinon.createSandbox()
tempDir = path.join(os.tmpdir(), `mcp-delete-test-${Date.now()}-${Math.random().toString(36).slice(2)}`)
await fs.mkdir(tempDir, { recursive: true })
settingsPath = path.join(tempDir, "cline_mcp_settings.json")
sandbox.stub(diskModule, "getMcpSettingsFilePath").resolves(settingsPath)
hub = Object.create(McpHub.prototype) as McpHub
;(hub as any).getSettingsDirectoryPath = async () => tempDir
;(hub as any).isUpdatingClineSettings = false
;(hub as any).connections = [makeConnection("alpha"), makeConnection("beta")]
// clearOAuthForConnection touches the OAuth manager; stub it out.
sandbox.stub(hub as any, "clearOAuthForConnection").resolves()
// updateServerConnectionsRPC normally opens real transports; reproduce only
// the relevant behavior: drop connections no longer present in the new set.
sandbox.stub(hub as any, "updateServerConnectionsRPC").callsFake((...args: unknown[]) => {
const newServers = args[0] as Record<string, unknown>
;(hub as any).connections = (hub as any).connections.filter((c: FakeConnection) =>
Object.hasOwn(newServers, c.server.name),
)
return Promise.resolve()
})
})
afterEach(async () => {
sandbox.restore()
try {
await fs.rm(tempDir, { recursive: true, force: true })
} catch {
// Ignore cleanup errors
}
})
it("returns the remaining servers (not an empty list) after deleting one", async () => {
await writeSettings({ alpha: { type: "stdio", command: "a" }, beta: { type: "stdio", command: "b" } })
const result = await hub.deleteServerRPC("alpha")
result.should.have.length(1)
result.map((s) => s.name).should.deepEqual(["beta"])
})
it("persists the remaining server to the settings file", async () => {
await writeSettings({ alpha: { type: "stdio", command: "a" }, beta: { type: "stdio", command: "b" } })
await hub.deleteServerRPC("alpha")
const persisted = JSON.parse(await fs.readFile(settingsPath, "utf-8"))
Object.keys(persisted.mcpServers).should.deepEqual(["beta"])
})
it("guards the write with isUpdatingClineSettings so the watcher skips its own event", async () => {
const clock = sandbox.useFakeTimers()
await writeSettings({ alpha: { type: "stdio", command: "a" }, beta: { type: "stdio", command: "b" } })
// Capture the flag at the moment the settings file is written.
let flagDuringWrite: boolean | undefined
const realWriteFile = fs.writeFile.bind(fs)
sandbox.stub(fs, "writeFile").callsFake((...args: unknown[]) => {
flagDuringWrite = (hub as any).isUpdatingClineSettings
return (realWriteFile as (...a: unknown[]) => Promise<void>)(...args)
})
await hub.deleteServerRPC("alpha")
// True during the write and still true immediately after (cleared on a timer).
flagDuringWrite!.should.be.true()
;(hub as any).isUpdatingClineSettings.should.be.true()
// The flag is cleared on a 300ms timer so external edits resume.
clock.tick(300)
;(hub as any).isUpdatingClineSettings.should.be.false()
})
it("throws and still clears the guard when the server is not found", async () => {
const clock = sandbox.useFakeTimers()
await writeSettings({ beta: { type: "stdio", command: "b" } })
let threw: Error | undefined
try {
await hub.deleteServerRPC("missing")
} catch (err) {
threw = err as Error
}
;(threw === undefined).should.be.false()
threw!.message.should.match(/not found in MCP configuration/)
clock.tick(300)
;(hub as any).isUpdatingClineSettings.should.be.false()
})
})
+142 -254
View File
@@ -144,22 +144,6 @@ export const CLAUDE_OPUS_1M_TIERS = [
cacheReadsPrice: 1.0,
},
]
export const CLAUDE_FABLE_1M_TIERS = [
{
contextWindow: 200000,
inputPrice: 10,
outputPrice: 50,
cacheWritesPrice: 12.5,
cacheReadsPrice: 1,
},
{
contextWindow: Number.MAX_SAFE_INTEGER,
inputPrice: 10,
outputPrice: 50,
cacheWritesPrice: 12.5,
cacheReadsPrice: 1,
},
]
export interface HicapCompatibleModelInfo extends ModelInfo {
temperature?: number
@@ -334,29 +318,6 @@ export const anthropicModels = {
cacheReadsPrice: 0.5,
tiers: CLAUDE_OPUS_1M_TIERS,
},
"claude-fable-5": {
maxTokens: 128_000,
contextWindow: 200_000,
supportsImages: true,
supportsPromptCache: true,
supportsReasoning: true,
inputPrice: 10,
outputPrice: 50,
cacheWritesPrice: 12.5,
cacheReadsPrice: 1,
},
"claude-fable-5:1m": {
maxTokens: 128_000,
contextWindow: 1_000_000,
supportsImages: true,
supportsPromptCache: true,
supportsReasoning: true,
inputPrice: 10,
outputPrice: 50,
cacheWritesPrice: 12.5,
cacheReadsPrice: 1,
tiers: CLAUDE_FABLE_1M_TIERS,
},
"claude-opus-4-7": {
maxTokens: 128_000,
contextWindow: 200_000,
@@ -544,17 +505,6 @@ export const claudeCodeModels = {
supportsImages: false,
supportsPromptCache: false,
},
"claude-fable-5": {
...anthropicModels["claude-fable-5"],
contextWindow: 200_000,
supportsImages: false,
supportsPromptCache: false,
},
"claude-fable-5[1m]": {
...anthropicModels["claude-fable-5:1m"],
supportsImages: false,
supportsPromptCache: false,
},
"claude-opus-4-7": {
...anthropicModels["claude-opus-4-7"],
contextWindow: 200_000,
@@ -735,31 +685,6 @@ export const bedrockModels = {
cacheReadsPrice: 0.5,
tiers: CLAUDE_OPUS_1M_TIERS,
},
"anthropic.claude-fable-5": {
maxTokens: 128_000,
contextWindow: 200_000,
supportsImages: true,
supportsPromptCache: true,
supportsReasoning: true,
supportsGlobalEndpoint: true,
inputPrice: 10,
outputPrice: 50,
cacheWritesPrice: 12.5,
cacheReadsPrice: 1,
},
"anthropic.claude-fable-5:1m": {
maxTokens: 128_000,
contextWindow: 1_000_000,
supportsImages: true,
supportsPromptCache: true,
supportsReasoning: true,
supportsGlobalEndpoint: true,
inputPrice: 10,
outputPrice: 50,
cacheWritesPrice: 12.5,
cacheReadsPrice: 1,
tiers: CLAUDE_FABLE_1M_TIERS,
},
"anthropic.claude-opus-4-7": {
maxTokens: 128_000,
contextWindow: 200_000,
@@ -997,7 +922,6 @@ export const openRouterClaudeSonnet461mModelId = `anthropic/claude-sonnet-4.6${C
export const openRouterClaudeOpus461mModelId = `anthropic/claude-opus-4.6${CLAUDE_SONNET_1M_SUFFIX}`
export const openRouterClaudeOpus471mModelId = `anthropic/claude-opus-4.7${CLAUDE_SONNET_1M_SUFFIX}`
export const openRouterClaudeOpus481mModelId = `anthropic/claude-opus-4.8${CLAUDE_SONNET_1M_SUFFIX}`
export const openRouterClaudeFable51mModelId = `anthropic/claude-fable-5${CLAUDE_SONNET_1M_SUFFIX}`
export const openRouterDefaultModelInfo: ModelInfo = {
maxTokens: 64_000,
contextWindow: 200_000,
@@ -1283,31 +1207,6 @@ export const vertexModels = {
supportsReasoning: true,
tiers: CLAUDE_OPUS_1M_TIERS,
},
"claude-fable-5": {
maxTokens: 128_000,
contextWindow: 200_000,
supportsImages: true,
supportsPromptCache: true,
supportsGlobalEndpoint: true,
inputPrice: 10,
outputPrice: 50,
cacheWritesPrice: 12.5,
cacheReadsPrice: 1,
supportsReasoning: true,
},
"claude-fable-5:1m": {
maxTokens: 128_000,
contextWindow: 1_000_000,
supportsImages: true,
supportsPromptCache: true,
supportsGlobalEndpoint: true,
inputPrice: 10,
outputPrice: 50,
cacheWritesPrice: 12.5,
cacheReadsPrice: 1,
supportsReasoning: true,
tiers: CLAUDE_FABLE_1M_TIERS,
},
"claude-opus-4-7": {
maxTokens: 128_000,
contextWindow: 200_000,
@@ -4101,6 +4000,33 @@ export const xaiModels = {
export type SambanovaModelId = keyof typeof sambanovaModels
export const sambanovaDefaultModelId: SambanovaModelId = "Meta-Llama-3.3-70B-Instruct"
export const sambanovaModels = {
"DeepSeek-R1-0528": {
maxTokens: 7168,
contextWindow: 131072,
supportsImages: false,
supportsPromptCache: false,
temperature: 0.6,
inputPrice: 5.0,
outputPrice: 7.0,
},
"DeepSeek-R1-Distill-Llama-70B": {
maxTokens: 4096,
contextWindow: 131072,
supportsImages: false,
supportsPromptCache: false,
temperature: 0.6,
inputPrice: 0.7,
outputPrice: 1.4,
},
"DeepSeek-V3-0324": {
maxTokens: 7168,
contextWindow: 131072,
supportsImages: false,
supportsPromptCache: false,
temperature: 0.3,
inputPrice: 3.0,
outputPrice: 4.5,
},
"DeepSeek-V3.1": {
maxTokens: 7168,
contextWindow: 131072,
@@ -4110,9 +4036,9 @@ export const sambanovaModels = {
inputPrice: 3.0,
outputPrice: 4.5,
},
"DeepSeek-V3.2": {
"DeepSeek-V3.1-Terminus": {
maxTokens: 7168,
contextWindow: 32768,
contextWindow: 131072,
supportsImages: false,
supportsPromptCache: false,
temperature: 0.6,
@@ -4128,6 +4054,15 @@ export const sambanovaModels = {
inputPrice: 0.63,
outputPrice: 1.8,
},
"Meta-Llama-3.1-8B-Instruct": {
maxTokens: 4096,
contextWindow: 16384,
supportsImages: false,
supportsPromptCache: false,
temperature: 0.6,
inputPrice: 0.1,
outputPrice: 0.2,
},
"Meta-Llama-3.3-70B-Instruct": {
maxTokens: 3072,
contextWindow: 131072,
@@ -4137,14 +4072,32 @@ export const sambanovaModels = {
inputPrice: 0.6,
outputPrice: 1.2,
},
"MiniMax-M2.7": {
maxTokens: 196608,
contextWindow: 196608,
"MiniMax-M2.5": {
maxTokens: 16384,
contextWindow: 163840,
supportsImages: false,
supportsPromptCache: false,
temperature: 1.0,
inputPrice: 0.6,
outputPrice: 2.4,
inputPrice: 0.3,
outputPrice: 1.2,
},
"Qwen3-235B": {
maxTokens: 4096,
contextWindow: 65536,
supportsImages: false,
supportsPromptCache: false,
temperature: 0.7,
inputPrice: 0.4,
outputPrice: 0.8,
},
"Qwen3-32B": {
maxTokens: 4096,
contextWindow: 32768,
supportsImages: false,
supportsPromptCache: false,
temperature: 0.6,
inputPrice: 0.4,
outputPrice: 0.8,
},
} as const satisfies Record<string, ModelInfo>
@@ -5105,144 +5058,107 @@ export const mainlandZAiModels = {
// Fireworks AI
export type FireworksModelId = keyof typeof fireworksModels
export const fireworksDefaultModelId: FireworksModelId = "accounts/fireworks/models/kimi-k2p6"
export const fireworksDefaultModelId: FireworksModelId = "accounts/fireworks/models/kimi-k2p5"
export const fireworksModels = {
"accounts/fireworks/models/kimi-k2p5": {
maxTokens: 256000,
contextWindow: 256000,
maxTokens: 16384,
contextWindow: 262144,
supportsImages: true,
supportsPromptCache: true,
inputPrice: 0.6,
outputPrice: 3,
cacheWritesPrice: 0,
cacheWritesPrice: 0.6,
cacheReadsPrice: 0.1,
description:
"Moonshot's flagship open agentic model. Kimi K2.5 unifies vision and text, thinking and non-thinking modes, and single-agent and multi-agent execution.",
},
"accounts/fireworks/models/kimi-k2p6": {
maxTokens: 262000,
contextWindow: 262000,
supportsImages: true,
supportsPromptCache: true,
inputPrice: 0.95,
outputPrice: 4,
cacheWritesPrice: 0,
cacheReadsPrice: 0.16,
description:
"Moonshot's latest open agentic model. Kimi K2.6 unifies vision and text, thinking and non-thinking modes, and single-agent and multi-agent execution.",
},
"accounts/fireworks/routers/kimi-k2p6-turbo": {
maxTokens: 262000,
contextWindow: 262000,
supportsImages: true,
supportsPromptCache: true,
inputPrice: 2,
outputPrice: 8,
cacheWritesPrice: 0,
cacheReadsPrice: 0.3,
description:
"Kimi K2.6 Turbo router for high-performance agentic workloads with vision and text reasoning.",
},
"accounts/fireworks/models/deepseek-v4-flash": {
maxTokens: 384000,
contextWindow: 1000000,
supportsImages: false,
supportsPromptCache: true,
inputPrice: 0.14,
outputPrice: 0.28,
cacheWritesPrice: 0,
cacheReadsPrice: 0.03,
description:
"DeepSeek V4 Flash is a fast, cost-efficient reasoning model with a 1M context window and strong tool-use capabilities.",
},
"accounts/fireworks/models/deepseek-v4-pro": {
maxTokens: 384000,
contextWindow: 1000000,
supportsImages: false,
supportsPromptCache: true,
inputPrice: 1.74,
outputPrice: 3.48,
cacheWritesPrice: 0,
cacheReadsPrice: 0.145,
description:
"DeepSeek V4 Pro is a flagship reasoning model with a 1M context window, advanced structured output, and agentic performance.",
},
"accounts/fireworks/models/glm-5p1": {
maxTokens: 131072,
contextWindow: 202800,
supportsImages: false,
supportsPromptCache: true,
inputPrice: 1.4,
outputPrice: 4.4,
cacheWritesPrice: 0,
cacheReadsPrice: 0.26,
description: "GLM 5.1 is a next-generation general-purpose model optimized for coding, reasoning, and agentic workflows.",
},
"accounts/fireworks/routers/glm-5p1-fast": {
maxTokens: 131072,
contextWindow: 202800,
supportsImages: false,
supportsPromptCache: true,
inputPrice: 2.8,
outputPrice: 8.8,
cacheWritesPrice: 0,
cacheReadsPrice: 0.52,
description: "GLM 5.1 Fast router for high-throughput coding, reasoning, and agentic workflows.",
},
"accounts/fireworks/models/minimax-m2p5": {
maxTokens: 196608,
contextWindow: 196608,
supportsImages: false,
supportsPromptCache: true,
inputPrice: 0.3,
outputPrice: 1.2,
cacheWritesPrice: 0,
cacheReadsPrice: 0.03,
description: "MiniMax M2.5 is built for state-of-the-art coding, agentic tool use.",
},
"accounts/fireworks/models/minimax-m2p7": {
maxTokens: 196608,
contextWindow: 196608,
supportsImages: false,
supportsPromptCache: true,
inputPrice: 0.3,
outputPrice: 1.2,
cacheWritesPrice: 0,
cacheReadsPrice: 0.06,
description: "MiniMax M2.7 is tuned for strong real-world performance across coding, agent-driven, and workflow-heavy tasks.",
},
"accounts/fireworks/models/qwen3p6-plus": {
maxTokens: 65536,
"accounts/fireworks/models/qwen3-vl-30b-a3b-thinking": {
maxTokens: 32768,
contextWindow: 262144,
supportsImages: true,
supportsPromptCache: true,
inputPrice: 0.5,
outputPrice: 3,
cacheWritesPrice: 0,
cacheReadsPrice: 0.1,
description: "Qwen 3.6 Plus with strong multimodal reasoning, long context support, and function calling.",
inputPrice: 0.15,
outputPrice: 0.6,
cacheWritesPrice: 0.15,
cacheReadsPrice: 0.07,
description:
"Reasoning-enabled Qwen3-VL model with strong multimodal understanding, long context support, and function calling.",
},
"accounts/fireworks/models/qwen3-vl-30b-a3b-instruct": {
maxTokens: 32768,
contextWindow: 262144,
supportsImages: true,
supportsPromptCache: false,
inputPrice: 0.15,
outputPrice: 0.6,
description: "Qwen3-VL instruct model with strong multimodal reasoning, long context support, and function calling.",
},
"accounts/fireworks/models/deepseek-v3p2": {
maxTokens: 16384,
contextWindow: 163840,
supportsImages: false,
supportsPromptCache: true,
inputPrice: 0.56,
outputPrice: 1.68,
cacheWritesPrice: 0.56,
cacheReadsPrice: 0.28,
description: "DeepSeek V3.2 model tuned for high computational efficiency and strong reasoning and agent performance.",
},
"accounts/fireworks/models/glm-4p7": {
maxTokens: 16384,
contextWindow: 202752,
supportsImages: false,
supportsPromptCache: true,
inputPrice: 0.6,
outputPrice: 2.2,
cacheWritesPrice: 0.6,
cacheReadsPrice: 0.3,
description: "GLM-4.7 is a next-generation general-purpose model optimized for coding, reasoning, and agentic workflows.",
},
"accounts/fireworks/models/glm-5": {
maxTokens: 16384,
contextWindow: 202752,
supportsImages: false,
supportsPromptCache: true,
inputPrice: 1.0,
outputPrice: 3.2,
cacheWritesPrice: 1.0,
cacheReadsPrice: 0.2,
description: "GLM-5 is Z.ai's flagship reasoning model for complex systems engineering and long-horizon agentic tasks.",
},
"accounts/fireworks/models/minimax-m2p5": {
maxTokens: 16384,
contextWindow: 196608,
supportsImages: false,
supportsPromptCache: true,
inputPrice: 0.3,
outputPrice: 1.2,
cacheWritesPrice: 0.3,
cacheReadsPrice: 0.03,
description: "MiniMax M2.5 is built for state-of-the-art coding, agentic tool use.",
},
"accounts/fireworks/models/minimax-m2p1": {
maxTokens: 16384,
contextWindow: 196608,
supportsImages: false,
supportsPromptCache: true,
inputPrice: 0.3,
outputPrice: 1.2,
cacheWritesPrice: 0.3,
cacheReadsPrice: 0.03,
description:
"MiniMax M2.1 is tuned for strong real-world performance across coding, agent-driven, and workflow-heavy tasks.",
},
"accounts/fireworks/models/gpt-oss-120b": {
maxTokens: 32768,
maxTokens: 16384,
contextWindow: 131072,
supportsImages: false,
supportsPromptCache: true,
inputPrice: 0.15,
outputPrice: 0.6,
cacheWritesPrice: 0,
cacheReadsPrice: 0.015,
description: "OpenAI GPT OSS 120B open-weight model for production and high-reasoning use cases.",
},
"accounts/fireworks/models/gpt-oss-20b": {
maxTokens: 32768,
contextWindow: 131072,
supportsImages: false,
supportsPromptCache: true,
inputPrice: 0.07,
outputPrice: 0.3,
cacheWritesPrice: 0,
cacheReadsPrice: 0.035,
description: "OpenAI GPT OSS 20B open-weight model for efficient production and reasoning use cases.",
cacheWritesPrice: 0.15,
cacheReadsPrice: 0.01,
description: "OpenAI gpt-oss-120b open-weight model for production and high-reasoning use cases.",
},
} as const satisfies Record<string, ModelInfo>
@@ -5281,34 +5197,6 @@ export const qwenCodeDefaultModelId: QwenCodeModelId = "qwen3-coder-plus"
export type MinimaxModelId = keyof typeof minimaxModels
export const minimaxDefaultModelId: MinimaxModelId = "MiniMax-M2.7"
export const minimaxModels = {
"MiniMax-M3": {
maxTokens: 32_000,
contextWindow: 1_000_000,
supportsImages: true,
supportsPromptCache: true,
supportsReasoning: true,
inputPrice: 0.6,
outputPrice: 2.4,
cacheWritesPrice: 0.6,
cacheReadsPrice: 0.12,
tiers: [
{
contextWindow: 512_000,
inputPrice: 0.6,
outputPrice: 2.4,
cacheWritesPrice: 0.6,
cacheReadsPrice: 0.12,
},
{
contextWindow: Number.MAX_SAFE_INTEGER,
inputPrice: 1.2,
outputPrice: 4.8,
cacheWritesPrice: 1.2,
cacheReadsPrice: 0.24,
},
],
description: "Latest M-series model for coding, agentic reasoning, tool use, and long-context multimodal tasks",
},
"MiniMax-M2.7": {
maxTokens: 128_000,
contextWindow: 192_000,
@@ -12,6 +12,8 @@ export enum FeatureFlag {
// Feature flag for DB-backed welcome banners (What's New modal)
// When off, hardcoded welcome items are shown instead
REMOTE_WELCOME_BANNERS = "remote-welcome-banners",
// Feature flag for upstream Cline recommended model cards
CLINE_RECOMMENDED_MODELS_UPSTREAM = "cline-recommended-models-upstream",
// Rollout flag for Cline provider model sourcing:
// off => OpenRouter model list, on => Cline endpoint model list.
EXTENSION_CLINE_MODELS_ENDPOINT = "extension_cline_models_endpoint",
@@ -26,6 +28,7 @@ export const FeatureFlagDefaultValue: Partial<Record<FeatureFlag, FeatureFlagPay
[FeatureFlag.REMOTE_BANNERS]: process.env.E2E_TEST === "true" || process.env.IS_DEV === "true",
[FeatureFlag.EXTENSION_REMOTE_BANNERS_TTL]: 24 * 60 * 60 * 1000,
[FeatureFlag.REMOTE_WELCOME_BANNERS]: process.env.E2E_TEST === "true" || process.env.IS_DEV === "true",
[FeatureFlag.CLINE_RECOMMENDED_MODELS_UPSTREAM]: false,
[FeatureFlag.EXTENSION_CLINE_MODELS_ENDPOINT]: false,
[FeatureFlag.OPENAI_RESPONSES_WEBSOCKET_MODE]: false,
}
@@ -12,10 +12,7 @@ export function isClaudeOpusAdaptiveThinkingModel(modelId?: string): boolean {
const id = modelId.toLowerCase()
const adaptiveVersions = ["4-6", "4.6", "4-7", "4.7", "4-8", "4.8"]
return (
id.includes("claude-fable-5") ||
adaptiveVersions.some((version) => id.includes(`claude-opus-${version}`) || id.includes(`claude-${version}-opus`))
)
return adaptiveVersions.some((version) => id.includes(`claude-opus-${version}`) || id.includes(`claude-${version}-opus`))
}
export function resolveClaudeOpusAdaptiveThinking(
@@ -2,8 +2,6 @@ export const E2E_REGISTERED_MOCK_ENDPOINTS = {
"/api/v1": {
GET: [
"/generation",
"/ai/cline/models",
"/ai/cline/recommended-models",
"/organizations/{orgId}/balance",
"/organizations/{orgId}/members/{memberId}/usages",
"/organizations/{orgId}/api-keys",
@@ -82,65 +80,3 @@ export const E2E_MOCK_API_RESPONSES = {
REPLACE_REQUEST: replace_in_file,
EDIT_REQUEST: edit_request,
}
export const E2E_MOCK_CLINE_RECOMMENDED_MODELS = {
free: [
{
id: "z-ai/glm-5",
name: "z-ai/glm-5",
description: "Free model for e2e onboarding",
tags: [],
},
],
recommended: [
{
id: "anthropic/claude-sonnet-4.6",
name: "anthropic/claude-sonnet-4.6",
description: "Recommended model for e2e onboarding",
tags: ["BEST"],
},
],
}
export const E2E_MOCK_CLINE_MODELS = [
{
id: "z-ai/glm-5",
name: "z-ai/glm-5",
description: "Free model for e2e onboarding",
context_length: 131_072,
top_provider: {
max_completion_tokens: 8_192,
context_length: 131_072,
is_moderated: false,
},
architecture: {
modality: "text->text",
},
pricing: {
prompt: "0",
completion: "0",
},
supported_parameters: [],
},
{
id: "anthropic/claude-sonnet-4.6",
name: "anthropic/claude-sonnet-4.6",
description: "Recommended model for e2e onboarding",
context_length: 200_000,
top_provider: {
max_completion_tokens: 64_000,
context_length: 200_000,
is_moderated: false,
},
architecture: {
modality: "text->text",
},
pricing: {
prompt: "0.000003",
completion: "0.000015",
input_cache_read: "0.0000003",
input_cache_write: "0.00000375",
},
supported_parameters: ["include_reasoning"],
},
]
@@ -2,12 +2,7 @@ import { createServer, type IncomingMessage, type Server, type ServerResponse }
import type { Socket } from "node:net"
import { v4 as uuidv4 } from "uuid"
import type { BalanceResponse, OrganizationBalanceResponse, UserResponse } from "../../../../shared/ClineAccount"
import {
E2E_MOCK_API_RESPONSES,
E2E_MOCK_CLINE_MODELS,
E2E_MOCK_CLINE_RECOMMENDED_MODELS,
E2E_REGISTERED_MOCK_ENDPOINTS,
} from "./api"
import { E2E_MOCK_API_RESPONSES, E2E_REGISTERED_MOCK_ENDPOINTS } from "./api"
import { ClineDataMock } from "./data"
const E2E_API_SERVER_PORT = 7777
@@ -171,11 +166,7 @@ export class ClineApiServerMock {
// Authentication middleware
const authHeader = req.headers.authorization
const isPublicApiRoute =
path === "/api/v1/auth/token" ||
path === "/api/v1/ai/cline/models" ||
path === "/api/v1/ai/cline/recommended-models"
const isAuthRequired = !path.startsWith("/.test/") && path !== "/health" && !isPublicApiRoute
const isAuthRequired = !path.startsWith("/.test/") && path !== "/health" && path !== "/api/v1/auth/token"
if (isAuthRequired && (!authHeader || !authHeader.startsWith("Bearer "))) {
return sendApiError("Unauthorized", 401)
@@ -224,14 +215,6 @@ export class ClineApiServerMock {
// API v1 endpoints
if (baseRoute === "/api/v1") {
if (endpoint === "/ai/cline/recommended-models" && method === "GET") {
return sendJson(E2E_MOCK_CLINE_RECOMMENDED_MODELS)
}
if (endpoint === "/ai/cline/models" && method === "GET") {
return sendJson({ data: E2E_MOCK_CLINE_MODELS })
}
// User endpoints
if (endpoint === "/users/me" && method === "GET") {
const currentUser = controller.currentUser
+37 -13
View File
@@ -5,7 +5,10 @@
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"jsx": "react",
"lib": ["es2022", "DOM"],
"lib": [
"es2022",
"DOM"
],
"module": "esnext",
"moduleResolution": "Bundler",
"noFallthroughCasesInSwitch": true,
@@ -18,23 +21,44 @@
"sourceMap": true,
"strict": true,
"target": "es2022",
"typeRoots": ["./node_modules/@types", "./src/types"],
"useDefineForClassFields": true,
"useUnknownInCatchVariables": false,
"paths": {
"@/*": ["./src/*"],
"@api/*": ["./src/core/api/*"],
"@core/*": ["./src/core/*"],
"@generated/*": ["./src/generated/*"],
"@hosts/*": ["./src/hosts/*"],
"@integrations/*": ["./src/integrations/*"],
"@packages/*": ["./src/packages/*"],
"@services/*": ["./src/services/*"],
"@shared/*": ["./src/shared/*"],
"@utils/*": ["./src/utils/*"]
"@/*": [
"./src/*"
],
"@api/*": [
"./src/core/api/*"
],
"@core/*": [
"./src/core/*"
],
"@generated/*": [
"./src/generated/*"
],
"@hosts/*": [
"./src/hosts/*"
],
"@integrations/*": [
"./src/integrations/*"
],
"@packages/*": [
"./src/packages/*"
],
"@services/*": [
"./src/services/*"
],
"@shared/*": [
"./src/shared/*"
],
"@utils/*": [
"./src/utils/*"
]
}
},
"include": ["./src/**/*"],
"include": [
"./src/**/*"
],
"exclude": [
"node_modules",
".vscode-test",
@@ -512,14 +512,6 @@ const ClineModelPicker: React.FC<ClineModelPickerProps> = ({ isPopup, currentMod
)}
</DropdownWrapper>
{/* Context window switcher for Claude Fable 5 */}
<ContextWindowSwitcher
base1mModelId={`anthropic/claude-fable-5${CLAUDE_SONNET_1M_SUFFIX}`}
base200kModelId="anthropic/claude-fable-5"
onModelChange={handleModelChange}
selectedModelId={selectedModelId}
/>
{/* Context window switcher for Claude Opus 4.8 */}
<ContextWindowSwitcher
base1mModelId={`anthropic/claude-opus-4.8${CLAUDE_SONNET_1M_SUFFIX}`}
@@ -323,14 +323,6 @@ const OpenRouterModelPicker: React.FC<OpenRouterModelPickerProps> = ({ isPopup,
)}
</DropdownWrapper>
{/* Context window switcher for Claude Fable 5 */}
<ContextWindowSwitcher
base1mModelId={`anthropic/claude-fable-5${CLAUDE_SONNET_1M_SUFFIX}`}
base200kModelId="anthropic/claude-fable-5"
onModelChange={handleModelChange}
selectedModelId={selectedModelId}
/>
{/* Context window switcher for Claude Opus 4.8 */}
<ContextWindowSwitcher
base1mModelId={`anthropic/claude-opus-4.8${CLAUDE_SONNET_1M_SUFFIX}`}
@@ -1,7 +1,6 @@
import type { ExtensionMessage } from "@shared/ExtensionMessage";
import { isClineInternalTester } from "@shared/internal/account";
import { ResetStateRequest } from "@shared/proto/cline/state";
import type { UserOrganization } from "@shared/proto/index.cline";
import type { ExtensionMessage } from "@shared/ExtensionMessage"
import { ResetStateRequest } from "@shared/proto/cline/state"
import { UserOrganization } from "@shared/proto/index.cline"
import {
CheckCheck,
FlaskConical,
@@ -12,53 +11,38 @@ import {
SquareMousePointer,
SquareTerminal,
Wrench,
} from "lucide-react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useEvent } from "react-use";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { type ClineUser, useClineAuth } from "@/context/ClineAuthContext";
import { useExtensionState } from "@/context/ExtensionStateContext";
import { cn } from "@/lib/utils";
import { StateServiceClient } from "@/services/grpc-client";
import { isAdminOrOwner } from "../account/helpers";
import { Tab, TabContent, TabList, TabTrigger } from "../common/Tab";
import ViewHeader from "../common/ViewHeader";
import SectionHeader from "./SectionHeader";
import AboutSection from "./sections/AboutSection";
import ApiConfigurationSection from "./sections/ApiConfigurationSection";
import BrowserSettingsSection from "./sections/BrowserSettingsSection";
import DebugSection from "./sections/DebugSection";
import FeatureSettingsSection from "./sections/FeatureSettingsSection";
import GeneralSettingsSection from "./sections/GeneralSettingsSection";
import { RemoteConfigSection } from "./sections/RemoteConfigSection";
import TerminalSettingsSection from "./sections/TerminalSettingsSection";
} from "lucide-react"
import { useCallback, useEffect, useMemo, useState } from "react"
import { useEvent } from "react-use"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { useClineAuth } from "@/context/ClineAuthContext"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { cn } from "@/lib/utils"
import { StateServiceClient } from "@/services/grpc-client"
import { isAdminOrOwner } from "../account/helpers"
import { Tab, TabContent, TabList, TabTrigger } from "../common/Tab"
import ViewHeader from "../common/ViewHeader"
import SectionHeader from "./SectionHeader"
import AboutSection from "./sections/AboutSection"
import ApiConfigurationSection from "./sections/ApiConfigurationSection"
import BrowserSettingsSection from "./sections/BrowserSettingsSection"
import DebugSection from "./sections/DebugSection"
import FeatureSettingsSection from "./sections/FeatureSettingsSection"
import GeneralSettingsSection from "./sections/GeneralSettingsSection"
import { RemoteConfigSection } from "./sections/RemoteConfigSection"
import TerminalSettingsSection from "./sections/TerminalSettingsSection"
const IS_DEV = process.env.IS_DEV;
const IS_DEV = process.env.IS_DEV
// Tab definitions
type SettingsTabID =
| "api-config"
| "features"
| "browser"
| "terminal"
| "general"
| "about"
| "debug"
| "remote-config";
type SettingsTabID = "api-config" | "features" | "browser" | "terminal" | "general" | "about" | "debug" | "remote-config"
interface SettingsTab {
id: SettingsTabID;
name: string;
tooltipText: string;
headerText: string;
icon: LucideIcon;
hidden?: (params?: {
user: ClineUser | null;
activeOrganization: UserOrganization | null;
}) => boolean;
id: SettingsTabID
name: string
tooltipText: string
headerText: string
icon: LucideIcon
hidden?: (params?: { activeOrganization: UserOrganization | null }) => boolean
}
export const SETTINGS_TABS: SettingsTab[] = [
@@ -103,9 +87,8 @@ export const SETTINGS_TABS: SettingsTab[] = [
tooltipText: "Remotely configured fields",
headerText: "Remote Config",
icon: HardDriveDownload,
hidden: (
{ activeOrganization } = { user: null, activeOrganization: null },
) => !activeOrganization || !isAdminOrOwner(activeOrganization),
hidden: ({ activeOrganization } = { activeOrganization: null }) =>
!activeOrganization || !isAdminOrOwner(activeOrganization),
},
{
id: "about",
@@ -121,21 +104,20 @@ export const SETTINGS_TABS: SettingsTab[] = [
tooltipText: "Debug Tools",
headerText: "Debug",
icon: FlaskConical,
hidden: ({ user } = { user: null, activeOrganization: null }) =>
!IS_DEV && !isClineInternalTester(user?.email || ""),
hidden: () => !IS_DEV,
},
];
]
type SettingsViewProps = {
onDone: () => void;
targetSection?: string;
};
onDone: () => void
targetSection?: string
}
// Helper to render section header - moved outside component for better performance
const renderSectionHeader = (tabId: string) => {
const tab = SETTINGS_TABS.find((t) => t.id === tabId);
const tab = SETTINGS_TABS.find((t) => t.id === tabId)
if (!tab) {
return null;
return null
}
return (
@@ -145,8 +127,8 @@ const renderSectionHeader = (tabId: string) => {
<div>{tab.headerText}</div>
</div>
</SectionHeader>
);
};
)
}
const SettingsView = ({ onDone, targetSection }: SettingsViewProps) => {
// Memoize to avoid recreation
@@ -162,85 +144,76 @@ const SettingsView = ({ onDone, targetSection }: SettingsViewProps) => {
debug: DebugSection,
}),
[],
); // Empty deps - these imports never change
) // Empty deps - these imports never change
const { version, environment, settingsInitialModelTab } = useExtensionState();
const { activeOrganization, clineUser } = useClineAuth();
const { version, environment, settingsInitialModelTab } = useExtensionState()
const { activeOrganization } = useClineAuth()
const [activeTab, setActiveTab] = useState<string>(
targetSection || SETTINGS_TABS[0].id,
);
const [activeTab, setActiveTab] = useState<string>(targetSection || SETTINGS_TABS[0].id)
// Optimized message handler with early returns
const handleMessage = useCallback((event: MessageEvent) => {
const message: ExtensionMessage = event.data;
const message: ExtensionMessage = event.data
if (message.type !== "grpc_response") {
return;
return
}
const grpcMessage = message.grpc_response?.message;
const grpcMessage = message.grpc_response?.message
if (grpcMessage?.key !== "scrollToSettings") {
return;
return
}
const tabId = grpcMessage.value;
const tabId = grpcMessage.value
if (!tabId) {
return;
return
}
// Check if valid tab ID
if (SETTINGS_TABS.some((tab) => tab.id === tabId)) {
setActiveTab(tabId);
return;
setActiveTab(tabId)
return
}
// Fallback to element scrolling
requestAnimationFrame(() => {
const element = document.getElementById(tabId);
const element = document.getElementById(tabId)
if (!element) {
return;
return
}
element.scrollIntoView({ behavior: "smooth" });
element.style.transition = "background-color 0.5s ease";
element.style.backgroundColor = "var(--vscode-textPreformat-background)";
element.scrollIntoView({ behavior: "smooth" })
element.style.transition = "background-color 0.5s ease"
element.style.backgroundColor = "var(--vscode-textPreformat-background)"
setTimeout(() => {
element.style.backgroundColor = "transparent";
}, 1200);
});
}, []);
element.style.backgroundColor = "transparent"
}, 1200)
})
}, [])
useEvent("message", handleMessage);
useEvent("message", handleMessage)
// Memoized reset state handler
const handleResetState = useCallback(async (resetGlobalState?: boolean) => {
try {
await StateServiceClient.resetState(
ResetStateRequest.create({ global: resetGlobalState }),
);
await StateServiceClient.resetState(ResetStateRequest.create({ global: resetGlobalState }))
} catch (error) {
console.error("Failed to reset state:", error);
console.error("Failed to reset state:", error)
}
}, []);
}, [])
// Update active tab when targetSection changes
useEffect(() => {
if (targetSection) {
setActiveTab(targetSection);
setActiveTab(targetSection)
}
}, [targetSection]);
}, [targetSection])
// Memoized tab item renderer
const renderTabItem = useCallback(
(tab: (typeof SETTINGS_TABS)[0]) => {
return (
<TabTrigger
className="flex justify-baseline"
data-testid={`tab-${tab.id}`}
key={tab.id}
value={tab.id}
>
<TabTrigger className="flex justify-baseline" data-testid={`tab-${tab.id}`} key={tab.id} value={tab.id}>
<Tooltip key={tab.id}>
<TooltipTrigger>
<div
@@ -250,8 +223,7 @@ const SettingsView = ({ onDone, targetSection }: SettingsViewProps) => {
"opacity-100 border-l-2 border-l-foreground border-t-0 border-r-0 border-b-0 bg-selection":
activeTab === tab.id,
},
)}
>
)}>
<tab.icon className="w-4 h-4" />
<span className="hidden sm:block">{tab.name}</span>
</div>
@@ -259,37 +231,30 @@ const SettingsView = ({ onDone, targetSection }: SettingsViewProps) => {
<TooltipContent side="right">{tab.tooltipText}</TooltipContent>
</Tooltip>
</TabTrigger>
);
)
},
[activeTab],
);
)
// Memoized active content component
const ActiveContent = useMemo(() => {
const Component =
TAB_CONTENT_MAP[activeTab as keyof typeof TAB_CONTENT_MAP];
const Component = TAB_CONTENT_MAP[activeTab as keyof typeof TAB_CONTENT_MAP]
if (!Component) {
return null;
return null
}
// Special props for specific components
const props: any = { renderSectionHeader };
const props: any = { renderSectionHeader }
if (activeTab === "debug") {
props.onResetState = handleResetState;
props.onResetState = handleResetState
} else if (activeTab === "about") {
props.version = version;
props.version = version
} else if (activeTab === "api-config") {
props.initialModelTab = settingsInitialModelTab;
props.initialModelTab = settingsInitialModelTab
}
return <Component {...props} />;
}, [
activeTab,
handleResetState,
settingsInitialModelTab,
version,
TAB_CONTENT_MAP,
]);
return <Component {...props} />
}, [activeTab, handleResetState, settingsInitialModelTab, version])
return (
<Tab>
@@ -299,19 +264,14 @@ const SettingsView = ({ onDone, targetSection }: SettingsViewProps) => {
<TabList
className="shrink-0 flex flex-col overflow-y-auto border-r border-sidebar-background"
onValueChange={setActiveTab}
value={activeTab}
>
{SETTINGS_TABS.filter(
(tab) => !tab.hidden?.({ user: clineUser, activeOrganization }),
).map(renderTabItem)}
value={activeTab}>
{SETTINGS_TABS.filter((tab) => !tab.hidden?.({ activeOrganization })).map(renderTabItem)}
</TabList>
<TabContent className="flex-1 overflow-auto">
{ActiveContent}
</TabContent>
<TabContent className="flex-1 overflow-auto">{ActiveContent}</TabContent>
</div>
</Tab>
);
};
)
}
export default SettingsView;
export default SettingsView
@@ -138,7 +138,7 @@ describe("ApiOptions Component", () => {
)
const modelIdSelect = screen.getByLabelText("Model")
expect(modelIdSelect).toBeInTheDocument()
expect(modelIdSelect).toHaveValue("accounts/fireworks/models/kimi-k2p6")
expect(modelIdSelect).toHaveValue("accounts/fireworks/models/kimi-k2p5")
})
})
@@ -13,7 +13,6 @@ const SUPPORTED_CLAUDE_CODE_THINKING_MODELS = [
...SUPPORTED_ANTHROPIC_THINKING_MODELS,
"sonnet",
"sonnet[1m]",
"claude-fable-5[1m]",
"claude-opus-4-8[1m]",
"claude-opus-4-7[1m]",
"claude-sonnet-4-6[1m]",
+1 -105
View File
@@ -15,109 +15,5 @@
},
"linter": {
"enabled": true
},
"overrides": [
{
"includes": [
"apps/vscode/**"
],
"assist": {
"enabled": true,
"actions": {
"source": {
"organizeImports": "on",
"useSortedAttributes": "on"
}
}
},
"linter": {
"rules": {
"recommended": true,
"correctness": {
"useExhaustiveDependencies": "info",
"noUndeclaredVariables": "off",
"noEmptyPattern": "info",
"useJsxKeyInIterable": "off",
"noInnerDeclarations": "off",
"useHookAtTopLevel": "info",
"useYield": "info",
"noConstructorReturn": "off",
"noInvalidPositionAtImportRule": "off",
"noSwitchDeclarations": "off",
"noUnusedImports": "error"
},
"a11y": "info",
"style": {
"useNodejsImportProtocol": "off",
"useImportType": "off",
"useBlockStatements": "off",
"useNamingConvention": "off",
"useThrowOnlyError": "info",
"useConsistentArrayType": "off",
"noParameterAssign": "off",
"useAsConstAssertion": "off",
"useDefaultParameterLast": "off",
"noNonNullAssertion": "info",
"useEnumInitializers": "off",
"useSelfClosingElements": "info",
"useSingleVarDeclarator": "off",
"useNumberNamespace": "off",
"noInferrableTypes": "off",
"useTemplate": "info",
"noUselessElse": "info"
},
"suspicious": {
"noDoubleEquals": "warn",
"noImplicitAnyLet": "info",
"noThenProperty": "off",
"noAsyncPromiseExecutor": "info",
"noImportAssign": "off",
"noExplicitAny": "info",
"noControlCharactersInRegex": "warn",
"noShadowRestrictedNames": "off",
"noArrayIndexKey": "info",
"noAssignInExpressions": "info",
"useIterableCallbackReturn": "info"
},
"complexity": {
"noUselessConstructor": "info",
"useOptionalChain": "info",
"noBannedTypes": "warn",
"useLiteralKeys": "info",
"noUselessCatch": "info",
"noUselessSwitchCase": "info",
"noStaticOnlyClass": "info"
},
"security": {
"noDangerouslySetInnerHtml": "info"
}
}
},
"formatter": {
"enabled": true,
"indentStyle": "tab",
"indentWidth": 4,
"lineWidth": 130,
"lineEnding": "lf",
"formatWithErrors": true
},
"javascript": {
"formatter": {
"semicolons": "asNeeded",
"arrowParentheses": "always",
"bracketSameLine": true,
"bracketSpacing": true,
"jsxQuoteStyle": "double",
"quoteProperties": "asNeeded",
"trailingCommas": "all"
}
},
"json": {
"formatter": {
"trailingCommas": "none",
"expand": "always"
}
}
}
]
}
}
+132 -138
View File
@@ -17,9 +17,9 @@
"typescript": "^5.9.3",
},
},
"apps/cli": {
"sdk/apps/cli": {
"name": "@cline/cli",
"version": "3.0.22",
"version": "3.0.15",
"bin": {
"cline": "src/index.ts",
},
@@ -41,7 +41,6 @@
"commander": "^14.0.3",
"fzf": "^0.5.2",
"marked": "^15.0.12",
"nanoid": "^5.1.7",
"open": "^10.2.0",
"opentui-spinner": "^0.0.6",
"pino": "^10.3.1",
@@ -55,12 +54,10 @@
"@cline/core": "workspace:*",
"@cline/shared": "workspace:*",
"@microsoft/tui-test": "^0.0.2",
"@types/bun": "^1.3.10",
"@types/react": "19.2.14",
"vitest": "^4.0.18",
},
},
"apps/cline-hub": {
"sdk/apps/cline-hub": {
"name": "@cline/cline-hub",
"version": "0.0.0",
"dependencies": {
@@ -69,7 +66,7 @@
"@cline/shared": "workspace:*",
},
},
"apps/cline-hub/src/webview": {
"sdk/apps/cline-hub/src/webview": {
"name": "@cline/cline-hub-webview",
"version": "0.0.0",
"dependencies": {
@@ -126,7 +123,7 @@
"vite": "^8.0.0",
},
},
"apps/examples/cli-agent": {
"sdk/apps/examples/cli-agent": {
"name": "@cline/example-cli-agent",
"version": "0.0.0",
"dependencies": {
@@ -137,7 +134,7 @@
"typescript": "^5.9.3",
},
},
"apps/examples/cline-core-cli-agent": {
"sdk/apps/examples/cline-core-cli-agent": {
"name": "@cline/example-cline-core-cli-agent",
"version": "0.0.0",
"dependencies": {
@@ -147,7 +144,7 @@
"typescript": "^5.9.3",
},
},
"apps/examples/code-review-bot": {
"sdk/apps/examples/code-review-bot": {
"name": "@cline/example-code-review-bot",
"version": "0.0.0",
"dependencies": {
@@ -158,7 +155,7 @@
"typescript": "^5.9.3",
},
},
"apps/examples/desktop-app": {
"sdk/apps/examples/desktop-app": {
"name": "@cline/code",
"version": "0.0.0",
"dependencies": {
@@ -225,17 +222,15 @@
"devDependencies": {
"@tailwindcss/postcss": "^4.2.0",
"@tauri-apps/cli": "^2.0.0",
"@types/bun": "^1.3.14",
"@types/react": "19.2.14",
"@types/react-dom": "19.2.3",
"postcss": "^8.5",
"tailwindcss": "^4.2.0",
"tw-animate-css": "1.3.3",
"typescript": "5.7.3",
"vitest": "^4.0.18",
},
},
"apps/examples/menubar": {
"sdk/apps/examples/menubar": {
"name": "@cline/menubar",
"version": "0.1.0",
"dependencies": {
@@ -249,7 +244,7 @@
"typescript": "^5.9.3",
},
},
"apps/examples/multi-agent": {
"sdk/apps/examples/multi-agent": {
"name": "@cline/example-multi-agent",
"version": "0.0.0",
"dependencies": {
@@ -259,7 +254,7 @@
"typescript": "^5.9.3",
},
},
"apps/examples/quickstart": {
"sdk/apps/examples/quickstart": {
"name": "@cline/example-quickstart",
"version": "0.0.0",
"dependencies": {
@@ -269,7 +264,7 @@
"typescript": "^5.9.3",
},
},
"apps/examples/vscode": {
"sdk/apps/examples/vscode": {
"name": "@cline/vscode",
"version": "0.0.0",
"dependencies": {
@@ -283,10 +278,9 @@
"@types/node": "^25.3.5",
"@types/vscode": "^1.90.0",
"typescript": "^5.9.3",
"vitest": "^4.0.18",
},
},
"apps/examples/vscode/src/webview": {
"sdk/apps/examples/vscode/src/webview": {
"name": "webview",
"version": "0.0.0",
"dependencies": {
@@ -371,7 +365,7 @@
},
"sdk/packages/agents": {
"name": "@cline/agents",
"version": "0.0.46",
"version": "0.0.42",
"dependencies": {
"@cline/llms": "workspace:*",
"@cline/shared": "workspace:*",
@@ -380,7 +374,7 @@
},
"sdk/packages/core": {
"name": "@cline/core",
"version": "0.0.46",
"version": "0.0.42",
"dependencies": {
"@cline/agents": "workspace:*",
"@cline/llms": "workspace:*",
@@ -411,7 +405,7 @@
},
"sdk/packages/llms": {
"name": "@cline/llms",
"version": "0.0.46",
"version": "0.0.42",
"dependencies": {
"@ai-sdk/amazon-bedrock": "^4.0.89",
"@ai-sdk/anthropic": "^3.0.68",
@@ -445,14 +439,14 @@
},
"sdk/packages/sdk": {
"name": "@cline/sdk",
"version": "0.0.46",
"version": "0.0.42",
"dependencies": {
"@cline/core": "workspace:*",
},
},
"sdk/packages/shared": {
"name": "@cline/shared",
"version": "0.0.46",
"version": "0.0.42",
"dependencies": {
"aws4fetch": "^1.0.20",
"jsonrepair": "^3.13.2",
@@ -464,15 +458,15 @@
"packages": {
"@agentclientprotocol/sdk": ["@agentclientprotocol/sdk@0.16.1", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-1ad+Sc/0sCtZGHthxxvgEUo5Wsbw16I+aF+YwdiLnPwkZG8KAGUEAPK6LM6Pf69lCyJPt1Aomk1d+8oE3C4ZEw=="],
"@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@4.0.112", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.81", "@ai-sdk/openai": "3.0.67", "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-PsSh7a6qW+3kQXPs1kD4wDwuZby0t1PIaB6j/1aMKmPFJ5LxcIcULLMF/bjITLt5o/8lc0t6TXIwG0zlhH7uZw=="],
"@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@4.0.111", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.81", "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cH71k96tcnLuq1x3xi0KP384Jxio8qM6VQzHDUfU4OuX2P83FC/pBvksR5YVRm17GdQliAfR1t5o6z1iJRtfpA=="],
"@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.81", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-B1JDd9Ugq9R5AgIaW3674lhGCMMYJcPUxnrZh8fzbGojgg4QvHFRv6eZahGQAUsmGHbcf74G9bdSBDLWQGY2GA=="],
"@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.123", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-rL+3Sp9crOlfE7MwguFPS30qVp6HFcr9na0KYMb4CcQdxAIjBJec3EEdCjc94UyRVZWLmgQ6Yr605FmPlFIi0w=="],
"@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.121", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-uY248djJRxa5W68MHiyqO8WLdOeKQoRClGg7PVX/VPhVW8SJNM7/l5DcrA5WAM3YfQrLyNkgZa2VOu8T0t8LUw=="],
"@ai-sdk/google": ["@ai-sdk/google@3.0.80", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-5ORbm/yFUPO0MEvZsxBMN0cdKw2+lwU/wVn5KN3KF8Dmk1LughuDuUohMh/7iU/XFTiyB0OvmTW/tdV/J7O9zg=="],
"@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@4.0.141", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.81", "@ai-sdk/google": "3.0.80", "@ai-sdk/openai-compatible": "2.0.48", "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "google-auth-library": "^10.5.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-UGXQeV+z30Gk1/mhKZoXKbYO7Q0U7pg0Nlz44hb2B1FpYRFBu8+ziaI60BdetncaoxPocdNKhP41AD15IA6nJg=="],
"@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@4.0.140", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.81", "@ai-sdk/google": "3.0.80", "@ai-sdk/openai-compatible": "2.0.48", "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "google-auth-library": "^10.5.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Fz3STER9hcrY0uJ6wMg8tSasS5+FbLnBU9N89cw9KBkSUtq+Hefeert7y/UA7RUgPS+d6Z+kzmkqbdAU5dn+oA=="],
"@ai-sdk/mistral": ["@ai-sdk/mistral@3.0.37", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-KkdaMjs4C2y+vrZWJE990E3ZxBFiOTHQ94ZlquuIttpphcqJTMxNoIpnKT/4UzMVWXL0BUEE2vs+1UEVXkN8Kg=="],
@@ -484,7 +478,7 @@
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw=="],
"@ai-sdk/react": ["@ai-sdk/react@3.0.197", "", { "dependencies": { "@ai-sdk/provider-utils": "4.0.27", "ai": "6.0.195", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1" } }, "sha512-sx5K0pvIWbgduMYGz++s28ldj+hu+GfDpXw9T2kL4n+bhRQpQvrH+jU0z3OqvjMrhD5oz9cGJ7bv/0JQEKXIbw=="],
"@ai-sdk/react": ["@ai-sdk/react@3.0.195", "", { "dependencies": { "@ai-sdk/provider-utils": "4.0.27", "ai": "6.0.193", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1" } }, "sha512-+yIH84d4bBNzLKfaDDf4EocEH0XQKKNwNShxbrz5xAiJMNIPnWVWT9cyrSerYaGH3iNVS/g2io42PE4HNbc4RA=="],
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
@@ -520,41 +514,41 @@
"@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="],
"@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.1059.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.16", "@aws-sdk/credential-provider-node": "^3.972.49", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/fetch-http-handler": "^5.4.6", "@smithy/node-http-handler": "^4.7.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-HW3Oq2rL65tbuqkQzoRrjfF3jauRfra056Xv0K2YtBWt+LaeLrr95D8SlRmgGzXZHwy38FO/w0N1EKjsTYmDKw=="],
"@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.1056.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.15", "@aws-sdk/credential-provider-node": "^3.972.46", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/fetch-http-handler": "^5.4.5", "@smithy/node-http-handler": "^4.7.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-Fywg6+B39uGiYZRYFEsOXbIeHQ8wvtMqlt6FUwWev8N2H+V0pVdgCKn32pSOzud1i17wnm5gpB2VXZEoyVHc2A=="],
"@aws-sdk/core": ["@aws-sdk/core@3.974.16", "", { "dependencies": { "@aws-sdk/types": "^3.973.10", "@aws-sdk/xml-builder": "^3.972.27", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/core": "^3.24.6", "@smithy/signature-v4": "^5.4.6", "@smithy/types": "^4.14.3", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-WXPvTfG7J2H4Ae6ewhd0285UC+8+9p/pKoibXXQlbXSqHexFLGM0oXHTwDfQEPmrNnvuWPpVjgoAUfW+cUFbXw=="],
"@aws-sdk/core": ["@aws-sdk/core@3.974.15", "", { "dependencies": { "@aws-sdk/types": "^3.973.9", "@aws-sdk/xml-builder": "^3.972.26", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/core": "^3.24.5", "@smithy/signature-v4": "^5.4.5", "@smithy/types": "^4.14.2", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-UpA0rTGW/tHGITcCqHisbuuEPraYg9GG+mWmXjY5+RxZBMLGe6aL9oe0ix50LztwAcPIkGZLH0yWdMIkCM10hw=="],
"@aws-sdk/credential-provider-cognito-identity": ["@aws-sdk/credential-provider-cognito-identity@3.972.39", "", { "dependencies": { "@aws-sdk/nested-clients": "^3.997.14", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-fp7hiew245BbBiaDGjLDaMXqxbOWnqzuhczWrJq/6/3gDNHtZvkorxHQXYpHYiddetR8sBWe3S49HfZ2BQbYSg=="],
"@aws-sdk/credential-provider-cognito-identity": ["@aws-sdk/credential-provider-cognito-identity@3.972.38", "", { "dependencies": { "@aws-sdk/nested-clients": "^3.997.13", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-OHkK6xOx/IHkSbQdDWxnVCLU+j28EFl8wyWgBILQDFAPY8n240C/O4gjmFx+zFU12lL8njgJQ5GWAIWq88CnSQ=="],
"@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.42", "", { "dependencies": { "@aws-sdk/core": "^3.974.16", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-0+MCOYqeHyADFdeVr5/e1G8JJoWm7/szZAiKssqJS84E9+tO07509YNyQRJ5a7x5wO9YFAV324syrwm6yIcs5w=="],
"@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.41", "", { "dependencies": { "@aws-sdk/core": "^3.974.15", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-n1EbJ98yvPWWdHZZv8bRBMqqDQJrtgtxyJ4xLy2Uqrh25BCOZQ7nnS1CsFXvuH8r0b0KVHDZEGEH5FxmEMP8jg=="],
"@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.44", "", { "dependencies": { "@aws-sdk/core": "^3.974.16", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/fetch-http-handler": "^5.4.6", "@smithy/node-http-handler": "^4.7.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-auuhqlnv4PUdfqcdHLKGTdoCceXOuby6WNeCMoxtZmQGWNCibbz95/lSYzNWq9cExN17UlRFqo1nvTcC+zHfEg=="],
"@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.43", "", { "dependencies": { "@aws-sdk/core": "^3.974.15", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/fetch-http-handler": "^5.4.5", "@smithy/node-http-handler": "^4.7.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-TT76RN1NkI9WoyZqCNxOw6/WBMF7pYOTJcXbMokNFU+euSG40Kaf/t/FhDACVZWP+43wEM6ZynIPIkzS1wR1iA=="],
"@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.47", "", { "dependencies": { "@aws-sdk/core": "^3.974.16", "@aws-sdk/credential-provider-env": "^3.972.42", "@aws-sdk/credential-provider-http": "^3.972.44", "@aws-sdk/credential-provider-login": "^3.972.46", "@aws-sdk/credential-provider-process": "^3.972.42", "@aws-sdk/credential-provider-sso": "^3.972.46", "@aws-sdk/credential-provider-web-identity": "^3.972.46", "@aws-sdk/nested-clients": "^3.997.14", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/credential-provider-imds": "^4.3.7", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-G+8JuG0CfLcC2IQXFVqMR1+KDF1rksebr+YL6+HHYbNjs/hiwX53ye45sU3pJKljpS2uIXqOrOsicHIv02vWrw=="],
"@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.45", "", { "dependencies": { "@aws-sdk/core": "^3.974.15", "@aws-sdk/credential-provider-env": "^3.972.41", "@aws-sdk/credential-provider-http": "^3.972.43", "@aws-sdk/credential-provider-login": "^3.972.45", "@aws-sdk/credential-provider-process": "^3.972.41", "@aws-sdk/credential-provider-sso": "^3.972.45", "@aws-sdk/credential-provider-web-identity": "^3.972.45", "@aws-sdk/nested-clients": "^3.997.13", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/credential-provider-imds": "^4.3.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-sJe5ZWibO4s7RWjFQ8Zol76KxoJcIYyEZH1/wxQSBMSIAAxzaJ8cS/ITAaIHWUQvDKQdt18+cJAHKWB7n1Jmrg=="],
"@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.46", "", { "dependencies": { "@aws-sdk/core": "^3.974.16", "@aws-sdk/nested-clients": "^3.997.14", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-+H6h2/1Q+iIJ4w+FPCKM//xwy4C9yADknDTR68+K3PbOtB/uLup3zIH8PUKn6QwnttME4VM4ftSTnbvoDf5wXg=="],
"@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.45", "", { "dependencies": { "@aws-sdk/core": "^3.974.15", "@aws-sdk/nested-clients": "^3.997.13", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-MZQv4SNjByk1iOKmrqmzcUF/uCB05wjvEHyXKxmGQTUANTIVayX6HPUF0bzkWLvtnkH7sAn9kUCfkXbSpj9sDA=="],
"@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.49", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.42", "@aws-sdk/credential-provider-http": "^3.972.44", "@aws-sdk/credential-provider-ini": "^3.972.47", "@aws-sdk/credential-provider-process": "^3.972.42", "@aws-sdk/credential-provider-sso": "^3.972.46", "@aws-sdk/credential-provider-web-identity": "^3.972.46", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/credential-provider-imds": "^4.3.7", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-hlyoc+2352BhC+HF91t9avIDJu1+EvQGGltxFB8ADCpAHGYNNLEQAZJIPzw3O/bRiiDSE2B8pdj/fFCXlDTEDg=="],
"@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.46", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.41", "@aws-sdk/credential-provider-http": "^3.972.43", "@aws-sdk/credential-provider-ini": "^3.972.45", "@aws-sdk/credential-provider-process": "^3.972.41", "@aws-sdk/credential-provider-sso": "^3.972.45", "@aws-sdk/credential-provider-web-identity": "^3.972.45", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/credential-provider-imds": "^4.3.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-cS4w0jzDRb1jOlkiJS3y80OxddHzkky/MN9k3NYs5jganNKVLjF0lpvjlwS118oGMr3cdAfOlVdo8gLurTSE7w=="],
"@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.42", "", { "dependencies": { "@aws-sdk/core": "^3.974.16", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-r996IYVtQ7rWa5UfSs3fZLT/Dq/SSgH9wv9zahx9lcg6vvaPPQHFpTy45nZajZu/+OUdEQxEjTn9rOMons59mA=="],
"@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.41", "", { "dependencies": { "@aws-sdk/core": "^3.974.15", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-7I/n1zkysouLOWvkEhjNEP4vMnD2v4kzzr3/3QBdrripEpn7ap1/I5DF3Hou1SUqkKWo1f3oPGMyFAA1FAMvsQ=="],
"@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.46", "", { "dependencies": { "@aws-sdk/core": "^3.974.16", "@aws-sdk/nested-clients": "^3.997.14", "@aws-sdk/token-providers": "3.1059.0", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-vd+UqRbiYLvXXH3kTAuTxq+Vdb3rOgg29/FeB35ETsgdJTTB7x3YuqtpRckATsL5bDRXFVQo0uBj0/vxCJ8hHg=="],
"@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.45", "", { "dependencies": { "@aws-sdk/core": "^3.974.15", "@aws-sdk/nested-clients": "^3.997.13", "@aws-sdk/token-providers": "3.1056.0", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-oHgbz/eFD8IKiksqDsz9ZMU4A59BpQq4QwJedBnGD80ZqYcHPPHZBwjBnxLVkB7iRVVHWpDclR8yWdD2PkQIUA=="],
"@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.46", "", { "dependencies": { "@aws-sdk/core": "^3.974.16", "@aws-sdk/nested-clients": "^3.997.14", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-xuxBbMorygYsbDV6E/tUGQUgIDfhnzxz8uJYX8rByzehI6gLUu8RPsgOpLmh9YWerqeHZxJbqzgheBSB7tpooQ=="],
"@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.45", "", { "dependencies": { "@aws-sdk/core": "^3.974.15", "@aws-sdk/nested-clients": "^3.997.13", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-CDhzKdb2onv5bpnjn/acgdNmJOQthPDLsPizU7rZflsEcgMMp8Mlri+U5hdxf8ldvZJpvM3vLU6D56vfJm5AMQ=="],
"@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.1059.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.1059.0", "@aws-sdk/core": "^3.974.16", "@aws-sdk/credential-provider-cognito-identity": "^3.972.39", "@aws-sdk/credential-provider-env": "^3.972.42", "@aws-sdk/credential-provider-http": "^3.972.44", "@aws-sdk/credential-provider-ini": "^3.972.47", "@aws-sdk/credential-provider-login": "^3.972.46", "@aws-sdk/credential-provider-node": "^3.972.49", "@aws-sdk/credential-provider-process": "^3.972.42", "@aws-sdk/credential-provider-sso": "^3.972.46", "@aws-sdk/credential-provider-web-identity": "^3.972.46", "@aws-sdk/nested-clients": "^3.997.14", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/credential-provider-imds": "^4.3.7", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-cSdDFb/O3cQHGg78VxPaNruyy9zGxcoeS7DlwYTdwxmYkE0GXmBRoej5N+q+Av9YWBayZ2n3QsSYhtnUXa/GXw=="],
"@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.1056.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.1056.0", "@aws-sdk/core": "^3.974.15", "@aws-sdk/credential-provider-cognito-identity": "^3.972.38", "@aws-sdk/credential-provider-env": "^3.972.41", "@aws-sdk/credential-provider-http": "^3.972.43", "@aws-sdk/credential-provider-ini": "^3.972.45", "@aws-sdk/credential-provider-login": "^3.972.45", "@aws-sdk/credential-provider-node": "^3.972.46", "@aws-sdk/credential-provider-process": "^3.972.41", "@aws-sdk/credential-provider-sso": "^3.972.45", "@aws-sdk/credential-provider-web-identity": "^3.972.45", "@aws-sdk/nested-clients": "^3.997.13", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/credential-provider-imds": "^4.3.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-Qp7ndCG+dZldiaURze6BM/dLkHQJxwi6WNRR1sR9lhX9jS9QG5ZIOiY3jm6T668vgGqHuNQS7r/P9pimxnHyyg=="],
"@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.14", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.16", "@aws-sdk/signature-v4-multi-region": "^3.996.31", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/fetch-http-handler": "^5.4.6", "@smithy/node-http-handler": "^4.7.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-T5CS1r4P27FjkBYIWwVibWqEuq32BbCga2Z5m5OBSdSdi2wPfW2vl6zLWAB/5MeeyC4s2pY/MY3cj2Gd3rgSkg=="],
"@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.13", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.15", "@aws-sdk/signature-v4-multi-region": "^3.996.30", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/fetch-http-handler": "^5.4.5", "@smithy/node-http-handler": "^4.7.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-2pA6eyb5nSo/ZD2cayhOTEMoGQYgspq0RI05GDLkzQ3ajZ6isS6waV6E92Am/hz4LIlLUTrbwPLurJ/fuiHvkg=="],
"@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.31", "", { "dependencies": { "@aws-sdk/types": "^3.973.10", "@smithy/signature-v4": "^5.4.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-Kn2up9SlG1KC6wRtwf0d7waTGF6rvp9DxYqB54x6UCKdQ6kyaXCqHL4WGb5vUJga5kS8FxnjhY0LqM28aMvnNQ=="],
"@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.30", "", { "dependencies": { "@aws-sdk/types": "^3.973.9", "@smithy/signature-v4": "^5.4.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-HULDLMVzkmTSEv6//7kx2kRevp/VYUpm8hJNNFbmhxDn0fUiGTxVcM9yg31TukvTq8nyOBDUN2gH0o5IRbKjdw=="],
"@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1059.0", "", { "dependencies": { "@aws-sdk/core": "^3.974.16", "@aws-sdk/nested-clients": "^3.997.14", "@aws-sdk/types": "^3.973.10", "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-xql+7YBAE7WYb9xJfY0vcAXM8rJXfClmB2wkt+g/EoLUMog0pOb7o741fE96wFqha0uQTEgTo/5lGGguzavxmw=="],
"@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1056.0", "", { "dependencies": { "@aws-sdk/core": "^3.974.15", "@aws-sdk/nested-clients": "^3.997.13", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-81duvlltQlsfn5K+o8zILcystBRdbT1G2JJYVCML5NZHBz4CL/zf+sAemCtBh/uh6RQUMyInGeZLQ7/8igZhbA=="],
"@aws-sdk/types": ["@aws-sdk/types@3.973.10", "", { "dependencies": { "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-992QrTO7G9qCvKD0fx1rMlqcL14plUcRAbwmqqYVsuF3GrqcvlAL9qxR+baMafarEZ+l7DUQ5lCMmt5mbMhF7g=="],
"@aws-sdk/types": ["@aws-sdk/types@3.973.9", "", { "dependencies": { "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-kuBfgQVdcz5Bmapc4A13YbpVw/pXkesfhetcFYwbntqas8sF41OHyd4o28+/TG2ZQdHBsv90Lsu5y6oitvYCdg=="],
"@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.965.5", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ=="],
"@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.27", "", { "dependencies": { "@smithy/types": "^4.14.3", "fast-xml-parser": "5.7.3", "tslib": "^2.6.2" } }, "sha512-hpsCXCOI436kxWpjtRuIHVvuPP81MOw8f18jzfZeg+UOiiOvlqWcmWChzEhJEu16cOC6+ku4ncBN+7rdt+DZ9g=="],
"@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.26", "", { "dependencies": { "@smithy/types": "^4.14.2", "fast-xml-parser": "5.7.3", "tslib": "^2.6.2" } }, "sha512-cDbrqvDS73whl6YAPSPq0U6whzG6UWI9PuWh0wrUuGoZexhWEqhdunbukV7iBoaWnFV1AODutM5hOD6rtn439g=="],
"@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.4", "", {}, "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ=="],
@@ -640,19 +634,19 @@
"@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.2", "", {}, "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA=="],
"@chat-adapter/discord": ["@chat-adapter/discord@4.30.0", "", { "dependencies": { "@chat-adapter/shared": "4.30.0", "chat": "4.30.0", "discord-api-types": "^0.37.119", "discord-interactions": "^4.4.0", "discord.js": "^14.25.1" } }, "sha512-hWMwCwVgMeQWB5F2JL91GLTWcF8rlE4eewzXzfPSoAsM7Y71yBDCfQ9QfL5VODwPKtYnVPU5Go2pGsuauaZ/yw=="],
"@chat-adapter/discord": ["@chat-adapter/discord@4.29.0", "", { "dependencies": { "@chat-adapter/shared": "4.29.0", "chat": "4.29.0", "discord-api-types": "^0.37.119", "discord-interactions": "^4.4.0", "discord.js": "^14.25.1" } }, "sha512-C6pDVKn7s0PhvgeqldYxoShWXJzthwtlo+sCesqenh56M03eHsioinqxeJttmtK8y1ZD8/qiP2lAZufDwuEQhA=="],
"@chat-adapter/gchat": ["@chat-adapter/gchat@4.30.0", "", { "dependencies": { "@chat-adapter/shared": "4.30.0", "@googleapis/chat": "^44.6.0", "@googleapis/workspaceevents": "^9.1.0", "chat": "4.30.0" } }, "sha512-s3rqHJqW17RE1drBhS9H+YYDpiasfd4YBaXdLrNqao5AS5sNWR/ekWiDGPeF/zlzDkOUNdnyDq3NbA7/2HsHsQ=="],
"@chat-adapter/gchat": ["@chat-adapter/gchat@4.29.0", "", { "dependencies": { "@chat-adapter/shared": "4.29.0", "@googleapis/chat": "^44.6.0", "@googleapis/workspaceevents": "^9.1.0", "chat": "4.29.0" } }, "sha512-SZ5ZgzFmZ4oRj0AAISbjPztPiICTpw+RdPiPywNwqGWt2LItUOL+8+dl8zZMk+F86E/r0sqRjdbc8+9GqUe2pA=="],
"@chat-adapter/linear": ["@chat-adapter/linear@4.30.0", "", { "dependencies": { "@chat-adapter/shared": "4.30.0", "@linear/sdk": "^76.0.0", "chat": "4.30.0" } }, "sha512-L0/B71Sdx8XuMbhaw0YShzR18DwhBS+Yfna61SnTpyoS+BzIRKxP1xEQU6LYGCiOiS+587GZOUm2vSWPakMAWw=="],
"@chat-adapter/linear": ["@chat-adapter/linear@4.29.0", "", { "dependencies": { "@chat-adapter/shared": "4.29.0", "@linear/sdk": "^76.0.0", "chat": "4.29.0" } }, "sha512-4cq7pBv0CICka8V0uuae7bNfb1rTdmYjsNjOs7Gkr4vChlhDxRUEGUoiBqvOEYKqxFtFOkRk9Wod0tIQmbPySw=="],
"@chat-adapter/shared": ["@chat-adapter/shared@4.30.0", "", { "dependencies": { "chat": "4.30.0" } }, "sha512-IuYtbn/p1FBXvp7JYGEMLCt07GHOMlyjx7OlZXPJwLTravcyJuP7Q6N31r6c1yubMhM8PLb8eT8l/YnjwYjs9Q=="],
"@chat-adapter/shared": ["@chat-adapter/shared@4.29.0", "", { "dependencies": { "chat": "4.29.0" } }, "sha512-ARqTDoHJHKN9rpytbFPJbNmqqx3fOg5xwsTZdlingQPAssOSeHDBdqrFJkgDhyCRGbmDtG09cuS0FkVzeoh2qg=="],
"@chat-adapter/slack": ["@chat-adapter/slack@4.30.0", "", { "dependencies": { "@chat-adapter/shared": "4.30.0", "@slack/socket-mode": "^2.0.5", "@slack/web-api": "^7.14.0", "chat": "4.30.0" } }, "sha512-ZB+G/JBKmaXzvl+DuUQPBb/gCwXP3fOtUK5Cyj6wLbbeeDYzi3NQPvpvieVum/GI4iuR8OUVcNAnVQiH6P6DOQ=="],
"@chat-adapter/slack": ["@chat-adapter/slack@4.29.0", "", { "dependencies": { "@chat-adapter/shared": "4.29.0", "@slack/socket-mode": "^2.0.5", "@slack/web-api": "^7.14.0", "chat": "4.29.0" } }, "sha512-s2DXAwkTpmiIKSATXgrO879s1pqFwS70Y0JPd+TRGRzDeh6nfqt5dnKt5Bug0P1zwkB6DoPurhnYS9nqhSmD/w=="],
"@chat-adapter/telegram": ["@chat-adapter/telegram@4.30.0", "", { "dependencies": { "@chat-adapter/shared": "4.30.0", "chat": "4.30.0" } }, "sha512-OX98fYMorz3gRvNoCVidYKOnb89Rf9UZQaUDQMhtk5IsgHX0k2qiUpEb2VOquMHyH8Pb7xkqrG52NFLnzGpspQ=="],
"@chat-adapter/telegram": ["@chat-adapter/telegram@4.29.0", "", { "dependencies": { "@chat-adapter/shared": "4.29.0", "chat": "4.29.0" } }, "sha512-015tU3HEjFQWw7DgebXWkDeQA6lTdTVEO4btAV3f6U1kEnOfjLVJq98latcsM1WkEvv+3LIFKpsz9pG53aamBw=="],
"@chat-adapter/whatsapp": ["@chat-adapter/whatsapp@4.30.0", "", { "dependencies": { "@chat-adapter/shared": "4.30.0", "chat": "4.30.0" } }, "sha512-4iXroN/FYRsWdhgVFUNzf4VXX67a9u6xcYoBGgX2eqTwlq5THywYSe7FSA5GP7Hg0SK0mRExdpR/Hvk9+tP9xw=="],
"@chat-adapter/whatsapp": ["@chat-adapter/whatsapp@4.29.0", "", { "dependencies": { "@chat-adapter/shared": "4.29.0", "chat": "4.29.0" } }, "sha512-CDKmlDHmiiJ2xtca0wZ2DIYnySUFuiKn6f3ZQvschFVa6pSaDmaldmJc3iul+ELi10+RE7x5+immfc//+TTzFg=="],
"@chevrotain/types": ["@chevrotain/types@11.1.2", "", {}, "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw=="],
@@ -662,35 +656,35 @@
"@cline/agents": ["@cline/agents@workspace:sdk/packages/agents"],
"@cline/cli": ["@cline/cli@workspace:apps/cli"],
"@cline/cli": ["@cline/cli@workspace:sdk/apps/cli"],
"@cline/cline-hub": ["@cline/cline-hub@workspace:apps/cline-hub"],
"@cline/cline-hub": ["@cline/cline-hub@workspace:sdk/apps/cline-hub"],
"@cline/cline-hub-webview": ["@cline/cline-hub-webview@workspace:apps/cline-hub/src/webview"],
"@cline/cline-hub-webview": ["@cline/cline-hub-webview@workspace:sdk/apps/cline-hub/src/webview"],
"@cline/code": ["@cline/code@workspace:apps/examples/desktop-app"],
"@cline/code": ["@cline/code@workspace:sdk/apps/examples/desktop-app"],
"@cline/core": ["@cline/core@workspace:sdk/packages/core"],
"@cline/example-cli-agent": ["@cline/example-cli-agent@workspace:apps/examples/cli-agent"],
"@cline/example-cli-agent": ["@cline/example-cli-agent@workspace:sdk/apps/examples/cli-agent"],
"@cline/example-cline-core-cli-agent": ["@cline/example-cline-core-cli-agent@workspace:apps/examples/cline-core-cli-agent"],
"@cline/example-cline-core-cli-agent": ["@cline/example-cline-core-cli-agent@workspace:sdk/apps/examples/cline-core-cli-agent"],
"@cline/example-code-review-bot": ["@cline/example-code-review-bot@workspace:apps/examples/code-review-bot"],
"@cline/example-code-review-bot": ["@cline/example-code-review-bot@workspace:sdk/apps/examples/code-review-bot"],
"@cline/example-multi-agent": ["@cline/example-multi-agent@workspace:apps/examples/multi-agent"],
"@cline/example-multi-agent": ["@cline/example-multi-agent@workspace:sdk/apps/examples/multi-agent"],
"@cline/example-quickstart": ["@cline/example-quickstart@workspace:apps/examples/quickstart"],
"@cline/example-quickstart": ["@cline/example-quickstart@workspace:sdk/apps/examples/quickstart"],
"@cline/llms": ["@cline/llms@workspace:sdk/packages/llms"],
"@cline/menubar": ["@cline/menubar@workspace:apps/examples/menubar"],
"@cline/menubar": ["@cline/menubar@workspace:sdk/apps/examples/menubar"],
"@cline/sdk": ["@cline/sdk@workspace:sdk/packages/sdk"],
"@cline/shared": ["@cline/shared@workspace:sdk/packages/shared"],
"@cline/vscode": ["@cline/vscode@workspace:apps/examples/vscode"],
"@cline/vscode": ["@cline/vscode@workspace:sdk/apps/examples/vscode"],
"@colors/colors": ["@colors/colors@1.6.0", "", {}, "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA=="],
@@ -712,7 +706,7 @@
"@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.71.0", "", { "dependencies": { "commander": "^11.1.0", "dotenv": "^17.2.1", "eciesjs": "^0.4.10", "enquirer": "^2.4.1", "execa": "^5.1.1", "fdir": "^6.2.0", "ignore": "^5.3.0", "object-treeify": "1.1.33", "picomatch": "^4.0.4", "which": "^4.0.0", "yocto-spinner": "^1.1.0" }, "bin": { "dotenvx": "src/cli/dotenvx.js" } }, "sha512-KEUw/mGu+EDRhYWRTNGHIimVCs9NvMFaIXOGrHSXoCteKLE5EsJnmPjOPpYorjXVg/0xG0fbdVw720azw1z4ag=="],
"@dotenvx/dotenvx": ["@dotenvx/dotenvx@1.69.1", "", { "dependencies": { "commander": "^11.1.0", "dotenv": "^17.2.1", "eciesjs": "^0.4.10", "enquirer": "^2.4.1", "execa": "^5.1.1", "fdir": "^6.2.0", "ignore": "^5.3.0", "object-treeify": "1.1.33", "picomatch": "^4.0.4", "which": "^4.0.0", "yocto-spinner": "^1.1.0" }, "bin": { "dotenvx": "src/cli/dotenvx.js" } }, "sha512-kwQB5KcAegxw/+NGUgXAo5ovyOSjlMhoXSSnSEpDhoHJwzMcMO0HE1U0VCYZ7jbAeCMGamed9XdWzOA5ixtTNg=="],
"@ecies/ciphers": ["@ecies/ciphers@0.2.6", "", { "peerDependencies": { "@noble/ciphers": "^1.0.0" } }, "sha512-patgsRPKGkhhoBjETV4XxD0En4ui5fbX0hzayqI3M8tvNMGUoUvmyYAIWwlxBc1KX5cturfqByYdj5bYGRpN9g=="],
@@ -880,15 +874,15 @@
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="],
"@inquirer/ansi": ["@inquirer/ansi@2.0.7", "", {}, "sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q=="],
"@inquirer/ansi": ["@inquirer/ansi@2.0.6", "", {}, "sha512-I/INw4sHGlVZ/afZOckpLiDP9SmbMl1g/GCqeHjLw1Afw/0PlRs2tRFgTGWmdI0hoNuWZn3y2iHNmG1vyECyQQ=="],
"@inquirer/confirm": ["@inquirer/confirm@6.1.1", "", { "dependencies": { "@inquirer/core": "^11.2.1", "@inquirer/type": "^4.0.7" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ=="],
"@inquirer/confirm": ["@inquirer/confirm@6.1.0", "", { "dependencies": { "@inquirer/core": "^11.2.0", "@inquirer/type": "^4.0.6" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-USpeB76eqK7yGricDlGAupxWlp4a59qpeZOoNWaxO/nJln7agpJveyNkQ1d5u8YXG6TOqxZtQpKPORQQDrdVsA=="],
"@inquirer/core": ["@inquirer/core@11.2.1", "", { "dependencies": { "@inquirer/ansi": "^2.0.7", "@inquirer/figures": "^2.0.7", "@inquirer/type": "^4.0.7", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA=="],
"@inquirer/core": ["@inquirer/core@11.2.0", "", { "dependencies": { "@inquirer/ansi": "^2.0.6", "@inquirer/figures": "^2.0.6", "@inquirer/type": "^4.0.6", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^4.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-joR1YS2sI0us+9d0I8ViqFbrRLONO8CFTuyvBX4ZVBSch+VsZiugUABdrhBXXJR1VyEzvpz5SQCix3keETQ58g=="],
"@inquirer/figures": ["@inquirer/figures@2.0.7", "", {}, "sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw=="],
"@inquirer/figures": ["@inquirer/figures@2.0.6", "", {}, "sha512-dsZgQtH2t5Q6ah3aPbZbeEZAxsD9qQu0DXf01AltuEfRTm+NoLN6+rLVbr+4edeEbNCp/wBNM6mALRWtsQpfkw=="],
"@inquirer/type": ["@inquirer/type@4.0.7", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g=="],
"@inquirer/type": ["@inquirer/type@4.0.6", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-J+9tdxOskuYuGjsvGaq00AamhDgjR7anhEW2dP4QdQpFCMPngCeC/bCYWQ5NsMWZRdsy53is7kAHb/+7cwDk2g=="],
"@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
@@ -1038,7 +1032,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.15.13", "", { "dependencies": { "cross-spawn": "7.0.6" } }, "sha512-4TwojIoQ8EG6/mVBuUVYZXiFcwNmiiytEnjnvyuvSJjGwFIlw2YIBFxtSVC3FbwwbwHT63teh1RHiQUUC4U5xw=="],
"@opencode-ai/sdk": ["@opencode-ai/sdk@1.15.12", "", { "dependencies": { "cross-spawn": "7.0.6" } }, "sha512-lOaBNX93dkakZe6C42ttX1bkSx3K2c6+Yv+w8Qv02v5rPlu1vCXbmdfYDh9/bw+oq+NKPSaBm9d6kPA19hA5Lg=="],
"@opentelemetry/api": ["@opentelemetry/api@1.9.1", "", {}, "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q=="],
@@ -1088,7 +1082,7 @@
"@opentui/react": ["@opentui/react@0.1.102", "", { "dependencies": { "@opentui/core": "0.1.102", "react-reconciler": "^0.32.0" }, "peerDependencies": { "react": ">=19.0.0", "react-devtools-core": "^7.0.1", "ws": "^8.18.0" } }, "sha512-c7EiK30xlvaHc6WBOLAH7TJ8xC4QbiD5ZA6tb4zZ74XM4SH5Tb+uBee8b4TztTLseq+hGtW85qrIUJM3OrdtNw=="],
"@oxc-project/types": ["@oxc-project/types@0.133.0", "", {}, "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA=="],
"@oxc-project/types": ["@oxc-project/types@0.132.0", "", {}, "sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ=="],
"@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="],
@@ -1238,35 +1232,35 @@
"@rive-app/webgl2": ["@rive-app/webgl2@2.37.8", "", {}, "sha512-Y2nXPwAeQtZrADNIzY7v3Lk7XZRMy4Gd+bpGFyzkaQ/EQ91Hp/6sCdd8yTCyjH4b71N/T6kU0MHIYA0IDmpjyQ=="],
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.3", "", { "os": "android", "cpu": "arm64" }, "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw=="],
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.2", "", { "os": "android", "cpu": "arm64" }, "sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ=="],
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA=="],
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w=="],
"@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg=="],
"@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA=="],
"@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g=="],
"@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA=="],
"@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.3", "", { "os": "linux", "cpu": "arm" }, "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw=="],
"@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.2", "", { "os": "linux", "cpu": "arm" }, "sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w=="],
"@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw=="],
"@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig=="],
"@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q=="],
"@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw=="],
"@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg=="],
"@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA=="],
"@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg=="],
"@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ=="],
"@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg=="],
"@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ=="],
"@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow=="],
"@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw=="],
"@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.3", "", { "os": "none", "cpu": "arm64" }, "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg=="],
"@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.2", "", { "os": "none", "cpu": "arm64" }, "sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w=="],
"@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.3", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg=="],
"@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.2", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ=="],
"@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g=="],
"@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A=="],
"@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA=="],
"@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.2", "", { "os": "win32", "cpu": "x64" }, "sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ=="],
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="],
@@ -1334,25 +1328,25 @@
"@slack/web-api": ["@slack/web-api@7.16.0", "", { "dependencies": { "@slack/logger": "^4.0.1", "@slack/types": "^2.21.0", "@types/node": ">=18", "@types/retry": "0.12.0", "axios": "^1.16.0", "eventemitter3": "^5.0.1", "form-data": "^4.0.4", "is-electron": "2.2.2", "is-stream": "^2", "p-queue": "^6", "p-retry": "^4", "retry": "^0.13.1" } }, "sha512-68SAV77uuGKuhyyaRytX8UijVnqSLsTSKslGXw17cjQYXn+jtNl7gbaEjHgC5x2rhCuFdahBrEC2VCLppbzReg=="],
"@smithy/core": ["@smithy/core@3.24.6", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-wBXDRup6UU97VKyaiRo8AssnfStPtG0oAAfpq/bC0a1YYau8pM86YB4kM6ccoVi1mS8l/UHbn9oDM+7uozr/ug=="],
"@smithy/core": ["@smithy/core@3.24.5", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-Kt8phUg45M15EjhYAbZ+fFikYneijLu9Liugz8ZsYz2i8j0hzGv27LWKpEHYRfvj+LyCOSijpcR/2i8RouV+cA=="],
"@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.3.7", "", { "dependencies": { "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-xj8gq/bjFABAh6qWPSDCYcY3kzQIm4b561C+YnHH4zGq8rOgzQ3Shk+JGlpUxSd41UGiO6FkLdUCtNX1FAeHgg=="],
"@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.3.6", "", { "dependencies": { "@smithy/core": "^3.24.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-tHhdiWZfG1ZIh2YcRfPJmY2gHcBmqbAzqm3ER4TIDFYsSEqTD5tICT7cgQ/kI8LRakxp12myOYyK68XPn7MnHw=="],
"@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.3.6", "", { "dependencies": { "@smithy/core": "^3.24.6", "tslib": "^2.6.2" } }, "sha512-Ussyv240JxwQP8AmkYdm26wGP/1I8QmIv0ZosgDJDlSzD73FEdj1BOpXMc06VrxX5KxTKhadFNomT2SWutUnpg=="],
"@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.3.5", "", { "dependencies": { "@smithy/core": "^3.24.5", "tslib": "^2.6.2" } }, "sha512-sM98Snchk/k9dFKwf3F2pyDUaiKilgOU/I+EAQin8Y1XXYusIP5EVRMpjKFVeIHXDnwijxg+6RmIM6HCDvYQoA=="],
"@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.4.6", "", { "dependencies": { "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-FEwEYJ1jlBKdhe9TPzfghEi1bP55ZeEImlDkEa62bBBYzUcnB6RUCyuiS2mqKt6ZVjUbBgcNhzfIctH+Hevx9g=="],
"@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.4.5", "", { "dependencies": { "@smithy/core": "^3.24.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-SK3VMeH0fibgdTg2QeB+O4p7Yy/2E5HBOHJeC58FshkDdeuX8lOgO7PfjYfLyPLP1ch55j91cQqKBzDS0mRjSQ=="],
"@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
"@smithy/node-http-handler": ["@smithy/node-http-handler@4.7.6", "", { "dependencies": { "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-3fya8i7GrJilQouk4cZJKdy5k8MWQBpjfXrRNaXDedH8r779tr0jcxyH3+yoTmsluc2+vF4S343yFbnvu8ExDQ=="],
"@smithy/node-http-handler": ["@smithy/node-http-handler@4.7.5", "", { "dependencies": { "@smithy/core": "^3.24.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-3dA9TQ+ybRSZ/m0wnbZhiBy4Dezjgq1Ib/ZZrYTpJDBgpoLLU/SDzZc/g0x0MNAdOJe1wPcM+x2PBRmoOur+Sw=="],
"@smithy/signature-v4": ["@smithy/signature-v4@5.4.6", "", { "dependencies": { "@smithy/core": "^3.24.6", "@smithy/types": "^4.14.3", "tslib": "^2.6.2" } }, "sha512-Ojg4B6oIDlIr1R86xCDJt1zJWnYa0VINmqdjfe9qxWjdRivHalZ3iSlQgVqYbW0MdpFOC5XfHEWsnbmdnpIILQ=="],
"@smithy/signature-v4": ["@smithy/signature-v4@5.4.5", "", { "dependencies": { "@smithy/core": "^3.24.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-QBJKWGqIknH0dc9LWpfH1mkdokAx6iXYN3UcQ3eY6uIEyScuoQAhfl94ge7ozUy9WgFUdE8xsvwBjaYBbWmPNA=="],
"@smithy/types": ["@smithy/types@4.14.3", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YupL0ZWmFtJexUN2cHzkvvF/b9pKrtAIfT1o7/oY/Ppu8IYeZ+lDPM5vZdQJaSeA132dJCqojjGC9NhXeF71VQ=="],
"@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
"@smithy/util-utf8": ["@smithy/util-utf8@4.3.6", "", { "dependencies": { "@smithy/core": "^3.24.6", "tslib": "^2.6.2" } }, "sha512-tAa4sePYB7mlJzdYbdBqdv37KwFKWixmM/r3ihcI0HFOVjf+a5oGvtcLXcGm4S1bY4DFsLAIOHgjubtp+oRufw=="],
"@smithy/util-utf8": ["@smithy/util-utf8@4.3.5", "", { "dependencies": { "@smithy/core": "^3.24.5", "tslib": "^2.6.2" } }, "sha512-l1d7I7YP2LjXjAZDC7eXqkzuEB75KfCANwhNj/knmT6+0a9XG3QasvI8kEn8WAI3tx/q8PdmSuuXcM+MTkk/7Q=="],
"@so-ric/colorspace": ["@so-ric/colorspace@1.1.6", "", { "dependencies": { "color": "^5.0.2", "text-hex": "1.0.x" } }, "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw=="],
@@ -1596,25 +1590,25 @@
"@types/yargs-parser": ["@types/yargs-parser@21.0.3", "", {}, "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.60.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.60.1", "@typescript-eslint/type-utils": "8.60.1", "@typescript-eslint/utils": "8.60.1", "@typescript-eslint/visitor-keys": "8.60.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.60.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.60.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.60.0", "@typescript-eslint/type-utils": "8.60.0", "@typescript-eslint/utils": "8.60.0", "@typescript-eslint/visitor-keys": "8.60.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.60.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-QYb/sa74/s7OKMbACMjrYnGspj9Hs5YI5aaffSL65UfeBUzVzBJfVo3oWSpbzPurvm7yaCCo2Lk7lVj610HqKw=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.60.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.60.1", "@typescript-eslint/types": "8.60.1", "@typescript-eslint/typescript-estree": "8.60.1", "@typescript-eslint/visitor-keys": "8.60.1", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.60.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.60.0", "@typescript-eslint/types": "8.60.0", "@typescript-eslint/typescript-estree": "8.60.0", "@typescript-eslint/visitor-keys": "8.60.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-fcqpj/MyK4sxDPcbe7STNPbpQL4RLZOPWuaTmwZYuc+hJKzRf58yRxfhqGpc6PIq9ZyfSBpfHgmUHmHs0KwHwg=="],
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.60.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.60.1", "@typescript-eslint/types": "^8.60.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw=="],
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.60.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.60.0", "@typescript-eslint/types": "^8.60.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-aZu74NNKJeUWqCjDddzdiKaS82dgYgV/vmf+Ui3ZdZejmgfXR/q+pRumgobnQ2cCJTgGTWp4ypiwsuofFubavg=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.60.1", "", { "dependencies": { "@typescript-eslint/types": "8.60.1", "@typescript-eslint/visitor-keys": "8.60.1" } }, "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.60.0", "", { "dependencies": { "@typescript-eslint/types": "8.60.0", "@typescript-eslint/visitor-keys": "8.60.0" } }, "sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw=="],
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.60.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA=="],
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.60.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ=="],
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.60.1", "", { "dependencies": { "@typescript-eslint/types": "8.60.1", "@typescript-eslint/typescript-estree": "8.60.1", "@typescript-eslint/utils": "8.60.1", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A=="],
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.60.0", "", { "dependencies": { "@typescript-eslint/types": "8.60.0", "@typescript-eslint/typescript-estree": "8.60.0", "@typescript-eslint/utils": "8.60.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-SX46wEUtitCpq7AN38HkUU/+zvUpdKf7ephtWAFgckH8O7PQIyL5gvrhQgBLuEYgLfuKWOVvWVskMbuFHAz5xg=="],
"@typescript-eslint/types": ["@typescript-eslint/types@8.60.1", "", {}, "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w=="],
"@typescript-eslint/types": ["@typescript-eslint/types@8.60.0", "", {}, "sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA=="],
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.60.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.60.1", "@typescript-eslint/tsconfig-utils": "8.60.1", "@typescript-eslint/types": "8.60.1", "@typescript-eslint/visitor-keys": "8.60.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew=="],
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.60.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.60.0", "@typescript-eslint/tsconfig-utils": "8.60.0", "@typescript-eslint/types": "8.60.0", "@typescript-eslint/visitor-keys": "8.60.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g=="],
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.60.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.60.1", "@typescript-eslint/types": "8.60.1", "@typescript-eslint/typescript-estree": "8.60.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg=="],
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.60.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.60.0", "@typescript-eslint/types": "8.60.0", "@typescript-eslint/typescript-estree": "8.60.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.60.1", "", { "dependencies": { "@typescript-eslint/types": "8.60.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.60.0", "", { "dependencies": { "@typescript-eslint/types": "8.60.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg=="],
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.1", "", {}, "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ=="],
@@ -1626,19 +1620,19 @@
"@vitejs/plugin-react-swc": ["@vitejs/plugin-react-swc@4.3.1", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.0", "@swc/core": "^1.15.11" }, "peerDependencies": { "vite": "^4 || ^5 || ^6 || ^7 || ^8" } }, "sha512-PaeokKjAGraNN+s5SIApgsktnJprIyt3zgEIu7awnEdfn29QiB2crTcCzyi2XGpX9rUnTc0cKU07Wm0N0g7H2w=="],
"@vitest/expect": ["@vitest/expect@4.1.8", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.8", "@vitest/utils": "4.1.8", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ=="],
"@vitest/expect": ["@vitest/expect@4.1.7", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.7", "@vitest/utils": "4.1.7", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w=="],
"@vitest/mocker": ["@vitest/mocker@4.1.8", "", { "dependencies": { "@vitest/spy": "4.1.8", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw=="],
"@vitest/mocker": ["@vitest/mocker@4.1.7", "", { "dependencies": { "@vitest/spy": "4.1.7", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA=="],
"@vitest/pretty-format": ["@vitest/pretty-format@4.1.8", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA=="],
"@vitest/pretty-format": ["@vitest/pretty-format@4.1.7", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw=="],
"@vitest/runner": ["@vitest/runner@4.1.8", "", { "dependencies": { "@vitest/utils": "4.1.8", "pathe": "^2.0.3" } }, "sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg=="],
"@vitest/runner": ["@vitest/runner@4.1.7", "", { "dependencies": { "@vitest/utils": "4.1.7", "pathe": "^2.0.3" } }, "sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw=="],
"@vitest/snapshot": ["@vitest/snapshot@4.1.8", "", { "dependencies": { "@vitest/pretty-format": "4.1.8", "@vitest/utils": "4.1.8", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ=="],
"@vitest/snapshot": ["@vitest/snapshot@4.1.7", "", { "dependencies": { "@vitest/pretty-format": "4.1.7", "@vitest/utils": "4.1.7", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw=="],
"@vitest/spy": ["@vitest/spy@4.1.8", "", {}, "sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA=="],
"@vitest/spy": ["@vitest/spy@4.1.7", "", {}, "sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q=="],
"@vitest/utils": ["@vitest/utils@4.1.8", "", { "dependencies": { "@vitest/pretty-format": "4.1.8", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg=="],
"@vitest/utils": ["@vitest/utils@4.1.7", "", { "dependencies": { "@vitest/pretty-format": "4.1.7", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw=="],
"@vladfrangu/async_event_emitter": ["@vladfrangu/async_event_emitter@2.4.7", "", {}, "sha512-Xfe6rpCTxSxfbswi/W/Pz7zp1WWSNn4A0eW4mLkQUewCrXXtMj31lCg+iQyTkh/CkusZSq9eDflu7tjEDXUY6g=="],
@@ -1648,9 +1642,9 @@
"@xterm/headless": ["@xterm/headless@5.5.0", "", {}, "sha512-5xXB7kdQlFBP82ViMJTwwEc3gKCLGKR/eoxQm4zge7GPBl86tCdI0IdPJjoKd8mUSFXz5V7i/25sfsEkP4j46g=="],
"@xyflow/react": ["@xyflow/react@12.11.0", "", { "dependencies": { "@xyflow/system": "0.0.77", "classcat": "^5.0.3", "zustand": "^4.4.0" }, "peerDependencies": { "@types/react": ">=17", "@types/react-dom": ">=17", "react": ">=17", "react-dom": ">=17" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-na4IO33FSs2OS72hASgZDmTYwFAkef7Z74uBUVrong3ARmQQHfnRUVaCFn1kTt5LbS6pK03TbYjCPGLjLFfziA=="],
"@xyflow/react": ["@xyflow/react@12.10.2", "", { "dependencies": { "@xyflow/system": "0.0.76", "classcat": "^5.0.3", "zustand": "^4.4.0" }, "peerDependencies": { "react": ">=17", "react-dom": ">=17" } }, "sha512-CgIi6HwlcHXwlkTpr0fxLv/0sRVNZ8IdwKLzzeCscaYBwpvfcH1QFOCeaTCuEn1FQEs/B8CjnTSjhs8udgmBgQ=="],
"@xyflow/system": ["@xyflow/system@0.0.77", "", { "dependencies": { "@types/d3-drag": "^3.0.7", "@types/d3-interpolate": "^3.0.4", "@types/d3-selection": "^3.0.10", "@types/d3-transition": "^3.0.8", "@types/d3-zoom": "^3.0.8", "d3-drag": "^3.0.0", "d3-interpolate": "^3.0.1", "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0" } }, "sha512-qCDCMCQAAgUu8yHnhloHG9F5mwPX5E+Wl8McpYIOPSSXfzFJJoZcwOcsDiAjitVKIg2de1WmJbCHfpcvxprsgg=="],
"@xyflow/system": ["@xyflow/system@0.0.76", "", { "dependencies": { "@types/d3-drag": "^3.0.7", "@types/d3-interpolate": "^3.0.4", "@types/d3-selection": "^3.0.10", "@types/d3-transition": "^3.0.8", "@types/d3-zoom": "^3.0.8", "d3-drag": "^3.0.0", "d3-interpolate": "^3.0.1", "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0" } }, "sha512-hvwvnRS1B3REwVDlWexsq7YQaPZeG3/mKo1jv38UmnpWmxihp14bW6VtEOuHEwJX2FvzFw8k77LyKSk/wiZVNA=="],
"abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
@@ -1662,7 +1656,7 @@
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
"ai": ["ai@6.0.195", "", { "dependencies": { "@ai-sdk/gateway": "3.0.123", "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "@opentelemetry/api": "^1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-IYZpuVz0boWbpIQYyinfWFrvQ1N0dG+EVB63it45B2YAU/MxxCnwz4zBswjYnPtHnJBedpMPNrwVbeczbl2GKg=="],
"ai": ["ai@6.0.193", "", { "dependencies": { "@ai-sdk/gateway": "3.0.121", "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "@opentelemetry/api": "^1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-VQOTOse8+X8kMtg61DNSXlYJzwOW4NjMLDJNk/qxClWsFe4oiyFJDHGGG1oezfGcFzuYuQe/8Z7r4kwiZWh2YQ=="],
"ai-sdk-provider-claude-code": ["ai-sdk-provider-claude-code@3.4.4", "", { "dependencies": { "@ai-sdk/provider": "^3.0.0", "@ai-sdk/provider-utils": "^4.0.1", "@anthropic-ai/claude-agent-sdk": "^0.2.63" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-iHcup5SHh4Tul1RIi9J+bnpngen8WX66yC3lsz1YlbtwAmRhUEzZUuGKzmFGIN8Pmx9uQrerGfLJdbFxIxKkyw=="],
@@ -1722,7 +1716,7 @@
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.33", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw=="],
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.32", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg=="],
"bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
@@ -1784,7 +1778,7 @@
"character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="],
"chat": ["chat@4.30.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-8LXrauKckMmR83FcYC/R8nNEda5VJDDdIhZwUUu+hzaSbk4lqsro0IWm7rB1GGYXONRrUOG2XJlkNr4C15vgMA=="],
"chat": ["chat@4.29.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-KdPfzaie5ivYytyRICTERg5xT+LeCbYefokvNAqTHe92eqkFaoTMXXkSitikxJVWhZIb2YoXF1b9UZHyzSzKzw=="],
"ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="],
@@ -1856,7 +1850,7 @@
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
"cytoscape": ["cytoscape@3.34.0", "", {}, "sha512-62rNSrioXw93uliKFBwjukeQyeWwH2PqDrTac31r2P6464u3AUvTk0xS4LVvT251g7IgkFunrI48ZEZGjywSOg=="],
"cytoscape": ["cytoscape@3.33.4", "", {}, "sha512-HIN5Pmd9MrX9BkV7tDwnOcEJCSFvCpc8X97h3f508J6I5FsqAY65wKOCvgH2CuP42CaahWaz4tuh32SOOIH7ww=="],
"cytoscape-cose-bilkent": ["cytoscape-cose-bilkent@4.1.0", "", { "dependencies": { "cose-base": "^1.0.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ=="],
@@ -1996,7 +1990,7 @@
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
"electron-to-chromium": ["electron-to-chromium@1.5.366", "", {}, "sha512-OlRuhb688YTCzzU3gXPLn6nGyd+F+53INE1qaKKlu6kETErE8FYsyDh0XqXEU+uBRn0MpCzz2vfNwORhkap8qg=="],
"electron-to-chromium": ["electron-to-chromium@1.5.364", "", {}, "sha512-G/dYE3+AYhyHwzTwg8UbnXf7zqMERYh7l2jJ3QujhFsH8agSYwtnGAR2aZ7f0AakIKJXd5En/Hre4igIUrdlYw=="],
"embla-carousel": ["embla-carousel@8.6.0", "", {}, "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA=="],
@@ -2212,7 +2206,7 @@
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
"graphql": ["graphql@16.14.1", "", {}, "sha512-cQOsSMS/IrDz82PVyRDvf/Q1F/bRbBVjJlh+xYOkI1qw2bWRvWGiWc+m2O0d6l4Bt1fyY+8kzJ8JFWGJqNeDBg=="],
"graphql": ["graphql@16.14.0", "", {}, "sha512-BBvQ/406p+4CZbTpCbVPSxfzrZrbnuWSP1ELYgyS6B+hNeKzgrdB4JczCa5VZUBQrDa9hUngm0KnexY6pJRN5Q=="],
"hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="],
@@ -2368,7 +2362,7 @@
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
"js-yaml": ["js-yaml@4.2.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw=="],
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
@@ -2532,7 +2526,7 @@
"mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="],
"media-chrome": ["media-chrome@4.19.1", "", { "dependencies": { "ce-la-react": "^0.3.2" } }, "sha512-1+x2l0mNulHKZN0lBxGJwJ+TV2W/KzLjaAd//UCGZz8GE5O5YNafFskWTcv/D6Ty0d9drX9SSfimOzGwob8eVQ=="],
"media-chrome": ["media-chrome@4.19.0", "", { "dependencies": { "ce-la-react": "^0.3.2" } }, "sha512-HWhDTwts+BSbdPkkB1VsJXp5kvL0IxY7xFT5tBwliM2+89kTPVTnHnev+9it2f9PweANjT/C8/C/S0PW9oyZbA=="],
"media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
@@ -2636,7 +2630,7 @@
"msw": ["msw@2.14.6", "", { "dependencies": { "@inquirer/confirm": "^6.0.11", "@mswjs/interceptors": "^0.41.3", "@open-draft/deferred-promise": "^3.0.0", "@types/statuses": "^2.0.6", "cookie": "^1.1.1", "graphql": "^16.13.2", "headers-polyfill": "^5.0.1", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "path-to-regexp": "^6.3.0", "picocolors": "^1.1.1", "rettime": "^0.11.11", "statuses": "^2.0.2", "strict-event-emitter": "^0.5.1", "tough-cookie": "^6.0.1", "type-fest": "^5.5.0", "until-async": "^3.0.2", "yargs": "^17.7.2" }, "peerDependencies": { "typescript": ">= 4.8.x" }, "optionalPeers": ["typescript"], "bin": { "msw": "cli/index.js" } }, "sha512-ALe+N10S72cyx94cMcy3Zs4HhXCj35sgeAL4c+WTvKi0zWnbd8/h0lcFqv0mb2P+aSgAdD7p9HzvA0DiUPxsyg=="],
"mute-stream": ["mute-stream@3.0.0", "", {}, "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw=="],
"mute-stream": ["mute-stream@4.0.0", "", {}, "sha512-gSrprq0fJ3EiOErzjdIZrjysVVmJ4uu1QWfCDss5LypA5OXvrMje5Ym5z6V6RLyJ2eF87lasX7t6a0AnFvZblg=="],
"nanoid": ["nanoid@5.1.11", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-v+KEsUv2ps74PaSKv0gHTxTCgMXOIfBEbaqa6w6ISIGC7ZsvHN4N9oJ8d4cmf0n5oTzQz2SLmThbQWhjd/8eKg=="],
@@ -2664,7 +2658,7 @@
"node-pty": ["node-pty@1.2.0-beta.11", "", { "dependencies": { "node-addon-api": "^7.1.0" } }, "sha512-THcUyu1WwdgoIyUvgXOZ70EOMXzheGa0q3tbEb5kUIfKgcpBJ+AJ9Q1kq0bKtYmQzr77usXiTORZTLmAUQlnoQ=="],
"node-releases": ["node-releases@2.0.47", "", {}, "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og=="],
"node-releases": ["node-releases@2.0.46", "", {}, "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ=="],
"node-rsa": ["node-rsa@1.1.1", "", { "dependencies": { "asn1": "^0.2.4" } }, "sha512-Jd4cvbJMryN21r5HgxQOpMEqv+ooke/korixNNK3mGqfGJmy0M77WDDzo/05969+OkMy3XW1UuZsSmW9KQm7Fw=="],
@@ -2806,9 +2800,9 @@
"proper-lockfile": ["proper-lockfile@4.1.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "retry": "^0.12.0", "signal-exit": "^3.0.2" } }, "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA=="],
"property-information": ["property-information@7.2.0", "", {}, "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg=="],
"property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="],
"protobufjs": ["protobufjs@7.6.2", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.5", "@protobufjs/eventemitter": "^1.1.1", "@protobufjs/fetch": "^1.1.1", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.2", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", "long": "^5.3.2" } }, "sha512-N9EiLovGEQOJSPF26Ij7qUGvahfEnq0eeYZ02aigIedkmz1qZSwjnP9SBITHJuF/6MYbIW4HDN8zdYjsjqJKXQ=="],
"protobufjs": ["protobufjs@7.6.1", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.5", "@protobufjs/eventemitter": "^1.1.1", "@protobufjs/fetch": "^1.1.1", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.2", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", "long": "^5.3.2" } }, "sha512-4K0myLaWL5EteuSAro91EGFgcfVgxb64Jx+7oDAY6GOkXD4M69yuSEljNcInGVCA5sOPxmZ/EqDLj2x0Q0+Ygg=="],
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
@@ -2836,7 +2830,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.77.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-Sslh9YDYc0GDlWT/lxasnIduNo4v3yyvqRGvmGKUre5AFjDs/HV9/OafHGD8d+sB2yoL4UIL9L8X9i0WlZZebg=="],
"react-hook-form": ["react-hook-form@7.76.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-rYM7tPiWlu3nZchkR/ex7piyzui2vFPyaLnXnI/RnblB/L4qfMmyses8llJVtF1NpE9WBBsJlGtcSZzPCXW1qQ=="],
"react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
@@ -2920,7 +2914,7 @@
"robust-predicates": ["robust-predicates@3.0.3", "", {}, "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA=="],
"rolldown": ["rolldown@1.0.3", "", { "dependencies": { "@oxc-project/types": "=0.133.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.3", "@rolldown/binding-darwin-arm64": "1.0.3", "@rolldown/binding-darwin-x64": "1.0.3", "@rolldown/binding-freebsd-x64": "1.0.3", "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", "@rolldown/binding-linux-arm64-gnu": "1.0.3", "@rolldown/binding-linux-arm64-musl": "1.0.3", "@rolldown/binding-linux-ppc64-gnu": "1.0.3", "@rolldown/binding-linux-s390x-gnu": "1.0.3", "@rolldown/binding-linux-x64-gnu": "1.0.3", "@rolldown/binding-linux-x64-musl": "1.0.3", "@rolldown/binding-openharmony-arm64": "1.0.3", "@rolldown/binding-wasm32-wasi": "1.0.3", "@rolldown/binding-win32-arm64-msvc": "1.0.3", "@rolldown/binding-win32-x64-msvc": "1.0.3" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g=="],
"rolldown": ["rolldown@1.0.2", "", { "dependencies": { "@oxc-project/types": "=0.132.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.2", "@rolldown/binding-darwin-arm64": "1.0.2", "@rolldown/binding-darwin-x64": "1.0.2", "@rolldown/binding-freebsd-x64": "1.0.2", "@rolldown/binding-linux-arm-gnueabihf": "1.0.2", "@rolldown/binding-linux-arm64-gnu": "1.0.2", "@rolldown/binding-linux-arm64-musl": "1.0.2", "@rolldown/binding-linux-ppc64-gnu": "1.0.2", "@rolldown/binding-linux-s390x-gnu": "1.0.2", "@rolldown/binding-linux-x64-gnu": "1.0.2", "@rolldown/binding-linux-x64-musl": "1.0.2", "@rolldown/binding-openharmony-arm64": "1.0.2", "@rolldown/binding-wasm32-wasi": "1.0.2", "@rolldown/binding-win32-arm64-msvc": "1.0.2", "@rolldown/binding-win32-x64-msvc": "1.0.2" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g=="],
"roughjs": ["roughjs@4.6.6", "", { "dependencies": { "hachure-fill": "^0.5.2", "path-data-parser": "^0.1.0", "points-on-curve": "^0.2.0", "points-on-path": "^0.2.1" } }, "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ=="],
@@ -2952,7 +2946,7 @@
"setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
"shadcn": ["shadcn@4.10.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/parser": "^7.28.0", "@babel/plugin-transform-typescript": "^7.28.0", "@babel/preset-typescript": "^7.27.1", "@dotenvx/dotenvx": "^1.48.4", "@modelcontextprotocol/sdk": "^1.26.0", "@types/validate-npm-package-name": "^4.0.2", "browserslist": "^4.26.2", "commander": "^14.0.0", "cosmiconfig": "^9.0.0", "dedent": "^1.6.0", "deepmerge": "^4.3.1", "diff": "^8.0.2", "execa": "^9.6.0", "fast-glob": "^3.3.3", "fs-extra": "^11.3.1", "fuzzysort": "^3.1.0", "https-proxy-agent": "^7.0.6", "kleur": "^4.1.5", "msw": "^2.10.4", "node-fetch": "^3.3.2", "open": "^11.0.0", "ora": "^8.2.0", "postcss": "^8.5.6", "postcss-selector-parser": "^7.1.0", "prompts": "^2.4.2", "recast": "^0.23.11", "stringify-object": "^5.0.0", "tailwind-merge": "^3.0.1", "ts-morph": "^26.0.0", "tsconfig-paths": "^4.2.0", "validate-npm-package-name": "^7.0.1", "zod": "^3.24.1", "zod-to-json-schema": "^3.24.6" }, "bin": { "shadcn": "dist/index.js" } }, "sha512-84IJhUsK0xqSCRJx3QxyZe2NpUXj2Nwk8Vc8Ow/tCOND3yz4CT6uU4655vqicNXhzG9Q1cyUt+TBl2SiCJwNgg=="],
"shadcn": ["shadcn@4.8.3", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/parser": "^7.28.0", "@babel/plugin-transform-typescript": "^7.28.0", "@babel/preset-typescript": "^7.27.1", "@dotenvx/dotenvx": "^1.48.4", "@modelcontextprotocol/sdk": "^1.26.0", "@types/validate-npm-package-name": "^4.0.2", "browserslist": "^4.26.2", "commander": "^14.0.0", "cosmiconfig": "^9.0.0", "dedent": "^1.6.0", "deepmerge": "^4.3.1", "diff": "^8.0.2", "execa": "^9.6.0", "fast-glob": "^3.3.3", "fs-extra": "^11.3.1", "fuzzysort": "^3.1.0", "https-proxy-agent": "^7.0.6", "kleur": "^4.1.5", "msw": "^2.10.4", "node-fetch": "^3.3.2", "open": "^11.0.0", "ora": "^8.2.0", "postcss": "^8.5.6", "postcss-selector-parser": "^7.1.0", "prompts": "^2.4.2", "recast": "^0.23.11", "stringify-object": "^5.0.0", "tailwind-merge": "^3.0.1", "ts-morph": "^26.0.0", "tsconfig-paths": "^4.2.0", "validate-npm-package-name": "^7.0.1", "zod": "^3.24.1", "zod-to-json-schema": "^3.24.6" }, "bin": { "shadcn": "dist/index.js" } }, "sha512-cnP485rIqtDb8waOp+IKUIfifVf64/PCd5VX/nnLeIP+qZ3yS4r6FOQtOdQkoQEvomQUsJS2OHr5CTzKWw0wKQ=="],
"sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="],
@@ -3076,15 +3070,15 @@
"tinycolor2": ["tinycolor2@1.6.0", "", {}, "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="],
"tinyexec": ["tinyexec@1.2.4", "", {}, "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg=="],
"tinyexec": ["tinyexec@1.2.3", "", {}, "sha512-g62dB+w1/OEFnPvmX0yd/HnetYITOL+1nJW7kitOycOeAvmbWC/nu0fwmmQ/kupNojqExzyC/T++pST/jRJ2mQ=="],
"tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="],
"tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
"tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="],
"tldts": ["tldts@7.4.2", "", { "dependencies": { "tldts-core": "^7.4.2" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-kCwffuaH8ntKtygnWe1b4BJKWiCUH30n5KfoTr6IchcXOwR7chAOFJxFrH3vjANafUYrIA4a7SDL+nn7SiR4Sw=="],
"tldts": ["tldts@7.4.0", "", { "dependencies": { "tldts-core": "^7.4.0" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-yHBe+zVfzNZ3QfTPW/Z6KK1G2t340gFjMHqI/4KKSt/abzYydzuCnpqdaF5gCCABby+9Yfbj59oR5F2Fd5CBzg=="],
"tldts-core": ["tldts-core@7.4.2", "", {}, "sha512-nwEyF4vl4RSJjwSjBUmOSxc3BFPoIFdlRthJ6e+5v9P3bHNsoD06UjuqMUspqp7vsEZ1beaHi1km+optiE17yA=="],
"tldts-core": ["tldts-core@7.4.1", "", {}, "sha512-sc2nGvGbixlJRHwTh/qQdPXTxJU1UDJboGPQm4d/01YUJ9r/u6aeIulQvEaxUlvKDN7hb1qCLjax+jhVAPLa/g=="],
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
@@ -3120,13 +3114,13 @@
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
"type-fest": ["type-fest@5.7.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg=="],
"type-fest": ["type-fest@5.6.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA=="],
"type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="],
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"typescript-eslint": ["typescript-eslint@8.60.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.60.1", "@typescript-eslint/parser": "8.60.1", "@typescript-eslint/typescript-estree": "8.60.1", "@typescript-eslint/utils": "8.60.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-6m5hkkRAp8lKvhVpcprAIn5KkehQEh+47oHH2VGnExEh7dhNxXlg6GPAOIu6TxbVQxhebrJDvjl3020ooiWCMA=="],
"typescript-eslint": ["typescript-eslint@8.60.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.60.0", "@typescript-eslint/parser": "8.60.0", "@typescript-eslint/typescript-estree": "8.60.0", "@typescript-eslint/utils": "8.60.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-9f65qWLZdAW9m1JaxBDUHcqRUfL8bkxxXL7XxEfI+F09q56PkBvIfCjLF3yInsDM/BBmwkqmCQdCZe/RYlIWEw=="],
"uc.micro": ["uc.micro@1.0.6", "", {}, "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="],
@@ -3194,9 +3188,9 @@
"victory-vendor": ["victory-vendor@36.9.2", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ=="],
"vite": ["vite@8.0.16", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.15", "rolldown": "1.0.3", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw=="],
"vite": ["vite@8.0.14", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.15", "rolldown": "1.0.2", "tinyglobby": "^0.2.16" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw=="],
"vitest": ["vitest@4.1.8", "", { "dependencies": { "@vitest/expect": "4.1.8", "@vitest/mocker": "4.1.8", "@vitest/pretty-format": "4.1.8", "@vitest/runner": "4.1.8", "@vitest/snapshot": "4.1.8", "@vitest/spy": "4.1.8", "@vitest/utils": "4.1.8", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.8", "@vitest/browser-preview": "4.1.8", "@vitest/browser-webdriverio": "4.1.8", "@vitest/coverage-istanbul": "4.1.8", "@vitest/coverage-v8": "4.1.8", "@vitest/ui": "4.1.8", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig=="],
"vitest": ["vitest@4.1.7", "", { "dependencies": { "@vitest/expect": "4.1.7", "@vitest/mocker": "4.1.7", "@vitest/pretty-format": "4.1.7", "@vitest/runner": "4.1.7", "@vitest/snapshot": "4.1.7", "@vitest/spy": "4.1.7", "@vitest/utils": "4.1.7", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.7", "@vitest/browser-preview": "4.1.7", "@vitest/browser-webdriverio": "4.1.7", "@vitest/coverage-istanbul": "4.1.7", "@vitest/coverage-v8": "4.1.7", "@vitest/ui": "4.1.7", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA=="],
"voca": ["voca@1.4.1", "", {}, "sha512-NJC/BzESaHT1p4B5k4JykxedeltmNbau4cummStd4RjFojgq/kLew5TzYge9N2geeWyI2w8T30wUET5v+F7ZHA=="],
@@ -3206,7 +3200,7 @@
"web-tree-sitter": ["web-tree-sitter@0.25.10", "", { "peerDependencies": { "@types/emscripten": "^1.40.0" }, "optionalPeers": ["@types/emscripten"] }, "sha512-Y09sF44/13XvgVKgO2cNDw5rGk6s26MgoZPXLESvMXeefBf7i6/73eFurre0IsTW6E14Y0ArIzhUMmjoc7xyzA=="],
"webview": ["webview@workspace:apps/examples/vscode/src/webview"],
"webview": ["webview@workspace:sdk/apps/examples/vscode/src/webview"],
"which": ["which@4.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="],
@@ -3630,7 +3624,7 @@
"react-dom/scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
"react-jsx-parser/@types/react": ["@types/react@18.3.30", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" } }, "sha512-3ek6mwJL5/VBewBcY4S66cqlCtK3qi4WIq37Z0m/NHw1hjhI7274Mx1qz/+ggSzyBCOEf7eHjBN6INjPAWYfYw=="],
"react-jsx-parser/@types/react": ["@types/react@18.3.29", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" } }, "sha512-ch0qJdr2JY0r04NXSprbK6TXOgnaJ1Tz23fm5W+z0/CBah6BSBc3n96h7K9GOtwh0HrilNWHIBzE1Ko4Dcw/Wg=="],
"react-jsx-parser/@types/react-dom": ["@types/react-dom@18.3.7", "", { "peerDependencies": { "@types/react": "^18.0.0" } }, "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ=="],
+11 -34
View File
@@ -22,7 +22,7 @@ cline connect
| Platform | Direct Command | Required Credentials |
|----------|---------------|---------------------|
| Telegram | `cline connect telegram` | Bot token |
| Slack | `cline connect slack` | Bot token plus webhook signing secret/base URL or socket app token |
| Slack | `cline connect slack` | Bot token, signing secret, base URL |
| Discord | `cline connect discord` | Application ID, bot token, public key, base URL |
| Google Chat | `cline connect gchat` | Service account credentials JSON, base URL |
| WhatsApp | `cline connect whatsapp` | Phone number ID, access token, app secret, verify token, base URL |
@@ -54,57 +54,34 @@ cline connect
### Security
By default, anyone who finds your bot can message it and it will execute tasks on your machine. The `cline connect` wizard asks whether to restrict Telegram access and can configure this for you.
By default, anyone who finds your bot can message it and it will execute tasks on your machine. Lock it down with the `--hook-command` flag.
<Steps>
<Step title="Get your Telegram user ID">
Message [@userinfobot](https://t.me/userinfobot) on Telegram. It replies with your numeric user ID immediately.
Message [@userinfobot](https://t.me/userinfobot) on Telegram. It replies with your user ID immediately.
</Step>
<Step title="Use the wizard">
```bash
cline connect
```
Choose Telegram, enter the bot token, answer yes to access restriction, then enter your user ID.
</Step>
<Step title="Or pass the flag manually">
Replace `12345` with your Telegram user ID:
<Step title="Start with access control">
Replace `12345` with your actual Telegram user ID:
```bash
cline connect telegram -k <BOT-TOKEN> \
--allowed-user-id 12345
--hook-command 'jq -r ".payload.actor.participantKey" | grep -q "telegram:id:12345" && echo "{\"action\":\"allow\"}" || echo "{\"action\":\"deny\",\"message\":\"unauthorized\"}"'
```
</Step>
</Steps>
Use `--hook-command` only when you need custom access logic. The hook receives each incoming message with sender info via stdin. Your script returns `{"action": "allow"}` or `{"action": "deny", "message": "reason"}`. Without `--allowed-user-id` or `--hook-command`, everything is auto-approved, so restrict Telegram bots that can reach a running Cline instance.
The `--hook-command` receives each incoming message with sender info via stdin. Your script returns `{"action": "allow"}` or `{"action": "deny", "message": "reason"}`. Without `--hook-command`, everything is auto-approved.
## Slack
Slack supports webhook mode and socket mode. Each Slack thread maps to an agent session, so the agent maintains conversation context within a thread.
Webhook mode requires a bot token, signing secret, and public base URL:
Requires a bot token, signing secret, and public base URL.
```bash
cline connect slack \
--bot-token <BOT-TOKEN> \
--signing-secret <SECRET> \
--base-url <URL>
cline connect slack --token <BOT-TOKEN> --signing-secret <SECRET> --base-url <URL>
```
Configure the Slack app's event subscription and interactivity request URLs to `<URL>/api/webhooks/slack`.
Socket mode requires a bot token and an app-level token with the `connections:write` scope:
```bash
cline connect slack \
--bot-token <BOT-TOKEN> \
--app-token <APP-LEVEL-TOKEN>
```
Enable Socket Mode in the Slack app. Socket mode does not need a public request URL and is single-workspace only.
Each Slack thread maps to an agent session, so the agent maintains conversation context within a thread.
## Discord
@@ -280,7 +257,7 @@ Multiple connectors can run simultaneously. They all share the same hub:
cline connect telegram -k $TELEGRAM_TOKEN
# Terminal 2
cline connect slack --bot-token $SLACK_TOKEN --signing-secret $SECRET --base-url $URL
cline connect slack --token $SLACK_TOKEN --signing-secret $SECRET --base-url $URL
```
Connectors require the hub. Start it with `cline hub start` if it doesn't auto-start.
-213
View File
@@ -1,213 +0,0 @@
---
title: "Supply-Chain Scan Alerts"
description: "Schedule the Cline CLI to scan your machine for compromised packages with Bumblebee and text you on Telegram when it finds one."
---
npm worms like Shai-Hulud spread through install scripts: the moment you run `npm install`, a `preinstall` hook executes and steals your npm, GitHub, AWS, and SSH credentials. New campaigns are reported almost every week.
This guide wires three pieces together so your machine checks itself automatically and pings your phone only when it matters:
- [Bumblebee](https://github.com/perplexityai/bumblebee), Perplexity's open-source, read-only supply-chain scanner. It maintains catalogs of recent campaigns and checks whether any compromised package or version is present on disk.
- The Cline CLI scheduler, which runs an agent on a cron schedule.
- The Cline CLI Telegram connector, which delivers the result to a chat.
The end result: every morning, a Cline agent pulls the latest threat intelligence, runs a read-only scan, and texts you a green check if you are clean or a red alert with details if you are exposed.
```mermaid
flowchart TD
cron["cline schedule (daily)"] --> agent["Cline agent"]
agent --> pull["git pull (latest catalogs)"]
agent --> scan["bumblebee scan (read-only)"]
scan --> q{"any findings?"}
q -- "no" --> clean["✅ Clean"]
q -- "yes" --> alert["🚨 Compromise detected"]
clean --> tg["Telegram on your phone"]
alert --> tg
```
## How Bumblebee works
Bumblebee answers one narrow question fast: when an advisory names a package and version, is it present on this machine right now?
The important design choice is that it is read-only. A scanner that runs `npm`, `pnpm`, or `pip` to enumerate your dependencies would trigger the very install-script payload it is looking for. Bumblebee never does that. It only reads metadata files directly:
| Surface | What it reads |
|---|---|
| npm / pnpm / yarn / bun | lockfiles and installed `package.json` metadata |
| PyPI | `*.dist-info/METADATA`, `*.egg-info/PKG-INFO` |
| Go modules | `go.sum`, `go.mod` |
| RubyGems | `Gemfile.lock`, installed gemspecs |
| Composer | `composer.lock`, `vendor/composer/installed.json` |
| MCP servers | `mcp.json`, `claude_desktop_config.json`, and similar configs |
| Editor extensions | VS Code-family extension manifests |
| Browser extensions | Chromium-family and Firefox extension manifests |
It never runs package managers, never executes install scripts or lifecycle hooks, and never reads your application source. It ships no bundled threat intelligence either: you point it at an exposure catalog, and it reports exact `(ecosystem, name, version)` matches.
The catalogs live in the repo under `threat_intel/`, maintained by Perplexity and updated via pull requests as new campaigns are reported. That is why this automation simply pulls the latest before each scan: a `git pull` is all it takes to stay current.
Read the announcement: [Perplexity is open-sourcing Bumblebee](https://www.perplexity.ai/hub/blog/perplexity-is-open-sourcing-bumblebee).
## Prerequisites
- Node.js 22 or newer (for the Cline CLI).
- Go 1.22 or newer (to build Bumblebee).
- A Telegram account.
- An AI provider key, or a Cline account.
## 1. Install the Cline CLI
```bash
npm install -g cline
cline # run once to configure inference provider and model
```
## 2. Clone and build Bumblebee
Clone the repository somewhere stable. The clone is both the scanner and the catalog source, so the scheduled job will run from inside it.
```bash
mkdir -p ~/tools
git clone https://github.com/perplexityai/bumblebee.git ~/tools/bumblebee
cd ~/tools/bumblebee
go build -o bumblebee ./cmd/bumblebee
```
Confirm it works with the built-in self test, which runs embedded fixtures and makes no network calls:
```bash
./bumblebee selftest
# selftest OK (2 findings in 1ms)
```
## 3. Run a scan manually
Point `--exposure-catalog` at the whole `threat_intel/` directory to use every maintained catalog at once. The `--findings-only` flag suppresses the full inventory so you only get matches.
```bash
cd ~/tools/bumblebee
./bumblebee scan --profile deep --root "$HOME" \
--exposure-catalog ./threat_intel/ \
--findings-only
```
Output is NDJSON, one JSON object per line. A match looks like this:
```json
{ "record_type": "finding", "severity": "critical", "ecosystem": "npm",
"package_name": "example-pkg", "version": "1.2.3",
"source_file": "/Users/you/code/app/pnpm-lock.yaml",
"evidence": "exact name+version match (version=1.2.3)" }
```
If you are clean, you get no `finding` records. Exit code is `0` on a successful run, `1` if the scan hit errors, `2` for bad arguments.
<Note>
Scan profiles control where Bumblebee looks. `baseline` checks standard global tool, editor, and browser locations. `project` scans your development directories (pass `--root ~/code`). `deep` walks whatever roots you give it, typically your whole home directory. Use `deep` for the most thorough "am I exposed anywhere" check, or `project` for a faster daily scan of your repos.
</Note>
## 4. Create a Telegram bot and start the connector
<Steps>
<Step title="Create a bot">
Open Telegram, start a chat with [@BotFather](https://t.me/BotFather), send `/newbot`, and follow the prompts. Copy the bot token it gives you (it looks like `7123456789:AAH...`). Treat it like a password.
</Step>
<Step title="Start the connector">
Run the connector and point its working directory at your Bumblebee clone, so the scheduled agent runs there:
```bash
cline connect telegram -k "<BOT-TOKEN>" --cwd ~/tools/bumblebee
```
Leave this process running. It polls Telegram and delivers scheduled results, so it must stay alive.
</Step>
<Step title="Open the chat">
In Telegram, search for your bot's username and send it any message (for example `/whereami`). This creates the thread binding that delivery needs.
</Step>
</Steps>
<Warning>
By default, anyone who finds your bot can message it and it will run tasks on your machine. Lock it down before leaving the connector running. The `cline connect` wizard can guide you through Telegram user ID setup, or you can message [@userinfobot](https://t.me/userinfobot) and restart the connector with your allowed user ID:
```bash
cline connect telegram -k "<BOT-TOKEN>" --cwd ~/tools/bumblebee \
--allowed-user-id 12345
```
Replace `12345` with your Telegram user ID.
</Warning>
## 5. Schedule the scan
There are two ways to create the scheduled scan. Both run the same agent and deliver the result to Telegram, so pick whichever you prefer.
### Option A: From the Telegram chat
Creating the schedule from the chat automatically targets that thread for delivery, so results come straight back to you. Send this to your bot as a single message:
```text
/schedule create "supply-chain-watch" --cron "0 8 * * *" --prompt "Pull the latest Bumblebee catalogs and scan this machine for compromised packages. Run: git pull --quiet && go build -o bumblebee ./cmd/bumblebee && ./bumblebee scan --profile deep --root $HOME --exposure-catalog ./threat_intel/ --findings-only. Read the NDJSON output. If any line has record_type set to finding, reply starting with '🚨 COMPROMISE DETECTED' and list each package name, version, ecosystem, and source_file. If there are no findings, reply with exactly '✅ Clean: no compromised packages found.'"
```
The bot replies with the new schedule, including its id.
### Option B: From your terminal
Create the schedule with the Cline CLI on the same machine and pass the delivery method explicitly. The running Telegram connector delivers the result to its chat:
```bash
cline schedule create "supply-chain-watch" \
--cron "0 8 * * *" \
--workspace ~/tools/bumblebee \
--delivery-adapter telegram \
--delivery-bot <bot-username> \
--prompt "Pull the latest Bumblebee catalogs and scan this machine for compromised packages. Run: git pull --quiet && go build -o bumblebee ./cmd/bumblebee && ./bumblebee scan --profile deep --root \$HOME --exposure-catalog ./threat_intel/ --findings-only. Read the NDJSON output. If any line has record_type set to finding, reply starting with '🚨 COMPROMISE DETECTED' and list each package name, version, ecosystem, and source_file. If there are no findings, reply with exactly '✅ Clean: no compromised packages found.'"
```
Either way, this schedules a daily scan at 8am.
## Why the green check matters
Scheduled delivery always sends the run's final reply, so the prompt is written to make that reply meaningful either way:
- Clean run: one line, `✅ Clean: no compromised packages found.` You get a daily heartbeat confirming the scan actually ran.
- Exposure: `🚨 COMPROMISE DETECTED` followed by the package, version, and the file where it was found, so you can act immediately (rotate credentials, remove the package, pin a safe version).
## Test it
Trigger the scan now instead of waiting for 8am.
First find your schedule id. The create step returns it (the Telegram bot's reply shows `id=...`), or list your schedules at any time:
```bash
cline schedule list
```
Then trigger it with that id. From the terminal:
```bash
cline schedule trigger <schedule-id>
```
Or from Telegram: `/schedule trigger <schedule-id>`. Within a few seconds you should get the result in your chat.
To see a real alert, add a package and version that matches a catalog entry to a throwaway project's lockfile and run the scan against it. Bumblebee reports the match, and the agent texts you the red alert.
## Keep it running
- The connector process (`cline connect telegram`) must stay running for delivery to work. Run it under a process manager (systemd, launchd, `pm2`, or a `tmux`/`screen` session) so it survives reboots.
- The hub runs the schedule and starts automatically when you create one. If it is not running, start it with `cline hub start`.
- Manage schedules anytime with `cline schedule list`, `cline schedule pause <id>`, `cline schedule resume <id>`, and `cline schedule delete <id>`.
## Customize
- Cadence: change the cron expression. `0 */6 * * *` scans every six hours; `0 8 * * MON-FRI` runs on weekdays only.
- Scope: swap `--profile deep --root $HOME` for `--profile project --root ~/code` for a faster scan of just your repos, or `--profile baseline` for global tools, editors, and browser extensions.
- Channels: the same delivery pattern works for Slack, Discord, WhatsApp, and Google Chat. See [Connectors](/cli/connectors).
- Fleet use: Bumblebee can `POST` NDJSON to an ingest endpoint with `--output http --http-url <url>` if you want to centralize findings across many machines.
## Credits
Bumblebee is built and open-sourced by Perplexity. See the [announcement](https://www.perplexity.ai/hub/blog/perplexity-is-open-sourcing-bumblebee) and the [repository](https://github.com/perplexityai/bumblebee).
+1 -2
View File
@@ -114,8 +114,7 @@
"cli/samples/github-issue-rca",
"cli/samples/github-integration",
"cli/samples/github-pr-review",
"cli/samples/model-orchestration",
"cli/samples/supply-chain-alerts"
"cli/samples/model-orchestration"
]
},
"cli/acp-editor-integrations"
+1 -1
View File
@@ -82,7 +82,7 @@ agent.subscribe((event) => {
})
```
For a complete working example of streaming agent events to a browser via SSE, see the [multi-agent example](https://github.com/cline/cline/tree/main/apps/examples/multi-agent). It spawns multiple agents in parallel and streams each agent's events to separate UI cards.
For a complete working example of streaming agent events to a browser via SSE, see the [multi-agent example](https://github.com/cline/cline/tree/main/sdk/apps/examples/multi-agent). It spawns multiple agents in parallel and streams each agent's events to separate UI cards.
## Usage Tracking Pattern
+6 -6
View File
@@ -4,12 +4,12 @@ sidebarTitle: "Examples"
description: "Explore complete, runnable SDK examples organized by difficulty."
---
Working examples are available in the [SDK repository](https://github.com/cline/cline/tree/main/apps/examples), organized by difficulty:
Working examples are available in the [SDK repository](https://github.com/cline/cline/tree/main/sdk/apps/examples), organized by difficulty:
| Example | Difficulty | Description |
|---------|------------|-------------|
| [quickstart](https://github.com/cline/cline/tree/main/apps/examples/quickstart) | Beginner | Send one prompt, stream the response (~15 lines) |
| [cli-agent](https://github.com/cline/cline/tree/main/apps/examples/cli-agent) | Beginner | Interactive terminal chat with a shell tool |
| [code-review-bot](https://github.com/cline/cline/tree/main/apps/examples/code-review-bot) | Intermediate | AI code reviewer with custom tools and structured output |
| [multi-agent](https://github.com/cline/cline/tree/main/apps/examples/multi-agent) | Advanced | Parallel agents with streaming web UI |
| [desktop-app](https://github.com/cline/cline/tree/main/apps/examples/desktop-app) | Advanced | Full Tauri + Next.js desktop app |
| [quickstart](https://github.com/cline/cline/tree/main/sdk/apps/examples/quickstart) | Beginner | Send one prompt, stream the response (~15 lines) |
| [cli-agent](https://github.com/cline/cline/tree/main/sdk/apps/examples/cli-agent) | Beginner | Interactive terminal chat with a shell tool |
| [code-review-bot](https://github.com/cline/cline/tree/main/sdk/apps/examples/code-review-bot) | Intermediate | AI code reviewer with custom tools and structured output |
| [multi-agent](https://github.com/cline/cline/tree/main/sdk/apps/examples/multi-agent) | Advanced | Parallel agents with streaming web UI |
| [desktop-app](https://github.com/cline/cline/tree/main/sdk/apps/examples/desktop-app) | Advanced | Full Tauri + Next.js desktop app |
+5 -5
View File
@@ -4,7 +4,7 @@ sidebarTitle: "Building an Agent"
description: "Walk through a complete code review bot that reads diffs, analyzes code, and produces structured feedback."
---
This tutorial walks through the [code-review-bot example](https://github.com/cline/cline/tree/main/apps/examples/code-review-bot) from the SDK repository. By the end, you'll understand how to combine custom tools, system prompts, completion lifecycle, and event streaming into a real application.
This tutorial walks through the [code-review-bot example](https://github.com/cline/cline/tree/main/sdk/apps/examples/code-review-bot) from the SDK repository. By the end, you'll understand how to combine custom tools, system prompts, completion lifecycle, and event streaming into a real application.
## What It Builds
@@ -24,11 +24,11 @@ A code review agent that:
```bash
git clone https://github.com/cline/cline.git
cd cline/apps/examples/code-review-bot
cd cline/sdk/apps/examples/code-review-bot
bun install
```
Or read along with the [source on GitHub](https://github.com/cline/cline/blob/main/apps/examples/code-review-bot/src/index.ts).
Or read along with the [source on GitHub](https://github.com/cline/cline/blob/main/sdk/apps/examples/code-review-bot/src/index.ts).
## How It Works
@@ -153,10 +153,10 @@ From here, you could:
## More Examples
<CardGroup cols={2}>
<Card title="CLI Agent" icon="terminal" href="https://github.com/cline/cline/tree/main/apps/examples/cli-agent">
<Card title="CLI Agent" icon="terminal" href="https://github.com/cline/cline/tree/main/sdk/apps/examples/cli-agent">
Interactive terminal chat with tools and multi-turn conversation.
</Card>
<Card title="Multi-Agent" icon="users" href="https://github.com/cline/cline/tree/main/apps/examples/multi-agent">
<Card title="Multi-Agent" icon="users" href="https://github.com/cline/cline/tree/main/sdk/apps/examples/multi-agent">
Parallel agents streaming to a web UI.
</Card>
</CardGroup>
+2 -2
View File
@@ -34,7 +34,7 @@ const getCurrentTime = createTool({
The SDK converts the zod schema to JSON Schema automatically. Input is fully typed in the `execute` function.
For working examples of tools in real agents, see the [cli-agent](https://github.com/cline/cline/tree/main/apps/examples/cli-agent) (shell tool with zod) and [code-review-bot](https://github.com/cline/cline/tree/main/apps/examples/code-review-bot) (multiple tools with completion lifecycle).
For working examples of tools in real agents, see the [cli-agent](https://github.com/cline/cline/tree/main/sdk/apps/examples/cli-agent) (shell tool with zod) and [code-review-bot](https://github.com/cline/cline/tree/main/sdk/apps/examples/code-review-bot) (multiple tools with completion lifecycle).
## Anatomy of a Tool
@@ -166,7 +166,7 @@ const submitResult = createTool({
})
```
See the [code-review-bot example](https://github.com/cline/cline/tree/main/apps/examples/code-review-bot) for this pattern in a complete application.
See the [code-review-bot example](https://github.com/cline/cline/tree/main/sdk/apps/examples/code-review-bot) for this pattern in a complete application.
## Testing Tools
-15
View File
@@ -284,21 +284,6 @@ Users can also install package plugins from git, npm, or a local path:
cline plugin install https://github.com/your-org/cline-github-plugin.git
```
## Bundling Skills
Package plugins can include skills by adding a top-level `skills/` directory next to `package.json`:
```txt
cline-github-plugin/
package.json
github-plugin.ts
skills/
triage/
SKILL.md
```
Each bundled skill follows the same directory format as any other [Cline skill](/customization/skills). When the plugin is installed or loaded through `pluginPaths`, Cline discovers those skills automatically.
See [Plugins](/customization/plugins) for the full manifest format, directory layout, and the [typescript-lsp-plugin](https://github.com/cline/typescript-lsp-plugin) for a complete working example.
## Plugin Design Guidelines
+1 -1
View File
@@ -50,7 +50,7 @@ const result = await agent.run("Explain what an SDK is in two sentences.")
```
<Note>
Here is a complete [quickstart example](https://github.com/cline/cline/tree/main/apps/examples/quickstart). Clone it and run `bun dev` to try it.
Here is a complete [quickstart example](https://github.com/cline/cline/tree/main/sdk/apps/examples/quickstart). Clone it and run `bun dev` to try it.
</Note>
## Packages
+1 -1
View File
@@ -72,7 +72,7 @@ const searchDatabase = createTool({
})
```
For complete examples of tools in action, see the [cli-agent](https://github.com/cline/cline/tree/main/apps/examples/cli-agent) (shell tool) and [code-review-bot](https://github.com/cline/cline/tree/main/apps/examples/code-review-bot) (multiple tools with completion lifecycle).
For complete examples of tools in action, see the [cli-agent](https://github.com/cline/cline/tree/main/sdk/apps/examples/cli-agent) (shell tool) and [code-review-bot](https://github.com/cline/cline/tree/main/sdk/apps/examples/code-review-bot) (multiple tools with completion lifecycle).
For a full tutorial, see [Creating Custom Tools](/sdk/guides/creating-custom-tools). For exact types, see [Tools API](/sdk/reference/tools-api).
+1 -1
View File
@@ -235,7 +235,7 @@ npx tsx evals/smoke-tests/run-smoke-tests.ts --trials 1
### Re-enabling CI
The old workflow built the legacy CLI from `cli/`. Re-enable it only after replacing that build step with the SDK CLI under `apps/cli`.
The old workflow built the legacy CLI from `cli/`. Re-enable it only after replacing that build step with the SDK CLI under `sdk/apps/cli`.
## Contract Tests (Layer 1)
+1 -1
View File
@@ -419,7 +419,7 @@ async function main() {
console.error("ERROR: cline CLI not found in PATH")
console.error("")
console.error("For local development:")
console.error(" Install cline from npm or link the SDK CLI from apps/cli")
console.error(" Install cline from npm or link the SDK CLI from sdk/apps/cli")
console.error("")
console.error("For CI:")
console.error(" Ensure a cline binary is available on PATH")
+11 -16
View File
@@ -3,11 +3,10 @@
"private": true,
"workspaces": [
"sdk/packages/*",
"apps/*",
"!apps/vscode",
"apps/cline-hub/src/webview",
"apps/examples/*",
"apps/examples/vscode/src/webview",
"sdk/apps/*",
"sdk/apps/cline-hub/src/webview",
"sdk/apps/examples/*",
"sdk/apps/examples/vscode/src/webview",
"sdk/examples",
"sdk/examples/plugins/*"
],
@@ -15,24 +14,24 @@
"prepare": "husky",
"build": "bun run clean && bun install && bun run build:sdk && bun -F @cline/cli build",
"build:sdk": "bun --production -F './sdk/packages/*' build",
"build:apps": "bun -F './apps/**' --production build",
"build:apps": "bun -F './sdk/apps/**' --production build",
"build:models": "bun -F @cline/llms generate:models && bun format --write",
"dev": "bun --conditions=development run build:sdk && bun run cli && bun run cli hub stop",
"cli": "bun --conditions=development --cwd apps/cli dev",
"cli": "bun --conditions=development --cwd sdk/apps/cli dev",
"code": "bun --conditions=development -F @cline/code dev",
"clean": "bun run sdk/scripts/clean.ts",
"types": "bun --parallel -F '*' typecheck",
"test": "bun --parallel -F './sdk/packages/**' -F @cline/cli test",
"test": "bun --parallel -F './sdk/packages/**' -F './sdk/apps/cli' test",
"test:unit": "bash -lc 'set -euo pipefail; bun -F @cline/agents test & p1=$!; bun -F @cline/llms test & p2=$!; bun -F @cline/core test:unit & p3=$!; bun -F @cline/cli test:unit & p4=$!; wait $p1; wait $p2; wait $p3; wait $p4'",
"test:e2e": "bun -F @cline/core test:e2e && bun -F @cline/cli test:e2e",
"test:e2e:interactive": "bun -F @cline/cli test:e2e:interactive",
"verify:routines": "zsh -lc 'cd sdk/packages/core && bunx vitest run src/cron/schedule-service.test.ts --config vitest.config.ts'",
"verify:workos-device-auth": "bun sdk/scripts/verify-workos-device-auth.ts",
"biome": "bunx --bun @biomejs/biome",
"format": "bun biome format sdk/ apps/cli/ apps/cline-hub/ apps/examples/",
"lint": "bun biome lint sdk/ apps/cli/ apps/cline-hub/ apps/examples/",
"fix": "bun biome check --write --unsafe --diagnostic-level=error sdk/ apps/cli/ apps/cline-hub/ apps/examples/",
"check": "bun biome check --diagnostic-level=error sdk/ apps/cli/ apps/cline-hub/ apps/examples/ && bun run build:sdk && bun run -F @cline/cli build && bun --parallel -F './sdk/packages/**' -F @cline/cli typecheck && bun sdk/scripts/check-publish.ts",
"format": "bun biome format sdk/",
"lint": "bun biome lint sdk/",
"fix": "bun biome check --write --unsafe --diagnostic-level=error sdk/",
"check": "bun biome check --diagnostic-level=error sdk/ && bun run build:sdk && bun run -F @cline/cli build && bun --parallel -F './sdk/packages/**' -F @cline/cli typecheck && bun sdk/scripts/check-publish.ts",
"version": "bun run types && bun sdk/scripts/version.ts",
"release": "bun sdk/scripts/release.ts"
},
@@ -40,10 +39,6 @@
"sdk/**": [
"sh -c 'bun run types'",
"bun biome check --no-errors-on-unmatched --files-ignore-unknown=true"
],
"apps/{cli,cline-hub,examples}/**": [
"sh -c 'bun run types'",
"bun biome check --no-errors-on-unmatched --files-ignore-unknown=true"
]
},
"module": "index.ts",
+3
View File
@@ -17,6 +17,9 @@ pnpm-lock.yaml
.actrc
CLAUDE.local.md
apps/vscode/webview-ui/src/**/*.js
apps/vscode/webview-ui/src/**/*.js.map
# Ignore coverage directories and files
coverage
coverage-unit
@@ -6,18 +6,15 @@
{
"id": "sdk-tool-handler-telemetry",
"rule": "Any new tool handler added to packages/agents/src or packages/core/src that performs a user-visible action (writes files, executes commands, modifies state, calls external APIs) must include a call to captureToolUsage() from packages/core/src/services/telemetry/core-events.ts, or emit a task.tool_used event via telemetry.capture(). Pure read-only helpers and getters are exempt. When in doubt, prefer instrumentation.",
"scope": [
"sdk/packages/agents/src/**",
"sdk/packages/core/src/**"
],
"scope": ["packages/agents/src/**", "packages/core/src/**"],
"severity": "high"
},
{
"id": "sdk-session-lifecycle-telemetry",
"rule": "New session start, end, or state-transition code paths in packages/core/src must call the appropriate typed helper from packages/core/src/services/telemetry/core-events.ts (captureTaskCreated, captureTaskCompleted, captureConversationTurnEvent, captureTokenUsage, etc.). Do not inline raw telemetry.capture() calls for session lifecycle events — always use the typed helper, which guarantees a consistent payload shape.",
"scope": [
"sdk/packages/core/src/cline-core/**",
"sdk/packages/core/src/runtime/**"
"packages/core/src/cline-core/**",
"packages/core/src/runtime/**"
],
"severity": "high"
},
@@ -25,8 +22,8 @@
"id": "sdk-no-raw-event-strings",
"rule": "All telemetry event name strings must be sourced from CORE_TELEMETRY_EVENTS in packages/core/src/services/telemetry/core-events.ts. If a PR introduces a string literal in a telemetry.capture(), telemetry.captureRequired(), or recordCounter()/recordHistogram()/recordGauge() call that does not reference CORE_TELEMETRY_EVENTS, flag it. New events must be added to CORE_TELEMETRY_EVENTS first, with a typed capture helper created alongside them.",
"scope": [
"sdk/packages/core/src/**",
"sdk/packages/agents/src/**",
"packages/core/src/**",
"packages/agents/src/**",
"apps/cli/src/**",
"apps/vscode/src/**"
],
@@ -35,17 +32,13 @@
{
"id": "sdk-auth-telemetry-completeness",
"rule": "Any new OAuth or authentication provider added under packages/core/src/auth must emit all four lifecycle events using the typed helpers from core-events.ts: captureAuthStarted (at flow entry), captureAuthSucceeded + identifyAccount (on token success), captureAuthFailed (on error), and captureAuthLoggedOut (on token invalidation or explicit logout). Flag PRs that introduce a new auth flow file without all four. Cross-reference packages/core/src/auth/cline.ts and packages/core/src/auth/codex.ts as canonical examples.",
"scope": [
"sdk/packages/core/src/auth/**"
],
"scope": ["packages/core/src/auth/**"],
"severity": "high"
},
{
"id": "sdk-telemetry-doc-update",
"rule": "Any PR that adds new event constants to CORE_TELEMETRY_EVENTS in packages/core/src/services/telemetry/core-events.ts, adds new capture* helper functions, or changes the payload shape of an existing event must update the Event Catalog section in DOC.md. Flag PRs that modify core-events.ts without a corresponding change to DOC.md.",
"scope": [
"sdk/packages/core/src/services/telemetry/core-events.ts"
],
"scope": ["packages/core/src/services/telemetry/core-events.ts"],
"severity": "medium"
}
]
@@ -1,19 +1,19 @@
{
"files": [
{
"path": "sdk/packages/core/src/services/telemetry/core-events.ts",
"path": "packages/core/src/services/telemetry/core-events.ts",
"description": "Single source of truth for all telemetry event names (CORE_TELEMETRY_EVENTS) and their typed capture helper functions. Every PR touching telemetry must be evaluated against this catalog. New events must be defined here first."
},
{
"path": "sdk/packages/shared/src/services/telemetry.ts",
"path": "packages/shared/src/services/telemetry.ts",
"description": "ITelemetryService interface definition. Defines the contract all telemetry implementations must satisfy (capture, captureRequired, recordCounter, recordHistogram, recordGauge, flush, dispose)."
},
{
"path": "sdk/packages/core/src/services/telemetry/TelemetryService.ts",
"path": "packages/core/src/services/telemetry/TelemetryService.ts",
"description": "Reference implementation of ITelemetryService used by all hosts. Multi-adapter fan-out service that forwards events to OpenTelemetry."
},
{
"path": "sdk/packages/core/src/services/telemetry/OpenTelemetryProvider.ts",
"path": "packages/core/src/services/telemetry/OpenTelemetryProvider.ts",
"description": "OpenTelemetry-backed provider that wires logs/metrics/traces exporters. Contains createConfiguredTelemetryService and createConfiguredTelemetryHandle, the canonical factories every host should use."
},
{
@@ -21,11 +21,11 @@
"description": "Public API and event documentation. The Event Catalog and 'Activation funnel' sections must be kept in sync with core-events.ts. Host integration rules (CLI dir ordering, hub daemon metadata forwarding) are documented here."
},
{
"path": "sdk/ARCHITECTURE.md",
"path": "ARCHITECTURE.md",
"description": "Architecture reference. Telemetry design decisions, completion semantics (submit_and_exit anchoring), and hub-daemon telemetry forwarding are documented here. Use as ground truth for design intent."
},
{
"path": "sdk/AGENTS.md",
"path": "AGENTS.md",
"description": "Package boundary rules. Telemetry runtime services live in @cline/core; @cline/agents must not own stateful telemetry. Use to evaluate whether a telemetry change is being made in the correct package."
}
]
@@ -10,9 +10,9 @@ The SDK uses OpenTelemetry (OTEL) as its sole telemetry transport. Events flow t
```
core-events.ts (event catalog + typed helpers)
ITelemetryService (sdk/packages/shared) ← interface contract
ITelemetryService (packages/shared) ← interface contract
TelemetryService (sdk/packages/core) ← multi-adapter fan-out
TelemetryService (packages/core) ← multi-adapter fan-out
OpenTelemetryAdapter → OpenTelemetryProvider ← OTLP transport
@@ -24,7 +24,7 @@ parallel-but-independent stacks; this `.greptile/` config covers only the SDK.
## The Single Source of Truth
`sdk/packages/core/src/services/telemetry/core-events.ts` is the single source of truth for all
`packages/core/src/services/telemetry/core-events.ts` is the single source of truth for all
event names. It exports:
- `CORE_TELEMETRY_EVENTS` — a frozen const object grouped by family
@@ -60,8 +60,8 @@ Emission ownership:
emitter in `prepareLocalRuntimeBootstrap`. Hosts must NOT re-emit these.
- `workspace.path_resolved`: emitted from default tool executors **only when**
`WorkspaceManager` exposes more than one root.
- `task.*`: emitted by core session lifecycle code in `sdk/packages/core/src/cline-core/` and
`sdk/packages/core/src/runtime/`. Hosts must not duplicate this emission.
- `task.*`: emitted by core session lifecycle code in `packages/core/src/cline-core/` and
`packages/core/src/runtime/`. Hosts must not duplicate this emission.
## `task.completed` Semantics
@@ -105,7 +105,7 @@ forwarding, hub-backed sessions silently drop their lifecycle telemetry.
## Auth Lifecycle Completeness
Every authentication provider in `sdk/packages/core/src/auth/` must emit all four auth lifecycle
Every authentication provider in `packages/core/src/auth/` must emit all four auth lifecycle
events using the typed helpers:
| Phase | Helper | Where it fires |
@@ -115,7 +115,7 @@ events using the typed helpers:
| Token error | `captureAuthFailed(provider, errorMessage)` | In the catch block |
| Token invalidation | `captureAuthLoggedOut(provider, reason)` | On invalid_grant or explicit logout |
Cross-reference `sdk/packages/core/src/auth/cline.ts` and `sdk/packages/core/src/auth/codex.ts` as
Cross-reference `packages/core/src/auth/cline.ts` and `packages/core/src/auth/codex.ts` as
canonical examples of all four phases.
## Single Telemetry Service Per Host
-45
View File
@@ -1,45 +0,0 @@
# Cline SDK Changelog
## 0.0.46
- Added support for configured agents as subagent tools
- Centralized OAuth management into the SDK
- Added Vertex GCP settings configuration
- Fixed the Azure Foundry API version for the CLI
- Fixed an error caused by disabled reasoning on Fable 5
## 0.0.45
- Added support for the Claude Fable 5 model
- Fixed MiniMax M3 thinking controls so they route correctly across gateways
## 0.0.44
- Added support for Vertex AI Application Default Credentials (ADC) with tool use
- Added a global auto-update setting for CLI startup updates
- Fixed empty message content replay for Bedrock
- Cleaned up the OpenAI Codex model list
## 0.0.43
- Added the Cline Hub web app for managing and monitoring agent sessions
- Added plugin uninstall support
- Added skills bundled with plugins, including grouping plugin skills in settings and rule contributions from sandboxed plugins
- Added support for global AGENTS rules
- Added Slack socket mode support and bound Discord sessions to individual message authors
- Synced the Fireworks AI model registry and updated the model catalog to current platform offerings
- Routed custom registered handlers through the agent runtime
- Added a CLINE_PLUGIN_IMPORT_TIMEOUT_MS environment override for plugin import timeouts
- Allowed a baseUrl field for Anthropic vendor-type providers
- Fixed SAP AI Core to use the AI SDK community provider
- Fixed the hub daemon to stay alive on runtime abort
- Fixed read-files tool input validation to use a union schema
- Fixed discovery of symlinked SDK skill directories
- Improved Cline provider migration
- Fixed OTEL variable bundling
- Added telemetry for run_commands timeouts
## 0.0.42
- Supports Bedrock bearer API keys, direct IAM credentials, AWS profiles, and the default AWS SDK credential chain
- Routes Z.AI GLM thinking through provider metadata while preserving generic thinking suppression for non-GLM Z.AI custom models

Some files were not shown because too many files have changed in this diff Show More