mirror of
https://github.com/cline/cline.git
synced 2026-09-09 06:45:53 +08:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dec7167118 | ||
|
|
4d36f93b18 | ||
|
|
bbba1235d2 | ||
|
|
d4a8e3d5a0 | ||
|
|
99eeace344 | ||
|
|
56aacb33ea |
@@ -92,7 +92,7 @@ jobs:
|
||||
|
||||
- name: Run Tests
|
||||
if: ${{ !cancelled() && steps.build_sdk_step.outcome == 'success' && steps.build_cli_step.outcome == 'success' && matrix.os != 'windows-latest' }}
|
||||
run: bun run test
|
||||
run: bun run test && bun -F cline-xml-tool-calling-plugin test
|
||||
|
||||
- name: Run SDK Tests (Windows)
|
||||
if: ${{ !cancelled() && steps.build_sdk_step.outcome == 'success' && steps.build_cli_step.outcome == 'success' && matrix.os == 'windows-latest' }}
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
# Cline CLI Changelog
|
||||
|
||||
## 3.0.40
|
||||
|
||||
- Added a manual API key escape hatch for Cline OAuth providers, so you can enter a key by hand from settings
|
||||
- Fixed provider config not reloading when switching models
|
||||
- Fixed auto-update failing to detect Bun global installs after symlink resolution
|
||||
- Fixed unexpected logouts caused by transient network or server errors during token refresh
|
||||
- The ClinePass usage-limit error is now surfaced clearly when you hit the limit
|
||||
- Session id is now preserved when continuing within the same session
|
||||
- Hardened context compaction budget handling
|
||||
|
||||
## 3.0.39
|
||||
|
||||
- You can now select Cline free models on the ClinePass provider in the model picker
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@cline/cli",
|
||||
"displayName": "cline",
|
||||
"version": "3.0.40",
|
||||
"version": "3.0.39",
|
||||
"description": "Autonomous coding agent CLI - capable of creating/editing files, running commands, using the browser, and more",
|
||||
"type": "module",
|
||||
"publishConfig": {
|
||||
|
||||
@@ -101,22 +101,6 @@ describe("getInstallationInfo", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("detects bun global installs from the resolved install path", () => {
|
||||
// bun symlinks ~/.bun/bin/cline -> ~/.bun/install/global/node_modules/...,
|
||||
// and realpathSync resolves through the symlink before detection runs.
|
||||
const wrapperPath = createTempFile(
|
||||
".bun/install/global/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.BUN,
|
||||
packageName: "cline",
|
||||
updateCommand: "bun add -g cline@latest",
|
||||
});
|
||||
});
|
||||
|
||||
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"];
|
||||
|
||||
@@ -118,12 +118,7 @@ export function getInstallationInfo(currentVersion: string): InstallationInfo {
|
||||
updateCommand: `yarn global add ${DEFAULT_PACKAGE_NAME}@${tag}`,
|
||||
};
|
||||
}
|
||||
// `bun add -g` symlinks bins into ~/.bun/bin, but realpathSync resolves
|
||||
// them to ~/.bun/install/global/node_modules/..., so match both.
|
||||
if (
|
||||
scriptPath.includes("/.bun/bin") ||
|
||||
scriptPath.includes("/.bun/install/global/")
|
||||
) {
|
||||
if (scriptPath.includes("/.bun/bin")) {
|
||||
return {
|
||||
packageManager: PackageManager.BUN,
|
||||
packageName: DEFAULT_PACKAGE_NAME,
|
||||
|
||||
@@ -158,9 +158,8 @@ vi.mock("./runtime/run-interactive", () => {
|
||||
});
|
||||
vi.mock("./utils/session", () => sessionMocks);
|
||||
vi.mock("./session/session", () => sessionMocks);
|
||||
vi.mock("@cline/core", async () => {
|
||||
vi.mock("@cline/core", () => {
|
||||
return {
|
||||
...(await vi.importActual("@cline/core")),
|
||||
resolveProviderConfig: llmMocks.resolveProviderConfig,
|
||||
createTeamName: vi.fn(() => "team-test"),
|
||||
createUserInstructionConfigService: vi.fn(() => ({
|
||||
|
||||
@@ -928,17 +928,6 @@ export async function runCli(): Promise<void> {
|
||||
runAgent,
|
||||
} = await loadCliRuntimeModules();
|
||||
|
||||
// Register the SDK early logger as early as possible — before any
|
||||
// provider settings reads — so the full startup sequence is captured.
|
||||
// These components operate before/outside ClineCore sessions, so the
|
||||
// session-scoped logger can't reach them.
|
||||
const { createCliLoggerAdapter } = await import("./logging/adapter");
|
||||
const loggerAdapter = createCliLoggerAdapter({
|
||||
runtime: "cli",
|
||||
component: "main",
|
||||
});
|
||||
coreServer.setSdkLogger(loggerAdapter.core);
|
||||
|
||||
const userInstructionService = createUserInstructionConfigService({
|
||||
skills: {
|
||||
workspacePath: workspaceRoot,
|
||||
|
||||
@@ -157,7 +157,6 @@ function makeManager() {
|
||||
ingestHookEvent: vi.fn(),
|
||||
subscribe: vi.fn(),
|
||||
updateSessionModel: vi.fn(),
|
||||
updateSessionConnection: vi.fn(async () => {}),
|
||||
pendingPrompts: {
|
||||
update: vi.fn(),
|
||||
},
|
||||
@@ -815,83 +814,6 @@ describe("createInteractiveSessionRuntime", () => {
|
||||
expect(runtime.getActiveSessionId()).toBe("session-2");
|
||||
});
|
||||
|
||||
it("preserves the session id and applies changed provider config when restarting with the current messages", async () => {
|
||||
const manager = makeManager();
|
||||
const config = {
|
||||
...createConfig(),
|
||||
providerId: "cline",
|
||||
modelId: "anthropic/claude-sonnet-4.6",
|
||||
apiKey: "cline-key",
|
||||
};
|
||||
const messages: Message[] = [
|
||||
{ role: "user", content: [{ type: "text", text: "hello" }] },
|
||||
];
|
||||
manager.readMessages.mockResolvedValue(messages);
|
||||
const runtime = await makeRuntime(manager, { config });
|
||||
|
||||
await runtime.ensureReady();
|
||||
expect(manager.start).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expect.objectContaining({
|
||||
config: expect.objectContaining({
|
||||
providerId: "cline",
|
||||
modelId: "anthropic/claude-sonnet-4.6",
|
||||
apiKey: "cline-key",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
config.providerId = "openai-compatible";
|
||||
config.modelId = "custom-model";
|
||||
config.apiKey = "new-key";
|
||||
await runtime.restartWithCurrentMessages();
|
||||
|
||||
expect(manager.start).toHaveBeenCalledTimes(2);
|
||||
expect(manager.start).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expect.objectContaining({
|
||||
config: expect.objectContaining({
|
||||
sessionId: "session-1",
|
||||
providerId: "openai-compatible",
|
||||
modelId: "custom-model",
|
||||
apiKey: "new-key",
|
||||
}),
|
||||
initialMessages: messages,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("updates the active session connection in place without restarting", async () => {
|
||||
const manager = makeManager();
|
||||
const runtime = await makeRuntime(manager);
|
||||
|
||||
await runtime.ensureReady();
|
||||
await runtime.updateCurrentSessionConnection({
|
||||
providerId: "openai",
|
||||
modelId: "codex-test",
|
||||
});
|
||||
|
||||
expect(manager.updateSessionConnection).toHaveBeenCalledWith("session-1", {
|
||||
providerId: "openai",
|
||||
modelId: "codex-test",
|
||||
});
|
||||
expect(manager.start).toHaveBeenCalledTimes(1);
|
||||
expect(runtime.getActiveSessionId()).toBe("session-1");
|
||||
});
|
||||
|
||||
it("does not reuse the session id when restarting empty", async () => {
|
||||
const manager = makeManager();
|
||||
const runtime = await makeRuntime(manager);
|
||||
|
||||
await runtime.ensureReady();
|
||||
await runtime.restartEmpty();
|
||||
|
||||
expect(manager.start).toHaveBeenCalledTimes(2);
|
||||
const secondStart = manager.start.mock.calls[1]?.[0] as {
|
||||
config?: { sessionId?: string };
|
||||
};
|
||||
expect(secondStart?.config?.sessionId).toBeUndefined();
|
||||
});
|
||||
|
||||
it("recovers empty read-driven restarts when the active interactive session disappeared", async () => {
|
||||
const manager = makeManager();
|
||||
manager.readMessages.mockRejectedValueOnce(
|
||||
|
||||
@@ -49,9 +49,6 @@ type RuntimeHooks = ReturnType<typeof createRuntimeHooks>;
|
||||
type StartedSession = Awaited<ReturnType<CliCore["start"]>>;
|
||||
type CurrentTurnInput = Omit<Parameters<CliCore["send"]>[0], "sessionId">;
|
||||
type CurrentTurnResult = Awaited<ReturnType<CliCore["send"]>>;
|
||||
export type SessionConnectionUpdate = Parameters<
|
||||
CliCore["updateSessionConnection"]
|
||||
>[1];
|
||||
type AskQuestionRef = {
|
||||
current: ((question: string, options: string[]) => Promise<string>) | null;
|
||||
};
|
||||
@@ -213,18 +210,12 @@ export function createInteractiveSessionRuntime(input: {
|
||||
initial: Message[] = [],
|
||||
sessionMetadata?: Record<string, unknown>,
|
||||
initialCompactionState?: SessionCompactionState,
|
||||
// Restarting an old session associate with this ID,
|
||||
// For continuing the same conversation, e.g. after a config change.
|
||||
sessionId?: string,
|
||||
): Promise<void> => {
|
||||
const generation = sessionStartGeneration;
|
||||
const manager = await ensureSessionManager();
|
||||
const started = await manager.start({
|
||||
source: SessionSource.CLI,
|
||||
config: {
|
||||
...buildSessionConfig(),
|
||||
...(sessionId ? { sessionId } : {}),
|
||||
},
|
||||
config: buildSessionConfig(),
|
||||
toolPolicies: input.config.toolPolicies,
|
||||
interactive: true,
|
||||
initialMessages: initial,
|
||||
@@ -424,14 +415,7 @@ export function createInteractiveSessionRuntime(input: {
|
||||
messages: Message[],
|
||||
sessionMetadata?: Record<string, unknown>,
|
||||
initialCompactionState?: SessionCompactionState,
|
||||
options?: { preserveSessionId?: boolean },
|
||||
): Promise<void> => {
|
||||
// Config-only restarts (model/mode/account changes) continue the same
|
||||
// conversation, so they must keep the session id — otherwise each
|
||||
// restart mints a new session history entry for the same conversation.
|
||||
const reuseSessionId = options?.preserveSessionId
|
||||
? activeSessionId || undefined
|
||||
: undefined;
|
||||
sessionStartGeneration += 1;
|
||||
pendingResumeSessionId = undefined;
|
||||
startupError = undefined;
|
||||
@@ -447,7 +431,6 @@ export function createInteractiveSessionRuntime(input: {
|
||||
messages,
|
||||
sessionMetadata,
|
||||
initialCompactionState,
|
||||
reuseSessionId,
|
||||
);
|
||||
})().catch((error) => {
|
||||
startupError = error;
|
||||
@@ -490,24 +473,9 @@ export function createInteractiveSessionRuntime(input: {
|
||||
systemPrompt: compactionState?.system_prompt,
|
||||
})
|
||||
: undefined,
|
||||
{ preserveSessionId: true },
|
||||
);
|
||||
};
|
||||
|
||||
const updateCurrentSessionConnection = async (
|
||||
update: SessionConnectionUpdate,
|
||||
): Promise<void> => {
|
||||
await ensureReady();
|
||||
const manager = sessionManager;
|
||||
const sessionId = activeSessionId;
|
||||
if (!manager || !sessionId) {
|
||||
// No live session to update; the next startup builds its config from
|
||||
// the already-mutated CLI config, so nothing else is needed.
|
||||
return;
|
||||
}
|
||||
await manager.updateSessionConnection(sessionId, update);
|
||||
};
|
||||
|
||||
const restartEmpty = async (): Promise<void> => {
|
||||
await restartWithMessages([]);
|
||||
};
|
||||
@@ -872,7 +840,6 @@ export function createInteractiveSessionRuntime(input: {
|
||||
resetForNewSession,
|
||||
restartWithMessages,
|
||||
restartWithCurrentMessages,
|
||||
updateCurrentSessionConnection,
|
||||
resumeSession,
|
||||
forkCurrentSession,
|
||||
compactCurrentSession,
|
||||
|
||||
@@ -43,15 +43,6 @@ const CLI_SUBSCRIPTION_URL =
|
||||
"https://app.cline.bot/promo?code=CLI-8OFF&personal=true";
|
||||
const SDK_CLINE_PASS_SUBSCRIPTION_MESSAGE = `No access to ClinePass subscription models yet. Subscribe to ClinePass, the low cost open weights model coding plan: ${CLINE_PASS_SUBSCRIPTION_URL}`;
|
||||
const CLI_CLINE_PASS_SUBSCRIPTION_MESSAGE = `No access to ClinePass subscription models yet. Subscribe to ClinePass, the low cost open weights model coding plan: ${CLI_SUBSCRIPTION_URL}`;
|
||||
const CLINE_PASS_LIMIT_DETAIL_MESSAGE =
|
||||
"You have reached your 5-hour Clinepass limit. The limit resets in 5h, please try again later.";
|
||||
const CLI_CLINE_PASS_LIMIT_MESSAGE = [
|
||||
"ClinePass limit reached",
|
||||
CLINE_PASS_LIMIT_DETAIL_MESSAGE,
|
||||
"Switch to Cline usage-based billing and retry with the Cline provider.",
|
||||
"Interactive CLI: open the model selector with /model, choose Cline, then retry.",
|
||||
"Headless CLI: rerun with --provider cline.",
|
||||
].join("\n");
|
||||
const CLINE_ORG_INDIVIDUAL_INFERENCE_SUBSCRIPTION_MESSAGE =
|
||||
"Organization accounts cannot use ClinePass subscriptions. Go to /account -> change account to switch to your personal account for ClinePass";
|
||||
|
||||
@@ -74,30 +65,6 @@ vi.mock("@cline/core", () => ({
|
||||
.includes(
|
||||
"organization accounts cannot use individual model inference subscriptions",
|
||||
),
|
||||
isClinePassLimitError: (error: unknown) =>
|
||||
error instanceof Error && error.name === "ClinePassLimitError",
|
||||
extractClinePassLimitMessage: (text: string) => {
|
||||
const normalized = text.toLowerCase();
|
||||
const prefix = "you have reached your";
|
||||
const suffix = "please try again later.";
|
||||
const start = normalized.indexOf(prefix);
|
||||
if (start === -1) return undefined;
|
||||
const suffixStart = normalized.indexOf(suffix, start);
|
||||
if (suffixStart === -1) return undefined;
|
||||
const end = suffixStart + suffix.length;
|
||||
if (!normalized.slice(start, end).includes("clinepass limit")) {
|
||||
return undefined;
|
||||
}
|
||||
return text.slice(start, end);
|
||||
},
|
||||
isClinePassLimitMessage: (text: string) => {
|
||||
const normalized = text.toLowerCase();
|
||||
return (
|
||||
normalized.includes("you have reached your") &&
|
||||
normalized.includes("clinepass limit") &&
|
||||
normalized.includes("please try again later.")
|
||||
);
|
||||
},
|
||||
prewarmFileIndex: vi.fn(async () => undefined),
|
||||
SessionSource: {
|
||||
CLI: "cli",
|
||||
@@ -802,126 +769,6 @@ describe("runAgent", () => {
|
||||
expect(outputMocks.writeErr).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("formats ClinePass limit errors with usage-based billing guidance", async () => {
|
||||
const startedAt = new Date("2026-03-22T00:00:00.000Z");
|
||||
const endedAt = new Date("2026-03-22T00:00:01.000Z");
|
||||
sessionManagerMocks.start.mockResolvedValue({
|
||||
sessionId: "session-1",
|
||||
manifestPath: "/tmp/manifest.json",
|
||||
messagesPath: "/tmp/messages.json",
|
||||
manifest: { session_id: "session-1" },
|
||||
result: {
|
||||
text: `Error: ${CLINE_PASS_LIMIT_DETAIL_MESSAGE}`,
|
||||
usage: {
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
cacheReadTokens: 0,
|
||||
cacheWriteTokens: 0,
|
||||
totalCost: 0,
|
||||
},
|
||||
messages: [],
|
||||
toolCalls: [],
|
||||
iterations: 1,
|
||||
finishReason: "error",
|
||||
model: { id: "premium-model", provider: "cline-pass", info: {} },
|
||||
startedAt,
|
||||
endedAt,
|
||||
durationMs: 1000,
|
||||
},
|
||||
});
|
||||
sessionManagerMocks.getAccumulatedUsage.mockResolvedValue(undefined);
|
||||
|
||||
const { runAgent } = await import("./run-agent");
|
||||
|
||||
await expect(
|
||||
runAgent("test prompt", {
|
||||
cwd: process.cwd(),
|
||||
enableAgentTeams: false,
|
||||
enableSpawnAgent: false,
|
||||
enableTools: [],
|
||||
execution: { maxConsecutiveMistakes: 3 },
|
||||
logger: undefined,
|
||||
mode: "yolo",
|
||||
modelId: "premium-model",
|
||||
outputMode: "text",
|
||||
providerId: "cline-pass",
|
||||
systemPrompt: "system",
|
||||
thinking: false,
|
||||
toolPolicies: { "*": { autoApprove: true } },
|
||||
verbose: false,
|
||||
workspaceRoot: process.cwd(),
|
||||
} as never),
|
||||
).resolves.toBeUndefined();
|
||||
|
||||
expect(process.exitCode).toBe(1);
|
||||
expect(outputMocks.writeErr).toHaveBeenCalledWith(
|
||||
CLI_CLINE_PASS_LIMIT_MESSAGE,
|
||||
);
|
||||
});
|
||||
|
||||
it("does not duplicate ClinePass limit errors already displayed by agent events", async () => {
|
||||
const startedAt = new Date("2026-03-22T00:00:00.000Z");
|
||||
const endedAt = new Date("2026-03-22T00:00:01.000Z");
|
||||
sessionManagerMocks.start.mockImplementation(async () => {
|
||||
sessionEventsMocks.listener?.({
|
||||
type: "error",
|
||||
error: new Error(`Error: ${CLINE_PASS_LIMIT_DETAIL_MESSAGE}`),
|
||||
recoverable: false,
|
||||
});
|
||||
|
||||
return {
|
||||
sessionId: "session-1",
|
||||
manifestPath: "/tmp/manifest.json",
|
||||
messagesPath: "/tmp/messages.json",
|
||||
manifest: { session_id: "session-1" },
|
||||
result: {
|
||||
text: `Error: ${CLINE_PASS_LIMIT_DETAIL_MESSAGE}`,
|
||||
usage: {
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
cacheReadTokens: 0,
|
||||
cacheWriteTokens: 0,
|
||||
totalCost: 0,
|
||||
},
|
||||
messages: [],
|
||||
toolCalls: [],
|
||||
iterations: 1,
|
||||
finishReason: "error",
|
||||
model: { id: "premium-model", provider: "cline-pass", info: {} },
|
||||
startedAt,
|
||||
endedAt,
|
||||
durationMs: 1000,
|
||||
},
|
||||
};
|
||||
});
|
||||
sessionManagerMocks.getAccumulatedUsage.mockResolvedValue(undefined);
|
||||
|
||||
const { runAgent } = await import("./run-agent");
|
||||
|
||||
await expect(
|
||||
runAgent("test prompt", {
|
||||
cwd: process.cwd(),
|
||||
enableAgentTeams: false,
|
||||
enableSpawnAgent: false,
|
||||
enableTools: [],
|
||||
execution: { maxConsecutiveMistakes: 3 },
|
||||
logger: undefined,
|
||||
mode: "yolo",
|
||||
modelId: "premium-model",
|
||||
outputMode: "text",
|
||||
providerId: "cline-pass",
|
||||
systemPrompt: "system",
|
||||
thinking: false,
|
||||
toolPolicies: { "*": { autoApprove: true } },
|
||||
verbose: false,
|
||||
workspaceRoot: process.cwd(),
|
||||
} as never),
|
||||
).resolves.toBeUndefined();
|
||||
|
||||
expect(process.exitCode).toBe(1);
|
||||
expect(outputMocks.writeErr).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("surfaces post-run bookkeeping failures after a completed result", async () => {
|
||||
const startedAt = new Date("2026-03-22T00:00:00.000Z");
|
||||
const endedAt = new Date("2026-03-22T00:00:01.000Z");
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { Config } from "../utils/types";
|
||||
import {
|
||||
applyInteractiveModelChange,
|
||||
resolveReasoningForModelChange,
|
||||
} from "./run-interactive";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { resolveReasoningForModelChange } from "./run-interactive";
|
||||
|
||||
describe("resolveReasoningForModelChange", () => {
|
||||
it("persists disabled reasoning only when thinking is explicitly false", () => {
|
||||
@@ -42,69 +38,3 @@ describe("resolveReasoningForModelChange", () => {
|
||||
).toEqual({ enabled: true, effort: "medium" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("applyInteractiveModelChange", () => {
|
||||
it("restarts with the current transcript so a provider switch reloads its complete configuration", async () => {
|
||||
const config = {
|
||||
providerId: "openai-compatible",
|
||||
modelId: "custom-model",
|
||||
apiKey: "new-key",
|
||||
thinking: undefined,
|
||||
reasoningEffort: undefined,
|
||||
} as Config;
|
||||
const getProviderSettings = vi.fn(() => ({
|
||||
provider: "openai-compatible",
|
||||
apiKey: "new-key",
|
||||
baseUrl: "https://example.com/v1",
|
||||
headers: { "X-Custom-Header": "custom-value" },
|
||||
client: "openai-compatible" as const,
|
||||
protocol: "openai-chat" as const,
|
||||
model: "old-model",
|
||||
}));
|
||||
const saveProviderSettings = vi.fn(() => ({
|
||||
version: 1 as const,
|
||||
providers: {},
|
||||
}));
|
||||
const ensureReady = vi.fn(async () => {});
|
||||
const restartWithCurrentMessages = vi.fn(async () => {});
|
||||
const updateCurrentSessionConnection = vi.fn(async () => {});
|
||||
|
||||
await applyInteractiveModelChange({
|
||||
config,
|
||||
providerSettingsManager: {
|
||||
getProviderSettings,
|
||||
saveProviderSettings,
|
||||
},
|
||||
sessionRuntime: {
|
||||
ensureReady,
|
||||
restartWithCurrentMessages,
|
||||
updateCurrentSessionConnection,
|
||||
},
|
||||
});
|
||||
|
||||
expect(saveProviderSettings).toHaveBeenCalledWith({
|
||||
provider: "openai-compatible",
|
||||
apiKey: "new-key",
|
||||
baseUrl: "https://example.com/v1",
|
||||
headers: { "X-Custom-Header": "custom-value" },
|
||||
client: "openai-compatible",
|
||||
protocol: "openai-chat",
|
||||
model: "custom-model",
|
||||
});
|
||||
expect(ensureReady).toHaveBeenCalledOnce();
|
||||
expect(restartWithCurrentMessages).toHaveBeenCalledOnce();
|
||||
expect(updateCurrentSessionConnection).toHaveBeenCalledWith({
|
||||
providerId: "openai-compatible",
|
||||
modelId: "custom-model",
|
||||
});
|
||||
expect(ensureReady.mock.invocationCallOrder[0]).toBeLessThan(
|
||||
restartWithCurrentMessages.mock.invocationCallOrder[0] ?? 0,
|
||||
);
|
||||
expect(saveProviderSettings.mock.invocationCallOrder[0]).toBeLessThan(
|
||||
restartWithCurrentMessages.mock.invocationCallOrder[0] ?? 0,
|
||||
);
|
||||
expect(restartWithCurrentMessages.mock.invocationCallOrder[0]).toBeLessThan(
|
||||
updateCurrentSessionConnection.mock.invocationCallOrder[0] ?? 0,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -82,51 +82,6 @@ export function resolveReasoningForModelChange(
|
||||
return existing.reasoning;
|
||||
}
|
||||
|
||||
export async function applyInteractiveModelChange(input: {
|
||||
config: Config;
|
||||
providerSettingsManager: Pick<
|
||||
ProviderSettingsManager,
|
||||
"getProviderSettings" | "saveProviderSettings"
|
||||
>;
|
||||
sessionRuntime: Pick<
|
||||
ReturnType<typeof createInteractiveSessionRuntime>,
|
||||
| "ensureReady"
|
||||
| "restartWithCurrentMessages"
|
||||
| "updateCurrentSessionConnection"
|
||||
>;
|
||||
}): Promise<void> {
|
||||
const { config, providerSettingsManager, sessionRuntime } = input;
|
||||
await sessionRuntime.ensureReady();
|
||||
await onProviderChange({
|
||||
config,
|
||||
providerId: config.providerId,
|
||||
});
|
||||
const existing = providerSettingsManager.getProviderSettings(
|
||||
config.providerId,
|
||||
) ?? {
|
||||
provider: config.providerId,
|
||||
};
|
||||
const reasoning = resolveReasoningForModelChange(config, existing);
|
||||
providerSettingsManager.saveProviderSettings({
|
||||
...existing,
|
||||
model: config.modelId,
|
||||
...(reasoning === undefined ? {} : { reasoning }),
|
||||
});
|
||||
|
||||
// Provider changes affect more than the model connection: startup resolves
|
||||
// the endpoint, headers, provider-specific options, tools, and plugins. Rebuild
|
||||
// the runtime with the existing transcript so all of that state changes
|
||||
// together. restartWithCurrentMessages preserves the session ID.
|
||||
await sessionRuntime.restartWithCurrentMessages();
|
||||
// A same-ID restart reuses the existing manifest. Sync its connection label
|
||||
// after the fully configured runtime is live so session history reflects the
|
||||
// provider/model that will handle subsequent turns.
|
||||
await sessionRuntime.updateCurrentSessionConnection({
|
||||
providerId: config.providerId,
|
||||
modelId: config.modelId,
|
||||
});
|
||||
}
|
||||
|
||||
export async function runInteractive(
|
||||
config: Config,
|
||||
userInstructionService?: UserInstructionConfigService,
|
||||
@@ -732,12 +687,25 @@ export async function runInteractive(
|
||||
onNewSession: async () => {
|
||||
await sessionRuntime.resetForNewSession();
|
||||
},
|
||||
onModelChange: () =>
|
||||
applyInteractiveModelChange({
|
||||
onModelChange: async () => {
|
||||
await sessionRuntime.ensureReady();
|
||||
await onProviderChange({
|
||||
config,
|
||||
providerSettingsManager,
|
||||
sessionRuntime,
|
||||
}),
|
||||
providerId: config.providerId,
|
||||
});
|
||||
const existing = providerSettingsManager.getProviderSettings(
|
||||
config.providerId,
|
||||
) ?? {
|
||||
provider: config.providerId,
|
||||
};
|
||||
const reasoning = resolveReasoningForModelChange(config, existing);
|
||||
providerSettingsManager.saveProviderSettings({
|
||||
...existing,
|
||||
model: config.modelId,
|
||||
...(reasoning === undefined ? {} : { reasoning }),
|
||||
});
|
||||
await sessionRuntime.restartWithCurrentMessages();
|
||||
},
|
||||
onSessionRestart: async () => {
|
||||
await sessionRuntime.ensureReady();
|
||||
await sessionRuntime.restartEmpty();
|
||||
|
||||
@@ -5,11 +5,9 @@ import { useEffect, useState } from "react";
|
||||
import "opentui-spinner/react";
|
||||
import {
|
||||
getClineOrgIndividualInferenceSubscriptionMessage,
|
||||
getClinePassLimitDetailMessage,
|
||||
getCliSubscriptionUrl,
|
||||
getIndividualPlanFeatures,
|
||||
isClineOrgIndividualInferenceSubscriptionErrorMessage,
|
||||
isClinePassLimitErrorMessage,
|
||||
isClinePassSubscriptionError,
|
||||
} from "../../utils/cline-pass-errors";
|
||||
import {
|
||||
@@ -421,54 +419,6 @@ function ClineOrgIndividualInferenceSubscriptionErrorView(props: {
|
||||
);
|
||||
}
|
||||
|
||||
function ClinePassLimitErrorView(props: {
|
||||
message: string;
|
||||
defaultFg?: string;
|
||||
terminalTheme: TerminalTheme;
|
||||
}) {
|
||||
const detail = getClinePassLimitDetailMessage(props.message) ?? props.message;
|
||||
|
||||
return (
|
||||
<box flexDirection="row">
|
||||
<text fg={palette.act} content="* " />
|
||||
<box
|
||||
flexDirection="column"
|
||||
border
|
||||
borderStyle="rounded"
|
||||
borderColor={palette.act}
|
||||
paddingX={1}
|
||||
>
|
||||
<text fg="red">ClinePass limit reached</text>
|
||||
<text fg={props.defaultFg} selectable content={detail} />
|
||||
<text
|
||||
fg={props.defaultFg}
|
||||
selectable
|
||||
content="Switch to Cline usage-based billing and retry with the Cline provider."
|
||||
/>
|
||||
<box flexDirection="row">
|
||||
<text fg="gray">Interactive CLI: </text>
|
||||
<text
|
||||
fg={props.defaultFg}
|
||||
selectable
|
||||
content="type /model, press tab to change provider, choose Cline, then retry."
|
||||
/>
|
||||
</box>
|
||||
<box flexDirection="row">
|
||||
<text fg="gray">Headless CLI: </text>
|
||||
<text fg={props.defaultFg} selectable content="rerun with " />
|
||||
<code
|
||||
content="--provider cline"
|
||||
filetype="bash"
|
||||
syntaxStyle={getSyntaxStyle(props.terminalTheme)}
|
||||
selectable
|
||||
/>
|
||||
<text fg={props.defaultFg} selectable content="." />
|
||||
</box>
|
||||
</box>
|
||||
</box>
|
||||
);
|
||||
}
|
||||
|
||||
export function ChatEntryView(props: {
|
||||
entry: ChatEntry;
|
||||
accent?: string;
|
||||
@@ -584,15 +534,6 @@ export function ChatEntryView(props: {
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (isClinePassLimitErrorMessage(entry.text)) {
|
||||
return (
|
||||
<ClinePassLimitErrorView
|
||||
message={entry.text}
|
||||
defaultFg={defaultFg}
|
||||
terminalTheme={terminalTheme}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<box flexDirection="row">
|
||||
<text fg="red" content="* " />
|
||||
|
||||
@@ -1,50 +1,8 @@
|
||||
import {
|
||||
getProviderAuthStorageId,
|
||||
type ProviderSettingsManager,
|
||||
saveLocalProviderSettings,
|
||||
} from "@cline/core";
|
||||
import { CLI_PROMO_CODE } from "../../../utils/cline-pass-errors";
|
||||
|
||||
const CLINE_PASS_SUBSCRIPTION_PATH = "/dashboard/subscription";
|
||||
const DEFAULT_APP_BASE_URL = "https://app.cline.bot";
|
||||
|
||||
/**
|
||||
* Persist a manually entered API key for an OAuth-capable provider — the
|
||||
* escape hatch for when OAuth login isn't working. Any stored OAuth tokens
|
||||
* are cleared: the auth handler prefers auth.accessToken over apiKey, so a
|
||||
* stale token would otherwise keep winning over the manual key.
|
||||
*
|
||||
* The key is written both to the provider's auth storage entry (cline-pass
|
||||
* stores credentials under "cline") and to the provider's own entry: settings
|
||||
* resolution lets a direct entry shadow the storage entry, and provider
|
||||
* switching copies merged settings (including auth) into direct entries, so
|
||||
* both must be updated for the manual key to reliably take effect.
|
||||
*/
|
||||
export function saveManualProviderApiKey(
|
||||
manager: ProviderSettingsManager,
|
||||
providerId: string,
|
||||
apiKey: string,
|
||||
): void {
|
||||
// Empty strings delete these keys from the stored auth object.
|
||||
const clearedAuth = { accessToken: "", refreshToken: "", apiKey: "" };
|
||||
const storageProviderId = getProviderAuthStorageId(providerId) ?? providerId;
|
||||
saveLocalProviderSettings(manager, {
|
||||
providerId: storageProviderId,
|
||||
apiKey,
|
||||
auth: clearedAuth,
|
||||
});
|
||||
if (
|
||||
providerId !== storageProviderId &&
|
||||
manager.read().providers[providerId]
|
||||
) {
|
||||
saveLocalProviderSettings(manager, {
|
||||
providerId,
|
||||
apiKey,
|
||||
auth: clearedAuth,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function buildClinePassSubscriptionPageUrl(
|
||||
appBaseUrl: string | undefined,
|
||||
): string {
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
import { mkdtempSync, rmSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { ProviderSettingsManager } from "@cline/core";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
getPersistedProviderApiKey,
|
||||
isProviderConfigured,
|
||||
} from "../../../utils/provider-auth";
|
||||
import {
|
||||
buildClinePassSubscriptionPageUrl,
|
||||
saveManualProviderApiKey,
|
||||
} from "./provider-picker-helpers";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildClinePassSubscriptionPageUrl } from "./provider-picker-helpers";
|
||||
|
||||
describe("buildClinePassSubscriptionPageUrl", () => {
|
||||
it("opens the personal subscription page on production by default", () => {
|
||||
@@ -27,99 +16,3 @@ describe("buildClinePassSubscriptionPageUrl", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("saveManualProviderApiKey", () => {
|
||||
const tempDirs: string[] = [];
|
||||
|
||||
afterEach(() => {
|
||||
for (const dir of tempDirs.splice(0)) {
|
||||
rmSync(dir, { force: true, recursive: true });
|
||||
}
|
||||
});
|
||||
|
||||
function createManager(): ProviderSettingsManager {
|
||||
const dir = mkdtempSync(join(tmpdir(), "cline-cli-provider-picker-"));
|
||||
tempDirs.push(dir);
|
||||
return new ProviderSettingsManager({
|
||||
filePath: join(dir, "providers.json"),
|
||||
});
|
||||
}
|
||||
|
||||
it("clears stored OAuth tokens so the manual key takes effect", () => {
|
||||
const manager = createManager();
|
||||
manager.saveProviderSettings({
|
||||
provider: "cline",
|
||||
auth: {
|
||||
accessToken: "stale-access-token",
|
||||
refreshToken: "stale-refresh-token",
|
||||
accountId: "acct_123",
|
||||
},
|
||||
});
|
||||
|
||||
saveManualProviderApiKey(manager, "cline", "manual-api-key");
|
||||
|
||||
const settings = manager.getProviderSettings("cline");
|
||||
expect(settings?.apiKey).toBe("manual-api-key");
|
||||
expect(settings?.auth?.accessToken).toBeUndefined();
|
||||
expect(settings?.auth?.refreshToken).toBeUndefined();
|
||||
expect(settings?.auth?.accountId).toBe("acct_123");
|
||||
expect(getPersistedProviderApiKey("cline", settings)).toBe(
|
||||
"manual-api-key",
|
||||
);
|
||||
expect(isProviderConfigured("cline", settings)).toBe(true);
|
||||
});
|
||||
|
||||
it("saves cline-pass keys to the shared cline auth storage entry", () => {
|
||||
const manager = createManager();
|
||||
manager.saveProviderSettings({
|
||||
provider: "cline",
|
||||
auth: {
|
||||
accessToken: "stale-access-token",
|
||||
refreshToken: "stale-refresh-token",
|
||||
},
|
||||
});
|
||||
|
||||
saveManualProviderApiKey(manager, "cline-pass", "manual-api-key");
|
||||
|
||||
// cline-pass inherits auth storage from the "cline" entry, so the key
|
||||
// must land there and the stale tokens must be gone for both providers.
|
||||
const clineSettings = manager.getProviderSettings("cline");
|
||||
expect(clineSettings?.apiKey).toBe("manual-api-key");
|
||||
expect(clineSettings?.auth?.accessToken).toBeUndefined();
|
||||
|
||||
const clinePassSettings = manager.getProviderSettings("cline-pass");
|
||||
expect(getPersistedProviderApiKey("cline-pass", clinePassSettings)).toBe(
|
||||
"manual-api-key",
|
||||
);
|
||||
expect(isProviderConfigured("cline-pass", clinePassSettings)).toBe(true);
|
||||
});
|
||||
|
||||
it("clears stale credentials copied into a direct cline-pass entry", () => {
|
||||
const manager = createManager();
|
||||
manager.saveProviderSettings({
|
||||
provider: "cline",
|
||||
auth: {
|
||||
accessToken: "stale-access-token",
|
||||
refreshToken: "stale-refresh-token",
|
||||
},
|
||||
});
|
||||
// Provider switching copies the merged settings (including auth) into
|
||||
// a direct cline-pass entry, which shadows the shared "cline" entry.
|
||||
manager.saveProviderSettings({
|
||||
provider: "cline-pass",
|
||||
apiKey: "stale-copied-key",
|
||||
auth: {
|
||||
accessToken: "stale-access-token",
|
||||
refreshToken: "stale-refresh-token",
|
||||
},
|
||||
});
|
||||
|
||||
saveManualProviderApiKey(manager, "cline-pass", "manual-api-key");
|
||||
|
||||
const clinePassSettings = manager.getProviderSettings("cline-pass");
|
||||
expect(clinePassSettings?.auth?.accessToken).toBeUndefined();
|
||||
expect(getPersistedProviderApiKey("cline-pass", clinePassSettings)).toBe(
|
||||
"manual-api-key",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -37,10 +37,7 @@ import {
|
||||
getSearchableListRowsWindow,
|
||||
type SearchableItem,
|
||||
} from "../searchable-list";
|
||||
import {
|
||||
buildClinePassSubscriptionPageUrl,
|
||||
saveManualProviderApiKey,
|
||||
} from "./provider-picker-helpers";
|
||||
import { buildClinePassSubscriptionPageUrl } from "./provider-picker-helpers";
|
||||
|
||||
interface ProviderItem {
|
||||
id: string;
|
||||
@@ -727,27 +724,13 @@ export function CodexCliStatusContent(
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves `true` on successful login, `"use_api_key"` when the user opts
|
||||
* into manual API key entry (only offered with `allowApiKeyFallback`).
|
||||
*/
|
||||
export type OAuthLoginResult = boolean | "use_api_key";
|
||||
|
||||
export function OAuthLoginContent(
|
||||
props: ChoiceContext<OAuthLoginResult> & {
|
||||
props: ChoiceContext<boolean> & {
|
||||
providerId: string;
|
||||
providerName: string;
|
||||
allowApiKeyFallback?: boolean;
|
||||
},
|
||||
) {
|
||||
const {
|
||||
resolve,
|
||||
dismiss,
|
||||
dialogId,
|
||||
providerId,
|
||||
providerName,
|
||||
allowApiKeyFallback,
|
||||
} = props;
|
||||
const { resolve, dismiss, dialogId, providerId, providerName } = props;
|
||||
const [mode, setMode] = useState<"browser" | "device">(
|
||||
providerId === "cline" ? "device" : "browser",
|
||||
);
|
||||
@@ -880,18 +863,9 @@ export function OAuthLoginContent(
|
||||
if (key.name === "escape") {
|
||||
cancelAuthAttempt();
|
||||
dismiss();
|
||||
return;
|
||||
}
|
||||
if (key.name === "k" && allowApiKeyFallback) {
|
||||
cancelAuthAttempt();
|
||||
resolve("use_api_key");
|
||||
}
|
||||
}, dialogId);
|
||||
|
||||
const escapeHint = allowApiKeyFallback
|
||||
? "K to enter an API key instead, Esc to cancel"
|
||||
: "Esc to cancel";
|
||||
|
||||
if (mode === "device") {
|
||||
return (
|
||||
<box flexDirection="column" paddingX={1} gap={1}>
|
||||
@@ -919,7 +893,7 @@ export function OAuthLoginContent(
|
||||
{deviceError && <text fg="red">{deviceError}</text>}
|
||||
|
||||
<text fg="gray">
|
||||
<em>{escapeHint}</em>
|
||||
<em>Esc to cancel</em>
|
||||
</text>
|
||||
</box>
|
||||
);
|
||||
@@ -942,82 +916,7 @@ export function OAuthLoginContent(
|
||||
{error && <text fg="red">{error}</text>}
|
||||
|
||||
<text fg="gray">
|
||||
<em>{escapeHint}</em>
|
||||
</text>
|
||||
</box>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Manual API key entry for OAuth-capable providers — the escape hatch for
|
||||
* when OAuth login isn't working. Saving clears any stored OAuth tokens so
|
||||
* the manual key takes effect (see saveManualProviderApiKey).
|
||||
*/
|
||||
export function OAuthApiKeyInputContent(
|
||||
props: ChoiceContext<boolean> & {
|
||||
providerId: string;
|
||||
providerName: string;
|
||||
providerSettingsManager: ProviderSettingsManager;
|
||||
},
|
||||
) {
|
||||
const {
|
||||
resolve,
|
||||
dismiss,
|
||||
dialogId,
|
||||
providerId,
|
||||
providerName,
|
||||
providerSettingsManager,
|
||||
} = props;
|
||||
const [value, setValue] = useState("");
|
||||
|
||||
const submit = () => {
|
||||
const apiKey = value.trim();
|
||||
if (!apiKey) return;
|
||||
saveManualProviderApiKey(providerSettingsManager, providerId, apiKey);
|
||||
resolve(true);
|
||||
};
|
||||
|
||||
useDialogKeyboard((key) => {
|
||||
if (key.name === "escape") {
|
||||
dismiss();
|
||||
return;
|
||||
}
|
||||
if (key.name === "return") {
|
||||
submit();
|
||||
}
|
||||
}, dialogId);
|
||||
|
||||
return (
|
||||
<box flexDirection="column" paddingX={1} gap={1}>
|
||||
<text fg={palette.act}>
|
||||
<strong>{providerName}</strong>
|
||||
</text>
|
||||
|
||||
<text fg="gray">
|
||||
Use an API key from your Cline dashboard instead of OAuth login. This
|
||||
replaces any saved login tokens.
|
||||
</text>
|
||||
|
||||
<box flexDirection="column">
|
||||
<text fg="gray">API key</text>
|
||||
<box
|
||||
border
|
||||
borderStyle="rounded"
|
||||
borderColor={palette.act}
|
||||
paddingX={1}
|
||||
>
|
||||
<input
|
||||
value={value}
|
||||
onInput={setValue}
|
||||
placeholder="Paste your API key"
|
||||
flexGrow={1}
|
||||
focused
|
||||
/>
|
||||
</box>
|
||||
</box>
|
||||
|
||||
<text fg="gray">
|
||||
<em>Enter to save, Esc to go back</em>
|
||||
<em>Esc to cancel</em>
|
||||
</text>
|
||||
</box>
|
||||
);
|
||||
|
||||
@@ -7,10 +7,7 @@ import {
|
||||
type AccountDialogAction,
|
||||
AccountDialogContent,
|
||||
} from "../components/dialogs/account-dialog";
|
||||
import {
|
||||
OAuthLoginContent,
|
||||
type OAuthLoginResult,
|
||||
} from "../components/dialogs/provider-picker";
|
||||
import { OAuthLoginContent } from "../components/dialogs/provider-picker";
|
||||
import type { OpenModelSelectorOptions } from "./use-model-selector";
|
||||
|
||||
export function useAccountDialog(opts: {
|
||||
@@ -63,14 +60,14 @@ export function useAccountDialog(opts: {
|
||||
return;
|
||||
}
|
||||
if (action === "login") {
|
||||
const saved = await dialog.choice<OAuthLoginResult>({
|
||||
const saved = await dialog.choice<boolean>({
|
||||
style: { maxHeight: termHeight - 2 },
|
||||
closeOnEscape: false,
|
||||
content: (ctx: ChoiceContext<OAuthLoginResult>) => (
|
||||
content: (ctx: ChoiceContext<boolean>) => (
|
||||
<OAuthLoginContent {...ctx} providerId="cline" providerName="Cline" />
|
||||
),
|
||||
});
|
||||
if (saved === true) {
|
||||
if (saved) {
|
||||
await onAccountChange?.();
|
||||
await openAccountDialog();
|
||||
return;
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
refreshProviderModelsFromSource,
|
||||
resolveProviderConfig,
|
||||
} from "@cline/core";
|
||||
import { isClineProvider } from "@cline/shared";
|
||||
import type { ChoiceContext } from "@opentui-ui/dialog";
|
||||
import type { DialogActions } from "@opentui-ui/dialog/react";
|
||||
import { useCallback } from "react";
|
||||
@@ -22,9 +21,7 @@ import {
|
||||
ClinePassSubscriptionContent,
|
||||
CodexCliStatusContent,
|
||||
type ExistingProviderOption,
|
||||
OAuthApiKeyInputContent,
|
||||
OAuthLoginContent,
|
||||
type OAuthLoginResult,
|
||||
ProviderConfigInputContent,
|
||||
ProviderPickerContent,
|
||||
UseExistingOrReconfigureContent,
|
||||
@@ -134,23 +131,6 @@ async function runProviderChange(
|
||||
);
|
||||
const existingSettings = manager.getProviderSettings(newProviderId);
|
||||
|
||||
// Manual API key entry is the escape hatch for when OAuth login isn't
|
||||
// working; only the Cline providers accept a dashboard API key.
|
||||
const supportsManualApiKey = isClineProvider(newProviderId);
|
||||
const openManualApiKeyDialog = async (): Promise<boolean | undefined> =>
|
||||
await dialog.choice<boolean>({
|
||||
style: { maxHeight: termHeight - 2 },
|
||||
closeOnEscape: false,
|
||||
content: (ctx: ChoiceContext<boolean>) => (
|
||||
<OAuthApiKeyInputContent
|
||||
{...ctx}
|
||||
providerId={newProviderId}
|
||||
providerName={displayName}
|
||||
providerSettingsManager={manager}
|
||||
/>
|
||||
),
|
||||
});
|
||||
|
||||
let needsAuth = true;
|
||||
if (isProviderConfigured(newProviderId, existingSettings)) {
|
||||
let option: ExistingProviderOption | undefined;
|
||||
@@ -185,22 +165,17 @@ async function runProviderChange(
|
||||
if (needsAuth) {
|
||||
let saved: boolean | undefined;
|
||||
if (isOAuthProvider(newProviderId)) {
|
||||
const loginResult = await dialog.choice<OAuthLoginResult>({
|
||||
saved = await dialog.choice<boolean>({
|
||||
style: { maxHeight: termHeight - 2 },
|
||||
closeOnEscape: false,
|
||||
content: (ctx: ChoiceContext<OAuthLoginResult>) => (
|
||||
content: (ctx: ChoiceContext<boolean>) => (
|
||||
<OAuthLoginContent
|
||||
{...ctx}
|
||||
providerId={newProviderId}
|
||||
providerName={displayName}
|
||||
allowApiKeyFallback={supportsManualApiKey}
|
||||
/>
|
||||
),
|
||||
});
|
||||
saved =
|
||||
loginResult === "use_api_key"
|
||||
? await openManualApiKeyDialog()
|
||||
: loginResult;
|
||||
} else if (isOpenAICodexCliProvider(newProviderId)) {
|
||||
saved = await dialog.choice<boolean>({
|
||||
style: { maxHeight: termHeight - 2 },
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
formatCliErrorMessage,
|
||||
getCliClinePassLimitMessage,
|
||||
getCliNotSubscribedMessage,
|
||||
getClineOrgIndividualInferenceSubscriptionMessage,
|
||||
getClinePassLimitDetailMessage,
|
||||
getCliSubscriptionUrl,
|
||||
isClineOrgIndividualInferenceSubscriptionErrorMessage,
|
||||
isClinePassLimitErrorMessage,
|
||||
isClinePassSubscriptionError,
|
||||
} from "./cline-pass-errors";
|
||||
|
||||
@@ -49,22 +46,4 @@ describe("cline-pass-errors", () => {
|
||||
).toBe(true);
|
||||
expect(formatCliErrorMessage(new Error(raw))).toBe(formatted);
|
||||
});
|
||||
|
||||
it("recognizes and formats ClinePass period limit errors with usage-billing guidance", () => {
|
||||
const raw =
|
||||
"Error: You have reached your 5-hour Clinepass limit. The limit resets in 5h, please try again later.";
|
||||
const detail =
|
||||
"You have reached your 5-hour Clinepass limit. The limit resets in 5h, please try again later.";
|
||||
|
||||
expect(isClinePassLimitErrorMessage(raw)).toBe(true);
|
||||
expect(isClinePassLimitErrorMessage(new Error(raw))).toBe(true);
|
||||
expect(getClinePassLimitDetailMessage(raw)).toBe(detail);
|
||||
expect(formatCliErrorMessage(new Error(raw))).toBe(
|
||||
getCliClinePassLimitMessage(raw),
|
||||
);
|
||||
expect(formatCliErrorMessage(new Error(raw))).toContain(
|
||||
"Switch to Cline usage-based billing",
|
||||
);
|
||||
expect(formatCliErrorMessage(new Error(raw))).toContain("--provider cline");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import {
|
||||
type ClineSubscriptionPlan,
|
||||
extractClinePassLimitMessage,
|
||||
getClineOrgIndividualInferenceSubscriptionMessage,
|
||||
isClineNotSubscribedError,
|
||||
isClineNotSubscribedMessage,
|
||||
isClineOrgIndividualInferenceSubscriptionError,
|
||||
isClineOrgIndividualInferenceSubscriptionMessage,
|
||||
isClinePassLimitError,
|
||||
isClinePassLimitMessage,
|
||||
} from "@cline/core";
|
||||
|
||||
import { getClineEnvironmentConfig } from "@cline/shared";
|
||||
@@ -27,18 +24,6 @@ export function getCliNotSubscribedMessage(): string {
|
||||
return `No access to ClinePass subscription models yet. Subscribe to ClinePass, the low cost open weights model coding plan: ${getCliSubscriptionUrl()}`;
|
||||
}
|
||||
|
||||
export function getCliClinePassLimitMessage(message: string): string {
|
||||
const detail = getClinePassLimitDetailMessage(message) ?? message.trim();
|
||||
const lines = [
|
||||
"ClinePass limit reached",
|
||||
detail,
|
||||
"Switch to Cline usage-based billing and retry with the Cline provider.",
|
||||
"Interactive CLI: open the model selector with /model, choose Cline, then retry.",
|
||||
"Headless CLI: rerun with --provider cline.",
|
||||
];
|
||||
return lines.filter((line) => line.trim().length > 0).join("\n");
|
||||
}
|
||||
|
||||
export function getIndividualPlanFeatures(
|
||||
plans: ClineSubscriptionPlan[],
|
||||
): string[] {
|
||||
@@ -93,27 +78,6 @@ export function isClineOrgIndividualInferenceSubscriptionErrorMessage(
|
||||
);
|
||||
}
|
||||
|
||||
export function getClinePassLimitDetailMessage(
|
||||
error: unknown,
|
||||
): string | undefined {
|
||||
return extractClinePassLimitMessage(
|
||||
error instanceof Error ? error.message : String(error),
|
||||
);
|
||||
}
|
||||
|
||||
export function isClinePassLimitErrorMessage(error: unknown): boolean {
|
||||
if (isClinePassLimitError(error)) {
|
||||
return true;
|
||||
}
|
||||
if (error instanceof Error) {
|
||||
return (
|
||||
error.name === "ClinePassLimitError" ||
|
||||
isClinePassLimitMessage(error.message)
|
||||
);
|
||||
}
|
||||
return typeof error === "string" && isClinePassLimitMessage(error);
|
||||
}
|
||||
|
||||
export function formatCliErrorMessage(error: unknown): string {
|
||||
if (isClinePassSubscriptionError(error)) {
|
||||
return getCliNotSubscribedMessage();
|
||||
@@ -121,11 +85,6 @@ export function formatCliErrorMessage(error: unknown): string {
|
||||
if (isClineOrgIndividualInferenceSubscriptionErrorMessage(error)) {
|
||||
return getClineOrgIndividualInferenceSubscriptionMessage();
|
||||
}
|
||||
if (isClinePassLimitErrorMessage(error)) {
|
||||
return getCliClinePassLimitMessage(
|
||||
error instanceof Error ? error.message : String(error),
|
||||
);
|
||||
}
|
||||
if (error instanceof Error) {
|
||||
return error.message;
|
||||
}
|
||||
|
||||
@@ -42,23 +42,14 @@ describe("resolveStatusNoticeLabel", () => {
|
||||
|
||||
describe("handleEvent text formatting", () => {
|
||||
let output = "";
|
||||
let errorOutput = "";
|
||||
|
||||
beforeEach(() => {
|
||||
output = "";
|
||||
errorOutput = "";
|
||||
setCurrentOutputMode("text");
|
||||
vi.spyOn(process.stdout, "write").mockImplementation((chunk: unknown) => {
|
||||
output += String(chunk);
|
||||
return true;
|
||||
});
|
||||
vi.spyOn(process.stderr, "write").mockImplementation((chunk: unknown) => {
|
||||
errorOutput += String(chunk);
|
||||
return true;
|
||||
});
|
||||
vi.spyOn(console, "error").mockImplementation((...args: unknown[]) => {
|
||||
errorOutput += `${args.map(String).join(" ")}\n`;
|
||||
});
|
||||
});
|
||||
|
||||
it("adds a ⎿ before text that follows a tool block", () => {
|
||||
@@ -205,23 +196,6 @@ describe("handleEvent text formatting", () => {
|
||||
expect(output).toContain("── aborted (2 iterations) ──");
|
||||
});
|
||||
|
||||
it("formats ClinePass limit agent errors before writing to stderr", () => {
|
||||
handleEvent(
|
||||
{
|
||||
type: "error",
|
||||
error: new Error(
|
||||
"Error: You have reached your 5-hour Clinepass limit. The limit resets in 5h, please try again later.",
|
||||
),
|
||||
recoverable: false,
|
||||
} as unknown as AgentEvent,
|
||||
{} as Config,
|
||||
);
|
||||
|
||||
expect(errorOutput).toContain("ClinePass limit reached");
|
||||
expect(errorOutput).toContain("Switch to Cline usage-based billing");
|
||||
expect(errorOutput).toContain("--provider cline");
|
||||
});
|
||||
|
||||
it("suppresses heartbeat-only team progress messages", () => {
|
||||
handleTeamEvent({
|
||||
type: "run_progress",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { AgentEvent, TeamEvent } from "@cline/core";
|
||||
import { formatCliErrorMessage } from "./cline-pass-errors";
|
||||
import { formatToolInput, formatToolOutput, truncate } from "./helpers";
|
||||
import {
|
||||
c,
|
||||
@@ -182,7 +181,7 @@ export function handleEvent(event: AgentEvent, config: Config): void {
|
||||
case "error":
|
||||
closeInlineStreamIfNeeded();
|
||||
if (!event.recoverable || config.verbose) {
|
||||
writeErr(formatCliErrorMessage(event.error));
|
||||
writeErr(event.error.message);
|
||||
}
|
||||
break;
|
||||
case "notice":
|
||||
|
||||
@@ -42,12 +42,11 @@ export function getPersistedProviderApiKey(
|
||||
* or endpoint config for the provider. Used by the picker to decide whether
|
||||
* to offer "Use existing configuration?" before opening the configure dialog.
|
||||
*
|
||||
* Treats OAuth providers as configured when an access token or a manually
|
||||
* saved API key is present (the /settings escape hatch for when OAuth isn't
|
||||
* working); for everything else, any persisted API key, base URL, or model id
|
||||
* counts. We don't enforce required fields here — the runtime no longer
|
||||
* pre-flights credentials, so a missing key only matters when the API call
|
||||
* actually runs and the provider's own auth error is surfaced.
|
||||
* Treats OAuth providers as configured when an access token is present; for
|
||||
* everything else, any persisted API key, base URL, or model id counts. We
|
||||
* don't enforce required fields here — the runtime no longer pre-flights
|
||||
* credentials, so a missing key only matters when the API call actually
|
||||
* runs and the provider's own auth error is surfaced.
|
||||
*/
|
||||
export function isProviderConfigured(
|
||||
providerId: string,
|
||||
@@ -55,8 +54,7 @@ export function isProviderConfigured(
|
||||
): boolean {
|
||||
if (!settings) return false;
|
||||
if (isOAuthProvider(providerId)) {
|
||||
// getPersistedProviderApiKey covers both auth.accessToken and apiKey.
|
||||
return Boolean(getPersistedProviderApiKey(providerId, settings));
|
||||
return Boolean(settings.auth?.accessToken?.trim());
|
||||
}
|
||||
if (getPersistedProviderApiKey(providerId, settings)) return true;
|
||||
if (settings.baseUrl?.trim()) return true;
|
||||
|
||||
@@ -20,13 +20,10 @@ import { cn } from "@/lib/utils";
|
||||
|
||||
type MarkdownCodeProps = ComponentProps<"code"> & {
|
||||
"data-block"?: boolean | string;
|
||||
// react-markdown/streamdown pass the hast `Element` here, whose
|
||||
// `properties` is a broad `Record`. Keep this assignable from that type
|
||||
// (rather than a narrow `{ metastring?: string }`) so the component stays
|
||||
// compatible with `Components` regardless of how strict the resolved
|
||||
// hast/streamdown types are; the metastring value is validated at read time.
|
||||
node?: {
|
||||
properties?: Record<string, unknown>;
|
||||
properties?: {
|
||||
metastring?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -70,8 +67,7 @@ const MarkdownCode = ({
|
||||
);
|
||||
}
|
||||
|
||||
const metaValue = node?.properties?.metastring;
|
||||
const meta = typeof metaValue === "string" ? metaValue : undefined;
|
||||
const meta = node?.properties?.metastring;
|
||||
const startLineMatch = meta?.match(START_LINE_PATTERN);
|
||||
const startLine = startLineMatch ? Number.parseInt(startLineMatch[1], 10) : 1;
|
||||
const showLineNumbers = meta ? !NO_LINE_NUMBERS_PATTERN.test(meta) : true;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { basename, join } from "node:path";
|
||||
import {
|
||||
buildConnectionUpdate,
|
||||
buildWorkspaceMetadata,
|
||||
type ClineCore,
|
||||
type CoreSessionConfig,
|
||||
@@ -180,43 +179,58 @@ function buildCoreSessionConfig(config: JsonRecord): JsonRecord {
|
||||
export function buildSessionConnectionUpdate(
|
||||
config: JsonRecord,
|
||||
): SessionConnectionUpdate {
|
||||
// Coerce the untrusted webview JSON (snake_case aliases, blank strings)
|
||||
// into typed fields; the thinking/reasoning transition rules live in the
|
||||
// shared @cline/core builder.
|
||||
const thinking =
|
||||
typeof config.thinking === "boolean" ? config.thinking : undefined;
|
||||
const reasoningEffort = readReasoningEffort(config.reasoningEffort);
|
||||
const thinkingBudgetTokens = readPositiveInteger(
|
||||
config.thinkingBudgetTokens ?? config.thinking_budget_tokens,
|
||||
);
|
||||
const updates: SessionConnectionUpdate = {};
|
||||
const providerId = String(config.provider ?? config.providerId ?? "").trim();
|
||||
if (providerId) {
|
||||
updates.providerId = providerId;
|
||||
}
|
||||
const modelId = String(config.model ?? config.modelId ?? "").trim();
|
||||
const rawApiKey =
|
||||
if (modelId) {
|
||||
updates.modelId = modelId;
|
||||
}
|
||||
const apiKey =
|
||||
typeof config.apiKey === "string"
|
||||
? config.apiKey.trim()
|
||||
: typeof config.api_key === "string"
|
||||
? config.api_key.trim()
|
||||
: undefined;
|
||||
const baseUrl =
|
||||
typeof config.baseUrl === "string" ? config.baseUrl.trim() : undefined;
|
||||
const reasoningEffort = readReasoningEffort(config.reasoningEffort);
|
||||
const thinkingBudgetTokens = readPositiveInteger(
|
||||
config.thinkingBudgetTokens ?? config.thinking_budget_tokens,
|
||||
);
|
||||
return buildConnectionUpdate({
|
||||
...(providerId ? { providerId } : {}),
|
||||
...(modelId ? { modelId } : {}),
|
||||
...(rawApiKey ? { apiKey: rawApiKey } : {}),
|
||||
...(baseUrl ? { baseUrl } : {}),
|
||||
...(config.headers && typeof config.headers === "object"
|
||||
? { headers: config.headers as Record<string, string> }
|
||||
: {}),
|
||||
...(config.providerConfig && typeof config.providerConfig === "object"
|
||||
? {
|
||||
providerConfig:
|
||||
config.providerConfig as SessionConnectionUpdate["providerConfig"],
|
||||
}
|
||||
: {}),
|
||||
...(typeof config.thinking === "boolean"
|
||||
? { thinking: config.thinking }
|
||||
: {}),
|
||||
...(reasoningEffort ? { reasoningEffort } : {}),
|
||||
...(thinkingBudgetTokens !== undefined ? { thinkingBudgetTokens } : {}),
|
||||
});
|
||||
if (apiKey) {
|
||||
updates.apiKey = apiKey;
|
||||
}
|
||||
if (typeof config.baseUrl === "string" && config.baseUrl.trim()) {
|
||||
updates.baseUrl = config.baseUrl.trim();
|
||||
}
|
||||
if (config.headers && typeof config.headers === "object") {
|
||||
updates.headers = config.headers as Record<string, string>;
|
||||
}
|
||||
if (config.providerConfig && typeof config.providerConfig === "object") {
|
||||
updates.providerConfig =
|
||||
config.providerConfig as SessionConnectionUpdate["providerConfig"];
|
||||
}
|
||||
if (thinking === false) {
|
||||
updates.thinking = false;
|
||||
updates.reasoningEffort = null;
|
||||
updates.thinkingBudgetTokens = null;
|
||||
return updates;
|
||||
}
|
||||
if (thinking === true) {
|
||||
updates.thinking = true;
|
||||
}
|
||||
if (reasoningEffort) {
|
||||
updates.thinking = true;
|
||||
updates.reasoningEffort = reasoningEffort;
|
||||
}
|
||||
if (thinkingBudgetTokens !== undefined) {
|
||||
updates.thinking = true;
|
||||
updates.thinkingBudgetTokens = thinkingBudgetTokens;
|
||||
}
|
||||
return updates;
|
||||
}
|
||||
|
||||
async function resolveSystemPrompt(config: JsonRecord): Promise<string> {
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
} from "./context";
|
||||
import { resolveWorkspaceRoot } from "./paths";
|
||||
import { startServer } from "./server";
|
||||
import { BunRuntime, SIDECAR_HOST, SIDECAR_MODE, SIDECAR_PORT } from "./types";
|
||||
import { BunRuntime, SIDECAR_MODE, SIDECAR_PORT } from "./types";
|
||||
|
||||
const SHUTDOWN_TIMEOUT_MS = 5_000;
|
||||
|
||||
@@ -59,10 +59,8 @@ async function main() {
|
||||
|
||||
const { port } = startServer(ctx, SIDECAR_PORT, shutdown);
|
||||
|
||||
// A wildcard bind isn't a dialable address; advertise loopback instead.
|
||||
const dialHost = SIDECAR_HOST === "0.0.0.0" ? "127.0.0.1" : SIDECAR_HOST;
|
||||
const endpoint = `http://${dialHost}:${port}`;
|
||||
const wsEndpoint = `ws://${dialHost}:${port}/transport`;
|
||||
const endpoint = `http://127.0.0.1:${port}`;
|
||||
const wsEndpoint = `ws://127.0.0.1:${port}/transport`;
|
||||
process.stdout.write(
|
||||
`${JSON.stringify({
|
||||
type: "ready",
|
||||
|
||||
@@ -4,7 +4,6 @@ import { sendEvent } from "./context";
|
||||
import { fetchMarketplaceCatalog } from "./marketplace";
|
||||
import {
|
||||
BunRuntime,
|
||||
SIDECAR_HOST,
|
||||
SIDECAR_MODE,
|
||||
SIDECAR_PORT,
|
||||
type SidecarContext,
|
||||
@@ -16,20 +15,12 @@ type SidecarServer = {
|
||||
upgrade(req: Request): boolean;
|
||||
};
|
||||
|
||||
// Comma-separated extra origins (e.g. a dev server on a nonstandard port when
|
||||
// the sidecar runs inside a container). Origin validation itself stays on.
|
||||
const EXTRA_TRUSTED_ORIGINS = (process.env.CLINE_SIDECAR_TRUSTED_ORIGINS ?? "")
|
||||
.split(",")
|
||||
.map((origin) => origin.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
const TRUSTED_BROWSER_ORIGINS = new Set([
|
||||
"tauri://localhost",
|
||||
"http://tauri.localhost",
|
||||
"https://tauri.localhost",
|
||||
"http://localhost:3125",
|
||||
"http://127.0.0.1:3125",
|
||||
...EXTRA_TRUSTED_ORIGINS,
|
||||
]);
|
||||
|
||||
const JSON_HEADERS = {
|
||||
@@ -124,7 +115,7 @@ export function startServer(
|
||||
for (const candidate of candidates) {
|
||||
try {
|
||||
server = BunRuntime.serve({
|
||||
hostname: SIDECAR_HOST,
|
||||
hostname: "127.0.0.1",
|
||||
port: candidate,
|
||||
fetch: createFetchHandler(ctx, onShutdown),
|
||||
websocket: createWebSocketHandler(ctx),
|
||||
|
||||
@@ -115,8 +115,4 @@ export type BunRuntimeApi = {
|
||||
export const BunRuntime = (globalThis as { Bun?: BunRuntimeApi }).Bun;
|
||||
|
||||
export const SIDECAR_PORT = Number(process.env.CLINE_SIDECAR_PORT) || 3126;
|
||||
// Loopback-only by default. Set CLINE_SIDECAR_HOST=0.0.0.0 to accept
|
||||
// connections from outside the local host (e.g. Docker port publishing).
|
||||
export const SIDECAR_HOST =
|
||||
process.env.CLINE_SIDECAR_HOST?.trim() || "127.0.0.1";
|
||||
export const SIDECAR_MODE = "sidecar";
|
||||
|
||||
@@ -35,9 +35,8 @@ let resolvedEndpointCache: string | null = null;
|
||||
* or an integration test harness.
|
||||
* 2. Tauri `get_desktop_backend_endpoint` command — used when running inside
|
||||
* the full Tauri app shell.
|
||||
* 3. `NEXT_PUBLIC_SIDECAR_WS_ENDPOINT` (inlined at build time), then fallback
|
||||
* to `ws://127.0.0.1:3126/transport` — the sidecar's default port when
|
||||
* running in plain web/dev mode (`bun run dev:sidecar` + `bun run dev:web`).
|
||||
* 3. Fallback to `ws://127.0.0.1:3126/transport` — the sidecar's default port
|
||||
* when running in plain web/dev mode (`bun run dev:sidecar` + `bun run dev:web`).
|
||||
*/
|
||||
export async function resolveDesktopBackendWsEndpoint(): Promise<string> {
|
||||
if (resolvedEndpointCache) return resolvedEndpointCache;
|
||||
@@ -65,10 +64,8 @@ export async function resolveDesktopBackendWsEndpoint(): Promise<string> {
|
||||
throw new Error("Tauri returned an empty desktop backend endpoint");
|
||||
}
|
||||
|
||||
// 3. Env override, then default sidecar port for local dev mode without
|
||||
// the Tauri bridge.
|
||||
const envEndpoint = process.env.NEXT_PUBLIC_SIDECAR_WS_ENDPOINT?.trim();
|
||||
resolvedEndpointCache = envEndpoint || "ws://127.0.0.1:3126/transport";
|
||||
// 3. Default sidecar port for local dev mode without the Tauri bridge.
|
||||
resolvedEndpointCache = "ws://127.0.0.1:3126/transport";
|
||||
return resolvedEndpointCache;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/types/routes.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
@@ -11,10 +11,6 @@ const nextConfig = {
|
||||
turbopack: {
|
||||
root: workspaceRoot,
|
||||
},
|
||||
// Dev-only: Next blocks HMR/font/dev-resource requests from origins that
|
||||
// don't match the dev server's own hostname. Both loopback spellings are
|
||||
// legitimate ways to reach a local or port-forwarded dev server.
|
||||
allowedDevOrigins: ["localhost", "127.0.0.1"],
|
||||
reactStrictMode: true,
|
||||
typescript: {
|
||||
ignoreBuildErrors: true,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { WebviewProvider } from "./core/webview"
|
||||
import "./utils/path" // necessary to have access to String.prototype.toPosix
|
||||
|
||||
import { setSdkLogger } from "@cline/core"
|
||||
import { HostProvider } from "@/hosts/host-provider"
|
||||
import { Logger } from "@/shared/services/Logger"
|
||||
import type { StorageContext } from "@/shared/storage/storage-context"
|
||||
@@ -36,17 +35,6 @@ export async function initialize(storageContext: StorageContext): Promise<Webvie
|
||||
Logger.subscribe((msg: string) => HostProvider.get().logToChannel(msg)) // File system logging
|
||||
Logger.subscribe((msg: string) => HostProvider.env.debugLog({ value: msg })) // Host debug logging
|
||||
|
||||
// Register the SDK early logger so diagnostic events from
|
||||
// ProviderSettingsManager, RuntimeOAuthTokenManager, and Cline auth
|
||||
// flow through Logger.debug → Cline output channel.
|
||||
// These components operate before/outside of ClineCore sessions, so the
|
||||
// session-scoped logger can't reach them.
|
||||
setSdkLogger({
|
||||
debug: (message) => Logger.debug(message),
|
||||
log: (message) => Logger.log(message),
|
||||
error: (message) => Logger.error(message),
|
||||
})
|
||||
|
||||
// Initialize ClineEndpoint configuration (reads bundled and ~/.cline/endpoints.json if present)
|
||||
// This must be done before any other code that calls ClineEnv.config()
|
||||
// Throws ClineConfigurationError if config file exists but is invalid
|
||||
|
||||
@@ -258,7 +258,7 @@ export class Controller {
|
||||
)
|
||||
|
||||
// Initialize SDK-backed auth and account services.
|
||||
this.authService = AuthService.getInstance(this, this.sdkTelemetry.telemetry)
|
||||
this.authService = AuthService.getInstance(this)
|
||||
this.ocaAuthService = OcaAuthService.initialize(this)
|
||||
this.accountService = ClineAccountService.getInstance()
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
// - Streaming subscription management
|
||||
// - workos: prefix handling
|
||||
|
||||
import { getValidClineCredentials, type ITelemetryService, type OAuthCredentials } from "@cline/core"
|
||||
import { getValidClineCredentials, type OAuthCredentials } from "@cline/core"
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest"
|
||||
import { AuthService, type ClineAuthInfo, LogoutReason } from "./auth-service"
|
||||
|
||||
@@ -18,8 +18,6 @@ import { AuthService, type ClineAuthInfo, LogoutReason } from "./auth-service"
|
||||
|
||||
const mockFeatureFlagsPoll = vi.hoisted(() => vi.fn().mockResolvedValue(undefined))
|
||||
const mockIdentifyAccount = vi.hoisted(() => vi.fn().mockResolvedValue(undefined))
|
||||
const mockCaptureAuthLoggedOut = vi.hoisted(() => vi.fn())
|
||||
const mockSdkTelemetry = { capture: vi.fn() } as unknown as ITelemetryService
|
||||
|
||||
// Mock StateManager
|
||||
const mockSecrets = new Map<string, string>()
|
||||
@@ -92,7 +90,6 @@ vi.mock("@/services/feature-flags", () => ({
|
||||
vi.mock("@/services/telemetry", () => ({
|
||||
telemetryService: {
|
||||
identifyAccount: mockIdentifyAccount,
|
||||
captureAuthLoggedOut: mockCaptureAuthLoggedOut,
|
||||
},
|
||||
}))
|
||||
|
||||
@@ -106,9 +103,7 @@ vi.mock("axios", () => ({
|
||||
const mockLoginClineOAuth = vi.hoisted(() => vi.fn())
|
||||
|
||||
// Mock @cline/core OAuth functions
|
||||
vi.mock("@cline/core", async () => ({
|
||||
sdkDebug: () => {},
|
||||
hashSecret: () => "hashed",
|
||||
vi.mock("@cline/core", () => ({
|
||||
createOAuthClientCallbacks: (opts: {
|
||||
onOutput?: (message: string) => void
|
||||
onPrompt: () => void
|
||||
@@ -213,7 +208,7 @@ describe("AuthService", () => {
|
||||
beforeEach(() => {
|
||||
// Reset the singleton between tests
|
||||
resetSingleton()
|
||||
authService = AuthService.getInstance(undefined, mockSdkTelemetry)
|
||||
authService = AuthService.getInstance()
|
||||
mockSecrets.clear()
|
||||
mockProviderSettings.clear()
|
||||
vi.clearAllMocks()
|
||||
@@ -383,7 +378,6 @@ describe("AuthService", () => {
|
||||
|
||||
// Persisted credentials should be cleared from providers.json.
|
||||
expect(mockProviderSettings.get("cline")?.auth).toBeUndefined()
|
||||
expect(mockCaptureAuthLoggedOut).toHaveBeenCalledWith("cline", LogoutReason.USER_INITIATED)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -413,11 +407,6 @@ describe("AuthService", () => {
|
||||
|
||||
expect(testAccess(authService)._authenticated).toBe(true)
|
||||
expect(testAccess(authService)._clineAuthInfo?.idToken).toBe("persisted-access-token")
|
||||
expect(getValidClineCredentials).toHaveBeenCalledWith(
|
||||
expect.any(Object),
|
||||
expect.objectContaining({ telemetry: mockSdkTelemetry }),
|
||||
expect.any(Object),
|
||||
)
|
||||
})
|
||||
|
||||
it("sets unauthenticated state when providers.json has no Cline auth", async () => {
|
||||
|
||||
@@ -8,15 +8,13 @@
|
||||
// disk — it's fetched from the Cline API on startup and cached in memory.
|
||||
// This matches the CLI's pattern (see apps/cli/src/runtime/interactive-welcome.ts).
|
||||
|
||||
import type { ITelemetryService, OAuthCredentials } from "@cline/core"
|
||||
import type { OAuthCredentials } from "@cline/core"
|
||||
import {
|
||||
createOAuthClientCallbacks,
|
||||
getValidClineCredentials,
|
||||
hashSecret,
|
||||
loginClineOAuth,
|
||||
loginOcaOAuth,
|
||||
loginOpenAICodex,
|
||||
sdkDebug,
|
||||
} from "@cline/core"
|
||||
import type { ApiProvider } from "@shared/api"
|
||||
import { AuthState, UserInfo } from "@shared/proto/cline/account"
|
||||
@@ -101,10 +99,7 @@ function readClineCredentials(): {
|
||||
try {
|
||||
const manager = getProviderSettingsManager()
|
||||
const settings = manager.getProviderSettings("cline")
|
||||
if (!settings?.auth?.accessToken) {
|
||||
sdkDebug("[SdkAuthService] readClineCredentials: no auth.accessToken found")
|
||||
return null
|
||||
}
|
||||
if (!settings?.auth?.accessToken) return null
|
||||
|
||||
// Strip workos: prefix if present (providers.json stores it with prefix)
|
||||
let accessToken = settings.auth.accessToken
|
||||
@@ -112,16 +107,12 @@ function readClineCredentials(): {
|
||||
accessToken = accessToken.slice(WORKOS_TOKEN_PREFIX.length)
|
||||
}
|
||||
|
||||
const result = {
|
||||
return {
|
||||
accessToken,
|
||||
refreshToken: settings.auth.refreshToken,
|
||||
expiresAt: (settings.auth as { expiresAt?: number }).expiresAt,
|
||||
accountId: settings.auth.accountId,
|
||||
}
|
||||
sdkDebug(
|
||||
`[SdkAuthService] readClineCredentials: found credentials (accessHash=${hashSecret(result.accessToken)}, refreshHash=${hashSecret(result.refreshToken)}, expiresAt=${result.expiresAt})`,
|
||||
)
|
||||
return result
|
||||
} catch (error) {
|
||||
Logger.error("[SdkAuthService] Failed to read credentials from providers.json:", error)
|
||||
return null
|
||||
@@ -159,9 +150,6 @@ function writeClineCredentials(credentials: {
|
||||
},
|
||||
{ tokenSource: "oauth", setLastUsed: true },
|
||||
)
|
||||
sdkDebug(
|
||||
`[SdkAuthService] writeClineCredentials: wrote (accessHash=${hashSecret(credentials.accessToken)}, refreshHash=${hashSecret(credentials.refreshToken)}, expiresAt=${credentials.expiresAt})`,
|
||||
)
|
||||
} catch (error) {
|
||||
Logger.error("[SdkAuthService] Failed to write credentials to providers.json:", error)
|
||||
}
|
||||
@@ -175,7 +163,6 @@ function clearClineCredentials(): void {
|
||||
const manager = getProviderSettingsManager()
|
||||
const existing = manager.getProviderSettings("cline")
|
||||
if (existing) {
|
||||
sdkDebug("[SdkAuthService] clearClineCredentials: clearing auth from providers.json")
|
||||
manager.saveProviderSettings(
|
||||
{
|
||||
...existing,
|
||||
@@ -202,7 +189,6 @@ export class AuthService {
|
||||
private _activeAuthStatusUpdateHandlers = new Set<StreamingResponseHandler<AuthState>>()
|
||||
private _handlerToController = new Map<StreamingResponseHandler<AuthState>, Controller>()
|
||||
private _refreshPromise: Promise<string | undefined> | null = null
|
||||
private _telemetry?: ITelemetryService
|
||||
|
||||
private constructor() {}
|
||||
|
||||
@@ -210,13 +196,10 @@ export class AuthService {
|
||||
* Gets the singleton instance of AuthService.
|
||||
* On first call with a controller, initializes BannerService.
|
||||
*/
|
||||
public static getInstance(controller?: Controller, telemetry?: ITelemetryService): AuthService {
|
||||
public static getInstance(controller?: Controller): AuthService {
|
||||
if (!AuthService.instance) {
|
||||
AuthService.instance = new AuthService()
|
||||
}
|
||||
if (telemetry) {
|
||||
AuthService.instance._telemetry = telemetry
|
||||
}
|
||||
// Initialize BannerService on first call with a controller
|
||||
// (mirrors classic AuthService behavior)
|
||||
if (controller) {
|
||||
@@ -268,9 +251,6 @@ export class AuthService {
|
||||
const bearerToken = accessToken.toLowerCase().startsWith(WORKOS_TOKEN_PREFIX)
|
||||
? accessToken
|
||||
: `${WORKOS_TOKEN_PREFIX}${accessToken}`
|
||||
sdkDebug(
|
||||
`[SdkAuthService] fetchUserInfoFromApi: GET ${apiBaseUrl}/api/v1/users/me (tokenHash=${hashSecret(accessToken)})`,
|
||||
)
|
||||
const response = await axios.get(`${apiBaseUrl}/api/v1/users/me`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${bearerToken}`,
|
||||
@@ -279,7 +259,6 @@ export class AuthService {
|
||||
},
|
||||
...getAxiosSettings(),
|
||||
})
|
||||
sdkDebug(`[SdkAuthService] fetchUserInfoFromApi: response status=${response.status}`)
|
||||
return response.data?.data ?? null
|
||||
} catch (error) {
|
||||
Logger.error("[SdkAuthService] Failed to fetch user info from API:", error)
|
||||
@@ -307,7 +286,7 @@ export class AuthService {
|
||||
|
||||
return getValidClineCredentials(
|
||||
this.toOAuthCredentials(authInfo),
|
||||
{ apiBaseUrl: ClineEnv.config().apiBaseUrl, telemetry: this._telemetry },
|
||||
{ apiBaseUrl: ClineEnv.config().apiBaseUrl },
|
||||
{ forceRefresh: options?.forceRefresh },
|
||||
)
|
||||
}
|
||||
@@ -356,11 +335,9 @@ export class AuthService {
|
||||
}
|
||||
|
||||
if (!this._clineAuthInfo?.refreshToken) {
|
||||
sdkDebug("[SdkAuthService] refreshAccessToken: no refresh token available")
|
||||
return false
|
||||
}
|
||||
|
||||
sdkDebug(`[SdkAuthService] refreshAccessToken: starting (currentTokenHash=${hashSecret(this._clineAuthInfo.idToken)})`)
|
||||
this._refreshPromise = (async () => {
|
||||
try {
|
||||
const currentInfo = this._clineAuthInfo
|
||||
@@ -369,7 +346,6 @@ export class AuthService {
|
||||
}
|
||||
const newCredentials = await this.resolveValidClineCredentials(currentInfo, { forceRefresh: true })
|
||||
if (!newCredentials) {
|
||||
sdkDebug("[SdkAuthService] refreshAccessToken: refresh returned null — clearing credentials")
|
||||
this._clineAuthInfo = null
|
||||
this._authenticated = false
|
||||
clearClineCredentials()
|
||||
@@ -399,9 +375,6 @@ export class AuthService {
|
||||
this._authenticated = true
|
||||
|
||||
if (credentialsChanged) {
|
||||
sdkDebug(
|
||||
`[SdkAuthService] refreshAccessToken: credentials changed (newTokenHash=${hashSecret(newCredentials.access)})`,
|
||||
)
|
||||
writeClineCredentials({
|
||||
accessToken: newCredentials.access,
|
||||
refreshToken: newCredentials.refresh,
|
||||
@@ -414,8 +387,6 @@ export class AuthService {
|
||||
Logger.error("[SdkAuthService] Error sending auth status update after refresh:", err)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
sdkDebug("[SdkAuthService] refreshAccessToken: credentials unchanged after refresh")
|
||||
}
|
||||
|
||||
return this._clineAuthInfo.idToken
|
||||
@@ -751,9 +722,8 @@ export class AuthService {
|
||||
/**
|
||||
* Handle deauthentication — clear tokens from providers.json and push unauthenticated state.
|
||||
*/
|
||||
async handleDeauth(reason: LogoutReason = LogoutReason.UNKNOWN): Promise<void> {
|
||||
async handleDeauth(_reason: LogoutReason = LogoutReason.UNKNOWN): Promise<void> {
|
||||
try {
|
||||
telemetryService.captureAuthLoggedOut("cline", reason)
|
||||
this._clineAuthInfo = null
|
||||
this._authenticated = false
|
||||
clearClineCredentials()
|
||||
|
||||
@@ -783,101 +783,6 @@ describe("translateSessionEvent — agent_event content_end", () => {
|
||||
expect(second.path).toBe("/src/package.json")
|
||||
})
|
||||
|
||||
it("content_end for read_files carries the requested line range into the readFile payload", () => {
|
||||
const state = new MessageTranslatorState()
|
||||
|
||||
translateSessionEvent(
|
||||
{
|
||||
type: "agent_event",
|
||||
payload: {
|
||||
sessionId: "s1",
|
||||
event: {
|
||||
type: "content_start",
|
||||
contentType: "tool",
|
||||
toolName: "read_files",
|
||||
toolCallId: "c1",
|
||||
input: { files: [{ path: "/src/big-file.ts", start_line: 100, end_line: 200 }] },
|
||||
} as AgentEvent,
|
||||
},
|
||||
},
|
||||
state,
|
||||
)
|
||||
|
||||
const endResult = translateSessionEvent(
|
||||
{
|
||||
type: "agent_event",
|
||||
payload: {
|
||||
sessionId: "s1",
|
||||
event: {
|
||||
type: "content_end",
|
||||
contentType: "tool",
|
||||
toolName: "read_files",
|
||||
toolCallId: "c1",
|
||||
} as AgentEvent,
|
||||
},
|
||||
},
|
||||
state,
|
||||
)
|
||||
|
||||
const endTool = JSON.parse(endResult.messages[0].text!)
|
||||
expect(endTool.tool).toBe("readFile")
|
||||
expect(endTool.path).toBe("/src/big-file.ts")
|
||||
expect(endTool.readLineStart).toBe(100)
|
||||
expect(endTool.readLineEnd).toBe(200)
|
||||
})
|
||||
|
||||
it("content_end for read_files treats a start_line-only read as open-ended and defaults a missing start_line to 1", () => {
|
||||
const state = new MessageTranslatorState()
|
||||
|
||||
translateSessionEvent(
|
||||
{
|
||||
type: "agent_event",
|
||||
payload: {
|
||||
sessionId: "s1",
|
||||
event: {
|
||||
type: "content_start",
|
||||
contentType: "tool",
|
||||
toolName: "read_files",
|
||||
toolCallId: "c1",
|
||||
input: {
|
||||
files: [
|
||||
{ path: "/src/paged.ts", start_line: 500, end_line: null },
|
||||
{ path: "/src/head.ts", end_line: 50 },
|
||||
{ path: "/src/whole.ts" },
|
||||
],
|
||||
},
|
||||
} as AgentEvent,
|
||||
},
|
||||
},
|
||||
state,
|
||||
)
|
||||
|
||||
const endResult = translateSessionEvent(
|
||||
{
|
||||
type: "agent_event",
|
||||
payload: {
|
||||
sessionId: "s1",
|
||||
event: {
|
||||
type: "content_end",
|
||||
contentType: "tool",
|
||||
toolName: "read_files",
|
||||
toolCallId: "c1",
|
||||
} as AgentEvent,
|
||||
},
|
||||
},
|
||||
state,
|
||||
)
|
||||
|
||||
expect(endResult.messages).toHaveLength(3)
|
||||
const [paged, head, whole] = endResult.messages.map((m) => JSON.parse(m.text!))
|
||||
expect(paged.readLineStart).toBe(500)
|
||||
expect(paged.readLineEnd).toBeUndefined()
|
||||
expect(head.readLineStart).toBe(1)
|
||||
expect(head.readLineEnd).toBe(50)
|
||||
expect(whole.readLineStart).toBeUndefined()
|
||||
expect(whole.readLineEnd).toBeUndefined()
|
||||
})
|
||||
|
||||
it("content_end without prior content_start still works (graceful fallback)", () => {
|
||||
const state = new MessageTranslatorState()
|
||||
|
||||
|
||||
@@ -452,11 +452,10 @@ function sdkToolToClineSayTool(toolName: string, input?: unknown): ClineSayTool
|
||||
switch (toolName) {
|
||||
case "read_files":
|
||||
case "read_file": {
|
||||
const fileRead = extractFileReads(parsedInput)[0]
|
||||
const filePath = extractFirstFilePath(parsedInput)
|
||||
return {
|
||||
tool: "readFile",
|
||||
path: fileRead?.path ?? "",
|
||||
...readLineRangeFields(fileRead),
|
||||
path: filePath,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -666,53 +665,32 @@ function getCompletionResultText(input: unknown): string {
|
||||
return getStringField(parsed, "summary") ?? getStringField(parsed, "result") ?? ""
|
||||
}
|
||||
|
||||
/** A single file read request parsed from a read_files/read_file input */
|
||||
interface FileReadRequest {
|
||||
path: string
|
||||
startLine?: number
|
||||
endLine?: number
|
||||
}
|
||||
|
||||
/** Extract file read requests (path + optional one-based inclusive line range) from a read_files/read_file input */
|
||||
function extractFileReads(input: Record<string, unknown> | undefined): FileReadRequest[] {
|
||||
/** Extract file paths from a read_files/read_file input */
|
||||
function extractFilePaths(input: Record<string, unknown> | undefined): string[] {
|
||||
if (!input) return []
|
||||
const files = input.files
|
||||
if (Array.isArray(files) && files.length > 0) {
|
||||
const reads = files
|
||||
.map((f): FileReadRequest => {
|
||||
if (typeof f === "string") return { path: f }
|
||||
const paths = files
|
||||
.map((f) => {
|
||||
if (typeof f === "string") return f
|
||||
if (typeof f === "object" && f !== null) {
|
||||
const entry = f as Record<string, unknown>
|
||||
return {
|
||||
path: (entry.path as string) ?? "",
|
||||
startLine: getNumberField(entry, "start_line"),
|
||||
endLine: getNumberField(entry, "end_line"),
|
||||
}
|
||||
return ((f as Record<string, unknown>).path as string) ?? ""
|
||||
}
|
||||
return { path: "" }
|
||||
return ""
|
||||
})
|
||||
.filter((read) => read.path)
|
||||
if (reads.length > 0) {
|
||||
return reads
|
||||
.filter(Boolean)
|
||||
if (paths.length > 0) {
|
||||
return paths
|
||||
}
|
||||
}
|
||||
const singlePath =
|
||||
(input.path as string) ?? (input.file_path as string) ?? (input.filePath as string) ?? (input.filename as string) ?? ""
|
||||
return singlePath
|
||||
? [{ path: singlePath, startLine: getNumberField(input, "start_line"), endLine: getNumberField(input, "end_line") }]
|
||||
: []
|
||||
return singlePath ? [singlePath] : []
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a read request's line range onto ClineSayTool fields. An omitted start_line with an
|
||||
* explicit end_line means the read began at line 1; an omitted end_line stays undefined
|
||||
* (open-ended read — the UI renders it as "start+").
|
||||
*/
|
||||
function readLineRangeFields(read: FileReadRequest | undefined): Pick<ClineSayTool, "readLineStart" | "readLineEnd"> {
|
||||
if (!read || (read.startLine == null && read.endLine == null)) {
|
||||
return {}
|
||||
}
|
||||
return { readLineStart: read.startLine ?? 1, readLineEnd: read.endLine }
|
||||
/** Extract the first file path from a read_files input */
|
||||
function extractFirstFilePath(input: Record<string, unknown> | undefined): string {
|
||||
return extractFilePaths(input)[0] ?? ""
|
||||
}
|
||||
|
||||
/** Get a string field from a parsed input object */
|
||||
@@ -723,14 +701,6 @@ function getStringField(input: Record<string, unknown> | undefined, field: strin
|
||||
return undefined
|
||||
}
|
||||
|
||||
/** Get a finite number field from a parsed input object (null/non-number → undefined) */
|
||||
function getNumberField(input: Record<string, unknown> | undefined, field: string): number | undefined {
|
||||
if (!input) return undefined
|
||||
const value = input[field]
|
||||
if (typeof value === "number" && Number.isFinite(value)) return value
|
||||
return undefined
|
||||
}
|
||||
|
||||
/** Get an array field from a parsed input object */
|
||||
function getArrayField(input: Record<string, unknown> | undefined, field: string): string[] | undefined {
|
||||
if (!input) return undefined
|
||||
@@ -1325,17 +1295,16 @@ function translateAgentEvent(event: AgentEvent, state: MessageTranslatorState):
|
||||
// list reflect what was actually read.
|
||||
if (toolName === "read_files" || toolName === "read_file") {
|
||||
const parsedInput = parseToolInput(storedInput)
|
||||
const fileReads = extractFileReads(parsedInput)
|
||||
if (fileReads.length > 1) {
|
||||
fileReads.forEach((fileRead, index) => {
|
||||
const filePaths = extractFilePaths(parsedInput)
|
||||
if (filePaths.length > 1) {
|
||||
filePaths.forEach((filePath, index) => {
|
||||
messages.push({
|
||||
ts: index === 0 ? ts : state.nextTs(),
|
||||
type: "say",
|
||||
say: "tool",
|
||||
text: JSON.stringify({
|
||||
tool: "readFile",
|
||||
path: fileRead.path,
|
||||
...readLineRangeFields(fileRead),
|
||||
path: filePath,
|
||||
} satisfies ClineSayTool),
|
||||
partial: false,
|
||||
})
|
||||
|
||||
@@ -275,7 +275,7 @@ export interface ClineSayTool {
|
||||
operationIsLocatedInWorkspace?: boolean
|
||||
/** Starting line numbers in the original file where each SEARCH block matched */
|
||||
startLineNumbers?: number[]
|
||||
/** One-based inclusive line range requested by read_file; readLineEnd omitted = open-ended read (for UI summaries). */
|
||||
/** Inclusive line range actually returned by read_file (for UI summaries). */
|
||||
readLineStart?: number
|
||||
readLineEnd?: number
|
||||
}
|
||||
|
||||
@@ -54,8 +54,7 @@ export class Logger {
|
||||
fullMessage += ` ${args.map((arg) => JSON.stringify(arg)).join(" ")}`
|
||||
}
|
||||
const errorSuffix = error?.message ? ` ${error.message}` : ""
|
||||
const ts = new Date().toISOString()
|
||||
Logger.output(`${ts} ${level} ${fullMessage}${errorSuffix}`.trimEnd())
|
||||
Logger.output(`${level} ${fullMessage}${errorSuffix}`.trimEnd())
|
||||
} catch {
|
||||
// do nothing if Logger fails
|
||||
}
|
||||
|
||||
@@ -507,11 +507,10 @@ export const ChatRowContent = memo(
|
||||
{tool.path && !tool.path.startsWith(".") && <span>/</span>}
|
||||
<span className="ph-no-capture whitespace-nowrap overflow-hidden text-ellipsis mr-2 text-left [direction: rtl]">
|
||||
{cleanPathPrefix(tool.path ?? "") + "\u200E"}
|
||||
{tool.readLineStart != null ? (
|
||||
{tool.readLineStart != null && tool.readLineEnd != null ? (
|
||||
<span className="opacity-80">
|
||||
{" "}
|
||||
({tool.readLineStart}
|
||||
{tool.readLineEnd != null ? `-${tool.readLineEnd}` : "+"})
|
||||
({tool.readLineStart}-{tool.readLineEnd})
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
|
||||
+2
-6
@@ -46,9 +46,7 @@ const getActivityText = (tool: ClineSayTool): string | null => {
|
||||
return null
|
||||
}
|
||||
const lineHint =
|
||||
tool.readLineStart != null
|
||||
? ` (lines ${tool.readLineStart}${tool.readLineEnd != null ? `-${tool.readLineEnd}` : "+"})`
|
||||
: ""
|
||||
tool.readLineStart != null && tool.readLineEnd != null ? ` (lines ${tool.readLineStart}-${tool.readLineEnd})` : ""
|
||||
return `Reading ${cleanedPath}${lineHint}...`
|
||||
}
|
||||
case "listFilesTopLevel":
|
||||
@@ -304,9 +302,7 @@ function getToolDisplayInfo(tool: ClineSayTool) {
|
||||
switch (tool.tool) {
|
||||
case "readFile": {
|
||||
const lineNote =
|
||||
tool.readLineStart != null
|
||||
? `lines ${tool.readLineStart}${tool.readLineEnd != null ? `-${tool.readLineEnd}` : "+"}`
|
||||
: null
|
||||
tool.readLineStart != null && tool.readLineEnd != null ? `lines ${tool.readLineStart}-${tool.readLineEnd}` : null
|
||||
return {
|
||||
icon,
|
||||
path: filePath,
|
||||
|
||||
@@ -281,13 +281,13 @@ const UserTypeSelectionStep = ({ userType, onSelectUserType, userTypeSelections
|
||||
{" "}
|
||||
<VSCodeLink
|
||||
className="inline"
|
||||
style={{ fontSize: "inherit" }}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
UiServiceClient.openUrl(
|
||||
StringRequest.create({ value: option.learnMoreUrl }),
|
||||
).catch((err) => console.error("Failed to open learn more link:", err))
|
||||
}}
|
||||
style={{ fontSize: "inherit" }}>
|
||||
}}>
|
||||
Learn more
|
||||
</VSCodeLink>
|
||||
</>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"apps/cli": {
|
||||
"name": "@cline/cli",
|
||||
"version": "3.0.39",
|
||||
"version": "3.0.38",
|
||||
"bin": {
|
||||
"cline": "src/index.ts",
|
||||
},
|
||||
@@ -615,9 +615,19 @@
|
||||
"@cline/core",
|
||||
],
|
||||
},
|
||||
"sdk/examples/plugins/xml-tool-calling": {
|
||||
"name": "cline-xml-tool-calling-plugin",
|
||||
"version": "0.1.0",
|
||||
"peerDependencies": {
|
||||
"@cline/core": "*",
|
||||
},
|
||||
"optionalPeers": [
|
||||
"@cline/core",
|
||||
],
|
||||
},
|
||||
"sdk/packages/agents": {
|
||||
"name": "@cline/agents",
|
||||
"version": "0.0.60",
|
||||
"version": "0.0.59",
|
||||
"dependencies": {
|
||||
"@cline/llms": "workspace:*",
|
||||
"@cline/shared": "workspace:*",
|
||||
@@ -626,7 +636,7 @@
|
||||
},
|
||||
"sdk/packages/core": {
|
||||
"name": "@cline/core",
|
||||
"version": "0.0.60",
|
||||
"version": "0.0.59",
|
||||
"dependencies": {
|
||||
"@cline/agents": "workspace:*",
|
||||
"@cline/llms": "workspace:*",
|
||||
@@ -664,7 +674,7 @@
|
||||
},
|
||||
"sdk/packages/llms": {
|
||||
"name": "@cline/llms",
|
||||
"version": "0.0.60",
|
||||
"version": "0.0.59",
|
||||
"dependencies": {
|
||||
"@ai-sdk/amazon-bedrock": "^4.0.89",
|
||||
"@ai-sdk/anthropic": "^3.0.68",
|
||||
@@ -698,14 +708,14 @@
|
||||
},
|
||||
"sdk/packages/sdk": {
|
||||
"name": "@cline/sdk",
|
||||
"version": "0.0.60",
|
||||
"version": "0.0.59",
|
||||
"dependencies": {
|
||||
"@cline/core": "workspace:*",
|
||||
},
|
||||
},
|
||||
"sdk/packages/shared": {
|
||||
"name": "@cline/shared",
|
||||
"version": "0.0.60",
|
||||
"version": "0.0.59",
|
||||
"dependencies": {
|
||||
"aws4fetch": "^1.0.20",
|
||||
"jsonrepair": "^3.13.2",
|
||||
@@ -742,15 +752,15 @@
|
||||
|
||||
"@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.129", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.93", "@ai-sdk/openai": "3.0.80", "@ai-sdk/provider": "3.0.13", "@ai-sdk/provider-utils": "4.0.35", "@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-z+JAe15rDrsK5tdguGJFFV/CGAlBBTHcjA1FCgdH34F5+Qo9raMRc1fyTeVICoWJ2/Lf9g+A3yuGf6S8UVBdzg=="],
|
||||
"@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@4.0.128", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.92", "@ai-sdk/openai": "3.0.80", "@ai-sdk/provider": "3.0.13", "@ai-sdk/provider-utils": "4.0.35", "@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-T6mvUEYjTCkRAYByOwCwHT12J8r2U9fxJqfEf6k04OceGFjERCfmDdc2wmEaZ4VLo62KpUKJJf6gdf9Bcrx1gw=="],
|
||||
|
||||
"@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.93", "", { "dependencies": { "@ai-sdk/provider": "3.0.13", "@ai-sdk/provider-utils": "4.0.35" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-RHLRn6TIfxvUOZyVztfWFSnYMZrmoUmqtaHCP2sWmWSLQkRZTOqD97PLk+oZdod5BQGAcSgzqVz8OTaXTADwcQ=="],
|
||||
"@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.92", "", { "dependencies": { "@ai-sdk/provider": "3.0.13", "@ai-sdk/provider-utils": "4.0.35" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-dFrf4xhx2yM686KHFm76Nn7nBekjkjiw1btqOyR26/kXz58QMguhNsjyvMqPktD8AW/wwTAq0fDRVyDvF2gH1w=="],
|
||||
|
||||
"@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.143", "", { "dependencies": { "@ai-sdk/provider": "3.0.13", "@ai-sdk/provider-utils": "4.0.35", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-RCH60KsUaNiZkI/fBuyau4yvYrVBIEgAcN+Ain94QpL1kVm28GduQzFKfGffAiJU2We0ZrmN4BHkoCZzACK96Q=="],
|
||||
"@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.142", "", { "dependencies": { "@ai-sdk/provider": "3.0.13", "@ai-sdk/provider-utils": "4.0.35", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Y1iwdxdebYXpoK5y/4CrcCfJGeFwEJWlEx+pMWIg/ZVWGi9KA+JXM7YBkhxcshpq/jAXx8YyQeFMyZr0TGfXZQ=="],
|
||||
|
||||
"@ai-sdk/google": ["@ai-sdk/google@3.0.88", "", { "dependencies": { "@ai-sdk/provider": "3.0.13", "@ai-sdk/provider-utils": "4.0.35" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-CN3PHCz5pa2sBowwZG4sNqE+7YfHWZT6+5KU12YMWuBssZ03s143Jr2jThkN5Fgemy8Kyg+ub2XbpHGhtIZ2yQ=="],
|
||||
|
||||
"@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@4.0.156", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.93", "@ai-sdk/google": "3.0.88", "@ai-sdk/openai-compatible": "2.0.56", "@ai-sdk/provider": "3.0.13", "@ai-sdk/provider-utils": "4.0.35", "google-auth-library": "^10.5.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-5Vkgl/wV7GCvb5alX5IpgdWAWfUpr8VNBDfhOYDRIZshaq8xe+99VMIFvLsAbcCXiXdgdIdwKXhKQmzyaRBxVg=="],
|
||||
"@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@4.0.155", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.92", "@ai-sdk/google": "3.0.88", "@ai-sdk/openai-compatible": "2.0.56", "@ai-sdk/provider": "3.0.13", "@ai-sdk/provider-utils": "4.0.35", "google-auth-library": "^10.5.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-DMH5rhnN2AcwDmLWcRqUwF/Mj5SlgaAK5uy9ke7SNZ/BCNUcnI7MBVr1urteBdro1P3BAfiNSXuTfjIlOhwl/Q=="],
|
||||
|
||||
"@ai-sdk/mistral": ["@ai-sdk/mistral@3.0.45", "", { "dependencies": { "@ai-sdk/provider": "3.0.13", "@ai-sdk/provider-utils": "4.0.35" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-IyOPiBqPkd6RPqTvhDN6/JcoF7VVP2499FY4acnMz08Y7a1c1ZplsL6lPRTOmcFEwto1491ePilD9H0GtY1QEg=="],
|
||||
|
||||
@@ -762,7 +772,7 @@
|
||||
|
||||
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.35", "", { "dependencies": { "@ai-sdk/provider": "3.0.13", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-bjYld/2KGPLt78kpqbya+fD4LYS7BqVQJyUjE3qAHrYB0FR2Q90BaWEVIBZaguTWXf/A8L6uG1zO1v9TxVlGWg=="],
|
||||
|
||||
"@ai-sdk/react": ["@ai-sdk/react@3.0.221", "", { "dependencies": { "@ai-sdk/provider-utils": "4.0.35", "ai": "6.0.219", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1" } }, "sha512-5Zd+rF0YbjOqre0NEzdNE+FvNPrEuqfHm4KGxw9ovbAHv0ut/7sgYIn2kzr6ekvXHNqWz0HEmi+zYOXOZCJxJw=="],
|
||||
"@ai-sdk/react": ["@ai-sdk/react@3.0.220", "", { "dependencies": { "@ai-sdk/provider-utils": "4.0.35", "ai": "6.0.218", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1" } }, "sha512-mUqM13WXUT2jNhoU0Kf7YxAtIS+atCUtzZ1LNkhF1q42oYs4g2gWFtq4gNCejACvFMu1KusGRQU2S7ONXtVLZw=="],
|
||||
|
||||
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
||||
|
||||
@@ -770,63 +780,63 @@
|
||||
|
||||
"@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.3.201", "", { "optionalDependencies": { "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.201", "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.201", "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.201", "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.201", "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.201", "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.201", "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.201", "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.201" }, "peerDependencies": { "@anthropic-ai/sdk": ">=0.93.0", "@modelcontextprotocol/sdk": "^1.29.0", "zod": "^4.0.0" } }, "sha512-InT1XLmf2QpldWdtznKDWEoGJT4p+sXh24yxbeBQ++lMJCzMrI0W27MEmmmDWx0otpa+ubdHCF5YQ6oiNt7cmg=="],
|
||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.3.198", "", { "optionalDependencies": { "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.198", "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.198", "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.198", "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.198", "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.198", "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.198", "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.198", "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.198" }, "peerDependencies": { "@anthropic-ai/sdk": ">=0.93.0", "@modelcontextprotocol/sdk": "^1.29.0", "zod": "^4.0.0" } }, "sha512-xt469sSCyclTPtzpLAg0Aschy665GiRMgZKabSmESbGUA5/H56HcILVOiFxclXswkeMUk2fQxfHJUY9UZfiTnA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-arm64": ["@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.201", "", { "os": "darwin", "cpu": "arm64" }, "sha512-8Mcb3BDyKUGfJWFFTWwt+at37lbDH3ZwVtUNPWGG1toZ75RDCJry5U4kXRvQ2xokvJQlA0E+eNp6keWe5ZH22Q=="],
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-arm64": ["@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.198", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ZmiAybQKIKcP1qEAE/vfXvfxtKxG9CnJn98QTXC5Zxiwuy7Mllx2ALXh9dfmsf0V87CGEodlZQmMgUJotNIsUw=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-x64": ["@anthropic-ai/claude-agent-sdk-darwin-x64@0.3.201", "", { "os": "darwin", "cpu": "x64" }, "sha512-TFR2bu0+ml3RHoMrtsgD0qDK5Oknw8kYGBV7qpQHn+IWmE96gnHhogG1LpJwpHtni08XkJIjfWk1DdlsUYtRkQ=="],
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-x64": ["@anthropic-ai/claude-agent-sdk-darwin-x64@0.3.198", "", { "os": "darwin", "cpu": "x64" }, "sha512-XwH5vgN46WSwg8aC1OagNofnJpV/G1ciEu118GEKer8ZhVkq/dvK/DqShxMkb6r1jV7u5IJ7zPXu9uKliyNJAw=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64": ["@anthropic-ai/claude-agent-sdk-linux-arm64@0.3.201", "", { "os": "linux", "cpu": "arm64" }, "sha512-mShTo3MwF0gkN4dDw78wWJiB6aBDVRkl81cnApvoBofpdyUBYgm9Gw16CCjDTgelMKeBFqN6ErJpwjI3wbP00A=="],
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64": ["@anthropic-ai/claude-agent-sdk-linux-arm64@0.3.198", "", { "os": "linux", "cpu": "arm64" }, "sha512-qmz8dxEtDIlKntU5qYe0R4aWTxTue5S7zIQknatLX7aJ6HN/nq1aCNXWn5smTH2FViBkUPPR+sCIsNwSk6AT6Q=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": ["@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.3.201", "", { "os": "linux", "cpu": "arm64" }, "sha512-EiqbpfJIpChfkn+8Uj061Qjyw0eaRcOXtdrvVuHANyj8ZErVOr8HlH6op9PSeIUa9TX0m2+tNgKPQvOGseQckA=="],
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": ["@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.3.198", "", { "os": "linux", "cpu": "arm64" }, "sha512-Q7lKVNjIrUQ2B/AR77OvRf0zeOdEjonFVaR9FYrrwtzGeEqum69WSht5nM7Y7el3wjbNi0/eV0QTUM0DlsTEfw=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64": ["@anthropic-ai/claude-agent-sdk-linux-x64@0.3.201", "", { "os": "linux", "cpu": "x64" }, "sha512-jrJBrRWrSuoFKIgjyqxHqmfd6Pb3Bs5Bvakg0knXCTC4fbUXGnC9Q6u7gdDwgXohUNP6/DD+s8U7bivvvVv0dg=="],
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64": ["@anthropic-ai/claude-agent-sdk-linux-x64@0.3.198", "", { "os": "linux", "cpu": "x64" }, "sha512-Zqxyz2AT1UM5WlOOoLJhLssZDgZo8rBK5ku6daveK12zp+UTJGZhGsjFghz1/ASxH08KqOTbUePNTORnPhHAEQ=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": ["@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.3.201", "", { "os": "linux", "cpu": "x64" }, "sha512-IbxnzO5UCbqbm2TnzCHkSyJorAFw2isdKdIsFCTxJJjSs3ZC+v3LC1QSUiVCx0qi+CV6w3MKx6mLI11mrvhbbQ=="],
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": ["@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.3.198", "", { "os": "linux", "cpu": "x64" }, "sha512-h1SrWVIMjLInYNPlf+TxXuKTOdoiOfJLBSoQG97315Z2Nh0IpBfqWExlqYTtPCgKE7q2iga31U283QfHpIDlSQ=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-win32-arm64": ["@anthropic-ai/claude-agent-sdk-win32-arm64@0.3.201", "", { "os": "win32", "cpu": "arm64" }, "sha512-UsoytRJ/037uHpb3ATrIoe+AgwTf+PwKuFLGjddHAV/11wERJs0hlrnSmcnp43kf0PFxoSNinngme96YYASmQg=="],
|
||||
"@anthropic-ai/claude-agent-sdk-win32-arm64": ["@anthropic-ai/claude-agent-sdk-win32-arm64@0.3.198", "", { "os": "win32", "cpu": "arm64" }, "sha512-mjIHf1HFiRuXefewWTaNZFlTZlCaEt/xsRjc1nSTCEEpFolZayVhrDKz+O2QFVcDtPl8x8GeYSL0kiikg1DZjQ=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-win32-x64": ["@anthropic-ai/claude-agent-sdk-win32-x64@0.3.201", "", { "os": "win32", "cpu": "x64" }, "sha512-PhalN/0cWcqDfbx7iwoLNR2gurjTiqhBk1G6K+NRScxEcQjWuu5xKXCcdbX8ePVpT+nbEMmFEFpn2y+8V8hIdA=="],
|
||||
"@anthropic-ai/claude-agent-sdk-win32-x64": ["@anthropic-ai/claude-agent-sdk-win32-x64@0.3.198", "", { "os": "win32", "cpu": "x64" }, "sha512-y3HLuCCz1kDwUrhd6OnqO+d5BUpTFSzNUsPT9kf3r1vk9HYKF+eMC9eIlcOhiW2kX491kxEvuEOfqgIkGx15cg=="],
|
||||
|
||||
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.37.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-tHjX2YbkUBwEgg0JZU3EFSSAQPoK4qQR/NFYa8Vtzd5UAyXzZksCw2In69Rml4R/TyHPBfRYaLK35XiOe33pjw=="],
|
||||
|
||||
"@asamuzakjp/css-color": ["@asamuzakjp/css-color@3.2.0", "", { "dependencies": { "@csstools/css-calc": "^2.1.3", "@csstools/css-color-parser": "^3.0.9", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "lru-cache": "^10.4.3" } }, "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw=="],
|
||||
|
||||
"@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.1079.0", "", { "dependencies": { "@aws-sdk/core": "^3.974.27", "@aws-sdk/credential-provider-node": "^3.972.62", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/fetch-http-handler": "^5.6.2", "@smithy/node-http-handler": "^4.9.2", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-HIScdAc8q/upCY/f3TPW0pNq1K1LL7tn5fEifKf1K+zs3NRPXLultta96ZwvcZ9Ax503JKKTo9f3xGpR3fpCxQ=="],
|
||||
"@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.1078.0", "", { "dependencies": { "@aws-sdk/core": "^3.974.26", "@aws-sdk/credential-provider-node": "^3.972.61", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/fetch-http-handler": "^5.6.2", "@smithy/node-http-handler": "^4.9.2", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-BYy0X/+GMXlitKShxkdTsCexWwDrn8usY2Y2Z06M5MSi4aRT3Ce5ilyA6OubQUqOWfsmDMYrm8oBNaTIcQFyrg=="],
|
||||
|
||||
"@aws-sdk/core": ["@aws-sdk/core@3.974.27", "", { "dependencies": { "@aws-sdk/types": "^3.973.15", "@aws-sdk/xml-builder": "^3.972.33", "@aws/lambda-invoke-store": "^0.3.0", "@smithy/core": "^3.29.0", "@smithy/signature-v4": "^5.6.1", "@smithy/types": "^4.15.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-WRWEgIq6vx+NU6ot3VrRu4Jovj9MIObitSi6of/GV5THDDPccBhivCRNkWJutMM+m3GvdeI3l/UbGNcoOobxOA=="],
|
||||
"@aws-sdk/core": ["@aws-sdk/core@3.974.26", "", { "dependencies": { "@aws-sdk/types": "^3.973.15", "@aws-sdk/xml-builder": "^3.972.33", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/core": "^3.29.0", "@smithy/signature-v4": "^5.6.1", "@smithy/types": "^4.15.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-wRj7Pthvjk3anees97pUWlxlTa0DUjeGrEQU5fKDZVdWZV0ekaprbof0df2uaE9g8u67t035v2j+ne2AW2UMkA=="],
|
||||
|
||||
"@aws-sdk/credential-provider-cognito-identity": ["@aws-sdk/credential-provider-cognito-identity@3.972.52", "", { "dependencies": { "@aws-sdk/nested-clients": "^3.997.27", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-m+akZFJsghShferf2xsMw0Hogl1jNIJl2zUoZBNTFyWvlaOj1aK5sMTzcnw8m1dICvlQ+lC4T1OPGGsmZ+ezXA=="],
|
||||
"@aws-sdk/credential-provider-cognito-identity": ["@aws-sdk/credential-provider-cognito-identity@3.972.51", "", { "dependencies": { "@aws-sdk/nested-clients": "^3.997.26", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-nXzAwRz0NOiHlG/HHea7oJ2ew2m21XZUU6h2cZMCrlNQqcWjMHCkun4D6E7CWqOxiFG0MeN8Gg5Iakrjv/UXrQ=="],
|
||||
|
||||
"@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.53", "", { "dependencies": { "@aws-sdk/core": "^3.974.27", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-+KDA3uc/HZ1vIneGu5QMQb0gAXDYrm2vOE60+BJ7lS0YinMQ5i2oV4PR1A16XkF6K1IbSwjEHd1hQIIgMsK48w=="],
|
||||
"@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.52", "", { "dependencies": { "@aws-sdk/core": "^3.974.26", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-sxuaHZGHqOgKB8OdL3doXa1NJjqmO60FPfyTnYVKGjX9taRsIEGS9pd+2yALmo06hijZ8L94uSK0kfXZsRmVyA=="],
|
||||
|
||||
"@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.55", "", { "dependencies": { "@aws-sdk/core": "^3.974.27", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/fetch-http-handler": "^5.6.2", "@smithy/node-http-handler": "^4.9.2", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-1gBfkWY3RWeBlCoB9lIJjXMx45/54wxcgfzv6BY9otTmMrZPcNPi1v+MwZxxaCUg441NV3jsr1efnFNCXiW70g=="],
|
||||
"@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.54", "", { "dependencies": { "@aws-sdk/core": "^3.974.26", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/fetch-http-handler": "^5.6.2", "@smithy/node-http-handler": "^4.9.2", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-e6yz52nq3SpR1oPLcvfsDM7H7k2gIYk/NSn/rwsFqzGXEwr3g0mRMlPbLaKCPCGNZJMU/gZg6/64B3eSam+gBw=="],
|
||||
|
||||
"@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.60", "", { "dependencies": { "@aws-sdk/core": "^3.974.27", "@aws-sdk/credential-provider-env": "^3.972.53", "@aws-sdk/credential-provider-http": "^3.972.55", "@aws-sdk/credential-provider-login": "^3.972.59", "@aws-sdk/credential-provider-process": "^3.972.53", "@aws-sdk/credential-provider-sso": "^3.972.59", "@aws-sdk/credential-provider-web-identity": "^3.972.59", "@aws-sdk/nested-clients": "^3.997.27", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/credential-provider-imds": "^4.4.5", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-CV2md+PXvABwRjApWGhQ0wACy9WSFIhnUGrovLcjnjBCd/46TbuivLADtkF8IWNjtCQmQ+2IagSaxqBYqXBNAQ=="],
|
||||
"@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.59", "", { "dependencies": { "@aws-sdk/core": "^3.974.26", "@aws-sdk/credential-provider-env": "^3.972.52", "@aws-sdk/credential-provider-http": "^3.972.54", "@aws-sdk/credential-provider-login": "^3.972.58", "@aws-sdk/credential-provider-process": "^3.972.52", "@aws-sdk/credential-provider-sso": "^3.972.58", "@aws-sdk/credential-provider-web-identity": "^3.972.58", "@aws-sdk/nested-clients": "^3.997.26", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/credential-provider-imds": "^4.4.5", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-9Um/UpruN76AdpiLnvwChVkJJwJ9Vx9ykk/2AeLxxSCM/YYRD8Kkq2towUk9fZQLV7dd9ATlsi87U7hKs0z/iQ=="],
|
||||
|
||||
"@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.59", "", { "dependencies": { "@aws-sdk/core": "^3.974.27", "@aws-sdk/nested-clients": "^3.997.27", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-JG4S9yyA1GFzJdJXqLKrUzZbyK+VDp2QIsJD7YOicJHAhqymfHpDJIok2dLnhOdVB0I37RjdC53uOwCMVS00gw=="],
|
||||
"@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.58", "", { "dependencies": { "@aws-sdk/core": "^3.974.26", "@aws-sdk/nested-clients": "^3.997.26", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-H3q96qF8/DJsPsXMVtMRqSWOc85K5O4zos32untdw+vE5vw0f3a6qJo1YqbND4BsEIKd4iZmzzVUq9kV4LjbHg=="],
|
||||
|
||||
"@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.62", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.53", "@aws-sdk/credential-provider-http": "^3.972.55", "@aws-sdk/credential-provider-ini": "^3.972.60", "@aws-sdk/credential-provider-process": "^3.972.53", "@aws-sdk/credential-provider-sso": "^3.972.59", "@aws-sdk/credential-provider-web-identity": "^3.972.59", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/credential-provider-imds": "^4.4.5", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-S6Slq3Tx7bvFk5yc34XNADyZYTX2HUXvaFAnowGRQnhjBO8J/mP62Fn7lxvJwjaDyYm/7gh9h6HEHaltRyMFXw=="],
|
||||
"@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.61", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.52", "@aws-sdk/credential-provider-http": "^3.972.54", "@aws-sdk/credential-provider-ini": "^3.972.59", "@aws-sdk/credential-provider-process": "^3.972.52", "@aws-sdk/credential-provider-sso": "^3.972.58", "@aws-sdk/credential-provider-web-identity": "^3.972.58", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/credential-provider-imds": "^4.4.5", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-2U2KHMRCt1dlZoLU3KZR5g5EL4b0h2HHw96SkaUBK7qvEXPZj5rGRO/3ZTeJmh37dIYQuCnA2273rZOQvmsiHw=="],
|
||||
|
||||
"@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.53", "", { "dependencies": { "@aws-sdk/core": "^3.974.27", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-EhfH+MQlqOMCkXIVa8MMObPzAQqwTTtxA7KhEJiyPeuNVA8PLOOUpgK7nBrgaDaGiIDLN/9LpGdaHuDjomeRTw=="],
|
||||
"@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.52", "", { "dependencies": { "@aws-sdk/core": "^3.974.26", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-Aff9Ebs42lz+Ep1wkS+Nlwh5S0eahakpyskPsuKGjiBJ6ExOjNtxbfKJTKovQtQNgJ7oG1BH6esJwGrbs7qgSA=="],
|
||||
|
||||
"@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.59", "", { "dependencies": { "@aws-sdk/core": "^3.974.27", "@aws-sdk/nested-clients": "^3.997.27", "@aws-sdk/token-providers": "3.1079.0", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-h8793pOjcImx0SB+VcLONcaQQ57VAvKVuqyewQMRKqqH+CSXsG2dwOeLMUJPMxLdNvL7dXOM0ueTukyNUnu5mA=="],
|
||||
"@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.58", "", { "dependencies": { "@aws-sdk/core": "^3.974.26", "@aws-sdk/nested-clients": "^3.997.26", "@aws-sdk/token-providers": "3.1078.0", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-syloC58mXOacUqM2toPNfwd7X3jT+tWj0F/cN7qdW1FQyI0q41J0tPf6DIZ56BF0x82iS9j3ALP45MoBz79YuQ=="],
|
||||
|
||||
"@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.59", "", { "dependencies": { "@aws-sdk/core": "^3.974.27", "@aws-sdk/nested-clients": "^3.997.27", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-VoyO9+vl3XVmpZwn4obskrWIkrA/Jf3lSe1E3ZERlaN9u0D4YZ6+HywC3+L98QOXqZesEfedk67gRER8tK8+8w=="],
|
||||
"@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.58", "", { "dependencies": { "@aws-sdk/core": "^3.974.26", "@aws-sdk/nested-clients": "^3.997.26", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-pTBImKzcGK+pcMKjL0fAJbnYzzYd1c0UDc7BSIOGNQhF9Nuk66vWlIXfYTYyzNSs+w8Q/vfbbNDDU8zdrouwLg=="],
|
||||
|
||||
"@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.1079.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.1079.0", "@aws-sdk/core": "^3.974.27", "@aws-sdk/credential-provider-cognito-identity": "^3.972.52", "@aws-sdk/credential-provider-env": "^3.972.53", "@aws-sdk/credential-provider-http": "^3.972.55", "@aws-sdk/credential-provider-ini": "^3.972.60", "@aws-sdk/credential-provider-login": "^3.972.59", "@aws-sdk/credential-provider-node": "^3.972.62", "@aws-sdk/credential-provider-process": "^3.972.53", "@aws-sdk/credential-provider-sso": "^3.972.59", "@aws-sdk/credential-provider-web-identity": "^3.972.59", "@aws-sdk/nested-clients": "^3.997.27", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/credential-provider-imds": "^4.4.5", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-emoshJjvvyJDjoMlognc1BtdsTDbe/8NQhXM2wIOz/6/vx4lynUYbwhcNdP6rXuT1q0HzugEDkQK9EvbzB94fA=="],
|
||||
"@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.1078.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.1078.0", "@aws-sdk/core": "^3.974.26", "@aws-sdk/credential-provider-cognito-identity": "^3.972.51", "@aws-sdk/credential-provider-env": "^3.972.52", "@aws-sdk/credential-provider-http": "^3.972.54", "@aws-sdk/credential-provider-ini": "^3.972.59", "@aws-sdk/credential-provider-login": "^3.972.58", "@aws-sdk/credential-provider-node": "^3.972.61", "@aws-sdk/credential-provider-process": "^3.972.52", "@aws-sdk/credential-provider-sso": "^3.972.58", "@aws-sdk/credential-provider-web-identity": "^3.972.58", "@aws-sdk/nested-clients": "^3.997.26", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/credential-provider-imds": "^4.4.5", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-V9Tr3MrNWUfTGgTMIr+WJaMC/VDbXY57BzrGDuyDZn7+vgZjAEG6nI5nMdfnTGdvV9wq1n0zZPYW2RDfcsWNCw=="],
|
||||
|
||||
"@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.27", "", { "dependencies": { "@aws-sdk/core": "^3.974.27", "@aws-sdk/signature-v4-multi-region": "^3.996.38", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/fetch-http-handler": "^5.6.2", "@smithy/node-http-handler": "^4.9.2", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-A8PIePF9NIIOJ/4Lg1rl9xm/+QaKkHGetq+Z9wb5B+3Da31YYXRo8n7IDMh5C+HQI5eyEmjrwkGWVdYtnLtbXQ=="],
|
||||
"@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.26", "", { "dependencies": { "@aws-sdk/core": "^3.974.26", "@aws-sdk/signature-v4-multi-region": "^3.996.38", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/fetch-http-handler": "^5.6.2", "@smithy/node-http-handler": "^4.9.2", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-Lwe3F6K7bs+jEubp1LbrvzeMBYb5fMazJ1IxV9TtKWPF8CSh67Fmwyq9fLz3NL/k55Dfpuph5Dimw76JFgr+SA=="],
|
||||
|
||||
"@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.38", "", { "dependencies": { "@aws-sdk/types": "^3.973.15", "@smithy/signature-v4": "^5.6.1", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-C379Sk+MiFZCfWZphKlMyLHKxV22OjoGM5KJjj5IJNJcOCWL4IGIpnEGzv1FQiRwhYXfq55SJMfxlqPE08JJ9g=="],
|
||||
|
||||
"@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1079.0", "", { "dependencies": { "@aws-sdk/core": "^3.974.27", "@aws-sdk/nested-clients": "^3.997.27", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-cbietrLlHPhhmbnMPTuDS4Zj/KNGhY+3vVhn6dwjO6Dqzrwothzg2srtcY34T9mlICsTXn34avDoWLHSntP54A=="],
|
||||
"@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1078.0", "", { "dependencies": { "@aws-sdk/core": "^3.974.26", "@aws-sdk/nested-clients": "^3.997.26", "@aws-sdk/types": "^3.973.15", "@smithy/core": "^3.29.0", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-/uyXLBGu3Lw1GbBA2X66hcOMnKtMcqAIF+3/eHfxBQmUeXF2sdqozDPrTfEr/TnSd0D6deZar+eVyhEqqWu29w=="],
|
||||
|
||||
"@aws-sdk/types": ["@aws-sdk/types@3.973.15", "", { "dependencies": { "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-IULn8uBV/SMtmOIANsm4WHXIOtVPBWfOWs3WGL0j/sI+KhaYehvOw0ET+9urnn8MBpiijuU/0JOpuwKOE451PQ=="],
|
||||
|
||||
"@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.33", "", { "dependencies": { "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-ezbwz9WpuLctm6o7P2t2naDhVVPI5jFGrVefVybhcKGjU57VIyT46pQVO0RI2RYkUdhdj2Z9uSIlAzGZE9NW9A=="],
|
||||
|
||||
"@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.3.0", "", {}, "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ=="],
|
||||
"@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.4", "", {}, "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ=="],
|
||||
|
||||
"@azu/format-text": ["@azu/format-text@1.0.2", "", {}, "sha512-Swi4N7Edy1Eqq82GxgEECXSSLyn6GOb5htRFPzBDdUkECGXtlf12ynO5oJSpWKPwCaUssOu7NfhDcCWpIC6Ywg=="],
|
||||
|
||||
@@ -972,9 +982,9 @@
|
||||
|
||||
"@chevrotain/types": ["@chevrotain/types@11.1.2", "", {}, "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw=="],
|
||||
|
||||
"@clack/core": ["@clack/core@1.4.3", "", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ=="],
|
||||
"@clack/core": ["@clack/core@1.4.2", "", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-0Ty/1Gfm+Kb07sXcuESjyKfwEhSy4Ns1AgeEisHb/bDY5fWme0tTeTkU14T1Gmcs17YIjB/teiDe4uaCghbYqQ=="],
|
||||
|
||||
"@clack/prompts": ["@clack/prompts@1.7.0", "", { "dependencies": { "@clack/core": "1.4.3", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A=="],
|
||||
"@clack/prompts": ["@clack/prompts@1.6.0", "", { "dependencies": { "@clack/core": "1.4.2", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-EYlRokl8szrP9Z25qT5aepMdBjzBvHF9ZEhzIiUBc9guz/T31EqRgvD0QSgZcpE93xiwrr+OkB4nz0BZyF6fSA=="],
|
||||
|
||||
"@cline/agents": ["@cline/agents@workspace:sdk/packages/agents"],
|
||||
|
||||
@@ -1434,7 +1444,7 @@
|
||||
|
||||
"@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
|
||||
|
||||
"@iconify/utils": ["@iconify/utils@3.1.4", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "import-meta-resolve": "^4.2.0" } }, "sha512-b1S7B1k9ohZ+iNTi2ATxbRYG9fTrJmUT0rc46bvVnNxqNRGW7dyo/vRREwyniI5IRN2RSJHDcm+s3BjWrSAjHw=="],
|
||||
"@iconify/utils": ["@iconify/utils@3.1.3", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "import-meta-resolve": "^4.2.0" } }, "sha512-LPKOXPn/zV+zis1oOfGWogaXVpqUybF3ZS6SCZIsz8vg0ivVp9+fVqyYB7xq0aiST/VhUQYGO1qo6uoYSiEJqw=="],
|
||||
|
||||
"@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="],
|
||||
|
||||
@@ -1658,9 +1668,9 @@
|
||||
|
||||
"@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.214.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-40lSJeqYO8Uz2Yj7u94/SJWE/wONa7rmMKjI1ZcIjgf3MHNHv1OZUCrCETGuaRF62d5pQD1wKIW+L4lmSMTzZA=="],
|
||||
|
||||
"@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@2.9.0", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-OQ0vzvbZBiUhjqLnUaoNfYmP8553Crr3aggB4y0ZUi815mZ7idpdJXQmoKdeBKJelYttoBlLSSHubmyw3wvX4w=="],
|
||||
"@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@2.8.0", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-/3FIraneMcng67SUJCxvyInk/oxzwsxyadufk0wwfOBLf5wqtAGX4MoQASwSbndBPeARzBryUM9Azr5kHIdWLw=="],
|
||||
|
||||
"@opentelemetry/core": ["@opentelemetry/core@2.9.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw=="],
|
||||
"@opentelemetry/core": ["@opentelemetry/core@2.8.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-hd1Lfh8p545nNz+jq1Ejfz+Mn1hyLuxYn1YzTfFNrxr8urEWMNQLPf1Th8kjOH+HxwawCrtgBp8JpBUR4ZSgww=="],
|
||||
|
||||
"@opentelemetry/exporter-logs-otlp-grpc": ["@opentelemetry/exporter-logs-otlp-grpc@0.56.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "1.29.0", "@opentelemetry/otlp-grpc-exporter-base": "0.56.0", "@opentelemetry/otlp-transformer": "0.56.0", "@opentelemetry/sdk-logs": "0.56.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-/ef8wcphVKZ0uI7A1oqQI/gEMiBUlkeBkM9AGx6AviQFIbgPVSdNK3+bHBkyq5qMkyWgkeQCSJ0uhc5vJpf0dw=="],
|
||||
|
||||
@@ -1698,19 +1708,17 @@
|
||||
|
||||
"@opentelemetry/propagator-jaeger": ["@opentelemetry/propagator-jaeger@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-Pj/BfnYEKIOImirH76M4hDaBSx6HyZ2CXUqk+Kj02m6BB80c/yo4BdWkn/1gDFfU+YPY+bPR2U0DKBfdxCKwmg=="],
|
||||
|
||||
"@opentelemetry/resources": ["@opentelemetry/resources@2.9.0", "", { "dependencies": { "@opentelemetry/core": "2.9.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg=="],
|
||||
"@opentelemetry/resources": ["@opentelemetry/resources@2.8.0", "", { "dependencies": { "@opentelemetry/core": "2.8.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-qmXQ27ilDbUK/vGMqwL8D4/rhn76C+sherM4wTbjlfknR8Nvfc/hCxjRJPhkzZzUsPiNg16SA31NxMabwttRjg=="],
|
||||
|
||||
"@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.214.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.214.0", "@opentelemetry/core": "2.6.1", "@opentelemetry/resources": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-zf6acnScjhsaBUU22zXZ/sLWim1dfhUAbGXdMmHmNG3LfBnQ3DKsOCITb2IZwoUsNNMTogqFKBnlIPPftUgGwA=="],
|
||||
|
||||
"@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.9.0", "", { "dependencies": { "@opentelemetry/core": "2.9.0", "@opentelemetry/resources": "2.9.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-Xx8RGS4H5XEBl01WuCreMIpiah9cCXMbSkeuIePPdD2cUpq/vUzYmj8E/MK1OsbOc93FuAD4jfn2WOacKwLn7Q=="],
|
||||
"@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.8.0", "", { "dependencies": { "@opentelemetry/core": "2.8.0", "@opentelemetry/resources": "2.8.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-UDBGaj6W0Rgy5rTTaoxs8gVGF/aGkAKyjurJv7se6wjRxJu7FoquTLT/vt54DZfo4crbprYfhX/SOK9+BPw1qg=="],
|
||||
|
||||
"@opentelemetry/sdk-node": ["@opentelemetry/sdk-node@0.56.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.56.0", "@opentelemetry/core": "1.29.0", "@opentelemetry/exporter-logs-otlp-grpc": "0.56.0", "@opentelemetry/exporter-logs-otlp-http": "0.56.0", "@opentelemetry/exporter-logs-otlp-proto": "0.56.0", "@opentelemetry/exporter-trace-otlp-grpc": "0.56.0", "@opentelemetry/exporter-trace-otlp-http": "0.56.0", "@opentelemetry/exporter-trace-otlp-proto": "0.56.0", "@opentelemetry/exporter-zipkin": "1.29.0", "@opentelemetry/instrumentation": "0.56.0", "@opentelemetry/resources": "1.29.0", "@opentelemetry/sdk-logs": "0.56.0", "@opentelemetry/sdk-metrics": "1.29.0", "@opentelemetry/sdk-trace-base": "1.29.0", "@opentelemetry/sdk-trace-node": "1.29.0", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-FOY7tWboBBxqftLNHPJFmDXo9fRoPd2PlzfEvSd6058BJM9gY4pWCg8lbVlu03aBrQjcfCTAhXk/tz1Yqd/m6g=="],
|
||||
|
||||
"@opentelemetry/sdk-trace": ["@opentelemetry/sdk-trace@2.9.0", "", { "dependencies": { "@opentelemetry/core": "2.9.0", "@opentelemetry/resources": "2.9.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-sGA19HvtrrSKYsseHphluH6j3p6Xa3fqc7c7y8f/7mYWejc1lyDFcpSdD1kYa50HCLUeEo4zA5bW0pniaPszuw=="],
|
||||
"@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.8.0", "", { "dependencies": { "@opentelemetry/core": "2.8.0", "@opentelemetry/resources": "2.8.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-mhU4jp+vW0mGbFRd+GeXHvmfA4aDqWjBjLC3pE5XMpLs0IE2ryYb019Ts2AQrOq67gaTF25D91+fgvEHDZEnuQ=="],
|
||||
|
||||
"@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.9.0", "", { "dependencies": { "@opentelemetry/core": "2.9.0", "@opentelemetry/resources": "2.9.0", "@opentelemetry/sdk-trace": "2.9.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-cp9zmTl62R8PJrpvFcmc8N2JQU/xfa0S+61q511Nji+QxCfZ8Ifvg7H27G8cANe4crg4RTrWsVvanHiXjSp6ag=="],
|
||||
|
||||
"@opentelemetry/sdk-trace-node": ["@opentelemetry/sdk-trace-node@2.9.0", "", { "dependencies": { "@opentelemetry/context-async-hooks": "2.9.0", "@opentelemetry/core": "2.9.0", "@opentelemetry/sdk-trace-base": "2.9.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-ec9a7ps37huy5itYk0MalaZdSLlM6AXWp/FhtEjgMpp5leEGojBDvAl/UWttQnkMZOvFHKzRESn8TD3yKTF5nQ=="],
|
||||
"@opentelemetry/sdk-trace-node": ["@opentelemetry/sdk-trace-node@2.8.0", "", { "dependencies": { "@opentelemetry/context-async-hooks": "2.8.0", "@opentelemetry/core": "2.8.0", "@opentelemetry/sdk-trace-base": "2.8.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-nZt9OGufioAc3AfoLTqA9bsAeaMJAictYDdI2VcNQ+PmT+3rfKjAZDZvgPfd8VPX0O5Bw1hdQF6kDK8VSpZiWg=="],
|
||||
|
||||
"@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.41.1", "", {}, "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA=="],
|
||||
|
||||
@@ -1744,9 +1752,9 @@
|
||||
|
||||
"@playwright/test": ["@playwright/test@1.61.1", "", { "dependencies": { "playwright": "1.61.1" }, "bin": { "playwright": "cli.js" } }, "sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig=="],
|
||||
|
||||
"@posthog/core": ["@posthog/core@1.39.6", "", { "dependencies": { "@posthog/types": "^1.392.0" } }, "sha512-o6ajIwN5zXoNP0D4H/QPmOyibNTUkSyOR6ya7AG5U2ywXx4awo72L2KnCoiZPQM5x/bXv6jPBdimH8M18Ax0aw=="],
|
||||
"@posthog/core": ["@posthog/core@1.39.3", "", { "dependencies": { "@posthog/types": "^1.392.0" } }, "sha512-oR+B8Q5O61N+W2+HVOBG9dbxAT/+OVxX+XvpNj6KYgptN2EB14JiKQ9Rm7DNpErNTLV7WApZEK/URkZgldOxfg=="],
|
||||
|
||||
"@posthog/types": ["@posthog/types@1.392.1", "", {}, "sha512-Qg6Gl7/1vlr8+gPtBi5gwnLgAgiyFoKOVmTvTtDcvya9cpTwZfna7rQmkGQ4B63CunUYNNbOlqcwiUwUDyTK6w=="],
|
||||
"@posthog/types": ["@posthog/types@1.392.0", "", {}, "sha512-nctNujXL3FC1v99FktaTMSugSD9ZOZekEpahUSafkU2TSvW+XGKNkQZbokuJtiWvPBK208dwMJva8UfBkChqpw=="],
|
||||
|
||||
"@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="],
|
||||
|
||||
@@ -1766,7 +1774,7 @@
|
||||
|
||||
"@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="],
|
||||
|
||||
"@protobufjs/utf8": ["@protobufjs/utf8@1.1.2", "", {}, "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug=="],
|
||||
"@protobufjs/utf8": ["@protobufjs/utf8@1.1.1", "", {}, "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg=="],
|
||||
|
||||
"@puppeteer/browsers": ["@puppeteer/browsers@2.6.1", "", { "dependencies": { "debug": "^4.4.0", "extract-zip": "^2.0.1", "progress": "^2.0.3", "proxy-agent": "^6.5.0", "semver": "^7.6.3", "tar-fs": "^3.0.6", "unbzip2-stream": "^1.4.3", "yargs": "^17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" } }, "sha512-aBSREisdsGH890S2rQqK82qmQYU3uFpSH8wcZWHgHzl3LfzsxAKbLNiAG9mO8v1Y0UICBeClICxPJvyr0rcuxg=="],
|
||||
|
||||
@@ -2212,19 +2220,19 @@
|
||||
|
||||
"@secretlint/types": ["@secretlint/types@10.2.2", "", {}, "sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg=="],
|
||||
|
||||
"@shikijs/core": ["@shikijs/core@4.3.1", "", { "dependencies": { "@shikijs/primitive": "4.3.1", "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-ANMDxuaPsNMdDC1m4vfvhlDmJweMwkE5XitTwrq2rWHx5jM+dlm4MmHt2PP6t0uejfR77SuhrhJ0zEijIF/uhA=="],
|
||||
"@shikijs/core": ["@shikijs/core@4.3.0", "", { "dependencies": { "@shikijs/primitive": "4.3.0", "@shikijs/types": "4.3.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-EooU3i9F6IAE8kEu+AnGf9DFZWkQBZ+hJn3tLVbsH+61mtQiva5biai66fAA6nvFPXkLgvrh7BrR7YcJU83xQQ=="],
|
||||
|
||||
"@shikijs/engine-javascript": ["@shikijs/engine-javascript@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.6" } }, "sha512-JBItcnPuYq7jVJdZo/vMj94r+szT7XEjHFX+mvFDGSEIbVAXAGyHAHzhbWzpGOwYidCZrErJLLgn2PVeiokHnQ=="],
|
||||
"@shikijs/engine-javascript": ["@shikijs/engine-javascript@4.3.0", "", { "dependencies": { "@shikijs/types": "4.3.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.6" } }, "sha512-hTv/KiFf2tpiqlACPiztGGurEARWIutB8YUhcrA1pUC7VzzwKO+g5crUocrLztrZ5ro5Z4hbXg7bYclETn3gSQ=="],
|
||||
|
||||
"@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-OXyNMzg0pews+msMj4cHeqT4xiYKKvbnn6VbdAXxfoFl3SSx4fJTc8FadECuc5/H9p3BzhNAoAUXKwAu9rWYhg=="],
|
||||
"@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@4.3.0", "", { "dependencies": { "@shikijs/types": "4.3.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1vMdN3gHfnKfLYwecUI2ITJI4RhHt96xEaJumVn7Heb0IlJ8WQMIH0Voak+2j22BpSNKdnOfB/pCTPnPm2gq7A=="],
|
||||
|
||||
"@shikijs/langs": ["@shikijs/langs@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1" } }, "sha512-m0l9nsDqgBHvbZbk7A0/kXz/impK3uB/c6rAn6Gpg/uPtdZRQ+alsN/17MU5thb68XTj/4DxkZAotrM0GGSpDQ=="],
|
||||
"@shikijs/langs": ["@shikijs/langs@4.3.0", "", { "dependencies": { "@shikijs/types": "4.3.0" } }, "sha512-rnlqFbBRSys9bT4gl/5rw9RnS0W/I84ZldXPkO7cvlEMoV85TyF/aU01N7/NbSR776RNLjrJKjfFUXJR6wN1Cg=="],
|
||||
|
||||
"@shikijs/primitive": ["@shikijs/primitive@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-CXQRQOYy1leqQ8ceTeJdmXv/bsUY++6QyLpXJ94LZAAYj5X2SKRdc5ipguv4NPyGVKItB2PPwUpRNe0Sjh5S1A=="],
|
||||
"@shikijs/primitive": ["@shikijs/primitive@4.3.0", "", { "dependencies": { "@shikijs/types": "4.3.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-CPkz64PTa5diRW1ggzMZH9VM/du4RNChYgVtgqrFcgruvIybmCvySv8GkiHSczUHXYuuR8TdKEwFx+UnZMpgdg=="],
|
||||
|
||||
"@shikijs/themes": ["@shikijs/themes@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1" } }, "sha512-dgpoJ4WqNi2yTmizQHBJ5zcX6j2lE6icN/0yt4l1kkf16jrY/pwPLoTb1ETsWMz0OBLf9ZNvwmxft+cH+N9qSA=="],
|
||||
"@shikijs/themes": ["@shikijs/themes@4.3.0", "", { "dependencies": { "@shikijs/types": "4.3.0" } }, "sha512-Avgt05YiT+Y3prjIc9lmQxhJzHBcCfR6cjiFW4OyaMBbt2A6trX5rfjUzx+Vj/mE9qpArYjatnqo9XPjQNW/AQ=="],
|
||||
|
||||
"@shikijs/types": ["@shikijs/types@4.3.1", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-CHFxE0jztBIZRHH6gxXE7DXUCFXjReEGxZ/j0rfSLGKZuwp2xBYycEP14875DSa9KLL/6700oxIq6oO6ef9K2g=="],
|
||||
"@shikijs/types": ["@shikijs/types@4.3.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oc8b9U2SYvofKZk8e/737nIX0qwf6eV2vHFATeObAu7r+mUVpLs8Re0BmVkIjAWAYgkmG/CzLNo7rzuBzRu/wQ=="],
|
||||
|
||||
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
|
||||
|
||||
@@ -2246,25 +2254,25 @@
|
||||
|
||||
"@slack/socket-mode": ["@slack/socket-mode@2.0.7", "", { "dependencies": { "@slack/logger": "^4.0.1", "@slack/web-api": "^7.15.0", "@types/node": ">=18", "@types/ws": "^8", "eventemitter3": "^5", "ws": "^8" } }, "sha512-qYy07je71WnEHgRwmw12DlAnZLi5HXmdlI2WUzUK2LH/rYXQpP6uEg462S5CwfE8FoCKUdIigHtYnOOfzZH1lQ=="],
|
||||
|
||||
"@slack/types": ["@slack/types@2.22.0", "", {}, "sha512-sZ9lIgJhPX2qft/tKWiklFlc0o1FWeI7QtciZJfW1+ErH1eGGHvOZ8e73sleTCFEFJp1q/R0WeS8Oa7AsiDprg=="],
|
||||
"@slack/types": ["@slack/types@2.21.1", "", {}, "sha512-I8vmSjNYWsaxuWPx6dz4yeh0h7vRBWbgAMK14LEmblbZ404BtrPbXs6jDPx4cYgGf8msDGF4A9opLZBu21FViQ=="],
|
||||
|
||||
"@slack/web-api": ["@slack/web-api@7.18.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-EWBsKUhOFFp87beQg/ToSC+asWB7BrGHuh7uPC1ZI9vr41GjS+3WmmyWIMqs+mF6U+mh4d6HhtFlv67TrJFsvw=="],
|
||||
|
||||
"@smithy/core": ["@smithy/core@3.29.1", "", { "dependencies": { "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-qoiY4nrk5OCu1+eIR1VB8l5DmON/oKiqrd5zZFAhXJXjJlLWQusKEW/SkBDAtGDcPaz86m9kfcE1lngU0GlM6A=="],
|
||||
"@smithy/core": ["@smithy/core@3.29.0", "", { "dependencies": { "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-sEvpvkBVoMxjoek35XyJFn2ZD3EJ1RpiZrT47WaZodxzAIWS44zkdvbqGE/ZlugtjiQp62cffYZ9ldyRkjAGnA=="],
|
||||
|
||||
"@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.4.6", "", { "dependencies": { "@smithy/core": "^3.29.1", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-B2WQ/PV/H6Jeg3lrIq6bKUfa6Hy01mtK7CGs6lhjzHA6k4aagldH6T6eEjnzKl4HI0cJnAsxfJ19pgb5PV+CVQ=="],
|
||||
"@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.4.5", "", { "dependencies": { "@smithy/core": "^3.29.0", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-LnjUTNG0GgQlKIq7IioeOrPaEmC5xOd1WtAz24TLSiYQnWX2uHr53GrFuQhkrJBktPYCMga/NbUOW7hFbSA2Cg=="],
|
||||
|
||||
"@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.4.6", "", { "dependencies": { "@smithy/core": "^3.29.1", "tslib": "^2.6.2" } }, "sha512-4N/HbPptAjynK/FDrTiSAEXmpEDcQw54SeY7qnKbNMOcbHK0B0nEq+OKDYXrnqyp5YhZGzXFDUJANGkdzz6H5Q=="],
|
||||
"@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.4.5", "", { "dependencies": { "@smithy/core": "^3.29.0", "tslib": "^2.6.2" } }, "sha512-rasV+96Obv6tEhWPKWaehEhR+MEd2/lE/rdOYcmSMh6tJiQdoZg4v21p37Y1A7DMZOFgFDUX/3eNl/J3+MJDdA=="],
|
||||
|
||||
"@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.6.3", "", { "dependencies": { "@smithy/core": "^3.29.1", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-CwCc/7SMTj45y97MUnDTbTaxvtAsiNNRm81z3abROIuMbMsC2Iy5EKfkkVdsKrz8WExQAAMx1EJapq+9j4fFTQ=="],
|
||||
"@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.6.2", "", { "dependencies": { "@smithy/core": "^3.29.0", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-q96PSDOAGw+X+nuELd7Cjebps0SYr+YlPbviEX9sLVw+VM4M7VV8hn1nL1mGS6urDu33eQ5A7WhlphaDO6kUyQ=="],
|
||||
|
||||
"@smithy/node-http-handler": ["@smithy/node-http-handler@4.9.3", "", { "dependencies": { "@smithy/core": "^3.29.1", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-qZTa4gQFUo8RM02rk6q5UVTDLNrQ1oS20LsepBzqq1QBVc/EHJ03OOUADcqMZiXHArW+Y7+OGY0BpdTwZRq/Yg=="],
|
||||
"@smithy/node-http-handler": ["@smithy/node-http-handler@4.9.2", "", { "dependencies": { "@smithy/core": "^3.29.0", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-s0yAIRj6TVfHgl+QzVyqal1KMGZ9B5512IrxKc6+dOpw8fUmFL3CvuAhjv0J+aNjUPfVZ2IhqPEDvkB5Ncx9oA=="],
|
||||
|
||||
"@smithy/signature-v4": ["@smithy/signature-v4@5.6.2", "", { "dependencies": { "@smithy/core": "^3.29.1", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-QgHflghMoPxCJ9axiCVh8KZfbC9fuP6vkXXyK//E3cq7nLaSSyyLj0GAoqVWezYeDQmXIZhmlRvLE16jsqDK6g=="],
|
||||
"@smithy/signature-v4": ["@smithy/signature-v4@5.6.1", "", { "dependencies": { "@smithy/core": "^3.29.0", "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-SqvuP75p/DmgWWI7jv4kf/UW+V4LFmlUn19s604SgAcRuJRB1vDnWwzZMYCLUcmKxko9wDn6iLgGEIpTNgZbIQ=="],
|
||||
|
||||
"@smithy/types": ["@smithy/types@4.15.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-x3L0XSACF6UYzKpa9biqiRMgvH5+wnFFew9Tm/grFYqgaupPwx/+ojDPpPJM8dZON3S9tjz5U+PQYsCBd1Mw5Q=="],
|
||||
|
||||
"@smithy/util-utf8": ["@smithy/util-utf8@4.4.6", "", { "dependencies": { "@smithy/core": "^3.29.1", "tslib": "^2.6.2" } }, "sha512-gncTZwzB/RTzm29VvK1nZhCHdPjBkR8pNaFtUMbQpkG6vFMjPHe/RsnmMXfrYj8Qs5s6QH5f1Mp9CBXFOHxqlQ=="],
|
||||
"@smithy/util-utf8": ["@smithy/util-utf8@4.4.5", "", { "dependencies": { "@smithy/core": "^3.29.0", "tslib": "^2.6.2" } }, "sha512-ZS7Y5X8mU9qRSsqwOeGKw86WWtPsRxa396kX2HyhYwADRqFHJ0cb3p2uFk6QnFF3BluUUBHTZJpPA9QuEl0tlg=="],
|
||||
|
||||
"@so-ric/colorspace": ["@so-ric/colorspace@1.1.6", "", { "dependencies": { "color": "^5.0.2", "text-hex": "1.0.x" } }, "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw=="],
|
||||
|
||||
@@ -2492,7 +2500,7 @@
|
||||
|
||||
"@types/d3-quadtree": ["@types/d3-quadtree@3.0.6", "", {}, "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg=="],
|
||||
|
||||
"@types/d3-random": ["@types/d3-random@3.0.4", "", {}, "sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA=="],
|
||||
"@types/d3-random": ["@types/d3-random@3.0.3", "", {}, "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ=="],
|
||||
|
||||
"@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="],
|
||||
|
||||
@@ -2612,25 +2620,25 @@
|
||||
|
||||
"@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="],
|
||||
|
||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.63.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.63.0", "@typescript-eslint/type-utils": "8.63.0", "@typescript-eslint/utils": "8.63.0", "@typescript-eslint/visitor-keys": "8.63.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.63.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-rvwSgqT+DHpWdzfSzPatRLm02a0GlESt++9iy3hLCDY4BgkaLcl8LBi9Yh7XGFBpwcBE/K3024QuXWTpbz4FfQ=="],
|
||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.62.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.62.1", "@typescript-eslint/type-utils": "8.62.1", "@typescript-eslint/utils": "8.62.1", "@typescript-eslint/visitor-keys": "8.62.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.62.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-4EQM77WgVNxj7OkL/5b/D/xZsw00G577+UriYTC7JF5opcF3T2AuoeY7ueLaZgSVjSgCS6yOAJB5bRGLPSJUzA=="],
|
||||
|
||||
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.63.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.63.0", "@typescript-eslint/types": "8.63.0", "@typescript-eslint/typescript-estree": "8.63.0", "@typescript-eslint/visitor-keys": "8.63.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-gwh4gvvlaVDKKxyfxMG+Gnu1u9X0OQBwyGLkbwB65dIzBKnxeRiJlNFqlI3zwVhNXJIs6qV7mlFCn/BIajlVig=="],
|
||||
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.62.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.62.1", "@typescript-eslint/types": "8.62.1", "@typescript-eslint/typescript-estree": "8.62.1", "@typescript-eslint/visitor-keys": "8.62.1", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-sPhE4iHuJDSvoAiec+Ro8JyXw8f0ql13HFR82P99nCm9GwTEKG0KYLvDe6REk8BCXuit6vJAv/Yxg5ABaNS2rA=="],
|
||||
|
||||
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.63.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.63.0", "@typescript-eslint/types": "^8.63.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-e5dh0/UI0ok53AlZ5wRkXCB32z/f2jUZqPR/ygAw5WYaSw8j9EoJWlS7wQjr/dmOaqWjnPIn2m+HhVPCMWGZVQ=="],
|
||||
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.62.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.62.1", "@typescript-eslint/types": "^8.62.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-yQ3RgY5RkSBpsNS1Bx/JQEcA24FOSdfGktoyprAr5u18390UQdtVcfnEv4nIrIshNnavlVyZBKxQwT1fIAE6cg=="],
|
||||
|
||||
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.63.0", "", { "dependencies": { "@typescript-eslint/types": "8.63.0", "@typescript-eslint/visitor-keys": "8.63.0" } }, "sha512-uUyfMWCnDSN8bCpcrY8nGP2BLkQ9Xn0GsipcONcpIDWhwhO4ZSyHvyS14U3X75mzxWxL3I2UZIrenTzdzcJO8A=="],
|
||||
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.62.1", "", { "dependencies": { "@typescript-eslint/types": "8.62.1", "@typescript-eslint/visitor-keys": "8.62.1" } }, "sha512-r4d249KbQ1SFdpeStvob8Ih6aPPIzfqllPVOtvhve6ZcpuVcYo5/7zUWckKpHE7StASX4kTKZTLf0WQm/wPkcg=="],
|
||||
|
||||
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.63.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-sUAbkulqBAsncKnbRP3+7CtQFRKicexnj7ZwNC6ddCR7EmrXvjvdCYMJbUIqMd6lwoEriZjwLo08aS5tSjVMHg=="],
|
||||
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.62.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-xadytJqX9vJVQ2fdQjkcIVigwaOJNWkpjdLt6cEQ+xPnrI1fkp+/jZE/I97k9KUjqtpd25i0HeyZf3T6dutv2g=="],
|
||||
|
||||
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.63.0", "", { "dependencies": { "@typescript-eslint/types": "8.63.0", "@typescript-eslint/typescript-estree": "8.63.0", "@typescript-eslint/utils": "8.63.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-Nzzh/OGxVCOjObjaj1CQF2RUasyYy2Jfuh+zZ3PjLzG2fYRriAiZLib9UKtO+CpQAS3YHiAS+ckZDclwqI1TPA=="],
|
||||
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.62.1", "", { "dependencies": { "@typescript-eslint/types": "8.62.1", "@typescript-eslint/typescript-estree": "8.62.1", "@typescript-eslint/utils": "8.62.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-aXM5xlqXiTxPibXB93cLAURfT3rlizf7uMXISCXy66Isr/9hISJx3yDsKl0L7lKa51b8JpFuNKby0/O0pEm9jg=="],
|
||||
|
||||
"@typescript-eslint/types": ["@typescript-eslint/types@8.63.0", "", {}, "sha512-xyLtl9DUBBFrcJS4x2pIqGLH68/tC2uOa4Z7pUteW09D3bXnnXUom4dyPikzWgB7llmIc1zoeI3aoUdC4rPK/Q=="],
|
||||
"@typescript-eslint/types": ["@typescript-eslint/types@8.62.1", "", {}, "sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.63.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.63.0", "@typescript-eslint/tsconfig-utils": "8.63.0", "@typescript-eslint/types": "8.63.0", "@typescript-eslint/visitor-keys": "8.63.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-ygBkU+B7ex5UI/gKhaqexWev79uISfIv7XQCRNYO/jmD8rGLPyWLAb3KMRT6nd8Gt9bmUBi9+iX6tBdYfOY81Q=="],
|
||||
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.62.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.62.1", "@typescript-eslint/tsconfig-utils": "8.62.1", "@typescript-eslint/types": "8.62.1", "@typescript-eslint/visitor-keys": "8.62.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-xMcW9oP9u7fAMXYs9A65CVmtLQe2r//oXINHfi8HV+oiqhih17sbLdhXr4540YWlgpDKQdY854OL5ZrdCiQsAA=="],
|
||||
|
||||
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.63.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.63.0", "@typescript-eslint/types": "8.63.0", "@typescript-eslint/typescript-estree": "8.63.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-fUKaeAvrTuQg/Tgt3nliAUSZHJM6DlCcfyEmxCvlX8kieWSStBX+5O5Fnidtc3i2JrH+9c/GL4RY2iasd/GPTA=="],
|
||||
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.62.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.62.1", "@typescript-eslint/types": "8.62.1", "@typescript-eslint/typescript-estree": "8.62.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-sHtbPfuKNZCG+ih8SyjjucqRntSVmp8XgL5u6o9mAhiSn8ds5o/M/XdM0abweme2Tln3szOstOrZ9OXitvPh0g=="],
|
||||
|
||||
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.63.0", "", { "dependencies": { "@typescript-eslint/types": "8.63.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-UexrHGnGTpbuQHct2ExOc2ZcFbGUS9FOesCxxqdBGcpI1BxYu/LZ6U8Aq6/72XtF/qRBk9nhuGHFJIXXMhPMdw=="],
|
||||
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.62.1", "", { "dependencies": { "@typescript-eslint/types": "8.62.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-4g3BLxfdTMy8iZG0MaBkadnlRrCJ74cQiFbyEVMrkwIoqdyaXXQM22cotDvrl4x28wgIZ9rEJRoM+mmhSJpJ1g=="],
|
||||
|
||||
"@typespec/ts-http-runtime": ["@typespec/ts-http-runtime@0.3.6", "", { "dependencies": { "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "tslib": "^2.6.2" } }, "sha512-jIXhD0eWQ1JA6ln/5Dltyx22UxWNrw0hZmhy2rlv6m6KgF7kplHx3g0fzi09lNmTJQRR91OlemYp3xFnvDK9og=="],
|
||||
|
||||
@@ -2644,21 +2652,21 @@
|
||||
|
||||
"@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/coverage-v8": ["@vitest/coverage-v8@3.2.7", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "@bcoe/v8-coverage": "^1.0.2", "ast-v8-to-istanbul": "^0.3.3", "debug": "^4.4.1", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-lib-source-maps": "^5.0.6", "istanbul-reports": "^3.1.7", "magic-string": "^0.30.17", "magicast": "^0.3.5", "std-env": "^3.9.0", "test-exclude": "^7.0.1", "tinyrainbow": "^2.0.0" }, "peerDependencies": { "@vitest/browser": "3.2.7", "vitest": "3.2.7" }, "optionalPeers": ["@vitest/browser"] }, "sha512-NEGWJS2XNu2PfRLQwOO3CTKj1tTETxNBdk454vDxVBhxJYhPaA/eS0nAI0c+1El1P7a60z8+i+ZrQoGESweGKg=="],
|
||||
"@vitest/coverage-v8": ["@vitest/coverage-v8@3.2.6", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "@bcoe/v8-coverage": "^1.0.2", "ast-v8-to-istanbul": "^0.3.3", "debug": "^4.4.1", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-lib-source-maps": "^5.0.6", "istanbul-reports": "^3.1.7", "magic-string": "^0.30.17", "magicast": "^0.3.5", "std-env": "^3.9.0", "test-exclude": "^7.0.1", "tinyrainbow": "^2.0.0" }, "peerDependencies": { "@vitest/browser": "3.2.6", "vitest": "3.2.6" }, "optionalPeers": ["@vitest/browser"] }, "sha512-LsAdmUapA0qSN306d8+zOyawM0hFm2m2Hg9IwVNIKBm+qJV8cijiq2c+gxKZcB1HCfIWAy+0qEZDCUQA58A1cw=="],
|
||||
|
||||
"@vitest/expect": ["@vitest/expect@4.1.10", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.10", "@vitest/utils": "4.1.10", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA=="],
|
||||
"@vitest/expect": ["@vitest/expect@4.1.9", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.9", "@vitest/utils": "4.1.9", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA=="],
|
||||
|
||||
"@vitest/mocker": ["@vitest/mocker@4.1.10", "", { "dependencies": { "@vitest/spy": "4.1.10", "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-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow=="],
|
||||
"@vitest/mocker": ["@vitest/mocker@4.1.9", "", { "dependencies": { "@vitest/spy": "4.1.9", "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-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw=="],
|
||||
|
||||
"@vitest/pretty-format": ["@vitest/pretty-format@4.1.10", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q=="],
|
||||
"@vitest/pretty-format": ["@vitest/pretty-format@4.1.9", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A=="],
|
||||
|
||||
"@vitest/runner": ["@vitest/runner@4.1.10", "", { "dependencies": { "@vitest/utils": "4.1.10", "pathe": "^2.0.3" } }, "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg=="],
|
||||
"@vitest/runner": ["@vitest/runner@4.1.9", "", { "dependencies": { "@vitest/utils": "4.1.9", "pathe": "^2.0.3" } }, "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg=="],
|
||||
|
||||
"@vitest/snapshot": ["@vitest/snapshot@4.1.10", "", { "dependencies": { "@vitest/pretty-format": "4.1.10", "@vitest/utils": "4.1.10", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw=="],
|
||||
"@vitest/snapshot": ["@vitest/snapshot@4.1.9", "", { "dependencies": { "@vitest/pretty-format": "4.1.9", "@vitest/utils": "4.1.9", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA=="],
|
||||
|
||||
"@vitest/spy": ["@vitest/spy@4.1.10", "", {}, "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw=="],
|
||||
"@vitest/spy": ["@vitest/spy@4.1.9", "", {}, "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA=="],
|
||||
|
||||
"@vitest/utils": ["@vitest/utils@4.1.10", "", { "dependencies": { "@vitest/pretty-format": "4.1.10", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA=="],
|
||||
"@vitest/utils": ["@vitest/utils@4.1.9", "", { "dependencies": { "@vitest/pretty-format": "4.1.9", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA=="],
|
||||
|
||||
"@vladfrangu/async_event_emitter": ["@vladfrangu/async_event_emitter@2.4.7", "", {}, "sha512-Xfe6rpCTxSxfbswi/W/Pz7zp1WWSNn4A0eW4mLkQUewCrXXtMj31lCg+iQyTkh/CkusZSq9eDflu7tjEDXUY6g=="],
|
||||
|
||||
@@ -2702,9 +2710,9 @@
|
||||
|
||||
"@xterm/headless": ["@xterm/headless@5.5.0", "", {}, "sha512-5xXB7kdQlFBP82ViMJTwwEc3gKCLGKR/eoxQm4zge7GPBl86tCdI0IdPJjoKd8mUSFXz5V7i/25sfsEkP4j46g=="],
|
||||
|
||||
"@xyflow/react": ["@xyflow/react@12.11.2", "", { "dependencies": { "@xyflow/system": "0.0.79", "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-eLAlDWJfWnQEhJwGMjlWdAXO9eYllKpliUmPQlAmOLxz6mExXuzMVDUKLMquixgkrtmMFFtug3jGKmYYld12cA=="],
|
||||
"@xyflow/react": ["@xyflow/react@12.11.1", "", { "dependencies": { "@xyflow/system": "0.0.78", "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-L+zBoLGSXham0MnlY8QqjfR7/C5JNw0zxkaey5aZ5XmCgJBAdH4+WRIu8CR40d3l/BdU635V6YbhBK1jMo8/6Q=="],
|
||||
|
||||
"@xyflow/system": ["@xyflow/system@0.0.79", "", { "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-czLyOh91NF0hIzbNzwi8I6GlqG23BHh2435OddfI6uiaLH3xdrdygO93gqgH1Bv9mhy8XPFQJOBn1FTq4LvEWA=="],
|
||||
"@xyflow/system": ["@xyflow/system@0.0.78", "", { "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-lY0z2qP33fUhTva9Vaxrk0lqZta2pkbxB1trHAx1omnJqRtPvDlAQYV2r5fhS6AdpkulYmbNW0svy+A4/t4B/g=="],
|
||||
|
||||
"abbrev": ["abbrev@3.0.1", "", {}, "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg=="],
|
||||
|
||||
@@ -2726,7 +2734,7 @@
|
||||
|
||||
"agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
|
||||
|
||||
"ai": ["ai@6.0.219", "", { "dependencies": { "@ai-sdk/gateway": "3.0.143", "@ai-sdk/provider": "3.0.13", "@ai-sdk/provider-utils": "4.0.35", "@opentelemetry/api": "^1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-rtTDz99Rc9HsstSJ7YdO8DX7DQwS442N2vQ5jOopXDdo4qfkLrtIRpORdztFMOZxX/XjBzHRlvqF6eMg3cpyLA=="],
|
||||
"ai": ["ai@6.0.218", "", { "dependencies": { "@ai-sdk/gateway": "3.0.142", "@ai-sdk/provider": "3.0.13", "@ai-sdk/provider-utils": "4.0.35", "@opentelemetry/api": "^1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-HsyCUNaaYgX/b/kGOoYfKkqfT1HvpUKKDb8YkN1FKeCNZjKdqXLGY+cKBpYGIRAvsPuOHskxLxZ46cK1dTBWQQ=="],
|
||||
|
||||
"ai-sdk-provider-claude-code": ["ai-sdk-provider-claude-code@3.5.0", "", { "dependencies": { "@ai-sdk/provider": "^3.0.0", "@ai-sdk/provider-utils": "^4.0.1", "@anthropic-ai/claude-agent-sdk": "^0.3.170" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-7SZrTGkuR4G4zeNrjnJgDzYkrKZqzq7GUQJcFBKCxFH5dJpS9lbs+g9BCt7bwT1gDm4SAUmOQ0T5rKqcC2HkVQ=="],
|
||||
|
||||
@@ -2808,7 +2816,7 @@
|
||||
|
||||
"bare-events": ["bare-events@2.9.1", "", { "peerDependencies": { "bare-abort-controller": "*" }, "optionalPeers": ["bare-abort-controller"] }, "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg=="],
|
||||
|
||||
"bare-fs": ["bare-fs@4.7.3", "", { "dependencies": { "bare-events": "^2.5.4", "bare-path": "^3.0.0", "bare-stream": "^2.6.4", "bare-url": "^2.2.2", "fast-fifo": "^1.3.2" }, "peerDependencies": { "bare-buffer": "*" }, "optionalPeers": ["bare-buffer"] }, "sha512-xRgplks8SvcKkdlv2M6Z2LZmRsmqd+x0nXXGXeMEjwdibj1HSDrlnqBRLeYdMvsgCox7Bq0e+DHwfczOfsn6IA=="],
|
||||
"bare-fs": ["bare-fs@4.7.2", "", { "dependencies": { "bare-events": "^2.5.4", "bare-path": "^3.0.0", "bare-stream": "^2.6.4", "bare-url": "^2.2.2", "fast-fifo": "^1.3.2" }, "peerDependencies": { "bare-buffer": "*" }, "optionalPeers": ["bare-buffer"] }, "sha512-aTvMFUWkBmjzKtEQMDGGDNF8bkfpD5N1b/FCwt7A3wrU4t1o/e/85Wzkluh6JlODCjqVESYCkQCdTXqZ9G7VFg=="],
|
||||
|
||||
"bare-os": ["bare-os@3.9.3", "", {}, "sha512-fF4Q7QsyKVF5Rj0qvI8BgUNjqzC2JvQlpTaPLjVJVxYVUX5Zr9un+y3w1HmA4nNKdFmRBT8z/WmrjvXzXVerKQ=="],
|
||||
|
||||
@@ -2820,7 +2828,7 @@
|
||||
|
||||
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
|
||||
|
||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.42", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q=="],
|
||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.40", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw=="],
|
||||
|
||||
"basic-ftp": ["basic-ftp@5.3.1", "", {}, "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw=="],
|
||||
|
||||
@@ -2860,7 +2868,7 @@
|
||||
|
||||
"browser-stdout": ["browser-stdout@1.3.1", "", {}, "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw=="],
|
||||
|
||||
"browserslist": ["browserslist@4.28.5", "", { "dependencies": { "baseline-browser-mapping": "^2.10.42", "caniuse-lite": "^1.0.30001800", "electron-to-chromium": "^1.5.387", "node-releases": "^2.0.50", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-Cu2E6QejHWzuDMTkuwgpABFgDfZrXLQq5V13YOACZx4mFAG4IwGTbTfHPMr4WtxlHoXSM8FIuRwYYCz5XiabaQ=="],
|
||||
"browserslist": ["browserslist@4.28.4", "", { "dependencies": { "baseline-browser-mapping": "^2.10.38", "caniuse-lite": "^1.0.30001799", "electron-to-chromium": "^1.5.376", "node-releases": "^2.0.48", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw=="],
|
||||
|
||||
"buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
|
||||
|
||||
@@ -2904,7 +2912,7 @@
|
||||
|
||||
"camelize": ["camelize@1.0.1", "", {}, "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ=="],
|
||||
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001802", "", {}, "sha512-vmv8ub2xwTNmljSKf82mtCk5JH7hC+YgzLj3P5zotvA0tPQ9016tdNNOG8WRca1IxOnhSsivB+J0z5FeE5LOUw=="],
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001800", "", {}, "sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA=="],
|
||||
|
||||
"case-anything": ["case-anything@2.1.13", "", {}, "sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng=="],
|
||||
|
||||
@@ -2964,6 +2972,8 @@
|
||||
|
||||
"cline-agent-squad-plugin": ["cline-agent-squad-plugin@workspace:sdk/examples/plugins/agents-squad"],
|
||||
|
||||
"cline-xml-tool-calling-plugin": ["cline-xml-tool-calling-plugin@workspace:sdk/examples/plugins/xml-tool-calling"],
|
||||
|
||||
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
|
||||
|
||||
"clone": ["clone@2.1.2", "", {}, "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="],
|
||||
@@ -2988,7 +2998,7 @@
|
||||
|
||||
"color-support": ["color-support@1.1.3", "", { "bin": { "color-support": "bin.js" } }, "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="],
|
||||
|
||||
"color2k": ["color2k@2.0.4", "", {}, "sha512-OXAPGFRNeLFnUfqDtloYdxkwsJoIdXe28+bjbpJiPqyei2HPa3VHmMCWa0Qe62+U4Ftf9Hj7hRssOkxz7WiWbg=="],
|
||||
"color2k": ["color2k@2.0.3", "", {}, "sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog=="],
|
||||
|
||||
"colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="],
|
||||
|
||||
@@ -3246,7 +3256,7 @@
|
||||
|
||||
"eight-colors": ["eight-colors@1.3.3", "", {}, "sha512-4B54S2Qi4pJjeHmCbDIsveQZWQ/TSSQng4ixYJ9/SYHHpeS5nYK0pzcHvWzWUfRsvJQjwoIENhAwqg59thQceg=="],
|
||||
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.387", "", {}, "sha512-TaxwufTFDufvPEoXdhwVrA3UdFWBeWGkYoJ1K8ldF1xe6gKfth6iRNS5lTQ5JPNOHdGQm8PT1QYKUqFLCiUefQ=="],
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.384", "", {}, "sha512-g6KAKY1vkYsADvSPWvdJsuYT0ixdcu6lUtD9P/wJKGBEDlZVXh2AX42j1mPqqaQPDluWjara9ziQ7xqAeXCt5A=="],
|
||||
|
||||
"embla-carousel": ["embla-carousel@8.6.0", "", {}, "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA=="],
|
||||
|
||||
@@ -3476,7 +3486,7 @@
|
||||
|
||||
"gauge": ["gauge@5.0.2", "", { "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.3", "console-control-strings": "^1.1.0", "has-unicode": "^2.0.1", "signal-exit": "^4.0.1", "string-width": "^4.2.3", "strip-ansi": "^6.0.1", "wide-align": "^1.1.5" } }, "sha512-pMaFftXPtiGIHCJHdcUUx9Rby/rFT/Kkt3fIIGCs+9PMDIljSyRiqraTlxNtBReJRDfUefpa263RQ3vnp5G/LQ=="],
|
||||
|
||||
"gaxios": ["gaxios@7.1.6", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2" } }, "sha512-aIQ0QL8Or8vsUhHyXGA6AohOFRrAAiHhrvsAG6myzcSlfhxSXtnwXA/pRuQTilFgjhLe30swK5rg1d7E1f8Izw=="],
|
||||
"gaxios": ["gaxios@7.1.5", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2" } }, "sha512-5FZy72Rh8LhtjmvDrKkI+lVhrsQrVKVsItxMoDm5mNQE+xR0WVIIs+jzPSJgBvKVsLi24fZhXJIsNI0bihDzFg=="],
|
||||
|
||||
"gcp-metadata": ["gcp-metadata@8.1.2", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="],
|
||||
|
||||
@@ -3524,7 +3534,7 @@
|
||||
|
||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||
|
||||
"graphql": ["graphql@17.0.2", "", {}, "sha512-FRWbddMxfkjiB7z+aQDWIR+E34xo9I8c9mtK2RPv8PmMzKRvrdsreHL/Ui/TmwHJfhHChEtsFPyMHKI+xuarQQ=="],
|
||||
"graphql": ["graphql@17.0.1", "", {}, "sha512-8eWbg5Zcv/8o20nzEjHUGPTj20MLFJjc5kagbIPxbaeGxvFwpitJhemEC/k17n5+UD4M/9ea5rTuce78mELujQ=="],
|
||||
|
||||
"grpc-health-check": ["grpc-health-check@2.1.0", "", { "dependencies": { "@grpc/proto-loader": "^0.7.13" } }, "sha512-HH3WjwNtusMTEQAtRelFgsFyNcOdihvpjusNDIrGYfWG8tPNSHqELrSyriIjm70k65YSxetsKG1y4H1L5gi1wQ=="],
|
||||
|
||||
@@ -3594,7 +3604,7 @@
|
||||
|
||||
"highlight.js": ["highlight.js@11.11.1", "", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="],
|
||||
|
||||
"hono": ["hono@4.12.28", "", {}, "sha512-YwUvVpSF7m1yOblFPrU3Hbo8XhPheBoiyfGuII6z19LnOr6JpDnyyp7LFNrfV56wS8tpvtBFGRISHN02pDdLOA=="],
|
||||
"hono": ["hono@4.12.27", "", {}, "sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q=="],
|
||||
|
||||
"hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="],
|
||||
|
||||
@@ -3796,7 +3806,7 @@
|
||||
|
||||
"jsonfile": ["jsonfile@6.2.1", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q=="],
|
||||
|
||||
"jsonrepair": ["jsonrepair@3.15.0", "", { "bin": { "jsonrepair": "bin/cli.js" } }, "sha512-wy8OTjwsJwQRnQJkKnMJJ9vcytRdBPAgIF/Hy6+s1dAj42BHMKiyL8JzEieIl3JY7idt8eyHwBWTO8mh/+mtwA=="],
|
||||
"jsonrepair": ["jsonrepair@3.14.1", "", { "bin": { "jsonrepair": "bin/cli.js" } }, "sha512-NpGgMhmzG/fajkBEFlS9jZvMSGDvc2xN/9wNCHZ+Nx32GZfLRELU6UE6dQkebvrQUct9S+7bvnpX29NB36Qbdw=="],
|
||||
|
||||
"jsonwebtoken": ["jsonwebtoken@9.0.3", "", { "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g=="],
|
||||
|
||||
@@ -4148,7 +4158,7 @@
|
||||
|
||||
"nice-grpc-common": ["nice-grpc-common@2.0.3", "", { "dependencies": { "ts-error": "^1.0.6" } }, "sha512-MEhnD3JMah0mgyivpb9hpRDbOBuXBxI/TVO+OK1h6rC97WM42HsPMR+zzRNQ0C5BqYJTw1nyWiQRD0DucO+pjQ=="],
|
||||
|
||||
"node-abi": ["node-abi@3.94.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-W5ZNO5KRPB5TkYmGVD9F6YqhsglXJzE6etpbmT+f6EQElhiX/UTG551cnsRGvLG3fyZEg9HwaDmNmj5nwJ4z9g=="],
|
||||
"node-abi": ["node-abi@3.93.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-Cu6yUpX5Iavugm8BeX7c0wgU9CvOqfd1yM6A1d2q2ZMjym7GjpASv2GdRcTq3Fx+Sb5OgBkEEpw4VnAbY6Y5RA=="],
|
||||
|
||||
"node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
|
||||
|
||||
@@ -4312,7 +4322,7 @@
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
|
||||
"picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
|
||||
|
||||
"pino": ["pino@10.3.1", "", { "dependencies": { "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^3.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^4.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg=="],
|
||||
|
||||
@@ -4346,13 +4356,13 @@
|
||||
|
||||
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
|
||||
|
||||
"posthog-js": ["posthog-js@1.396.9", "", { "dependencies": { "@posthog/core": "^1.39.6", "@posthog/types": "^1.392.1", "core-js": "^3.38.1", "dompurify": "^3.3.2", "fflate": "^0.4.8", "preact": "^10.29.2", "query-selector-shadow-dom": "^1.0.1", "web-vitals": "^5.3.0" } }, "sha512-tORn1rcL78MauLsibfID2IcXDqn9+r6cHswOdRWwVOJOVjesIdkvQwV2qwXbNOXqqlMl0jox0PErTTy5cv5TUA=="],
|
||||
"posthog-js": ["posthog-js@1.396.4", "", { "dependencies": { "@posthog/core": "^1.39.3", "@posthog/types": "^1.392.0", "core-js": "^3.38.1", "dompurify": "^3.3.2", "fflate": "^0.4.8", "preact": "^10.29.2", "query-selector-shadow-dom": "^1.0.1", "web-vitals": "^5.3.0" } }, "sha512-PycBmwKQD1T7YFYrGRb8rjQET/UVnexgUy8gVe6UBEhwHXEIhZF4na5VakJbn4zu1wg4tzjt8r7PA4VLu6bDjg=="],
|
||||
|
||||
"posthog-node": ["posthog-node@5.39.4", "", { "dependencies": { "@posthog/core": "^1.39.5" }, "peerDependencies": { "rxjs": "^7.0.0" }, "optionalPeers": ["rxjs"] }, "sha512-+fCQ7htBFRQQFbIzl1T0TA7bDwYyaB9XP308ZFMCUoB5LzTzOFxBa6TYVrxdH/VQl43WXTp6sf0QsG2Z4XlNBg=="],
|
||||
"posthog-node": ["posthog-node@5.39.2", "", { "dependencies": { "@posthog/core": "^1.39.3" }, "peerDependencies": { "rxjs": "^7.0.0" }, "optionalPeers": ["rxjs"] }, "sha512-5piMedjlQ2x+UKLvHWTC5ls5/T1dDZKE1Pu5AKkYh9EkbZOjvu0cac6lWFB7mgbGkKQ0I1bhbjDx1QAYRJ7Unw=="],
|
||||
|
||||
"powershell-utils": ["powershell-utils@0.1.0", "", {}, "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A=="],
|
||||
|
||||
"preact": ["preact@10.29.4", "", {}, "sha512-GMpwh9+NJ8tSmqwIaVyFRQkiKfBEzQ+k7r7tle4W+kaJ+7wJiB9hFz9BixAomMtenPPSBfM4bZhXozGxhf0uFQ=="],
|
||||
"preact": ["preact@10.29.3", "", {}, "sha512-D9NL1GAnJZhc3RndVs4gDdxEeU9TcHgywMrhhOsnpdlvFjdbx0gAsLUnH6JEhlJH5giL7Tx5biWPUSEXE/HPzw=="],
|
||||
|
||||
"prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="],
|
||||
|
||||
@@ -4434,7 +4444,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.81.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-ocbmr2p5KBMoAfj4WCUvped33lVi1Kd5DuDUvQDnB6VEAacOjPI/jMbtDdbhco4y9ct4xUuCmMY0b/C9L0QHjw=="],
|
||||
"react-hook-form": ["react-hook-form@7.80.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-4P+fk6oXsxY+6xSj7Euhc2sumQD8zQqCuVHoJwoyp9EchP+IUW9OESB7uHFJOKsIBQ4MQqYE84INJFqUCYNoOg=="],
|
||||
|
||||
"react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
|
||||
|
||||
@@ -4614,7 +4624,7 @@
|
||||
|
||||
"setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
|
||||
|
||||
"shadcn": ["shadcn@4.13.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", "kleur": "^4.1.5", "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", "undici": "^7.27.2", "validate-npm-package-name": "^7.0.1", "zod": "^3.24.1", "zod-to-json-schema": "^3.24.6" }, "bin": { "shadcn": "dist/index.js" } }, "sha512-5fuJ4jI/GcPeA/iTL4cJivCZuYQGXz/N3bIzyd+Gd/FM6xUCy2MxGG+LaDQuw2cjNy9zGPSFPTEmI048UwPTZA=="],
|
||||
"shadcn": ["shadcn@4.12.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", "kleur": "^4.1.5", "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", "undici": "^7.27.2", "validate-npm-package-name": "^7.0.1", "zod": "^3.24.1", "zod-to-json-schema": "^3.24.6" }, "bin": { "shadcn": "dist/index.js" } }, "sha512-o781ieQziCnXH2FKsEqxp1fnbHdbgAPO9inTSPeZ59hQfsZXuMGp3ul8oFSV5KQS4nbUK9b+DrDE6C7OvfKKQQ=="],
|
||||
|
||||
"shallow-clone": ["shallow-clone@3.0.1", "", { "dependencies": { "kind-of": "^6.0.2" } }, "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA=="],
|
||||
|
||||
@@ -4626,7 +4636,7 @@
|
||||
|
||||
"shell-quote": ["shell-quote@1.9.0", "", {}, "sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA=="],
|
||||
|
||||
"shiki": ["shiki@4.3.1", "", { "dependencies": { "@shikijs/core": "4.3.1", "@shikijs/engine-javascript": "4.3.1", "@shikijs/engine-oniguruma": "4.3.1", "@shikijs/langs": "4.3.1", "@shikijs/themes": "4.3.1", "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oR+qDVi2OjX1tmDpyv+3KviX01KzO6Af+0NNnKnsp9491UEGz2YpxTuJboS/6VhYpTdqzmuJBuiTlrAWWJAssw=="],
|
||||
"shiki": ["shiki@4.3.0", "", { "dependencies": { "@shikijs/core": "4.3.0", "@shikijs/engine-javascript": "4.3.0", "@shikijs/engine-oniguruma": "4.3.0", "@shikijs/langs": "4.3.0", "@shikijs/themes": "4.3.0", "@shikijs/types": "4.3.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-NKKjWzR6LIGL3sXBrWDw9sDS9cxx42/DkysaNqJEeOWE8Kix5gpak0bc00OfDVEO4oyXSyz8+aRaqKoBD1yo7A=="],
|
||||
|
||||
"shimmer": ["shimmer@1.2.1", "", {}, "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw=="],
|
||||
|
||||
@@ -4778,7 +4788,7 @@
|
||||
|
||||
"symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="],
|
||||
|
||||
"systeminformation": ["systeminformation@5.31.13", "", { "os": "!aix", "bin": { "systeminformation": "lib/cli.js" } }, "sha512-iUJXJoKzm4vtLSeT3nwe2s9QjoJAxHg7wYJ0KaQ54Xy2u9jsTq0ULWQQ0+T72FXjX2XnGqubazNx9lUfng7ELw=="],
|
||||
"systeminformation": ["systeminformation@5.31.11", "", { "os": "!aix", "bin": { "systeminformation": "lib/cli.js" } }, "sha512-I6O7iaUj23AXRgCPDDnvi3xHvdOLp4+1YMbF+X194lJwY1NeWojgHJPhslVKcmTtrLTguRk3QJK+xEdTiI3P0w=="],
|
||||
|
||||
"tabbable": ["tabbable@6.5.0", "", {}, "sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA=="],
|
||||
|
||||
@@ -4890,7 +4900,7 @@
|
||||
|
||||
"ts-poet": ["ts-poet@6.12.0", "", { "dependencies": { "dprint-node": "^1.0.8" } }, "sha512-xo+iRNMWqyvXpFTaOAvLPA5QAWO6TZrSUs5s4Odaya3epqofBu/fMLHEWl8jPmjhA0s9sgj9sNvF1BmaQlmQkA=="],
|
||||
|
||||
"ts-proto": ["ts-proto@2.12.0", "", { "dependencies": { "@bufbuild/protobuf": "^2.10.2", "case-anything": "^2.1.13", "ts-poet": "^6.12.0", "ts-proto-descriptors": "2.1.0" }, "bin": { "protoc-gen-ts_proto": "protoc-gen-ts_proto" } }, "sha512-ezMxg57ZiK/ZTW14U7y38+qyWHJr8cn8ELKuppANER666YnUteuNFO/mM1qI+9/wAHoTAfRadnIwtVdp1xw0jQ=="],
|
||||
"ts-proto": ["ts-proto@2.11.10", "", { "dependencies": { "@bufbuild/protobuf": "^2.10.2", "case-anything": "^2.1.13", "ts-poet": "^6.12.0", "ts-proto-descriptors": "2.1.0" }, "bin": { "protoc-gen-ts_proto": "protoc-gen-ts_proto" } }, "sha512-7mvz2RbOZc0J/+x8biIcVHJ0nx7xjH79vtoEnkpKT5l8yFF5ERe03dee2W3CbB5vb1QgnbQXGzeBGGryJ+Q9EA=="],
|
||||
|
||||
"ts-proto-descriptors": ["ts-proto-descriptors@2.1.0", "", { "dependencies": { "@bufbuild/protobuf": "^2.0.0" } }, "sha512-S5EZYEQ6L9KLFfjSRpZWDIXDV/W7tAj8uW7pLsihIxyr62EAVSiKuVPwE8iWnr849Bqa53enex1jhDUcpgquzA=="],
|
||||
|
||||
@@ -4920,7 +4930,7 @@
|
||||
|
||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"typescript-eslint": ["typescript-eslint@8.63.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.63.0", "@typescript-eslint/parser": "8.63.0", "@typescript-eslint/typescript-estree": "8.63.0", "@typescript-eslint/utils": "8.63.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-xgwXyzG4sK9ALkBxbyGkTMMOS+imnW65iPhxCQMK83KhxyoDNW7l+IDqEf9vMdoUidHpOoS967RCq4eMiTexwQ=="],
|
||||
"typescript-eslint": ["typescript-eslint@8.62.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.62.1", "@typescript-eslint/parser": "8.62.1", "@typescript-eslint/typescript-estree": "8.62.1", "@typescript-eslint/utils": "8.62.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-vymnnM5g0AKQDSAyfP12nMIBvgwgA42syg74kkuZ4x1VuTzwQKwc5h9rGxeShCjny5o+zWAb6OEoz7XLgrIkIw=="],
|
||||
|
||||
"uc.micro": ["uc.micro@1.0.6", "", {}, "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="],
|
||||
|
||||
@@ -5016,11 +5026,11 @@
|
||||
|
||||
"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.1.3", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.16", "rolldown": "~1.1.3", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.3.0", "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-Ds+gBRbj0lwRO2Y5hwnUBdxSwlAve9LeRyU4sNnAr0ewW0gWF0n5bgXgUzbgZ49MV9BVUAQUFYVcDUcilUExMA=="],
|
||||
"vite": ["vite@8.1.2", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.16", "rolldown": "~1.1.3", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.3.0", "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-6YYPbRXTxx6bRXmOn7XdnQAy5DQNHhDgtjhDHI13oe4pY93kkcdGJWxpGwOm++/Wh0QpQhDrpIoVMrmrsI5AGQ=="],
|
||||
|
||||
"vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="],
|
||||
|
||||
"vitest": ["vitest@4.1.10", "", { "dependencies": { "@vitest/expect": "4.1.10", "@vitest/mocker": "4.1.10", "@vitest/pretty-format": "4.1.10", "@vitest/runner": "4.1.10", "@vitest/snapshot": "4.1.10", "@vitest/spy": "4.1.10", "@vitest/utils": "4.1.10", "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.10", "@vitest/browser-preview": "4.1.10", "@vitest/browser-webdriverio": "4.1.10", "@vitest/coverage-istanbul": "4.1.10", "@vitest/coverage-v8": "4.1.10", "@vitest/ui": "4.1.10", "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-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw=="],
|
||||
"vitest": ["vitest@4.1.9", "", { "dependencies": { "@vitest/expect": "4.1.9", "@vitest/mocker": "4.1.9", "@vitest/pretty-format": "4.1.9", "@vitest/runner": "4.1.9", "@vitest/snapshot": "4.1.9", "@vitest/spy": "4.1.9", "@vitest/utils": "4.1.9", "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.9", "@vitest/browser-preview": "4.1.9", "@vitest/browser-webdriverio": "4.1.9", "@vitest/coverage-istanbul": "4.1.9", "@vitest/coverage-v8": "4.1.9", "@vitest/ui": "4.1.9", "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-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ=="],
|
||||
|
||||
"voca": ["voca@1.4.1", "", {}, "sha512-NJC/BzESaHT1p4B5k4JykxedeltmNbau4cummStd4RjFojgq/kLew5TzYge9N2geeWyI2w8T30wUET5v+F7ZHA=="],
|
||||
|
||||
@@ -5112,7 +5122,7 @@
|
||||
|
||||
"yocto-queue": ["yocto-queue@1.2.2", "", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="],
|
||||
|
||||
"yocto-spinner": ["yocto-spinner@1.2.1", "", { "dependencies": { "yoctocolors": "^2.1.1" } }, "sha512-9cbFWLhbiZp+820O4pkHGNncI7+MrUGzBOjw8NMG+ewsY+aG0DdEXnr19Smxao32YOjLZRMdn1UtaxcrXOYOIg=="],
|
||||
"yocto-spinner": ["yocto-spinner@1.2.0", "", { "dependencies": { "yoctocolors": "^2.1.1" } }, "sha512-Yw0hUB6UA3o4YUgKy3oSe9a4cxoaZ9sBfYDw+JSxo6Id0KoJGoxzPA24qqUXYKBWABs/zDSGTz9kww7t3F0XGw=="],
|
||||
|
||||
"yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="],
|
||||
|
||||
@@ -5216,8 +5226,6 @@
|
||||
|
||||
"@heroui/use-is-mobile/@react-aria/ssr": ["@react-aria/ssr@3.9.10", "", { "dependencies": { "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ=="],
|
||||
|
||||
"@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.11.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA=="],
|
||||
|
||||
"@jest/types/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="],
|
||||
|
||||
"@jest/types/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||
@@ -5684,9 +5692,9 @@
|
||||
|
||||
"@streamdown/code/shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA=="],
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA=="],
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="],
|
||||
|
||||
@@ -5758,7 +5766,7 @@
|
||||
|
||||
"body-parser/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="],
|
||||
|
||||
"body-parser/iconv-lite": ["iconv-lite@0.7.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ=="],
|
||||
"body-parser/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||
|
||||
"chai/assertion-error": ["assertion-error@1.1.0", "", {}, "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw=="],
|
||||
|
||||
@@ -5970,7 +5978,7 @@
|
||||
|
||||
"onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
|
||||
|
||||
"open-graph-scraper/iconv-lite": ["iconv-lite@0.7.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ=="],
|
||||
"open-graph-scraper/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||
|
||||
"ora/cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="],
|
||||
|
||||
@@ -6100,7 +6108,7 @@
|
||||
|
||||
"radix-ui/@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.7" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1wNZBggTDK3GRuuQ6nP4k2yi7a6l7I5qbMPbZcRsrGsGVead/f/d5FhEzUvqFs0bcrDLx7n1zKQ3JvLR6whaaw=="],
|
||||
|
||||
"raw-body/iconv-lite": ["iconv-lite@0.7.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ=="],
|
||||
"raw-body/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||
|
||||
"rc/strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
|
||||
|
||||
@@ -6202,6 +6210,8 @@
|
||||
|
||||
"unzipper/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
||||
|
||||
"vaul/@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.18", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-dismissable-layer": "1.1.14", "@radix-ui/react-focus-guards": "1.1.4", "@radix-ui/react-focus-scope": "1.1.11", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-portal": "1.1.13", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-slot": "1.3.0", "@radix-ui/react-use-controllable-state": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-apa28mldjMgORmE6g/w3sCcA0Y9UAVeeDVoozN4i7kOw12mLl9RBchfzK3Nn6qxOWjrZhK1Lfy7f07kyzxtnBw=="],
|
||||
|
||||
"verror/core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="],
|
||||
|
||||
"vite-node/es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="],
|
||||
@@ -6234,7 +6244,7 @@
|
||||
|
||||
"webview-ui/vite": ["vite@7.3.6", "", { "dependencies": { "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.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", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg=="],
|
||||
|
||||
"webview-ui/vitest": ["vitest@3.2.7", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.7", "@vitest/mocker": "3.2.7", "@vitest/pretty-format": "^3.2.7", "@vitest/runner": "3.2.7", "@vitest/snapshot": "3.2.7", "@vitest/spy": "3.2.7", "@vitest/utils": "3.2.7", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.7", "@vitest/ui": "3.2.7", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "./vitest.mjs" } }, "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg=="],
|
||||
"webview-ui/vitest": ["vitest@3.2.6", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.6", "@vitest/mocker": "3.2.6", "@vitest/pretty-format": "^3.2.6", "@vitest/runner": "3.2.6", "@vitest/snapshot": "3.2.6", "@vitest/spy": "3.2.6", "@vitest/utils": "3.2.6", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.6", "@vitest/ui": "3.2.6", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-xejya+bT/j/+R/AGa1XOfRxLmNUlLtlwjRsFUILF+xHfzElmGcmFydy2gqqIrd62ptIEfwVMofd19uNWD9L7Nw=="],
|
||||
|
||||
"wide-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
@@ -6776,7 +6786,7 @@
|
||||
|
||||
"googleapis-common/gaxios/rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="],
|
||||
|
||||
"googleapis-common/google-auth-library/gaxios": ["gaxios@7.1.6", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2" } }, "sha512-aIQ0QL8Or8vsUhHyXGA6AohOFRrAAiHhrvsAG6myzcSlfhxSXtnwXA/pRuQTilFgjhLe30swK5rg1d7E1f8Izw=="],
|
||||
"googleapis-common/google-auth-library/gaxios": ["gaxios@7.1.5", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2" } }, "sha512-5FZy72Rh8LhtjmvDrKkI+lVhrsQrVKVsItxMoDm5mNQE+xR0WVIIs+jzPSJgBvKVsLi24fZhXJIsNI0bihDzFg=="],
|
||||
|
||||
"hast-to-hyperscript/style-to-object/inline-style-parser": ["inline-style-parser@0.1.1", "", {}, "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q=="],
|
||||
|
||||
@@ -6978,6 +6988,28 @@
|
||||
|
||||
"unzipper/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
||||
|
||||
"vaul/@radix-ui/react-dialog/@radix-ui/primitive": ["@radix-ui/primitive@1.1.4", "", {}, "sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ=="],
|
||||
|
||||
"vaul/@radix-ui/react-dialog/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="],
|
||||
|
||||
"vaul/@radix-ui/react-dialog/@radix-ui/react-context": ["@radix-ui/react-context@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg=="],
|
||||
|
||||
"vaul/@radix-ui/react-dialog/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-effect-event": "0.0.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-4lUhWTWAjbDIqFrAPWJ3WqBOpO5YchVZ88X3nh6H9Lu5AFi5nCUeTPj3D8FSDmabmFeRe9ME0BDA4MwKTha5GQ=="],
|
||||
|
||||
"vaul/@radix-ui/react-dialog/@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q=="],
|
||||
|
||||
"vaul/@radix-ui/react-dialog/@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.11", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Mn88Vg2whaRocGJNOH+DKFqYm6ySFPQaiwHNxZPyjn99B52KAEJWWY9NP83+nWdk2HM3rdov+STu9AG471Rt9w=="],
|
||||
|
||||
"vaul/@radix-ui/react-dialog/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="],
|
||||
|
||||
"vaul/@radix-ui/react-dialog/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.13", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-z3oXfmaHLJTF1wktbjgD6cn9jiEbq3WSondB10LIuIt2m2Ym4iJlrW04/euMwENDdWDdE7z+OuY7Qyp1YpRSwA=="],
|
||||
|
||||
"vaul/@radix-ui/react-dialog/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.6", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ=="],
|
||||
|
||||
"vaul/@radix-ui/react-dialog/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.7", "", { "dependencies": { "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ=="],
|
||||
|
||||
"vaul/@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA=="],
|
||||
|
||||
"webview-ui/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
||||
|
||||
"webview-ui/@vitejs/plugin-react-swc/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="],
|
||||
@@ -6990,19 +7022,19 @@
|
||||
|
||||
"webview-ui/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],
|
||||
|
||||
"webview-ui/vitest/@vitest/expect": ["@vitest/expect@3.2.7", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.7", "@vitest/utils": "3.2.7", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-E8eBXaKibuvH2pSZErOjdVb5vF4PbKYcrnluBTYxEk1l/VhhwZg1kZQsdtjq+CsF5CFydf2Rdkz7jDHKSisi3w=="],
|
||||
"webview-ui/vitest/@vitest/expect": ["@vitest/expect@3.2.6", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.6", "@vitest/utils": "3.2.6", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-1+7q9BtaKzEmO+fmNT3kYvoNn5Y71XWAx2Q5HRim4tTVRQVRv4uJFAQ5FbK0OPUeNP/WmVCpxYxoJdvuHVjzBQ=="],
|
||||
|
||||
"webview-ui/vitest/@vitest/mocker": ["@vitest/mocker@3.2.7", "", { "dependencies": { "@vitest/spy": "3.2.7", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA=="],
|
||||
"webview-ui/vitest/@vitest/mocker": ["@vitest/mocker@3.2.6", "", { "dependencies": { "@vitest/spy": "3.2.6", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-EZOrpDbkKotFAP7wPAQV1UIyoGOk4oX7ynWhBhLB7v+meMHbQhU16oPpIYGTTe4oFlhpryGpgpcZP/sin3hYuw=="],
|
||||
|
||||
"webview-ui/vitest/@vitest/pretty-format": ["@vitest/pretty-format@3.2.7", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA=="],
|
||||
"webview-ui/vitest/@vitest/pretty-format": ["@vitest/pretty-format@3.2.6", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-lb7XXXzmm2h2ASzFnRvQpDo6onT1NmMJA3tkGTWiBFtRJ9lxGY3d3mm/Apt36gej2bkkOVLL/yTOtufDaFa/jA=="],
|
||||
|
||||
"webview-ui/vitest/@vitest/runner": ["@vitest/runner@3.2.7", "", { "dependencies": { "@vitest/utils": "3.2.7", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-sB9y4ovltoQP+WaUPwmSxO9WIg9Ig694Di5PalVPsYHklAdE027mehpWF2SQSVq+k6sFgaivbTjTJwZLSHbedA=="],
|
||||
"webview-ui/vitest/@vitest/runner": ["@vitest/runner@3.2.6", "", { "dependencies": { "@vitest/utils": "3.2.6", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-HYcoSj1w5tcgUnzoF0HcyaAQjpA1gj9ftUJ7iSJSuipc02jW9gKkigwZbjFldAfYHA1fa8UZVRftdMY5msWM9Q=="],
|
||||
|
||||
"webview-ui/vitest/@vitest/snapshot": ["@vitest/snapshot@3.2.7", "", { "dependencies": { "@vitest/pretty-format": "3.2.7", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g=="],
|
||||
"webview-ui/vitest/@vitest/snapshot": ["@vitest/snapshot@3.2.6", "", { "dependencies": { "@vitest/pretty-format": "3.2.6", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-H+ZjNTWGpObenh0YnlBctAPnJSI20P81PL8BPzWpx54YXLLTm8hEsWawtcYLMrwvpK48hGxLLbCS+1KRXhsKhw=="],
|
||||
|
||||
"webview-ui/vitest/@vitest/spy": ["@vitest/spy@3.2.7", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ=="],
|
||||
"webview-ui/vitest/@vitest/spy": ["@vitest/spy@3.2.6", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-oq6BbH68WzcWmwtBrU9nqLeaXTR4XwJF7FSLkKEZo4i6eoXcrxjcwSuTvWBIRUTC6VC72nXYunzqgZA+IKdtxg=="],
|
||||
|
||||
"webview-ui/vitest/@vitest/utils": ["@vitest/utils@3.2.7", "", { "dependencies": { "@vitest/pretty-format": "3.2.7", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw=="],
|
||||
"webview-ui/vitest/@vitest/utils": ["@vitest/utils@3.2.6", "", { "dependencies": { "@vitest/pretty-format": "3.2.6", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-lI23nIs4bnT3T8NIoh+vFaz5s2/DdP0Jgt2jxwgWljvwn82cLJtyi/If+fjFyoLMGIOz0U/fKvWE0d4jsNQEfg=="],
|
||||
|
||||
"webview-ui/vitest/chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="],
|
||||
|
||||
@@ -7224,6 +7256,10 @@
|
||||
|
||||
"test-exclude/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
||||
|
||||
"vaul/@radix-ui/react-dialog/@radix-ui/react-dismissable-layer/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw=="],
|
||||
|
||||
"vaul/@radix-ui/react-dialog/@radix-ui/react-focus-scope/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw=="],
|
||||
|
||||
"webview-ui/vitest/@vitest/utils/loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="],
|
||||
|
||||
"webview-ui/vitest/chai/check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="],
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
# Cline SDK Changelog
|
||||
|
||||
## 0.0.60
|
||||
|
||||
- Fixed an issue where a transient network or server error during token refresh could log you out — transient failures no longer clear your credentials
|
||||
- Added the ClinePass usage-limit error so limit-reached responses are surfaced clearly
|
||||
- Session id is now preserved when continuing within the same session
|
||||
- Fixed infinite loading when initializing a task with an image
|
||||
- Hardened compaction budget handling
|
||||
- Added telemetry for auth-refresh outcomes and Cline credential lifecycle debug logging
|
||||
|
||||
## 0.0.59
|
||||
|
||||
- You can now select Cline free models on the ClinePass provider
|
||||
|
||||
@@ -24,6 +24,7 @@ What a plugin can do:
|
||||
| [openrouter-provider.ts](./openrouter-provider.ts) | Custom model provider via `registerProvider` | Registers an OpenAI-compatible model provider (pointed at OpenRouter) plus its model catalog so the agent can run inference against it. Swap the base URL, API key env var, and models to add any OpenAI-compatible endpoint Cline does not bundle. Requires `OPENROUTER_API_KEY`. |
|
||||
| [typescript-lsp/](./typescript-lsp/) | `goto_definition` tool powered by the TypeScript Language Service | Adds `goto_definition(file, line)` for TypeScript/JavaScript projects. It loads the target project’s own TypeScript version, finds identifiers on a line, and resolves definitions through imports, re-exports, aliases, and other language-service semantics. |
|
||||
| [agents-squad/](./agents-squad/) | Multi-agent team — spin up subagents with their own models and personalities | Adds tools for starting, messaging, polling, and coordinating background subagents. It includes bundled agent presets, skill discovery/loading, and a shared handoff store for passing notes between subagents in the same conversation. |
|
||||
| [xml-tool-calling/](./xml-tool-calling/) | Legacy-style XML tool calling via rules + `beforeModel`/`afterModel` | Replaces native function calling with the XML tag format the legacy Cline extension used — for local/weak models that fumble native tool schemas. A rule adds the XML instructions, `beforeModel` strips tool schemas and injects per-turn tool docs into the provider-bound messages, and `afterModel` parses XML tool uses from assistant text back into native tool calls. |
|
||||
|
||||
The runtime-hook variant of compaction lives in [../hooks/custom-compaction-hook.example.ts](../hooks/custom-compaction-hook.example.ts).
|
||||
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
# XML Tool Calling
|
||||
|
||||
Drives Cline's tools through XML tags in plain assistant text — the format the
|
||||
legacy Cline extension used before native function calling — instead of
|
||||
provider-native tool schemas. Local and weaker models that fumble native tool
|
||||
calling tend to handle this format far better.
|
||||
|
||||
## How it works
|
||||
|
||||
The plugin is a pure translation shim at the model boundary. Internal session
|
||||
state stays in native form; only the provider-bound request is translated, so
|
||||
approvals, tool executors, completion tools, events, and persistence all work
|
||||
exactly as they do with native tool calling.
|
||||
|
||||
A registered rule adds the static `TOOL USE` instructions (the XML format and
|
||||
usage guidelines) to the system prompt. Then, per model call:
|
||||
|
||||
1. **`beforeModel`** strips the native tool schemas from the request
|
||||
(`tools: []`), injects a `TOOL DOCUMENTATION` block into the
|
||||
provider-bound first user message with per-tool XML docs generated from
|
||||
the live tool registry (including tools contributed by other plugins),
|
||||
and rewrites prior turns in the provider-bound history — native tool
|
||||
calls become XML text, tool results become plain user messages. The docs
|
||||
can't live in the rule: rules are resolved before the effective tool set
|
||||
(mode filtering, tool policies, other plugins' tools) is knowable, and
|
||||
the set can change between runs.
|
||||
2. The model replies with tool uses as XML tags:
|
||||
|
||||
```
|
||||
I'll read that file.
|
||||
<read_files>
|
||||
<paths>["src/main.ts"]</paths>
|
||||
</read_files>
|
||||
```
|
||||
|
||||
3. **`afterModel`** parses the XML out of the assistant text and replaces the
|
||||
message with one carrying native `tool-call` parts, which the runtime then
|
||||
executes through the ordinary tool pipeline.
|
||||
|
||||
Parameter values are coerced to the tool's JSON Schema types: numbers,
|
||||
booleans, and JSON for `array`/`object` params. Values that fail coercion pass
|
||||
through as raw strings so the tool's own input validation produces an error
|
||||
the model can react to.
|
||||
|
||||
The parser is a port of the legacy extension's `parseAssistantMessageV2`,
|
||||
generalized from a fixed tool list to schema-derived tool and parameter names.
|
||||
It keeps the legacy recovery trick for parameter values that contain their own
|
||||
closing tag (e.g. file content containing `</content>`), generalized to every
|
||||
parameter.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
cline plugin install ./sdk/examples/plugins/xml-tool-calling
|
||||
cline -i "..."
|
||||
```
|
||||
|
||||
Or from an SDK host, pass it via `extensions`:
|
||||
|
||||
```typescript
|
||||
import xmlToolCalling from "./sdk/examples/plugins/xml-tool-calling/index.ts";
|
||||
|
||||
await host.start({
|
||||
config: {
|
||||
// a local model that struggles with native tool calling
|
||||
providerId: "ollama",
|
||||
modelId: "qwen3:8b",
|
||||
extensions: [xmlToolCalling],
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
Requires an SDK build where `afterModel` hook results support `message`
|
||||
replacement.
|
||||
|
||||
## Caveats
|
||||
|
||||
- **Streaming**: text streams as raw `assistant-text-delta` events before
|
||||
`afterModel` runs, so live UIs show the XML while it streams. The final
|
||||
persisted message is clean (tool calls become native parts).
|
||||
- **Unclosed tool uses** (max-tokens truncation, malformed XML) are kept as
|
||||
raw text and not executed.
|
||||
- **Plain-text replies end the run**, same as a native model turn without tool
|
||||
calls. Pair with a completion policy that requires a completion tool if you
|
||||
want the runtime to nudge the model instead.
|
||||
- Tool results are rendered as text; image outputs are JSON-stringified rather
|
||||
than passed as image blocks.
|
||||
|
||||
## Test
|
||||
|
||||
```bash
|
||||
bun test
|
||||
```
|
||||
|
||||
Covers the parser, prompt generation, schema coercion, history rewriting, and
|
||||
an end-to-end run through `AgentRuntime` with a scripted model.
|
||||
@@ -0,0 +1,273 @@
|
||||
/**
|
||||
* XML tool calling for models without reliable native function calling.
|
||||
*
|
||||
* The legacy Cline extension drove tools through XML tags in plain assistant
|
||||
* text — a format that weak/local models handle far better than native tool
|
||||
* schemas. This plugin recreates that mode on the SDK runtime as a pure
|
||||
* translation shim at the model boundary:
|
||||
*
|
||||
* - A registered rule adds the static XML "TOOL USE" instructions to the
|
||||
* system prompt.
|
||||
* - `beforeModel` strips the native tool schemas from the provider request,
|
||||
* injects per-turn "TOOL DOCUMENTATION" (generated from the live tool
|
||||
* registry) into the provider-bound first user message, and rewrites prior
|
||||
* tool calls/results in history into the XML wire format.
|
||||
* - `afterModel` parses XML tool uses out of the assistant's text and
|
||||
* replaces the message with one carrying native `tool-call` parts.
|
||||
*
|
||||
* Everything downstream — approval hooks, tool executors, completion tools,
|
||||
* events, persistence — sees ordinary native tool calls. Internal session
|
||||
* state stays in native form; only the provider-bound request is translated.
|
||||
*/
|
||||
|
||||
import type { AgentPlugin } from "@cline/core";
|
||||
import {
|
||||
buildXmlToolDocs,
|
||||
coerceToolInput,
|
||||
formatToolResultText,
|
||||
parseAssistantXml,
|
||||
serializeToolCallXml,
|
||||
toXmlToolSpecs,
|
||||
XML_TOOL_CALLING_RULE,
|
||||
type XmlToolSpec,
|
||||
} from "./xml-format.ts";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Runtime types, derived structurally from the plugin contract
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
type RuntimeHooks = NonNullable<AgentPlugin["hooks"]>;
|
||||
type BeforeModelContext = Parameters<
|
||||
NonNullable<RuntimeHooks["beforeModel"]>
|
||||
>[0];
|
||||
type AfterModelContext = Parameters<NonNullable<RuntimeHooks["afterModel"]>>[0];
|
||||
type RuntimeMessage = BeforeModelContext["request"]["messages"][number];
|
||||
type RuntimeMessagePart = RuntimeMessage["content"][number];
|
||||
|
||||
function isInsideMarkdownFence(text: string): boolean {
|
||||
let activeFence: { marker: string; length: number } | undefined;
|
||||
for (const line of text.split(/\r?\n/)) {
|
||||
const match = /^ {0,3}(`{3,}|~{3,})/.exec(line);
|
||||
if (!match) continue;
|
||||
const run = match[1];
|
||||
if (!run) continue;
|
||||
if (!activeFence) {
|
||||
activeFence = { marker: run[0] ?? "", length: run.length };
|
||||
} else if (
|
||||
run[0] === activeFence.marker &&
|
||||
run.length >= activeFence.length &&
|
||||
line.slice(match[0].length).trim().length === 0
|
||||
) {
|
||||
activeFence = undefined;
|
||||
}
|
||||
}
|
||||
return activeFence !== undefined;
|
||||
}
|
||||
|
||||
function isExecutableXmlCall(text: string, raw: string): boolean {
|
||||
const callStart = text.indexOf(raw);
|
||||
if (callStart === -1 || text.slice(callStart + raw.length).trim()) {
|
||||
return false;
|
||||
}
|
||||
const lineStart = text.lastIndexOf("\n", callStart - 1) + 1;
|
||||
return (
|
||||
/^ {0,3}$/.test(text.slice(lineStart, callStart)) &&
|
||||
!isInsideMarkdownFence(text.slice(0, callStart))
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Per-agent state
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Tool specs captured in `beforeModel`, keyed by agent id. `afterModel` does
|
||||
* not receive the tool list, and `beforeModel` always runs first in the same
|
||||
* turn, so the entry is guaranteed fresh when the parse step reads it.
|
||||
*/
|
||||
const toolSpecsByAgent = new Map<string, Map<string, XmlToolSpec>>();
|
||||
|
||||
let toolCallCounter = 0;
|
||||
function nextToolCallId(): string {
|
||||
toolCallCounter += 1;
|
||||
return `xml_call_${toolCallCounter}`;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Provider-bound history rewriting (native parts -> XML wire format)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function rewriteHistoryForXml(
|
||||
messages: readonly RuntimeMessage[],
|
||||
): RuntimeMessage[] {
|
||||
return messages.map((message) => {
|
||||
const hasToolPart = message.content.some(
|
||||
(part) => part.type === "tool-call" || part.type === "tool-result",
|
||||
);
|
||||
if (!hasToolPart) {
|
||||
return message;
|
||||
}
|
||||
const content: RuntimeMessagePart[] = message.content.map((part) => {
|
||||
if (part.type === "tool-call") {
|
||||
return {
|
||||
type: "text",
|
||||
text: serializeToolCallXml(part.toolName, part.input),
|
||||
};
|
||||
}
|
||||
if (part.type === "tool-result") {
|
||||
return {
|
||||
type: "text",
|
||||
text: formatToolResultText(part.toolName, part.output, part.isError),
|
||||
};
|
||||
}
|
||||
return part;
|
||||
});
|
||||
// Tool-result messages carry the "tool" role, which providers reject
|
||||
// when no tool schemas are in the request — they become user messages,
|
||||
// matching how the legacy extension fed results back.
|
||||
const role = message.role === "tool" ? "user" : message.role;
|
||||
return { ...message, role, content };
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Assistant text -> native tool-call parts
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function convertAssistantXml(
|
||||
message: AfterModelContext["assistantMessage"],
|
||||
specs: ReadonlyMap<string, XmlToolSpec>,
|
||||
): AfterModelContext["assistantMessage"] | undefined {
|
||||
const parsedParts = message.content.map((part) =>
|
||||
part.type === "text" ? parseAssistantXml(part.text, specs) : undefined,
|
||||
);
|
||||
const candidates = parsedParts.flatMap((blocks, partIndex) =>
|
||||
(blocks ?? [])
|
||||
.filter((block) => block.type === "tool_use")
|
||||
.map((block) => ({ block, partIndex })),
|
||||
);
|
||||
const candidate = candidates[0];
|
||||
const candidatePart = candidate && message.content[candidate.partIndex];
|
||||
if (
|
||||
candidates.length !== 1 ||
|
||||
!candidate ||
|
||||
candidatePart?.type !== "text" ||
|
||||
candidate.block.partial ||
|
||||
!specs.has(candidate.block.name) ||
|
||||
message.content
|
||||
.slice(candidate.partIndex + 1)
|
||||
.some((part) =>
|
||||
part.type === "text" ? part.text.trim().length > 0 : true,
|
||||
) ||
|
||||
!isExecutableXmlCall(candidatePart.text, candidate.block.raw)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const content: RuntimeMessagePart[] = [];
|
||||
let converted = false;
|
||||
for (const [partIndex, part] of message.content.entries()) {
|
||||
if (part.type !== "text") {
|
||||
content.push(part);
|
||||
continue;
|
||||
}
|
||||
for (const block of parsedParts[partIndex] ?? []) {
|
||||
if (block.type === "text") {
|
||||
content.push({ type: "text", text: block.text });
|
||||
continue;
|
||||
}
|
||||
const spec = specs.get(block.name);
|
||||
if (block.partial || !spec) {
|
||||
// Unclosed tool use (truncation or malformed XML): keep the raw
|
||||
// source as text rather than executing a half-parsed call.
|
||||
content.push({ type: "text", text: block.raw });
|
||||
continue;
|
||||
}
|
||||
content.push({
|
||||
type: "tool-call",
|
||||
toolCallId: nextToolCallId(),
|
||||
toolName: block.name,
|
||||
input: coerceToolInput(block.params, spec),
|
||||
});
|
||||
converted = true;
|
||||
}
|
||||
}
|
||||
if (!converted) {
|
||||
return undefined;
|
||||
}
|
||||
return { ...message, content };
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Per-turn tool documentation, injected into the provider-bound messages
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Prepend the TOOL DOCUMENTATION block to the first user message of the
|
||||
* provider-bound copy. The docs cannot live in the registered rule because
|
||||
* rules are resolved before the effective tool set (mode filtering, tool
|
||||
* policies, other plugins' tools) is knowable, and the set can change
|
||||
* between runs — `request.tools` in `beforeModel` is the only accurate
|
||||
* per-turn source.
|
||||
*/
|
||||
function injectToolDocs(
|
||||
messages: readonly RuntimeMessage[],
|
||||
docs: string,
|
||||
): RuntimeMessage[] {
|
||||
const docsPart: RuntimeMessagePart = {
|
||||
type: "text",
|
||||
text: `${docs}\n\n====\n`,
|
||||
};
|
||||
const firstUserIndex = messages.findIndex(
|
||||
(message) => message.role === "user",
|
||||
);
|
||||
return messages.map((message, index) =>
|
||||
index === firstUserIndex
|
||||
? { ...message, content: [docsPart, ...message.content] }
|
||||
: message,
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// The plugin
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const plugin: AgentPlugin = {
|
||||
name: "xml-tool-calling",
|
||||
manifest: {
|
||||
capabilities: ["hooks", "rules"],
|
||||
},
|
||||
setup(api) {
|
||||
api.registerRule({
|
||||
id: "xml-tool-calling:instructions",
|
||||
source: "xml-tool-calling",
|
||||
content: XML_TOOL_CALLING_RULE,
|
||||
});
|
||||
},
|
||||
hooks: {
|
||||
beforeModel({ snapshot, request }: BeforeModelContext) {
|
||||
if (request.tools.length === 0) {
|
||||
toolSpecsByAgent.delete(snapshot.agentId);
|
||||
return undefined;
|
||||
}
|
||||
const specs = toXmlToolSpecs(request.tools);
|
||||
toolSpecsByAgent.set(snapshot.agentId, specs);
|
||||
const messages = injectToolDocs(
|
||||
rewriteHistoryForXml(request.messages),
|
||||
buildXmlToolDocs(specs),
|
||||
);
|
||||
return { tools: [], messages };
|
||||
},
|
||||
afterModel({ snapshot, assistantMessage }: AfterModelContext) {
|
||||
const specs = toolSpecsByAgent.get(snapshot.agentId);
|
||||
if (!specs || specs.size === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const converted = convertAssistantXml(assistantMessage, specs);
|
||||
return converted ? { message: converted } : undefined;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export { convertAssistantXml, plugin, rewriteHistoryForXml };
|
||||
export default plugin;
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "cline-xml-tool-calling-plugin",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"description": "XML tool calling for models without reliable native function calling (local/weak models)",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "bun test",
|
||||
"clean": "rm -rf node_modules dist"
|
||||
},
|
||||
"exports": {
|
||||
".": "./index.ts"
|
||||
},
|
||||
"cline": {
|
||||
"plugins": [
|
||||
{
|
||||
"paths": [
|
||||
"./index.ts"
|
||||
],
|
||||
"capabilities": [
|
||||
"hooks",
|
||||
"rules"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cline/core": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@cline/core": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { AgentRuntime } from "@cline/agents";
|
||||
import type {
|
||||
AgentModel,
|
||||
AgentModelEvent,
|
||||
AgentModelRequest,
|
||||
AgentRuntimeStateSnapshot,
|
||||
AgentTool,
|
||||
} from "@cline/shared";
|
||||
import plugin, { rewriteHistoryForXml } from "./index.ts";
|
||||
|
||||
function makeSnapshot(): AgentRuntimeStateSnapshot {
|
||||
return {
|
||||
agentId: "agent-test",
|
||||
status: "running",
|
||||
iteration: 1,
|
||||
messages: [],
|
||||
pendingToolCalls: [],
|
||||
usage: {
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
cacheReadTokens: 0,
|
||||
cacheWriteTokens: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const ECHO_TOOL: AgentTool<{ text: string }, { echoed: string }> = {
|
||||
name: "echo",
|
||||
description: "Echo input text back.",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: { text: { type: "string" } },
|
||||
required: ["text"],
|
||||
},
|
||||
async execute(input) {
|
||||
return { echoed: input.text };
|
||||
},
|
||||
};
|
||||
|
||||
class ScriptedModel implements AgentModel {
|
||||
public readonly requests: AgentModelRequest[] = [];
|
||||
|
||||
constructor(
|
||||
private readonly steps: Array<
|
||||
(request: AgentModelRequest) => AgentModelEvent[]
|
||||
>,
|
||||
) {}
|
||||
|
||||
async stream(
|
||||
request: AgentModelRequest,
|
||||
): Promise<AsyncIterable<AgentModelEvent>> {
|
||||
this.requests.push(request);
|
||||
const step = this.steps.shift();
|
||||
if (!step) {
|
||||
throw new Error("No scripted model step available");
|
||||
}
|
||||
const events = step(request);
|
||||
return (async function* () {
|
||||
yield* events;
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
describe("beforeModel", () => {
|
||||
it("strips native tools, appends XML docs, and rewrites tool history", async () => {
|
||||
const result = await plugin.hooks?.beforeModel?.({
|
||||
snapshot: makeSnapshot(),
|
||||
request: {
|
||||
systemPrompt: "You are a helpful assistant.",
|
||||
messages: [
|
||||
{
|
||||
id: "u1",
|
||||
role: "user",
|
||||
content: [{ type: "text", text: "echo hi" }],
|
||||
createdAt: 1,
|
||||
},
|
||||
{
|
||||
id: "a1",
|
||||
role: "assistant",
|
||||
content: [
|
||||
{ type: "text", text: "Echoing." },
|
||||
{
|
||||
type: "tool-call",
|
||||
toolCallId: "call_1",
|
||||
toolName: "echo",
|
||||
input: { text: "hi" },
|
||||
},
|
||||
],
|
||||
createdAt: 2,
|
||||
},
|
||||
{
|
||||
id: "t1",
|
||||
role: "tool",
|
||||
content: [
|
||||
{
|
||||
type: "tool-result",
|
||||
toolCallId: "call_1",
|
||||
toolName: "echo",
|
||||
output: { echoed: "hi" },
|
||||
},
|
||||
],
|
||||
createdAt: 3,
|
||||
},
|
||||
],
|
||||
tools: [ECHO_TOOL],
|
||||
},
|
||||
});
|
||||
|
||||
expect(result?.tools).toEqual([]);
|
||||
|
||||
const messages = result?.messages;
|
||||
expect(messages).toHaveLength(3);
|
||||
// Tool docs are injected at the top of the first user message.
|
||||
const firstUser = messages?.[0];
|
||||
expect(firstUser?.role).toBe("user");
|
||||
const docsPart = firstUser?.content[0];
|
||||
if (docsPart?.type !== "text") throw new Error("expected text part");
|
||||
expect(docsPart.text).toContain("TOOL DOCUMENTATION");
|
||||
expect(docsPart.text).toContain("## echo");
|
||||
expect(firstUser?.content[1]).toEqual({ type: "text", text: "echo hi" });
|
||||
|
||||
const assistant = messages?.[1];
|
||||
expect(assistant?.content).toEqual([
|
||||
{ type: "text", text: "Echoing." },
|
||||
{ type: "text", text: "<echo>\n<text>hi</text>\n</echo>" },
|
||||
]);
|
||||
const toolTurn = messages?.[2];
|
||||
expect(toolTurn?.role).toBe("user");
|
||||
expect(toolTurn?.content).toEqual([
|
||||
{
|
||||
type: "text",
|
||||
text: `[echo] Result:\n${JSON.stringify({ echoed: "hi" }, null, 2)}`,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("does nothing when the request has no tools", async () => {
|
||||
const result = await plugin.hooks?.beforeModel?.({
|
||||
snapshot: makeSnapshot(),
|
||||
request: {
|
||||
messages: [
|
||||
{
|
||||
id: "u1",
|
||||
role: "user",
|
||||
content: [{ type: "text", text: "hello" }],
|
||||
createdAt: 1,
|
||||
},
|
||||
],
|
||||
tools: [],
|
||||
},
|
||||
});
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("afterModel", () => {
|
||||
it("converts XML tool uses into native tool-call parts", async () => {
|
||||
const snapshot = makeSnapshot();
|
||||
await plugin.hooks?.beforeModel?.({
|
||||
snapshot,
|
||||
request: { messages: [], tools: [ECHO_TOOL] },
|
||||
});
|
||||
|
||||
const result = await plugin.hooks?.afterModel?.({
|
||||
snapshot,
|
||||
assistantMessage: {
|
||||
id: "a1",
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "I will echo now.\n<echo>\n<text>hi there</text>\n</echo>",
|
||||
},
|
||||
],
|
||||
createdAt: 1,
|
||||
},
|
||||
finishReason: "stop",
|
||||
});
|
||||
|
||||
expect(result?.message?.content).toEqual([
|
||||
{ type: "text", text: "I will echo now." },
|
||||
{
|
||||
type: "tool-call",
|
||||
toolCallId: expect.stringMatching(/^xml_call_\d+$/),
|
||||
toolName: "echo",
|
||||
input: { text: "hi there" },
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("leaves plain-text replies untouched", async () => {
|
||||
const snapshot = makeSnapshot();
|
||||
await plugin.hooks?.beforeModel?.({
|
||||
snapshot,
|
||||
request: { messages: [], tools: [ECHO_TOOL] },
|
||||
});
|
||||
|
||||
const result = await plugin.hooks?.afterModel?.({
|
||||
snapshot,
|
||||
assistantMessage: {
|
||||
id: "a1",
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "All done!" }],
|
||||
createdAt: 1,
|
||||
},
|
||||
finishReason: "stop",
|
||||
});
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it("keeps unclosed tool uses as raw text instead of executing them", async () => {
|
||||
const snapshot = makeSnapshot();
|
||||
await plugin.hooks?.beforeModel?.({
|
||||
snapshot,
|
||||
request: { messages: [], tools: [ECHO_TOOL] },
|
||||
});
|
||||
|
||||
const result = await plugin.hooks?.afterModel?.({
|
||||
snapshot,
|
||||
assistantMessage: {
|
||||
id: "a1",
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "<echo>\n<text>truncat" }],
|
||||
createdAt: 1,
|
||||
},
|
||||
finishReason: "max-tokens",
|
||||
});
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it("does not execute quoted, fenced, trailing, or multiple XML calls", async () => {
|
||||
const snapshot = makeSnapshot();
|
||||
await plugin.hooks?.beforeModel?.({
|
||||
snapshot,
|
||||
request: { messages: [], tools: [ECHO_TOOL] },
|
||||
});
|
||||
const calls = [
|
||||
"Example: <echo>\n<text>quoted</text>\n</echo>",
|
||||
"```xml\n<echo>\n<text>fenced</text>\n</echo>\n```",
|
||||
"```xml\n<echo>\n<text>unclosed fence</text>\n</echo>",
|
||||
"<echo>\n<text>not terminal</text>\n</echo>\nMore text.",
|
||||
"<echo>\n<text>one</text>\n</echo>\n<echo>\n<text>two</text>\n</echo>",
|
||||
];
|
||||
|
||||
for (const text of calls) {
|
||||
const result = await plugin.hooks?.afterModel?.({
|
||||
snapshot,
|
||||
assistantMessage: {
|
||||
id: "a1",
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text }],
|
||||
createdAt: 1,
|
||||
},
|
||||
finishReason: "stop",
|
||||
});
|
||||
expect(result).toBeUndefined();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("setup", () => {
|
||||
it("registers the static XML instructions as a rule", async () => {
|
||||
const rules: Array<{ id: string; content: unknown }> = [];
|
||||
const api = {
|
||||
registerTool: () => {},
|
||||
registerCommand: () => {},
|
||||
registerRule: (rule: { id: string; content: unknown }) => {
|
||||
rules.push(rule);
|
||||
},
|
||||
registerMessageBuilder: () => {},
|
||||
registerProvider: () => {},
|
||||
registerAutomationEventType: () => {},
|
||||
};
|
||||
await plugin.setup?.(api as never, {});
|
||||
expect(rules).toHaveLength(1);
|
||||
expect(rules[0]?.id).toBe("xml-tool-calling:instructions");
|
||||
expect(String(rules[0]?.content)).toContain("TOOL USE");
|
||||
expect(String(rules[0]?.content)).toContain("<tool_name>");
|
||||
});
|
||||
});
|
||||
|
||||
describe("rewriteHistoryForXml", () => {
|
||||
it("leaves messages without tool parts untouched", () => {
|
||||
const message = {
|
||||
id: "u1",
|
||||
role: "user" as const,
|
||||
content: [{ type: "text" as const, text: "hello" }],
|
||||
createdAt: 1,
|
||||
};
|
||||
expect(rewriteHistoryForXml([message])).toEqual([message]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("end to end with AgentRuntime", () => {
|
||||
it("drives a full XML tool-calling turn through the runtime", async () => {
|
||||
const model = new ScriptedModel([
|
||||
(request) => {
|
||||
expect(request.tools).toEqual([]);
|
||||
// Tool docs ride in the provider-bound first user message. (The
|
||||
// static rule is merged into the system prompt by the core
|
||||
// orchestrator, which this runtime-level test bypasses.)
|
||||
const firstUser = request.messages.find(
|
||||
(message) => message.role === "user",
|
||||
);
|
||||
expect(JSON.stringify(firstUser?.content)).toContain(
|
||||
"TOOL DOCUMENTATION",
|
||||
);
|
||||
expect(JSON.stringify(firstUser?.content)).toContain("## echo");
|
||||
return [
|
||||
{
|
||||
type: "text-delta",
|
||||
text: "Echoing.\n<echo>\n<text>hello world</text>\n</echo>",
|
||||
},
|
||||
{ type: "finish", reason: "stop" },
|
||||
];
|
||||
},
|
||||
(request) => {
|
||||
expect(request.tools).toEqual([]);
|
||||
// The assistant's tool call went back out as XML text...
|
||||
const assistant = request.messages.find(
|
||||
(message) => message.role === "assistant",
|
||||
);
|
||||
expect(assistant?.content.every((part) => part.type === "text")).toBe(
|
||||
true,
|
||||
);
|
||||
expect(JSON.stringify(assistant?.content)).toContain("<echo>");
|
||||
// ...and the tool result came back as a plain user message.
|
||||
const last = request.messages.at(-1);
|
||||
expect(last?.role).toBe("user");
|
||||
expect(JSON.stringify(last?.content)).toContain("[echo] Result:");
|
||||
expect(JSON.stringify(last?.content)).toContain("hello world");
|
||||
return [
|
||||
{ type: "text-delta", text: "done" },
|
||||
{ type: "finish", reason: "stop" },
|
||||
];
|
||||
},
|
||||
]);
|
||||
|
||||
const runtime = new AgentRuntime({
|
||||
model,
|
||||
systemPrompt: "You are a test agent.",
|
||||
tools: [ECHO_TOOL],
|
||||
hooks: plugin.hooks,
|
||||
});
|
||||
|
||||
const result = await runtime.run("Please echo 'hello world'.");
|
||||
|
||||
expect(result.status).toBe("completed");
|
||||
expect(result.outputText).toBe("done");
|
||||
expect(model.requests).toHaveLength(2);
|
||||
|
||||
// Internal state stays native: the stored assistant message carries a
|
||||
// real tool-call part, and the tool message a real tool-result part.
|
||||
const assistant = result.messages.find(
|
||||
(message) =>
|
||||
message.role === "assistant" &&
|
||||
message.content.some((part) => part.type === "tool-call"),
|
||||
);
|
||||
expect(assistant).toBeDefined();
|
||||
const toolMessage = result.messages.find(
|
||||
(message) => message.role === "tool",
|
||||
);
|
||||
expect(
|
||||
toolMessage?.content.some(
|
||||
(part) =>
|
||||
part.type === "tool-result" &&
|
||||
JSON.stringify(part.output).includes("hello world"),
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"include": [
|
||||
"index.ts",
|
||||
"xml-format.ts",
|
||||
"xml-format.test.ts",
|
||||
"plugin.test.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import {
|
||||
buildXmlToolDocs,
|
||||
coerceToolInput,
|
||||
formatToolResultText,
|
||||
parseAssistantXml,
|
||||
serializeToolCallXml,
|
||||
toXmlToolSpecs,
|
||||
XML_TOOL_CALLING_RULE,
|
||||
type XmlToolDefinition,
|
||||
} from "./xml-format.ts";
|
||||
|
||||
const TOOLS: XmlToolDefinition[] = [
|
||||
{
|
||||
name: "read_file",
|
||||
description: "Read a file from disk.",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
path: { type: "string", description: "Relative file path." },
|
||||
},
|
||||
required: ["path"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "write_to_file",
|
||||
description: "Create or overwrite a file.",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
path: { type: "string" },
|
||||
content: { type: "string" },
|
||||
},
|
||||
required: ["path", "content"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "run_commands",
|
||||
description: "Run shell commands.",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
commands: { type: "array", items: { type: "string" } },
|
||||
timeout_secs: {
|
||||
anyOf: [{ type: "integer" }, { type: "null" }],
|
||||
},
|
||||
background: { type: "boolean" },
|
||||
},
|
||||
required: ["commands"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "attempt_completion",
|
||||
description: "Present the final result.",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: { result: { type: "string" } },
|
||||
required: ["result"],
|
||||
},
|
||||
lifecycle: { completesRun: true },
|
||||
},
|
||||
];
|
||||
|
||||
const specs = toXmlToolSpecs(TOOLS);
|
||||
|
||||
describe("parseAssistantXml", () => {
|
||||
it("parses a single tool use with surrounding text", () => {
|
||||
const blocks = parseAssistantXml(
|
||||
"Let me read that file.\n<read_file>\n<path>src/main.ts</path>\n</read_file>",
|
||||
specs,
|
||||
);
|
||||
expect(blocks).toEqual([
|
||||
{ type: "text", text: "Let me read that file." },
|
||||
{
|
||||
type: "tool_use",
|
||||
name: "read_file",
|
||||
params: { path: "src/main.ts" },
|
||||
partial: false,
|
||||
raw: "<read_file>\n<path>src/main.ts</path>\n</read_file>",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("parses multiple parameters", () => {
|
||||
const blocks = parseAssistantXml(
|
||||
"<write_to_file>\n<path>a.txt</path>\n<content>hello world</content>\n</write_to_file>",
|
||||
specs,
|
||||
);
|
||||
expect(blocks).toHaveLength(1);
|
||||
const tool = blocks[0];
|
||||
if (tool?.type !== "tool_use") throw new Error("expected tool_use");
|
||||
expect(tool.params).toEqual({ path: "a.txt", content: "hello world" });
|
||||
expect(tool.partial).toBe(false);
|
||||
});
|
||||
|
||||
it("preserves meaningful parameter whitespace", () => {
|
||||
const blocks = parseAssistantXml(
|
||||
"<write_to_file>\n<path>a.txt</path>\n<content>\n\n indented\n\n</content>\n</write_to_file>",
|
||||
specs,
|
||||
);
|
||||
const tool = blocks[0];
|
||||
if (tool?.type !== "tool_use") throw new Error("expected tool_use");
|
||||
expect(tool.params.content).toBe("\n indented\n");
|
||||
});
|
||||
|
||||
it("recovers content values containing their own closing tag", () => {
|
||||
const content =
|
||||
"<note>first</note>\nliteral </content> inside\n<note>second</note>";
|
||||
const blocks = parseAssistantXml(
|
||||
`<write_to_file>\n<path>notes.xml</path>\n<content>\n${content}\n</content>\n</write_to_file>`,
|
||||
specs,
|
||||
);
|
||||
const tool = blocks[0];
|
||||
if (tool?.type !== "tool_use") throw new Error("expected tool_use");
|
||||
expect(tool.params.path).toBe("notes.xml");
|
||||
expect(tool.params.content).toBe(content);
|
||||
});
|
||||
|
||||
it("marks an unclosed tool use as partial and keeps its raw source", () => {
|
||||
const text = "Working on it.\n<read_file>\n<path>src/main.ts";
|
||||
const blocks = parseAssistantXml(text, specs);
|
||||
expect(blocks).toEqual([
|
||||
{ type: "text", text: "Working on it." },
|
||||
{
|
||||
type: "tool_use",
|
||||
name: "read_file",
|
||||
params: { path: "src/main.ts" },
|
||||
partial: true,
|
||||
raw: "<read_file>\n<path>src/main.ts",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("treats unknown tags as plain text", () => {
|
||||
const blocks = parseAssistantXml(
|
||||
"<thinking>hmm</thinking> just text <unknown_tool><path>x</path></unknown_tool>",
|
||||
specs,
|
||||
);
|
||||
expect(blocks).toEqual([
|
||||
{
|
||||
type: "text",
|
||||
text: "<thinking>hmm</thinking> just text <unknown_tool><path>x</path></unknown_tool>",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("parses multiple tool uses in one message", () => {
|
||||
const blocks = parseAssistantXml(
|
||||
"<read_file><path>a.ts</path></read_file>then<read_file><path>b.ts</path></read_file>",
|
||||
specs,
|
||||
);
|
||||
expect(
|
||||
blocks.map((block) =>
|
||||
block.type === "tool_use" ? block.params.path : block.text,
|
||||
),
|
||||
).toEqual(["a.ts", "then", "b.ts"]);
|
||||
});
|
||||
|
||||
it("returns a single text block when no tools are present", () => {
|
||||
expect(parseAssistantXml("All done!", specs)).toEqual([
|
||||
{ type: "text", text: "All done!" },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("coerceToolInput", () => {
|
||||
const runCommandsSpec = specs.get("run_commands");
|
||||
if (!runCommandsSpec) throw new Error("missing spec");
|
||||
|
||||
it("coerces schema-typed params from strings", () => {
|
||||
expect(
|
||||
coerceToolInput(
|
||||
{
|
||||
commands: '["ls", "pwd"]',
|
||||
timeout_secs: "30",
|
||||
background: "true",
|
||||
},
|
||||
runCommandsSpec,
|
||||
),
|
||||
).toEqual({ commands: ["ls", "pwd"], timeout_secs: 30, background: true });
|
||||
});
|
||||
|
||||
it("passes through values that fail coercion for the tool to validate", () => {
|
||||
expect(
|
||||
coerceToolInput(
|
||||
{ commands: "not json", timeout_secs: "soon", background: "maybe" },
|
||||
runCommandsSpec,
|
||||
),
|
||||
).toEqual({
|
||||
commands: "not json",
|
||||
timeout_secs: "soon",
|
||||
background: "maybe",
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps string params verbatim", () => {
|
||||
const readSpec = specs.get("read_file");
|
||||
if (!readSpec) throw new Error("missing spec");
|
||||
expect(coerceToolInput({ path: "42" }, readSpec)).toEqual({ path: "42" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("prompt content", () => {
|
||||
const docs = buildXmlToolDocs(specs);
|
||||
|
||||
it("keeps the static rule free of tool-specific content", () => {
|
||||
expect(XML_TOOL_CALLING_RULE).toContain("TOOL USE");
|
||||
expect(XML_TOOL_CALLING_RULE).toContain("<tool_name>");
|
||||
for (const tool of TOOLS) {
|
||||
expect(XML_TOOL_CALLING_RULE).not.toContain(tool.name);
|
||||
}
|
||||
});
|
||||
|
||||
it("documents every tool with usage skeletons", () => {
|
||||
expect(docs).toContain("TOOL DOCUMENTATION");
|
||||
for (const tool of TOOLS) {
|
||||
expect(docs).toContain(`## ${tool.name}`);
|
||||
expect(docs).toContain(`<${tool.name}>`);
|
||||
expect(docs).toContain(`</${tool.name}>`);
|
||||
}
|
||||
expect(docs).toContain("- path: (required, text)");
|
||||
expect(docs).toContain("- commands: (required, JSON array)");
|
||||
expect(docs).toContain("- background: (optional, true or false)");
|
||||
});
|
||||
|
||||
it("points at completion tools when present", () => {
|
||||
expect(docs).toContain("`attempt_completion`");
|
||||
});
|
||||
|
||||
it("falls back to plain-text completion guidance without completion tools", () => {
|
||||
const withoutCompletion = buildXmlToolDocs(
|
||||
toXmlToolSpecs(TOOLS.filter((tool) => !tool.lifecycle?.completesRun)),
|
||||
);
|
||||
expect(withoutCompletion).toContain("reply in plain text");
|
||||
});
|
||||
});
|
||||
|
||||
describe("serialization round trip", () => {
|
||||
it("serializes tool calls back into parseable XML", () => {
|
||||
const xml = serializeToolCallXml("run_commands", {
|
||||
commands: ["ls", "pwd"],
|
||||
timeout_secs: 30,
|
||||
background: false,
|
||||
});
|
||||
const blocks = parseAssistantXml(xml, specs);
|
||||
expect(blocks).toHaveLength(1);
|
||||
const tool = blocks[0];
|
||||
if (tool?.type !== "tool_use") throw new Error("expected tool_use");
|
||||
const runCommandsSpec = specs.get("run_commands");
|
||||
if (!runCommandsSpec) throw new Error("missing spec");
|
||||
expect(coerceToolInput(tool.params, runCommandsSpec)).toEqual({
|
||||
commands: ["ls", "pwd"],
|
||||
timeout_secs: 30,
|
||||
background: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("formats string and structured tool results", () => {
|
||||
expect(formatToolResultText("read_file", "file body", undefined)).toBe(
|
||||
"[read_file] Result:\nfile body",
|
||||
);
|
||||
expect(formatToolResultText("run_commands", { code: 1 }, true)).toBe(
|
||||
`[run_commands] Error:\n${JSON.stringify({ code: 1 }, null, 2)}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,517 @@
|
||||
/**
|
||||
* Pure XML tool-calling primitives: prompt generation, assistant-message
|
||||
* parsing, and provider-bound serialization.
|
||||
*
|
||||
* This module is dependency-free on purpose — the types below are structural
|
||||
* mirrors of the `@cline/core` agent contracts, so the plugin can pass its
|
||||
* runtime values straight in while the parser stays unit-testable in
|
||||
* isolation.
|
||||
*
|
||||
* The parser is a port of the legacy Cline extension's
|
||||
* `parseAssistantMessageV2` (apps/vscode/src/core/assistant-message/),
|
||||
* generalized from a fixed tool list to schema-derived tool and parameter
|
||||
* names.
|
||||
*/
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tool specs (derived from JSON Schema tool definitions)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Structural mirror of `AgentToolDefinition`. */
|
||||
export interface XmlToolDefinition {
|
||||
name: string;
|
||||
description: string;
|
||||
inputSchema: Record<string, unknown>;
|
||||
lifecycle?: {
|
||||
completesRun?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export interface XmlToolParamSpec {
|
||||
name: string;
|
||||
type: string;
|
||||
description?: string;
|
||||
required: boolean;
|
||||
}
|
||||
|
||||
export interface XmlToolSpec {
|
||||
name: string;
|
||||
description: string;
|
||||
params: XmlToolParamSpec[];
|
||||
completesRun: boolean;
|
||||
}
|
||||
|
||||
function concreteSchemaTypeOf(propSchema: unknown): string | undefined {
|
||||
if (!propSchema || typeof propSchema !== "object") {
|
||||
return undefined;
|
||||
}
|
||||
const record = propSchema as Record<string, unknown>;
|
||||
const type = record.type;
|
||||
if (typeof type === "string" && type !== "null") {
|
||||
return type;
|
||||
}
|
||||
if (Array.isArray(type)) {
|
||||
const first = type.find(
|
||||
(entry) => typeof entry === "string" && entry !== "null",
|
||||
);
|
||||
if (typeof first === "string") {
|
||||
return first;
|
||||
}
|
||||
}
|
||||
for (const keyword of ["anyOf", "oneOf"] as const) {
|
||||
const alternatives = record[keyword];
|
||||
if (!Array.isArray(alternatives)) continue;
|
||||
for (const alternative of alternatives) {
|
||||
const nestedType = concreteSchemaTypeOf(alternative);
|
||||
if (nestedType) return nestedType;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function schemaTypeOf(propSchema: unknown): string {
|
||||
return concreteSchemaTypeOf(propSchema) ?? "string";
|
||||
}
|
||||
|
||||
function schemaDescriptionOf(propSchema: unknown): string | undefined {
|
||||
if (!propSchema || typeof propSchema !== "object") {
|
||||
return undefined;
|
||||
}
|
||||
const record = propSchema as Record<string, unknown>;
|
||||
const parts: string[] = [];
|
||||
if (typeof record.description === "string" && record.description.trim()) {
|
||||
parts.push(record.description.trim());
|
||||
}
|
||||
if (Array.isArray(record.enum)) {
|
||||
parts.push(
|
||||
`One of: ${record.enum.map((v) => JSON.stringify(v)).join(", ")}.`,
|
||||
);
|
||||
}
|
||||
return parts.length > 0 ? parts.join(" ") : undefined;
|
||||
}
|
||||
|
||||
export function toXmlToolSpec(tool: XmlToolDefinition): XmlToolSpec {
|
||||
const schema = tool.inputSchema ?? {};
|
||||
const properties =
|
||||
schema.properties && typeof schema.properties === "object"
|
||||
? (schema.properties as Record<string, unknown>)
|
||||
: {};
|
||||
const required = new Set(
|
||||
Array.isArray(schema.required)
|
||||
? schema.required.filter(
|
||||
(entry): entry is string => typeof entry === "string",
|
||||
)
|
||||
: [],
|
||||
);
|
||||
return {
|
||||
name: tool.name,
|
||||
description: tool.description,
|
||||
params: Object.entries(properties).map(([name, propSchema]) => ({
|
||||
name,
|
||||
type: schemaTypeOf(propSchema),
|
||||
description: schemaDescriptionOf(propSchema),
|
||||
required: required.has(name),
|
||||
})),
|
||||
completesRun: tool.lifecycle?.completesRun === true,
|
||||
};
|
||||
}
|
||||
|
||||
export function toXmlToolSpecs(
|
||||
tools: readonly XmlToolDefinition[],
|
||||
): Map<string, XmlToolSpec> {
|
||||
const specs = new Map<string, XmlToolSpec>();
|
||||
for (const tool of tools) {
|
||||
specs.set(tool.name, toXmlToolSpec(tool));
|
||||
}
|
||||
return specs;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// System prompt section
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function paramPlaceholder(param: XmlToolParamSpec): string {
|
||||
switch (param.type) {
|
||||
case "number":
|
||||
case "integer":
|
||||
return "42";
|
||||
case "boolean":
|
||||
return "true or false";
|
||||
case "array":
|
||||
return '["item1", "item2"] (a JSON array)';
|
||||
case "object":
|
||||
return '{"key": "value"} (a JSON object)';
|
||||
default:
|
||||
return `${param.name.replaceAll("_", " ")} here`;
|
||||
}
|
||||
}
|
||||
|
||||
function paramTypeLabel(param: XmlToolParamSpec): string {
|
||||
switch (param.type) {
|
||||
case "number":
|
||||
case "integer":
|
||||
return "number";
|
||||
case "boolean":
|
||||
return "true or false";
|
||||
case "array":
|
||||
return "JSON array";
|
||||
case "object":
|
||||
return "JSON object";
|
||||
default:
|
||||
return "text";
|
||||
}
|
||||
}
|
||||
|
||||
function buildToolDoc(spec: XmlToolSpec): string {
|
||||
const lines: string[] = [
|
||||
`## ${spec.name}`,
|
||||
`Description: ${spec.description}`,
|
||||
];
|
||||
if (spec.params.length === 0) {
|
||||
lines.push("Parameters: none");
|
||||
lines.push("Usage:", `<${spec.name}>`, `</${spec.name}>`);
|
||||
return lines.join("\n");
|
||||
}
|
||||
lines.push("Parameters:");
|
||||
for (const param of spec.params) {
|
||||
const requirement = param.required ? "required" : "optional";
|
||||
const description = param.description ? ` ${param.description}` : "";
|
||||
lines.push(
|
||||
`- ${param.name}: (${requirement}, ${paramTypeLabel(param)})${description}`,
|
||||
);
|
||||
}
|
||||
lines.push("Usage:", `<${spec.name}>`);
|
||||
for (const param of spec.params) {
|
||||
lines.push(`<${param.name}>${paramPlaceholder(param)}</${param.name}>`);
|
||||
}
|
||||
lines.push(`</${spec.name}>`);
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Static XML tool-use instructions, registered as a system prompt rule via
|
||||
* `api.registerRule`. Adapted from the legacy Cline extension's XML tool-use
|
||||
* prompt. The per-tool documentation is dynamic (the tool set varies per
|
||||
* turn) and travels separately — see `buildXmlToolDocs`.
|
||||
*/
|
||||
export const XML_TOOL_CALLING_RULE = `====
|
||||
|
||||
TOOL USE
|
||||
|
||||
You do NOT have access to native function calling. Instead, you use tools by writing XML-style tags directly in your plain-text reply. Tool uses are parsed from your reply and executed by the user's system; you receive each result in the next user message. The available tools are documented under "TOOL DOCUMENTATION" in the first user message.
|
||||
|
||||
# Tool Use Formatting
|
||||
|
||||
A tool use is formatted with the tool name as the outer XML tag and each parameter inside its own tag:
|
||||
|
||||
<tool_name>
|
||||
<parameter1_name>value 1</parameter1_name>
|
||||
<parameter2_name>value 2</parameter2_name>
|
||||
</tool_name>
|
||||
|
||||
Always use the actual tool name as the XML tag name, exactly as documented. Do not wrap tool calls in code fences or JSON. Parameter values are plain text between the tags; for parameters typed as JSON array or JSON object, write valid JSON between the tags.
|
||||
|
||||
# Tool Use Guidelines
|
||||
|
||||
1. Use exactly ONE tool per message, at the end of your reply.
|
||||
2. Wait for the tool result in the next message before continuing. NEVER assume a tool succeeded.
|
||||
3. If a tool result reports an error, address it before retrying.
|
||||
4. Only use tools listed in TOOL DOCUMENTATION.`;
|
||||
|
||||
/**
|
||||
* The dynamic "TOOL DOCUMENTATION" block generated from the live tool
|
||||
* registry each turn and injected into the provider-bound first user
|
||||
* message. Kept out of the rule because rules are resolved before the
|
||||
* effective tool set (mode filtering, policies, other plugins' tools) is
|
||||
* knowable, and the set can change between runs.
|
||||
*/
|
||||
export function buildXmlToolDocs(
|
||||
specs: ReadonlyMap<string, XmlToolSpec>,
|
||||
): string {
|
||||
const completionTools = [...specs.values()]
|
||||
.filter((spec) => spec.completesRun)
|
||||
.map((spec) => spec.name);
|
||||
const docs = [...specs.values()].map(buildToolDoc).join("\n\n");
|
||||
const completionGuidance =
|
||||
completionTools.length > 0
|
||||
? `When the task is fully complete, use ${completionTools
|
||||
.map((name) => `\`${name}\``)
|
||||
.join(" or ")} to finish.`
|
||||
: "When the task is fully complete, reply in plain text without any tool tags.";
|
||||
return `TOOL DOCUMENTATION
|
||||
|
||||
These are the tools currently available to you. Invoke them with XML tags as described in the TOOL USE section of your instructions.
|
||||
|
||||
${docs}
|
||||
|
||||
${completionGuidance}`;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Parsing assistant text into tool uses
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface ParsedTextBlock {
|
||||
type: "text";
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface ParsedToolUseBlock {
|
||||
type: "tool_use";
|
||||
name: string;
|
||||
params: Record<string, string>;
|
||||
/** True when the input ended before the tool's closing tag. */
|
||||
partial: boolean;
|
||||
/** Original source slice for this tool use (open tag through close tag). */
|
||||
raw: string;
|
||||
}
|
||||
|
||||
export type ParsedAssistantBlock = ParsedTextBlock | ParsedToolUseBlock;
|
||||
|
||||
interface OpenToolState {
|
||||
name: string;
|
||||
spec: XmlToolSpec;
|
||||
params: Record<string, string>;
|
||||
/** Absolute index of `<` of the opening tag. */
|
||||
openTagStart: number;
|
||||
/** Absolute index just past the opening tag. */
|
||||
contentStart: number;
|
||||
/** Param name -> absolute index just past its consumed closing tag. */
|
||||
paramCloseEnds: Map<string, number>;
|
||||
}
|
||||
|
||||
function removeStructuralNewlines(value: string): string {
|
||||
const start = value.startsWith("\r\n") ? 2 : value.startsWith("\n") ? 1 : 0;
|
||||
let end = value.length;
|
||||
if (end > start) {
|
||||
end -= value.endsWith("\r\n") ? 2 : value.endsWith("\n") ? 1 : 0;
|
||||
}
|
||||
return value.slice(start, Math.max(start, end));
|
||||
}
|
||||
|
||||
/**
|
||||
* Recover parameter values whose text contains their own closing tag (the
|
||||
* classic case: file content containing `</content>`). Sequential parsing
|
||||
* consumes the first closing tag; when another closing occurrence exists
|
||||
* later in the tool body, re-extract the value spanning from the first
|
||||
* opening tag to the last closing tag — the legacy parser's `write_to_file`
|
||||
* special case, generalized to every captured parameter.
|
||||
*/
|
||||
function recoverTruncatedParams(
|
||||
text: string,
|
||||
tool: OpenToolState,
|
||||
contentEnd: number,
|
||||
): void {
|
||||
const contentSlice = text.slice(tool.contentStart, contentEnd);
|
||||
for (const [paramName, consumedEnd] of tool.paramCloseEnds) {
|
||||
const closeTag = `</${paramName}>`;
|
||||
const extraClose = text.indexOf(closeTag, consumedEnd);
|
||||
if (extraClose === -1 || extraClose >= contentEnd) {
|
||||
continue;
|
||||
}
|
||||
const openTag = `<${paramName}>`;
|
||||
const openIndex = contentSlice.indexOf(openTag);
|
||||
const lastClose = contentSlice.lastIndexOf(closeTag);
|
||||
if (openIndex === -1 || lastClose <= openIndex) {
|
||||
continue;
|
||||
}
|
||||
tool.params[paramName] = removeStructuralNewlines(
|
||||
contentSlice.slice(openIndex + openTag.length, lastClose),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function parseAssistantXml(
|
||||
text: string,
|
||||
specs: ReadonlyMap<string, XmlToolSpec>,
|
||||
): ParsedAssistantBlock[] {
|
||||
const blocks: ParsedAssistantBlock[] = [];
|
||||
const toolOpenTags = new Map<string, XmlToolSpec>();
|
||||
for (const spec of specs.values()) {
|
||||
toolOpenTags.set(`<${spec.name}>`, spec);
|
||||
}
|
||||
|
||||
let textStart = 0;
|
||||
let tool: OpenToolState | undefined;
|
||||
let paramName: string | undefined;
|
||||
let paramValueStart = 0;
|
||||
|
||||
const len = text.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
// Inside a parameter: only its closing tag matters.
|
||||
if (tool && paramName) {
|
||||
const closeTag = `</${paramName}>`;
|
||||
if (
|
||||
i >= closeTag.length - 1 &&
|
||||
text.startsWith(closeTag, i - closeTag.length + 1)
|
||||
) {
|
||||
tool.params[paramName] = removeStructuralNewlines(
|
||||
text.slice(paramValueStart, i - closeTag.length + 1),
|
||||
);
|
||||
tool.paramCloseEnds.set(paramName, i + 1);
|
||||
paramName = undefined;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Inside a tool body: look for a parameter opening tag or the tool close.
|
||||
if (tool && !paramName) {
|
||||
let startedParam = false;
|
||||
for (const param of tool.spec.params) {
|
||||
const openTag = `<${param.name}>`;
|
||||
if (
|
||||
i >= openTag.length - 1 &&
|
||||
text.startsWith(openTag, i - openTag.length + 1)
|
||||
) {
|
||||
paramName = param.name;
|
||||
paramValueStart = i + 1;
|
||||
startedParam = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (startedParam) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const toolCloseTag = `</${tool.name}>`;
|
||||
if (
|
||||
i >= toolCloseTag.length - 1 &&
|
||||
text.startsWith(toolCloseTag, i - toolCloseTag.length + 1)
|
||||
) {
|
||||
const contentEnd = i - toolCloseTag.length + 1;
|
||||
recoverTruncatedParams(text, tool, contentEnd);
|
||||
blocks.push({
|
||||
type: "tool_use",
|
||||
name: tool.name,
|
||||
params: tool.params,
|
||||
partial: false,
|
||||
raw: text.slice(tool.openTagStart, i + 1),
|
||||
});
|
||||
tool = undefined;
|
||||
textStart = i + 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// In plain text: look for a tool opening tag.
|
||||
for (const [openTag, spec] of toolOpenTags) {
|
||||
if (
|
||||
i >= openTag.length - 1 &&
|
||||
text.startsWith(openTag, i - openTag.length + 1)
|
||||
) {
|
||||
const tagStart = i - openTag.length + 1;
|
||||
const leadingText = text.slice(textStart, tagStart).trim();
|
||||
if (leadingText.length > 0) {
|
||||
blocks.push({ type: "text", text: leadingText });
|
||||
}
|
||||
tool = {
|
||||
name: spec.name,
|
||||
spec,
|
||||
params: {},
|
||||
openTagStart: tagStart,
|
||||
contentStart: i + 1,
|
||||
paramCloseEnds: new Map(),
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize whatever is still open at end of input.
|
||||
if (tool && paramName) {
|
||||
tool.params[paramName] = text.slice(paramValueStart).trim();
|
||||
}
|
||||
if (tool) {
|
||||
blocks.push({
|
||||
type: "tool_use",
|
||||
name: tool.name,
|
||||
params: tool.params,
|
||||
partial: true,
|
||||
raw: text.slice(tool.openTagStart),
|
||||
});
|
||||
} else {
|
||||
const trailingText = text.slice(textStart).trim();
|
||||
if (trailingText.length > 0) {
|
||||
blocks.push({ type: "text", text: trailingText });
|
||||
}
|
||||
}
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Coercing parsed string params into schema-typed tool input
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Coerce flat string parameter values into the types declared by the tool's
|
||||
* schema. Values that fail coercion are passed through as raw strings so the
|
||||
* tool's own input validation produces the error the model gets to react to.
|
||||
*/
|
||||
export function coerceToolInput(
|
||||
params: Record<string, string>,
|
||||
spec: XmlToolSpec,
|
||||
): Record<string, unknown> {
|
||||
const types = new Map(spec.params.map((param) => [param.name, param.type]));
|
||||
const input: Record<string, unknown> = {};
|
||||
for (const [key, raw] of Object.entries(params)) {
|
||||
switch (types.get(key)) {
|
||||
case "number":
|
||||
case "integer": {
|
||||
const value = Number(raw);
|
||||
input[key] = Number.isNaN(value) ? raw : value;
|
||||
break;
|
||||
}
|
||||
case "boolean":
|
||||
input[key] = raw === "true" ? true : raw === "false" ? false : raw;
|
||||
break;
|
||||
case "array":
|
||||
case "object":
|
||||
try {
|
||||
input[key] = JSON.parse(raw);
|
||||
} catch {
|
||||
input[key] = raw;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
input[key] = raw;
|
||||
}
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Serializing native tool parts back into XML/plain text for the provider
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function formatParamValue(value: unknown): string {
|
||||
if (typeof value === "string") {
|
||||
return value;
|
||||
}
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
/** Render a native tool call as the XML the model was instructed to write. */
|
||||
export function serializeToolCallXml(toolName: string, input: unknown): string {
|
||||
const record =
|
||||
input && typeof input === "object" && !Array.isArray(input)
|
||||
? (input as Record<string, unknown>)
|
||||
: {};
|
||||
const params = Object.entries(record)
|
||||
.filter(([, value]) => value !== undefined)
|
||||
.map(([key, value]) => `<${key}>${formatParamValue(value)}</${key}>`);
|
||||
return [`<${toolName}>`, ...params, `</${toolName}>`].join("\n");
|
||||
}
|
||||
|
||||
/** Render a native tool result as the plain-text user message the model reads. */
|
||||
export function formatToolResultText(
|
||||
toolName: string,
|
||||
output: unknown,
|
||||
isError: boolean | undefined,
|
||||
): string {
|
||||
const body =
|
||||
typeof output === "string" ? output : JSON.stringify(output, null, 2);
|
||||
const label = isError ? "Error" : "Result";
|
||||
return `[${toolName}] ${label}:\n${body ?? ""}`;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@cline/agents",
|
||||
"version": "0.0.60",
|
||||
"version": "0.0.59",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cline/cline",
|
||||
|
||||
@@ -1284,6 +1284,75 @@ describe("AgentRuntime", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("replaces the assistant message from afterModel and executes injected tool calls", async () => {
|
||||
const model = new ScriptedModel([
|
||||
(request) => {
|
||||
expect(request.tools[0]?.lifecycle).toEqual({ completesRun: false });
|
||||
return [
|
||||
{ type: "text-delta", text: "<echo><text>hi</text></echo>" },
|
||||
{ type: "usage", usage: { inputTokens: 7, outputTokens: 3 } },
|
||||
{ type: "finish", reason: "stop" },
|
||||
];
|
||||
},
|
||||
(request) => {
|
||||
const toolMessage = request.messages.at(-1) as AgentMessage;
|
||||
expect(toolMessage.role).toBe("tool");
|
||||
return [
|
||||
{ type: "text-delta", text: "done" },
|
||||
{ type: "finish", reason: "stop" },
|
||||
];
|
||||
},
|
||||
]);
|
||||
const runtime = new AgentRuntime({
|
||||
model,
|
||||
tools: [{ ...createEchoTool(), lifecycle: { completesRun: false } }],
|
||||
hooks: {
|
||||
afterModel: ({ assistantMessage }) => {
|
||||
const text = assistantMessage.content
|
||||
.filter((part) => part.type === "text")
|
||||
.map((part) => part.text)
|
||||
.join("");
|
||||
if (!text.includes("<echo>")) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
message: {
|
||||
...assistantMessage,
|
||||
content: [
|
||||
{
|
||||
type: "tool-call",
|
||||
toolCallId: "call_xml_1",
|
||||
toolName: "echo",
|
||||
input: { text: "hi" },
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const result = await runtime.run("Start");
|
||||
|
||||
expect(result.status).toBe("completed");
|
||||
expect(result.outputText).toBe("done");
|
||||
const assistantWithToolCall = result.messages.find(
|
||||
(message) =>
|
||||
message.role === "assistant" &&
|
||||
message.content.some((part) => part.type === "tool-call"),
|
||||
);
|
||||
expect(assistantWithToolCall).toBeDefined();
|
||||
expect(assistantWithToolCall?.metrics).toMatchObject({
|
||||
inputTokens: 7,
|
||||
outputTokens: 3,
|
||||
});
|
||||
const toolMessages = result.messages.filter(
|
||||
(message) => message.role === "tool",
|
||||
);
|
||||
expect(toolMessages).toHaveLength(1);
|
||||
expect(JSON.stringify(toolMessages[0]?.content)).toContain("hi");
|
||||
});
|
||||
|
||||
it("stamps runtime identity metadata onto model requests", async () => {
|
||||
const model = new ScriptedModel([
|
||||
(request) => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createGateway, type GatewayProviderSettings } from "@cline/llms";
|
||||
import type {
|
||||
AgentAfterModelResult,
|
||||
AgentAfterToolResult,
|
||||
AgentBeforeModelResult,
|
||||
AgentBeforeToolResult,
|
||||
@@ -789,6 +790,7 @@ export class AgentRuntime {
|
||||
name: tool.name,
|
||||
description: tool.description,
|
||||
inputSchema: tool.inputSchema,
|
||||
lifecycle: tool.lifecycle ? { ...tool.lifecycle } : undefined,
|
||||
})),
|
||||
signal: this.abortController?.signal,
|
||||
options: mergeModelOptions(this.config.modelOptions, {
|
||||
@@ -998,7 +1000,7 @@ export class AgentRuntime {
|
||||
});
|
||||
}
|
||||
|
||||
const message = createMessage(
|
||||
let message = createMessage(
|
||||
"assistant",
|
||||
content,
|
||||
invalidToolCalls.length > 0 ? { invalidToolCalls } : undefined,
|
||||
@@ -1012,12 +1014,19 @@ export class AgentRuntime {
|
||||
message.modelInfo = { ...this.config.messageModelInfo };
|
||||
}
|
||||
for (const hook of this.hooks.afterModel) {
|
||||
const control = (await hook({
|
||||
const result = (await hook({
|
||||
snapshot: this.snapshot(),
|
||||
assistantMessage: message,
|
||||
finishReason,
|
||||
})) as AgentStopControl | undefined;
|
||||
this.applyStopControl(control);
|
||||
})) as AgentAfterModelResult | undefined;
|
||||
if (result?.message) {
|
||||
message = {
|
||||
...result.message,
|
||||
metrics: result.message.metrics ?? message.metrics,
|
||||
modelInfo: result.message.modelInfo ?? message.modelInfo,
|
||||
};
|
||||
}
|
||||
this.applyStopControl(result);
|
||||
}
|
||||
|
||||
return { message, finishReason };
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@cline/core",
|
||||
"description": "Cline Core SDK for Node Runtime",
|
||||
"version": "0.0.60",
|
||||
"version": "0.0.59",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cline/cline",
|
||||
|
||||
@@ -93,22 +93,8 @@ describe("auth/cline getValidClineCredentials", () => {
|
||||
),
|
||||
) as unknown as typeof fetch;
|
||||
|
||||
const capture = vi.fn();
|
||||
const result = await getValidClineCredentials(current, {
|
||||
...PROVIDER_OPTIONS,
|
||||
telemetry: { capture } as never,
|
||||
});
|
||||
const result = await getValidClineCredentials(current, PROVIDER_OPTIONS);
|
||||
expect(result).toBeNull();
|
||||
expect(capture).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
event: "user.auth_logged_out",
|
||||
properties: expect.objectContaining({
|
||||
reason: "invalid_grant",
|
||||
status: 401,
|
||||
errorCode: "invalid_grant",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
nowSpy.mockRestore();
|
||||
});
|
||||
|
||||
@@ -129,68 +115,11 @@ describe("auth/cline getValidClineCredentials", () => {
|
||||
),
|
||||
) as unknown as typeof fetch;
|
||||
|
||||
const capture = vi.fn();
|
||||
const result = await getValidClineCredentials(
|
||||
current,
|
||||
{ ...PROVIDER_OPTIONS, telemetry: { capture } as never },
|
||||
{
|
||||
refreshBufferMs: 60_000,
|
||||
retryableTokenGraceMs: 30_000,
|
||||
},
|
||||
);
|
||||
const result = await getValidClineCredentials(current, PROVIDER_OPTIONS, {
|
||||
refreshBufferMs: 60_000,
|
||||
retryableTokenGraceMs: 30_000,
|
||||
});
|
||||
expect(result).toBe(current);
|
||||
expect(capture).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
event: "user.auth_refresh_soft_failure",
|
||||
properties: expect.objectContaining({
|
||||
status: 500,
|
||||
tokenExpired: false,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
nowSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("throws on transient refresh error when the token is already expired", async () => {
|
||||
// A network blip landing after expiry is NOT an invalid grant; returning
|
||||
// null here is what made clients wipe stored credentials over an outage.
|
||||
const nowSpy = vi.spyOn(Date, "now").mockReturnValue(100_000);
|
||||
const current = createCredentials({ expires: 90_000 });
|
||||
globalThis.fetch = vi.fn(
|
||||
async () =>
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
error: "server_error",
|
||||
error_description: "temporary issue",
|
||||
}),
|
||||
{
|
||||
status: 500,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
},
|
||||
),
|
||||
) as unknown as typeof fetch;
|
||||
|
||||
const capture = vi.fn();
|
||||
await expect(
|
||||
getValidClineCredentials(current, {
|
||||
...PROVIDER_OPTIONS,
|
||||
telemetry: { capture } as never,
|
||||
}),
|
||||
).rejects.toThrow("Token refresh failed: 500");
|
||||
// The "prevented logout" counter: this exact situation used to wipe
|
||||
// stored credentials.
|
||||
expect(capture).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
event: "user.auth_refresh_soft_failure",
|
||||
properties: expect.objectContaining({
|
||||
status: 500,
|
||||
tokenExpired: true,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
expect(capture).not.toHaveBeenCalledWith(
|
||||
expect.objectContaining({ event: "user.auth_logged_out" }),
|
||||
);
|
||||
nowSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,11 +2,9 @@ import {
|
||||
getClineEnvironmentConfig,
|
||||
type ITelemetryService,
|
||||
} from "@cline/shared";
|
||||
import { hashSecret, sdkDebug } from "../logging/early-logger";
|
||||
import {
|
||||
captureAuthFailed,
|
||||
captureAuthLoggedOut,
|
||||
captureAuthRefreshSoftFailure,
|
||||
captureAuthStarted,
|
||||
captureAuthSucceeded,
|
||||
identifyAccount,
|
||||
@@ -623,34 +621,27 @@ export async function refreshClineToken(
|
||||
current: ClineOAuthCredentials,
|
||||
options: ClineOAuthProviderOptions,
|
||||
): Promise<ClineOAuthCredentials> {
|
||||
const refreshUrl = resolveUrl(
|
||||
options.apiBaseUrl,
|
||||
DEFAULT_AUTH_ENDPOINTS.refresh,
|
||||
);
|
||||
sdkDebug(
|
||||
`cline.refresh.request url=${refreshUrl} refreshTokenHash=${hashSecret(current.refresh)}`,
|
||||
);
|
||||
const response = await fetch(refreshUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(await resolveHeaders(options.headers)),
|
||||
const response = await fetch(
|
||||
resolveUrl(options.apiBaseUrl, DEFAULT_AUTH_ENDPOINTS.refresh),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(await resolveHeaders(options.headers)),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
refreshToken: current.refresh,
|
||||
grantType: "refresh_token",
|
||||
}),
|
||||
signal: AbortSignal.timeout(
|
||||
options.requestTimeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS,
|
||||
),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
refreshToken: current.refresh,
|
||||
grantType: "refresh_token",
|
||||
}),
|
||||
signal: AbortSignal.timeout(
|
||||
options.requestTimeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS,
|
||||
),
|
||||
});
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text().catch(() => "");
|
||||
const details = parseOAuthError(text);
|
||||
sdkDebug(
|
||||
`cline.refresh.error status=${response.status} errorCode=${details.code ?? "none"} message=${details.message ?? "none"}`,
|
||||
);
|
||||
throw new ClineOAuthTokenError(
|
||||
`Token refresh failed: ${response.status}${details.message ? ` - ${details.message}` : ""}`,
|
||||
{ status: response.status, errorCode: details.code },
|
||||
@@ -660,32 +651,19 @@ export async function refreshClineToken(
|
||||
const json = (await response.json()) as ClineTokenResponse;
|
||||
const provider =
|
||||
(current.metadata?.provider as string | undefined) ?? options.provider;
|
||||
const result = toClineCredentials(
|
||||
return toClineCredentials(
|
||||
requireClineTokenResponse(json, "Invalid token refresh response"),
|
||||
provider,
|
||||
current,
|
||||
);
|
||||
sdkDebug(
|
||||
`cline.refresh.success newAccessTokenHash=${hashSecret(result.access)} newRefreshTokenHash=${hashSecret(result.refresh)} expires=${result.expires}`,
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve valid Cline credentials, refreshing when needed.
|
||||
*
|
||||
* Contract: returns refreshed/current credentials when usable; returns `null`
|
||||
* ONLY when the refresh token was rejected (invalid grant — re-auth required);
|
||||
* THROWS on transient failures (network, timeout, 5xx) so callers can leave
|
||||
* stored credentials untouched and retry later.
|
||||
*/
|
||||
export async function getValidClineCredentials(
|
||||
currentCredentials: ClineOAuthCredentials | null,
|
||||
providerOptions: ClineOAuthProviderOptions,
|
||||
options?: ClineTokenResolution,
|
||||
): Promise<ClineOAuthCredentials | null> {
|
||||
if (!currentCredentials) {
|
||||
sdkDebug("cline.getCredentials outcome=no_current_credentials");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -698,64 +676,24 @@ export async function getValidClineCredentials(
|
||||
!forceRefresh &&
|
||||
!isCredentialLikelyExpired(currentCredentials, refreshBufferMs)
|
||||
) {
|
||||
sdkDebug(
|
||||
`cline.getCredentials outcome=still_valid forceRefresh=${forceRefresh}`,
|
||||
);
|
||||
return currentCredentials;
|
||||
}
|
||||
|
||||
sdkDebug(
|
||||
`cline.getCredentials outcome=needs_refresh forceRefresh=${forceRefresh} accessTokenHash=${hashSecret(currentCredentials.access)}`,
|
||||
);
|
||||
|
||||
try {
|
||||
return await refreshClineToken(currentCredentials, providerOptions);
|
||||
} catch (error) {
|
||||
const failureDetails = {
|
||||
status: error instanceof ClineOAuthTokenError ? error.status : undefined,
|
||||
errorCode:
|
||||
error instanceof ClineOAuthTokenError ? error.errorCode : undefined,
|
||||
errorName: error instanceof Error ? error.name : undefined,
|
||||
};
|
||||
if (error instanceof ClineOAuthTokenError && error.isLikelyInvalidGrant()) {
|
||||
sdkDebug(
|
||||
`cline.getCredentials outcome=invalid_grant status=${error.status} errorCode=${error.errorCode ?? "none"}`,
|
||||
);
|
||||
captureAuthLoggedOut(
|
||||
providerOptions.telemetry,
|
||||
providerOptions.provider ?? "cline",
|
||||
"invalid_grant",
|
||||
{ status: error.status, errorCode: error.errorCode },
|
||||
);
|
||||
return null;
|
||||
}
|
||||
if (currentCredentials.expires - Date.now() > retryableTokenGraceMs) {
|
||||
// Keep current token on transient refresh failures while still valid.
|
||||
sdkDebug(
|
||||
`cline.getCredentials outcome=transient_failure_kept_current error=${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
captureAuthRefreshSoftFailure(
|
||||
providerOptions.telemetry,
|
||||
providerOptions.provider ?? "cline",
|
||||
{ ...failureDetails, tokenExpired: false },
|
||||
);
|
||||
return currentCredentials;
|
||||
}
|
||||
// Transient failure with an already-expired token: rethrow instead of
|
||||
// returning null. A null from this function means the refresh token was
|
||||
// REJECTED (re-auth required); a network blip that happens to land after
|
||||
// expiry must not be mistaken for that — callers were wiping stored
|
||||
// credentials over it, logging out every Cline process on the machine.
|
||||
sdkDebug(
|
||||
`cline.getCredentials outcome=transient_failure_rethrown error=${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
// Every one of these events was a hard logout before the
|
||||
// transient-vs-invalid_grant fix — the "prevented logout" counter.
|
||||
captureAuthRefreshSoftFailure(
|
||||
providerOptions.telemetry,
|
||||
providerOptions.provider ?? "cline",
|
||||
{ ...failureDetails, tokenExpired: true },
|
||||
);
|
||||
throw error;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +147,10 @@ export async function runAgenticCompaction(options: {
|
||||
options.context.triggerTokens,
|
||||
MIN_AGENTIC_SUMMARY_INPUT_TOKENS,
|
||||
);
|
||||
if (resolvedSummarizerInputLimit === undefined && !canUseActiveContextLimit) {
|
||||
if (
|
||||
resolvedSummarizerInputLimit === undefined &&
|
||||
!canUseActiveContextLimit
|
||||
) {
|
||||
options.logger?.log(
|
||||
"Agentic compaction summarizer has no known input limit; using conservative summary budget",
|
||||
{
|
||||
@@ -173,15 +176,12 @@ export async function runAgenticCompaction(options: {
|
||||
const availableSummaryInputTokens =
|
||||
summarizerInputLimit - summaryRequestOverheadTokens;
|
||||
if (availableSummaryInputTokens <= 0) {
|
||||
options.logger?.debug(
|
||||
"Skipped agentic compaction: summarizer budget exhausted",
|
||||
{
|
||||
summarizerProviderId: summarizerProviderConfig.providerId,
|
||||
summarizerModelId: summarizerProviderConfig.modelId,
|
||||
summarizerInputLimit,
|
||||
summaryRequestOverheadTokens,
|
||||
},
|
||||
);
|
||||
options.logger?.debug("Skipped agentic compaction: summarizer budget exhausted", {
|
||||
summarizerProviderId: summarizerProviderConfig.providerId,
|
||||
summarizerModelId: summarizerProviderConfig.modelId,
|
||||
summarizerInputLimit,
|
||||
summaryRequestOverheadTokens,
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
const summaryInputBudget = buildAgenticSummaryInputBudget({
|
||||
@@ -270,7 +270,8 @@ export async function runAgenticCompaction(options: {
|
||||
});
|
||||
const budgetActionCount = summaryInputBudget.actions.filter(
|
||||
(action) =>
|
||||
action.reason === "over_budget" || action.reason === "tool_pair_boundary",
|
||||
action.reason === "over_budget" ||
|
||||
action.reason === "tool_pair_boundary",
|
||||
).length;
|
||||
return {
|
||||
messages: resultMessages,
|
||||
|
||||
@@ -475,7 +475,8 @@ export function runBasicCompaction(options: {
|
||||
);
|
||||
const budgetActionCount = budgeted.actions.filter(
|
||||
(action) =>
|
||||
action.reason === "over_budget" || action.reason === "tool_pair_boundary",
|
||||
action.reason === "over_budget" ||
|
||||
action.reason === "tool_pair_boundary",
|
||||
).length;
|
||||
options.logger?.debug("Performed basic compaction", {
|
||||
messagesBefore: options.context.messages.length,
|
||||
|
||||
@@ -230,7 +230,9 @@ describe("buildBudgetProjection", () => {
|
||||
{ role: "user", content: "latest typed prompt" },
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "tool_use", id: "tool_1", name: "read", input: {} }],
|
||||
content: [
|
||||
{ type: "tool_use", id: "tool_1", name: "read", input: {} },
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
@@ -375,11 +377,7 @@ describe("buildBudgetProjection", () => {
|
||||
name: "read",
|
||||
content: [
|
||||
{ type: "text", text: "a".repeat(200) },
|
||||
{
|
||||
type: "file",
|
||||
path: "/tmp/huge.txt",
|
||||
content: "b".repeat(1_000),
|
||||
},
|
||||
{ type: "file", path: "/tmp/huge.txt", content: "b".repeat(1_000) },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -424,7 +422,7 @@ describe("buildBudgetProjection", () => {
|
||||
(message) => message.role === "assistant",
|
||||
);
|
||||
expect(JSON.stringify(assistant)).not.toContain("b".repeat(100));
|
||||
expect(JSON.stringify(assistant)).not.toContain('"thinking"');
|
||||
expect(JSON.stringify(assistant)).not.toContain("\"thinking\"");
|
||||
expect(result.actions).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
|
||||
@@ -21,7 +21,9 @@ interface ProjectionPolicy {
|
||||
dropThinkingBlocks: boolean;
|
||||
}
|
||||
|
||||
function resolveProjectionPolicy(intent: BudgetPolicyIntent): ProjectionPolicy {
|
||||
function resolveProjectionPolicy(
|
||||
intent: BudgetPolicyIntent,
|
||||
): ProjectionPolicy {
|
||||
switch (intent) {
|
||||
case "agentic_summary":
|
||||
case "basic_compaction_projection":
|
||||
@@ -121,11 +123,7 @@ function buildToolPairIndex(
|
||||
messages: MessageWithMetadata[],
|
||||
): Map<string, Set<number>> {
|
||||
const index = new Map<string, Set<number>>();
|
||||
for (
|
||||
let messageIndex = 0;
|
||||
messageIndex < messages.length;
|
||||
messageIndex += 1
|
||||
) {
|
||||
for (let messageIndex = 0; messageIndex < messages.length; messageIndex += 1) {
|
||||
for (const id of collectToolIds(messages[messageIndex])) {
|
||||
const existing = index.get(id);
|
||||
if (existing) {
|
||||
@@ -209,9 +207,7 @@ function shouldDropWholeBlock(
|
||||
if (policy.dropThinkingBlocks && block.type === "thinking") {
|
||||
return true;
|
||||
}
|
||||
return (
|
||||
policy.dropUnsafeOutsideLiveTail && !isProtected && isUnsafeBlock(block)
|
||||
);
|
||||
return policy.dropUnsafeOutsideLiveTail && !isProtected && isUnsafeBlock(block);
|
||||
}
|
||||
|
||||
function pruneEmptyMessages(
|
||||
@@ -225,13 +221,13 @@ function pruneEmptyMessages(
|
||||
for (let index = 0; index < messages.length; index += 1) {
|
||||
const message = messages[index];
|
||||
if (Array.isArray(message.content) && message.content.length === 0) {
|
||||
actions.push({
|
||||
kind: "dropped_message",
|
||||
path: { messageIndex: originalIndexes[index] },
|
||||
reason,
|
||||
originalSize: safeJsonSize(message),
|
||||
finalSize: 0,
|
||||
});
|
||||
actions.push({
|
||||
kind: "dropped_message",
|
||||
path: { messageIndex: originalIndexes[index] },
|
||||
reason,
|
||||
originalSize: safeJsonSize(message),
|
||||
finalSize: 0,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
next.push(message);
|
||||
@@ -326,6 +322,7 @@ function dropThinkingBlocks(
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function truncateText(text: string, maxChars: number): string {
|
||||
if (maxChars <= 0) {
|
||||
return "";
|
||||
@@ -579,7 +576,8 @@ export function buildBudgetProjection(
|
||||
const targetChars = Math.max(
|
||||
16,
|
||||
Math.floor(
|
||||
(options.targetTokens * charsPerToken) / Math.max(1, messages.length),
|
||||
(options.targetTokens * charsPerToken) /
|
||||
Math.max(1, messages.length),
|
||||
),
|
||||
);
|
||||
messages[index] = truncateMessageText(messages[index], targetChars);
|
||||
|
||||
@@ -1021,15 +1021,13 @@ describe("createContextCompactionPrepareTurn", () => {
|
||||
it("budgets agentic summary input against the configured summarizer context window", async () => {
|
||||
let summaryRequest = "";
|
||||
createHandlerMock.mockReturnValue({
|
||||
createMessage: vi.fn(
|
||||
(_system: string, messages: LlmsProviders.Message[]) => {
|
||||
summaryRequest = String(messages[0]?.content ?? "");
|
||||
return streamChunks([
|
||||
{ type: "text", id: "summary-small", text: "## Goal\nSummarized" },
|
||||
{ type: "done", id: "summary-small", success: true },
|
||||
]);
|
||||
},
|
||||
),
|
||||
createMessage: vi.fn((_system: string, messages: LlmsProviders.Message[]) => {
|
||||
summaryRequest = String(messages[0]?.content ?? "");
|
||||
return streamChunks([
|
||||
{ type: "text", id: "summary-small", text: "## Goal\nSummarized" },
|
||||
{ type: "done", id: "summary-small", success: true },
|
||||
]);
|
||||
}),
|
||||
});
|
||||
|
||||
const summarizerLimit = 600;
|
||||
|
||||
@@ -130,6 +130,35 @@ describe("plugin-sandbox", () => {
|
||||
"utf8",
|
||||
);
|
||||
|
||||
await writeFile(
|
||||
join(dir, "plugin-model-hooks.mjs"),
|
||||
[
|
||||
"export default {",
|
||||
" name: 'sandbox-model-hooks',",
|
||||
" manifest: { capabilities: ['hooks'] },",
|
||||
" hooks: {",
|
||||
" beforeModel(ctx) {",
|
||||
" return {",
|
||||
" tools: [],",
|
||||
" messages: ctx.request.messages.concat([",
|
||||
" { id: 'docs', role: 'user', content: [{ type: 'text', text: 'TOOL DOCUMENTATION' }], createdAt: 0 },",
|
||||
" ]),",
|
||||
" };",
|
||||
" },",
|
||||
" afterModel(ctx) {",
|
||||
" return {",
|
||||
" message: {",
|
||||
" ...ctx.assistantMessage,",
|
||||
" content: [{ type: 'tool-call', toolCallId: 'xml_1', toolName: 'echo', input: { text: 'hi' } }],",
|
||||
" },",
|
||||
" };",
|
||||
" },",
|
||||
" },",
|
||||
"};",
|
||||
].join("\n"),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
await writeFile(
|
||||
join(dir, "plugin-message-builder.mjs"),
|
||||
[
|
||||
@@ -358,6 +387,7 @@ describe("plugin-sandbox", () => {
|
||||
join(dir, "plugin.mjs"),
|
||||
join(dir, "plugin-events.mjs"),
|
||||
join(dir, "plugin-run-end.mjs"),
|
||||
join(dir, "plugin-model-hooks.mjs"),
|
||||
join(dir, "plugin-automation-events.mjs"),
|
||||
join(dir, "plugin-message-builder.mjs"),
|
||||
join(dir, "plugin-rules.mjs"),
|
||||
@@ -415,6 +445,58 @@ describe("plugin-sandbox", () => {
|
||||
expect(result).toEqual({ echoed: "ok" });
|
||||
});
|
||||
|
||||
it("round-trips beforeModel/afterModel transform results across the sandbox", async () => {
|
||||
const extension = sharedExtensions.get("sandbox-model-hooks");
|
||||
expect(extension?.name).toBe("sandbox-model-hooks");
|
||||
|
||||
const beforeResult = await extension?.hooks?.beforeModel?.({
|
||||
snapshot: makeSnapshot(),
|
||||
request: {
|
||||
systemPrompt: "base prompt",
|
||||
messages: [
|
||||
{
|
||||
id: "u1",
|
||||
role: "user",
|
||||
content: [{ type: "text", text: "hi" }],
|
||||
createdAt: 1,
|
||||
},
|
||||
],
|
||||
tools: [
|
||||
{
|
||||
name: "echo",
|
||||
description: "echo",
|
||||
inputSchema: { type: "object" },
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(beforeResult?.tools).toEqual([]);
|
||||
expect(beforeResult?.messages).toHaveLength(2);
|
||||
expect(beforeResult?.messages?.[1]?.content).toEqual([
|
||||
{ type: "text", text: "TOOL DOCUMENTATION" },
|
||||
]);
|
||||
|
||||
const afterResult = await extension?.hooks?.afterModel?.({
|
||||
snapshot: makeSnapshot(),
|
||||
assistantMessage: {
|
||||
id: "a1",
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "<echo><text>hi</text></echo>" }],
|
||||
createdAt: 1,
|
||||
},
|
||||
finishReason: "stop",
|
||||
});
|
||||
expect(afterResult?.message?.content).toEqual([
|
||||
{
|
||||
type: "tool-call",
|
||||
toolCallId: "xml_1",
|
||||
toolName: "echo",
|
||||
input: { text: "hi" },
|
||||
},
|
||||
]);
|
||||
expect(afterResult?.message?.id).toBe("a1");
|
||||
});
|
||||
|
||||
it("enforces hook timeout and cancels sandbox process", async () => {
|
||||
const timeoutDir = await mkdtemp(
|
||||
join(tmpdir(), "core-plugin-sandbox-timeout-"),
|
||||
|
||||
@@ -9,7 +9,6 @@ export {
|
||||
ClineNotSubscribedError,
|
||||
ClineOrgIndividualInferenceSubscriptionError,
|
||||
ClinePassLimitError,
|
||||
extractClinePassLimitMessage,
|
||||
getClineOrgIndividualInferenceSubscriptionMessage,
|
||||
getClineNotSubscribedMessage,
|
||||
getClinePassSubscriptionUrl,
|
||||
@@ -132,15 +131,10 @@ export {
|
||||
isClineAccountActionRequest,
|
||||
type ProviderActionExecutor,
|
||||
RpcClineAccountService,
|
||||
type UserCurrentPlan,
|
||||
type UserRemoteConfigOrganization,
|
||||
type UserRemoteConfigResponse,
|
||||
type UserCurrentPlan,
|
||||
} from "./account";
|
||||
export {
|
||||
hashSecret,
|
||||
setSdkLogger,
|
||||
sdkDebug,
|
||||
} from "./logging/early-logger";
|
||||
export {
|
||||
createOAuthClientCallbacks,
|
||||
type OAuthClientCallbacksOptions,
|
||||
@@ -308,10 +302,7 @@ export {
|
||||
type McpServerTransportConfig,
|
||||
type McpSettingsFile,
|
||||
type McpSettingsLockOptions,
|
||||
McpSettingsLockTimeoutError,
|
||||
type McpSettingsMutator,
|
||||
McpSettingsMutatorPurityError,
|
||||
McpSettingsUpdateSkippedError,
|
||||
type McpSseTransportConfig,
|
||||
type McpStdioTransportConfig,
|
||||
type McpStreamableHttpTransportConfig,
|
||||
@@ -330,6 +321,9 @@ export {
|
||||
updateMcpServerOAuthStateAsync,
|
||||
updateMcpSettingsFile,
|
||||
updateMcpSettingsFileSync,
|
||||
McpSettingsLockTimeoutError,
|
||||
McpSettingsMutatorPurityError,
|
||||
McpSettingsUpdateSkippedError,
|
||||
} from "./extensions/mcp";
|
||||
export {
|
||||
type AgentTask,
|
||||
@@ -418,11 +412,6 @@ export {
|
||||
} from "./remote-config/integration";
|
||||
export type { RuntimeCapabilities } from "./runtime/capabilities";
|
||||
export { normalizeRuntimeCapabilities } from "./runtime/capabilities";
|
||||
export type {
|
||||
ConnectionUpdate,
|
||||
ConnectionUpdateInput,
|
||||
} from "./runtime/config/connection-update";
|
||||
export { buildConnectionUpdate } from "./runtime/config/connection-update";
|
||||
export { listSessionHistoryFromBackend } from "./runtime/host/history";
|
||||
export type { SessionBackend } from "./runtime/host/host";
|
||||
export {
|
||||
@@ -485,10 +474,7 @@ export {
|
||||
type FeatureFlagsServiceOptions,
|
||||
NoOpFeatureFlagsProvider,
|
||||
} from "./services/feature-flags";
|
||||
export type {
|
||||
GlobalCompactionStrategy,
|
||||
GlobalSettings,
|
||||
} from "./services/global-settings";
|
||||
export type { GlobalSettings } from "./services/global-settings";
|
||||
export {
|
||||
filterDisabledPluginPaths,
|
||||
filterDisabledTools,
|
||||
@@ -510,6 +496,16 @@ export {
|
||||
toggleDisabledTool,
|
||||
writeGlobalSettings,
|
||||
} from "./services/global-settings";
|
||||
export type { GlobalCompactionStrategy } from "./services/global-settings";
|
||||
export type {
|
||||
McpInstallOptions,
|
||||
McpInstallResult,
|
||||
} from "./services/mcp-install";
|
||||
export {
|
||||
buildMcpInstallTransport,
|
||||
installMcpServer,
|
||||
parseMcpInstallArgs,
|
||||
} from "./services/mcp-install";
|
||||
export type {
|
||||
MarketplaceActionResult,
|
||||
MarketplaceEntryInput,
|
||||
@@ -530,15 +526,6 @@ export {
|
||||
uninstallMarketplacePlugin,
|
||||
uninstallMarketplaceSkill,
|
||||
} from "./services/marketplace";
|
||||
export type {
|
||||
McpInstallOptions,
|
||||
McpInstallResult,
|
||||
} from "./services/mcp-install";
|
||||
export {
|
||||
buildMcpInstallTransport,
|
||||
installMcpServer,
|
||||
parseMcpInstallArgs,
|
||||
} from "./services/mcp-install";
|
||||
export type {
|
||||
ParsedPluginSource,
|
||||
PluginInstallOptions,
|
||||
@@ -700,11 +687,11 @@ export {
|
||||
} from "./services/workspace/workspace-manifest";
|
||||
export {
|
||||
buildCheckpointWorkspaceDiff,
|
||||
compareCheckpointToWorkspace,
|
||||
createCheckpointComparePlan,
|
||||
type CheckpointComparePlan,
|
||||
type CheckpointContentDiff,
|
||||
type CheckpointWorkspaceCompareResult,
|
||||
compareCheckpointToWorkspace,
|
||||
createCheckpointComparePlan,
|
||||
} from "./session/checkpoint-diff";
|
||||
export {
|
||||
findCheckpointForRun,
|
||||
@@ -804,17 +791,17 @@ export {
|
||||
getCoreHeadlessToolNames,
|
||||
MAX_COMMAND_OUTPUT_CHARS,
|
||||
resolveCoreSelectedToolIds,
|
||||
type ShellExecutor,
|
||||
type ShellExecutorOptions,
|
||||
type StructuredCommandInput,
|
||||
StructuredCommandInputSchema,
|
||||
TEAM_TOOL_NAMES,
|
||||
truncateCommandOutput,
|
||||
type ToolCatalogEntry,
|
||||
type ToolExecutors,
|
||||
type ShellExecutor,
|
||||
type ShellExecutorOptions,
|
||||
type ToolPolicyPresetName,
|
||||
type ToolPresetName,
|
||||
ToolPresets,
|
||||
truncateCommandOutput,
|
||||
} from "./extensions/tools";
|
||||
export {
|
||||
type ClineRecommendedModel,
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/**
|
||||
* Early SDK logger for components that operate before/outside of `ClineCore`
|
||||
* sessions, plus a secret-hashing helper for credential diagnostics.
|
||||
*
|
||||
* `ClineCore.create({ logger })` receives a `BasicLogger` but it is only
|
||||
* threaded to session-scoped components. `ProviderSettingsManager`,
|
||||
* `RuntimeOAuthTokenManager`, and the Cline auth functions in `cline.ts` are
|
||||
* constructed or called before `ClineCore` exists or outside a session.
|
||||
* Hosts call `setSdkLogger()` once at startup and every early component picks
|
||||
* it up without threading loggers through every constructor.
|
||||
*
|
||||
* When no logger is registered, every call is a no-op.
|
||||
*
|
||||
* Secrets are never logged in cleartext; {@link hashSecret} produces an
|
||||
* 8-hex-digit fingerprint that is stable for the same value but irreversible.
|
||||
*/
|
||||
|
||||
import { createHash } from "node:crypto";
|
||||
import type { BasicLogger } from "@cline/shared";
|
||||
|
||||
let earlyLogger: BasicLogger | undefined;
|
||||
|
||||
/**
|
||||
* Register the logger used by {@link sdkDebug}. Pass `undefined` to disable.
|
||||
*/
|
||||
export function setSdkLogger(logger: BasicLogger | undefined): void {
|
||||
earlyLogger = logger;
|
||||
}
|
||||
|
||||
/** @internal Returns the currently registered early logger (for testing). */
|
||||
export function getSdkLogger(): BasicLogger | undefined {
|
||||
return earlyLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Short, stable fingerprint of a secret for debug logging (8 hex digits of a
|
||||
* SHA-256 digest). The same input always yields the same hash, making it easy
|
||||
* to eyeball whether a token changed across log entries without leaking it.
|
||||
*
|
||||
* Returns `"unset"` for undefined/null/empty so absence is still visible.
|
||||
*/
|
||||
export function hashSecret(value: unknown): string {
|
||||
if (typeof value !== "string" || value.length === 0) {
|
||||
return "unset";
|
||||
}
|
||||
return createHash("sha256").update(value).digest("hex").slice(0, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a debug-level SDK diagnostic message (already a formatted string).
|
||||
* Best-effort: swallows errors from the underlying logger so logging never
|
||||
* breaks auth/storage flows.
|
||||
*/
|
||||
export function sdkDebug(message: string): void {
|
||||
try {
|
||||
earlyLogger?.debug(message);
|
||||
} catch {
|
||||
// Never let logging break the app.
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildConnectionUpdate } from "./connection-update";
|
||||
|
||||
describe("buildConnectionUpdate", () => {
|
||||
const base = {
|
||||
providerId: "openai",
|
||||
modelId: "codex-test",
|
||||
apiKey: "sk-test",
|
||||
};
|
||||
|
||||
it("includes only the connection fields that are defined", () => {
|
||||
expect(buildConnectionUpdate(base)).toEqual(base);
|
||||
expect(
|
||||
buildConnectionUpdate({
|
||||
...base,
|
||||
baseUrl: "https://example.test",
|
||||
headers: { "x-a": "1" },
|
||||
}),
|
||||
).toEqual({
|
||||
...base,
|
||||
baseUrl: "https://example.test",
|
||||
headers: { "x-a": "1" },
|
||||
});
|
||||
});
|
||||
|
||||
it("passes an empty string through so callers can clear a field", () => {
|
||||
expect(buildConnectionUpdate({ ...base, apiKey: "" })).toEqual({
|
||||
...base,
|
||||
apiKey: "",
|
||||
});
|
||||
});
|
||||
|
||||
it("clears reasoning when thinking is explicitly false", () => {
|
||||
expect(
|
||||
buildConnectionUpdate({
|
||||
...base,
|
||||
thinking: false,
|
||||
reasoningEffort: "high",
|
||||
thinkingBudgetTokens: 1024,
|
||||
}),
|
||||
).toEqual({
|
||||
...base,
|
||||
thinking: false,
|
||||
reasoningEffort: null,
|
||||
thinkingBudgetTokens: null,
|
||||
});
|
||||
});
|
||||
|
||||
it("enables reasoning with the selected effort", () => {
|
||||
expect(
|
||||
buildConnectionUpdate({
|
||||
...base,
|
||||
thinking: true,
|
||||
reasoningEffort: "low",
|
||||
}),
|
||||
).toEqual({
|
||||
...base,
|
||||
thinking: true,
|
||||
reasoningEffort: "low",
|
||||
thinkingBudgetTokens: null,
|
||||
});
|
||||
});
|
||||
|
||||
it("clears stale effort and budget when thinking is enabled without them", () => {
|
||||
expect(buildConnectionUpdate({ ...base, thinking: true })).toEqual({
|
||||
...base,
|
||||
thinking: true,
|
||||
reasoningEffort: null,
|
||||
thinkingBudgetTokens: null,
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps a provided budget when thinking is enabled explicitly", () => {
|
||||
expect(
|
||||
buildConnectionUpdate({
|
||||
...base,
|
||||
thinking: true,
|
||||
thinkingBudgetTokens: 1024,
|
||||
}),
|
||||
).toEqual({
|
||||
...base,
|
||||
thinking: true,
|
||||
reasoningEffort: null,
|
||||
thinkingBudgetTokens: 1024,
|
||||
});
|
||||
});
|
||||
|
||||
it("enables thinking when only an effort is provided", () => {
|
||||
expect(
|
||||
buildConnectionUpdate({ ...base, reasoningEffort: "medium" }),
|
||||
).toEqual({ ...base, thinking: true, reasoningEffort: "medium" });
|
||||
});
|
||||
|
||||
it("enables thinking and truncates a fractional budget", () => {
|
||||
expect(
|
||||
buildConnectionUpdate({ ...base, thinkingBudgetTokens: 2048.7 }),
|
||||
).toEqual({ ...base, thinking: true, thinkingBudgetTokens: 2048 });
|
||||
});
|
||||
|
||||
it("leaves reasoning untouched when thinking is unset", () => {
|
||||
expect(buildConnectionUpdate(base)).toEqual(base);
|
||||
});
|
||||
});
|
||||
@@ -12,72 +12,6 @@ export interface ConnectionUpdate {
|
||||
thinkingBudgetTokens?: CoreSessionConfig["thinkingBudgetTokens"] | null;
|
||||
}
|
||||
|
||||
export interface ConnectionUpdateInput {
|
||||
providerId?: string;
|
||||
modelId?: string;
|
||||
apiKey?: string;
|
||||
baseUrl?: string;
|
||||
headers?: Record<string, string>;
|
||||
providerConfig?: CoreSessionConfig["providerConfig"];
|
||||
thinking?: boolean;
|
||||
reasoningEffort?: CoreSessionConfig["reasoningEffort"];
|
||||
thinkingBudgetTokens?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a ConnectionUpdate for switching a live session's provider/model
|
||||
* connection. Shared by clients (CLI, desktop sidecar, …) so the
|
||||
* thinking/reasoning transition rules stay consistent:
|
||||
* - `thinking: false` disables reasoning and clears effort/budget.
|
||||
* - `thinking: true` (or a provided effort/budget) enables reasoning; an
|
||||
* explicit `thinking: true` resets the reasoning state, so a stale effort or
|
||||
* token budget from the previous connection is cleared unless a replacement
|
||||
* accompanies it. A bare effort/budget without a `thinking` flag updates
|
||||
* only the provided field.
|
||||
* - `thinking: undefined` leaves the session's reasoning state untouched.
|
||||
* Connection fields are included only when defined; an empty string is passed
|
||||
* through (it means "clear"), so callers that want to skip blanks must drop
|
||||
* them before calling.
|
||||
*/
|
||||
export function buildConnectionUpdate(
|
||||
input: ConnectionUpdateInput,
|
||||
): ConnectionUpdate {
|
||||
const update: ConnectionUpdate = {};
|
||||
if (input.providerId !== undefined) update.providerId = input.providerId;
|
||||
if (input.modelId !== undefined) update.modelId = input.modelId;
|
||||
if (input.apiKey !== undefined) update.apiKey = input.apiKey;
|
||||
if (input.baseUrl !== undefined) update.baseUrl = input.baseUrl;
|
||||
if (input.headers !== undefined) update.headers = input.headers;
|
||||
if (input.providerConfig !== undefined) {
|
||||
update.providerConfig = input.providerConfig;
|
||||
}
|
||||
if (input.thinking === false) {
|
||||
update.thinking = false;
|
||||
update.reasoningEffort = null;
|
||||
update.thinkingBudgetTokens = null;
|
||||
return update;
|
||||
}
|
||||
if (input.thinking === true) {
|
||||
update.thinking = true;
|
||||
update.reasoningEffort = input.reasoningEffort ?? null;
|
||||
// Cleared symmetrically with reasoningEffort; a valid budget below
|
||||
// overwrites the null.
|
||||
update.thinkingBudgetTokens = null;
|
||||
} else if (input.reasoningEffort !== undefined) {
|
||||
update.thinking = true;
|
||||
update.reasoningEffort = input.reasoningEffort;
|
||||
}
|
||||
if (
|
||||
typeof input.thinkingBudgetTokens === "number" &&
|
||||
Number.isFinite(input.thinkingBudgetTokens) &&
|
||||
input.thinkingBudgetTokens > 0
|
||||
) {
|
||||
update.thinking = true;
|
||||
update.thinkingBudgetTokens = Math.trunc(input.thinkingBudgetTokens);
|
||||
}
|
||||
return update;
|
||||
}
|
||||
|
||||
export function normalizeConnectionUpdate(
|
||||
updates: ConnectionUpdate,
|
||||
): ConnectionUpdate {
|
||||
|
||||
@@ -342,87 +342,6 @@ describe("LocalRuntimeHost", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("persists provider/model connection updates to the session manifest", async () => {
|
||||
const sessionId = "sess-connection-manifest-update";
|
||||
const manifest = createManifest(sessionId);
|
||||
const sessionService = {
|
||||
ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
|
||||
createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
|
||||
manifestPath: "/tmp/manifest.json",
|
||||
messagesPath: "/tmp/messages.json",
|
||||
manifest,
|
||||
}),
|
||||
persistSessionMessages: vi.fn(),
|
||||
updateSessionStatus: vi.fn().mockResolvedValue({ updated: true }),
|
||||
writeSessionManifest: vi.fn(),
|
||||
listSessions: vi.fn().mockResolvedValue([]),
|
||||
deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
|
||||
};
|
||||
const runtimeBuilder = {
|
||||
build: vi.fn().mockReturnValue({ tools: [], shutdown: vi.fn() }),
|
||||
};
|
||||
const agent = {
|
||||
run: vi.fn().mockResolvedValue(createResult()),
|
||||
continue: vi.fn().mockResolvedValue(createResult()),
|
||||
getMessages: vi.fn().mockReturnValue([]),
|
||||
getAgentId: vi.fn().mockReturnValue("agent-root-1"),
|
||||
getConversationId: vi.fn().mockReturnValue("conv-root-1"),
|
||||
abort: vi.fn(),
|
||||
subscribeEvents: vi.fn().mockReturnValue(() => {}),
|
||||
updateConnection: vi.fn(),
|
||||
canStartRun: vi.fn().mockReturnValue(true),
|
||||
shutdown: vi.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
const createAgent = vi.fn(() => agent as never);
|
||||
const manager = new RuntimeHostUnderTest({
|
||||
distinctId,
|
||||
sessionService: sessionService as never,
|
||||
runtimeBuilder: runtimeBuilder as never,
|
||||
createAgent,
|
||||
});
|
||||
|
||||
await manager.startSession(
|
||||
normalizeStartInput({
|
||||
config: createConfig({ sessionId }),
|
||||
prompt: "hello",
|
||||
interactive: true,
|
||||
}),
|
||||
);
|
||||
sessionService.writeSessionManifest.mockClear();
|
||||
|
||||
// A disk-only writer (compaction path, rename) updated the manifest
|
||||
// behind the in-memory copy's back; the connection update must re-read
|
||||
// and preserve it instead of clobbering it with the stale copy.
|
||||
const diskManifest = {
|
||||
...manifest,
|
||||
compaction_path: "/tmp/compaction.json",
|
||||
title: "renamed session",
|
||||
};
|
||||
const readSessionManifest = vi.fn().mockResolvedValue(diskManifest);
|
||||
(sessionService as Record<string, unknown>).readSessionManifest =
|
||||
readSessionManifest;
|
||||
|
||||
await manager.updateSessionConnection(sessionId, {
|
||||
providerId: "openai",
|
||||
modelId: "codex-test",
|
||||
});
|
||||
|
||||
expect(sessionService.writeSessionManifest).toHaveBeenCalledWith(
|
||||
"/tmp/manifest.json",
|
||||
expect.objectContaining({
|
||||
session_id: sessionId,
|
||||
provider: "openai",
|
||||
model: "codex-test",
|
||||
compaction_path: "/tmp/compaction.json",
|
||||
title: "renamed session",
|
||||
}),
|
||||
);
|
||||
|
||||
sessionService.writeSessionManifest.mockClear();
|
||||
await manager.updateSessionConnection(sessionId, { thinking: true });
|
||||
expect(sessionService.writeSessionManifest).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("persists thinking budget token connection updates", async () => {
|
||||
const sessionId = "sess-thinking-budget-update";
|
||||
const manifest = createManifest(sessionId);
|
||||
|
||||
@@ -226,8 +226,6 @@ export class LocalRuntimeHost implements RuntimeHost {
|
||||
private readonly defaultFetch?: typeof fetch;
|
||||
private readonly events = new RuntimeHostEventBus();
|
||||
private readonly sessions = new Map<string, ActiveSession>();
|
||||
// Serializes manifest read-modify-writes per session; see mutateSessionManifest.
|
||||
private readonly manifestMutationQueues = new Map<string, Promise<void>>();
|
||||
private readonly usageBySession = new Map<string, SessionAccumulatedUsage>();
|
||||
private readonly aggregateUsageBySession = new Map<
|
||||
string,
|
||||
@@ -1333,60 +1331,6 @@ export class LocalRuntimeHost implements RuntimeHost {
|
||||
);
|
||||
session.agent.updateConnection(updates);
|
||||
session.runtime.teamRuntime?.updateTeammateConnections(teammateUpdates);
|
||||
// Keep the persisted manifest in sync so session history reflects the
|
||||
// connection the session is now using, not the one it started with.
|
||||
if (updates.providerId || updates.modelId) {
|
||||
await this.mutateSessionManifest(session, (manifest) => {
|
||||
if (updates.providerId) manifest.provider = updates.providerId;
|
||||
if (updates.modelId) manifest.model = updates.modelId;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialized read-modify-write for a live session's manifest. Every
|
||||
* manifest write for an active session MUST go through this helper: it
|
||||
* re-reads the disk manifest first so disk-only writers (compaction-path
|
||||
* updates, title/metadata renames) are never reverted by a stale in-memory
|
||||
* copy, applies the field-level mutation, syncs the in-memory copy, and
|
||||
* persists — one mutation at a time per session.
|
||||
*/
|
||||
private async mutateSessionManifest(
|
||||
session: ActiveSession,
|
||||
mutate: (manifest: SessionManifest) => void,
|
||||
): Promise<SessionManifest | undefined> {
|
||||
const artifacts = session.artifacts;
|
||||
if (!artifacts) return undefined;
|
||||
const sessionId = session.sessionId;
|
||||
const tail =
|
||||
this.manifestMutationQueues.get(sessionId) ?? Promise.resolve();
|
||||
const next = tail.then(async () => {
|
||||
const latest =
|
||||
(await this.invokeOptionalValue<SessionManifest>(
|
||||
"readSessionManifest",
|
||||
sessionId,
|
||||
)) ?? artifacts.manifest;
|
||||
mutate(latest);
|
||||
artifacts.manifest = latest;
|
||||
await this.invoke<void>(
|
||||
"writeSessionManifest",
|
||||
artifacts.manifestPath,
|
||||
latest,
|
||||
);
|
||||
return latest;
|
||||
});
|
||||
const queued = next.then(
|
||||
() => undefined,
|
||||
() => undefined,
|
||||
);
|
||||
this.manifestMutationQueues.set(sessionId, queued);
|
||||
try {
|
||||
return await next;
|
||||
} finally {
|
||||
if (this.manifestMutationQueues.get(sessionId) === queued) {
|
||||
this.manifestMutationQueues.delete(sessionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Retained for unit tests that reach in via Reflect.
|
||||
@@ -1959,26 +1903,31 @@ export class LocalRuntimeHost implements RuntimeHost {
|
||||
exitCode,
|
||||
);
|
||||
if (!result.updated) return;
|
||||
const latestManifest = await this.mutateSessionManifest(
|
||||
session,
|
||||
(manifest) => {
|
||||
manifest.status = status;
|
||||
if (isNonTerminalSessionStatus(status)) {
|
||||
delete manifest.ended_at;
|
||||
manifest.exit_code = null;
|
||||
} else {
|
||||
manifest.ended_at = result.endedAt ?? nowIso();
|
||||
manifest.exit_code = typeof exitCode === "number" ? exitCode : null;
|
||||
}
|
||||
},
|
||||
);
|
||||
if (!latestManifest) return;
|
||||
const latestManifest =
|
||||
(await this.invokeOptionalValue<SessionManifest>(
|
||||
"readSessionManifest",
|
||||
session.sessionId,
|
||||
)) ?? session.artifacts.manifest;
|
||||
latestManifest.status = status;
|
||||
if (isNonTerminalSessionStatus(status)) {
|
||||
delete latestManifest.ended_at;
|
||||
latestManifest.exit_code = null;
|
||||
} else {
|
||||
latestManifest.ended_at = result.endedAt ?? nowIso();
|
||||
latestManifest.exit_code = typeof exitCode === "number" ? exitCode : null;
|
||||
}
|
||||
session.artifacts.manifest = latestManifest;
|
||||
session.status = status;
|
||||
session.updatedAt = result.endedAt ?? nowIso();
|
||||
session.endedAt = isNonTerminalSessionStatus(status)
|
||||
? null
|
||||
: latestManifest.ended_at;
|
||||
session.exitCode = latestManifest.exit_code;
|
||||
await this.invoke<void>(
|
||||
"writeSessionManifest",
|
||||
session.artifacts.manifestPath,
|
||||
latestManifest,
|
||||
);
|
||||
this.emitStatus(session.sessionId, status);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { ITelemetryService } from "@cline/shared";
|
||||
import { hashSecret, sdkDebug } from "../../logging/early-logger";
|
||||
import {
|
||||
getProviderAuthHandler,
|
||||
getProviderOAuthCredentialsFromSettings,
|
||||
@@ -114,9 +113,6 @@ export class RuntimeOAuthTokenManager {
|
||||
const settings =
|
||||
this.providerSettingsManager.getProviderSettings(storageProviderId);
|
||||
if (!settings) {
|
||||
sdkDebug(
|
||||
`oauth.resolve providerId=${providerId} storageProviderId=${storageProviderId} outcome=no_settings`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -125,16 +121,9 @@ export class RuntimeOAuthTokenManager {
|
||||
settings,
|
||||
);
|
||||
if (!currentCredentials) {
|
||||
sdkDebug(
|
||||
`oauth.resolve providerId=${providerId} storageProviderId=${storageProviderId} outcome=no_credentials`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
sdkDebug(
|
||||
`oauth.resolve.start providerId=${providerId} storageProviderId=${storageProviderId} forceRefresh=${forceRefresh} accessTokenHash=${hashSecret(currentCredentials.access)} refreshTokenHash=${hashSecret(currentCredentials.refresh)}`,
|
||||
);
|
||||
|
||||
const nextCredentials = await handler.refresh({
|
||||
settings,
|
||||
credentials: currentCredentials,
|
||||
@@ -142,9 +131,6 @@ export class RuntimeOAuthTokenManager {
|
||||
telemetry: this.telemetry,
|
||||
});
|
||||
if (!nextCredentials) {
|
||||
sdkDebug(
|
||||
`oauth.resolve providerId=${providerId} outcome=refresh_returned_null`,
|
||||
);
|
||||
throw new OAuthReauthRequiredError(providerId);
|
||||
}
|
||||
|
||||
@@ -158,15 +144,10 @@ export class RuntimeOAuthTokenManager {
|
||||
});
|
||||
const wasRefreshed = !authSettingsEqual(settings.auth, nextSettings.auth);
|
||||
if (wasRefreshed) {
|
||||
sdkDebug(
|
||||
`oauth.resolve.refreshed providerId=${providerId} newAccessTokenHash=${hashSecret(nextCredentials.access)} newRefreshTokenHash=${hashSecret(nextCredentials.refresh)} savingToDisk=true`,
|
||||
);
|
||||
this.providerSettingsManager.saveProviderSettings(nextSettings, {
|
||||
setLastUsed: false,
|
||||
tokenSource: "oauth",
|
||||
});
|
||||
} else {
|
||||
sdkDebug(`oauth.resolve providerId=${providerId} outcome=not_refreshed`);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -649,6 +649,78 @@ describe("SessionRuntime message preparation", () => {
|
||||
expect(configs).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("chains afterModel message replacements across extensions", async () => {
|
||||
const seenBySecondHook: unknown[] = [];
|
||||
const replacer: AgentExtension = {
|
||||
name: "xml-parser-ext",
|
||||
manifest: { capabilities: ["hooks"] },
|
||||
hooks: {
|
||||
afterModel: ({ assistantMessage }) => ({
|
||||
message: {
|
||||
...assistantMessage,
|
||||
content: [
|
||||
{
|
||||
type: "tool-call",
|
||||
toolCallId: "call_xml_1",
|
||||
toolName: "echo",
|
||||
input: { text: "hi" },
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
const observer: AgentExtension = {
|
||||
name: "observer-ext",
|
||||
manifest: { capabilities: ["hooks"] },
|
||||
hooks: {
|
||||
afterModel: ({ assistantMessage }) => {
|
||||
seenBySecondHook.push(assistantMessage.content[0]?.type);
|
||||
return undefined;
|
||||
},
|
||||
},
|
||||
};
|
||||
const { deps } = makeRecordingRuntimeFactory();
|
||||
const session = new SessionRuntime(
|
||||
makeAgentConfig({ extensions: [replacer, observer] }),
|
||||
deps,
|
||||
);
|
||||
|
||||
await (
|
||||
session as unknown as {
|
||||
ensureExtensionsInitialized(): Promise<void>;
|
||||
}
|
||||
).ensureExtensionsInitialized();
|
||||
const hooks = (
|
||||
session as unknown as {
|
||||
createRuntimeHooks(): AgentRuntimeConfig["hooks"];
|
||||
}
|
||||
).createRuntimeHooks();
|
||||
const afterModel = hooks?.afterModel;
|
||||
expect(afterModel).toBeDefined();
|
||||
|
||||
const result = await afterModel?.({
|
||||
snapshot: makeSnapshot(),
|
||||
assistantMessage: {
|
||||
id: "a1",
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "<echo><text>hi</text></echo>" }],
|
||||
createdAt: 1,
|
||||
},
|
||||
finishReason: "stop",
|
||||
});
|
||||
|
||||
expect(result?.message?.content).toEqual([
|
||||
{
|
||||
type: "tool-call",
|
||||
toolCallId: "call_xml_1",
|
||||
toolName: "echo",
|
||||
input: { text: "hi" },
|
||||
},
|
||||
]);
|
||||
expect(seenBySecondHook).toEqual(["tool-call"]);
|
||||
});
|
||||
|
||||
it("adapts prepareTurn with API-safe messages for runtime compaction", async () => {
|
||||
const prepareTurn = vi.fn(() => ({
|
||||
messages: [
|
||||
|
||||
@@ -189,11 +189,20 @@ function mergeRuntimeHooks(
|
||||
return aggregate;
|
||||
},
|
||||
afterModel: async (ctx) => {
|
||||
let assistantMessage = ctx.assistantMessage;
|
||||
let aggregate:
|
||||
| Awaited<ReturnType<NonNullable<AgentRuntimeHooks["afterModel"]>>>
|
||||
| undefined;
|
||||
for (const hook of hooks) {
|
||||
const result = await hook.afterModel?.(ctx);
|
||||
if (result?.stop) return result;
|
||||
const result = await hook.afterModel?.({ ...ctx, assistantMessage });
|
||||
if (!result) continue;
|
||||
if (result.stop) return { ...aggregate, ...result };
|
||||
aggregate = { ...aggregate, ...result };
|
||||
if (result.message) {
|
||||
assistantMessage = result.message;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
return aggregate;
|
||||
},
|
||||
beforeTool: async (ctx) => {
|
||||
let input = ctx.input;
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import {
|
||||
mkdirSync,
|
||||
mkdtempSync,
|
||||
readdirSync,
|
||||
readFileSync,
|
||||
rmSync,
|
||||
writeFileSync,
|
||||
} from "node:fs";
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import * as LlmsModels from "@cline/llms";
|
||||
@@ -58,50 +51,6 @@ describe("ProviderSettingsManager", () => {
|
||||
expect(reloaded.read().providers.anthropic?.tokenSource).toBe("manual");
|
||||
});
|
||||
|
||||
it("writes atomically, leaving no temp file behind", () => {
|
||||
const tempDir = mkdtempSync(
|
||||
path.join(os.tmpdir(), "core-provider-settings-"),
|
||||
);
|
||||
tempDirs.push(tempDir);
|
||||
const filePath = path.join(tempDir, "provider-settings.json");
|
||||
const manager = new ProviderSettingsManager({ filePath });
|
||||
|
||||
manager.saveProviderSettings(
|
||||
{ provider: "anthropic", apiKey: "test-key" },
|
||||
{ setLastUsed: true },
|
||||
);
|
||||
|
||||
const siblings = readdirSync(tempDir);
|
||||
expect(siblings).toEqual(["provider-settings.json"]);
|
||||
});
|
||||
|
||||
it("preserves the previous file when the staged write cannot be renamed", () => {
|
||||
const tempDir = mkdtempSync(
|
||||
path.join(os.tmpdir(), "core-provider-settings-"),
|
||||
);
|
||||
tempDirs.push(tempDir);
|
||||
const filePath = path.join(tempDir, "provider-settings.json");
|
||||
const manager = new ProviderSettingsManager({ filePath });
|
||||
manager.saveProviderSettings(
|
||||
{ provider: "anthropic", apiKey: "before" },
|
||||
{ setLastUsed: true },
|
||||
);
|
||||
const before = readFileSync(filePath, "utf8");
|
||||
|
||||
// Occupying the temp path with a directory makes writeFileSync fail,
|
||||
// simulating a mid-write crash: the destination must be untouched.
|
||||
mkdirSync(`${filePath}.${process.pid}.tmp`);
|
||||
expect(() =>
|
||||
manager.saveProviderSettings(
|
||||
{ provider: "anthropic", apiKey: "after" },
|
||||
{ setLastUsed: true },
|
||||
),
|
||||
).toThrow();
|
||||
rmSync(`${filePath}.${process.pid}.tmp`, { recursive: true, force: true });
|
||||
|
||||
expect(readFileSync(filePath, "utf8")).toBe(before);
|
||||
});
|
||||
|
||||
it("resolves auth storage settings for providers registered with a storage provider id", () => {
|
||||
const tempDir = mkdtempSync(
|
||||
path.join(os.tmpdir(), "core-provider-settings-"),
|
||||
|
||||
@@ -3,15 +3,12 @@ import {
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
readFileSync,
|
||||
renameSync,
|
||||
rmSync,
|
||||
writeFileSync,
|
||||
} from "node:fs";
|
||||
import { basename, dirname } from "node:path";
|
||||
import { resolveProviderSettingsPath } from "@cline/shared/storage";
|
||||
import { getLiveModelsCatalog } from "../..";
|
||||
import { getProviderAuthHandler } from "../../auth/provider-auth-registry";
|
||||
import { hashSecret, sdkDebug } from "../../logging/early-logger";
|
||||
import {
|
||||
emptyStoredProviderSettings,
|
||||
type ProviderConfig,
|
||||
@@ -102,10 +99,6 @@ export class ProviderSettingsManager {
|
||||
const result = StoredProviderSettingsSchema.safeParse(parsed);
|
||||
if (result.success) {
|
||||
registerConfiguredProvidersFromSettings(result.data);
|
||||
const clineAuth = result.data.providers["cline"]?.settings?.auth;
|
||||
sdkDebug(
|
||||
`providers.read providers=[${Object.keys(result.data.providers).join(",")}] lastUsed=${result.data.lastUsedProvider ?? "none"} clineAuthPresent=${!!clineAuth?.accessToken} clineAccessTokenHash=${hashSecret(clineAuth?.accessToken)} clineRefreshTokenHash=${hashSecret(clineAuth?.refreshToken)}`,
|
||||
);
|
||||
return result.data;
|
||||
}
|
||||
} catch {
|
||||
@@ -121,21 +114,11 @@ export class ProviderSettingsManager {
|
||||
if (!existsSync(dir)) {
|
||||
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
||||
}
|
||||
// Stage to a pid-unique temp file and rename into place. Concurrent
|
||||
// Cline processes (CLI, extension, hub) share this file; a bare
|
||||
// writeFileSync lets readers catch a partial file, which read() treats
|
||||
// as empty settings — indistinguishable from being logged out.
|
||||
const tempPath = `${this.filePath}.${process.pid}.tmp`;
|
||||
try {
|
||||
writeFileSync(tempPath, `${JSON.stringify(normalized, null, 2)}\n`, {
|
||||
encoding: "utf8",
|
||||
mode: 0o600,
|
||||
});
|
||||
renameSync(tempPath, this.filePath);
|
||||
} catch (error) {
|
||||
rmSync(tempPath, { force: true });
|
||||
throw error;
|
||||
}
|
||||
writeFileSync(
|
||||
this.filePath,
|
||||
`${JSON.stringify(normalized, null, 2)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
// Restrict file to owner-only read/write (best-effort; no-op on Windows).
|
||||
try {
|
||||
chmodSync(this.filePath, 0o600);
|
||||
@@ -171,16 +154,6 @@ export class ProviderSettingsManager {
|
||||
: previous.lastUsedProvider,
|
||||
};
|
||||
this.write(next);
|
||||
const prevClineAuth = previous.providers["cline"]?.settings?.auth;
|
||||
const nextClineAuth =
|
||||
validatedSettings.provider === "cline"
|
||||
? validatedSettings.auth
|
||||
: next.providers["cline"]?.settings?.auth;
|
||||
const authDropped =
|
||||
!!prevClineAuth?.accessToken && !nextClineAuth?.accessToken;
|
||||
sdkDebug(
|
||||
`providers.save providerId=${providerId} tokenSource=${tokenSource} clineAuthWasPresent=${!!prevClineAuth?.accessToken} clineAuthIsPresent=${!!nextClineAuth?.accessToken} authDropped=${authDropped}`,
|
||||
);
|
||||
return next;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,6 @@ export const CORE_TELEMETRY_EVENTS = {
|
||||
AUTH_SUCCEEDED: "user.auth_succeeded",
|
||||
AUTH_FAILED: "user.auth_failed",
|
||||
AUTH_LOGGED_OUT: "user.auth_logged_out",
|
||||
AUTH_REFRESH_SOFT_FAILURE: "user.auth_refresh_soft_failure",
|
||||
PROVIDER_CONFIGURED: "user.provider_configured",
|
||||
TELEMETRY_OPT_OUT: "user.opt_out",
|
||||
},
|
||||
@@ -253,40 +252,10 @@ export function captureAuthLoggedOut(
|
||||
telemetry: ITelemetryService | undefined,
|
||||
provider?: string,
|
||||
reason?: string,
|
||||
details?: { status?: number; errorCode?: string },
|
||||
): void {
|
||||
emit(telemetry, CORE_TELEMETRY_EVENTS.USER.AUTH_LOGGED_OUT, {
|
||||
provider,
|
||||
reason,
|
||||
status: details?.status,
|
||||
errorCode: details?.errorCode,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires when a token refresh fails for a reason that does NOT invalidate the
|
||||
* session (network error, timeout, 5xx) and stored credentials were kept.
|
||||
* Before the transient-vs-invalid_grant fix, `tokenExpired: true` instances
|
||||
* were misclassified as invalid grants and wiped stored credentials — this
|
||||
* event is the "prevented logout" counter for tracking that fix in
|
||||
* production.
|
||||
*/
|
||||
export function captureAuthRefreshSoftFailure(
|
||||
telemetry: ITelemetryService | undefined,
|
||||
provider?: string,
|
||||
details?: {
|
||||
status?: number;
|
||||
errorCode?: string;
|
||||
errorName?: string;
|
||||
tokenExpired?: boolean;
|
||||
},
|
||||
): void {
|
||||
emit(telemetry, CORE_TELEMETRY_EVENTS.USER.AUTH_REFRESH_SOFT_FAILURE, {
|
||||
provider,
|
||||
status: details?.status,
|
||||
errorCode: details?.errorCode,
|
||||
errorName: details?.errorName,
|
||||
tokenExpired: details?.tokenExpired,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@cline/llms",
|
||||
"version": "0.0.60",
|
||||
"version": "0.0.59",
|
||||
"description": "Config-driven SDK for selecting, extending, and instantiating LLM providers and models",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -63,7 +63,6 @@ export {
|
||||
ClinePassLimitError,
|
||||
createHandler,
|
||||
createHandlerAsync,
|
||||
extractClinePassLimitMessage,
|
||||
getClineOrgIndividualInferenceSubscriptionMessage,
|
||||
getClineNotSubscribedMessage,
|
||||
getClinePassSubscriptionUrl,
|
||||
|
||||
@@ -2,7 +2,6 @@ export {
|
||||
ClineNotSubscribedError,
|
||||
ClineOrgIndividualInferenceSubscriptionError,
|
||||
ClinePassLimitError,
|
||||
extractClinePassLimitMessage,
|
||||
getClineOrgIndividualInferenceSubscriptionMessage,
|
||||
getClineNotSubscribedMessage,
|
||||
getClinePassSubscriptionUrl,
|
||||
|
||||
@@ -32,7 +32,6 @@ export {
|
||||
ClineNotSubscribedError,
|
||||
ClineOrgIndividualInferenceSubscriptionError,
|
||||
ClinePassLimitError,
|
||||
extractClinePassLimitMessage,
|
||||
getClineOrgIndividualInferenceSubscriptionMessage,
|
||||
getClineNotSubscribedMessage,
|
||||
getClinePassSubscriptionUrl,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@cline/sdk",
|
||||
"description": "Cline SDK - user-facing alias for @cline/core",
|
||||
"version": "0.0.60",
|
||||
"version": "0.0.59",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cline/cline",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@cline/shared",
|
||||
"version": "0.0.60",
|
||||
"version": "0.0.59",
|
||||
"description": "Shared utilities, types, and schemas for Cline packages",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -290,6 +290,19 @@ export interface AgentAfterModelContext {
|
||||
finishReason: AgentModelFinishReason;
|
||||
}
|
||||
|
||||
export interface AgentAfterModelResult {
|
||||
stop?: boolean;
|
||||
reason?: string;
|
||||
/**
|
||||
* Replacement assistant message. When set, the runtime uses this message
|
||||
* instead of the streamed one — including its `tool-call` parts, which are
|
||||
* executed as if the model had emitted them natively. Metrics and model
|
||||
* info from the original message are preserved unless the replacement
|
||||
* carries its own.
|
||||
*/
|
||||
message?: AgentMessage;
|
||||
}
|
||||
|
||||
export interface AgentBeforeToolContext {
|
||||
snapshot: AgentRuntimeStateSnapshot;
|
||||
tool: AgentTool;
|
||||
@@ -348,7 +361,10 @@ export interface AgentRuntimeHooks {
|
||||
| Promise<AgentBeforeModelResult | undefined>;
|
||||
afterModel?: (
|
||||
context: AgentAfterModelContext,
|
||||
) => AgentStopControl | undefined | Promise<AgentStopControl | undefined>;
|
||||
) =>
|
||||
| AgentAfterModelResult
|
||||
| undefined
|
||||
| Promise<AgentAfterModelResult | undefined>;
|
||||
beforeTool?: (
|
||||
context: AgentBeforeToolContext,
|
||||
) =>
|
||||
|
||||
Reference in New Issue
Block a user