mirror of
https://github.com/cline/cline.git
synced 2026-09-04 20:02:30 +08:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 18e2bdf194 | |||
| b83c9ec240 | |||
| 711a8c6ec0 | |||
| d68badae38 | |||
| d67c403392 | |||
| 02f405f6e2 | |||
| b4ed8a226e | |||
| cbf40961db | |||
| 2d05ba52da | |||
| 5d3778b5cf | |||
| 8d0eb54a1f | |||
| a3989acc38 | |||
| d199b1bff9 | |||
| d41eed1198 | |||
| 6309971089 | |||
| 2d2c669421 | |||
| c5f146a418 | |||
| 261ee4c313 | |||
| d45b051c04 | |||
| 78c83cdf33 |
@@ -1,5 +1,15 @@
|
||||
# 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.39",
|
||||
"version": "3.0.40",
|
||||
"description": "Autonomous coding agent CLI - capable of creating/editing files, running commands, using the browser, and more",
|
||||
"type": "module",
|
||||
"publishConfig": {
|
||||
|
||||
@@ -101,6 +101,22 @@ 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,7 +118,12 @@ export function getInstallationInfo(currentVersion: string): InstallationInfo {
|
||||
updateCommand: `yarn global add ${DEFAULT_PACKAGE_NAME}@${tag}`,
|
||||
};
|
||||
}
|
||||
if (scriptPath.includes("/.bun/bin")) {
|
||||
// `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/")
|
||||
) {
|
||||
return {
|
||||
packageManager: PackageManager.BUN,
|
||||
packageName: DEFAULT_PACKAGE_NAME,
|
||||
|
||||
@@ -158,8 +158,9 @@ vi.mock("./runtime/run-interactive", () => {
|
||||
});
|
||||
vi.mock("./utils/session", () => sessionMocks);
|
||||
vi.mock("./session/session", () => sessionMocks);
|
||||
vi.mock("@cline/core", () => {
|
||||
vi.mock("@cline/core", async () => {
|
||||
return {
|
||||
...(await vi.importActual("@cline/core")),
|
||||
resolveProviderConfig: llmMocks.resolveProviderConfig,
|
||||
createTeamName: vi.fn(() => "team-test"),
|
||||
createUserInstructionConfigService: vi.fn(() => ({
|
||||
|
||||
@@ -928,6 +928,17 @@ 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,6 +157,7 @@ function makeManager() {
|
||||
ingestHookEvent: vi.fn(),
|
||||
subscribe: vi.fn(),
|
||||
updateSessionModel: vi.fn(),
|
||||
updateSessionConnection: vi.fn(async () => {}),
|
||||
pendingPrompts: {
|
||||
update: vi.fn(),
|
||||
},
|
||||
@@ -814,6 +815,83 @@ 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,6 +49,9 @@ 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;
|
||||
};
|
||||
@@ -210,12 +213,18 @@ 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(),
|
||||
config: {
|
||||
...buildSessionConfig(),
|
||||
...(sessionId ? { sessionId } : {}),
|
||||
},
|
||||
toolPolicies: input.config.toolPolicies,
|
||||
interactive: true,
|
||||
initialMessages: initial,
|
||||
@@ -415,7 +424,14 @@ 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;
|
||||
@@ -431,6 +447,7 @@ export function createInteractiveSessionRuntime(input: {
|
||||
messages,
|
||||
sessionMetadata,
|
||||
initialCompactionState,
|
||||
reuseSessionId,
|
||||
);
|
||||
})().catch((error) => {
|
||||
startupError = error;
|
||||
@@ -473,9 +490,24 @@ 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([]);
|
||||
};
|
||||
@@ -840,6 +872,7 @@ export function createInteractiveSessionRuntime(input: {
|
||||
resetForNewSession,
|
||||
restartWithMessages,
|
||||
restartWithCurrentMessages,
|
||||
updateCurrentSessionConnection,
|
||||
resumeSession,
|
||||
forkCurrentSession,
|
||||
compactCurrentSession,
|
||||
|
||||
@@ -43,6 +43,15 @@ 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";
|
||||
|
||||
@@ -65,6 +74,30 @@ 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",
|
||||
@@ -769,6 +802,126 @@ 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,5 +1,9 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { resolveReasoningForModelChange } from "./run-interactive";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { Config } from "../utils/types";
|
||||
import {
|
||||
applyInteractiveModelChange,
|
||||
resolveReasoningForModelChange,
|
||||
} from "./run-interactive";
|
||||
|
||||
describe("resolveReasoningForModelChange", () => {
|
||||
it("persists disabled reasoning only when thinking is explicitly false", () => {
|
||||
@@ -38,3 +42,69 @@ 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,6 +82,51 @@ 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,
|
||||
@@ -687,25 +732,12 @@ export async function runInteractive(
|
||||
onNewSession: async () => {
|
||||
await sessionRuntime.resetForNewSession();
|
||||
},
|
||||
onModelChange: async () => {
|
||||
await sessionRuntime.ensureReady();
|
||||
await onProviderChange({
|
||||
onModelChange: () =>
|
||||
applyInteractiveModelChange({
|
||||
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 }),
|
||||
});
|
||||
await sessionRuntime.restartWithCurrentMessages();
|
||||
},
|
||||
providerSettingsManager,
|
||||
sessionRuntime,
|
||||
}),
|
||||
onSessionRestart: async () => {
|
||||
await sessionRuntime.ensureReady();
|
||||
await sessionRuntime.restartEmpty();
|
||||
|
||||
@@ -5,9 +5,11 @@ import { useEffect, useState } from "react";
|
||||
import "opentui-spinner/react";
|
||||
import {
|
||||
getClineOrgIndividualInferenceSubscriptionMessage,
|
||||
getClinePassLimitDetailMessage,
|
||||
getCliSubscriptionUrl,
|
||||
getIndividualPlanFeatures,
|
||||
isClineOrgIndividualInferenceSubscriptionErrorMessage,
|
||||
isClinePassLimitErrorMessage,
|
||||
isClinePassSubscriptionError,
|
||||
} from "../../utils/cline-pass-errors";
|
||||
import {
|
||||
@@ -419,6 +421,54 @@ 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;
|
||||
@@ -534,6 +584,15 @@ 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,8 +1,50 @@
|
||||
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,5 +1,16 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildClinePassSubscriptionPageUrl } from "./provider-picker-helpers";
|
||||
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";
|
||||
|
||||
describe("buildClinePassSubscriptionPageUrl", () => {
|
||||
it("opens the personal subscription page on production by default", () => {
|
||||
@@ -16,3 +27,99 @@ 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,7 +37,10 @@ import {
|
||||
getSearchableListRowsWindow,
|
||||
type SearchableItem,
|
||||
} from "../searchable-list";
|
||||
import { buildClinePassSubscriptionPageUrl } from "./provider-picker-helpers";
|
||||
import {
|
||||
buildClinePassSubscriptionPageUrl,
|
||||
saveManualProviderApiKey,
|
||||
} from "./provider-picker-helpers";
|
||||
|
||||
interface ProviderItem {
|
||||
id: string;
|
||||
@@ -724,13 +727,27 @@ 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<boolean> & {
|
||||
props: ChoiceContext<OAuthLoginResult> & {
|
||||
providerId: string;
|
||||
providerName: string;
|
||||
allowApiKeyFallback?: boolean;
|
||||
},
|
||||
) {
|
||||
const { resolve, dismiss, dialogId, providerId, providerName } = props;
|
||||
const {
|
||||
resolve,
|
||||
dismiss,
|
||||
dialogId,
|
||||
providerId,
|
||||
providerName,
|
||||
allowApiKeyFallback,
|
||||
} = props;
|
||||
const [mode, setMode] = useState<"browser" | "device">(
|
||||
providerId === "cline" ? "device" : "browser",
|
||||
);
|
||||
@@ -863,9 +880,18 @@ 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}>
|
||||
@@ -893,7 +919,7 @@ export function OAuthLoginContent(
|
||||
{deviceError && <text fg="red">{deviceError}</text>}
|
||||
|
||||
<text fg="gray">
|
||||
<em>Esc to cancel</em>
|
||||
<em>{escapeHint}</em>
|
||||
</text>
|
||||
</box>
|
||||
);
|
||||
@@ -916,7 +942,82 @@ export function OAuthLoginContent(
|
||||
{error && <text fg="red">{error}</text>}
|
||||
|
||||
<text fg="gray">
|
||||
<em>Esc to cancel</em>
|
||||
<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>
|
||||
</text>
|
||||
</box>
|
||||
);
|
||||
|
||||
@@ -7,7 +7,10 @@ import {
|
||||
type AccountDialogAction,
|
||||
AccountDialogContent,
|
||||
} from "../components/dialogs/account-dialog";
|
||||
import { OAuthLoginContent } from "../components/dialogs/provider-picker";
|
||||
import {
|
||||
OAuthLoginContent,
|
||||
type OAuthLoginResult,
|
||||
} from "../components/dialogs/provider-picker";
|
||||
import type { OpenModelSelectorOptions } from "./use-model-selector";
|
||||
|
||||
export function useAccountDialog(opts: {
|
||||
@@ -60,14 +63,14 @@ export function useAccountDialog(opts: {
|
||||
return;
|
||||
}
|
||||
if (action === "login") {
|
||||
const saved = await dialog.choice<boolean>({
|
||||
const saved = await dialog.choice<OAuthLoginResult>({
|
||||
style: { maxHeight: termHeight - 2 },
|
||||
closeOnEscape: false,
|
||||
content: (ctx: ChoiceContext<boolean>) => (
|
||||
content: (ctx: ChoiceContext<OAuthLoginResult>) => (
|
||||
<OAuthLoginContent {...ctx} providerId="cline" providerName="Cline" />
|
||||
),
|
||||
});
|
||||
if (saved) {
|
||||
if (saved === true) {
|
||||
await onAccountChange?.();
|
||||
await openAccountDialog();
|
||||
return;
|
||||
|
||||
@@ -6,6 +6,7 @@ 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";
|
||||
@@ -21,7 +22,9 @@ import {
|
||||
ClinePassSubscriptionContent,
|
||||
CodexCliStatusContent,
|
||||
type ExistingProviderOption,
|
||||
OAuthApiKeyInputContent,
|
||||
OAuthLoginContent,
|
||||
type OAuthLoginResult,
|
||||
ProviderConfigInputContent,
|
||||
ProviderPickerContent,
|
||||
UseExistingOrReconfigureContent,
|
||||
@@ -131,6 +134,23 @@ 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;
|
||||
@@ -165,17 +185,22 @@ async function runProviderChange(
|
||||
if (needsAuth) {
|
||||
let saved: boolean | undefined;
|
||||
if (isOAuthProvider(newProviderId)) {
|
||||
saved = await dialog.choice<boolean>({
|
||||
const loginResult = await dialog.choice<OAuthLoginResult>({
|
||||
style: { maxHeight: termHeight - 2 },
|
||||
closeOnEscape: false,
|
||||
content: (ctx: ChoiceContext<boolean>) => (
|
||||
content: (ctx: ChoiceContext<OAuthLoginResult>) => (
|
||||
<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,10 +1,13 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
formatCliErrorMessage,
|
||||
getCliClinePassLimitMessage,
|
||||
getCliNotSubscribedMessage,
|
||||
getClineOrgIndividualInferenceSubscriptionMessage,
|
||||
getClinePassLimitDetailMessage,
|
||||
getCliSubscriptionUrl,
|
||||
isClineOrgIndividualInferenceSubscriptionErrorMessage,
|
||||
isClinePassLimitErrorMessage,
|
||||
isClinePassSubscriptionError,
|
||||
} from "./cline-pass-errors";
|
||||
|
||||
@@ -46,4 +49,22 @@ 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,10 +1,13 @@
|
||||
import {
|
||||
type ClineSubscriptionPlan,
|
||||
extractClinePassLimitMessage,
|
||||
getClineOrgIndividualInferenceSubscriptionMessage,
|
||||
isClineNotSubscribedError,
|
||||
isClineNotSubscribedMessage,
|
||||
isClineOrgIndividualInferenceSubscriptionError,
|
||||
isClineOrgIndividualInferenceSubscriptionMessage,
|
||||
isClinePassLimitError,
|
||||
isClinePassLimitMessage,
|
||||
} from "@cline/core";
|
||||
|
||||
import { getClineEnvironmentConfig } from "@cline/shared";
|
||||
@@ -24,6 +27,18 @@ 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[] {
|
||||
@@ -78,6 +93,27 @@ 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();
|
||||
@@ -85,6 +121,11 @@ 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,14 +42,23 @@ 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", () => {
|
||||
@@ -196,6 +205,23 @@ 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,4 +1,5 @@
|
||||
import type { AgentEvent, TeamEvent } from "@cline/core";
|
||||
import { formatCliErrorMessage } from "./cline-pass-errors";
|
||||
import { formatToolInput, formatToolOutput, truncate } from "./helpers";
|
||||
import {
|
||||
c,
|
||||
@@ -181,7 +182,7 @@ export function handleEvent(event: AgentEvent, config: Config): void {
|
||||
case "error":
|
||||
closeInlineStreamIfNeeded();
|
||||
if (!event.recoverable || config.verbose) {
|
||||
writeErr(event.error.message);
|
||||
writeErr(formatCliErrorMessage(event.error));
|
||||
}
|
||||
break;
|
||||
case "notice":
|
||||
|
||||
@@ -42,11 +42,12 @@ 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 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.
|
||||
* 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.
|
||||
*/
|
||||
export function isProviderConfigured(
|
||||
providerId: string,
|
||||
@@ -54,7 +55,8 @@ export function isProviderConfigured(
|
||||
): boolean {
|
||||
if (!settings) return false;
|
||||
if (isOAuthProvider(providerId)) {
|
||||
return Boolean(settings.auth?.accessToken?.trim());
|
||||
// getPersistedProviderApiKey covers both auth.accessToken and apiKey.
|
||||
return Boolean(getPersistedProviderApiKey(providerId, settings));
|
||||
}
|
||||
if (getPersistedProviderApiKey(providerId, settings)) return true;
|
||||
if (settings.baseUrl?.trim()) return true;
|
||||
|
||||
@@ -20,10 +20,13 @@ 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?: {
|
||||
metastring?: string;
|
||||
};
|
||||
properties?: Record<string, unknown>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -67,7 +70,8 @@ const MarkdownCode = ({
|
||||
);
|
||||
}
|
||||
|
||||
const meta = node?.properties?.metastring;
|
||||
const metaValue = node?.properties?.metastring;
|
||||
const meta = typeof metaValue === "string" ? metaValue : undefined;
|
||||
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,6 +1,7 @@
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { basename, join } from "node:path";
|
||||
import {
|
||||
buildConnectionUpdate,
|
||||
buildWorkspaceMetadata,
|
||||
type ClineCore,
|
||||
type CoreSessionConfig,
|
||||
@@ -179,58 +180,43 @@ function buildCoreSessionConfig(config: JsonRecord): JsonRecord {
|
||||
export function buildSessionConnectionUpdate(
|
||||
config: JsonRecord,
|
||||
): SessionConnectionUpdate {
|
||||
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 = {};
|
||||
// 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 providerId = String(config.provider ?? config.providerId ?? "").trim();
|
||||
if (providerId) {
|
||||
updates.providerId = providerId;
|
||||
}
|
||||
const modelId = String(config.model ?? config.modelId ?? "").trim();
|
||||
if (modelId) {
|
||||
updates.modelId = modelId;
|
||||
}
|
||||
const apiKey =
|
||||
const rawApiKey =
|
||||
typeof config.apiKey === "string"
|
||||
? config.apiKey.trim()
|
||||
: typeof config.api_key === "string"
|
||||
? config.api_key.trim()
|
||||
: undefined;
|
||||
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;
|
||||
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 } : {}),
|
||||
});
|
||||
}
|
||||
|
||||
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_MODE, SIDECAR_PORT } from "./types";
|
||||
import { BunRuntime, SIDECAR_HOST, SIDECAR_MODE, SIDECAR_PORT } from "./types";
|
||||
|
||||
const SHUTDOWN_TIMEOUT_MS = 5_000;
|
||||
|
||||
@@ -59,8 +59,10 @@ async function main() {
|
||||
|
||||
const { port } = startServer(ctx, SIDECAR_PORT, shutdown);
|
||||
|
||||
const endpoint = `http://127.0.0.1:${port}`;
|
||||
const wsEndpoint = `ws://127.0.0.1:${port}/transport`;
|
||||
// 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`;
|
||||
process.stdout.write(
|
||||
`${JSON.stringify({
|
||||
type: "ready",
|
||||
|
||||
@@ -4,6 +4,7 @@ import { sendEvent } from "./context";
|
||||
import { fetchMarketplaceCatalog } from "./marketplace";
|
||||
import {
|
||||
BunRuntime,
|
||||
SIDECAR_HOST,
|
||||
SIDECAR_MODE,
|
||||
SIDECAR_PORT,
|
||||
type SidecarContext,
|
||||
@@ -15,12 +16,20 @@ 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 = {
|
||||
@@ -115,7 +124,7 @@ export function startServer(
|
||||
for (const candidate of candidates) {
|
||||
try {
|
||||
server = BunRuntime.serve({
|
||||
hostname: "127.0.0.1",
|
||||
hostname: SIDECAR_HOST,
|
||||
port: candidate,
|
||||
fetch: createFetchHandler(ctx, onShutdown),
|
||||
websocket: createWebSocketHandler(ctx),
|
||||
|
||||
@@ -115,4 +115,8 @@ 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,8 +35,9 @@ 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. 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. `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`).
|
||||
*/
|
||||
export async function resolveDesktopBackendWsEndpoint(): Promise<string> {
|
||||
if (resolvedEndpointCache) return resolvedEndpointCache;
|
||||
@@ -64,8 +65,10 @@ export async function resolveDesktopBackendWsEndpoint(): Promise<string> {
|
||||
throw new Error("Tauri returned an empty desktop backend endpoint");
|
||||
}
|
||||
|
||||
// 3. Default sidecar port for local dev mode without the Tauri bridge.
|
||||
resolvedEndpointCache = "ws://127.0.0.1:3126/transport";
|
||||
// 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";
|
||||
return resolvedEndpointCache;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
/// <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,6 +11,10 @@ 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,5 +1,6 @@
|
||||
import fs from "node:fs"
|
||||
import path from "node:path"
|
||||
import { createRequire } from "node:module"
|
||||
import { fileURLToPath } from "node:url"
|
||||
import * as esbuild from "esbuild"
|
||||
|
||||
@@ -178,6 +179,39 @@ const e2eBuildConfig = {
|
||||
plugins: [aliasResolverPlugin, esbuildProblemMatcherPlugin],
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the plugin sandbox bootstrap from the built @cline/core package into
|
||||
* the extension's dist directory. The bootstrap runs in an isolated child
|
||||
* process spawned by SubprocessSandbox and must be a separate file — it cannot
|
||||
* be inlined into the main bundle. resolveBootstrap() (bundled into
|
||||
* extension.js) searches for it at dist/extensions/plugin-sandbox-bootstrap.js.
|
||||
*
|
||||
* The bootstrap has external runtime dependencies (jiti for TypeScript
|
||||
* transpilation, @cline/shared) that it resolves via Node's standard module
|
||||
* resolution from its on-disk location. Both must be direct dependencies of
|
||||
* the extension so they are present in node_modules and resolvable from
|
||||
* dist/extensions/. The CLI build performs the same copy in apps/cli/bun.mts.
|
||||
*/
|
||||
function copyPluginSandboxBootstrap() {
|
||||
if (e2eBuild) return
|
||||
const projectRequire = createRequire(import.meta.url)
|
||||
let corePackageDir
|
||||
try {
|
||||
corePackageDir = path.dirname(projectRequire.resolve("@cline/core/package.json"))
|
||||
} catch {
|
||||
console.warn("[esbuild] @cline/core not found — skipping plugin sandbox bootstrap copy")
|
||||
return
|
||||
}
|
||||
const bootstrapSrc = path.join(corePackageDir, "dist", "extensions", "plugin-sandbox-bootstrap.js")
|
||||
if (!fs.existsSync(bootstrapSrc)) {
|
||||
console.warn(`[esbuild] plugin-sandbox-bootstrap.js not found at ${bootstrapSrc} — build @cline/core first`)
|
||||
return
|
||||
}
|
||||
const bootstrapDest = path.join(__dirname, destDir, "extensions", "plugin-sandbox-bootstrap.js")
|
||||
fs.mkdirSync(path.dirname(bootstrapDest), { recursive: true })
|
||||
fs.copyFileSync(bootstrapSrc, bootstrapDest)
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const config = standalone ? standaloneConfig : e2eBuild ? e2eBuildConfig : extensionConfig
|
||||
const extensionCtx = await esbuild.context(config)
|
||||
@@ -187,6 +221,7 @@ async function main() {
|
||||
await extensionCtx.rebuild()
|
||||
await extensionCtx.dispose()
|
||||
}
|
||||
copyPluginSandboxBootstrap()
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
|
||||
@@ -467,6 +467,7 @@
|
||||
"@cline/agents": "workspace:*",
|
||||
"@cline/core": "workspace:*",
|
||||
"@cline/llms": "workspace:*",
|
||||
"@cline/sdk": "workspace:*",
|
||||
"@cline/shared": "workspace:*",
|
||||
"@google/genai": "^1.30.0",
|
||||
"@grpc/grpc-js": "^1.9.15",
|
||||
@@ -519,6 +520,7 @@
|
||||
"ignore": "^7.0.3",
|
||||
"image-size": "^2.0.2",
|
||||
"isbinaryfile": "^5.0.2",
|
||||
"jiti": "^2.7.0",
|
||||
"js-yaml": "^4.1.1",
|
||||
"jschardet": "^3.1.4",
|
||||
"json5": "^2.2.3",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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"
|
||||
@@ -35,6 +36,17 @@ 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
|
||||
|
||||
@@ -21,6 +21,23 @@ export async function getAvailableSlashCommands(controller: Controller, _request
|
||||
)
|
||||
}
|
||||
|
||||
// Add plugin-registered commands
|
||||
try {
|
||||
const pluginCommands = await controller.getPluginSlashCommands()
|
||||
for (const cmd of pluginCommands) {
|
||||
commands.push(
|
||||
SlashCommandInfo.create({
|
||||
name: cmd.name,
|
||||
description: cmd.description ?? `Plugin command: ${cmd.name}`,
|
||||
section: "custom",
|
||||
cliCompatible: true,
|
||||
}),
|
||||
)
|
||||
}
|
||||
} catch {
|
||||
// Plugin command discovery is best-effort; don't fail the whole list.
|
||||
}
|
||||
|
||||
// Get workflow toggles from state
|
||||
const localWorkflowToggles = controller.stateManager.getWorkspaceStateKey("workflowToggles") ?? {}
|
||||
const globalWorkflowToggles = controller.stateManager.getGlobalSettingsKey("globalWorkflowToggles") ?? {}
|
||||
|
||||
@@ -27,6 +27,7 @@ export async function getStateToPostToWebview(controller: {
|
||||
backgroundCommandTaskId?: string
|
||||
workspaceManager?: any
|
||||
checkpointRestoreInput?: ExtensionState["checkpointRestoreInput"]
|
||||
getPluginSlashCommands?: () => Promise<{ name: string; description?: string }[]>
|
||||
}): Promise<ExtensionState> {
|
||||
const stateManager = controller.stateManager
|
||||
|
||||
@@ -108,6 +109,15 @@ export async function getStateToPostToWebview(controller: {
|
||||
// Codex OAuth not available
|
||||
}
|
||||
|
||||
// Plugin slash commands are fetched best-effort so autocomplete failures
|
||||
// don't block state posting.
|
||||
let pluginSlashCommands: { name: string; description?: string }[] = []
|
||||
try {
|
||||
pluginSlashCommands = (await controller.getPluginSlashCommands?.()) ?? []
|
||||
} catch {
|
||||
// Plugin command discovery is best-effort.
|
||||
}
|
||||
|
||||
return {
|
||||
version,
|
||||
apiConfiguration,
|
||||
@@ -155,6 +165,7 @@ export async function getStateToPostToWebview(controller: {
|
||||
taskHistory: processedTaskHistory,
|
||||
shouldShowAnnouncement,
|
||||
favoritedModelIds,
|
||||
pluginSlashCommands,
|
||||
backgroundCommandRunning: controller.backgroundCommandRunning ?? false,
|
||||
backgroundCommandTaskId: controller.backgroundCommandTaskId,
|
||||
workspaceRoots: controller.workspaceManager?.getRoots?.() ?? [],
|
||||
|
||||
@@ -71,6 +71,7 @@ import { SdkInteractionCoordinator } from "./sdk-interaction-coordinator"
|
||||
import { SdkMcpCoordinator } from "./sdk-mcp-coordinator"
|
||||
import { SdkMessageCoordinator, type SessionEventListener } from "./sdk-message-coordinator"
|
||||
import { SdkModeCoordinator } from "./sdk-mode-coordinator"
|
||||
import { type PluginSlashCommand, SdkPluginCommandCoordinator } from "./sdk-plugin-commands"
|
||||
import { SdkProviderChangeCoordinator } from "./sdk-provider-change-coordinator"
|
||||
import { SdkSessionConfigBuilder } from "./sdk-session-config-builder"
|
||||
import { SdkSessionEventCoordinator } from "./sdk-session-event-coordinator"
|
||||
@@ -166,6 +167,7 @@ export class Controller {
|
||||
private compaction: SdkCompactionCoordinator
|
||||
private sessionEvents: SdkSessionEventCoordinator
|
||||
private sessionHistory: SdkSessionHistoryLoader
|
||||
private pluginCommands: SdkPluginCommandCoordinator
|
||||
private readonly sdkTelemetry: VscodeSdkTelemetryHandle
|
||||
private readonly providerFailureTelemetryTurnGate = new ProviderFailureTelemetryTurnGate()
|
||||
private readonly providerConfigStore: ProviderConfigStore
|
||||
@@ -258,7 +260,7 @@ export class Controller {
|
||||
)
|
||||
|
||||
// Initialize SDK-backed auth and account services.
|
||||
this.authService = AuthService.getInstance(this)
|
||||
this.authService = AuthService.getInstance(this, this.sdkTelemetry.telemetry)
|
||||
this.ocaAuthService = OcaAuthService.initialize(this)
|
||||
this.accountService = ClineAccountService.getInstance()
|
||||
|
||||
@@ -480,6 +482,9 @@ export class Controller {
|
||||
},
|
||||
postStateToWebview: () => this.postStateToWebview(),
|
||||
})
|
||||
this.pluginCommands = new SdkPluginCommandCoordinator({
|
||||
getWorkspaceRoot: () => this.getWorkspaceRoot(),
|
||||
})
|
||||
this.taskStart = new SdkTaskStartCoordinator({
|
||||
stateManager: this.stateManager,
|
||||
sessions: this.sessions,
|
||||
@@ -572,6 +577,15 @@ export class Controller {
|
||||
this.providerCatalog.invalidateProviderListings()
|
||||
}
|
||||
|
||||
/**
|
||||
* Return plugin-registered slash commands for autocomplete. Used by the
|
||||
* getAvailableSlashCommands gRPC handler to surface plugin commands in the
|
||||
* webview's slash command picker.
|
||||
*/
|
||||
getPluginSlashCommands(): Promise<PluginSlashCommand[]> {
|
||||
return this.pluginCommands.getSlashCommands()
|
||||
}
|
||||
|
||||
private handleProviderConfigChange(event: ProviderConfigChange): void {
|
||||
this.scheduleProviderConfigStatePost()
|
||||
|
||||
@@ -674,6 +688,7 @@ export class Controller {
|
||||
// are disposed below — see StatePostDebouncer.dispose().
|
||||
await this.statePostDebouncer.dispose()
|
||||
await this.invalidateUserInstructionService()
|
||||
await this.pluginCommands.dispose()
|
||||
this.messages.cancelPendingSave()
|
||||
// Clear MCP tool list change callback before disposing McpHub
|
||||
this.mcpHub?.clearToolListChangeCallback()
|
||||
@@ -734,14 +749,48 @@ export class Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand a leading `/workflow` or `/skill` slash command into its instruction
|
||||
* body. Mirrors the CLI's `buildUserInputMessage`. Returns the input unchanged
|
||||
* if it is not a known command or expansion fails.
|
||||
* Expand a leading slash command. First checks plugin-registered commands
|
||||
* (e.g. `/goal`), then falls back to workflow/skill expansion via the
|
||||
* user-instruction service. For plugin commands:
|
||||
* - If the handler returns `submitPrompt`, that becomes the prompt text.
|
||||
* - If the handler returns `reply`, it is emitted as a say message.
|
||||
* - If only `reply` is returned (no `submitPrompt`), returns empty string
|
||||
* so the agent turn is suppressed (the reply was already shown).
|
||||
* Returns the input unchanged if it is not a known command.
|
||||
*/
|
||||
private async resolveSlashCommands(text: string): Promise<string> {
|
||||
if (this.isDisposed) {
|
||||
return text
|
||||
}
|
||||
|
||||
// Check plugin commands first — they take precedence over
|
||||
// workflow/skill expansion so plugin names cannot be shadowed.
|
||||
try {
|
||||
const result = await this.pluginCommands.resolveCommand(text)
|
||||
if (result) {
|
||||
if (result.reply) {
|
||||
this.messages.emitSessionEvents(
|
||||
[
|
||||
{
|
||||
ts: Date.now(),
|
||||
type: "say",
|
||||
say: "text",
|
||||
text: result.reply,
|
||||
partial: false,
|
||||
},
|
||||
],
|
||||
{
|
||||
type: "status",
|
||||
payload: { sessionId: this.sessions.getActiveSession()?.sessionId ?? "", status: "running" },
|
||||
},
|
||||
)
|
||||
}
|
||||
return result.submitPrompt ?? ""
|
||||
}
|
||||
} catch (error) {
|
||||
Logger.warn("[SdkController] Plugin command resolution failed, falling through:", error)
|
||||
}
|
||||
|
||||
try {
|
||||
const workspaceRoot = await this.getWorkspaceRoot()
|
||||
const service = await this.ensureUserInstructionService(workspaceRoot)
|
||||
@@ -1800,6 +1849,7 @@ export class Controller {
|
||||
mcpHub: this.mcpHub,
|
||||
backgroundCommandRunning: this.backgroundCommandRunning,
|
||||
backgroundCommandTaskId: this.backgroundCommandTaskId,
|
||||
getPluginSlashCommands: () => this.pluginCommands.getSlashCommands(),
|
||||
})
|
||||
const sdkTaskHistory = (await this.taskHistory.listHistory({ limit: 100, hydrate: false }))
|
||||
.map(sessionHistoryRecordToHistoryItem)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
// - Streaming subscription management
|
||||
// - workos: prefix handling
|
||||
|
||||
import { getValidClineCredentials, type OAuthCredentials } from "@cline/core"
|
||||
import { getValidClineCredentials, type ITelemetryService, type OAuthCredentials } from "@cline/core"
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest"
|
||||
import { AuthService, type ClineAuthInfo, LogoutReason } from "./auth-service"
|
||||
|
||||
@@ -18,6 +18,8 @@ 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>()
|
||||
@@ -90,6 +92,7 @@ vi.mock("@/services/feature-flags", () => ({
|
||||
vi.mock("@/services/telemetry", () => ({
|
||||
telemetryService: {
|
||||
identifyAccount: mockIdentifyAccount,
|
||||
captureAuthLoggedOut: mockCaptureAuthLoggedOut,
|
||||
},
|
||||
}))
|
||||
|
||||
@@ -103,7 +106,9 @@ vi.mock("axios", () => ({
|
||||
const mockLoginClineOAuth = vi.hoisted(() => vi.fn())
|
||||
|
||||
// Mock @cline/core OAuth functions
|
||||
vi.mock("@cline/core", () => ({
|
||||
vi.mock("@cline/core", async () => ({
|
||||
sdkDebug: () => {},
|
||||
hashSecret: () => "hashed",
|
||||
createOAuthClientCallbacks: (opts: {
|
||||
onOutput?: (message: string) => void
|
||||
onPrompt: () => void
|
||||
@@ -208,7 +213,7 @@ describe("AuthService", () => {
|
||||
beforeEach(() => {
|
||||
// Reset the singleton between tests
|
||||
resetSingleton()
|
||||
authService = AuthService.getInstance()
|
||||
authService = AuthService.getInstance(undefined, mockSdkTelemetry)
|
||||
mockSecrets.clear()
|
||||
mockProviderSettings.clear()
|
||||
vi.clearAllMocks()
|
||||
@@ -378,6 +383,7 @@ describe("AuthService", () => {
|
||||
|
||||
// Persisted credentials should be cleared from providers.json.
|
||||
expect(mockProviderSettings.get("cline")?.auth).toBeUndefined()
|
||||
expect(mockCaptureAuthLoggedOut).toHaveBeenCalledWith("cline", LogoutReason.USER_INITIATED)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -407,6 +413,11 @@ 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,13 +8,15 @@
|
||||
// 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 { OAuthCredentials } from "@cline/core"
|
||||
import type { ITelemetryService, 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"
|
||||
@@ -99,7 +101,10 @@ function readClineCredentials(): {
|
||||
try {
|
||||
const manager = getProviderSettingsManager()
|
||||
const settings = manager.getProviderSettings("cline")
|
||||
if (!settings?.auth?.accessToken) return null
|
||||
if (!settings?.auth?.accessToken) {
|
||||
sdkDebug("[SdkAuthService] readClineCredentials: no auth.accessToken found")
|
||||
return null
|
||||
}
|
||||
|
||||
// Strip workos: prefix if present (providers.json stores it with prefix)
|
||||
let accessToken = settings.auth.accessToken
|
||||
@@ -107,12 +112,16 @@ function readClineCredentials(): {
|
||||
accessToken = accessToken.slice(WORKOS_TOKEN_PREFIX.length)
|
||||
}
|
||||
|
||||
return {
|
||||
const result = {
|
||||
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
|
||||
@@ -150,6 +159,9 @@ 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)
|
||||
}
|
||||
@@ -163,6 +175,7 @@ function clearClineCredentials(): void {
|
||||
const manager = getProviderSettingsManager()
|
||||
const existing = manager.getProviderSettings("cline")
|
||||
if (existing) {
|
||||
sdkDebug("[SdkAuthService] clearClineCredentials: clearing auth from providers.json")
|
||||
manager.saveProviderSettings(
|
||||
{
|
||||
...existing,
|
||||
@@ -189,6 +202,7 @@ 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() {}
|
||||
|
||||
@@ -196,10 +210,13 @@ export class AuthService {
|
||||
* Gets the singleton instance of AuthService.
|
||||
* On first call with a controller, initializes BannerService.
|
||||
*/
|
||||
public static getInstance(controller?: Controller): AuthService {
|
||||
public static getInstance(controller?: Controller, telemetry?: ITelemetryService): 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) {
|
||||
@@ -251,6 +268,9 @@ 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}`,
|
||||
@@ -259,6 +279,7 @@ 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)
|
||||
@@ -286,7 +307,7 @@ export class AuthService {
|
||||
|
||||
return getValidClineCredentials(
|
||||
this.toOAuthCredentials(authInfo),
|
||||
{ apiBaseUrl: ClineEnv.config().apiBaseUrl },
|
||||
{ apiBaseUrl: ClineEnv.config().apiBaseUrl, telemetry: this._telemetry },
|
||||
{ forceRefresh: options?.forceRefresh },
|
||||
)
|
||||
}
|
||||
@@ -335,9 +356,11 @@ 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
|
||||
@@ -346,6 +369,7 @@ 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()
|
||||
@@ -375,6 +399,9 @@ export class AuthService {
|
||||
this._authenticated = true
|
||||
|
||||
if (credentialsChanged) {
|
||||
sdkDebug(
|
||||
`[SdkAuthService] refreshAccessToken: credentials changed (newTokenHash=${hashSecret(newCredentials.access)})`,
|
||||
)
|
||||
writeClineCredentials({
|
||||
accessToken: newCredentials.access,
|
||||
refreshToken: newCredentials.refresh,
|
||||
@@ -387,6 +414,8 @@ 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
|
||||
@@ -722,8 +751,9 @@ 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,6 +783,101 @@ 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,10 +452,11 @@ function sdkToolToClineSayTool(toolName: string, input?: unknown): ClineSayTool
|
||||
switch (toolName) {
|
||||
case "read_files":
|
||||
case "read_file": {
|
||||
const filePath = extractFirstFilePath(parsedInput)
|
||||
const fileRead = extractFileReads(parsedInput)[0]
|
||||
return {
|
||||
tool: "readFile",
|
||||
path: filePath,
|
||||
path: fileRead?.path ?? "",
|
||||
...readLineRangeFields(fileRead),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -665,32 +666,53 @@ function getCompletionResultText(input: unknown): string {
|
||||
return getStringField(parsed, "summary") ?? getStringField(parsed, "result") ?? ""
|
||||
}
|
||||
|
||||
/** Extract file paths from a read_files/read_file input */
|
||||
function extractFilePaths(input: Record<string, unknown> | undefined): string[] {
|
||||
/** 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[] {
|
||||
if (!input) return []
|
||||
const files = input.files
|
||||
if (Array.isArray(files) && files.length > 0) {
|
||||
const paths = files
|
||||
.map((f) => {
|
||||
if (typeof f === "string") return f
|
||||
const reads = files
|
||||
.map((f): FileReadRequest => {
|
||||
if (typeof f === "string") return { path: f }
|
||||
if (typeof f === "object" && f !== null) {
|
||||
return ((f as Record<string, unknown>).path as string) ?? ""
|
||||
const entry = f as Record<string, unknown>
|
||||
return {
|
||||
path: (entry.path as string) ?? "",
|
||||
startLine: getNumberField(entry, "start_line"),
|
||||
endLine: getNumberField(entry, "end_line"),
|
||||
}
|
||||
}
|
||||
return ""
|
||||
return { path: "" }
|
||||
})
|
||||
.filter(Boolean)
|
||||
if (paths.length > 0) {
|
||||
return paths
|
||||
.filter((read) => read.path)
|
||||
if (reads.length > 0) {
|
||||
return reads
|
||||
}
|
||||
}
|
||||
const singlePath =
|
||||
(input.path as string) ?? (input.file_path as string) ?? (input.filePath as string) ?? (input.filename as string) ?? ""
|
||||
return singlePath ? [singlePath] : []
|
||||
return singlePath
|
||||
? [{ path: singlePath, startLine: getNumberField(input, "start_line"), endLine: getNumberField(input, "end_line") }]
|
||||
: []
|
||||
}
|
||||
|
||||
/** Extract the first file path from a read_files input */
|
||||
function extractFirstFilePath(input: Record<string, unknown> | undefined): string {
|
||||
return extractFilePaths(input)[0] ?? ""
|
||||
/**
|
||||
* 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 }
|
||||
}
|
||||
|
||||
/** Get a string field from a parsed input object */
|
||||
@@ -701,6 +723,14 @@ 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
|
||||
@@ -1295,16 +1325,17 @@ function translateAgentEvent(event: AgentEvent, state: MessageTranslatorState):
|
||||
// list reflect what was actually read.
|
||||
if (toolName === "read_files" || toolName === "read_file") {
|
||||
const parsedInput = parseToolInput(storedInput)
|
||||
const filePaths = extractFilePaths(parsedInput)
|
||||
if (filePaths.length > 1) {
|
||||
filePaths.forEach((filePath, index) => {
|
||||
const fileReads = extractFileReads(parsedInput)
|
||||
if (fileReads.length > 1) {
|
||||
fileReads.forEach((fileRead, index) => {
|
||||
messages.push({
|
||||
ts: index === 0 ? ts : state.nextTs(),
|
||||
type: "say",
|
||||
say: "tool",
|
||||
text: JSON.stringify({
|
||||
tool: "readFile",
|
||||
path: filePath,
|
||||
path: fileRead.path,
|
||||
...readLineRangeFields(fileRead),
|
||||
} satisfies ClineSayTool),
|
||||
partial: false,
|
||||
})
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { describe, expect, it, vi } from "vitest"
|
||||
import { normalizePluginCommandName, SdkPluginCommandCoordinator } from "./sdk-plugin-commands"
|
||||
|
||||
describe("SdkPluginCommandCoordinator", () => {
|
||||
it("loads plugins from the active workspace", async () => {
|
||||
const loadPlugins = vi.fn(async () => ({
|
||||
extensions: [],
|
||||
pluginPaths: [],
|
||||
failures: [],
|
||||
warnings: [],
|
||||
}))
|
||||
const coordinator = new SdkPluginCommandCoordinator({
|
||||
getWorkspaceRoot: async () => "/workspace/project",
|
||||
loadPlugins,
|
||||
})
|
||||
|
||||
await coordinator.getSlashCommands()
|
||||
|
||||
expect(loadPlugins).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
cwd: "/workspace/project",
|
||||
workspacePath: "/workspace/project",
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("normalizes plugin command names like the CLI", () => {
|
||||
expect(normalizePluginCommandName(" /Goal ")).toBe("goal")
|
||||
expect(normalizePluginCommandName("GOAL")).toBe("goal")
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,174 @@
|
||||
// SdkPluginCommandCoordinator — discovers and executes plugin-registered
|
||||
// slash commands, mirroring the CLI's createWorkspaceChatCommandHost.
|
||||
//
|
||||
// Plugins register commands via `api.registerCommand({ name, handler })` in
|
||||
// their setup(). The ContributionRegistry runs setup() and collects the
|
||||
// registered commands. This coordinator:
|
||||
// 1. Lazily loads plugins via resolveAndLoadAgentPlugins (sandbox mode)
|
||||
// 2. Initializes a ContributionRegistry to run setup() and gather commands
|
||||
// 3. Exposes getSlashCommands() for autocomplete
|
||||
// 4. Exposes resolveCommand(text) to execute a /command and return its result
|
||||
|
||||
import {
|
||||
type AgentExtensionCommand,
|
||||
type AgentExtensionCommandResult,
|
||||
createContributionRegistry,
|
||||
noopBasicLogger,
|
||||
resolveAndLoadAgentPlugins,
|
||||
} from "@cline/core"
|
||||
import type { AgentTool, Message } from "@cline/shared"
|
||||
import { Logger } from "@shared/services/Logger"
|
||||
|
||||
export interface PluginSlashCommand {
|
||||
name: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
export interface PluginCommandResult {
|
||||
reply?: string
|
||||
submitPrompt?: string
|
||||
}
|
||||
|
||||
interface LoadedPlugins {
|
||||
commands: AgentExtensionCommand[]
|
||||
shutdown: () => Promise<void>
|
||||
}
|
||||
|
||||
export interface SdkPluginCommandCoordinatorOptions {
|
||||
getWorkspaceRoot: () => Promise<string>
|
||||
loadPlugins?: typeof resolveAndLoadAgentPlugins
|
||||
}
|
||||
|
||||
export function normalizePluginCommandName(name: string): string {
|
||||
const trimmed = name.trim()
|
||||
return (trimmed.startsWith("/") ? trimmed.slice(1) : trimmed).toLowerCase()
|
||||
}
|
||||
|
||||
export class SdkPluginCommandCoordinator {
|
||||
private loadedPromise: Promise<LoadedPlugins | undefined> | undefined
|
||||
|
||||
constructor(private readonly options: SdkPluginCommandCoordinatorOptions) {}
|
||||
|
||||
/**
|
||||
* Lazily load plugins and initialize the contribution registry. The result
|
||||
* is cached so subsequent calls reuse the same sandbox process. Returns
|
||||
* undefined if no plugins are installed or loading fails.
|
||||
*/
|
||||
private ensureLoaded(): Promise<LoadedPlugins | undefined> {
|
||||
if (this.loadedPromise) {
|
||||
return this.loadedPromise
|
||||
}
|
||||
this.loadedPromise = (async () => {
|
||||
let loaded: Awaited<ReturnType<typeof resolveAndLoadAgentPlugins>>
|
||||
try {
|
||||
const workspaceRoot = await this.options.getWorkspaceRoot()
|
||||
loaded = await (this.options.loadPlugins ?? resolveAndLoadAgentPlugins)({
|
||||
cwd: workspaceRoot,
|
||||
workspacePath: workspaceRoot,
|
||||
logger: noopBasicLogger,
|
||||
})
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error)
|
||||
Logger.warn(`[PluginCommands] Plugin loading failed; continuing without plugin commands (${message})`)
|
||||
return undefined
|
||||
}
|
||||
if (!loaded.extensions.length) {
|
||||
await loaded.shutdown?.().catch(() => {})
|
||||
return undefined
|
||||
}
|
||||
|
||||
const registry = createContributionRegistry<(typeof loaded.extensions)[number], AgentTool, Message[]>({
|
||||
extensions: loaded.extensions,
|
||||
})
|
||||
try {
|
||||
await registry.initialize()
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error)
|
||||
Logger.warn(`[PluginCommands] Contribution registry initialization failed (${message})`)
|
||||
await loaded.shutdown?.().catch(() => {})
|
||||
return undefined
|
||||
}
|
||||
|
||||
return {
|
||||
commands: registry.getRegistrySnapshot().commands,
|
||||
shutdown: async () => {
|
||||
await loaded.shutdown?.().catch(() => {})
|
||||
},
|
||||
}
|
||||
})()
|
||||
return this.loadedPromise
|
||||
}
|
||||
|
||||
/**
|
||||
* Return plugin-registered slash commands for autocomplete. Returns an
|
||||
* empty array if no plugins are installed or loading fails.
|
||||
*/
|
||||
async getSlashCommands(): Promise<PluginSlashCommand[]> {
|
||||
const loaded = await this.ensureLoaded()
|
||||
if (!loaded) {
|
||||
return []
|
||||
}
|
||||
return loaded.commands
|
||||
.filter((cmd) => typeof cmd.handler === "function")
|
||||
.map((cmd) => ({
|
||||
name: normalizePluginCommandName(cmd.name),
|
||||
description: cmd.description,
|
||||
}))
|
||||
.filter((cmd) => cmd.name.length > 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a leading /command from a plugin. Returns null if the text does
|
||||
* not match a plugin command. Returns { reply?, submitPrompt? } from the
|
||||
* command handler.
|
||||
*/
|
||||
async resolveCommand(text: string): Promise<PluginCommandResult | null> {
|
||||
if (!text.startsWith("/") || text.length < 2) {
|
||||
return null
|
||||
}
|
||||
const match = text.match(/^\/(\S+)/)
|
||||
if (!match?.[1]) {
|
||||
return null
|
||||
}
|
||||
const name = normalizePluginCommandName(match[1])
|
||||
const remainder = text.slice(name.length + 1).trim()
|
||||
|
||||
const loaded = await this.ensureLoaded()
|
||||
if (!loaded) {
|
||||
return null
|
||||
}
|
||||
const command = loaded.commands.find(
|
||||
(cmd) => normalizePluginCommandName(cmd.name) === name && typeof cmd.handler === "function",
|
||||
)
|
||||
if (!command?.handler) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
const result: AgentExtensionCommandResult = await command.handler(remainder)
|
||||
if (typeof result === "string") {
|
||||
return { reply: result }
|
||||
}
|
||||
return {
|
||||
reply: result.reply,
|
||||
submitPrompt: result.submitPrompt,
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error)
|
||||
Logger.warn(`[PluginCommands] Command "/${name}" failed: ${message}`)
|
||||
return { reply: `Command /${name} failed: ${message}` }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shut down the plugin sandbox process. Called on extension disposal.
|
||||
*/
|
||||
async dispose(): Promise<void> {
|
||||
const promise = this.loadedPromise
|
||||
this.loadedPromise = undefined
|
||||
if (promise) {
|
||||
const loaded = await promise.catch(() => undefined)
|
||||
await loaded?.shutdown().catch(() => {})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import { OnboardingModelGroup } from "./proto/cline/state"
|
||||
import { Mode } from "./storage/types"
|
||||
import { TelemetrySetting } from "./TelemetrySetting"
|
||||
import { UserInfo } from "./UserInfo"
|
||||
import type { SlashCommand } from "./slashCommands"
|
||||
// webview will hold state
|
||||
export interface ExtensionMessage {
|
||||
type: "grpc_response" // New type for gRPC responses
|
||||
@@ -114,6 +115,8 @@ export interface ExtensionState {
|
||||
worktreesEnabled?: ClineFeatureSetting
|
||||
customPrompt?: string
|
||||
favoritedModelIds: string[]
|
||||
/** Plugin-registered slash commands surfaced for autocomplete. */
|
||||
pluginSlashCommands?: SlashCommand[]
|
||||
// NEW: Add workspace information
|
||||
workspaceRoots: WorkspaceRoot[]
|
||||
primaryRootIndex: number
|
||||
@@ -275,7 +278,7 @@ export interface ClineSayTool {
|
||||
operationIsLocatedInWorkspace?: boolean
|
||||
/** Starting line numbers in the original file where each SEARCH block matched */
|
||||
startLineNumbers?: number[]
|
||||
/** Inclusive line range actually returned by read_file (for UI summaries). */
|
||||
/** One-based inclusive line range requested by read_file; readLineEnd omitted = open-ended read (for UI summaries). */
|
||||
readLineStart?: number
|
||||
readLineEnd?: number
|
||||
}
|
||||
|
||||
@@ -54,7 +54,8 @@ export class Logger {
|
||||
fullMessage += ` ${args.map((arg) => JSON.stringify(arg)).join(" ")}`
|
||||
}
|
||||
const errorSuffix = error?.message ? ` ${error.message}` : ""
|
||||
Logger.output(`${level} ${fullMessage}${errorSuffix}`.trimEnd())
|
||||
const ts = new Date().toISOString()
|
||||
Logger.output(`${ts} ${level} ${fullMessage}${errorSuffix}`.trimEnd())
|
||||
} catch {
|
||||
// do nothing if Logger fails
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import { existsSync } from "node:fs"
|
||||
import { readFile } from "node:fs/promises"
|
||||
import { createRequire } from "node:module"
|
||||
import { join } from "node:path"
|
||||
import { describe, expect, it } from "bun:test"
|
||||
import { $ } from "bun"
|
||||
|
||||
/**
|
||||
* Integration test for CLINE-2584: the plugin sandbox bootstrap
|
||||
* (`plugin-sandbox-bootstrap.js`) must be shipped with the VS Code extension.
|
||||
*
|
||||
* The bootstrap runs in an isolated child process spawned by
|
||||
* `SubprocessSandbox` — it cannot be inlined into `extension.js` because the
|
||||
* sandbox spawns it via `node <bootstrapFile>`. The CLI build copies this
|
||||
* file (`apps/cli/bun.mts`); the extension build (`esbuild.mjs`) must do the
|
||||
* same.
|
||||
*
|
||||
* The bootstrap also has external runtime dependencies that must be resolvable
|
||||
* from its on-disk location via Node's standard module resolution:
|
||||
* - jiti (TypeScript transpilation of .ts plugins)
|
||||
* - @cline/shared, @cline/sdk (host-provided SDK packages that plugins import)
|
||||
*
|
||||
* This test runs the real `bun esbuild.mjs` build and checks the real
|
||||
* `dist/` output, exercising the same build pipeline CI uses.
|
||||
*/
|
||||
|
||||
const projectRoot = join(import.meta.dir, "..", "..")
|
||||
const distDir = join(projectRoot, "dist")
|
||||
const bootstrapPath = join(distDir, "extensions", "plugin-sandbox-bootstrap.js")
|
||||
|
||||
describe("plugin-sandbox bootstrap build artifact (CLINE-2584)", () => {
|
||||
it("esbuild.mjs emits plugin-sandbox-bootstrap.js into dist/", async () => {
|
||||
const result = await $`bun esbuild.mjs`.cwd(projectRoot).quiet()
|
||||
expect(result.exitCode).toBe(0)
|
||||
|
||||
expect(existsSync(join(distDir, "extension.js"))).toBe(true)
|
||||
expect(existsSync(bootstrapPath)).toBe(true)
|
||||
}, 60_000)
|
||||
|
||||
it("the bootstrap is a real executable script with IPC handling", async () => {
|
||||
expect(existsSync(bootstrapPath)).toBe(true)
|
||||
|
||||
const content = await readFile(bootstrapPath, "utf8")
|
||||
expect(content.length).toBeGreaterThan(1000)
|
||||
expect(content).toMatch(/process\.on\(.process\.message|process\.send|type:\s*["']response["']/)
|
||||
}, 60_000)
|
||||
|
||||
it("the bootstrap's runtime dependencies resolve from dist/", () => {
|
||||
expect(existsSync(bootstrapPath)).toBe(true)
|
||||
|
||||
// The bootstrap is spawned as a standalone Node child process. It
|
||||
// imports jiti (for TypeScript transpilation) and @cline/shared as
|
||||
// external modules, and plugins import @cline/sdk. Node resolves
|
||||
// these by walking up from the bootstrap's directory. All must be
|
||||
// direct dependencies of the extension so they appear in
|
||||
// node_modules and are resolvable.
|
||||
const requireFromBootstrap = createRequire(bootstrapPath)
|
||||
expect(() => requireFromBootstrap.resolve("jiti")).not.toThrow()
|
||||
expect(() => requireFromBootstrap.resolve("@cline/shared")).not.toThrow()
|
||||
// @cline/sdk is a host-provided SDK specifier that plugins import.
|
||||
// The bootstrap's findHostPackageRoot walks up from dist/extensions/
|
||||
// looking for node_modules/@cline/sdk/package.json.
|
||||
expect(
|
||||
existsSync(join(projectRoot, "node_modules", "@cline", "sdk", "package.json")),
|
||||
).toBe(true)
|
||||
}, 60_000)
|
||||
})
|
||||
@@ -507,10 +507,11 @@ 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.readLineEnd != null ? (
|
||||
{tool.readLineStart != null ? (
|
||||
<span className="opacity-80">
|
||||
{" "}
|
||||
({tool.readLineStart}-{tool.readLineEnd})
|
||||
({tool.readLineStart}
|
||||
{tool.readLineEnd != null ? `-${tool.readLineEnd}` : "+"})
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
|
||||
@@ -224,6 +224,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
remoteConfigSettings,
|
||||
navigateToSettingsModelPicker,
|
||||
mcpServers,
|
||||
pluginSlashCommands,
|
||||
} = useExtensionState()
|
||||
const [isTextAreaFocused, setIsTextAreaFocused] = useState(false)
|
||||
const [isDraggingOver, setIsDraggingOver] = useState(false)
|
||||
@@ -489,6 +490,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
remoteWorkflowToggles,
|
||||
remoteConfigSettings?.remoteGlobalWorkflows,
|
||||
mcpServers,
|
||||
pluginSlashCommands,
|
||||
)
|
||||
|
||||
if (allCommands.length === 0) {
|
||||
@@ -514,6 +516,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
remoteWorkflowToggles,
|
||||
remoteConfigSettings?.remoteGlobalWorkflows,
|
||||
mcpServers,
|
||||
pluginSlashCommands,
|
||||
)
|
||||
if (commands.length > 0) {
|
||||
handleSlashCommandsSelect(commands[selectedSlashCommandsIndex])
|
||||
@@ -673,6 +676,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
slashCommandsQuery,
|
||||
handleSlashCommandsSelect,
|
||||
sendingDisabled,
|
||||
pluginSlashCommands,
|
||||
],
|
||||
)
|
||||
|
||||
@@ -984,6 +988,8 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
globalWorkflowToggles,
|
||||
remoteWorkflowToggles,
|
||||
remoteConfigSettings?.remoteGlobalWorkflows,
|
||||
mcpServers,
|
||||
pluginSlashCommands,
|
||||
)
|
||||
|
||||
if (isValidCommand) {
|
||||
@@ -997,7 +1003,14 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
highlightLayerRef.current.innerHTML = processedText
|
||||
highlightLayerRef.current.scrollTop = textAreaRef.current.scrollTop
|
||||
highlightLayerRef.current.scrollLeft = textAreaRef.current.scrollLeft
|
||||
}, [localWorkflowToggles, globalWorkflowToggles, remoteWorkflowToggles, remoteConfigSettings])
|
||||
}, [
|
||||
localWorkflowToggles,
|
||||
globalWorkflowToggles,
|
||||
remoteWorkflowToggles,
|
||||
remoteConfigSettings,
|
||||
mcpServers,
|
||||
pluginSlashCommands,
|
||||
])
|
||||
|
||||
useLayoutEffect(() => {
|
||||
updateHighlights()
|
||||
@@ -1406,6 +1419,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
mcpServers={mcpServers}
|
||||
onMouseDown={handleMenuMouseDown}
|
||||
onSelect={handleSlashCommandsSelect}
|
||||
pluginSlashCommands={pluginSlashCommands}
|
||||
query={slashCommandsQuery}
|
||||
remoteWorkflows={remoteConfigSettings?.remoteGlobalWorkflows}
|
||||
remoteWorkflowToggles={remoteWorkflowToggles}
|
||||
|
||||
@@ -16,6 +16,7 @@ interface SlashCommandMenuProps {
|
||||
remoteWorkflowToggles?: Record<string, boolean>
|
||||
remoteWorkflows?: any[]
|
||||
mcpServers?: McpServer[]
|
||||
pluginSlashCommands?: SlashCommand[]
|
||||
}
|
||||
|
||||
const SlashCommandMenu: React.FC<SlashCommandMenuProps> = ({
|
||||
@@ -29,6 +30,7 @@ const SlashCommandMenu: React.FC<SlashCommandMenuProps> = ({
|
||||
remoteWorkflowToggles,
|
||||
remoteWorkflows,
|
||||
mcpServers = [],
|
||||
pluginSlashCommands = [],
|
||||
}) => {
|
||||
const menuRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
@@ -40,6 +42,7 @@ const SlashCommandMenu: React.FC<SlashCommandMenuProps> = ({
|
||||
remoteWorkflowToggles,
|
||||
remoteWorkflows,
|
||||
mcpServers,
|
||||
pluginSlashCommands,
|
||||
)
|
||||
const defaultCommands = filteredCommands.filter((cmd) => cmd.section === "default" || !cmd.section)
|
||||
const workflowCommands = filteredCommands.filter((cmd) => cmd.section === "custom")
|
||||
|
||||
+6
-2
@@ -46,7 +46,9 @@ const getActivityText = (tool: ClineSayTool): string | null => {
|
||||
return null
|
||||
}
|
||||
const lineHint =
|
||||
tool.readLineStart != null && tool.readLineEnd != null ? ` (lines ${tool.readLineStart}-${tool.readLineEnd})` : ""
|
||||
tool.readLineStart != null
|
||||
? ` (lines ${tool.readLineStart}${tool.readLineEnd != null ? `-${tool.readLineEnd}` : "+"})`
|
||||
: ""
|
||||
return `Reading ${cleanedPath}${lineHint}...`
|
||||
}
|
||||
case "listFilesTopLevel":
|
||||
@@ -302,7 +304,9 @@ function getToolDisplayInfo(tool: ClineSayTool) {
|
||||
switch (tool.tool) {
|
||||
case "readFile": {
|
||||
const lineNote =
|
||||
tool.readLineStart != null && tool.readLineEnd != null ? `lines ${tool.readLineStart}-${tool.readLineEnd}` : null
|
||||
tool.readLineStart != null
|
||||
? `lines ${tool.readLineStart}${tool.readLineEnd != null ? `-${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>
|
||||
</>
|
||||
|
||||
@@ -289,6 +289,7 @@ export const ExtensionStateContextProvider: React.FC<{
|
||||
localAgentsRulesToggles: {},
|
||||
localWorkflowToggles: {},
|
||||
globalWorkflowToggles: {},
|
||||
pluginSlashCommands: [],
|
||||
shellIntegrationTimeout: 4000,
|
||||
terminalReuseEnabled: true,
|
||||
vscodeTerminalExecutionMode: "vscodeTerminal",
|
||||
@@ -906,6 +907,7 @@ export const ExtensionStateContextProvider: React.FC<{
|
||||
localAgentsRulesToggles: state.localAgentsRulesToggles || {},
|
||||
localWorkflowToggles: state.localWorkflowToggles || {},
|
||||
globalWorkflowToggles: state.globalWorkflowToggles || {},
|
||||
pluginSlashCommands: state.pluginSlashCommands || [],
|
||||
remoteRulesToggles: state.remoteRulesToggles || {},
|
||||
remoteWorkflowToggles: state.remoteWorkflowToggles || {},
|
||||
enableCheckpointsSetting: state.enableCheckpointsSetting,
|
||||
|
||||
@@ -181,6 +181,7 @@ export function getMatchingSlashCommands(
|
||||
remoteWorkflowToggles?: Record<string, boolean>,
|
||||
remoteWorkflows?: any[],
|
||||
mcpServers: McpServer[] = [],
|
||||
pluginSlashCommands: SlashCommand[] = [],
|
||||
): SlashCommand[] {
|
||||
const workflowCommands = getWorkflowCommands(
|
||||
localWorkflowToggles,
|
||||
@@ -189,7 +190,7 @@ export function getMatchingSlashCommands(
|
||||
remoteWorkflows,
|
||||
)
|
||||
const mcpPromptCommands = getMcpPromptCommands(mcpServers)
|
||||
const allCommands = [...DEFAULT_SLASH_COMMANDS, ...workflowCommands, ...mcpPromptCommands]
|
||||
const allCommands = [...DEFAULT_SLASH_COMMANDS, ...workflowCommands, ...mcpPromptCommands, ...pluginSlashCommands]
|
||||
|
||||
if (!query) {
|
||||
return allCommands
|
||||
@@ -233,6 +234,7 @@ export function validateSlashCommand(
|
||||
remoteWorkflowToggles?: Record<string, boolean>,
|
||||
remoteWorkflows?: any[],
|
||||
mcpServers: McpServer[] = [],
|
||||
pluginSlashCommands: SlashCommand[] = [],
|
||||
): "full" | "partial" | null {
|
||||
if (!command) {
|
||||
return null
|
||||
@@ -245,7 +247,7 @@ export function validateSlashCommand(
|
||||
remoteWorkflows,
|
||||
)
|
||||
const mcpPromptCommands = getMcpPromptCommands(mcpServers)
|
||||
const allCommands = [...DEFAULT_SLASH_COMMANDS, ...workflowCommands, ...mcpPromptCommands]
|
||||
const allCommands = [...DEFAULT_SLASH_COMMANDS, ...workflowCommands, ...mcpPromptCommands, ...pluginSlashCommands]
|
||||
|
||||
// case insensitive matching
|
||||
const exactMatch = allCommands.some((cmd) => cmd.name.toLowerCase() === command.toLowerCase())
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"apps/cli": {
|
||||
"name": "@cline/cli",
|
||||
"version": "3.0.38",
|
||||
"version": "3.0.39",
|
||||
"bin": {
|
||||
"cline": "src/index.ts",
|
||||
},
|
||||
@@ -354,6 +354,7 @@
|
||||
"@cline/agents": "workspace:*",
|
||||
"@cline/core": "workspace:*",
|
||||
"@cline/llms": "workspace:*",
|
||||
"@cline/sdk": "workspace:*",
|
||||
"@cline/shared": "workspace:*",
|
||||
"@google/genai": "^1.30.0",
|
||||
"@grpc/grpc-js": "^1.9.15",
|
||||
@@ -406,6 +407,7 @@
|
||||
"ignore": "^7.0.3",
|
||||
"image-size": "^2.0.2",
|
||||
"isbinaryfile": "^5.0.2",
|
||||
"jiti": "^2.7.0",
|
||||
"js-yaml": "^4.1.1",
|
||||
"jschardet": "^3.1.4",
|
||||
"json5": "^2.2.3",
|
||||
@@ -617,7 +619,7 @@
|
||||
},
|
||||
"sdk/packages/agents": {
|
||||
"name": "@cline/agents",
|
||||
"version": "0.0.59",
|
||||
"version": "0.0.60",
|
||||
"dependencies": {
|
||||
"@cline/llms": "workspace:*",
|
||||
"@cline/shared": "workspace:*",
|
||||
@@ -626,7 +628,7 @@
|
||||
},
|
||||
"sdk/packages/core": {
|
||||
"name": "@cline/core",
|
||||
"version": "0.0.59",
|
||||
"version": "0.0.60",
|
||||
"dependencies": {
|
||||
"@cline/agents": "workspace:*",
|
||||
"@cline/llms": "workspace:*",
|
||||
@@ -664,7 +666,7 @@
|
||||
},
|
||||
"sdk/packages/llms": {
|
||||
"name": "@cline/llms",
|
||||
"version": "0.0.59",
|
||||
"version": "0.0.60",
|
||||
"dependencies": {
|
||||
"@ai-sdk/amazon-bedrock": "^4.0.89",
|
||||
"@ai-sdk/anthropic": "^3.0.68",
|
||||
@@ -698,14 +700,14 @@
|
||||
},
|
||||
"sdk/packages/sdk": {
|
||||
"name": "@cline/sdk",
|
||||
"version": "0.0.59",
|
||||
"version": "0.0.60",
|
||||
"dependencies": {
|
||||
"@cline/core": "workspace:*",
|
||||
},
|
||||
},
|
||||
"sdk/packages/shared": {
|
||||
"name": "@cline/shared",
|
||||
"version": "0.0.59",
|
||||
"version": "0.0.60",
|
||||
"dependencies": {
|
||||
"aws4fetch": "^1.0.20",
|
||||
"jsonrepair": "^3.13.2",
|
||||
@@ -742,15 +744,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.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/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/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/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/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/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/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.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/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/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 +764,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.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=="],
|
||||
"@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=="],
|
||||
|
||||
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
||||
|
||||
@@ -770,63 +772,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.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": ["@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-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-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-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-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-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": ["@anthropic-ai/claude-agent-sdk-linux-arm64@0.3.201", "", { "os": "linux", "cpu": "arm64" }, "sha512-mShTo3MwF0gkN4dDw78wWJiB6aBDVRkl81cnApvoBofpdyUBYgm9Gw16CCjDTgelMKeBFqN6ErJpwjI3wbP00A=="],
|
||||
|
||||
"@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-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-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": ["@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-musl": ["@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.3.198", "", { "os": "linux", "cpu": "x64" }, "sha512-h1SrWVIMjLInYNPlf+TxXuKTOdoiOfJLBSoQG97315Z2Nh0IpBfqWExlqYTtPCgKE7q2iga31U283QfHpIDlSQ=="],
|
||||
"@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-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-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-x64": ["@anthropic-ai/claude-agent-sdk-win32-x64@0.3.198", "", { "os": "win32", "cpu": "x64" }, "sha512-y3HLuCCz1kDwUrhd6OnqO+d5BUpTFSzNUsPT9kf3r1vk9HYKF+eMC9eIlcOhiW2kX491kxEvuEOfqgIkGx15cg=="],
|
||||
"@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/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.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/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/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/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/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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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/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/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/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/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.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/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/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.2.4", "", {}, "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ=="],
|
||||
"@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.3.0", "", {}, "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ=="],
|
||||
|
||||
"@azu/format-text": ["@azu/format-text@1.0.2", "", {}, "sha512-Swi4N7Edy1Eqq82GxgEECXSSLyn6GOb5htRFPzBDdUkECGXtlf12ynO5oJSpWKPwCaUssOu7NfhDcCWpIC6Ywg=="],
|
||||
|
||||
@@ -972,9 +974,9 @@
|
||||
|
||||
"@chevrotain/types": ["@chevrotain/types@11.1.2", "", {}, "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw=="],
|
||||
|
||||
"@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/core": ["@clack/core@1.4.3", "", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ=="],
|
||||
|
||||
"@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=="],
|
||||
"@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=="],
|
||||
|
||||
"@cline/agents": ["@cline/agents@workspace:sdk/packages/agents"],
|
||||
|
||||
@@ -1434,7 +1436,7 @@
|
||||
|
||||
"@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
|
||||
|
||||
"@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=="],
|
||||
"@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=="],
|
||||
|
||||
"@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="],
|
||||
|
||||
@@ -1658,9 +1660,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.8.0", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-/3FIraneMcng67SUJCxvyInk/oxzwsxyadufk0wwfOBLf5wqtAGX4MoQASwSbndBPeARzBryUM9Azr5kHIdWLw=="],
|
||||
"@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@2.9.0", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-OQ0vzvbZBiUhjqLnUaoNfYmP8553Crr3aggB4y0ZUi815mZ7idpdJXQmoKdeBKJelYttoBlLSSHubmyw3wvX4w=="],
|
||||
|
||||
"@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/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/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,17 +1700,19 @@
|
||||
|
||||
"@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.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/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/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.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-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-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-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": ["@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-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/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/semantic-conventions": ["@opentelemetry/semantic-conventions@1.41.1", "", {}, "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA=="],
|
||||
|
||||
@@ -1742,9 +1746,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.3", "", { "dependencies": { "@posthog/types": "^1.392.0" } }, "sha512-oR+B8Q5O61N+W2+HVOBG9dbxAT/+OVxX+XvpNj6KYgptN2EB14JiKQ9Rm7DNpErNTLV7WApZEK/URkZgldOxfg=="],
|
||||
"@posthog/core": ["@posthog/core@1.39.6", "", { "dependencies": { "@posthog/types": "^1.392.0" } }, "sha512-o6ajIwN5zXoNP0D4H/QPmOyibNTUkSyOR6ya7AG5U2ywXx4awo72L2KnCoiZPQM5x/bXv6jPBdimH8M18Ax0aw=="],
|
||||
|
||||
"@posthog/types": ["@posthog/types@1.392.0", "", {}, "sha512-nctNujXL3FC1v99FktaTMSugSD9ZOZekEpahUSafkU2TSvW+XGKNkQZbokuJtiWvPBK208dwMJva8UfBkChqpw=="],
|
||||
"@posthog/types": ["@posthog/types@1.392.1", "", {}, "sha512-Qg6Gl7/1vlr8+gPtBi5gwnLgAgiyFoKOVmTvTtDcvya9cpTwZfna7rQmkGQ4B63CunUYNNbOlqcwiUwUDyTK6w=="],
|
||||
|
||||
"@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="],
|
||||
|
||||
@@ -1764,7 +1768,7 @@
|
||||
|
||||
"@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="],
|
||||
|
||||
"@protobufjs/utf8": ["@protobufjs/utf8@1.1.1", "", {}, "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg=="],
|
||||
"@protobufjs/utf8": ["@protobufjs/utf8@1.1.2", "", {}, "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug=="],
|
||||
|
||||
"@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=="],
|
||||
|
||||
@@ -2210,19 +2214,19 @@
|
||||
|
||||
"@secretlint/types": ["@secretlint/types@10.2.2", "", {}, "sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg=="],
|
||||
|
||||
"@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/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/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-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-oniguruma": ["@shikijs/engine-oniguruma@4.3.0", "", { "dependencies": { "@shikijs/types": "4.3.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1vMdN3gHfnKfLYwecUI2ITJI4RhHt96xEaJumVn7Heb0IlJ8WQMIH0Voak+2j22BpSNKdnOfB/pCTPnPm2gq7A=="],
|
||||
"@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/langs": ["@shikijs/langs@4.3.0", "", { "dependencies": { "@shikijs/types": "4.3.0" } }, "sha512-rnlqFbBRSys9bT4gl/5rw9RnS0W/I84ZldXPkO7cvlEMoV85TyF/aU01N7/NbSR776RNLjrJKjfFUXJR6wN1Cg=="],
|
||||
"@shikijs/langs": ["@shikijs/langs@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1" } }, "sha512-m0l9nsDqgBHvbZbk7A0/kXz/impK3uB/c6rAn6Gpg/uPtdZRQ+alsN/17MU5thb68XTj/4DxkZAotrM0GGSpDQ=="],
|
||||
|
||||
"@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/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/themes": ["@shikijs/themes@4.3.0", "", { "dependencies": { "@shikijs/types": "4.3.0" } }, "sha512-Avgt05YiT+Y3prjIc9lmQxhJzHBcCfR6cjiFW4OyaMBbt2A6trX5rfjUzx+Vj/mE9qpArYjatnqo9XPjQNW/AQ=="],
|
||||
"@shikijs/themes": ["@shikijs/themes@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1" } }, "sha512-dgpoJ4WqNi2yTmizQHBJ5zcX6j2lE6icN/0yt4l1kkf16jrY/pwPLoTb1ETsWMz0OBLf9ZNvwmxft+cH+N9qSA=="],
|
||||
|
||||
"@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/types": ["@shikijs/types@4.3.1", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-CHFxE0jztBIZRHH6gxXE7DXUCFXjReEGxZ/j0rfSLGKZuwp2xBYycEP14875DSa9KLL/6700oxIq6oO6ef9K2g=="],
|
||||
|
||||
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
|
||||
|
||||
@@ -2244,25 +2248,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.21.1", "", {}, "sha512-I8vmSjNYWsaxuWPx6dz4yeh0h7vRBWbgAMK14LEmblbZ404BtrPbXs6jDPx4cYgGf8msDGF4A9opLZBu21FViQ=="],
|
||||
"@slack/types": ["@slack/types@2.22.0", "", {}, "sha512-sZ9lIgJhPX2qft/tKWiklFlc0o1FWeI7QtciZJfW1+ErH1eGGHvOZ8e73sleTCFEFJp1q/R0WeS8Oa7AsiDprg=="],
|
||||
|
||||
"@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.0", "", { "dependencies": { "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-sEvpvkBVoMxjoek35XyJFn2ZD3EJ1RpiZrT47WaZodxzAIWS44zkdvbqGE/ZlugtjiQp62cffYZ9ldyRkjAGnA=="],
|
||||
"@smithy/core": ["@smithy/core@3.29.1", "", { "dependencies": { "@smithy/types": "^4.15.1", "tslib": "^2.6.2" } }, "sha512-qoiY4nrk5OCu1+eIR1VB8l5DmON/oKiqrd5zZFAhXJXjJlLWQusKEW/SkBDAtGDcPaz86m9kfcE1lngU0GlM6A=="],
|
||||
|
||||
"@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/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/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/eventstream-codec": ["@smithy/eventstream-codec@4.4.6", "", { "dependencies": { "@smithy/core": "^3.29.1", "tslib": "^2.6.2" } }, "sha512-4N/HbPptAjynK/FDrTiSAEXmpEDcQw54SeY7qnKbNMOcbHK0B0nEq+OKDYXrnqyp5YhZGzXFDUJANGkdzz6H5Q=="],
|
||||
|
||||
"@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/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/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/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/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/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/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.5", "", { "dependencies": { "@smithy/core": "^3.29.0", "tslib": "^2.6.2" } }, "sha512-ZS7Y5X8mU9qRSsqwOeGKw86WWtPsRxa396kX2HyhYwADRqFHJ0cb3p2uFk6QnFF3BluUUBHTZJpPA9QuEl0tlg=="],
|
||||
"@smithy/util-utf8": ["@smithy/util-utf8@4.4.6", "", { "dependencies": { "@smithy/core": "^3.29.1", "tslib": "^2.6.2" } }, "sha512-gncTZwzB/RTzm29VvK1nZhCHdPjBkR8pNaFtUMbQpkG6vFMjPHe/RsnmMXfrYj8Qs5s6QH5f1Mp9CBXFOHxqlQ=="],
|
||||
|
||||
"@so-ric/colorspace": ["@so-ric/colorspace@1.1.6", "", { "dependencies": { "color": "^5.0.2", "text-hex": "1.0.x" } }, "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw=="],
|
||||
|
||||
@@ -2490,7 +2494,7 @@
|
||||
|
||||
"@types/d3-quadtree": ["@types/d3-quadtree@3.0.6", "", {}, "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg=="],
|
||||
|
||||
"@types/d3-random": ["@types/d3-random@3.0.3", "", {}, "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ=="],
|
||||
"@types/d3-random": ["@types/d3-random@3.0.4", "", {}, "sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA=="],
|
||||
|
||||
"@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="],
|
||||
|
||||
@@ -2610,25 +2614,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.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/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/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/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/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/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/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/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/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.62.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-xadytJqX9vJVQ2fdQjkcIVigwaOJNWkpjdLt6cEQ+xPnrI1fkp+/jZE/I97k9KUjqtpd25i0HeyZf3T6dutv2g=="],
|
||||
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.63.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-sUAbkulqBAsncKnbRP3+7CtQFRKicexnj7ZwNC6ddCR7EmrXvjvdCYMJbUIqMd6lwoEriZjwLo08aS5tSjVMHg=="],
|
||||
|
||||
"@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/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/types": ["@typescript-eslint/types@8.62.1", "", {}, "sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q=="],
|
||||
"@typescript-eslint/types": ["@typescript-eslint/types@8.63.0", "", {}, "sha512-xyLtl9DUBBFrcJS4x2pIqGLH68/tC2uOa4Z7pUteW09D3bXnnXUom4dyPikzWgB7llmIc1zoeI3aoUdC4rPK/Q=="],
|
||||
|
||||
"@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/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/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/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/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=="],
|
||||
"@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=="],
|
||||
|
||||
"@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=="],
|
||||
|
||||
@@ -2642,21 +2646,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.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/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/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/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/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/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/pretty-format": ["@vitest/pretty-format@4.1.9", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A=="],
|
||||
"@vitest/pretty-format": ["@vitest/pretty-format@4.1.10", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q=="],
|
||||
|
||||
"@vitest/runner": ["@vitest/runner@4.1.9", "", { "dependencies": { "@vitest/utils": "4.1.9", "pathe": "^2.0.3" } }, "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg=="],
|
||||
"@vitest/runner": ["@vitest/runner@4.1.10", "", { "dependencies": { "@vitest/utils": "4.1.10", "pathe": "^2.0.3" } }, "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg=="],
|
||||
|
||||
"@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/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/spy": ["@vitest/spy@4.1.9", "", {}, "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA=="],
|
||||
"@vitest/spy": ["@vitest/spy@4.1.10", "", {}, "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw=="],
|
||||
|
||||
"@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=="],
|
||||
"@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=="],
|
||||
|
||||
"@vladfrangu/async_event_emitter": ["@vladfrangu/async_event_emitter@2.4.7", "", {}, "sha512-Xfe6rpCTxSxfbswi/W/Pz7zp1WWSNn4A0eW4mLkQUewCrXXtMj31lCg+iQyTkh/CkusZSq9eDflu7tjEDXUY6g=="],
|
||||
|
||||
@@ -2700,9 +2704,9 @@
|
||||
|
||||
"@xterm/headless": ["@xterm/headless@5.5.0", "", {}, "sha512-5xXB7kdQlFBP82ViMJTwwEc3gKCLGKR/eoxQm4zge7GPBl86tCdI0IdPJjoKd8mUSFXz5V7i/25sfsEkP4j46g=="],
|
||||
|
||||
"@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/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/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=="],
|
||||
"@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=="],
|
||||
|
||||
"abbrev": ["abbrev@3.0.1", "", {}, "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg=="],
|
||||
|
||||
@@ -2724,7 +2728,7 @@
|
||||
|
||||
"agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
|
||||
|
||||
"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": ["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-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=="],
|
||||
|
||||
@@ -2806,7 +2810,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.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-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-os": ["bare-os@3.9.3", "", {}, "sha512-fF4Q7QsyKVF5Rj0qvI8BgUNjqzC2JvQlpTaPLjVJVxYVUX5Zr9un+y3w1HmA4nNKdFmRBT8z/WmrjvXzXVerKQ=="],
|
||||
|
||||
@@ -2818,7 +2822,7 @@
|
||||
|
||||
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
|
||||
|
||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.40", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw=="],
|
||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.42", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q=="],
|
||||
|
||||
"basic-ftp": ["basic-ftp@5.3.1", "", {}, "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw=="],
|
||||
|
||||
@@ -2858,7 +2862,7 @@
|
||||
|
||||
"browser-stdout": ["browser-stdout@1.3.1", "", {}, "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw=="],
|
||||
|
||||
"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=="],
|
||||
"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=="],
|
||||
|
||||
"buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
|
||||
|
||||
@@ -2902,7 +2906,7 @@
|
||||
|
||||
"camelize": ["camelize@1.0.1", "", {}, "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ=="],
|
||||
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001800", "", {}, "sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA=="],
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001802", "", {}, "sha512-vmv8ub2xwTNmljSKf82mtCk5JH7hC+YgzLj3P5zotvA0tPQ9016tdNNOG8WRca1IxOnhSsivB+J0z5FeE5LOUw=="],
|
||||
|
||||
"case-anything": ["case-anything@2.1.13", "", {}, "sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng=="],
|
||||
|
||||
@@ -2986,7 +2990,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.3", "", {}, "sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog=="],
|
||||
"color2k": ["color2k@2.0.4", "", {}, "sha512-OXAPGFRNeLFnUfqDtloYdxkwsJoIdXe28+bjbpJiPqyei2HPa3VHmMCWa0Qe62+U4Ftf9Hj7hRssOkxz7WiWbg=="],
|
||||
|
||||
"colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="],
|
||||
|
||||
@@ -3244,7 +3248,7 @@
|
||||
|
||||
"eight-colors": ["eight-colors@1.3.3", "", {}, "sha512-4B54S2Qi4pJjeHmCbDIsveQZWQ/TSSQng4ixYJ9/SYHHpeS5nYK0pzcHvWzWUfRsvJQjwoIENhAwqg59thQceg=="],
|
||||
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.384", "", {}, "sha512-g6KAKY1vkYsADvSPWvdJsuYT0ixdcu6lUtD9P/wJKGBEDlZVXh2AX42j1mPqqaQPDluWjara9ziQ7xqAeXCt5A=="],
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.387", "", {}, "sha512-TaxwufTFDufvPEoXdhwVrA3UdFWBeWGkYoJ1K8ldF1xe6gKfth6iRNS5lTQ5JPNOHdGQm8PT1QYKUqFLCiUefQ=="],
|
||||
|
||||
"embla-carousel": ["embla-carousel@8.6.0", "", {}, "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA=="],
|
||||
|
||||
@@ -3474,7 +3478,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.5", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2" } }, "sha512-5FZy72Rh8LhtjmvDrKkI+lVhrsQrVKVsItxMoDm5mNQE+xR0WVIIs+jzPSJgBvKVsLi24fZhXJIsNI0bihDzFg=="],
|
||||
"gaxios": ["gaxios@7.1.6", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2" } }, "sha512-aIQ0QL8Or8vsUhHyXGA6AohOFRrAAiHhrvsAG6myzcSlfhxSXtnwXA/pRuQTilFgjhLe30swK5rg1d7E1f8Izw=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
@@ -3522,7 +3526,7 @@
|
||||
|
||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||
|
||||
"graphql": ["graphql@17.0.1", "", {}, "sha512-8eWbg5Zcv/8o20nzEjHUGPTj20MLFJjc5kagbIPxbaeGxvFwpitJhemEC/k17n5+UD4M/9ea5rTuce78mELujQ=="],
|
||||
"graphql": ["graphql@17.0.2", "", {}, "sha512-FRWbddMxfkjiB7z+aQDWIR+E34xo9I8c9mtK2RPv8PmMzKRvrdsreHL/Ui/TmwHJfhHChEtsFPyMHKI+xuarQQ=="],
|
||||
|
||||
"grpc-health-check": ["grpc-health-check@2.1.0", "", { "dependencies": { "@grpc/proto-loader": "^0.7.13" } }, "sha512-HH3WjwNtusMTEQAtRelFgsFyNcOdihvpjusNDIrGYfWG8tPNSHqELrSyriIjm70k65YSxetsKG1y4H1L5gi1wQ=="],
|
||||
|
||||
@@ -3592,7 +3596,7 @@
|
||||
|
||||
"highlight.js": ["highlight.js@11.11.1", "", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="],
|
||||
|
||||
"hono": ["hono@4.12.27", "", {}, "sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q=="],
|
||||
"hono": ["hono@4.12.28", "", {}, "sha512-YwUvVpSF7m1yOblFPrU3Hbo8XhPheBoiyfGuII6z19LnOr6JpDnyyp7LFNrfV56wS8tpvtBFGRISHN02pDdLOA=="],
|
||||
|
||||
"hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="],
|
||||
|
||||
@@ -3794,7 +3798,7 @@
|
||||
|
||||
"jsonfile": ["jsonfile@6.2.1", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q=="],
|
||||
|
||||
"jsonrepair": ["jsonrepair@3.14.1", "", { "bin": { "jsonrepair": "bin/cli.js" } }, "sha512-NpGgMhmzG/fajkBEFlS9jZvMSGDvc2xN/9wNCHZ+Nx32GZfLRELU6UE6dQkebvrQUct9S+7bvnpX29NB36Qbdw=="],
|
||||
"jsonrepair": ["jsonrepair@3.15.0", "", { "bin": { "jsonrepair": "bin/cli.js" } }, "sha512-wy8OTjwsJwQRnQJkKnMJJ9vcytRdBPAgIF/Hy6+s1dAj42BHMKiyL8JzEieIl3JY7idt8eyHwBWTO8mh/+mtwA=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
@@ -4146,7 +4150,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.93.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-Cu6yUpX5Iavugm8BeX7c0wgU9CvOqfd1yM6A1d2q2ZMjym7GjpASv2GdRcTq3Fx+Sb5OgBkEEpw4VnAbY6Y5RA=="],
|
||||
"node-abi": ["node-abi@3.94.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-W5ZNO5KRPB5TkYmGVD9F6YqhsglXJzE6etpbmT+f6EQElhiX/UTG551cnsRGvLG3fyZEg9HwaDmNmj5nwJ4z9g=="],
|
||||
|
||||
"node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
|
||||
|
||||
@@ -4310,7 +4314,7 @@
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
|
||||
"picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
@@ -4344,13 +4348,13 @@
|
||||
|
||||
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
|
||||
|
||||
"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-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-node": ["posthog-node@5.39.2", "", { "dependencies": { "@posthog/core": "^1.39.3" }, "peerDependencies": { "rxjs": "^7.0.0" }, "optionalPeers": ["rxjs"] }, "sha512-5piMedjlQ2x+UKLvHWTC5ls5/T1dDZKE1Pu5AKkYh9EkbZOjvu0cac6lWFB7mgbGkKQ0I1bhbjDx1QAYRJ7Unw=="],
|
||||
"posthog-node": ["posthog-node@5.39.4", "", { "dependencies": { "@posthog/core": "^1.39.5" }, "peerDependencies": { "rxjs": "^7.0.0" }, "optionalPeers": ["rxjs"] }, "sha512-+fCQ7htBFRQQFbIzl1T0TA7bDwYyaB9XP308ZFMCUoB5LzTzOFxBa6TYVrxdH/VQl43WXTp6sf0QsG2Z4XlNBg=="],
|
||||
|
||||
"powershell-utils": ["powershell-utils@0.1.0", "", {}, "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A=="],
|
||||
|
||||
"preact": ["preact@10.29.3", "", {}, "sha512-D9NL1GAnJZhc3RndVs4gDdxEeU9TcHgywMrhhOsnpdlvFjdbx0gAsLUnH6JEhlJH5giL7Tx5biWPUSEXE/HPzw=="],
|
||||
"preact": ["preact@10.29.4", "", {}, "sha512-GMpwh9+NJ8tSmqwIaVyFRQkiKfBEzQ+k7r7tle4W+kaJ+7wJiB9hFz9BixAomMtenPPSBfM4bZhXozGxhf0uFQ=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
@@ -4432,7 +4436,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.80.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-4P+fk6oXsxY+6xSj7Euhc2sumQD8zQqCuVHoJwoyp9EchP+IUW9OESB7uHFJOKsIBQ4MQqYE84INJFqUCYNoOg=="],
|
||||
"react-hook-form": ["react-hook-form@7.81.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-ocbmr2p5KBMoAfj4WCUvped33lVi1Kd5DuDUvQDnB6VEAacOjPI/jMbtDdbhco4y9ct4xUuCmMY0b/C9L0QHjw=="],
|
||||
|
||||
"react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
|
||||
|
||||
@@ -4612,7 +4616,7 @@
|
||||
|
||||
"setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
|
||||
|
||||
"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=="],
|
||||
"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=="],
|
||||
|
||||
"shallow-clone": ["shallow-clone@3.0.1", "", { "dependencies": { "kind-of": "^6.0.2" } }, "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA=="],
|
||||
|
||||
@@ -4624,7 +4628,7 @@
|
||||
|
||||
"shell-quote": ["shell-quote@1.9.0", "", {}, "sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA=="],
|
||||
|
||||
"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=="],
|
||||
"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=="],
|
||||
|
||||
"shimmer": ["shimmer@1.2.1", "", {}, "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw=="],
|
||||
|
||||
@@ -4776,7 +4780,7 @@
|
||||
|
||||
"symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="],
|
||||
|
||||
"systeminformation": ["systeminformation@5.31.11", "", { "os": "!aix", "bin": { "systeminformation": "lib/cli.js" } }, "sha512-I6O7iaUj23AXRgCPDDnvi3xHvdOLp4+1YMbF+X194lJwY1NeWojgHJPhslVKcmTtrLTguRk3QJK+xEdTiI3P0w=="],
|
||||
"systeminformation": ["systeminformation@5.31.13", "", { "os": "!aix", "bin": { "systeminformation": "lib/cli.js" } }, "sha512-iUJXJoKzm4vtLSeT3nwe2s9QjoJAxHg7wYJ0KaQ54Xy2u9jsTq0ULWQQ0+T72FXjX2XnGqubazNx9lUfng7ELw=="],
|
||||
|
||||
"tabbable": ["tabbable@6.5.0", "", {}, "sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA=="],
|
||||
|
||||
@@ -4888,7 +4892,7 @@
|
||||
|
||||
"ts-poet": ["ts-poet@6.12.0", "", { "dependencies": { "dprint-node": "^1.0.8" } }, "sha512-xo+iRNMWqyvXpFTaOAvLPA5QAWO6TZrSUs5s4Odaya3epqofBu/fMLHEWl8jPmjhA0s9sgj9sNvF1BmaQlmQkA=="],
|
||||
|
||||
"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": ["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-descriptors": ["ts-proto-descriptors@2.1.0", "", { "dependencies": { "@bufbuild/protobuf": "^2.0.0" } }, "sha512-S5EZYEQ6L9KLFfjSRpZWDIXDV/W7tAj8uW7pLsihIxyr62EAVSiKuVPwE8iWnr849Bqa53enex1jhDUcpgquzA=="],
|
||||
|
||||
@@ -4918,7 +4922,7 @@
|
||||
|
||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"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=="],
|
||||
"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=="],
|
||||
|
||||
"uc.micro": ["uc.micro@1.0.6", "", {}, "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="],
|
||||
|
||||
@@ -5014,11 +5018,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.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": ["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-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.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=="],
|
||||
"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=="],
|
||||
|
||||
"voca": ["voca@1.4.1", "", {}, "sha512-NJC/BzESaHT1p4B5k4JykxedeltmNbau4cummStd4RjFojgq/kLew5TzYge9N2geeWyI2w8T30wUET5v+F7ZHA=="],
|
||||
|
||||
@@ -5110,7 +5114,7 @@
|
||||
|
||||
"yocto-queue": ["yocto-queue@1.2.2", "", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="],
|
||||
|
||||
"yocto-spinner": ["yocto-spinner@1.2.0", "", { "dependencies": { "yoctocolors": "^2.1.1" } }, "sha512-Yw0hUB6UA3o4YUgKy3oSe9a4cxoaZ9sBfYDw+JSxo6Id0KoJGoxzPA24qqUXYKBWABs/zDSGTz9kww7t3F0XGw=="],
|
||||
"yocto-spinner": ["yocto-spinner@1.2.1", "", { "dependencies": { "yoctocolors": "^2.1.1" } }, "sha512-9cbFWLhbiZp+820O4pkHGNncI7+MrUGzBOjw8NMG+ewsY+aG0DdEXnr19Smxao32YOjLZRMdn1UtaxcrXOYOIg=="],
|
||||
|
||||
"yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="],
|
||||
|
||||
@@ -5214,6 +5218,8 @@
|
||||
|
||||
"@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=="],
|
||||
@@ -5680,9 +5686,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.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="],
|
||||
"@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/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="],
|
||||
"@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/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="],
|
||||
|
||||
@@ -5754,7 +5760,7 @@
|
||||
|
||||
"body-parser/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="],
|
||||
|
||||
"body-parser/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||
"body-parser/iconv-lite": ["iconv-lite@0.7.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ=="],
|
||||
|
||||
"chai/assertion-error": ["assertion-error@1.1.0", "", {}, "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw=="],
|
||||
|
||||
@@ -5966,7 +5972,7 @@
|
||||
|
||||
"onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
|
||||
|
||||
"open-graph-scraper/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||
"open-graph-scraper/iconv-lite": ["iconv-lite@0.7.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ=="],
|
||||
|
||||
"ora/cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="],
|
||||
|
||||
@@ -6096,7 +6102,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.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||
"raw-body/iconv-lite": ["iconv-lite@0.7.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ=="],
|
||||
|
||||
"rc/strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
|
||||
|
||||
@@ -6198,8 +6204,6 @@
|
||||
|
||||
"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=="],
|
||||
@@ -6232,7 +6236,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.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=="],
|
||||
"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=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
@@ -6774,7 +6778,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.5", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2" } }, "sha512-5FZy72Rh8LhtjmvDrKkI+lVhrsQrVKVsItxMoDm5mNQE+xR0WVIIs+jzPSJgBvKVsLi24fZhXJIsNI0bihDzFg=="],
|
||||
"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=="],
|
||||
|
||||
"hast-to-hyperscript/style-to-object/inline-style-parser": ["inline-style-parser@0.1.1", "", {}, "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q=="],
|
||||
|
||||
@@ -6976,28 +6980,6 @@
|
||||
|
||||
"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=="],
|
||||
@@ -7010,19 +6992,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.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/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/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/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/pretty-format": ["@vitest/pretty-format@3.2.6", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-lb7XXXzmm2h2ASzFnRvQpDo6onT1NmMJA3tkGTWiBFtRJ9lxGY3d3mm/Apt36gej2bkkOVLL/yTOtufDaFa/jA=="],
|
||||
"webview-ui/vitest/@vitest/pretty-format": ["@vitest/pretty-format@3.2.7", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA=="],
|
||||
|
||||
"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/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/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/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/spy": ["@vitest/spy@3.2.6", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-oq6BbH68WzcWmwtBrU9nqLeaXTR4XwJF7FSLkKEZo4i6eoXcrxjcwSuTvWBIRUTC6VC72nXYunzqgZA+IKdtxg=="],
|
||||
"webview-ui/vitest/@vitest/spy": ["@vitest/spy@3.2.7", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ=="],
|
||||
|
||||
"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/@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/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=="],
|
||||
|
||||
@@ -7244,10 +7226,6 @@
|
||||
|
||||
"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,5 +1,14 @@
|
||||
# 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@cline/agents",
|
||||
"version": "0.0.59",
|
||||
"version": "0.0.60",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cline/cline",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@cline/core",
|
||||
"description": "Cline Core SDK for Node Runtime",
|
||||
"version": "0.0.59",
|
||||
"version": "0.0.60",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cline/cline",
|
||||
|
||||
@@ -93,8 +93,22 @@ describe("auth/cline getValidClineCredentials", () => {
|
||||
),
|
||||
) as unknown as typeof fetch;
|
||||
|
||||
const result = await getValidClineCredentials(current, PROVIDER_OPTIONS);
|
||||
const capture = vi.fn();
|
||||
const result = await getValidClineCredentials(current, {
|
||||
...PROVIDER_OPTIONS,
|
||||
telemetry: { capture } as never,
|
||||
});
|
||||
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();
|
||||
});
|
||||
|
||||
@@ -115,11 +129,68 @@ describe("auth/cline getValidClineCredentials", () => {
|
||||
),
|
||||
) as unknown as typeof fetch;
|
||||
|
||||
const result = await getValidClineCredentials(current, PROVIDER_OPTIONS, {
|
||||
refreshBufferMs: 60_000,
|
||||
retryableTokenGraceMs: 30_000,
|
||||
});
|
||||
const capture = vi.fn();
|
||||
const result = await getValidClineCredentials(
|
||||
current,
|
||||
{ ...PROVIDER_OPTIONS, telemetry: { capture } as never },
|
||||
{
|
||||
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,9 +2,11 @@ import {
|
||||
getClineEnvironmentConfig,
|
||||
type ITelemetryService,
|
||||
} from "@cline/shared";
|
||||
import { hashSecret, sdkDebug } from "../logging/early-logger";
|
||||
import {
|
||||
captureAuthFailed,
|
||||
captureAuthLoggedOut,
|
||||
captureAuthRefreshSoftFailure,
|
||||
captureAuthStarted,
|
||||
captureAuthSucceeded,
|
||||
identifyAccount,
|
||||
@@ -621,27 +623,34 @@ export async function refreshClineToken(
|
||||
current: ClineOAuthCredentials,
|
||||
options: ClineOAuthProviderOptions,
|
||||
): Promise<ClineOAuthCredentials> {
|
||||
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,
|
||||
),
|
||||
},
|
||||
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)),
|
||||
},
|
||||
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 },
|
||||
@@ -651,19 +660,32 @@ export async function refreshClineToken(
|
||||
const json = (await response.json()) as ClineTokenResponse;
|
||||
const provider =
|
||||
(current.metadata?.provider as string | undefined) ?? options.provider;
|
||||
return toClineCredentials(
|
||||
const result = 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;
|
||||
}
|
||||
|
||||
@@ -676,24 +698,64 @@ 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;
|
||||
}
|
||||
return null;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,10 +147,7 @@ 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",
|
||||
{
|
||||
@@ -176,12 +173,15 @@ 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,8 +270,7 @@ 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,8 +475,7 @@ 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,9 +230,7 @@ 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",
|
||||
@@ -377,7 +375,11 @@ 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),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -422,7 +424,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,9 +21,7 @@ interface ProjectionPolicy {
|
||||
dropThinkingBlocks: boolean;
|
||||
}
|
||||
|
||||
function resolveProjectionPolicy(
|
||||
intent: BudgetPolicyIntent,
|
||||
): ProjectionPolicy {
|
||||
function resolveProjectionPolicy(intent: BudgetPolicyIntent): ProjectionPolicy {
|
||||
switch (intent) {
|
||||
case "agentic_summary":
|
||||
case "basic_compaction_projection":
|
||||
@@ -123,7 +121,11 @@ 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) {
|
||||
@@ -207,7 +209,9 @@ function shouldDropWholeBlock(
|
||||
if (policy.dropThinkingBlocks && block.type === "thinking") {
|
||||
return true;
|
||||
}
|
||||
return policy.dropUnsafeOutsideLiveTail && !isProtected && isUnsafeBlock(block);
|
||||
return (
|
||||
policy.dropUnsafeOutsideLiveTail && !isProtected && isUnsafeBlock(block)
|
||||
);
|
||||
}
|
||||
|
||||
function pruneEmptyMessages(
|
||||
@@ -221,13 +225,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);
|
||||
@@ -322,7 +326,6 @@ function dropThinkingBlocks(
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function truncateText(text: string, maxChars: number): string {
|
||||
if (maxChars <= 0) {
|
||||
return "";
|
||||
@@ -576,8 +579,7 @@ 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,13 +1021,15 @@ 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;
|
||||
|
||||
@@ -9,6 +9,7 @@ export {
|
||||
ClineNotSubscribedError,
|
||||
ClineOrgIndividualInferenceSubscriptionError,
|
||||
ClinePassLimitError,
|
||||
extractClinePassLimitMessage,
|
||||
getClineOrgIndividualInferenceSubscriptionMessage,
|
||||
getClineNotSubscribedMessage,
|
||||
getClinePassSubscriptionUrl,
|
||||
@@ -131,10 +132,15 @@ 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,
|
||||
@@ -302,7 +308,10 @@ export {
|
||||
type McpServerTransportConfig,
|
||||
type McpSettingsFile,
|
||||
type McpSettingsLockOptions,
|
||||
McpSettingsLockTimeoutError,
|
||||
type McpSettingsMutator,
|
||||
McpSettingsMutatorPurityError,
|
||||
McpSettingsUpdateSkippedError,
|
||||
type McpSseTransportConfig,
|
||||
type McpStdioTransportConfig,
|
||||
type McpStreamableHttpTransportConfig,
|
||||
@@ -321,9 +330,6 @@ export {
|
||||
updateMcpServerOAuthStateAsync,
|
||||
updateMcpSettingsFile,
|
||||
updateMcpSettingsFileSync,
|
||||
McpSettingsLockTimeoutError,
|
||||
McpSettingsMutatorPurityError,
|
||||
McpSettingsUpdateSkippedError,
|
||||
} from "./extensions/mcp";
|
||||
export {
|
||||
type AgentTask,
|
||||
@@ -412,6 +418,11 @@ 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 {
|
||||
@@ -474,7 +485,10 @@ export {
|
||||
type FeatureFlagsServiceOptions,
|
||||
NoOpFeatureFlagsProvider,
|
||||
} from "./services/feature-flags";
|
||||
export type { GlobalSettings } from "./services/global-settings";
|
||||
export type {
|
||||
GlobalCompactionStrategy,
|
||||
GlobalSettings,
|
||||
} from "./services/global-settings";
|
||||
export {
|
||||
filterDisabledPluginPaths,
|
||||
filterDisabledTools,
|
||||
@@ -496,16 +510,6 @@ 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,
|
||||
@@ -526,6 +530,15 @@ 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,
|
||||
@@ -687,11 +700,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,
|
||||
@@ -791,17 +804,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,
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* 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.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
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,6 +12,72 @@ 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,6 +342,87 @@ 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,6 +226,8 @@ 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,
|
||||
@@ -1331,6 +1333,60 @@ 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.
|
||||
@@ -1903,31 +1959,26 @@ export class LocalRuntimeHost implements RuntimeHost {
|
||||
exitCode,
|
||||
);
|
||||
if (!result.updated) 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;
|
||||
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;
|
||||
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,4 +1,5 @@
|
||||
import type { ITelemetryService } from "@cline/shared";
|
||||
import { hashSecret, sdkDebug } from "../../logging/early-logger";
|
||||
import {
|
||||
getProviderAuthHandler,
|
||||
getProviderOAuthCredentialsFromSettings,
|
||||
@@ -113,6 +114,9 @@ export class RuntimeOAuthTokenManager {
|
||||
const settings =
|
||||
this.providerSettingsManager.getProviderSettings(storageProviderId);
|
||||
if (!settings) {
|
||||
sdkDebug(
|
||||
`oauth.resolve providerId=${providerId} storageProviderId=${storageProviderId} outcome=no_settings`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -121,9 +125,16 @@ 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,
|
||||
@@ -131,6 +142,9 @@ export class RuntimeOAuthTokenManager {
|
||||
telemetry: this.telemetry,
|
||||
});
|
||||
if (!nextCredentials) {
|
||||
sdkDebug(
|
||||
`oauth.resolve providerId=${providerId} outcome=refresh_returned_null`,
|
||||
);
|
||||
throw new OAuthReauthRequiredError(providerId);
|
||||
}
|
||||
|
||||
@@ -144,10 +158,15 @@ 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 {
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import {
|
||||
mkdirSync,
|
||||
mkdtempSync,
|
||||
readdirSync,
|
||||
readFileSync,
|
||||
rmSync,
|
||||
writeFileSync,
|
||||
} from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import * as LlmsModels from "@cline/llms";
|
||||
@@ -51,6 +58,50 @@ 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,12 +3,15 @@ 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,
|
||||
@@ -99,6 +102,10 @@ 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 {
|
||||
@@ -114,11 +121,21 @@ export class ProviderSettingsManager {
|
||||
if (!existsSync(dir)) {
|
||||
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
||||
}
|
||||
writeFileSync(
|
||||
this.filePath,
|
||||
`${JSON.stringify(normalized, null, 2)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
// 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;
|
||||
}
|
||||
// Restrict file to owner-only read/write (best-effort; no-op on Windows).
|
||||
try {
|
||||
chmodSync(this.filePath, 0o600);
|
||||
@@ -154,6 +171,16 @@ 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,6 +48,7 @@ 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",
|
||||
},
|
||||
@@ -252,10 +253,40 @@ 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.59",
|
||||
"version": "0.0.60",
|
||||
"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,6 +63,7 @@ export {
|
||||
ClinePassLimitError,
|
||||
createHandler,
|
||||
createHandlerAsync,
|
||||
extractClinePassLimitMessage,
|
||||
getClineOrgIndividualInferenceSubscriptionMessage,
|
||||
getClineNotSubscribedMessage,
|
||||
getClinePassSubscriptionUrl,
|
||||
|
||||
@@ -2,6 +2,7 @@ export {
|
||||
ClineNotSubscribedError,
|
||||
ClineOrgIndividualInferenceSubscriptionError,
|
||||
ClinePassLimitError,
|
||||
extractClinePassLimitMessage,
|
||||
getClineOrgIndividualInferenceSubscriptionMessage,
|
||||
getClineNotSubscribedMessage,
|
||||
getClinePassSubscriptionUrl,
|
||||
|
||||
@@ -32,6 +32,7 @@ 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.59",
|
||||
"version": "0.0.60",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cline/cline",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@cline/shared",
|
||||
"version": "0.0.59",
|
||||
"version": "0.0.60",
|
||||
"description": "Shared utilities, types, and schemas for Cline packages",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
Reference in New Issue
Block a user