diff --git a/core/config/ConfigHandler.ts b/core/config/ConfigHandler.ts index 65cff17d25f..49eb7881ef2 100644 --- a/core/config/ConfigHandler.ts +++ b/core/config/ConfigHandler.ts @@ -1,6 +1,5 @@ import { ConfigResult, ConfigValidationError } from "@continuedev/config-yaml"; -import { ControlPlaneClient } from "../control-plane/client.js"; import { BrowserSerializedContinueConfig, ContinueConfig, @@ -12,12 +11,6 @@ import { import { GlobalContext } from "../util/GlobalContext.js"; import EventEmitter from "node:events"; -import { - AuthType, - ControlPlaneSessionInfo, -} from "../control-plane/AuthTypes.js"; -import { getControlPlaneEnv } from "../control-plane/env.js"; -import { PolicySingleton } from "../control-plane/PolicySingleton.js"; import { Logger } from "../util/Logger.js"; import { @@ -25,7 +18,6 @@ import { LoadAssistantFilesOptions, } from "./loadLocalAssistants.js"; import LocalProfileLoader from "./profile/LocalProfileLoader.js"; -import PlatformProfileLoader from "./profile/PlatformProfileLoader.js"; import { OrganizationDescription, OrgWithProfiles, @@ -39,7 +31,6 @@ export type { ProfileDescription }; type ConfigUpdateFunction = (payload: ConfigResult) => void; export class ConfigHandler { - controlPlaneClient: ControlPlaneClient; private readonly globalContext = new GlobalContext(); private globalLocalProfileManager: ProfileLifecycleManager; @@ -60,16 +51,10 @@ export class ConfigHandler { constructor( private readonly ide: IDE, private llmLogger: ILLMLogger, - initialSessionInfoPromise: Promise, ) { - this.controlPlaneClient = new ControlPlaneClient( - initialSessionInfoPromise, - this.ide, - ); - // This profile manager will always be available this.globalLocalProfileManager = new ProfileLifecycleManager( - new LocalProfileLoader(ide, this.controlPlaneClient, this.llmLogger), + new LocalProfileLoader(ide, this.llmLogger), this.ide, ); @@ -100,14 +85,12 @@ export class ConfigHandler { return `${workspaceId}:::${orgId}`; } - private async cascadeInit(reason: string, isLogin?: boolean) { + private async cascadeInit(reason: string) { const signal = this.cascadeAbortController.signal; this.workspaceDirs = null; // forces workspace dirs reload - // Always update globalLocalProfileManager before recreating all the loaders - // during every cascadeInit so it holds the most recent controlPlaneClient. this.globalLocalProfileManager = new ProfileLifecycleManager( - new LocalProfileLoader(this.ide, this.controlPlaneClient, this.llmLogger), + new LocalProfileLoader(this.ide, this.llmLogger), this.ide, ); @@ -118,12 +101,7 @@ export class ConfigHandler { const workspaceId = await this.getWorkspaceId(); const selectedOrgs = this.globalContext.get("lastSelectedOrgIdForWorkspace") ?? {}; - let currentSelection = selectedOrgs[workspaceId]; - - // reset personal org to first available non-personal org on login - if (isLogin && currentSelection === "personal") { - currentSelection = null; - } + const currentSelection = selectedOrgs[workspaceId]; const firstNonPersonal = orgs.find( (org) => org.id !== this.PERSONAL_ORG_DESC.id, @@ -144,7 +122,7 @@ export class ConfigHandler { } if (signal.aborted) { - return; // local only case, no`fetch to throw abort error + return; } this.globalContext.update("lastSelectedOrgIdForWorkspace", { @@ -172,43 +150,6 @@ export class ConfigHandler { errors?: ConfigValidationError[]; }> { const errors: ConfigValidationError[] = []; - const isSignedIn = await this.controlPlaneClient.isSignedIn(); - if (isSignedIn) { - try { - // TODO use policy returned with org, not policy endpoint - const policyResponse = await this.controlPlaneClient.getPolicy(); - PolicySingleton.getInstance().policy = policyResponse; - const orgDescriptions = - await this.controlPlaneClient.listOrganizations(); - const orgsWithPolicy = orgDescriptions.map((d) => ({ - ...d, - policy: policyResponse?.policy, - })); - - if (policyResponse?.policy?.allowOtherOrgs === false) { - if (orgsWithPolicy.length === 0) { - return { orgs: [] }; - } else { - const firstOrg = await this.getNonPersonalHubOrg(orgsWithPolicy[0]); - return { orgs: [firstOrg] }; - } - } - const orgs = await Promise.all([ - this.getPersonalHubOrg(), - ...orgsWithPolicy.map((org) => this.getNonPersonalHubOrg(org)), - ]); - // TODO make try/catch more granular here, to catch specific org errors - return { orgs }; - } catch (e) { - errors.push({ - fatal: false, - message: `Error loading Continue Hub assistants${e instanceof Error ? ":\n" + e.message : ""}`, - }); - } - } else { - PolicySingleton.getInstance().policy = null; - } - // Load local org if not signed in or hub orgs fail try { const orgs = [await this.getLocalOrg()]; return { orgs }; @@ -235,58 +176,12 @@ export class ConfigHandler { })); } - private async getHubProfiles(orgScopeId: string | null) { - const assistants = await this.controlPlaneClient.listAssistants(orgScopeId); - - return await Promise.all( - assistants.map(async (assistant) => { - const profileLoader = await PlatformProfileLoader.create({ - configResult: { - ...assistant.configResult, - config: assistant.configResult.config, - }, - ownerSlug: assistant.ownerSlug, - packageSlug: assistant.packageSlug, - iconUrl: assistant.iconUrl, - versionSlug: assistant.configResult.config?.version ?? "latest", - controlPlaneClient: this.controlPlaneClient, - ide: this.ide, - llmLogger: this.llmLogger, - rawYaml: assistant.rawYaml, - orgScopeId: orgScopeId, - }); - - return new ProfileLifecycleManager(profileLoader, this.ide); - }), - ); - } - - private async getNonPersonalHubOrg( - org: OrganizationDescription, - ): Promise { - const localProfiles = await this.getLocalProfiles({ - includeGlobal: false, - includeWorkspace: true, - }); - const profiles = [...(await this.getHubProfiles(org.id)), ...localProfiles]; - return this.rectifyProfilesForOrg(org, profiles); - } - private PERSONAL_ORG_DESC: OrganizationDescription = { iconUrl: "", id: "personal", name: "Personal", slug: undefined, }; - private async getPersonalHubOrg() { - const localProfiles = await this.getLocalProfiles({ - includeGlobal: true, - includeWorkspace: true, - }); - const hubProfiles = await this.getHubProfiles(null); - const profiles = [...hubProfiles, ...localProfiles]; - return this.rectifyProfilesForOrg(this.PERSONAL_ORG_DESC, profiles); - } private async getLocalOrg() { const localProfiles = await this.getLocalProfiles({ @@ -344,13 +239,6 @@ export class ConfigHandler { /** * Users can define as many local agents as they want in a `.continue/agents` (or previous .continue/assistants) folder */ - - // Local customization disabled for on-premise deployments - const env = await getControlPlaneEnv(this.ide.getIdeSettings()); - if (env.AUTH_TYPE === AuthType.OnPrem) { - return []; - } - const localProfiles: ProfileLifecycleManager[] = []; if (options.includeGlobal) { @@ -369,12 +257,7 @@ export class ConfigHandler { "agents", ); const profiles = [...assistantFiles, ...agentFiles].map((assistant) => { - return new LocalProfileLoader( - this.ide, - this.controlPlaneClient, - this.llmLogger, - assistant, - ); + return new LocalProfileLoader(this.ide, this.llmLogger, assistant); }); const localAssistantProfiles = profiles.map( (profile) => new ProfileLifecycleManager(profile, this.ide), @@ -399,49 +282,6 @@ export class ConfigHandler { await this.cascadeInit("IDE settings update"); } - // Session change: refresh session and cascade refresh from the top - async updateControlPlaneSessionInfo( - sessionInfo: ControlPlaneSessionInfo | undefined, - ) { - const currentSession = await this.controlPlaneClient.sessionInfoPromise; - const newSession = sessionInfo; - - let reload = false; - let isLogin = false; - if (newSession) { - if (currentSession) { - if ( - newSession.AUTH_TYPE !== AuthType.OnPrem && - currentSession.AUTH_TYPE !== AuthType.OnPrem - ) { - if (newSession.account.id !== currentSession.account.id) { - // session id change (non-on-prem) - reload = true; - } - } - } else { - // log in - reload = true; - isLogin = true; - } - } else { - if (currentSession) { - // log out - reload = true; - } - } - - if (reload) { - this.controlPlaneClient = new ControlPlaneClient( - Promise.resolve(sessionInfo), - this.ide, - ); - this.abortCascade(); - await this.cascadeInit("Control plane session info update", isLogin); - } - return reload; - } - // Org id: check id validity, save selection, switch and reload async setSelectedOrgId(orgId: string, profileId?: string) { if (orgId === this.currentOrg?.id) { @@ -617,13 +457,8 @@ export class ConfigHandler { return; } - if (profile.profileDescription.profileType === "local") { - const configFile = element?.sourceFile ?? profile.profileDescription.uri; - await this.ide.openFile(configFile); - } else { - const env = await getControlPlaneEnv(this.ide.getIdeSettings()); - await this.ide.openUrl(`${env.APP_URL}${openProfileId}`); - } + const configFile = element?.sourceFile ?? profile.profileDescription.uri; + await this.ide.openFile(configFile); } // Ancient method of adding custom providers through vs code diff --git a/core/config/default.ts b/core/config/default.ts index 817bd791e97..98e76ec9286 100644 --- a/core/config/default.ts +++ b/core/config/default.ts @@ -1,7 +1,7 @@ import { ConfigYaml } from "@continuedev/config-yaml"; export const defaultConfig: ConfigYaml = { - name: "Local Config", + name: "Main Config", version: "1.0.0", schema: "v1", models: [], diff --git a/core/config/load.ts b/core/config/load.ts index e4c7e13c4f0..366e7db248f 100644 --- a/core/config/load.ts +++ b/core/config/load.ts @@ -36,7 +36,6 @@ import { getLegacyBuiltInSlashCommandFromDescription } from "../commands/slash/b import { convertCustomCommandToSlashCommand } from "../commands/slash/customSlashCommand"; import { slashCommandFromPromptFile } from "../commands/slash/promptFileSlashCommand"; import { MCPManagerSingleton } from "../context/mcp/MCPManagerSingleton"; -import { useHub } from "../control-plane/env"; import { BaseLLM } from "../llm"; import { LLMClasses, llmFromDescription } from "../llm/llms"; import CustomLLMClass from "../llm/llms/CustomLLM"; @@ -60,7 +59,6 @@ import { localPathToUri } from "../util/pathToUri"; import { loadJsonMcpConfigs } from "../context/mcp/json/loadJsonMcpConfigs"; import CustomContextProviderClass from "../context/providers/CustomContextProvider"; -import { PolicySingleton } from "../control-plane/PolicySingleton"; import { getBaseToolDefinitions, serializeTool } from "../tools"; import { resolveRelativePathInDir } from "../util/ideUtils"; import { getWorkspaceRcConfigs } from "./json/loadRcConfigs"; @@ -240,7 +238,6 @@ async function intermediateToFinalConfig({ ideInfo, uniqueId, llmLogger, - workOsAccessToken, loadPromptFiles = true, }: { config: Config; @@ -249,7 +246,6 @@ async function intermediateToFinalConfig({ ideInfo: IdeInfo; uniqueId: string; llmLogger: ILLMLogger; - workOsAccessToken: string | undefined; loadPromptFiles?: boolean; }): Promise<{ config: ContinueConfig; errors: ConfigValidationError[] }> { const errors: ConfigValidationError[] = []; @@ -550,10 +546,7 @@ async function intermediateToFinalConfig({ // Trigger MCP server refreshes (Config is reloaded again once connected!) const mcpManager = MCPManagerSingleton.getInstance(); - const orgPolicy = PolicySingleton.getInstance().policy; - if (orgPolicy?.policy?.allowMcpServers === false) { - await mcpManager.shutdown(); - } else { + { const mcpOptions: InternalMcpOptions[] = ( config.experimental?.modelContextProtocolServers ?? [] ).map((server, index) => ({ @@ -677,7 +670,6 @@ async function finalToBrowserConfig( tools: final.tools.map(serializeTool), mcpServerStatuses: final.mcpServerStatuses, tabAutocompleteOptions: final.tabAutocompleteOptions, - usePlatform: await useHub(ide.getIdeSettings()), modelsByRole: Object.fromEntries( Object.entries(final.modelsByRole).map(([k, v]) => [ k, @@ -828,7 +820,6 @@ async function loadContinueConfigFromJson( ideInfo: IdeInfo, uniqueId: string, llmLogger: ILLMLogger, - workOsAccessToken: string | undefined, overrideConfigJson: SerializedContinueConfig | undefined, ): Promise> { const workspaceConfigs = await getWorkspaceRcConfigs(ide); @@ -924,7 +915,6 @@ async function loadContinueConfigFromJson( ideInfo, uniqueId, llmLogger, - workOsAccessToken, }); return { config: finalConfig, diff --git a/core/config/profile/LocalProfileLoader.ts b/core/config/profile/LocalProfileLoader.ts index 1ddde69ad8c..d6cf30c0297 100644 --- a/core/config/profile/LocalProfileLoader.ts +++ b/core/config/profile/LocalProfileLoader.ts @@ -1,6 +1,5 @@ import { ConfigResult, parseConfigYaml } from "@continuedev/config-yaml"; -import { ControlPlaneClient } from "../../control-plane/client.js"; import { ContinueConfig, IDE, ILLMLogger } from "../../index.js"; import { ProfileDescription } from "../ProfileLifecycleManager.js"; @@ -15,7 +14,6 @@ export default class LocalProfileLoader implements IProfileLoader { constructor( private ide: IDE, - private controlPlaneClient: ControlPlaneClient, private llmLogger: ILLMLogger, private overrideAssistantFile?: | { path: string; content: string } @@ -32,7 +30,7 @@ export default class LocalProfileLoader implements IProfileLoader { iconUrl: "", title: overrideAssistantFile?.path ? getUriPathBasename(overrideAssistantFile.path) - : "Local Config", + : "Main Config", errors: undefined, uri: overrideAssistantFile?.path ?? @@ -56,11 +54,9 @@ export default class LocalProfileLoader implements IProfileLoader { async doLoadConfig(): Promise> { const result = await doLoadConfig({ ide: this.ide, - controlPlaneClient: this.controlPlaneClient, llmLogger: this.llmLogger, profileId: this.description.id, overrideConfigYamlByPath: this.overrideAssistantFile?.path, - orgScopeId: null, packageIdentifier: { uriType: "file", fileUri: this.overrideAssistantFile?.path ?? getPrimaryConfigFilePath(), diff --git a/core/config/profile/LocalProfileLoader.vitest.ts b/core/config/profile/LocalProfileLoader.vitest.ts index 0253936aef3..0e9fa532960 100644 --- a/core/config/profile/LocalProfileLoader.vitest.ts +++ b/core/config/profile/LocalProfileLoader.vitest.ts @@ -1,6 +1,5 @@ import { describe, expect, it, vi } from "vitest"; -import { ControlPlaneClient } from "../../control-plane/client.js"; import { LLMLogger } from "../../llm/logger.js"; import { testIde } from "../../test/fixtures.js"; import LocalProfileLoader from "./LocalProfileLoader.js"; @@ -17,10 +16,6 @@ vi.mock("./doLoadConfig.js", () => ({ })); describe("LocalProfileLoader", () => { - const controlPlaneClient = new ControlPlaneClient( - Promise.resolve(undefined), - testIde, - ); const llmLogger = new LLMLogger(); it("should pass pre-read content in packageIdentifier for override files", async () => { @@ -29,12 +24,7 @@ describe("LocalProfileLoader", () => { content: "name: Test\nversion: 1.0.0\nschema: v1\n", }; - const loader = new LocalProfileLoader( - testIde, - controlPlaneClient, - llmLogger, - overrideFile, - ); + const loader = new LocalProfileLoader(testIde, llmLogger, overrideFile); await loader.doLoadConfig(); @@ -50,11 +40,7 @@ describe("LocalProfileLoader", () => { }); it("should not include content in packageIdentifier when no override file", async () => { - const loader = new LocalProfileLoader( - testIde, - controlPlaneClient, - llmLogger, - ); + const loader = new LocalProfileLoader(testIde, llmLogger); await loader.doLoadConfig(); diff --git a/core/config/profile/PlatformProfileLoader.ts b/core/config/profile/PlatformProfileLoader.ts deleted file mode 100644 index 0540d924a23..00000000000 --- a/core/config/profile/PlatformProfileLoader.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { AssistantUnrolled, ConfigResult } from "@continuedev/config-yaml"; - -import { ControlPlaneClient } from "../../control-plane/client.js"; -import { getControlPlaneEnv } from "../../control-plane/env.js"; -import { ContinueConfig, IDE, ILLMLogger } from "../../index.js"; -import { ProfileDescription } from "../ProfileLifecycleManager.js"; - -import doLoadConfig from "./doLoadConfig.js"; -import { IProfileLoader } from "./IProfileLoader.js"; - -/** - * Metadata about the package that is currently being loaded - * If this is `undefined`, it's not a config from the platform, - * could be local for example. - */ -export interface PlatformConfigMetadata { - ownerSlug: string; - packageSlug: string; -} - -export default class PlatformProfileLoader implements IProfileLoader { - static RELOAD_INTERVAL = 1000 * 5; // 5 seconds - - private configResult: ConfigResult; - private readonly ownerSlug: string; - private readonly packageSlug: string; - private readonly iconUrl: string; - private readonly versionSlug: string; - private readonly controlPlaneClient: ControlPlaneClient; - private readonly ide: IDE; - private llmLogger: ILLMLogger; - readonly description: ProfileDescription; - private readonly orgScopeId: string | null; - - private constructor({ - configResult, - ownerSlug, - packageSlug, - iconUrl, - versionSlug, - controlPlaneClient, - ide, - llmLogger, - description, - orgScopeId, - }: { - configResult: ConfigResult; - ownerSlug: string; - packageSlug: string; - iconUrl: string; - versionSlug: string; - controlPlaneClient: ControlPlaneClient; - ide: IDE; - llmLogger: ILLMLogger; - description: ProfileDescription; - orgScopeId: string | null; - }) { - this.configResult = configResult; - this.ownerSlug = ownerSlug; - this.packageSlug = packageSlug; - this.iconUrl = iconUrl; - this.versionSlug = versionSlug; - this.controlPlaneClient = controlPlaneClient; - this.ide = ide; - this.llmLogger = llmLogger; - this.description = description; - this.orgScopeId = orgScopeId; - } - - static async create({ - configResult, - ownerSlug, - packageSlug, - iconUrl, - versionSlug, - controlPlaneClient, - ide, - llmLogger, - rawYaml, - orgScopeId, - }: { - configResult: ConfigResult; - ownerSlug: string; - packageSlug: string; - iconUrl: string; - versionSlug: string; - controlPlaneClient: ControlPlaneClient; - ide: IDE; - llmLogger: ILLMLogger; - rawYaml: string; - orgScopeId: string | null; - }): Promise { - const controlPlaneEnv = await getControlPlaneEnv(ide.getIdeSettings()); - - const description: ProfileDescription = { - id: `${ownerSlug}/${packageSlug}`, - profileType: "platform", - fullSlug: { - ownerSlug, - packageSlug, - versionSlug, - }, - title: configResult.config?.name ?? `${ownerSlug}/${packageSlug}`, - errors: configResult.errors, - iconUrl: iconUrl, - uri: `${controlPlaneEnv.APP_URL}${ownerSlug}/${packageSlug}`, - rawYaml, - }; - - return new PlatformProfileLoader({ - configResult, - ownerSlug, - packageSlug, - iconUrl, - versionSlug, - controlPlaneClient, - ide, - llmLogger, - description, - orgScopeId, - }); - } - - async doLoadConfig(): Promise> { - if (this.configResult.errors?.find((e) => e.fatal)) { - return { - config: undefined, - errors: this.configResult.errors, - configLoadInterrupted: false, - }; - } - - const results = await doLoadConfig({ - ide: this.ide, - controlPlaneClient: this.controlPlaneClient, - llmLogger: this.llmLogger, - overrideConfigYaml: this.configResult.config, - profileId: this.description.id, - orgScopeId: this.orgScopeId, - packageIdentifier: { - uriType: "slug", - fullSlug: { - ownerSlug: this.ownerSlug, - packageSlug: this.packageSlug, - versionSlug: this.versionSlug, - }, - }, - }); - - return { - config: results.config, - errors: [...(this.configResult.errors ?? []), ...(results.errors ?? [])], - configLoadInterrupted: results.configLoadInterrupted, - }; - } - - setIsActive(isActive: boolean): void {} -} diff --git a/core/config/profile/doLoadConfig.ts b/core/config/profile/doLoadConfig.ts index d1b13720e2b..3cc321dc499 100644 --- a/core/config/profile/doLoadConfig.ts +++ b/core/config/profile/doLoadConfig.ts @@ -4,7 +4,6 @@ import { AssistantUnrolled, ConfigResult, ConfigValidationError, - ModelRole, PackageIdentifier, } from "@continuedev/config-yaml"; @@ -20,13 +19,7 @@ import { import { stringifyMcpPrompt } from "../../commands/slash/mcpSlashCommand"; import { convertRuleBlockToSlashCommand } from "../../commands/slash/ruleBlockSlashCommand"; import { MCPManagerSingleton } from "../../context/mcp/MCPManagerSingleton"; -import ContinueProxyContextProvider from "../../context/providers/ContinueProxyContextProvider"; import MCPContextProvider from "../../context/providers/MCPContextProvider"; -import { ControlPlaneProxyInfo } from "../../control-plane/analytics/IAnalyticsProvider.js"; -import { ControlPlaneClient } from "../../control-plane/client.js"; -import { getControlPlaneEnv } from "../../control-plane/env.js"; -import { PolicySingleton } from "../../control-plane/PolicySingleton"; -import ContinueProxy from "../../llm/llms/stubs/ContinueProxy"; import { initSlashCommand } from "../../promptFiles/initPrompt"; import { getConfigDependentToolDefinitions } from "../../tools"; import { encodeMCPToolUri } from "../../tools/callTool"; @@ -70,32 +63,26 @@ async function loadRules(ide: IDE) { export default async function doLoadConfig(options: { ide: IDE; - controlPlaneClient: ControlPlaneClient; llmLogger: ILLMLogger; overrideConfigJson?: SerializedContinueConfig; overrideConfigYaml?: AssistantUnrolled; profileId: string; overrideConfigYamlByPath?: string; - orgScopeId: string | null; packageIdentifier: PackageIdentifier; }): Promise> { const { ide, - controlPlaneClient, llmLogger, overrideConfigJson, overrideConfigYaml, profileId, overrideConfigYamlByPath, - orgScopeId, packageIdentifier, } = options; const ideInfo = await ide.getIdeInfo(); const uniqueId = await ide.getUniqueId(); const ideSettings = await ide.getIdeSettings(); - const workOsAccessToken = await controlPlaneClient.getAccessToken(); - const isSignedIn = await controlPlaneClient.isSignedIn(); // Migrations for old config files // Removes @@ -128,10 +115,7 @@ export default async function doLoadConfig(options: { uniqueId, llmLogger, overrideConfigYaml, - controlPlaneClient, - orgScopeId, packageIdentifier, - workOsAccessToken, }); newConfig = result.config; errors = result.errors; @@ -143,7 +127,6 @@ export default async function doLoadConfig(options: { ideInfo, uniqueId, llmLogger, - workOsAccessToken, overrideConfigJson, ); newConfig = result.config; @@ -181,14 +164,6 @@ export default async function doLoadConfig(options: { newConfig.slashCommands.push(initSlashCommand); - const proxyContextProvider = newConfig.contextProviders?.find( - (cp) => cp.description.title === "continue-proxy", - ); - if (proxyContextProvider) { - (proxyContextProvider as ContinueProxyContextProvider).workOsAccessToken = - workOsAccessToken; - } - // Show deprecation warnings for providers const globalContext = new GlobalContext(); newConfig.contextProviders.forEach((provider) => { @@ -311,7 +286,6 @@ export default async function doLoadConfig(options: { rules: newConfig.rules, enableExperimentalTools: newConfig.experimental?.enableExperimentalTools ?? false, - isSignedIn, isRemote: await ide.isWorkspaceRemote(), modelName: newConfig.selectedModelByRole.chat?.model, ide, @@ -368,76 +342,11 @@ export default async function doLoadConfig(options: { } } - // Org policies - const policy = PolicySingleton.getInstance().policy?.policy; - if (policy?.allowAnonymousTelemetry === false) { - newConfig.allowAnonymousTelemetry = false; - } - if (policy?.allowCodebaseIndexing === false) { - newConfig.disableIndexing = true; - } - // Setup IdeInfoService - IdeInfoService.setup( - uniqueId, - ideInfo, - ); + IdeInfoService.setup(uniqueId, ideInfo); // TODO: pass config to pre-load non-system TTS models await TTS.setup(); - // Set up control plane proxy if configured - const controlPlane = (newConfig as any).controlPlane; - const useOnPremProxy = - controlPlane?.useContinueForTeamsProxy === false && controlPlane?.proxyUrl; - - const env = await getControlPlaneEnv(Promise.resolve(ideSettings)); - let controlPlaneProxyUrl: string = useOnPremProxy - ? controlPlane?.proxyUrl - : env.DEFAULT_CONTROL_PLANE_PROXY_URL; - - if (!controlPlaneProxyUrl.endsWith("/")) { - controlPlaneProxyUrl += "/"; - } - const controlPlaneProxyInfo = { - profileId, - controlPlaneProxyUrl, - workOsAccessToken, - }; - - newConfig = await injectControlPlaneProxyInfo( - newConfig, - controlPlaneProxyInfo, - ); - return { config: newConfig, errors, configLoadInterrupted: false }; } - -// Pass ControlPlaneProxyInfo to objects that need it -async function injectControlPlaneProxyInfo( - config: ContinueConfig, - info: ControlPlaneProxyInfo, -): Promise { - Object.keys(config.modelsByRole).forEach((key) => { - config.modelsByRole[key as ModelRole].forEach((model) => { - if (model.providerName === "continue-proxy") { - (model as ContinueProxy).controlPlaneProxyInfo = info; - } - }); - }); - - Object.keys(config.selectedModelByRole).forEach((key) => { - const model = config.selectedModelByRole[key as ModelRole]; - if (model?.providerName === "continue-proxy") { - (model as ContinueProxy).controlPlaneProxyInfo = info; - } - }); - - config.modelsByRole.chat.forEach((model) => { - if (model.providerName === "continue-proxy") { - (model as ContinueProxy).controlPlaneProxyInfo = info; - } - }); - - return config; -} diff --git a/core/config/profile/doLoadConfig.vitest.ts b/core/config/profile/doLoadConfig.vitest.ts index fcd7333f486..5ff8512e6bd 100644 --- a/core/config/profile/doLoadConfig.vitest.ts +++ b/core/config/profile/doLoadConfig.vitest.ts @@ -72,17 +72,6 @@ vi.mock("../../util/GlobalContext", () => ({ update() {} }, })); -vi.mock("../../control-plane/env", () => ({ - getControlPlaneEnv: vi.fn().mockResolvedValue({ - DEFAULT_CONTROL_PLANE_PROXY_URL: "https://proxy.example.com/", - }), - getControlPlaneEnvSync: vi.fn().mockReturnValue({ - CONTROL_PLANE_URL: "https://api.example.com/", - }), -})); -vi.mock("../../control-plane/PolicySingleton", () => ({ - PolicySingleton: { getInstance: () => ({ policy: null }) }, -})); vi.mock("../../promptFiles/initPrompt", () => ({ initSlashCommand: { name: "init", description: "init" }, })); @@ -116,12 +105,6 @@ const mockIde = { isWorkspaceRemote: vi.fn().mockResolvedValue(true), } as any; -const mockControlPlaneClient = { - getAccessToken: vi.fn().mockResolvedValue("token"), - isSignedIn: vi.fn().mockResolvedValue(false), - sessionInfoPromise: Promise.resolve(undefined), -} as any; - const mockLlmLogger = {} as any; describe("doLoadConfig pre-read content bypass", () => { @@ -138,11 +121,11 @@ describe("doLoadConfig pre-read content bypass", () => { await doLoadConfig({ ide: mockIde, - controlPlaneClient: mockControlPlaneClient, + llmLogger: mockLlmLogger, profileId: "test-profile", overrideConfigYamlByPath: packageIdentifier.fileUri, - orgScopeId: null, + packageIdentifier, }); @@ -162,11 +145,11 @@ describe("doLoadConfig pre-read content bypass", () => { await doLoadConfig({ ide: mockIde, - controlPlaneClient: mockControlPlaneClient, + llmLogger: mockLlmLogger, profileId: "test-profile", overrideConfigYamlByPath: packageIdentifier.fileUri, - orgScopeId: null, + packageIdentifier, }); diff --git a/core/config/types.ts b/core/config/types.ts index d9f58aca6e2..3808748b0c9 100644 --- a/core/config/types.ts +++ b/core/config/types.ts @@ -809,7 +809,6 @@ declare global { | "currentFile" | "greptile" | "outline" - | "continue-proxy" | "highlights" | "file" | "issue" diff --git a/core/config/usesFreeTrialApiKey.vitest.ts b/core/config/usesFreeTrialApiKey.vitest.ts index 79dfca35a2c..91d415c2284 100644 --- a/core/config/usesFreeTrialApiKey.vitest.ts +++ b/core/config/usesFreeTrialApiKey.vitest.ts @@ -74,7 +74,7 @@ describe("usesFreeTrialApiKey", () => { slashCommands: [], tools: [], mcpServerStatuses: [], - usePlatform: false, + rules: [], }; @@ -124,7 +124,7 @@ describe("usesFreeTrialApiKey", () => { slashCommands: [], tools: [], mcpServerStatuses: [], - usePlatform: false, + rules: [], }; @@ -186,7 +186,7 @@ describe("usesFreeTrialApiKey", () => { slashCommands: [], tools: [], mcpServerStatuses: [], - usePlatform: false, + rules: [], }; @@ -249,7 +249,7 @@ describe("usesFreeTrialApiKey", () => { slashCommands: [], tools: [], mcpServerStatuses: [], - usePlatform: false, + rules: [], }; @@ -305,7 +305,7 @@ describe("usesFreeTrialApiKey", () => { slashCommands: [], tools: [], mcpServerStatuses: [], - usePlatform: false, + rules: [], }; @@ -349,7 +349,7 @@ describe("usesFreeTrialApiKey", () => { slashCommands: [], tools: [], mcpServerStatuses: [], - usePlatform: false, + rules: [], }; diff --git a/core/config/yaml/LocalPlatformClient.ts b/core/config/yaml/LocalPlatformClient.ts index c93b69530f8..c03ce387bb1 100644 --- a/core/config/yaml/LocalPlatformClient.ts +++ b/core/config/yaml/LocalPlatformClient.ts @@ -6,16 +6,11 @@ import { } from "@continuedev/config-yaml"; import * as dotenv from "dotenv"; import { IDE } from "../.."; -import { ControlPlaneClient } from "../../control-plane/client"; import { getContinueDotEnv } from "../../util/paths"; import { joinPathsToUri } from "../../util/uri"; export class LocalPlatformClient implements PlatformClient { - constructor( - private orgScopeId: string | null, - private readonly client: ControlPlaneClient, - private readonly ide: IDE, - ) {} + constructor(private readonly ide: IDE) {} /** * searches for the first valid secret file in order of ~/.continue/.env, /.continue/.env, /.env @@ -97,41 +92,28 @@ export class LocalPlatformClient implements PlatformClient { return []; } - let results: (SecretResult | undefined)[] = []; - try { - results = await this.client.resolveFQSNs(fqsns, this.orgScopeId); - } catch (e) { - console.error("Error getting secrets from control plane", e); - } - - // For any secret that isn't found, look in .env files, then process.env - for (let i = 0; i < results.length; i++) { - if (!results[i]?.found) { - let secretResult = await this.findSecretInEnvFiles(fqsns[i]); + const results: (SecretResult | undefined)[] = []; - // If not found in .env files, try process.env - if (!secretResult?.found) { - const secretValueFromProcessEnv = process.env[fqsns[i].secretName]; - if (secretValueFromProcessEnv !== undefined) { - secretResult = { - found: true, - fqsn: fqsns[i], - value: secretValueFromProcessEnv, - secretLocation: { - secretName: fqsns[i].secretName, - // Cast to SecretType.ProcessEnv is necessary because the specific type - // ProcessEnvSecretLocation expects secretType to be exactly SecretType.ProcessEnv, - // not the general enum SecretType. - secretType: SecretType.ProcessEnv as SecretType.ProcessEnv, - }, - }; - } - } + for (let i = 0; i < fqsns.length; i++) { + let secretResult = await this.findSecretInEnvFiles(fqsns[i]); - if (secretResult?.found) { - results[i] = secretResult; + // If not found in .env files, try process.env + if (!secretResult?.found) { + const secretValueFromProcessEnv = process.env[fqsns[i].secretName]; + if (secretValueFromProcessEnv !== undefined) { + secretResult = { + found: true, + fqsn: fqsns[i], + value: secretValueFromProcessEnv, + secretLocation: { + secretName: fqsns[i].secretName, + secretType: SecretType.ProcessEnv as SecretType.ProcessEnv, + }, + }; } } + + results[i] = secretResult; } return results; diff --git a/core/config/yaml/LocalPlatformClient.vitest.ts b/core/config/yaml/LocalPlatformClient.vitest.ts index c4d9515fd89..c058e0f570a 100644 --- a/core/config/yaml/LocalPlatformClient.vitest.ts +++ b/core/config/yaml/LocalPlatformClient.vitest.ts @@ -9,7 +9,6 @@ import { vi, } from "vitest"; import { IDE } from "../.."; -import { ControlPlaneClient } from "../../control-plane/client"; import { LocalPlatformClient } from "./LocalPlatformClient"; vi.mock("../../util/paths", { spy: true }); @@ -34,23 +33,11 @@ describe("LocalPlatformClient", () => { secretName: "TEST_WORKSPACE_SECRET_KEY", }; - const testResolvedFQSN: SecretResult = { - found: true, - fqsn: testFQSN, - secretLocation: { - secretName: testFQSN.secretName, - secretType: SecretType.Organization, - orgSlug: "test-org-slug", - }, - }; - - let testControlPlaneClient: ControlPlaneClient; let testIde: IDE; beforeEach( /**dynamic import before each test for test isolation */ async () => { const testFixtures = await import("../../test/fixtures"); - testControlPlaneClient = testFixtures.testControlPlaneClient; testIde = testFixtures.testIde; }, ); @@ -79,28 +66,11 @@ describe("LocalPlatformClient", () => { }); test("should not be able to resolve FQSNs if they do not exist", async () => { - const localPlatformClient = new LocalPlatformClient( - null, - testControlPlaneClient, - testIde, - ); + const localPlatformClient = new LocalPlatformClient(testIde); const resolvedFQSNs = await localPlatformClient.resolveFQSNs([testFQSN]); expect(resolvedFQSNs.length).toBeGreaterThan(0); - expect(resolvedFQSNs[0]?.found).toBe(false); - }); - - test("should be able to resolve FQSNs if they exist", async () => { - testControlPlaneClient.resolveFQSNs = vi.fn(async () => [testResolvedFQSN]); - const localPlatformClient = new LocalPlatformClient( - null, - testControlPlaneClient, - testIde, - ); - const resolvedFQSNs = await localPlatformClient.resolveFQSNs([testFQSN]); - expect(testControlPlaneClient.resolveFQSNs).toHaveBeenCalled(); - expect(resolvedFQSNs).toEqual([testResolvedFQSN]); - expect(resolvedFQSNs[0]?.found).toBe(true); + expect(resolvedFQSNs[0]?.found).toBeUndefined(); }); describe("searches for secrets in local .env files", () => { @@ -112,11 +82,7 @@ describe("LocalPlatformClient", () => { }); test("should be able to get secrets from ~/.continue/.env files", async () => { - const localPlatformClient = new LocalPlatformClient( - null, - testControlPlaneClient, - testIde, - ); + const localPlatformClient = new LocalPlatformClient(testIde); const resolvedFQSNs = await localPlatformClient.resolveFQSNs([testFQSN]); expect(getContinueDotEnv).toHaveBeenCalled(); expect(resolvedFQSNs.length).toBe(1); @@ -155,11 +121,7 @@ describe("LocalPlatformClient", () => { return originalIdeReadFile(fileUri); }); - const localPlatformClient = new LocalPlatformClient( - null, - testControlPlaneClient, - testIde, - ); + const localPlatformClient = new LocalPlatformClient(testIde); const resolvedFQSNs = await localPlatformClient.resolveFQSNs([ testFQSN, testFQSN2, @@ -209,11 +171,7 @@ describe("LocalPlatformClient", () => { return originalIdeReadFile(fileUri); }); - const localPlatformClient = new LocalPlatformClient( - null, - testControlPlaneClient, - testIde, - ); + const localPlatformClient = new LocalPlatformClient(testIde); const resolvedFQSNs = await localPlatformClient.resolveFQSNs([testFQSN]); expect(resolvedFQSNs.length).toBe(1); @@ -234,20 +192,6 @@ describe("LocalPlatformClient", () => { const ogProcessEnv = { ...process.env }; beforeEach(async () => { - // Ensure secrets are not found in ControlPlane initially for these tests - const mockResolveFQSNsNotFound = async ( - fqsns: FQSN[], - ): Promise<(SecretResult | undefined)[]> => - fqsns.map((fqsn) => ({ - found: false, - fqsn, - secretLocation: { - secretName: fqsn.secretName, - secretType: SecretType.NotFound as SecretType.NotFound, - }, - })); - testControlPlaneClient.resolveFQSNs = vi.fn(mockResolveFQSNsNotFound); - // Ensure secrets are not found in local .env files const utilPaths = await import("../../util/paths"); utilPaths.getContinueDotEnv = vi.fn(() => ({})); @@ -270,11 +214,7 @@ describe("LocalPlatformClient", () => { const processEnvSecretValue = "secret-from-process-env"; process.env[testFQSN.secretName] = processEnvSecretValue; - const localPlatformClient = new LocalPlatformClient( - null, - testControlPlaneClient, - testIde, - ); + const localPlatformClient = new LocalPlatformClient(testIde); const resolvedFQSNs = await localPlatformClient.resolveFQSNs([testFQSN]); expect(resolvedFQSNs.length).toBe(1); @@ -297,58 +237,11 @@ describe("LocalPlatformClient", () => { // Ensure it's not in process.env expect(process.env[testFQSN.secretName]).toBeUndefined(); - const localPlatformClient = new LocalPlatformClient( - null, - testControlPlaneClient, - testIde, - ); - const resolvedFQSNs = await localPlatformClient.resolveFQSNs([testFQSN]); - - expect(resolvedFQSNs.length).toBe(1); - expect(resolvedFQSNs[0]?.found).toBe(false); - expect(resolvedFQSNs[0]?.secretLocation?.secretType).toBe( - SecretType.NotFound, - ); - }); - - test("should prioritize ControlPlane over process.env", async () => { - const controlPlaneValue = "secret-from-control-plane"; - - const mockResolveFQSNsControlPlaneFound = async (): Promise< - (SecretResult | undefined)[] - > => [ - { - found: true, - fqsn: testFQSN, - value: controlPlaneValue, - secretLocation: { - secretType: SecretType.Organization as SecretType.Organization, - orgSlug: (testResolvedFQSN.secretLocation as any).orgSlug, - secretName: testFQSN.secretName, - }, - }, - ]; - testControlPlaneClient.resolveFQSNs = vi.fn( - mockResolveFQSNsControlPlaneFound, - ); - - process.env[testFQSN.secretName] = - "secret-from-process-env-should-be-ignored"; - - const localPlatformClient = new LocalPlatformClient( - null, - testControlPlaneClient, - testIde, - ); + const localPlatformClient = new LocalPlatformClient(testIde); const resolvedFQSNs = await localPlatformClient.resolveFQSNs([testFQSN]); expect(resolvedFQSNs.length).toBe(1); - const result = resolvedFQSNs[0]; - expect(result?.found).toBe(true); - expect((result as SecretResult & { value: unknown })?.value).toBe( - controlPlaneValue, - ); - expect(result?.secretLocation?.secretType).toBe(SecretType.Organization); + expect(resolvedFQSNs[0]).toBeUndefined(); }); test("should prioritize local ~/.continue/.env file over process.env", async () => { @@ -361,11 +254,7 @@ describe("LocalPlatformClient", () => { process.env[testFQSN.secretName] = "secret-from-process-env-should-be-ignored"; - const localPlatformClient = new LocalPlatformClient( - null, - testControlPlaneClient, - testIde, - ); + const localPlatformClient = new LocalPlatformClient(testIde); const resolvedFQSNs = await localPlatformClient.resolveFQSNs([testFQSN]); expect(resolvedFQSNs.length).toBe(1); @@ -393,11 +282,7 @@ describe("LocalPlatformClient", () => { process.env[testFQSN.secretName] = "secret-from-process-env-should-be-ignored"; - const localPlatformClient = new LocalPlatformClient( - null, - testControlPlaneClient, - testIde, - ); + const localPlatformClient = new LocalPlatformClient(testIde); const resolvedFQSNs = await localPlatformClient.resolveFQSNs([testFQSN]); expect(resolvedFQSNs.length).toBe(1); diff --git a/core/config/yaml/default.ts b/core/config/yaml/default.ts index 22d7d06854e..97bc6f11dd0 100644 --- a/core/config/yaml/default.ts +++ b/core/config/yaml/default.ts @@ -4,7 +4,7 @@ import { AssistantUnrolled } from "@continuedev/config-yaml"; export const defaultConfigYaml: AssistantUnrolled = { models: [], context: [], - name: "Local Config", + name: "Main Config", version: "1.0.0", schema: "v1", }; @@ -12,7 +12,7 @@ export const defaultConfigYaml: AssistantUnrolled = { export const defaultConfigYamlJetBrains: AssistantUnrolled = { models: [], context: [], - name: "Local Config", + name: "Main Config", version: "1.0.0", schema: "v1", }; diff --git a/core/config/yaml/loadLocalYamlBlocks.ts b/core/config/yaml/loadLocalYamlBlocks.ts index aa769eb0c31..baa3a8b4b0d 100644 --- a/core/config/yaml/loadLocalYamlBlocks.ts +++ b/core/config/yaml/loadLocalYamlBlocks.ts @@ -6,7 +6,6 @@ import { unrollAssistantFromContent, } from "@continuedev/config-yaml"; import { IDE } from "../.."; -import { ControlPlaneClient } from "../../control-plane/client"; import { LocalPlatformClient } from "./LocalPlatformClient"; // This is a certain approach to unrolling local YAML where it @@ -16,8 +15,6 @@ export async function unrollLocalYamlBlocks( packageIdentifiers: PackageIdentifier[], ide: IDE, registryClient: RegistryClient, - orgScopeId: string | null, - controlPlaneClient: ControlPlaneClient, ): Promise> { try { const unrollResult = await unrollAssistantFromContent( @@ -29,13 +26,7 @@ export async function unrollLocalYamlBlocks( registryClient, { currentUserSlug: "", - onPremProxyUrl: null, - orgScopeId, - platformClient: new LocalPlatformClient( - orgScopeId, - controlPlaneClient, - ide, - ), + platformClient: new LocalPlatformClient(ide), renderSecrets: true, injectBlocks: packageIdentifiers, }, diff --git a/core/config/yaml/loadYaml.ts b/core/config/yaml/loadYaml.ts index 40278fdbe47..a7bccf4f52f 100644 --- a/core/config/yaml/loadYaml.ts +++ b/core/config/yaml/loadYaml.ts @@ -23,7 +23,6 @@ import { InternalMcpOptions, } from "../.."; import { MCPManagerSingleton } from "../../context/mcp/MCPManagerSingleton"; -import { ControlPlaneClient } from "../../control-plane/client"; import TransformersJsEmbeddingsProvider from "../../llm/llms/TransformersJsEmbeddingsProvider"; import { getAllPromptFiles } from "../../promptFiles/getPromptFiles"; import { GlobalContext } from "../../util/GlobalContext"; @@ -32,8 +31,6 @@ import { modifyAnyConfigWithSharedConfig } from "../sharedConfig"; import { convertPromptBlockToSlashCommand } from "../../commands/slash/promptBlockSlashCommand"; import { slashCommandFromPromptFile } from "../../commands/slash/promptFileSlashCommand"; import { loadJsonMcpConfigs } from "../../context/mcp/json/loadJsonMcpConfigs"; -import { getControlPlaneEnvSync } from "../../control-plane/env"; -import { PolicySingleton } from "../../control-plane/PolicySingleton"; import { getBaseToolDefinitions } from "../../tools"; import { getCleanUriPath } from "../../util/uri"; import { loadConfigContextProviders } from "../loadContextProviders"; @@ -48,20 +45,11 @@ import { async function loadConfigYaml(options: { overrideConfigYaml: AssistantUnrolled | undefined; - controlPlaneClient: ControlPlaneClient; - orgScopeId: string | null; ideSettings: IdeSettings; ide: IDE; packageIdentifier: PackageIdentifier; }): Promise> { - const { - overrideConfigYaml, - controlPlaneClient, - orgScopeId, - ideSettings, - ide, - packageIdentifier, - } = options; + const { overrideConfigYaml, ideSettings, ide, packageIdentifier } = options; // Add local .continue blocks // Use "content" field to pass pre-read content directly, avoiding @@ -82,10 +70,6 @@ async function loadConfigYaml(options: { await Promise.all(localBlockPromises) ).flat(); - // logger.info( - // `Loading config.yaml from ${JSON.stringify(packageIdentifier)} with root path ${rootPath}`, - // ); - // Registry client is only used if local blocks are present, but logic same for hub/local assistants const getRegistryClient = async () => { const rootPath = @@ -93,9 +77,6 @@ async function loadConfigYaml(options: { ? dirname(getCleanUriPath(packageIdentifier.fileUri)) : undefined; return new RegistryClient({ - accessToken: await controlPlaneClient.getAccessToken(), - apiBase: getControlPlaneEnvSync(ideSettings.continueTestEnvironment) - .CONTROL_PLANE_URL, rootPath, }); }; @@ -111,8 +92,6 @@ async function loadConfigYaml(options: { localPackageIdentifiers, ide, await getRegistryClient(), - orgScopeId, - controlPlaneClient, ); if (unrolledLocal.errors) { errors.push(...unrolledLocal.errors); @@ -129,13 +108,7 @@ async function loadConfigYaml(options: { { renderSecrets: true, currentUserSlug: "", - onPremProxyUrl: null, - orgScopeId, - platformClient: new LocalPlatformClient( - orgScopeId, - controlPlaneClient, - ide, - ), + platformClient: new LocalPlatformClient(ide), injectBlocks: localPackageIdentifiers, }, ); @@ -186,7 +159,6 @@ export async function configYamlToContinueConfig(options: { ideInfo: IdeInfo; uniqueId: string; llmLogger: ILLMLogger; - workOsAccessToken: string | undefined; }): Promise<{ config: ContinueConfig; errors: ConfigValidationError[] }> { let { unrolledAssistant, ide, ideInfo, uniqueId, llmLogger } = options; @@ -406,23 +378,18 @@ export async function configYamlToContinueConfig(options: { // Trigger MCP server refreshes (Config is reloaded again once connected!) const mcpManager = MCPManagerSingleton.getInstance(); - const orgPolicy = PolicySingleton.getInstance().policy; - if (orgPolicy?.policy?.allowMcpServers === false) { - await mcpManager.shutdown(); - } else { - const mcpOptions: InternalMcpOptions[] = (config.mcpServers ?? []).map( - (server) => - convertYamlMcpConfigToInternalMcpOptions(server, config.requestOptions), - ); - const { errors: jsonMcpErrors, mcpServers } = await loadJsonMcpConfigs( - ide, - true, - config.requestOptions, - ); - localErrors.push(...jsonMcpErrors); - mcpOptions.push(...mcpServers); - mcpManager.setConnections(mcpOptions, false, { ide }); - } + const mcpOptions: InternalMcpOptions[] = (config.mcpServers ?? []).map( + (server) => + convertYamlMcpConfigToInternalMcpOptions(server, config.requestOptions), + ); + const { errors: jsonMcpErrors, mcpServers } = await loadJsonMcpConfigs( + ide, + true, + config.requestOptions, + ); + localErrors.push(...jsonMcpErrors); + mcpOptions.push(...mcpServers); + mcpManager.setConnections(mcpOptions, false, { ide }); return { config: continueConfig, errors: localErrors }; } @@ -433,10 +400,7 @@ export async function loadContinueConfigFromYaml(options: { ideInfo: IdeInfo; uniqueId: string; llmLogger: ILLMLogger; - workOsAccessToken: string | undefined; overrideConfigYaml: AssistantUnrolled | undefined; - controlPlaneClient: ControlPlaneClient; - orgScopeId: string | null; packageIdentifier: PackageIdentifier; }): Promise> { const { @@ -445,17 +409,12 @@ export async function loadContinueConfigFromYaml(options: { ideInfo, uniqueId, llmLogger, - workOsAccessToken, overrideConfigYaml, - controlPlaneClient, - orgScopeId, packageIdentifier, } = options; const configYamlResult = await loadConfigYaml({ overrideConfigYaml, - controlPlaneClient, - orgScopeId, ideSettings, ide, packageIdentifier, @@ -476,7 +435,6 @@ export async function loadContinueConfigFromYaml(options: { ideInfo, uniqueId, llmLogger, - workOsAccessToken, }); // Apply shared config diff --git a/core/context/providers/ContinueProxyContextProvider.ts b/core/context/providers/ContinueProxyContextProvider.ts deleted file mode 100644 index 9e348bb2cc9..00000000000 --- a/core/context/providers/ContinueProxyContextProvider.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { getControlPlaneEnv } from "../../control-plane/env.js"; -import { - ContextItem, - ContextProviderDescription, - ContextProviderExtras, - ContextSubmenuItem, - LoadSubmenuItemsArgs, -} from "../../index.js"; -import { BaseContextProvider } from "../index.js"; - -class ContinueProxyContextProvider extends BaseContextProvider { - static description: ContextProviderDescription = { - title: "continue-proxy", - displayTitle: "Continue Proxy", - description: "Retrieve a context item from a Continue for Teams add-on", - type: "submenu", - }; - - workOsAccessToken: string | undefined = undefined; - - override get description(): ContextProviderDescription { - return { - title: - this.options.title || ContinueProxyContextProvider.description.title, - displayTitle: - this.options.displayTitle || - this.options.name || - ContinueProxyContextProvider.description.displayTitle, - description: - this.options.description || - ContinueProxyContextProvider.description.description, - type: this.options.type || ContinueProxyContextProvider.description.type, - }; - } - - async loadSubmenuItems( - args: LoadSubmenuItemsArgs, - ): Promise { - const env = await getControlPlaneEnv(args.ide.getIdeSettings()); - const response = await args.fetch( - new URL(`/proxy/context/${this.options.id}/list`, env.CONTROL_PLANE_URL), - { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${this.workOsAccessToken}`, - }, - }, - ); - const data = await response.json(); - return data.items; - } - - async getContextItems( - query: string, - extras: ContextProviderExtras, - ): Promise { - const env = await getControlPlaneEnv(extras.ide.getIdeSettings()); - const response = await extras.fetch( - new URL( - `/proxy/context/${this.options.id}/retrieve`, - env.CONTROL_PLANE_URL, - ), - { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${this.workOsAccessToken}`, - }, - body: JSON.stringify({ - query: query || "", - fullInput: extras.fullInput, - }), - }, - ); - - const items: any = await response.json(); - return items; - } -} - -export default ContinueProxyContextProvider; diff --git a/core/context/providers/RulesContextProvider.ts b/core/context/providers/RulesContextProvider.ts index dd543b88da0..36d4eb8314d 100644 --- a/core/context/providers/RulesContextProvider.ts +++ b/core/context/providers/RulesContextProvider.ts @@ -8,8 +8,6 @@ import { LoadSubmenuItemsArgs, RuleWithSource, } from "../.."; -import { getControlPlaneEnv } from "../../control-plane/env"; - class RulesContextProvider extends BaseContextProvider { static description: ContextProviderDescription = { title: "rules", @@ -33,10 +31,7 @@ class RulesContextProvider extends BaseContextProvider { return rule.description ?? rule.name ?? ""; } - private getUriFromRule( - rule: RuleWithSource, - appUrl: string, - ): ContextItemUri | undefined { + private getUriFromRule(rule: RuleWithSource): ContextItemUri | undefined { if (rule.sourceFile) { return { type: "file", @@ -44,14 +39,6 @@ class RulesContextProvider extends BaseContextProvider { }; } - if (rule.slug) { - let url = `${appUrl}${rule.slug}`; - return { - type: "url", - value: url, - }; - } - return undefined; } @@ -66,13 +53,12 @@ class RulesContextProvider extends BaseContextProvider { return []; } - const env = await getControlPlaneEnv(extras.ide.getIdeSettings()); return [ { name: this.getNameFromRule(rule), content: rule.rule, description: this.getDescriptionFromRule(rule), - uri: this.getUriFromRule(rule, env.APP_URL), + uri: this.getUriFromRule(rule), }, ]; } diff --git a/core/context/providers/WebContextProvider.ts b/core/context/providers/WebContextProvider.ts index 9ea4bac410b..dd329bc8c21 100644 --- a/core/context/providers/WebContextProvider.ts +++ b/core/context/providers/WebContextProvider.ts @@ -6,7 +6,7 @@ import { FetchFunction, } from "../.."; import { getHeaders } from "../../continueServer/stubs/headers"; -import { TRIAL_PROXY_URL } from "../../control-plane/client"; +const TRIAL_PROXY_URL = "https://proxy-server-blue-l6vsfbzhba-uw.a.run.app"; export const fetchSearchResults = async ( query: string, diff --git a/core/context/providers/_context-providers.vitest.ts b/core/context/providers/_context-providers.vitest.ts index 40020e70058..521e97d774c 100644 --- a/core/context/providers/_context-providers.vitest.ts +++ b/core/context/providers/_context-providers.vitest.ts @@ -29,11 +29,7 @@ async function getContextProviderExtras( setUpTestDir(); const ide = new FileSystemIde(TEST_DIR); const llmLogger = new LLMLogger(); - const configHandler = new ConfigHandler( - ide, - llmLogger, - Promise.resolve(undefined), - ); + const configHandler = new ConfigHandler(ide, llmLogger); await configHandler.isInitialized; const { config } = await configHandler.loadConfig(); if (!config) { diff --git a/core/context/providers/index.ts b/core/context/providers/index.ts index 1c0e980eda1..d7875f6ae2f 100644 --- a/core/context/providers/index.ts +++ b/core/context/providers/index.ts @@ -4,7 +4,7 @@ import { ContextProviderName } from "../../"; import ClipboardContextProvider from "./ClipboardContextProvider"; import CodebaseContextProvider from "./CodebaseContextProvider"; import CodeContextProvider from "./CodeContextProvider"; -import ContinueProxyContextProvider from "./ContinueProxyContextProvider"; + import CurrentFileContextProvider from "./CurrentFileContextProvider"; import DatabaseContextProvider from "./DatabaseContextProvider"; import DebugLocalsProvider from "./DebugLocalsProvider"; @@ -63,7 +63,6 @@ export const Providers: (typeof BaseContextProvider)[] = [ CodeContextProvider, CurrentFileContextProvider, URLContextProvider, - ContinueProxyContextProvider, RepoMapContextProvider, DiscordContextProvider, GreptileContextProvider, diff --git a/core/control-plane/AuthTypes.ts b/core/control-plane/AuthTypes.ts deleted file mode 100644 index 3b79d91ae29..00000000000 --- a/core/control-plane/AuthTypes.ts +++ /dev/null @@ -1,51 +0,0 @@ -export interface HubSessionInfo { - AUTH_TYPE: AuthType.WorkOsProd | AuthType.WorkOsStaging; - accessToken: string; - account: { - label: string; - id: string; - }; -} - -export interface OnPremSessionInfo { - AUTH_TYPE: AuthType.OnPrem; -} - -export type ControlPlaneSessionInfo = HubSessionInfo | OnPremSessionInfo; - -export function isOnPremSession( - sessionInfo: ControlPlaneSessionInfo | undefined, -): sessionInfo is OnPremSessionInfo { - return sessionInfo !== undefined && sessionInfo.AUTH_TYPE === AuthType.OnPrem; -} - -export enum AuthType { - WorkOsProd = "continue", - WorkOsStaging = "continue-staging", - OnPrem = "on-prem", -} - -export interface HubEnv { - DEFAULT_CONTROL_PLANE_PROXY_URL: string; - CONTROL_PLANE_URL: string; - AUTH_TYPE: AuthType.WorkOsProd | AuthType.WorkOsStaging; - WORKOS_CLIENT_ID: string; - APP_URL: string; -} - -export interface OnPremEnv { - AUTH_TYPE: AuthType.OnPrem; - DEFAULT_CONTROL_PLANE_PROXY_URL: string; - CONTROL_PLANE_URL: string; - APP_URL: string; -} - -export type ControlPlaneEnv = HubEnv | OnPremEnv; - -export function isHubEnv(env: ControlPlaneEnv): env is HubEnv { - return ( - "AUTH_TYPE" in env && - env.AUTH_TYPE !== "on-prem" && - "WORKOS_CLIENT_ID" in env - ); -} diff --git a/core/control-plane/PolicySingleton.ts b/core/control-plane/PolicySingleton.ts deleted file mode 100644 index 705e67b494e..00000000000 --- a/core/control-plane/PolicySingleton.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { PolicyResponse } from "./client.js"; - -export class PolicySingleton { - private static instance: PolicySingleton; - public policy: PolicyResponse | null = null; - - private constructor() {} - - public static getInstance(): PolicySingleton { - if (!PolicySingleton.instance) { - PolicySingleton.instance = new PolicySingleton(); - } - return PolicySingleton.instance; - } -} diff --git a/core/control-plane/analytics/IAnalyticsProvider.ts b/core/control-plane/analytics/IAnalyticsProvider.ts deleted file mode 100644 index 6631a7c5024..00000000000 --- a/core/control-plane/analytics/IAnalyticsProvider.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { AnalyticsConfig } from "../.."; - -export interface AnalyticsMetadata { - extensionVersion: string; -} - -export interface ControlPlaneProxyInfo { - workspaceId?: string; - controlPlaneProxyUrl: string; - workOsAccessToken?: string; -} - -export interface IAnalyticsProvider { - capture(event: string, properties: { [key: string]: any }): Promise; - setup( - config: AnalyticsConfig, - uniqueId: string, - controlPlaneProxyInfo?: ControlPlaneProxyInfo, - ): Promise; - shutdown(): Promise; -} diff --git a/core/control-plane/auth/index.ts b/core/control-plane/auth/index.ts deleted file mode 100644 index 5e8748ab9d8..00000000000 --- a/core/control-plane/auth/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { v4 as uuidv4 } from "uuid"; -import { IdeSettings } from "../.."; -import { isHubEnv } from "../AuthTypes"; -import { getControlPlaneEnv } from "../env"; - -export async function getAuthUrlForTokenPage( - ideSettingsPromise: Promise, - useOnboarding: boolean, -): Promise { - const env = await getControlPlaneEnv(ideSettingsPromise); - - if (!isHubEnv(env)) { - throw new Error("Sign in disabled"); - } - - const url = new URL("https://api.workos.com/user_management/authorize"); - const params = { - response_type: "code", - client_id: env.WORKOS_CLIENT_ID, - redirect_uri: `${env.APP_URL}tokens/${useOnboarding ? "onboarding-" : ""}callback`, - // redirect_uri: "http://localhost:3000/tokens/callback", - state: uuidv4(), - provider: "authkit", - }; - Object.keys(params).forEach((key) => - url.searchParams.append(key, params[key as keyof typeof params]), - ); - return url.toString(); -} diff --git a/core/control-plane/client.ts b/core/control-plane/client.ts deleted file mode 100644 index bb2fc687cf1..00000000000 --- a/core/control-plane/client.ts +++ /dev/null @@ -1,662 +0,0 @@ -import { ConfigJson } from "@continuedev/config-types"; -import { - AssistantUnrolled, - ConfigResult, - FQSN, - FullSlug, - Policy, - SecretResult, - SecretType, -} from "@continuedev/config-yaml"; -import fetch, { RequestInit, Response } from "node-fetch"; - -import { OrganizationDescription } from "../config/ProfileLifecycleManager.js"; -import { - BaseSessionMetadata, - IDE, - ModelDescription, - Session, -} from "../index.js"; -import { Logger } from "../util/Logger.js"; - -import { - ControlPlaneSessionInfo, - HubSessionInfo, - isOnPremSession, -} from "./AuthTypes.js"; -import { getControlPlaneEnv } from "./env.js"; - -export interface PolicyResponse { - orgSlug?: string; - policy?: Policy; -} - -export interface ControlPlaneWorkspace { - id: string; - name: string; - settings: ConfigJson; -} - -export interface ControlPlaneModelDescription extends ModelDescription {} - -export interface CreditStatus { - optedInToFreeTrial: boolean; - hasCredits: boolean; - creditBalance: number; - hasPurchasedCredits: boolean; -} - -export const TRIAL_PROXY_URL = - "https://proxy-server-blue-l6vsfbzhba-uw.a.run.app"; - -export interface RemoteSessionMetadata extends BaseSessionMetadata { - isRemote: true; - remoteId: string; -} - -export interface AgentSessionMetadata { - createdBy: string; - github_repo: string; - organizationId?: string; - idempotencyKey?: string; - source?: string; - continueApiKeyId?: string; - s3Url?: string; - prompt?: string | null; - createdBySlug?: string; -} - -export interface AgentSessionView { - id: string; - devboxId: string | null; - name: string | null; - icon: string | null; - status: string; - agentStatus: string | null; - unread: boolean; - state: string; - metadata: AgentSessionMetadata; - repoUrl: string; - branch: string | null; - pullRequestUrl: string | null; - pullRequestStatus: string | null; - tunnelUrl: string | null; - createdAt: string; - updatedAt: string; - create_time_ms: string; - end_time_ms: string; -} - -export class ControlPlaneClient { - constructor( - readonly sessionInfoPromise: Promise, - private readonly ide: IDE, - ) {} - - async resolveFQSNs( - fqsns: FQSN[], - orgScopeId: string | null, - ): Promise<(SecretResult | undefined)[]> { - if (!(await this.isSignedIn())) { - return fqsns.map((fqsn) => ({ - found: false, - fqsn, - secretLocation: { - secretName: fqsn.secretName, - secretType: SecretType.NotFound, - }, - })); - } - - const resp = await this.requestAndHandleError("ide/sync-secrets", { - method: "POST", - body: JSON.stringify({ fqsns, orgScopeId }), - }); - return (await resp.json()) as any; - } - - async isSignedIn(): Promise { - const sessionInfo = await this.sessionInfoPromise; - return !!sessionInfo; - } - - async getAccessToken(): Promise { - const sessionInfo = await this.sessionInfoPromise; - return isOnPremSession(sessionInfo) ? undefined : sessionInfo?.accessToken; - } - - private async request(path: string, init: RequestInit): Promise { - const sessionInfo = await this.sessionInfoPromise; - const onPremSession = isOnPremSession(sessionInfo); - const accessToken = await this.getAccessToken(); - - // Bearer token not necessary for on-prem auth type - if (!accessToken && !onPremSession) { - throw new Error("No access token"); - } - - const env = await getControlPlaneEnv(this.ide.getIdeSettings()); - const url = new URL(path, env.CONTROL_PLANE_URL).toString(); - const ideInfo = await this.ide.getIdeInfo(); - - const resp = await fetch(url, { - ...init, - headers: { - ...init.headers, - Authorization: `Bearer ${accessToken}`, - ...{ - "x-extension-version": ideInfo.extensionVersion, - "x-is-prerelease": String(ideInfo.isPrerelease), - }, - }, - }); - - return resp; - } - - private async requestAndHandleError( - path: string, - init: RequestInit, - ): Promise { - const resp = await this.request(path, init); - - if (!resp.ok) { - throw new Error( - `Control plane request failed: ${resp.status} ${await resp.text()}`, - ); - } - - return resp; - } - - public async listAssistants(organizationId: string | null): Promise< - { - configResult: ConfigResult; - ownerSlug: string; - packageSlug: string; - iconUrl: string; - rawYaml: string; - }[] - > { - if (!(await this.isSignedIn())) { - return []; - } - - try { - const url = organizationId - ? `ide/list-assistants?organizationId=${organizationId}` - : "ide/list-assistants"; - - const resp = await this.requestAndHandleError(url, { - method: "GET", - }); - return (await resp.json()) as any; - } catch (e) { - Logger.error(e, { - context: "control_plane_list_assistants", - organizationId, - }); - return []; - } - } - - public async listOrganizations(): Promise> { - if (!(await this.isSignedIn())) { - return []; - } - - // We try again here because when users sign up with an email domain that is - // captured by an org, we need to wait for the user account creation webhook to - // take effect. Otherwise the organization(s) won't show up. - // This error manifests as a 404 (user not found) - let retries = 0; - const maxRetries = 10; - const maxWaitTime = 20000; // 20 seconds in milliseconds - - while (retries < maxRetries) { - const resp = await this.request("ide/list-organizations", { - method: "GET", - }); - - if (resp.status === 404) { - retries++; - if (retries >= maxRetries) { - console.warn( - `Failed to list organizations after ${maxRetries} retries: user not found`, - ); - return []; - } - const waitTime = Math.min( - Math.pow(2, retries) * 100, - maxWaitTime / maxRetries, - ); - await new Promise((resolve) => setTimeout(resolve, waitTime)); - continue; - } else if (!resp.ok) { - console.warn( - `Failed to list organizations (${resp.status}): ${await resp.text()}`, - ); - return []; - } - const { organizations } = (await resp.json()) as any; - return organizations; - } - - // This should never be reached due to the while condition, but adding for safety - console.warn( - `Failed to list organizations after ${maxRetries} retries: maximum attempts exceeded`, - ); - return []; - } - - public async listAssistantFullSlugs( - organizationId: string | null, - ): Promise { - if (!(await this.isSignedIn())) { - return null; - } - - const url = organizationId - ? `ide/list-assistant-full-slugs?organizationId=${organizationId}` - : "ide/list-assistant-full-slugs"; - - try { - const resp = await this.requestAndHandleError(url, { - method: "GET", - }); - const { fullSlugs } = (await resp.json()) as any; - return fullSlugs; - } catch (e) { - Logger.error(e, { - context: "control_plane_list_assistant_slugs", - organizationId, - }); - return null; - } - } - - public async getPolicy(): Promise { - if (!(await this.isSignedIn())) { - return null; - } - - try { - const resp = await this.request(`ide/policy`, { - method: "GET", - }); - return (await resp.json()) as PolicyResponse; - } catch (e) { - return null; - } - } - - public async getCreditStatus(): Promise { - if (!(await this.isSignedIn())) { - return null; - } - - try { - const resp = await this.requestAndHandleError("ide/credits", { - method: "GET", - }); - return (await resp.json()) as CreditStatus; - } catch (e) { - Logger.error(e, { - context: "control_plane_credit_status", - }); - return null; - } - } - - /** - * JetBrains does not support deep links, so we only check for `vsCodeUriScheme` - * @param vsCodeUriScheme - * @returns - */ - public async getModelsAddOnCheckoutUrl( - vsCodeUriScheme?: string, - ): Promise<{ url: string } | null> { - if (!(await this.isSignedIn())) { - return null; - } - - try { - const params = new URLSearchParams({ - // LocalProfileLoader ID - profile_id: "local", - }); - - if (vsCodeUriScheme) { - params.set("vscode_uri_scheme", vsCodeUriScheme); - } - - const resp = await this.requestAndHandleError( - `ide/get-models-add-on-checkout-url?${params.toString()}`, - { - method: "GET", - }, - ); - return (await resp.json()) as { url: string }; - } catch (e) { - Logger.error(e, { - context: "control_plane_models_checkout_url", - vsCodeUriScheme, - }); - return null; - } - } - - /** - * Check if remote sessions should be enabled based on feature flags - */ - public async shouldEnableRemoteSessions(): Promise { - // Check if user is signed in - if (!(await this.isSignedIn())) { - return false; - } - - try { - const sessionInfo = await this.sessionInfoPromise; - if (isOnPremSession(sessionInfo) || !sessionInfo) { - return false; - } - - return true; - } catch (e) { - Logger.error(e, { - context: "control_plane_check_remote_sessions_enabled", - }); - return false; - } - } - - /** - * Get current user's session info - */ - public async getSessionInfo(): Promise { - return await this.sessionInfoPromise; - } - - /** - * Fetch remote agents/sessions from the control plane - */ - public async listRemoteSessions(): Promise { - if (!(await this.isSignedIn())) { - return []; - } - - try { - const resp = await this.requestAndHandleError("agents/devboxes", { - method: "GET", - }); - - const agents = (await resp.json()) as any[]; - - return agents.map( - (agent: any): RemoteSessionMetadata => ({ - sessionId: `remote-${agent.id}`, - title: agent.name || "Remote Agent", - dateCreated: new Date(agent.create_time_ms).toISOString(), - workspaceDirectory: "", - isRemote: true, - remoteId: agent.id, - }), - ); - } catch (e) { - // Log error but don't throw - remote sessions are optional - Logger.error(e, { - context: "control_plane_list_remote_sessions", - }); - return []; - } - } - - public async loadRemoteSession(remoteId: string): Promise { - if (!(await this.isSignedIn())) { - throw new Error("Not signed in to load remote session"); - } - - try { - // First get the tunnel URL for the remote agent - const tunnelResp = await this.requestAndHandleError( - `agents/devboxes/${remoteId}/tunnel`, - { - method: "POST", - }, - ); - - const tunnelData = (await tunnelResp.json()) as { url?: string }; - const tunnelUrl = tunnelData.url; - - if (!tunnelUrl) { - throw new Error(`Failed to get tunnel URL for agent ${remoteId}`); - } - - // Now fetch the session state from the remote agent's /state endpoint - const stateResponse = await fetch(`${tunnelUrl}/state`); - if (!stateResponse.ok) { - throw new Error( - `Failed to fetch state from remote agent: ${stateResponse.statusText}`, - ); - } - - const remoteState = (await stateResponse.json()) as { session?: Session }; - - // The remote state contains a session property with the full session data - if (!remoteState.session) { - throw new Error( - "Remote agent returned invalid state - no session found", - ); - } - - return remoteState.session; - } catch (e) { - Logger.error(e, { - context: "control_plane_load_remote_session", - remoteId, - }); - throw new Error( - `Failed to load remote session: ${e instanceof Error ? e.message : "Unknown error"}`, - ); - } - } - - /** - * Create a new background agent - */ - public async createBackgroundAgent( - prompt: string, - repoUrl: string, - name: string, - branch?: string, - organizationId?: string, - contextItems?: any[], - selectedCode?: any[], - agent?: string, - ): Promise<{ id: string }> { - if (!(await this.isSignedIn())) { - throw new Error("Not signed in to Continue"); - } - - const requestBody: any = { - prompt, - repoUrl, - name, - branchName: branch, - }; - - if (organizationId) { - requestBody.organizationId = organizationId; - } - - // Include context items if provided - if (contextItems && contextItems.length > 0) { - requestBody.contextItems = contextItems.map((item) => ({ - content: item.content, - description: item.description, - name: item.name, - uri: item.uri, - })); - } - - // Include selected code if provided - if (selectedCode && selectedCode.length > 0) { - requestBody.selectedCode = selectedCode.map((code) => ({ - filepath: code.filepath, - range: code.range, - contents: code.contents, - })); - } - - // Include agent configuration if provided - if (agent) { - requestBody.agent = agent; - } - - const resp = await this.requestAndHandleError("agents", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(requestBody), - }); - - return (await resp.json()) as { id: string }; - } - - /** - * List all background agents for the current user or organization - * @param organizationId - Optional organization ID to filter agents by organization scope - * @param limit - Optional limit for number of agents to return (default: 5) - */ - public async listBackgroundAgents( - organizationId?: string, - limit?: number, - ): Promise<{ - agents: Array<{ - id: string; - name: string | null; - status: string; - repoUrl: string; - createdAt: string; - metadata?: { - github_repo?: string; - }; - }>; - totalCount: number; - }> { - if (!(await this.isSignedIn())) { - return { agents: [], totalCount: 0 }; - } - - try { - // Build URL with query parameters - const params = new URLSearchParams(); - if (organizationId) { - params.set("organizationId", organizationId); - } - if (limit !== undefined) { - params.set("limit", limit.toString()); - } - - const url = `agents${params.toString() ? `?${params.toString()}` : ""}`; - - const resp = await this.requestAndHandleError(url, { - method: "GET", - }); - - const result = (await resp.json()) as { - agents: AgentSessionView[]; - totalCount: number; - }; - - return { - agents: result.agents.map((agent) => ({ - id: agent.id, - name: agent.name, - status: agent.status, - repoUrl: agent.repoUrl, - createdAt: agent.createdAt, - metadata: { - github_repo: agent.metadata.github_repo, - }, - })), - totalCount: result.totalCount, - }; - } catch (e) { - Logger.error(e, { - context: "control_plane_list_background_agents", - }); - return { agents: [], totalCount: 0 }; - } - } - - /** - * Get the full agent session information - * @param agentSessionId - The ID of the agent session - * @returns The agent session view including metadata and status - */ - public async getAgentSession( - agentSessionId: string, - ): Promise { - if (!(await this.isSignedIn())) { - return null; - } - - try { - const resp = await this.requestAndHandleError( - `agents/${agentSessionId}`, - { - method: "GET", - }, - ); - - return (await resp.json()) as AgentSessionView; - } catch (e) { - Logger.error(e, { - context: "control_plane_get_agent_session", - agentSessionId, - }); - return null; - } - } - - /** - * Get the state of a specific background agent - * @param agentSessionId - The ID of the agent session - * @returns The agent's session state including history, workspace, and branch - */ - public async getAgentState(agentSessionId: string): Promise<{ - session: Session; - isProcessing: boolean; - messageQueueLength: number; - pendingPermission: any; - } | null> { - if (!(await this.isSignedIn())) { - return null; - } - - try { - const resp = await this.requestAndHandleError( - `agents/${agentSessionId}/state`, - { - method: "GET", - }, - ); - - const result = (await resp.json()) as { - session: Session; - isProcessing: boolean; - messageQueueLength: number; - pendingPermission: any; - }; - return result; - } catch (e) { - Logger.error(e, { - context: "control_plane_get_agent_state", - agentSessionId, - }); - return null; - } - } -} diff --git a/core/control-plane/env.ts b/core/control-plane/env.ts deleted file mode 100644 index 2c7723cc550..00000000000 --- a/core/control-plane/env.ts +++ /dev/null @@ -1,105 +0,0 @@ -import * as fs from "node:fs"; -import { IdeSettings } from ".."; -import { - getLocalEnvironmentDotFilePath, - getStagingEnvironmentDotFilePath, -} from "../util/paths"; -import { AuthType, ControlPlaneEnv } from "./AuthTypes"; -import { getLicenseKeyData } from "./mdm/mdm"; - -export const EXTENSION_NAME = "continue"; - -const WORKOS_CLIENT_ID_PRODUCTION = "client_01J0FW6XN8N2XJAECF7NE0Y65J"; -const WORKOS_CLIENT_ID_STAGING = "client_01J0FW6XCPMJMQ3CG51RB4HBZQ"; - -const PRODUCTION_HUB_ENV: ControlPlaneEnv = { - DEFAULT_CONTROL_PLANE_PROXY_URL: "https://api.continue.dev/", - CONTROL_PLANE_URL: "https://api.continue.dev/", - AUTH_TYPE: AuthType.WorkOsProd, - WORKOS_CLIENT_ID: WORKOS_CLIENT_ID_PRODUCTION, - APP_URL: "https://continue.dev/", -}; - -const STAGING_ENV: ControlPlaneEnv = { - DEFAULT_CONTROL_PLANE_PROXY_URL: "https://api.continue-stage.tools/", - CONTROL_PLANE_URL: "https://api.continue-stage.tools/", - AUTH_TYPE: AuthType.WorkOsStaging, - WORKOS_CLIENT_ID: WORKOS_CLIENT_ID_STAGING, - APP_URL: "https://hub.continue-stage.tools/", -}; - -const TEST_ENV: ControlPlaneEnv = { - DEFAULT_CONTROL_PLANE_PROXY_URL: "https://api-test.continue.dev/", - CONTROL_PLANE_URL: "https://api-test.continue.dev/", - AUTH_TYPE: AuthType.WorkOsStaging, - WORKOS_CLIENT_ID: WORKOS_CLIENT_ID_STAGING, - APP_URL: "https://app-test.continue.dev/", -}; - -const LOCAL_ENV: ControlPlaneEnv = { - DEFAULT_CONTROL_PLANE_PROXY_URL: "http://localhost:3001/", - CONTROL_PLANE_URL: "http://localhost:3001/", - AUTH_TYPE: AuthType.WorkOsStaging, - WORKOS_CLIENT_ID: WORKOS_CLIENT_ID_STAGING, - APP_URL: "http://localhost:3000/", -}; - -export async function enableHubContinueDev() { - return true; -} - -export async function getControlPlaneEnv( - ideSettingsPromise: Promise, -): Promise { - const ideSettings = await ideSettingsPromise; - return getControlPlaneEnvSync(ideSettings.continueTestEnvironment); -} - -export function getControlPlaneEnvSync( - ideTestEnvironment: IdeSettings["continueTestEnvironment"], -): ControlPlaneEnv { - // MDM override - const licenseKeyData = getLicenseKeyData(); - if (licenseKeyData?.unsignedData?.apiUrl) { - const { apiUrl } = licenseKeyData.unsignedData; - return { - AUTH_TYPE: AuthType.OnPrem, - DEFAULT_CONTROL_PLANE_PROXY_URL: apiUrl, - CONTROL_PLANE_URL: apiUrl, - APP_URL: "https://continue.dev/", - }; - } - - // Note .local overrides .staging - if (fs.existsSync(getLocalEnvironmentDotFilePath())) { - return LOCAL_ENV; - } - - if (fs.existsSync(getStagingEnvironmentDotFilePath())) { - return STAGING_ENV; - } - - const env = - ideTestEnvironment === "production" - ? "hub" - : ideTestEnvironment === "staging" - ? "staging" - : ideTestEnvironment === "local" - ? "local" - : process.env.CONTROL_PLANE_ENV; - - return env === "local" - ? LOCAL_ENV - : env === "staging" - ? STAGING_ENV - : env === "test" - ? TEST_ENV - : PRODUCTION_HUB_ENV; -} - -export async function useHub( - ideSettingsPromise: Promise, -): Promise { - const ideSettings = await ideSettingsPromise; - return ideSettings.continueTestEnvironment !== "none"; -} diff --git a/core/control-plane/mdm/mdm.ts b/core/control-plane/mdm/mdm.ts deleted file mode 100644 index 6971a988282..00000000000 --- a/core/control-plane/mdm/mdm.ts +++ /dev/null @@ -1,370 +0,0 @@ -import * as crypto from "crypto"; -import * as fs from "fs"; -import * as os from "os"; -import * as path from "path"; -import * as plist from "plist"; - -export interface MdmKeys { - licenseKey: string; -} - -const CONTINUE_PUBLIC_KEY = `-----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz1pFVzsW2UScSnaPAwFp -93QU4+txtyJj8AOC3Kx7YkX1d48DGU2Fy1he7SXPHgcuhXYIqfWGn/Vy/4yJxXD7 -HlU8RM7LlWHRk7ecAvF4WtxZDjPE0OSG5T69w5f7tMCtQPQseInCKqleJuCjxrvA -zyTWTwKA1S6e+KxXS/kbqsumriljFujNr6Gmh8ygDnpF055Xo0vTenkMipVw/oYn -U0EHx5ic+Fmcob3EyOj25lu/CVYtU1Rn8jPbrBOMiIq5sePh2QXOwGRsuTiZk2mP -LXSsjLbeWHifRrQ18wj/PC990E33QaFGNFp0NcBXMPbq5eKYKEzKZ0GsxtLoGxar -FQIDAQAB ------END PUBLIC KEY-----`; - -interface LicenseKeyUnsignedData { - apiUrl: string; -} - -export interface LicenseKey { - signature: string; - data: string; - unsignedData: LicenseKeyUnsignedData; -} - -export interface LicenseKeyData { - customerId: string; - createdAt: string; - expiresAt: string; -} - -export function validateLicenseKey(licenseKey: string): { - isValid: boolean; - licenseKeyData?: LicenseKey; -} { - try { - // Decode the base64 license key - const decodedString = Buffer.from(licenseKey, "base64").toString("utf8"); - const { data, signature, unsignedData } = JSON.parse(decodedString); - - // Verify the signature - const verify = crypto.createVerify("SHA256"); - verify.update(data); - verify.end(); - - const isValid = verify.verify(CONTINUE_PUBLIC_KEY, signature, "base64"); - - if (!isValid) return { isValid: false }; - - // Check license expiration - const licenseData: LicenseKeyData = JSON.parse(data); - const expirationDate = new Date(licenseData.expiresAt); - const now = new Date(); - - const isNotExpired = expirationDate > now; - - if (!isNotExpired) return { isValid: false }; - - // Return both validation result and license key data - const licenseKeyData: LicenseKey = { - signature, - data, - unsignedData, - }; - - return { isValid: true, licenseKeyData }; - } catch (error) { - console.error("License validation error:", error); - return { isValid: false }; - } -} - -const MACOS_MDM_PATHS = [ - // Organization-specific MDM plist - "/Library/Managed Preferences/dev.continue.app.plist", - // User-specific MDM plist - path.join(os.homedir(), "Library/Managed Preferences/dev.continue.app.plist"), -]; - -function readMdmKeysMacOS(): MdmKeys | undefined { - try { - // MDM configuration is typically stored in /Library/Managed Preferences/ on macOS - // The filename is often the bundle identifier of the application - - // Try to find a valid MDM configuration file - for (const mdmPath of MACOS_MDM_PATHS) { - if (fs.existsSync(mdmPath)) { - // Read the file content - const fileContent = fs.readFileSync(mdmPath, "utf8"); - - try { - // Parse the plist file using the plist package - const config = plist.parse(fileContent) as - | { licenseKey: string | undefined } - | undefined; - - if (config && config?.licenseKey) { - // Extract the license key from the configuration - return { licenseKey: config?.licenseKey }; - } else { - console.error("Invalid MDM configuration format"); - } - } catch (parseError) { - console.error(`Error parsing MDM configuration: ${parseError}`); - } - } - } - - return undefined; - } catch (error) { - console.error("Error reading macOS MDM keys:", error); - return undefined; - } -} - -function readMdmKeysWindows(): MdmKeys | undefined { - try { - // For Windows, we need to read from the registry - // Using regedit or another synchronous registry access module - const { execSync } = require("child_process"); - - // Path to the registry where MDM configuration is stored - const regPath = "HKLM\\Software\\Continue\\MDM"; - const userRegPath = "HKCU\\Software\\Continue\\MDM"; - - // Try to read from HKEY_LOCAL_MACHINE first - try { - // Use REG QUERY command to read registry values - const licenseKeyCmd = `reg query "${regPath}" /v licenseKey`; - const licenseKeyOutput = execSync(licenseKeyCmd, { - encoding: "utf8", - stdio: ["pipe", "pipe", "pipe"], - }); - // Extract values from command output - const licenseKey = extractRegValue(licenseKeyOutput); - if (licenseKey) { - return { licenseKey }; - } - } catch (error) { - // Registry key might not exist, fallback to HKEY_CURRENT_USER - } - - // Try HKEY_CURRENT_USER if not found in HKEY_LOCAL_MACHINE - try { - const licenseKeyCmd = `reg query "${userRegPath}" /v licenseKey`; - const licenseKeyOutput = execSync(licenseKeyCmd, { - encoding: "utf8", - stdio: ["pipe", "pipe", "pipe"], - }); - // Extract values from command output - const licenseKey = extractRegValue(licenseKeyOutput); - if (licenseKey) { - return { licenseKey }; - } - } catch (error) { - // Registry key might not exist in HKCU either - } - - return undefined; - } catch (error) { - console.error("Error reading Windows MDM keys:", error); - return undefined; - } -} - -// Helper function to extract registry values from reg query output -function extractRegValue(output: string): string | undefined { - const match = output.match(/REG_SZ\s+(.+)$/m); - return match ? match[1].trim() : undefined; -} - -// Common locations for MDM configurations in Linux systems -const LINUX_MDM_PATHS = [ - // System-wide configuration - "/etc/continue/mdm.json", - "/var/lib/continue/mdm.json", - // User-specific configuration - path.join(os.homedir(), ".config/continue/mdm.json"), -]; - -function readMdmKeysLinux(): MdmKeys | undefined { - try { - // Try to find a valid MDM configuration file - for (const mdmPath of LINUX_MDM_PATHS) { - if (fs.existsSync(mdmPath)) { - // Read the file content - const fileContent = fs.readFileSync(mdmPath, "utf8"); - - try { - // Parse the JSON configuration file - const config = JSON.parse(fileContent); - - // Check if required key is present - if (config.licenseKey) { - return { - licenseKey: config.licenseKey, - }; - } - } catch (parseError) { - console.error(`Error parsing Linux MDM configuration: ${parseError}`); - } - } - } - - return undefined; - } catch (error) { - console.error("Error reading Linux MDM keys:", error); - return undefined; - } -} - -function readMdmKeys(): MdmKeys | undefined { - const platform = os.platform(); - - switch (platform) { - case "darwin": - return readMdmKeysMacOS(); - - case "win32": - return readMdmKeysWindows(); - - case "linux": - return readMdmKeysLinux(); - - default: - console.error(`MDM keys not supported on platform: ${platform}`); - return undefined; - } -} - -/** - * Read and validate MDM keys from the operating system's configuration files or registry. - */ -export function getLicenseKeyData(): LicenseKey | undefined { - try { - const mdmKeys = readMdmKeys(); - - if (mdmKeys) { - const { isValid, licenseKeyData } = validateLicenseKey( - mdmKeys.licenseKey, - ); - if (!isValid) { - console.error("Invalid license key found: ", mdmKeys.licenseKey); - return undefined; - } - - return licenseKeyData; - } - - return undefined; - } catch (e) { - console.warn("Error reading MDM keys: ", e); - return undefined; - } -} - -function writeMdmKeysMacOS(licenseKey: string): boolean { - try { - // Write to user-specific MDM plist - const userMdmPath = path.join( - os.homedir(), - "Library/Managed Preferences/dev.continue.app.plist", - ); - - const config = { - licenseKey, - }; - - // Ensure directory exists - fs.mkdirSync(path.dirname(userMdmPath), { recursive: true }); - - // Write the plist file - const plistContent = plist.build(config); - fs.writeFileSync(userMdmPath, plistContent, "utf8"); - - return true; - } catch (error) { - console.error("Error writing macOS MDM keys:", error); - return false; - } -} - -function writeMdmKeysWindows(licenseKey: string): boolean { - try { - const { execSync } = require("child_process"); - - // Use HKEY_CURRENT_USER to avoid needing admin privileges - const userRegPath = "HKCU\\Software\\Continue\\MDM"; - - // Create the registry key if it doesn't exist - try { - execSync(`reg add "${userRegPath}" /f`); - } catch (error) { - // Key might already exist - } - - // Set the license key and API URL - execSync( - `reg add "${userRegPath}" /v licenseKey /t REG_SZ /d "${licenseKey}" /f`, - ); - - return true; - } catch (error) { - console.error("Error writing Windows MDM keys:", error); - return false; - } -} - -function writeMdmKeysLinux(licenseKey: string): boolean { - try { - // Write to user-specific configuration - const userMdmPath = path.join(os.homedir(), ".config/continue/mdm.json"); - - const config = { - licenseKey, - }; - - // Ensure directory exists - fs.mkdirSync(path.dirname(userMdmPath), { recursive: true }); - - // Write the JSON configuration file - fs.writeFileSync(userMdmPath, JSON.stringify(config, null, 2), "utf8"); - - return true; - } catch (error) { - console.error("Error writing Linux MDM keys:", error); - return false; - } -} - -/** - * Store the license key in the appropriate OS-specific location. - * For now, we'll use a default API URL since it's not provided in the command. - */ -export function setMdmLicenseKey(licenseKey: string): boolean { - try { - // Validate the license key first - const { isValid } = validateLicenseKey(licenseKey); - if (!isValid) { - return false; - } - - const platform = os.platform(); - - switch (platform) { - case "darwin": - return writeMdmKeysMacOS(licenseKey); - - case "win32": - return writeMdmKeysWindows(licenseKey); - - case "linux": - return writeMdmKeysLinux(licenseKey); - - default: - console.error( - `Setting MDM keys not supported on platform: ${platform}`, - ); - return false; - } - } catch (error) { - console.error("Error setting MDM license key:", error); - return false; - } -} diff --git a/core/control-plane/mdm/mdm.vitest.ts b/core/control-plane/mdm/mdm.vitest.ts deleted file mode 100644 index 193960e27bb..00000000000 --- a/core/control-plane/mdm/mdm.vitest.ts +++ /dev/null @@ -1,211 +0,0 @@ -import * as crypto from "crypto"; -import { expect, test } from "vitest"; -import { LicenseKeyData, validateLicenseKey } from "./mdm"; - -// We'll create a real key pair once for all tests -const testKeyPair = crypto.generateKeyPairSync("rsa", { - modulusLength: 2048, - publicKeyEncoding: { - type: "spki", - format: "pem", - }, - privateKeyEncoding: { - type: "pkcs8", - format: "pem", - }, -}); - -// Custom function to create test licenses with the new structure including unsignedData -function createTestLicense( - licenseData: LicenseKeyData, - apiUrl: string = "https://api.continue.dev", - useValidSignature: boolean = true, -): string { - // Convert license data to a string - const licenseDataStr = JSON.stringify(licenseData); - - // Create a signature with our generated private key - const sign = crypto.createSign("SHA256"); - sign.update(licenseDataStr); - sign.end(); - - let signature: string; - if (useValidSignature) { - signature = sign.sign(testKeyPair.privateKey, "base64"); - } else { - // For invalid tests, create an intentionally wrong signature by signing different data - const wrongSign = crypto.createSign("SHA256"); - wrongSign.update(licenseDataStr + "tampered"); - wrongSign.end(); - signature = wrongSign.sign(testKeyPair.privateKey, "base64"); - } - - // Combine data, signature, and unsigned data into a license key - const licenseKeyObj = { - data: licenseDataStr, - signature: signature, - unsignedData: { - apiUrl: apiUrl, - }, - }; - - return Buffer.from(JSON.stringify(licenseKeyObj)).toString("base64"); -} - -// We need to test the actual implementation - which we can't mock with ESM -// Instead, we'll have tests that bypass validation or are expected to fail -// depending on what we're testing - -test("validateLicenseKey returns false for malformed license key", () => { - // Test with a completely invalid license key - const invalidLicenseKey = "not-a-valid-license-key"; - - // Test - no mocks, real validation - const result = validateLicenseKey(invalidLicenseKey); - - // Assert - expect(result.isValid).toBe(false); - expect(result.licenseKeyData).toBeUndefined(); -}); - -test("validateLicenseKey returns false for invalid JSON in license key", () => { - // Create a malformed but base64-encoded string - const malformedLicenseKey = Buffer.from("this is not valid JSON").toString( - "base64", - ); - - // Test - no mocks, real validation - const result = validateLicenseKey(malformedLicenseKey); - - // Assert - expect(result.isValid).toBe(false); - expect(result.licenseKeyData).toBeUndefined(); -}); - -test("validateLicenseKey returns false for expired license", () => { - // Without mocking, we expect this to fail signature verification - // We're testing the date logic by itself - const licenseData: LicenseKeyData = { - customerId: "customer123", - createdAt: new Date(Date.now() - 2000 * 60 * 60 * 24).toISOString(), // Created 2 days ago - expiresAt: new Date(Date.now() - 1000 * 60 * 60 * 24).toISOString(), // Expired 1 day ago - }; - - // Create a license key with apiUrl in unsignedData - will fail verification but test logic still works - const expiredLicenseKey = createTestLicense( - licenseData, - "https://api.continue.dev", - ); - - // Test - expect false, either due to signature or expiration - both are valid test cases - const result = validateLicenseKey(expiredLicenseKey); - - // Assert - expect(result.isValid).toBe(false); - expect(result.licenseKeyData).toBeUndefined(); -}); - -test("validateLicenseKey returns false for invalid signature", () => { - // Create license data with valid dates - const licenseData: LicenseKeyData = { - customerId: "customer123", - createdAt: new Date().toISOString(), - expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24).toISOString(), // Valid for 1 day - }; - - // Create a license with an invalid signature but valid unsignedData structure - const invalidSignatureKey = createTestLicense( - licenseData, - "https://api.continue.dev", - false, - ); - - // Test - no mocks, real validation - const result = validateLicenseKey(invalidSignatureKey); - - // Assert - expect(result.isValid).toBe(false); - expect(result.licenseKeyData).toBeUndefined(); -}); - -test("validateLicenseKey handles JSON parsing errors gracefully", () => { - // Create a base64 string that decodes to valid JSON but lacks required fields - const invalidStructureKey = Buffer.from( - JSON.stringify({ - notTheRightField: "wrong structure", - }), - ).toString("base64"); - - // Test - no mocks, real validation - const result = validateLicenseKey(invalidStructureKey); - - // Assert - expect(result.isValid).toBe(false); - expect(result.licenseKeyData).toBeUndefined(); -}); - -test("validateLicenseKey handles missing unsignedData gracefully", () => { - // Create a license key structure without unsignedData field - const licenseData: LicenseKeyData = { - customerId: "customer123", - createdAt: new Date().toISOString(), - expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24).toISOString(), - }; - - const licenseDataStr = JSON.stringify(licenseData); - const sign = crypto.createSign("SHA256"); - sign.update(licenseDataStr); - sign.end(); - const signature = sign.sign(testKeyPair.privateKey, "base64"); - - // Create license key without unsignedData - const licenseKeyObj = { - data: licenseDataStr, - signature: signature, - // Missing unsignedData field - }; - - const licenseKey = Buffer.from(JSON.stringify(licenseKeyObj)).toString( - "base64", - ); - - // Test - no mocks, real validation - const result = validateLicenseKey(licenseKey); - - // Assert - should fail due to signature verification with wrong public key - expect(result.isValid).toBe(false); - expect(result.licenseKeyData).toBeUndefined(); -}); - -test("validateLicenseKey handles invalid unsignedData structure", () => { - // Create license data with valid dates - const licenseData: LicenseKeyData = { - customerId: "customer123", - createdAt: new Date().toISOString(), - expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24).toISOString(), - }; - - const licenseDataStr = JSON.stringify(licenseData); - const sign = crypto.createSign("SHA256"); - sign.update(licenseDataStr); - sign.end(); - const signature = sign.sign(testKeyPair.privateKey, "base64"); - - // Create license key with invalid unsignedData structure - const licenseKeyObj = { - data: licenseDataStr, - signature: signature, - unsignedData: "not an object", // Should be an object with apiUrl - }; - - const licenseKey = Buffer.from(JSON.stringify(licenseKeyObj)).toString( - "base64", - ); - - // Test - no mocks, real validation - const result = validateLicenseKey(licenseKey); - - // Assert - should fail due to signature verification with wrong public key - expect(result.isValid).toBe(false); - expect(result.licenseKeyData).toBeUndefined(); -}); diff --git a/core/control-plane/schema.ts b/core/control-plane/schema.ts deleted file mode 100644 index 191a34884a7..00000000000 --- a/core/control-plane/schema.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { z } from "zod"; - -const modelDescriptionSchema = z.object({ - title: z.string(), - provider: z.enum([ - "openai", - "anthropic", - "cohere", - "ollama", - "huggingface-tgi", - "huggingface-inference-api", - "replicate", - "gemini", - "mistral", - "bedrock", - "sagemaker", - "cloudflare", - "azure", - "ovhcloud", - "nebius", - "siliconflow", - "scaleway", - "watsonx", - ]), - model: z.string(), - apiKey: z.string().optional(), - apiBase: z.string().optional(), - contextLength: z.number().optional(), - maxStopWords: z.number().optional(), - template: z - .enum([ - "llama2", - "alpaca", - "zephyr", - "phi2", - "phind", - "anthropic", - "chatml", - "none", - "openchat", - "deepseek", - "xwin-coder", - "neural-chat", - "codellama-70b", - "llava", - "gemma", - "llama3", - "codestral", - ]) - .optional(), - completionOptions: z - .object({ - temperature: z.number().optional(), - topP: z.number().optional(), - topK: z.number().optional(), - minP: z.number().optional(), - presencePenalty: z.number().optional(), - frequencyPenalty: z.number().optional(), - mirostat: z.number().optional(), - stop: z.array(z.string()).optional(), - maxTokens: z.number().optional(), - numThreads: z.number().optional(), - useMmap: z.boolean().optional(), - keepAlive: z.number().optional(), - numGpu: z.number().optional(), - raw: z.boolean().optional(), - stream: z.boolean().optional(), - }) - .optional(), - systemMessage: z.string().optional(), - requestOptions: z - .object({ - timeout: z.number().optional(), - verifySsl: z.boolean().optional(), - caBundlePath: z.union([z.string(), z.array(z.string())]).optional(), - proxy: z.string().optional(), - headers: z.record(z.string()).optional(), - extraBodyProperties: z.record(z.any()).optional(), - noProxy: z.array(z.string()).optional(), - }) - .optional(), - promptTemplates: z.record(z.string()).optional(), -}); - -const embeddingsProviderSchema = z.object({ - provider: z.enum([ - "transformers.js", - "ollama", - "openai", - "cohere", - "gemini", - "ovhcloud", - "nebius", - "siliconflow", - "scaleway", - "watsonx", - ]), - apiBase: z.string().optional(), - apiKey: z.string().optional(), - model: z.string().optional(), - deployment: z.string().optional(), - apiType: z.string().optional(), - apiVersion: z.string().optional(), - requestOptions: z - .object({ - timeout: z.number().optional(), - verifySsl: z.boolean().optional(), - caBundlePath: z.union([z.string(), z.array(z.string())]).optional(), - proxy: z.string().optional(), - headers: z.record(z.string()).optional(), - extraBodyProperties: z.record(z.any()).optional(), - noProxy: z.array(z.string()).optional(), - }) - .optional(), -}); - -const rerankerSchema = z.object({ - name: z.enum(["cohere", "voyage", "llm", "watsonx"]), - params: z.record(z.any()).optional(), -}); - -const analyticsSchema = z.object({ - url: z.string().optional(), - clientKey: z.string().optional(), -}); - -export type ControlPlaneAnalytics = z.infer; - -const devDataSchema = z.object({ - url: z.string().optional(), -}); - -export const controlPlaneSettingsSchema = z.object({ - models: z.array(modelDescriptionSchema), - tabAutocompleteModel: modelDescriptionSchema, - embeddingsModel: embeddingsProviderSchema, - reranker: rerankerSchema, - analytics: analyticsSchema, - devData: devDataSchema, -}); - -export type ControlPlaneSettings = z.infer; diff --git a/core/core.ts b/core/core.ts index a9041915b72..e2fa8dab7a1 100644 --- a/core/core.ts +++ b/core/core.ts @@ -9,8 +9,6 @@ import { } from "./autocomplete/util/openedFilesLruCache"; import { ConfigHandler } from "./config/ConfigHandler"; import { addModel, deleteModel } from "./config/util"; -import { getAuthUrlForTokenPage } from "./control-plane/auth/index"; -import { getControlPlaneEnv } from "./control-plane/env"; import { DevDataSqliteDb } from "./data/devdataSqlite"; import { DataLogger } from "./data/log"; import { CodebaseIndexer } from "./indexing/CodebaseIndexer"; @@ -69,7 +67,6 @@ import { } from "./config/workspace/workspaceBlocks"; import { MCPManagerSingleton } from "./context/mcp/MCPManagerSingleton"; import { performAuth, removeMCPAuth } from "./context/mcp/MCPOauth"; -import { setMdmLicenseKey } from "./control-plane/mdm/mdm"; import { myersDiff } from "./diff/myers"; import { ApplyAbortManager } from "./edit/applyAbortManager"; import { streamDiffLines } from "./edit/streamDiffLines"; @@ -137,19 +134,7 @@ export class Core { const ideInfoPromise = messenger.request("getIdeInfo", undefined); const ideSettingsPromise = messenger.request("getIdeSettings", undefined); - const initialSessionInfoPromise = messenger.request( - "getControlPlaneSessionInfo", - { - silent: true, - useOnboarding: false, - }, - ); - - this.configHandler = new ConfigHandler( - this.ide, - this.llmLogger, - initialSessionInfoPromise, - ); + this.configHandler = new ConfigHandler(this.ide, this.llmLogger); this.docsService = DocsService.createSingleton( this.configHandler, @@ -318,26 +303,9 @@ export class Core { // History on("history/list", async (msg) => { - const localSessions = historyManager.list(msg.data); - - // Check if remote sessions should be enabled based on feature flags - const shouldFetchRemote = - await this.configHandler.controlPlaneClient.shouldEnableRemoteSessions(); - - // Get remote sessions from control plane if feature is enabled - const remoteSessions = shouldFetchRemote - ? await this.configHandler.controlPlaneClient.listRemoteSessions() - : []; - - // Combine and sort by date (most recent first) - const allSessions = [...localSessions, ...remoteSessions].sort( - (a, b) => - new Date(b.dateCreated).getTime() - new Date(a.dateCreated).getTime(), - ); - - // Apply limit if specified + const sessions = historyManager.list(msg.data); const limit = msg.data?.limit ?? 100; - return allSessions.slice(0, limit); + return sessions.slice(0, limit); }); on("history/delete", (msg) => { @@ -348,12 +316,6 @@ export class Core { return historyManager.load(msg.data.id); }); - on("history/loadRemote", async (msg) => { - return this.configHandler.controlPlaneClient.loadRemoteSession( - msg.data.remoteId, - ); - }); - on("history/save", (msg) => { historyManager.save(msg.data); }); @@ -492,28 +454,6 @@ export class Core { return newSelectedModels; }); - on("controlPlane/openUrl", async (msg) => { - const env = await getControlPlaneEnv(this.ide.getIdeSettings()); - const urlPath = msg.data.path.startsWith("/") - ? msg.data.path.slice(1) - : msg.data.path; - let url; - if (msg.data.orgSlug) { - url = `${env.APP_URL}organizations/${msg.data.orgSlug}/${urlPath}`; - } else { - url = `${env.APP_URL}${urlPath}`; - } - await this.messenger.request("openUrl", url); - }); - - on("controlPlane/getEnvironment", async (msg) => { - return await getControlPlaneEnv(this.ide.getIdeSettings()); - }); - - on("controlPlane/getCreditStatus", async (msg) => { - return this.configHandler.controlPlaneClient.getCreditStatus(); - }); - on("mcp/reloadServer", async (msg) => { await MCPManagerSingleton.getInstance().refreshConnection(msg.data.id); }); @@ -1099,21 +1039,8 @@ export class Core { } }); - on("didChangeControlPlaneSessionInfo", async (msg) => { - this.messenger.send("sessionUpdate", { - sessionInfo: msg.data.sessionInfo, - }); - await this.configHandler.updateControlPlaneSessionInfo( - msg.data.sessionInfo, - ); - }); - - on("auth/getAuthUrl", async (msg) => { - const url = await getAuthUrlForTokenPage( - ideSettingsPromise, - msg.data.useOnboarding, - ); - return { url }; + on("auth/getAuthUrl", async (_msg) => { + return { url: "" }; }); on("tools/call", async ({ data: { toolCall } }) => @@ -1204,11 +1131,6 @@ export class Core { on("process/killTerminalProcess", async ({ data: { toolCallId } }) => { await killTerminalProcess(toolCallId); }); - - on("mdm/setLicenseKey", ({ data: { licenseKey } }) => { - const isValid = setMdmLicenseKey(licenseKey); - return isValid; - }); } private async handleToolCall(toolCall: ToolCall) { diff --git a/core/data/log.ts b/core/data/log.ts index c4cf27805d4..57e00e4bfbe 100644 --- a/core/data/log.ts +++ b/core/data/log.ts @@ -189,13 +189,8 @@ export class DataLogger { "Content-Type": "application/json", }; - // If an API key is provided, use it, otherwise use the Continue access token if (dataConfig.apiKey) { headers["Authorization"] = `Bearer ${dataConfig.apiKey}`; - } else { - const accessToken = - await this.core?.configHandler.controlPlaneClient.getAccessToken(); - headers["Authorization"] = `Bearer ${accessToken}`; } const profileId = diff --git a/core/data/log.vitest.ts b/core/data/log.vitest.ts index 25defe504bd..cbdd6610dcd 100644 --- a/core/data/log.vitest.ts +++ b/core/data/log.vitest.ts @@ -87,9 +87,6 @@ describe("DataLogger", () => { id: "test-profile-id", }, }, - controlPlaneClient: { - getAccessToken: vi.fn().mockResolvedValue("test-access-token"), - }, }, } as unknown as Core; diff --git a/core/index.d.ts b/core/index.d.ts index 068c51abcec..ba4154d924b 100644 --- a/core/index.d.ts +++ b/core/index.d.ts @@ -667,7 +667,6 @@ export interface LLMOptions { apiKeyLocation?: string; envSecretLocations?: Record; apiBase?: string; - orgScopeId?: string | null; onPremProxyUrl?: string | null; @@ -1028,7 +1027,6 @@ export type ContextProviderName = | "currentFile" | "greptile" | "outline" - | "continue-proxy" | "highlights" | "file" | "issue" @@ -1187,7 +1185,6 @@ interface ToolChoice { export interface ConfigDependentToolParams { rules: RuleWithSource[]; enableExperimentalTools: boolean; - isSignedIn: boolean; isRemote: boolean; modelName: string | undefined; ide: IDE; @@ -1235,7 +1232,6 @@ export interface ModelDescription { apiBase?: string; apiKeyLocation?: string; envSecretLocations?: Record; - orgScopeId?: string | null; onPremProxyUrl?: string | null; @@ -1861,7 +1857,6 @@ export interface BrowserSerializedContinueConfig { tools: Omit[]; mcpServerStatuses: MCPServerStatus[]; rules: RuleWithSource[]; - usePlatform: boolean; tabAutocompleteOptions?: Partial; modelsByRole: Record; selectedModelByRole: Record; diff --git a/core/indexing/docs/crawlers/DefaultCrawler.ts b/core/indexing/docs/crawlers/DefaultCrawler.ts index d5e717fc056..f1d64f31868 100644 --- a/core/indexing/docs/crawlers/DefaultCrawler.ts +++ b/core/indexing/docs/crawlers/DefaultCrawler.ts @@ -1,7 +1,7 @@ import { URL } from "node:url"; import { getHeaders } from "../../../continueServer/stubs/headers"; -import { TRIAL_PROXY_URL } from "../../../control-plane/client"; +const TRIAL_PROXY_URL = "https://proxy-server-blue-l6vsfbzhba-uw.a.run.app"; import { PageData } from "./DocsCrawler"; export class DefaultCrawler { diff --git a/core/llm/autodetect.ts b/core/llm/autodetect.ts index 4085d3798b7..0e14c53adf3 100644 --- a/core/llm/autodetect.ts +++ b/core/llm/autodetect.ts @@ -56,7 +56,6 @@ const PROVIDER_HANDLES_TEMPLATING: string[] = [ "bedrock", "cohere", "sagemaker", - "continue-proxy", "mistral", "mimo", "sambanova", @@ -119,7 +118,6 @@ const PROVIDER_SUPPORTS_IMAGES: string[] = [ "anthropic", "bedrock", "sagemaker", - "continue-proxy", "openrouter", "venice", "sambanova", diff --git a/core/llm/index.ts b/core/llm/index.ts index 7c5e18cacbd..1e00b1f4da3 100644 --- a/core/llm/index.ts +++ b/core/llm/index.ts @@ -99,10 +99,6 @@ export abstract class BaseLLM implements ILLM { return (this.constructor as typeof BaseLLM).providerName; } - /** - * This exists because for the continue-proxy, sometimes we want to get the value of the underlying provider that is used on the server - * For example, the underlying provider should always be sent with dev data - */ get underlyingProviderName(): string { return this.providerName; } @@ -168,8 +164,6 @@ export abstract class BaseLLM implements ILLM { apiKeyLocation?: string; envSecretLocations?: Record; apiBase?: string; - orgScopeId?: string | null; - onPremProxyUrl?: string | null; cacheBehavior?: CacheBehavior; @@ -222,11 +216,7 @@ export abstract class BaseLLM implements ILLM { this.model = options.model; // Use @continuedev/llm-info package to autodetect certain parameters - const modelSearchString = - this.providerName === "continue-proxy" - ? this.model?.split("/").pop() || this.model - : this.model; - const llmInfo = findLlmInfo(modelSearchString, this.underlyingProviderName); + const llmInfo = findLlmInfo(this.model, this.underlyingProviderName); const templateType = options.template ?? autodetectTemplateType(options.model); @@ -272,7 +262,6 @@ export abstract class BaseLLM implements ILLM { // continueProperties this.apiKeyLocation = options.apiKeyLocation; this.envSecretLocations = options.envSecretLocations; - this.orgScopeId = options.orgScopeId; this.apiBase = options.apiBase; this.onPremProxyUrl = options.onPremProxyUrl; diff --git a/core/llm/llms/index.ts b/core/llm/llms/index.ts index 04f58e393de..5320616e488 100644 --- a/core/llm/llms/index.ts +++ b/core/llm/llms/index.ts @@ -56,7 +56,7 @@ import SageMaker from "./SageMaker"; import SambaNova from "./SambaNova"; import Scaleway from "./Scaleway"; import SiliconFlow from "./SiliconFlow"; -import ContinueProxy from "./stubs/ContinueProxy"; + import TARS from "./TARS"; import TestLLM from "./Test"; import TextGenWebUI from "./TextGenWebUI"; @@ -100,7 +100,6 @@ export const LLMClasses = [ Groq, Fireworks, NCompass, - ContinueProxy, Cloudflare, Deepseek, Docker, @@ -180,16 +179,6 @@ export async function llmFromDescription( uniqueId, }; - if (desc.provider === "continue-proxy") { - options.apiKey = ideSettings.userToken; - if (ideSettings.remoteConfigServerUrl) { - options.apiBase = new URL( - "/proxy/v1", - ideSettings.remoteConfigServerUrl, - ).toString(); - } - } - return new cls(options); } diff --git a/core/llm/llms/stubs/ContinueProxy.ts b/core/llm/llms/stubs/ContinueProxy.ts deleted file mode 100644 index d107d05fe53..00000000000 --- a/core/llm/llms/stubs/ContinueProxy.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { - ContinueProperties, - decodeSecretLocation, - parseProxyModelName, - SecretType, -} from "@continuedev/config-yaml"; - -import { ControlPlaneProxyInfo } from "../../../control-plane/analytics/IAnalyticsProvider.js"; -import { IdeInfoService } from "../../../util/IdeInfoService.js"; -import OpenAI from "../OpenAI.js"; - -import type { Chunk, LLMOptions } from "../../../index.js"; -import { LLMConfigurationStatuses } from "../../constants.js"; -import { LlmApiRequestType } from "../../openaiTypeConverters.js"; - -class ContinueProxy extends OpenAI { - set controlPlaneProxyInfo(value: ControlPlaneProxyInfo) { - this.apiKey = value.workOsAccessToken; - if (!this.onPremProxyUrl) { - this.apiBase = new URL( - "model-proxy/v1/", - value.controlPlaneProxyUrl, - ).toString(); - } - } - - protected useOpenAIAdapterFor: (LlmApiRequestType | "*")[] = []; - - // The apiKey and apiBase are set to the values for the proxy, - // but we need to keep track of the actual values that the proxy will use - // to call whatever LLM API is chosen - private actualApiBase?: string; - - // Contains extra properties that we pass along to the proxy. Originally from `env` property on LLMOptions - private configEnv?: Record; - - constructor(options: LLMOptions) { - super(options); - this.configEnv = options.env; - // This it set to `undefined` to handle the case where we are proxying requests to Azure. We pass the correct env vars - // needed to do this in `extraBodyProperties` below, but if we don't set `apiType` to `undefined`, we end up proxying to - // `/openai/deployments/` which is invalid since that URL construction happens on the proxy. - this.apiType = undefined; - this.actualApiBase = options.apiBase; - this.apiKeyLocation = options.apiKeyLocation; - this.envSecretLocations = options.envSecretLocations; - this.orgScopeId = options.orgScopeId; - this.onPremProxyUrl = options.onPremProxyUrl; - if (this.onPremProxyUrl) { - this.apiBase = new URL("model-proxy/v1/", this.onPremProxyUrl).toString(); - } - } - - static providerName = "continue-proxy"; - static defaultOptions: Partial = { - useLegacyCompletionsEndpoint: false, - }; - - get underlyingProviderName(): string { - const { provider } = parseProxyModelName(this.model); - return provider; - } - - protected extraBodyProperties(): Record { - const continueProperties: ContinueProperties = { - apiKeyLocation: this.apiKeyLocation, - envSecretLocations: this.envSecretLocations, - apiBase: this.actualApiBase, - orgScopeId: this.orgScopeId ?? null, - env: this.configEnv, - }; - return { - continueProperties, - }; - } - - getConfigurationStatus() { - if (!this.apiKeyLocation && !this.envSecretLocations) { - return LLMConfigurationStatuses.VALID; - } - - if (this.apiKeyLocation) { - const secretLocation = decodeSecretLocation(this.apiKeyLocation); - if (secretLocation.secretType === SecretType.NotFound) { - return LLMConfigurationStatuses.MISSING_API_KEY; - } - } - - if (this.envSecretLocations) { - for (const secretLocation of Object.values(this.envSecretLocations)) { - const decoded = decodeSecretLocation(secretLocation); - if (decoded.secretType === SecretType.NotFound) { - return LLMConfigurationStatuses.MISSING_ENV_SECRET; - } - } - } - - return LLMConfigurationStatuses.VALID; - } - - protected _getHeaders() { - const headers: any = super._getHeaders(); - headers["x-continue-unique-id"] = IdeInfoService.uniqueId; - headers["user-agent"] = this._getUserAgent(); - return headers; - } - - private _getUserAgent(): string { - const ideInfo = IdeInfoService.ideInfo; - const extensionVersion = ideInfo?.extensionVersion ?? "unknown"; - const ideName = ideInfo?.name ?? "unknown"; - const ideType = ideInfo?.ideType ?? "unknown"; - return `Continue/${extensionVersion} (${ideName}; ${ideType})`; - } - - supportsCompletions(): boolean { - // This was a hotfix and contains duplicate logic from class-specific completion support methods - if (this.underlyingProviderName === "vllm") { - return true; - } - // other providers that don't support completions include groq, mistral, nvidia, deepseek, etc. - // For now disabling all except vllm - return false; - } - - supportsFim(): boolean { - const { provider } = parseProxyModelName(this.model); - if (provider === "vllm") { - return false; - } - return true; - } - - async rerank(query: string, chunks: Chunk[]): Promise { - const url = new URL("rerank", this.apiBase); - const resp = await this.fetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${this.apiKey}`, - "user-agent": this._getUserAgent(), - }, - body: JSON.stringify({ - query, - documents: chunks.map((chunk) => chunk.content), - model: this.model, - ...this.extraBodyProperties(), - }), - }); - const data: any = await resp.json(); - const results = data.data.sort((a: any, b: any) => a.index - b.index); - return results.map((result: any) => result.relevance_score); - } -} - -export default ContinueProxy; diff --git a/core/llm/llms/stubs/ContinueProxy.vitest.ts b/core/llm/llms/stubs/ContinueProxy.vitest.ts deleted file mode 100644 index 20bed0ec195..00000000000 --- a/core/llm/llms/stubs/ContinueProxy.vitest.ts +++ /dev/null @@ -1,758 +0,0 @@ -import { afterEach, describe, expect, test, vi } from "vitest"; -import { ILLM } from "../../../index.js"; -import ContinueProxy from "./ContinueProxy.js"; - -vi.mock("@continuedev/config-yaml", async (importOriginal) => { - const mod = (await importOriginal()) as any; - return { - ...mod, - parseProxyModelName: vi.fn(() => ({ - provider: "test-provider", - model: "test-model", - ownerSlug: "test-owner", - packageSlug: "test-package", - })), - }; -}); - -interface LlmTestCase { - llm: ILLM; - methodToTest: keyof ILLM; - params: any[]; - expectedRequest: { - url: string; - method: string; - headers?: Record; - body?: Record; - }; - mockResponse?: any; - mockStream?: any[]; -} - -function createMockStream(mockStream: any[]) { - const encoder = new TextEncoder(); - return new ReadableStream({ - start(controller) { - for (const chunk of mockStream) { - controller.enqueue( - encoder.encode( - `data: ${ - typeof chunk === "string" ? chunk : JSON.stringify(chunk) - }\n\n`, - ), - ); - } - controller.close(); - }, - }); -} - -function setupMockFetch(mockResponse?: any, mockStream?: any[]) { - const mockFetch = vi.fn(); - - if (mockStream) { - const stream = createMockStream(mockStream); - mockFetch.mockResolvedValue( - new Response(stream, { - headers: { - "Content-Type": "text/event-stream", - }, - }), - ); - } else { - mockFetch.mockResolvedValue( - new Response(JSON.stringify(mockResponse), { - headers: { "Content-Type": "application/json" }, - }), - ); - } - - return mockFetch; -} - -function setupReadableStreamPolyfill() { - // This can be removed if https://github.com/nodejs/undici/issues/2888 is resolved - // @ts-ignore - const originalFrom = ReadableStream.from; - // @ts-ignore - ReadableStream.from = (body) => { - if (body?.source) { - return body; - } - return originalFrom(body); - }; -} - -async function executeLlmMethod( - llm: ILLM, - methodToTest: keyof ILLM, - params: any[], -) { - if (typeof (llm as any)[methodToTest] !== "function") { - throw new Error( - `Method ${String(methodToTest)} does not exist on the LLM instance.`, - ); - } - - const result = await (llm as any)[methodToTest](...params); - if (result?.next) { - for await (const _ of result) { - } - } -} - -function assertFetchCall(mockFetch: any, expectedRequest: any) { - expect(mockFetch).toHaveBeenCalledTimes(1); - const [url, options] = mockFetch.mock.calls[0]; - - expect(url.toString()).toBe(expectedRequest.url); - expect(options.method).toBe(expectedRequest.method); - - if (expectedRequest.headers) { - expect(options.headers).toEqual( - expect.objectContaining(expectedRequest.headers), - ); - } - - if (expectedRequest.body) { - const actualBody = JSON.parse(options.body as string); - expect(actualBody).toEqual(expectedRequest.body); - } -} - -async function runLlmTest(testCase: LlmTestCase) { - const { - llm, - methodToTest, - params, - expectedRequest, - mockResponse, - mockStream, - } = testCase; - - const mockFetch = setupMockFetch(mockResponse, mockStream); - setupReadableStreamPolyfill(); - - (llm as any).fetch = mockFetch; - - await executeLlmMethod(llm, methodToTest, params); - assertFetchCall(mockFetch, expectedRequest); -} - -describe("ContinueProxy", () => { - afterEach(() => { - vi.clearAllMocks(); - }); - - test("rerank should send a valid request", async () => { - const proxy = new ContinueProxy({ - apiKey: "test-api-key", - model: "test-model", - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - }); - - await runLlmTest({ - llm: proxy, - methodToTest: "rerank", - params: [ - "test query", - [{ content: "document1" }, { content: "document2" }], - ], - expectedRequest: { - url: "https://proxy.continue.dev/model-proxy/v1/rerank", - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer test-api-key", - }, - body: { - query: "test query", - documents: ["document1", "document2"], - model: "test-model", - continueProperties: { - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - orgScopeId: null, - }, - }, - }, - mockResponse: { - data: [ - { index: 0, relevance_score: 0.9 }, - { index: 1, relevance_score: 0.1 }, - ], - }, - }); - }); - - test("streamChat should send a valid request", async () => { - const proxy = new ContinueProxy({ - apiKey: "test-api-key", - model: "test-model", - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - }); - - await runLlmTest({ - llm: proxy, - methodToTest: "streamChat", - params: [[{ role: "user", content: "hello" }]], - expectedRequest: { - url: "https://proxy.continue.dev/model-proxy/v1/chat/completions", - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer test-api-key", - "api-key": "test-api-key", - "x-continue-unique-id": "NOT_UNIQUE", - }, - body: { - model: "test-model", - messages: [{ role: "user", content: "hello" }], - max_tokens: 4096, - stream: true, - continueProperties: { - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - orgScopeId: null, - }, - }, - }, - mockStream: [ - '{"choices": [{"delta": {"content": "response"}}]}', - "[DONE]", - ], - }); - }); - - test("chat should send a valid request", async () => { - const proxy = new ContinueProxy({ - apiKey: "test-api-key", - model: "test-model", - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - }); - - await runLlmTest({ - llm: proxy, - methodToTest: "chat", - params: [[{ role: "user", content: "hello" }]], - expectedRequest: { - url: "https://proxy.continue.dev/model-proxy/v1/chat/completions", - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer test-api-key", - "api-key": "test-api-key", - "x-continue-unique-id": "NOT_UNIQUE", - }, - body: { - model: "test-model", - messages: [{ role: "user", content: "hello" }], - max_tokens: 4096, - stream: true, - continueProperties: { - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - orgScopeId: null, - }, - }, - }, - mockStream: [ - '{"choices": [{"delta": {"content": "Hello! How can I help you today?"}}]}', - "[DONE]", - ], - }); - }); - - test("streamComplete should send a valid request", async () => { - const proxy = new ContinueProxy({ - apiKey: "test-api-key", - model: "test-model", - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - }); - - await runLlmTest({ - llm: proxy, - methodToTest: "streamComplete", - params: ["Complete this: Hello"], - expectedRequest: { - url: "https://proxy.continue.dev/model-proxy/v1/chat/completions", - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer test-api-key", - "api-key": "test-api-key", - "x-continue-unique-id": "NOT_UNIQUE", - }, - body: { - model: "test-model", - messages: [{ role: "user", content: "Complete this: Hello" }], - max_tokens: 4096, - stream: true, - continueProperties: { - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - orgScopeId: null, - }, - }, - }, - mockStream: [ - '{"choices": [{"delta": {"content": " world!"}}]}', - "[DONE]", - ], - }); - }); - - test("complete should send a valid request", async () => { - const proxy = new ContinueProxy({ - apiKey: "test-api-key", - model: "test-model", - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - }); - - await runLlmTest({ - llm: proxy, - methodToTest: "complete", - params: ["Complete this: Hello"], - expectedRequest: { - url: "https://proxy.continue.dev/model-proxy/v1/chat/completions", - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer test-api-key", - "api-key": "test-api-key", - "x-continue-unique-id": "NOT_UNIQUE", - }, - body: { - model: "test-model", - messages: [{ role: "user", content: "Complete this: Hello" }], - max_tokens: 4096, - stream: true, - continueProperties: { - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - orgScopeId: null, - }, - }, - }, - mockStream: [ - '{"choices": [{"delta": {"content": " world!"}}]}', - "[DONE]", - ], - }); - }); - - test("streamFim should send a valid request", async () => { - const proxy = new ContinueProxy({ - apiKey: "test-api-key", - model: "test-model", - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - }); - - await runLlmTest({ - llm: proxy, - methodToTest: "streamFim", - params: ["function test() {\n ", "\n}"], - expectedRequest: { - url: "https://proxy.continue.dev/model-proxy/v1/fim/completions", - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - "x-api-key": "test-api-key", - Authorization: "Bearer test-api-key", - }, - body: { - model: "test-model", - prompt: "function test() {\n ", - suffix: "\n}", - max_tokens: 4096, - stream: true, - continueProperties: { - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - orgScopeId: null, - }, - }, - }, - mockStream: [ - '{"choices": [{"delta": {"content": "console.log("Hello");"}}]}', - "[DONE]", - ], - }); - }); - - test("embed should send a valid request", async () => { - const proxy = new ContinueProxy({ - apiKey: "test-api-key", - model: "test-model", - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - }); - - await runLlmTest({ - llm: proxy, - methodToTest: "embed", - params: [["text to embed", "another text"]], - expectedRequest: { - url: "https://proxy.continue.dev/model-proxy/v1/embeddings", - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer test-api-key", - "api-key": "test-api-key", - }, - body: { - input: ["text to embed", "another text"], - model: "test-model", - continueProperties: { - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - orgScopeId: null, - }, - }, - }, - mockResponse: { - data: [{ embedding: [0.1, 0.2, 0.3] }, { embedding: [0.4, 0.5, 0.6] }], - }, - }); - }); - - test("listModels should send a valid request", async () => { - const proxy = new ContinueProxy({ - apiKey: "test-api-key", - model: "test-model", - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - }); - - await runLlmTest({ - llm: proxy, - methodToTest: "listModels", - params: [], - expectedRequest: { - url: "https://proxy.continue.dev/model-proxy/v1/models", - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer test-api-key", - "api-key": "test-api-key", - }, - }, - mockResponse: { - data: [{ id: "gpt-4" }, { id: "gpt-3.5-turbo" }], - }, - }); - }); - - describe("Different provider configurations", () => { - test("should handle on-prem proxy URL", async () => { - const proxy = new ContinueProxy({ - apiKey: "test-api-key", - model: "test-model", - onPremProxyUrl: "https://my-proxy.company.com/", - }); - - await runLlmTest({ - llm: proxy, - methodToTest: "rerank", - params: [ - "test query", - [{ content: "document1" }, { content: "document2" }], - ], - expectedRequest: { - url: "https://my-proxy.company.com/model-proxy/v1/rerank", - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer test-api-key", - }, - body: { - query: "test query", - documents: ["document1", "document2"], - model: "test-model", - continueProperties: { - apiBase: undefined, - apiKeyLocation: undefined, - env: undefined, - envSecretLocations: undefined, - orgScopeId: null, - }, - }, - }, - mockResponse: { - data: [ - { index: 0, relevance_score: 0.9 }, - { index: 1, relevance_score: 0.1 }, - ], - }, - }); - }); - - test("should handle environment variables", async () => { - const proxy = new ContinueProxy({ - apiKey: "test-api-key", - model: "test-model", - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - env: { - CUSTOM_VAR: "custom-value", - ANOTHER_VAR: "another-value", - }, - }); - - await runLlmTest({ - llm: proxy, - methodToTest: "streamChat", - params: [[{ role: "user", content: "hello" }]], - expectedRequest: { - url: "https://proxy.continue.dev/model-proxy/v1/chat/completions", - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer test-api-key", - "api-key": "test-api-key", - "x-continue-unique-id": "NOT_UNIQUE", - }, - body: { - model: "test-model", - messages: [{ role: "user", content: "hello" }], - max_tokens: 4096, - stream: true, - continueProperties: { - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - env: { - CUSTOM_VAR: "custom-value", - ANOTHER_VAR: "another-value", - }, - orgScopeId: null, - }, - }, - }, - mockStream: [ - '{"choices": [{"delta": {"content": "response"}}]}', - "[DONE]", - ], - }); - }); - - test("should handle API key location", async () => { - const proxy = new ContinueProxy({ - apiKey: "test-api-key", - model: "test-model", - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - apiKeyLocation: "env:OPENAI_API_KEY", - }); - - await runLlmTest({ - llm: proxy, - methodToTest: "embed", - params: [["test text"]], - expectedRequest: { - url: "https://proxy.continue.dev/model-proxy/v1/embeddings", - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer test-api-key", - "api-key": "test-api-key", - }, - body: { - input: ["test text"], - model: "test-model", - continueProperties: { - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - apiKeyLocation: "env:OPENAI_API_KEY", - env: undefined, - envSecretLocations: undefined, - orgScopeId: null, - }, - }, - }, - mockResponse: { - data: [{ embedding: [0.1, 0.2, 0.3] }], - }, - }); - }); - - test("should handle env secret locations", async () => { - const proxy = new ContinueProxy({ - apiKey: "test-api-key", - model: "test-model", - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - envSecretLocations: { - AZURE_API_KEY: "env:AZURE_API_KEY", - AZURE_ENDPOINT: "env:AZURE_ENDPOINT", - }, - }); - - await runLlmTest({ - llm: proxy, - methodToTest: "streamFim", - params: ["const x = ", ";"], - expectedRequest: { - url: "https://proxy.continue.dev/model-proxy/v1/fim/completions", - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - "x-api-key": "test-api-key", - Authorization: "Bearer test-api-key", - }, - body: { - model: "test-model", - prompt: "const x = ", - suffix: ";", - max_tokens: 4096, - stream: true, - continueProperties: { - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - envSecretLocations: { - AZURE_API_KEY: "env:AZURE_API_KEY", - AZURE_ENDPOINT: "env:AZURE_ENDPOINT", - }, - orgScopeId: null, - }, - }, - }, - mockStream: ['{"choices": [{"delta": {"content": "42"}}]}', "[DONE]"], - }); - }); - - test("should handle organization scope ID", async () => { - const proxy = new ContinueProxy({ - apiKey: "test-api-key", - model: "test-model", - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - orgScopeId: "org_12345", - }); - - await runLlmTest({ - llm: proxy, - methodToTest: "listModels", - params: [], - expectedRequest: { - url: "https://proxy.continue.dev/model-proxy/v1/models", - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer test-api-key", - "api-key": "test-api-key", - }, - }, - mockResponse: { - data: [{ id: "gpt-4" }], - }, - }); - }); - }); - - describe("Edge cases", () => { - test("should handle empty chunks array for rerank", async () => { - const proxy = new ContinueProxy({ - apiKey: "test-api-key", - model: "test-model", - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - }); - - await runLlmTest({ - llm: proxy, - methodToTest: "rerank", - params: ["test query", []], - expectedRequest: { - url: "https://proxy.continue.dev/model-proxy/v1/rerank", - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer test-api-key", - }, - body: { - query: "test query", - documents: [], - model: "test-model", - continueProperties: { - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - apiKeyLocation: undefined, - env: undefined, - envSecretLocations: undefined, - orgScopeId: null, - }, - }, - }, - mockResponse: { - data: [], - }, - }); - }); - - test("should handle complex chat messages", async () => { - const proxy = new ContinueProxy({ - apiKey: "test-api-key", - model: "test-model", - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - }); - - const complexMessages = [ - { role: "system", content: "You are a helpful assistant." }, - { role: "user", content: "Hello!" }, - { role: "assistant", content: "Hi there! How can I help you today?" }, - { role: "user", content: "What's the weather like?" }, - ]; - - await runLlmTest({ - llm: proxy, - methodToTest: "streamChat", - params: [complexMessages], - expectedRequest: { - url: "https://proxy.continue.dev/model-proxy/v1/chat/completions", - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer test-api-key", - "api-key": "test-api-key", - "x-continue-unique-id": "NOT_UNIQUE", - }, - body: { - model: "test-model", - messages: complexMessages, - max_tokens: 4096, - stream: true, - continueProperties: { - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - orgScopeId: null, - }, - }, - }, - mockStream: [ - '{"choices": [{"delta": {"content": "I don\'t have access to current weather data."}}]}', - "[DONE]", - ], - }); - }); - - test("should handle single embedding chunk", async () => { - const proxy = new ContinueProxy({ - apiKey: "test-api-key", - model: "test-model", - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - }); - - await runLlmTest({ - llm: proxy, - methodToTest: "embed", - params: [["single text"]], - expectedRequest: { - url: "https://proxy.continue.dev/model-proxy/v1/embeddings", - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer test-api-key", - "api-key": "test-api-key", - }, - body: { - input: ["single text"], - model: "test-model", - continueProperties: { - apiBase: "https://proxy.continue.dev/model-proxy/v1/", - apiKeyLocation: undefined, - env: undefined, - envSecretLocations: undefined, - orgScopeId: null, - }, - }, - }, - mockResponse: { - data: [{ embedding: [0.1, 0.2, 0.3] }], - }, - }); - }); - }); -}); diff --git a/core/llm/llms/test/supportsFim.test.ts b/core/llm/llms/test/supportsFim.test.ts index 6cb7525aecf..415a5212b74 100644 --- a/core/llm/llms/test/supportsFim.test.ts +++ b/core/llm/llms/test/supportsFim.test.ts @@ -4,35 +4,10 @@ import Deepseek from "../Deepseek.js"; import FunctionNetwork from "../FunctionNetwork.js"; import Mistral from "../Mistral.js"; import OpenAI from "../OpenAI.js"; -import ContinueProxy from "../stubs/ContinueProxy.js"; import Vllm from "../Vllm.js"; -// Mock the parseProxyModelName function -const mockParseProxyModelName = jest.fn(); -jest.mock("@continuedev/config-yaml", () => ({ - parseProxyModelName: mockParseProxyModelName, - decodeSecretLocation: jest.fn(), - SecretType: { NotFound: "not-found" }, -})); - // Test cases: [LLM class, model, expected supportsFim result, description] const testCases: [any, string, boolean, string][] = [ - [ContinueProxy, "owner/package/vllm/some-model", false, "vllm provider"], - [ContinueProxy, "owner/package/openai/gpt-4", true, "openai provider"], - [ - ContinueProxy, - "owner/package/anthropic/claude-3", - true, - "anthropic provider", - ], - [ContinueProxy, "owner/package/cohere/command-r", true, "cohere provider"], - [ - ContinueProxy, - "owner/package/unknown-provider/some-model", - true, - "unknown provider", - ], - [ContinueProxy, "owner/package/groq/llama-model", true, "groq provider"], [Vllm, "any-model", false, "Vllm"], [Anthropic, "claude-3-5-sonnet-latest", false, "Anthropic"], [FunctionNetwork, "any-model", false, "FunctionNetwork"], diff --git a/core/llm/streamChat.ts b/core/llm/streamChat.ts index 013222ec0cd..bd4aebb22e9 100644 --- a/core/llm/streamChat.ts +++ b/core/llm/streamChat.ts @@ -1,12 +1,10 @@ import { fetchwithRequestOptions } from "@continuedev/fetch"; import { ChatMessage, IDE, PromptLog } from ".."; import { ConfigHandler } from "../config/ConfigHandler"; -import { usesCreditsBasedApiKey } from "../config/usesFreeTrialApiKey"; import { FromCoreProtocol, ToCoreProtocol } from "../protocol"; import { IMessenger, Message } from "../protocol/messenger"; import { TTS } from "../util/tts"; -import { isOutOfStarterCredits } from "./utils/starterCredits"; export async function* llmStreamChat( configHandler: ConfigHandler, @@ -136,8 +134,6 @@ export async function* llmStreamChat( void TTS.read(next.value?.completion); } - void checkForOutOfStarterCredits(configHandler, messenger); - if (!next.done) { throw new Error("Will never happen"); } @@ -149,24 +145,3 @@ export async function* llmStreamChat( throw error; } } - -async function checkForOutOfStarterCredits( - configHandler: ConfigHandler, - messenger: IMessenger, -) { - try { - const { config } = await configHandler.getSerializedConfig(); - const creditStatus = - await configHandler.controlPlaneClient.getCreditStatus(); - - if ( - config && - creditStatus && - isOutOfStarterCredits(usesCreditsBasedApiKey(config), creditStatus) - ) { - void messenger.request("freeTrialExceeded", undefined); - } - } catch (error) { - console.error("Error checking free trial status:", error); - } -} diff --git a/core/llm/toolSupport.test.ts b/core/llm/toolSupport.test.ts index 5be68bfa21c..f545adc67b1 100644 --- a/core/llm/toolSupport.test.ts +++ b/core/llm/toolSupport.test.ts @@ -2,70 +2,6 @@ import { PROVIDER_TOOL_SUPPORT, isRecommendedAgentModel } from "./toolSupport"; describe("PROVIDER_TOOL_SUPPORT", () => { - describe("continue-proxy", () => { - const supportsFn = PROVIDER_TOOL_SUPPORT["continue-proxy"]; - - it("should return true for Claude 3.5 models", () => { - expect( - supportsFn("ownerSlug/packageSlug/anthropic/claude-3-5-sonnet"), - ).toBe(true); - expect( - supportsFn("ownerSlug/packageSlug/anthropic/claude-3.5-sonnet"), - ).toBe(true); - }); - - it("should return true for Claude 3.7 models", () => { - expect( - supportsFn("ownerSlug/packageSlug/anthropic/claude-3-7-haiku"), - ).toBe(true); - expect( - supportsFn("ownerSlug/packageSlug/anthropic/claude-3.7-sonnet"), - ).toBe(true); - }); - - it("should return true for GPT-4 models", () => { - expect(supportsFn("ownerSlug/packageSlug/openai/gpt-4-turbo")).toBe(true); - expect( - supportsFn("ownerSlug/packageSlug/openai/gpt-4-1106-preview"), - ).toBe(true); - }); - - it("should return true for Gemma models", () => { - expect(supportsFn("ownerSlug/packageSlug/openai/gemma")).toBe(true); - expect(supportsFn("ownerSlug/packageSlug/openai/gemma3")).toBe(true); - }); - - it("should return true for O3 models", () => { - expect(supportsFn("ownerSlug/packageSlug/openai/o3-preview")).toBe(true); - }); - - it("should return true for Gemini models", () => { - expect(supportsFn("ownerSlug/packageSlug/gemini/gemini-pro")).toBe(true); - expect(supportsFn("ownerSlug/packageSlug/gemini/gemini-2.5-pro")).toBe( - true, - ); - }); - - it("should return false for unsupported models", () => { - expect(supportsFn("ownerSlug/packageSlug/openai/gpt-3.5-turbo")).toBe( - false, - ); - expect(supportsFn("ownerSlug/packageSlug/anthropic/claude-2")).toBe( - false, - ); - expect(supportsFn("ownerSlug/packageSlug/together/llama-3")).toBe(false); - }); - - it("should handle case insensitivity", () => { - expect( - supportsFn("ownerSlug/packageSlug/anthropic/CLAUDE-3-5-sonnet"), - ).toBe(true); - expect(supportsFn("ownerSlug/packageSlug/openai/GPT-4-turbo")).toBe(true); - expect(supportsFn("ownerSlug/packageSlug/openai/Gemma3")).toBe(true); - expect(supportsFn("ownerSlug/packageSlug/gemini/GEMINI-pro")).toBe(true); - }); - }); - describe("anthropic", () => { const supportsFn = PROVIDER_TOOL_SUPPORT["anthropic"]; @@ -395,7 +331,6 @@ describe("PROVIDER_TOOL_SUPPORT", () => { describe("edge cases", () => { it("should handle empty model names", () => { - expect(PROVIDER_TOOL_SUPPORT["continue-proxy"]("")).toBe(false); expect(PROVIDER_TOOL_SUPPORT["anthropic"]("")).toBe(false); expect(PROVIDER_TOOL_SUPPORT["openai"]("")).toBe(false); expect(PROVIDER_TOOL_SUPPORT["gemini"]("")).toBe(false); diff --git a/core/llm/toolSupport.ts b/core/llm/toolSupport.ts index 099424c61ab..55aa739040d 100644 --- a/core/llm/toolSupport.ts +++ b/core/llm/toolSupport.ts @@ -1,23 +1,7 @@ -import { parseProxyModelName } from "@continuedev/config-yaml"; import { ModelDescription } from ".."; export const PROVIDER_TOOL_SUPPORT: Record boolean> = { - "continue-proxy": (model) => { - try { - const { provider, model: _model } = parseProxyModelName(model); - if (provider && _model && provider !== "continue-proxy") { - const fn = PROVIDER_TOOL_SUPPORT[provider]; - if (fn) { - return fn(_model); - } - } - } catch (e) {} - - return ["claude", "gpt-4", "o3", "gemini", "gemma"].some((part) => - model.toLowerCase().startsWith(part), - ); - }, anthropic: (model) => { if (model.includes("claude-2") || model.includes("claude-instant")) { return false; diff --git a/core/llm/utils/starterCredits.ts b/core/llm/utils/starterCredits.ts deleted file mode 100644 index 9f0d021d74b..00000000000 --- a/core/llm/utils/starterCredits.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { CreditStatus } from "../../control-plane/client"; - -export function isOutOfStarterCredits( - usingModelsAddOnApiKey: boolean, - creditStatus: CreditStatus, -): boolean { - return ( - usingModelsAddOnApiKey && - !creditStatus.hasCredits && - !creditStatus.hasPurchasedCredits - ); -} diff --git a/core/protocol/core.ts b/core/protocol/core.ts index 758a08faf90..6c4b79a213d 100644 --- a/core/protocol/core.ts +++ b/core/protocol/core.ts @@ -45,11 +45,6 @@ import { AutocompleteCodeSnippet } from "../autocomplete/snippets/types"; import { GetLspDefinitionsFunction } from "../autocomplete/types"; import { ConfigHandler } from "../config/ConfigHandler"; import { SerializedOrgWithProfiles } from "../config/ProfileLifecycleManager"; -import { - ControlPlaneEnv, - ControlPlaneSessionInfo, -} from "../control-plane/AuthTypes"; -import { CreditStatus, RemoteSessionMetadata } from "../control-plane/client"; import { ProcessedItem } from "../nextEdit/NextEditPrefetchQueue"; import { NextEditOutcome } from "../nextEdit/types"; import { ContinueErrorReason } from "../util/errors"; @@ -72,13 +67,9 @@ export type ToCoreFromIdeOrWebviewProtocol = { cancelApply: [undefined, void]; // History - "history/list": [ - ListHistoryOptions, - (BaseSessionMetadata | RemoteSessionMetadata)[], - ]; + "history/list": [ListHistoryOptions, BaseSessionMetadata[]]; "history/delete": [{ id: string }, void]; "history/load": [{ id: string }, Session]; - "history/loadRemote": [{ remoteId: string }, Session]; "history/save": [Session, void]; "history/share": [{ id: string; outputDir?: string }, void]; "history/clear": [undefined, void]; @@ -338,14 +329,7 @@ export type ToCoreFromIdeOrWebviewProtocol = { }, ]; "clipboardCache/add": [{ content: string }, void]; - "controlPlane/openUrl": [{ path: string; orgSlug?: string }, void]; - "controlPlane/getEnvironment": [undefined, ControlPlaneEnv]; - "controlPlane/getCreditStatus": [undefined, CreditStatus | null]; isItemTooBig: [{ item: ContextItemWithId }, boolean]; - didChangeControlPlaneSessionInfo: [ - { sessionInfo: ControlPlaneSessionInfo | undefined }, - void, - ]; "process/markAsBackgrounded": [{ toolCallId: string }, void]; "process/isBackgrounded": [{ toolCallId: string }, boolean]; "process/killTerminalProcess": [{ toolCallId: string }, void]; diff --git a/core/protocol/ide.ts b/core/protocol/ide.ts index 73492f18077..5aec1ea9a2a 100644 --- a/core/protocol/ide.ts +++ b/core/protocol/ide.ts @@ -14,8 +14,6 @@ import type { TerminalOptions, Thread, } from "../"; -import { ControlPlaneSessionInfo } from "../control-plane/AuthTypes"; - export interface GetGhTokenArgs { force?: boolean; } @@ -88,11 +86,6 @@ export type ToIdeFromWebviewOrCoreProtocol = { getReferences: [{ location: Location }, RangeInFile[]]; getDocumentSymbols: [{ textDocumentIdentifier: string }, DocumentSymbol[]]; - getControlPlaneSessionInfo: [ - { silent: boolean; useOnboarding: boolean }, - ControlPlaneSessionInfo | undefined, - ]; - logoutOfControlPlane: [undefined, void]; reportError: [any, void]; closeSidebar: [undefined, void]; }; diff --git a/core/protocol/ideWebview.ts b/core/protocol/ideWebview.ts index f1d2692920d..79e7de10b4f 100644 --- a/core/protocol/ideWebview.ts +++ b/core/protocol/ideWebview.ts @@ -6,10 +6,8 @@ import { AddToChatPayload, ApplyState, ApplyToFilePayload, - ContextItemWithId, HighlightedCodePayload, MessageContent, - RangeInFile, RangeInFileWithContents, SetCodeToEditPayload, ShowFilePayload, @@ -52,38 +50,6 @@ export type ToIdeFromWebviewProtocol = ToIdeFromWebviewOrCoreProtocol & { "edit/addCurrentSelection": [undefined, void]; "edit/clearDecorations": [undefined, void]; "session/share": [{ sessionId: string }, void]; - createBackgroundAgent: [ - { - content: MessageContent; - contextItems: ContextItemWithId[]; - selectedCode: RangeInFile[]; - organizationId?: string; - agent?: string; - }, - void, - ]; - listBackgroundAgents: [ - { organizationId?: string; limit?: number }, - { - agents: Array<{ - id: string; - name: string | null; - status: string; - repoUrl: string; - createdAt: string; - metadata?: { - github_repo?: string; - }; - }>; - totalCount: number; - }, - ]; - openAgentLocally: [ - { - agentSessionId: string; - }, - void, - ]; }; export type ToWebviewFromIdeProtocol = ToWebviewFromIdeOrCoreProtocol & { @@ -100,7 +66,6 @@ export type ToWebviewFromIdeProtocol = ToWebviewFromIdeOrCoreProtocol & { focusContinueSessionId: [{ sessionId: string | undefined }, void]; newSession: [undefined, void]; - loadAgentSession: [{ session: any }, void]; setTheme: [{ theme: any }, void]; setColors: [{ [key: string]: string }, void]; "jetbrains/editorInsetRefresh": [undefined, void]; diff --git a/core/protocol/messenger/reverseMessageIde.ts b/core/protocol/messenger/reverseMessageIde.ts index 9ce82db54a9..0a1abfba040 100644 --- a/core/protocol/messenger/reverseMessageIde.ts +++ b/core/protocol/messenger/reverseMessageIde.ts @@ -109,11 +109,6 @@ export class ReverseMessageIde { return this.ide.showLines(data.filepath, data.startLine, data.endLine); }); - this.on("getControlPlaneSessionInfo", async (msg) => { - // Not supported in testing - return undefined; - }); - this.on("writeFile", (data) => { return this.ide.writeFile(data.path, data.contents); }); diff --git a/core/protocol/passThrough.ts b/core/protocol/passThrough.ts index 9d752b31178..0e45deb97c6 100644 --- a/core/protocol/passThrough.ts +++ b/core/protocol/passThrough.ts @@ -13,7 +13,6 @@ export const WEBVIEW_TO_CORE_PASS_THROUGH: (keyof ToCoreFromWebviewProtocol)[] = "history/list", "history/delete", "history/load", - "history/loadRemote", "history/save", "history/clear", "devdata/log", @@ -84,9 +83,6 @@ export const WEBVIEW_TO_CORE_PASS_THROUGH: (keyof ToCoreFromWebviewProtocol)[] = "tools/call", "tools/evaluatePolicy", "tools/preprocessArgs", - "controlPlane/getEnvironment", - "controlPlane/getCreditStatus", - "controlPlane/openUrl", "isItemTooBig", "process/markAsBackgrounded", "process/isBackgrounded", diff --git a/core/protocol/webview.ts b/core/protocol/webview.ts index 1a8d40313fa..50c935fc5cd 100644 --- a/core/protocol/webview.ts +++ b/core/protocol/webview.ts @@ -1,6 +1,5 @@ import { ConfigResult } from "@continuedev/config-yaml"; import { SerializedOrgWithProfiles } from "../config/ProfileLifecycleManager.js"; -import { ControlPlaneSessionInfo } from "../control-plane/AuthTypes.js"; import type { BrowserSerializedContinueConfig, ContextItemWithId, @@ -41,7 +40,7 @@ export type ToWebviewFromIdeOrCoreProtocol = { getWebviewHistoryLength: [undefined, number]; getCurrentSessionId: [undefined, string]; "jetbrains/setColors": [Record, void]; - sessionUpdate: [{ sessionInfo: ControlPlaneSessionInfo | undefined }, void]; + sessionUpdate: [{ sessionInfo: any | undefined }, void]; toolCallPartialOutput: [{ toolCallId: string; contextItems: any[] }, void]; freeTrialExceeded: [undefined, void]; }; diff --git a/core/test/fixtures.ts b/core/test/fixtures.ts index 51c90d9ef1a..d288498bc77 100644 --- a/core/test/fixtures.ts +++ b/core/test/fixtures.ts @@ -1,5 +1,4 @@ import { ConfigHandler } from "../config/ConfigHandler"; -import { ControlPlaneClient } from "../control-plane/client"; import Mock from "../llm/llms/Mock"; import { LLMLogger } from "../llm/logger"; import FileSystemIde from "../util/filesystem"; @@ -10,16 +9,7 @@ export const testIde = new FileSystemIde(TEST_DIR); export const ideSettingsPromise = testIde.getIdeSettings(); -export const testControlPlaneClient = new ControlPlaneClient( - Promise.resolve(undefined), - testIde, -); - -export const testConfigHandler = new ConfigHandler( - testIde, - new LLMLogger(), - Promise.resolve(undefined), -); +export const testConfigHandler = new ConfigHandler(testIde, new LLMLogger()); export const testLLM = new Mock({ model: "mock-model", diff --git a/core/tools/definitions/toolDefinitions.test.ts b/core/tools/definitions/toolDefinitions.test.ts index 6b64f542ca6..250ddfed0b3 100644 --- a/core/tools/definitions/toolDefinitions.test.ts +++ b/core/tools/definitions/toolDefinitions.test.ts @@ -7,7 +7,6 @@ describe("Tool Definitions", () => { const mockParams: ConfigDependentToolParams = { rules: [], enableExperimentalTools: false, - isSignedIn: false, isRemote: false, modelName: "a model", ide: {} as any, diff --git a/core/tools/index.ts b/core/tools/index.ts index 15d52d3aca4..f03eca03161 100644 --- a/core/tools/index.ts +++ b/core/tools/index.ts @@ -18,16 +18,13 @@ export const getBaseToolDefinitions = () => [ export const getConfigDependentToolDefinitions = async ( params: ConfigDependentToolParams, ): Promise => { - const { modelName, isSignedIn, enableExperimentalTools, isRemote } = params; + const { modelName, enableExperimentalTools, isRemote } = params; const tools: Tool[] = []; tools.push(await toolDefinitions.requestRuleTool(params)); tools.push(await toolDefinitions.readSkillTool(params)); - if (isSignedIn) { - // Web search is only available for signed-in users - tools.push(toolDefinitions.searchWebTool); - } + tools.push(toolDefinitions.searchWebTool); if (enableExperimentalTools) { tools.push( diff --git a/core/tools/searchWebGating.vitest.ts b/core/tools/searchWebGating.vitest.ts index e49984bea60..871aae47d71 100644 --- a/core/tools/searchWebGating.vitest.ts +++ b/core/tools/searchWebGating.vitest.ts @@ -2,35 +2,18 @@ import { expect, test } from "vitest"; import { BuiltInToolNames } from "./builtIn"; import { getConfigDependentToolDefinitions } from "./index"; -test("searchWeb tool is only available when user is signed in", async () => { - // Test with signed-in user - const signedInTools = await getConfigDependentToolDefinitions({ +test("searchWeb tool is always available", async () => { + const tools = await getConfigDependentToolDefinitions({ rules: [], enableExperimentalTools: false, - isSignedIn: true, isRemote: false, modelName: "", ide: {} as any, }); - const searchWebToolSignedIn = signedInTools.find( + const searchWebTool = tools.find( (tool) => tool.function.name === BuiltInToolNames.SearchWeb, ); - expect(searchWebToolSignedIn).toBeDefined(); - expect(searchWebToolSignedIn?.displayTitle).toBe("Search Web"); - - // Test with non-signed-in user - const notSignedInTools = await getConfigDependentToolDefinitions({ - rules: [], - enableExperimentalTools: false, - isSignedIn: false, - isRemote: false, - modelName: "", - ide: {} as any, - }); - - const searchWebToolNotSignedIn = notSignedInTools.find( - (tool) => tool.function.name === BuiltInToolNames.SearchWeb, - ); - expect(searchWebToolNotSignedIn).toBeUndefined(); + expect(searchWebTool).toBeDefined(); + expect(searchWebTool?.displayTitle).toBe("Search Web"); }); diff --git a/core/util/constants.ts b/core/util/constants.ts index 359a2571959..9418c48ed42 100644 --- a/core/util/constants.ts +++ b/core/util/constants.ts @@ -1,3 +1,5 @@ +export const EXTENSION_NAME = "continue"; + export const NEW_SESSION_TITLE = "New Session"; export const GITHUB_LINK = diff --git a/extensions/cli/src/configLoader.ts b/extensions/cli/src/configLoader.ts index 2be926ac162..8d2f5199ac5 100644 --- a/extensions/cli/src/configLoader.ts +++ b/extensions/cli/src/configLoader.ts @@ -408,8 +408,6 @@ export async function unrollPackageIdentifiersAsConfigYaml( }, "name: Agent\nschema: v1\nversion: 0.0.1", new RegistryClient({ - accessToken: accessToken ?? undefined, - apiBase: env.apiBase, rootPath: undefined, // TODO verify this doesn't cause issues with file blocks }), { @@ -444,8 +442,6 @@ async function unrollAssistantWithConfig( const unrollResult = await unrollAssistant( packageIdentifier, new RegistryClient({ - accessToken: accessToken ?? undefined, - apiBase: env.apiBase, rootPath: packageIdentifier.uriType === "file" ? dirname(packageIdentifier.fileUri) diff --git a/extensions/cli/src/util/yamlConfigUpdater.ts b/extensions/cli/src/util/yamlConfigUpdater.ts index 08b1c27fb2a..2b749b93c76 100644 --- a/extensions/cli/src/util/yamlConfigUpdater.ts +++ b/extensions/cli/src/util/yamlConfigUpdater.ts @@ -39,7 +39,7 @@ export function updateAnthropicModelInYaml( // If document is empty or has no content, create a new config if (!doc.contents || doc.contents === null) { const defaultConfig: ConfigStructure = { - name: "Local Config", + name: "Main Config", version: "1.0.0", schema: "v1", models: [newModel], @@ -75,7 +75,7 @@ export function updateAnthropicModelInYaml( } catch { // If parsing fails completely, create a new config const defaultConfig: ConfigStructure = { - name: "Local Config", + name: "Main Config", version: "1.0.0", schema: "v1", models: [newModel], diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/constants/MessageTypes.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/constants/MessageTypes.kt index b7224d8d5de..145b7c2d7a2 100644 --- a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/constants/MessageTypes.kt +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/constants/MessageTypes.kt @@ -34,8 +34,6 @@ class MessageTypes { "getFileStats", "insertAtCursor", "applyToFile", - "getControlPlaneSessionInfo", - "logoutOfControlPlane", "showToast", "openUrl", "isWorkspaceRemote", @@ -148,7 +146,6 @@ class MessageTypes { "tools/call", "tools/evaluatePolicy", "tools/preprocessArgs", - "controlPlane/openUrl", "isItemTooBig", "process/markAsBackgrounded", "process/isBackgrounded", diff --git a/extensions/intellij/src/testIntegration/kotlin/com/github/continuedev/continueintellijextension/test-continue/config.json b/extensions/intellij/src/testIntegration/kotlin/com/github/continuedev/continueintellijextension/test-continue/config.json index 0160335f519..52ce3f15bf4 100644 --- a/extensions/intellij/src/testIntegration/kotlin/com/github/continuedev/continueintellijextension/test-continue/config.json +++ b/extensions/intellij/src/testIntegration/kotlin/com/github/continuedev/continueintellijextension/test-continue/config.json @@ -14,9 +14,6 @@ "model": "this field is not used" } ], - "analytics": { - "provider": "continue-proxy" - }, "tabAutocompleteModel": { "title": "TEST LLM", "provider": "test", diff --git a/extensions/vscode/config_schema.json b/extensions/vscode/config_schema.json index 18793b803a0..a355af3f358 100644 --- a/extensions/vscode/config_schema.json +++ b/extensions/vscode/config_schema.json @@ -2854,7 +2854,6 @@ "ollama", "openai", "cohere", - "continue-proxy", "gemini", "voyage", "nvidia", diff --git a/extensions/vscode/e2e/test-continue/config.json b/extensions/vscode/e2e/test-continue/config.json index 814552d9115..754e3d614cc 100644 --- a/extensions/vscode/e2e/test-continue/config.json +++ b/extensions/vscode/e2e/test-continue/config.json @@ -69,9 +69,6 @@ } ], "systemMessage": "TEST_SYS_MSG", - "analytics": { - "provider": "continue-proxy" - }, "tabAutocompleteModel": { "title": "TEST LLM", "provider": "test", diff --git a/extensions/vscode/src/ContinueConsoleWebviewViewProvider.ts b/extensions/vscode/src/ContinueConsoleWebviewViewProvider.ts index 766f9100c5e..0bee3ea3f17 100644 --- a/extensions/vscode/src/ContinueConsoleWebviewViewProvider.ts +++ b/extensions/vscode/src/ContinueConsoleWebviewViewProvider.ts @@ -1,5 +1,5 @@ import { LLMInteractionItem } from "core"; -import { EXTENSION_NAME } from "core/control-plane/env"; +import { EXTENSION_NAME } from "core/util/constants"; import { LLMLogger } from "core/llm/logger"; import * as vscode from "vscode"; diff --git a/extensions/vscode/src/VsCodeIde.ts b/extensions/vscode/src/VsCodeIde.ts index 9e5853afa3b..9770d318898 100644 --- a/extensions/vscode/src/VsCodeIde.ts +++ b/extensions/vscode/src/VsCodeIde.ts @@ -2,7 +2,7 @@ import * as child_process from "node:child_process"; import { exec } from "node:child_process"; import { Range } from "core"; -import { EXTENSION_NAME } from "core/control-plane/env"; +import { EXTENSION_NAME } from "core/util/constants"; import { DEFAULT_IGNORES, defaultIgnoresGlob } from "core/indexing/ignore"; import * as URI from "uri-js"; import * as vscode from "vscode"; diff --git a/extensions/vscode/src/activation/InlineTipManager.ts b/extensions/vscode/src/activation/InlineTipManager.ts index 4a544147cc3..5e88c5ad16d 100644 --- a/extensions/vscode/src/activation/InlineTipManager.ts +++ b/extensions/vscode/src/activation/InlineTipManager.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { EXTENSION_NAME } from "core/control-plane/env"; +import { EXTENSION_NAME } from "core/util/constants"; // @ts-ignore import svgBuilder from "svg-builder"; import * as vscode from "vscode"; diff --git a/extensions/vscode/src/activation/NextEditWindowManager.ts b/extensions/vscode/src/activation/NextEditWindowManager.ts index 07b40795ad6..7ecf543ad87 100644 --- a/extensions/vscode/src/activation/NextEditWindowManager.ts +++ b/extensions/vscode/src/activation/NextEditWindowManager.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { EXTENSION_NAME } from "core/control-plane/env"; +import { EXTENSION_NAME } from "core/util/constants"; // @ts-ignore import * as vscode from "vscode"; diff --git a/extensions/vscode/src/activation/NextEditWindowManager.vitest.ts b/extensions/vscode/src/activation/NextEditWindowManager.vitest.ts index 9766955cc7a..d2d4ff85795 100644 --- a/extensions/vscode/src/activation/NextEditWindowManager.vitest.ts +++ b/extensions/vscode/src/activation/NextEditWindowManager.vitest.ts @@ -77,7 +77,7 @@ vi.mock("vscode", () => ({ }, })); -vi.mock("core/control-plane/env", () => ({ +vi.mock("core/util/constants", () => ({ EXTENSION_NAME: "continue", })); diff --git a/extensions/vscode/src/autocomplete/statusBar.ts b/extensions/vscode/src/autocomplete/statusBar.ts index 106a4fac258..dda4c8405e7 100644 --- a/extensions/vscode/src/autocomplete/statusBar.ts +++ b/extensions/vscode/src/autocomplete/statusBar.ts @@ -1,5 +1,5 @@ import { ILLM } from "core"; -import { EXTENSION_NAME } from "core/control-plane/env"; +import { EXTENSION_NAME } from "core/util/constants"; import * as vscode from "vscode"; import { Battery } from "../util/battery"; diff --git a/extensions/vscode/src/commands.ts b/extensions/vscode/src/commands.ts index 9328b6601d0..effd2044179 100644 --- a/extensions/vscode/src/commands.ts +++ b/extensions/vscode/src/commands.ts @@ -4,7 +4,7 @@ import * as fs from "node:fs"; import { ContextMenuConfig, ILLM, ModelInstaller } from "core"; import { CompletionProvider } from "core/autocomplete/CompletionProvider"; import { ConfigHandler } from "core/config/ConfigHandler"; -import { EXTENSION_NAME } from "core/control-plane/env"; +import { EXTENSION_NAME } from "core/util/constants"; import { Core } from "core/core"; import { walkDirAsync } from "core/indexing/walkDir"; import { isModelInstaller } from "core/llm"; diff --git a/extensions/vscode/src/extension/VsCodeExtension.ts b/extensions/vscode/src/extension/VsCodeExtension.ts index 962c4ec01b5..ae6d62a047c 100644 --- a/extensions/vscode/src/extension/VsCodeExtension.ts +++ b/extensions/vscode/src/extension/VsCodeExtension.ts @@ -3,7 +3,7 @@ import path from "path"; import { IContextProvider } from "core"; import { ConfigHandler } from "core/config/ConfigHandler"; -import { EXTENSION_NAME, getControlPlaneEnv } from "core/control-plane/env"; +import { EXTENSION_NAME } from "core/util/constants"; import { Core } from "core/core"; import { FromCoreProtocol, ToCoreProtocol } from "core/protocol"; import { InProcessMessenger } from "core/protocol/messenger"; @@ -30,12 +30,7 @@ import { registerAllCodeLensProviders } from "../lang-server/codeLens"; import { registerAllPromptFilesCompletionProviders } from "../lang-server/promptFileCompletions"; import EditDecorationManager from "../quickEdit/EditDecorationManager"; import { QuickEdit } from "../quickEdit/QuickEditQuickPick"; -import { setupRemoteConfigSync } from "../stubs/activation"; import { UriEventHandler } from "../stubs/uriHandler"; -import { - getControlPlaneSessionInfo, - WorkOsAuthProvider, -} from "../stubs/WorkOsAuthProvider"; import { Battery } from "../util/battery"; import { FileSearch } from "../util/FileSearch"; import { VsCodeIdeUtils } from "../util/ideUtils"; @@ -80,7 +75,6 @@ export class VsCodeExtension { webviewProtocolPromise: Promise; private core: Core; private battery: Battery; - private workOsAuthProvider: WorkOsAuthProvider; private fileSearch: FileSearch; private uriHandler = new UriEventHandler(); private completionProvider: ContinueCompletionProvider; @@ -176,12 +170,6 @@ export class VsCodeExtension { } constructor(context: vscode.ExtensionContext) { - // Register auth provider - this.workOsAuthProvider = new WorkOsAuthProvider(context, this.uriHandler); - - void this.workOsAuthProvider.refreshSessions(); - context.subscriptions.push(this.workOsAuthProvider); - this.editDecorationManager = new EditDecorationManager(context); let resolveWebviewProtocol: any = undefined; @@ -285,7 +273,6 @@ export class VsCodeExtension { this.ide, verticalDiffManagerPromise, configHandlerPromise, - this.workOsAuthProvider, this.editDecorationManager, context, this, @@ -304,12 +291,6 @@ export class VsCodeExtension { ); resolveVerticalDiffManager?.(this.verticalDiffManager); - void setupRemoteConfigSync(() => - this.configHandler.reloadConfig.bind(this.configHandler)( - "Remote config sync", - ), - ); - void this.configHandler.loadConfig().then(async ({ config }) => { const shouldUseFullFileDiff = await getUsingFullFileDiff(); this.completionProvider.updateUsingFullFileDiff(shouldUseFullFileDiff); @@ -560,28 +541,8 @@ export class VsCodeExtension { // When GitHub sign-in status changes, reload config vscode.authentication.onDidChangeSessions(async (e) => { - const env = await getControlPlaneEnv(this.ide.getIdeSettings()); - if (e.provider.id === env.AUTH_TYPE) { - void vscode.commands.executeCommand( - "setContext", - "continue.isSignedInToControlPlane", - true, - ); - - const sessionInfo = await getControlPlaneSessionInfo(true, false); - void this.core.invoke("didChangeControlPlaneSessionInfo", { - sessionInfo, - }); - } else { - void vscode.commands.executeCommand( - "setContext", - "continue.isSignedInToControlPlane", - false, - ); - - if (e.provider.id === "github") { - this.configHandler.reloadConfig("Github sign-in status changed"); - } + if (e.provider.id === "github") { + this.configHandler.reloadConfig("Github sign-in status changed"); } }); diff --git a/extensions/vscode/src/extension/VsCodeMessenger.ts b/extensions/vscode/src/extension/VsCodeMessenger.ts index fd71d9354c6..711acd54629 100644 --- a/extensions/vscode/src/extension/VsCodeMessenger.ts +++ b/extensions/vscode/src/extension/VsCodeMessenger.ts @@ -15,28 +15,18 @@ import { WEBVIEW_TO_CORE_PASS_THROUGH, } from "core/protocol/passThrough"; import { stripImages } from "core/util/messageContent"; -import { normalizeRepoUrl } from "core/util/repoUrl"; -import { - sanitizeShellArgument, - validateGitHubRepoUrl, -} from "core/util/sanitization"; import * as vscode from "vscode"; import { ApplyManager } from "../apply"; import { VerticalDiffManager } from "../diff/vertical/manager"; import { addCurrentSelectionToEdit } from "../quickEdit/AddCurrentSelection"; import EditDecorationManager from "../quickEdit/EditDecorationManager"; -import { - getControlPlaneSessionInfo, - WorkOsAuthProvider, -} from "../stubs/WorkOsAuthProvider"; import { handleLLMError } from "../util/errorHandling"; import { showTutorial } from "../util/tutorial"; import { getExtensionUri } from "../util/vscode"; import { VsCodeIde } from "../VsCodeIde"; import { VsCodeWebviewProtocol } from "../webviewProtocol"; -import { encodeFullSlug } from "../../../../packages/config-yaml/dist"; import { VsCodeExtension } from "./VsCodeExtension"; type ToIdeOrWebviewFromCoreProtocol = ToIdeFromCoreProtocol & @@ -88,7 +78,6 @@ export class VsCodeMessenger { private readonly ide: VsCodeIde, private readonly verticalDiffManagerPromise: Promise, private readonly configHandlerPromise: Promise, - private readonly workOsAuthProvider: WorkOsAuthProvider, private readonly editDecorationManager: EditDecorationManager, private readonly context: vscode.ExtensionContext, private readonly vsCodeExtension: VsCodeExtension, @@ -269,388 +258,6 @@ export class VsCodeMessenger { ); }); - this.onWebview("createBackgroundAgent", async (msg) => { - const configHandler = await configHandlerPromise; - const { content, contextItems, selectedCode, organizationId } = msg.data; - - // Convert resolved content to plain text prompt - const prompt = stripImages(content); - - if (!prompt || prompt.trim().length === 0) { - vscode.window.showErrorMessage( - "Please enter a prompt to create a background agent", - ); - return; - } - - // Get workspace information - const workspaceDirs = await this.ide.getWorkspaceDirs(); - if (workspaceDirs.length === 0) { - vscode.window.showErrorMessage( - "No workspace folder found. Please open a workspace to create a background agent.", - ); - return; - } - - const workspaceDir = workspaceDirs[0]; - let repoUrl = ""; - let branch = ""; - - try { - // Get repo name/URL - const repoName = await this.ide.getRepoName(workspaceDir); - if (repoName) { - // Normalize the URL first to get canonical form - const normalized = normalizeRepoUrl(repoName); - - // Validate the normalized URL to prevent injection attacks - // This ensures we validate what we'll actually use, not just the input - if (!validateGitHubRepoUrl(normalized)) { - vscode.window.showErrorMessage( - "Invalid repository format. Please ensure you're using a valid GitHub repository.", - ); - return; - } - - repoUrl = normalized; - } - - // Get current branch - const branchInfo = await this.ide.getBranch(workspaceDir); - if (branchInfo) { - branch = branchInfo; - } - } catch (e) { - console.error("Error getting repo info:", e); - } - - if (!repoUrl) { - vscode.window.showErrorMessage( - "Unable to determine repository URL. Make sure you're in a git repository.", - ); - return; - } - - // Generate a name from the prompt (first 50 chars, cleaned up) - let name = prompt.substring(0, 50).replace(/\n/g, " ").trim(); - if (prompt.length > 50) { - name += "..."; - } - // Fallback to a generic name if prompt is too short - if (name.length < 3) { - const repoName = await this.ide.getRepoName(workspaceDir); - name = `Agent for ${repoName || "repository"}`; - } - - // Get the current agent configuration from the selected profile - let agent: string | undefined; - try { - const currentProfile = configHandler.currentProfile; - if ( - currentProfile && - currentProfile.profileDescription.profileType !== "local" - ) { - // Encode the full slug to pass as the agent parameter - agent = encodeFullSlug(currentProfile.profileDescription.fullSlug); - } - } catch (e) { - console.error("Error getting agent configuration from profile:", e); - // Continue without agent config - will use default - } - - // Create the background agent - try { - console.log("Creating background agent with:", { - name, - prompt: prompt.substring(0, 50) + "...", - repoUrl, - branch, - contextItemsCount: contextItems?.length || 0, - selectedCodeCount: selectedCode?.length || 0, - agent: agent || "default", - }); - - const result = - await configHandler.controlPlaneClient.createBackgroundAgent( - prompt, - repoUrl, - name, - branch, - organizationId, - contextItems, - selectedCode, - agent, - ); - - vscode.window.showInformationMessage( - `Background agent created successfully! Agent ID: ${result.id}`, - ); - } catch (e) { - console.error("Failed to create background agent:", e); - const errorMessage = - e instanceof Error ? e.message : "Unknown error occurred"; - - // Check if this is a GitHub authorization error - if ( - errorMessage.includes("GitHub token") || - errorMessage.includes("GitHub App") - ) { - const selection = await vscode.window.showErrorMessage( - "Background agents need GitHub access. Please connect your GitHub account to Continue.", - "Connect GitHub", - "Cancel", - ); - - if (selection === "Connect GitHub") { - await this.inProcessMessenger.externalRequest( - "controlPlane/openUrl", - { - path: "settings/integrations", - orgSlug: configHandler.currentOrg?.slug, - }, - ); - } - } else { - vscode.window.showErrorMessage( - `Failed to create background agent: ${errorMessage}`, - ); - } - } - }); - - this.onWebview("listBackgroundAgents", async (msg) => { - const configHandler = await configHandlerPromise; - const { organizationId, limit } = msg.data; - - try { - const result = - await configHandler.controlPlaneClient.listBackgroundAgents( - organizationId, - limit, - ); - return result; - } catch (e) { - console.error("Error listing background agents:", e); - return { agents: [], totalCount: 0 }; - } - }); - - this.onWebview("openAgentLocally", async (msg) => { - const configHandler = await configHandlerPromise; - const { agentSessionId } = msg.data; - - try { - // First, fetch the agent session to get repo URL and branch - const agentSession = - await configHandler.controlPlaneClient.getAgentSession( - agentSessionId, - ); - if (!agentSession) { - vscode.window.showErrorMessage( - "Failed to load agent session details.", - ); - return; - } - - const repoUrl = agentSession.repoUrl; - const branch = agentSession.branch; - - if (!repoUrl || !branch) { - vscode.window.showErrorMessage( - "Agent session is missing repository or branch information.", - ); - return; - } - - // Validate the repo URL from API response to prevent injection attacks - if (!validateGitHubRepoUrl(repoUrl)) { - vscode.window.showErrorMessage( - "Invalid repository URL from agent session. Please contact support.", - ); - return; - } - - // Get workspace directories - const workspaceDirs = await this.ide.getWorkspaceDirs(); - if (workspaceDirs.length === 0) { - vscode.window.showErrorMessage("No workspace folder is open."); - return; - } - - // Normalize and validate again to ensure the normalized form is safe - const normalizedAgentRepo = normalizeRepoUrl(repoUrl); - if (!validateGitHubRepoUrl(normalizedAgentRepo)) { - vscode.window.showErrorMessage( - "Invalid repository URL after normalization. Please contact support.", - ); - return; - } - - // Find the workspace that matches the agent's repo URL - let matchingWorkspace: string | null = null; - for (const workspaceDir of workspaceDirs) { - const repoName = await this.ide.getRepoName(workspaceDir); - if (repoName) { - const normalizedRepoName = normalizeRepoUrl(repoName); - - if (normalizedRepoName === normalizedAgentRepo) { - matchingWorkspace = workspaceDir; - break; - } - } - } - - if (!matchingWorkspace) { - vscode.window.showErrorMessage( - `This agent is for repository ${repoUrl}. Please open that workspace to take over the workflow.`, - ); - return; - } - - // Get the git repository - const repo = await this.ide.getRepo(matchingWorkspace); - if (!repo) { - vscode.window.showErrorMessage("Could not access git repository."); - return; - } - - // Ask user what to do with uncommitted changes - if ( - repo.state.workingTreeChanges.length > 0 || - repo.state.indexChanges.length > 0 - ) { - const changeCount = - repo.state.workingTreeChanges.length + - repo.state.indexChanges.length; - - const choice = await vscode.window.showWarningMessage( - `You have ${changeCount} uncommitted change(s). What would you like to do?`, - "Stash & Continue", - "Continue Without Stashing", - "Cancel", - ); - - if (choice === "Cancel" || !choice) { - return; - } - - if (choice === "Stash & Continue") { - try { - await vscode.window.withProgress( - { - location: vscode.ProgressLocation.Notification, - title: "Stashing local changes...", - cancellable: false, - }, - async () => { - const workspacePath = - vscode.Uri.parse(matchingWorkspace).fsPath; - // Sanitize agentSessionId to prevent command injection - const stashMessage = `Continue: Stashed before opening agent ${agentSessionId}`; - await this.ide.subprocess( - `git stash push -m ${sanitizeShellArgument(stashMessage)}`, - workspacePath, - ); - }, - ); - vscode.window.showInformationMessage( - "Local changes have been stashed.", - ); - } catch (e) { - console.error("Failed to stash changes:", e); - const errorMsg = e instanceof Error ? e.message : String(e); - vscode.window.showErrorMessage( - `Failed to stash changes: ${errorMsg}`, - ); - return; // Stop on stash failure - } - } - // If "Continue Without Stashing" was chosen, just proceed - } - - // Check if we're already on the target branch - try { - const currentBranch = await this.ide.getBranch(matchingWorkspace); - console.log( - `Current branch: ${currentBranch}, Target branch: ${branch}`, - ); - - if (currentBranch !== branch) { - // Try to switch to the branch using VS Code Git API - await vscode.window.withProgress( - { - location: vscode.ProgressLocation.Notification, - title: `Switching to branch ${branch}...`, - cancellable: false, - }, - async () => { - try { - // Use VS Code Git API for checkout - await repo.checkout(branch); - } catch (checkoutError: any) { - console.log( - "Checkout failed, trying to fetch first...", - checkoutError, - ); - // If checkout fails, fetch and try again - await repo.fetch(); - await repo.checkout(branch); - } - }, - ); - vscode.window.showInformationMessage( - `Switched to branch ${branch}`, - ); - } else { - console.log("Already on target branch, skipping checkout"); - } - } catch (e: any) { - console.error("Failed to switch branch:", e); - vscode.window.showErrorMessage( - `Failed to switch to branch ${branch}: ${e.message || String(e)}`, - ); - return; - } - - // Fetch the agent state - const agentState = - await configHandler.controlPlaneClient.getAgentState(agentSessionId); - - if (!agentState) { - vscode.window.showErrorMessage( - "Failed to fetch agent state from API. The agent may not exist or you may not have permission.", - ); - return; - } - - if (!agentState.session) { - console.error( - "Agent state is missing session field. Full response:", - agentState, - ); - vscode.window.showErrorMessage( - "Agent state returned but missing session data. This may be a backend issue.", - ); - return; - } - - // For MVP: Simply load the session by sending to webview - // The webview will dispatch the newSession action with the session data - this.webviewProtocol.send("loadAgentSession", { - session: agentState.session, - }); - - vscode.window.showInformationMessage( - `Successfully loaded agent workflow: ${agentState.session.title || "Untitled"}`, - ); - } catch (e) { - console.error("Failed to open agent locally:", e); - vscode.window.showErrorMessage( - `Failed to open agent locally: ${e instanceof Error ? e.message : "Unknown error"}`, - ); - } - }); - /** PASS THROUGH FROM WEBVIEW TO CORE AND BACK **/ WEBVIEW_TO_CORE_PASS_THROUGH.forEach((messageType) => { this.onWebview(messageType, async (msg) => { @@ -754,23 +361,6 @@ export class VsCodeMessenger { this.onWebviewOrCore("showToast", (msg) => { this.ide.showToast(...msg.data); }); - this.onWebviewOrCore("getControlPlaneSessionInfo", async (msg) => { - return getControlPlaneSessionInfo( - msg.data.silent, - msg.data.useOnboarding, - ); - }); - this.onWebviewOrCore("logoutOfControlPlane", async (msg) => { - const sessions = await this.workOsAuthProvider.getSessions(); - await Promise.all( - sessions.map((session) => workOsAuthProvider.removeSession(session.id)), - ); - vscode.commands.executeCommand( - "setContext", - "continue.isSignedInToControlPlane", - false, - ); - }); this.onWebviewOrCore("saveFile", async (msg) => { return await ide.saveFile(msg.data.filepath); }); diff --git a/extensions/vscode/src/stubs/WorkOsAuthProvider.ts b/extensions/vscode/src/stubs/WorkOsAuthProvider.ts deleted file mode 100644 index 3523ecbf2ec..00000000000 --- a/extensions/vscode/src/stubs/WorkOsAuthProvider.ts +++ /dev/null @@ -1,634 +0,0 @@ -import crypto from "crypto"; -import { EventEmitter as NodeEventEmitter } from "node:events"; - -import { - AuthType, - ControlPlaneSessionInfo, - HubEnv, - isHubEnv, -} from "core/control-plane/AuthTypes"; -import { getControlPlaneEnvSync } from "core/control-plane/env"; -import { Logger } from "core/util/Logger"; -import fetch from "node-fetch"; -import { v4 as uuidv4 } from "uuid"; -import { - authentication, - AuthenticationProvider, - AuthenticationProviderAuthenticationSessionsChangeEvent, - AuthenticationSession, - Disposable, - env, - EventEmitter, - ExtensionContext, - ProgressLocation, - Uri, - window, -} from "vscode"; - -import { PromiseAdapter, promiseFromEvent } from "./promiseUtils"; -import { SecretStorage } from "./SecretStorage"; -import { UriEventHandler } from "./uriHandler"; - -const AUTH_NAME = "Continue"; - -const controlPlaneEnv = getControlPlaneEnvSync(true ? "production" : "none"); - -const SESSIONS_SECRET_KEY = `${controlPlaneEnv.AUTH_TYPE}.sessions`; - -// Function to generate a random string of specified length -function generateRandomString(length: number): string { - const possibleCharacters = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"; - let randomString = ""; - for (let i = 0; i < length; i++) { - const randomIndex = Math.floor(Math.random() * possibleCharacters.length); - randomString += possibleCharacters[randomIndex]; - } - return randomString; -} - -// Function to generate a code challenge from the code verifier - -async function generateCodeChallenge(verifier: string) { - // Create a SHA-256 hash of the verifier - const hash = crypto.createHash("sha256").update(verifier).digest(); - - // Convert the hash to a base64 URL-encoded string - const base64String = hash - .toString("base64") - .replace(/\+/g, "-") - .replace(/\//g, "_") - .replace(/=+$/, ""); - - return base64String; -} - -interface ContinueAuthenticationSession extends AuthenticationSession { - refreshToken: string; - expiresInMs: number; - loginNeeded: boolean; -} - -export class WorkOsAuthProvider implements AuthenticationProvider, Disposable { - private _sessionChangeEmitter = - new EventEmitter(); - private _disposable: Disposable; - private _pendingStates: string[] = []; - private _codeExchangePromises = new Map< - string, - { promise: Promise; cancel: EventEmitter } - >(); - private _refreshInterval: NodeJS.Timeout | null = null; - private _isRefreshing = false; - - private static EXPIRATION_TIME_MS = 1000 * 60 * 15; // 15 minutes - private static REFRESH_INTERVAL_MS = 1000 * 60 * 10; // 10 minutes - - private secretStorage: SecretStorage; - - constructor( - private readonly context: ExtensionContext, - private readonly _uriHandler: UriEventHandler, - ) { - this._disposable = Disposable.from( - authentication.registerAuthenticationProvider( - controlPlaneEnv.AUTH_TYPE, - AUTH_NAME, - this, - { supportsMultipleAccounts: false }, - ), - window.registerUriHandler(this._uriHandler), - ); - - this.secretStorage = new SecretStorage(context); - - // Immediately refresh any existing sessions - this.attemptEmitter = new NodeEventEmitter(); - WorkOsAuthProvider.hasAttemptedRefresh = new Promise((resolve) => { - this.attemptEmitter.on("attempted", resolve); - }); - void this.refreshSessions(); - - // Set up a regular interval to refresh tokens - this._refreshInterval = setInterval(() => { - void this.refreshSessions(); - }, WorkOsAuthProvider.REFRESH_INTERVAL_MS); - } - - private decodeJwt(jwt: string): Record | null { - try { - const decodedToken = JSON.parse( - Buffer.from(jwt.split(".")[1], "base64").toString(), - ); - return decodedToken; - } catch (e: any) { - Logger.error(e, { - context: "workOS_auth_jwt_decode", - jwtLength: jwt.length, - jwtPrefix: jwt.substring(0, 20) + "...", // Safe prefix for debugging - }); - - console.warn(`Error decoding JWT: ${e}`); - return null; - } - } - - private jwtIsExpiredOrInvalid(jwt: string): boolean { - const decodedToken = this.decodeJwt(jwt); - if (!decodedToken) { - return true; - } - return decodedToken.exp * 1000 < Date.now(); - } - - private getExpirationTimeMs(jwt: string): number { - const decodedToken = this.decodeJwt(jwt); - if (!decodedToken) { - return WorkOsAuthProvider.EXPIRATION_TIME_MS; - } - return decodedToken.exp && decodedToken.iat - ? (decodedToken.exp - decodedToken.iat) * 1000 - : WorkOsAuthProvider.EXPIRATION_TIME_MS; - } - - private async storeSessions(value: ContinueAuthenticationSession[]) { - const data = JSON.stringify(value, null, 2); - await this.secretStorage.store(SESSIONS_SECRET_KEY, data); - } - - public async getSessions( - scopes?: string[], - ): Promise { - // await this.hasAttemptedRefresh; - try { - const data = await this.secretStorage.get(SESSIONS_SECRET_KEY); - if (!data) { - return []; - } - - const value = JSON.parse(data) as ContinueAuthenticationSession[]; - return value; - } catch (e: any) { - Logger.error(e, { - context: "workOS_sessions_retrieval", - errorMessage: e.message, - }); - - console.warn(`Error retrieving or parsing sessions: ${e.message}`); - - // Delete the corrupted cache file to allow fresh start on next attempt - // This handles cases where decryption succeeded but JSON parsing failed - try { - await this.secretStorage.delete(SESSIONS_SECRET_KEY); - } catch (deleteError: any) { - console.error( - `Failed to delete corrupted sessions cache:`, - deleteError.message, - ); - } - - return []; - } - } - - get onDidChangeSessions() { - return this._sessionChangeEmitter.event; - } - - get ideRedirectUri() { - // We redirect to a page that says "you can close this page", and that page finishes the redirect - const url = new URL(controlPlaneEnv.APP_URL); - url.pathname = `/auth/${env.uriScheme}-redirect`; - return url.toString(); - } - - public static useOnboardingUri: boolean = false; - get redirectUri() { - return this.ideRedirectUri; - } - - public static hasAttemptedRefresh: Promise; - private attemptEmitter: NodeEventEmitter; - async refreshSessions() { - // Prevent concurrent refresh operations - if (this._isRefreshing) { - return; - } - - try { - this._isRefreshing = true; - await this._refreshSessions(); - } catch (e) { - Logger.error(e, { - context: "workOS_auth_session_refresh", - authType: controlPlaneEnv.AUTH_TYPE, - }); - - console.error(`Error refreshing sessions: ${e}`); - } finally { - this._isRefreshing = false; - } - } - - // It is important that every path in this function emits the attempted event - // As config loading in core will be locked until refresh is attempted - private async _refreshSessions(): Promise { - const sessions = await this.getSessions(); - if (!sessions.length) { - this.attemptEmitter.emit("attempted"); - return; - } - - const finalSessions = []; - for (const session of sessions) { - try { - const newSession = await this._refreshSessionWithRetry( - session.refreshToken, - ); - finalSessions.push({ - ...session, - accessToken: newSession.accessToken, - refreshToken: newSession.refreshToken, - expiresInMs: newSession.expiresInMs, - }); - } catch (e: any) { - Logger.error(e, { - context: "workOS_individual_session_refresh", - sessionId: session.id, - }); - - // If refresh fails (after retries for valid tokens), drop the session - console.debug(`Error refreshing session token: ${e.message}`); - this._sessionChangeEmitter.fire({ - added: [], - removed: [session], - changed: [], - }); - } - } - - await this.storeSessions(finalSessions); - this._sessionChangeEmitter.fire({ - added: [], - removed: [], - changed: finalSessions, - }); - } - - private async _refreshSessionWithRetry( - refreshToken: string, - attempt = 1, - baseDelay = 1000, - ): Promise<{ - accessToken: string; - refreshToken: string; - expiresInMs: number; - }> { - try { - return await this._refreshSession(refreshToken); - } catch (error: any) { - Logger.error(error, { - attempt, - errorMessage: error.message, - isAuthError: - error.message?.includes("401") || - error.message?.includes("Invalid refresh token") || - error.message?.includes("Unauthorized"), - }); - - this.attemptEmitter.emit("attempted"); - // Don't retry for auth errors - if ( - error.message?.includes("401") || - error.message?.includes("Invalid refresh token") || - error.message?.includes("Unauthorized") - ) { - throw error; - } - - // For network errors or transient server issues, retry with backoff - // Calculate exponential backoff delay with jitter - const delay = Math.min( - baseDelay * Math.pow(2, attempt - 1) * (0.5 + Math.random() * 0.5), - 2 * 60 * 1000, // 2 minutes - ); - - return new Promise((resolve, reject) => { - setTimeout(() => { - this._refreshSessionWithRetry(refreshToken, attempt + 1, baseDelay) - .then(resolve) - .catch(reject); - }, delay); - }); - } finally { - this.attemptEmitter.emit("attempted"); - } - } - - private async _refreshSession(refreshToken: string): Promise<{ - accessToken: string; - refreshToken: string; - expiresInMs: number; - }> { - const response = await fetch( - new URL("/auth/refresh", controlPlaneEnv.CONTROL_PLANE_URL), - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - refreshToken, - }), - }, - ); - if (!response.ok) { - const text = await response.text(); - throw new Error("Error refreshing token: " + text); - } - const data = (await response.json()) as any; - return { - accessToken: data.accessToken, - refreshToken: data.refreshToken, - expiresInMs: this.getExpirationTimeMs(data.accessToken), - }; - } - - private _formatProfileLabel( - firstName: string | null, - lastName: string | null, - ) { - return ((firstName ?? "") + " " + (lastName ?? "")).trim(); - } - - /** - * Create a new auth session - * @param scopes - * @returns - */ - public async createSession( - scopes: string[], - ): Promise { - try { - const codeVerifier = generateRandomString(64); - const codeChallenge = await generateCodeChallenge(codeVerifier); - - if (!isHubEnv(controlPlaneEnv)) { - throw new Error("Login is disabled"); - } - - const token = await this.login(codeChallenge, controlPlaneEnv, scopes); - if (!token) { - throw new Error(`Continue login failure`); - } - - const userInfo = (await this.getUserInfo( - token, - codeVerifier, - controlPlaneEnv, - )) as any; - const { user, access_token, refresh_token } = userInfo; - - const session: ContinueAuthenticationSession = { - id: uuidv4(), - accessToken: access_token, - refreshToken: refresh_token, - expiresInMs: this.getExpirationTimeMs(access_token), - loginNeeded: false, - account: { - label: this._formatProfileLabel(user.first_name, user.last_name), - id: user.email, - }, - scopes: [], - }; - - await this.storeSessions([session]); - - this._sessionChangeEmitter.fire({ - added: [session], - removed: [], - changed: [], - }); - - return session; - } catch (e) { - Logger.error(e, { - context: "workOS_auth_session_creation", - scopes: scopes.join(","), - authType: controlPlaneEnv.AUTH_TYPE, - }); - - void window.showErrorMessage(`Sign in failed: ${e}`); - throw e; - } - } - - /** - * Remove an existing session - * @param sessionId - */ - public async removeSession(sessionId: string): Promise { - const sessions = await this.getSessions(); - const sessionIdx = sessions.findIndex((s) => s.id === sessionId); - const session = sessions[sessionIdx]; - sessions.splice(sessionIdx, 1); - - await this.storeSessions(sessions); - - if (session) { - this._sessionChangeEmitter.fire({ - added: [], - removed: [session], - changed: [], - }); - } - } - - /** - * Dispose the registered services - */ - public async dispose() { - if (this._refreshInterval) { - clearInterval(this._refreshInterval); - this._refreshInterval = null; - } - this._disposable.dispose(); - } - - /** - * Log in to Continue - */ - private async login( - codeChallenge: string, - hubEnv: HubEnv, - scopes: string[] = [], - ) { - return await window.withProgress( - { - location: ProgressLocation.Notification, - title: "Signing in to Continue...", - cancellable: true, - }, - async (_, token) => { - const stateId = uuidv4(); - - this._pendingStates.push(stateId); - - const scopeString = scopes.join(" "); - - const url = new URL("https://api.workos.com/user_management/authorize"); - const params = { - response_type: "code", - client_id: hubEnv.WORKOS_CLIENT_ID, - redirect_uri: this.redirectUri, - state: stateId, - code_challenge: codeChallenge, - code_challenge_method: "S256", - provider: "authkit", - screen_hint: WorkOsAuthProvider.useOnboardingUri - ? "sign-up" - : "sign-in", - }; - - Object.keys(params).forEach((key) => - url.searchParams.append(key, params[key as keyof typeof params]), - ); - - const oauthUrl = url; - if (oauthUrl) { - await env.openExternal(Uri.parse(oauthUrl.toString())); - } else { - return; - } - - let codeExchangePromise = this._codeExchangePromises.get(scopeString); - if (!codeExchangePromise) { - codeExchangePromise = promiseFromEvent( - this._uriHandler.event, - this.handleUri(scopes), - ); - this._codeExchangePromises.set(scopeString, codeExchangePromise); - } - - try { - return await Promise.race([ - codeExchangePromise.promise, - new Promise( - (_, reject) => - setTimeout(() => reject("Cancelled"), 60 * 60 * 1_000), // 60min timeout - ), - promiseFromEvent( - token.onCancellationRequested, - (_, __, reject) => { - reject("User Cancelled"); - }, - ).promise, - ]); - } finally { - this._pendingStates = this._pendingStates.filter( - (n) => n !== stateId, - ); - codeExchangePromise?.cancel.fire(); - this._codeExchangePromises.delete(scopeString); - } - }, - ); - } - - /** - * Handle the redirect to VS Code (after sign in from Continue) - * @param scopes - * @returns - */ - private handleUri: ( - scopes: readonly string[], - ) => PromiseAdapter = - (scopes) => async (uri, resolve, reject) => { - const query = new URLSearchParams(uri.query); - const access_token = query.get("code"); - const state = query.get("state"); - - if (!access_token) { - reject(new Error("No token")); - return; - } - if (!state) { - reject(new Error("No state")); - return; - } - - // Check if it is a valid auth request started by the extension - if (!this._pendingStates.some((n) => n === state)) { - reject(new Error("State not found")); - return; - } - - resolve(access_token); - }; - - /** - * Get the user info from WorkOS - * @param token - * @returns - */ - private async getUserInfo( - token: string, - codeVerifier: string, - hubEnv: HubEnv, - ) { - const resp = await fetch( - "https://api.workos.com/user_management/authenticate", - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - client_id: hubEnv.WORKOS_CLIENT_ID, - code_verifier: codeVerifier, - grant_type: "authorization_code", - code: token, - }), - }, - ); - const text = await resp.text(); - const data = JSON.parse(text); - return data; - } -} - -export async function getControlPlaneSessionInfo( - silent: boolean, - useOnboarding: boolean, -): Promise { - if (!isHubEnv(controlPlaneEnv)) { - return { - AUTH_TYPE: AuthType.OnPrem, - }; - } - - try { - if (useOnboarding) { - WorkOsAuthProvider.useOnboardingUri = true; - } - await WorkOsAuthProvider.hasAttemptedRefresh; - const session = await authentication.getSession( - controlPlaneEnv.AUTH_TYPE, - [], - silent ? { silent: true } : { createIfNone: true }, - ); - if (!session) { - return undefined; - } - return { - AUTH_TYPE: controlPlaneEnv.AUTH_TYPE, - accessToken: session.accessToken, - account: { - id: session.account.id, - label: session.account.label, - }, - }; - } finally { - WorkOsAuthProvider.useOnboardingUri = false; - } -} diff --git a/extensions/vscode/src/stubs/WorkOsAuthProvider.vitest.ts b/extensions/vscode/src/stubs/WorkOsAuthProvider.vitest.ts deleted file mode 100644 index f0a7b746de2..00000000000 --- a/extensions/vscode/src/stubs/WorkOsAuthProvider.vitest.ts +++ /dev/null @@ -1,805 +0,0 @@ -// @ts-nocheck -import fetch from "node-fetch"; -import { afterEach, beforeEach, expect, it, vi } from "vitest"; -import { EventEmitter } from "vscode"; - -// Don't import WorkOsAuthProvider directly here - -// Mock the modules we need -vi.mock("vscode", () => ({ - authentication: { - registerAuthenticationProvider: vi.fn(), - }, - window: { - registerUriHandler: vi.fn(), - }, - EventEmitter: vi.fn(() => ({ - event: { dispose: vi.fn() }, - fire: vi.fn(), - })), - Disposable: { - from: vi.fn(() => ({ dispose: vi.fn() })), - }, - env: { - uriScheme: "vscode", - }, -})); - -// Properly mock node-fetch -vi.mock("node-fetch", () => { - return { - __esModule: true, - default: vi.fn(), - }; -}); - -vi.mock("core/control-plane/env", () => ({ - getControlPlaneEnvSync: vi.fn(() => ({ - AUTH_TYPE: "workos", - APP_URL: "https://continue.dev", - CONTROL_PLANE_URL: "https://api.continue.dev", - WORKOS_CLIENT_ID: "client_123", - })), -})); - -vi.mock("crypto", () => ({ - createHash: vi.fn(() => ({ - update: vi.fn(() => ({ - digest: vi.fn(() => Buffer.from("test-hash")), - })), - })), -})); - -// Create a simple SecretStorage mock that we can control -const mockSecretStorageGet = vi.fn(); -const mockSecretStorageStore = vi.fn(); - -// Mock SecretStorage class -vi.mock("./SecretStorage", () => { - return { - SecretStorage: vi.fn().mockImplementation(() => ({ - store: mockSecretStorageStore, - get: mockSecretStorageGet, - })), - }; -}); - -// Helper to create valid and expired JWTs -function createJwt({ expired }: { expired: boolean }): string { - const now = Math.floor(Date.now() / 1000); - const header = { alg: "HS256", typ: "JWT" }; - const payload = { - sub: "user123", - iat: now, - exp: expired ? now - 3600 : now + 3600, // Expired 1 hour ago or valid for 1 hour - }; - - const base64Header = Buffer.from(JSON.stringify(header)) - .toString("base64") - .replace(/=/g, ""); - const base64Payload = Buffer.from(JSON.stringify(payload)) - .toString("base64") - .replace(/=/g, ""); - const signature = "dummysignature"; - - return `${base64Header}.${base64Payload}.${signature}`; -} - -beforeEach(() => { - // Set up fake timers before each test - vi.useFakeTimers(); -}); - -afterEach(() => { - vi.clearAllMocks(); - vi.clearAllTimers(); - vi.useRealTimers(); // Restore real timers after each test -}); - -it("should refresh tokens on initialization when sessions exist", async () => { - // Mock setInterval to prevent the refresh interval - const originalSetInterval = global.setInterval; - global.setInterval = vi.fn().mockReturnValue(123 as any); - - // Setup existing sessions with a valid token - const validToken = createJwt({ expired: false }); - const mockSession = { - id: "test-id", - accessToken: validToken, - refreshToken: "refresh-token", - expiresInMs: 3600000, // 1 hour - account: { label: "Test User", id: "user@example.com" }, - scopes: [], - loginNeeded: false, - }; - - // Setup fetch mock - const fetchMock = fetch as any; - fetchMock.mockClear(); - - // Setup successful token refresh - fetchMock.mockResolvedValueOnce({ - ok: true, - json: async () => ({ - accessToken: createJwt({ expired: false }), - refreshToken: "new-refresh-token", - }), - text: async () => "", - }); - - // Create a mock UriHandler - const mockUriHandler = { - event: new EventEmitter(), - handleCallback: vi.fn(), - }; - - // Create a mock ExtensionContext - const mockContext = { - secrets: { - store: vi.fn(), - get: vi.fn(), - }, - subscriptions: [], - }; - - // Set up our SecretStorage mock to return the session - mockSecretStorageGet.mockResolvedValue(JSON.stringify([mockSession])); - - // Import WorkOsAuthProvider after setting up all mocks - const { WorkOsAuthProvider } = await import("./WorkOsAuthProvider"); - - // Create provider instance - this will automatically call refreshSessions - const provider = new WorkOsAuthProvider(mockContext, mockUriHandler); - - // Wait for all promises to resolve, including any nested promise chains - await new Promise(process.nextTick); - - // Verify that the token refresh endpoint was called - expect(fetchMock).toHaveBeenCalledWith( - expect.any(URL), - expect.objectContaining({ - method: "POST", - body: expect.stringContaining("refresh-token"), - }), - ); - - // Restore setInterval - global.setInterval = originalSetInterval; - - // Clean up - if (provider._refreshInterval) { - clearInterval(provider._refreshInterval); - provider._refreshInterval = null; - } -}); - -it("should not remove sessions during transient network errors", async () => { - // Setup existing sessions with a valid token - const validToken = createJwt({ expired: false }); - const mockSession = { - id: "test-id", - accessToken: validToken, - refreshToken: "refresh-token", - expiresInMs: 300000, // 5 minutes - account: { label: "Test User", id: "user@example.com" }, - scopes: [], - loginNeeded: false, - }; - - // Setup fetch mock - const fetchMock = fetch as any; - fetchMock.mockClear(); - - // First refresh attempt fails with network error - fetchMock.mockRejectedValueOnce(new Error("Network error")); - - // Second refresh attempt should succeed - fetchMock.mockResolvedValueOnce({ - ok: true, - json: async () => ({ - accessToken: createJwt({ expired: false }), - refreshToken: "new-refresh-token", - }), - text: async () => "", - }); - - // Create a mock UriHandler - const mockUriHandler = { - event: new EventEmitter(), - handleCallback: vi.fn(), - }; - - // Create a mock ExtensionContext - const mockContext = { - secrets: { - store: vi.fn(), - get: vi.fn(), - }, - subscriptions: [], - }; - - // Set up our SecretStorage mock to return the session - mockSecretStorageGet.mockResolvedValue(JSON.stringify([mockSession])); - - // Import WorkOsAuthProvider after setting up all mocks - const { WorkOsAuthProvider } = await import("./WorkOsAuthProvider"); - - // Allow setInterval to work normally with fake timers - // We're not mocking it anymore, instead we'll control when it fires - - // Create provider instance - this will automatically call refreshSessions with the network error - const provider = new WorkOsAuthProvider(mockContext, mockUriHandler); - - // Run microtasks to process promises from the initial refresh call - await Promise.resolve(); - - // Check that sessions were not cleared after network error - expect(mockSecretStorageStore).not.toHaveBeenCalledWith( - expect.anything(), - expect.stringMatching(/\[\]/), - ); - - // Reset the fetch mock call count to verify the next call - fetchMock.mockClear(); - - // Advance timers to the next refresh interval to simulate the timer firing - vi.advanceTimersByTime(WorkOsAuthProvider.REFRESH_INTERVAL_MS); - - // Run microtasks to process promises from the interval-triggered refresh - await Promise.resolve(); - - // Verify the second attempt was made via the interval - expect(fetchMock).toHaveBeenCalledTimes(1); - expect(fetchMock).toHaveBeenCalledWith( - expect.any(URL), - expect.objectContaining({ - method: "POST", - headers: expect.objectContaining({ - "Content-Type": "application/json", - }), - body: expect.stringContaining("refresh-token"), - }), - ); - - // Clean up - if (provider._refreshInterval) { - clearInterval(provider._refreshInterval); - provider._refreshInterval = null; - } -}); - -it("should refresh tokens at regular intervals rather than based on expiration", async () => { - // Setup existing sessions with a valid token - const validToken = createJwt({ expired: false }); - const mockSession = { - id: "test-id", - accessToken: validToken, - refreshToken: "refresh-token", - expiresInMs: 3600000, // 1 hour - account: { label: "Test User", id: "user@example.com" }, - scopes: [], - loginNeeded: false, - }; - - // Setup fetch mock - const fetchMock = fetch as any; - fetchMock.mockClear(); - - // Setup successful token refresh responses for multiple calls - fetchMock.mockResolvedValue({ - ok: true, - json: async () => ({ - accessToken: createJwt({ expired: false }), - refreshToken: "new-refresh-token", - }), - text: async () => "", - }); - - // Create a mock UriHandler - const mockUriHandler = { - event: new EventEmitter(), - handleCallback: vi.fn(), - }; - - // Create a mock ExtensionContext - const mockContext = { - secrets: { - store: vi.fn(), - get: vi.fn(), - }, - subscriptions: [], - }; - - // Set up our SecretStorage mock to return the session - mockSecretStorageGet.mockResolvedValue(JSON.stringify([mockSession])); - - // Import WorkOsAuthProvider after setting up all mocks - const { WorkOsAuthProvider } = await import("./WorkOsAuthProvider"); - - // Capture the original setInterval to restore it later - const originalSetInterval = global.setInterval; - - // Create our own implementation of setInterval that we can control better - let intervalCallback: Function; - global.setInterval = vi.fn((callback, ms) => { - intervalCallback = callback; - return 123 as any; // Return a dummy interval ID - }); - - // Create provider instance - this will automatically call refreshSessions - const provider = new WorkOsAuthProvider(mockContext, mockUriHandler); - - // Wait for all promises to resolve, including any nested promise chains - await new Promise(process.nextTick); - - // First refresh should happen immediately on initialization - expect(fetchMock).toHaveBeenCalledTimes(1); - fetchMock.mockClear(); - - // Verify that setInterval was called to set up regular refreshes - expect(global.setInterval).toHaveBeenCalled(); - - // Get the interval time from the call to setInterval - const intervalTime = (global.setInterval as any).mock.calls[0][1]; - - // Should be a reasonable interval (less than the expiration time) - expect(intervalTime).toBeLessThan(mockSession.expiresInMs); - - // Now manually trigger the interval callback - First interval - intervalCallback(); - - // Wait for all promises to resolve - await new Promise(process.nextTick); - - // Verify that refresh was called again when the interval callback fired - expect(fetchMock).toHaveBeenCalledTimes(1); - - // Check that we're making refresh calls to the right endpoint with the right data - expect(fetchMock).toHaveBeenCalledWith( - expect.objectContaining({ pathname: "/auth/refresh" }), - expect.objectContaining({ - method: "POST", - headers: expect.objectContaining({ - "Content-Type": "application/json", - }), - body: expect.stringContaining("refresh-token"), - }), - ); - - // Clear mock calls for the second interval test - fetchMock.mockClear(); - - // Trigger the callback again - Second interval - intervalCallback(); - - // Wait for all promises to resolve - await new Promise(process.nextTick); - - // Verify the refresh was called a second time - expect(fetchMock).toHaveBeenCalledTimes(1); - - // Verify the second call has the same correct parameters - expect(fetchMock).toHaveBeenCalledWith( - expect.objectContaining({ pathname: "/auth/refresh" }), - expect.objectContaining({ - method: "POST", - headers: expect.objectContaining({ - "Content-Type": "application/json", - }), - body: expect.stringContaining("refresh-token"), - }), - ); - - // Restore the original setInterval - global.setInterval = originalSetInterval; - - // Clean up - if (provider._refreshInterval) { - clearInterval(provider._refreshInterval); - provider._refreshInterval = null; - } -}); - -it("should remove session if token refresh fails with authentication error", async () => { - // Setup existing sessions with a valid token - const validToken = createJwt({ expired: false }); - const mockSession = { - id: "test-id", - accessToken: validToken, - refreshToken: "invalid-refresh-token", - expiresInMs: 300000, // 5 minutes - account: { label: "Test User", id: "user@example.com" }, - scopes: [], - loginNeeded: false, - }; - - // Setup fetch mock - const fetchMock = fetch as any; - fetchMock.mockClear(); - - // Setup refresh to fail with 401 unauthorized - fetchMock.mockResolvedValueOnce({ - ok: false, - status: 401, - text: async () => "Invalid refresh token", - }); - - // Create a mock UriHandler - const mockUriHandler = { - event: new EventEmitter(), - handleCallback: vi.fn(), - }; - - // Create a mock ExtensionContext - const mockContext = { - secrets: { - store: vi.fn(), - get: vi.fn(), - }, - subscriptions: [], - }; - - // Mock setInterval to prevent continuous refreshes - const originalSetInterval = global.setInterval; - global.setInterval = vi.fn().mockReturnValue(123 as any); - - // Set up our SecretStorage mock to return the session - mockSecretStorageGet.mockResolvedValue(JSON.stringify([mockSession])); - mockSecretStorageStore.mockClear(); - - // Import WorkOsAuthProvider after setting up all mocks - const { WorkOsAuthProvider } = await import("./WorkOsAuthProvider"); - - // Create provider instance - this will automatically call refreshSessions - const provider = new WorkOsAuthProvider(mockContext, mockUriHandler); - - // Wait for all promises to resolve, including any nested promise chains - await new Promise(process.nextTick); - - // Verify that the token refresh endpoint was called - expect(fetchMock).toHaveBeenCalledWith( - expect.any(URL), - expect.objectContaining({ - method: "POST", - body: expect.stringContaining("invalid-refresh-token"), - }), - ); - - // Verify sessions were removed due to auth error - expect(mockSecretStorageStore).toHaveBeenCalledWith( - "workos.sessions", // Use the hard-coded key that matches our mock - expect.stringMatching(/\[\]/), - ); - - // Restore setInterval - global.setInterval = originalSetInterval; - - // Clean up - if (provider._refreshInterval) { - clearInterval(provider._refreshInterval); - provider._refreshInterval = null; - } -}); - -it("should remove session if token refresh returns Unauthorized error message", async () => { - // Setup existing sessions with a valid token - const validToken = createJwt({ expired: false }); - const mockSession = { - id: "test-id", - accessToken: validToken, - refreshToken: "invalid-refresh-token", - expiresInMs: 300000, // 5 minutes - account: { label: "Test User", id: "user@example.com" }, - scopes: [], - loginNeeded: false, - }; - - // Setup fetch mock - const fetchMock = fetch as any; - fetchMock.mockClear(); - - // Setup refresh to return an error containing "Unauthorized" in the message - // Status code doesn't matter here, what matters is the error message text - fetchMock.mockResolvedValueOnce({ - ok: false, - status: 403, // Could be any error code - text: async () => "Unauthorized", - }); - - // Create a mock UriHandler - const mockUriHandler = { - event: new EventEmitter(), - handleCallback: vi.fn(), - }; - - // Create a mock ExtensionContext - const mockContext = { - secrets: { - store: vi.fn(), - get: vi.fn(), - }, - subscriptions: [], - }; - - // Mock setInterval to prevent continuous refreshes - const originalSetInterval = global.setInterval; - global.setInterval = vi.fn().mockReturnValue(123 as any); - - // Set up our SecretStorage mock to return the session - mockSecretStorageGet.mockResolvedValue(JSON.stringify([mockSession])); - mockSecretStorageStore.mockClear(); - - // Import WorkOsAuthProvider after setting up all mocks - const { WorkOsAuthProvider } = await import("./WorkOsAuthProvider"); - - // Create provider instance - this will automatically call refreshSessions - const provider = new WorkOsAuthProvider(mockContext, mockUriHandler); - - // Wait for all promises to resolve, including any nested promise chains - await new Promise(process.nextTick); - - // Verify that the token refresh endpoint was called - expect(fetchMock).toHaveBeenCalledWith( - expect.any(URL), - expect.objectContaining({ - method: "POST", - body: expect.stringContaining("invalid-refresh-token"), - }), - ); - - // Verify sessions were removed due to Unauthorized error message - expect(mockSecretStorageStore).toHaveBeenCalledWith( - "workos.sessions", // Use the hard-coded key that matches our mock - expect.stringMatching(/\[\]/), - ); - - // Restore setInterval - global.setInterval = originalSetInterval; - - // Clean up - if (provider._refreshInterval) { - clearInterval(provider._refreshInterval); - provider._refreshInterval = null; - } -}); - -it("should preserve valid tokens during network errors by retrying", async () => { - // Mock Date.now to return a fixed timestamp for token validation - const originalDateNow = Date.now; - const currentTimestamp = Date.now(); - Date.now = vi.fn(() => currentTimestamp); - - // Setup with a valid token - const validToken = createJwt({ expired: false }); - const validSession = { - id: "valid-id", - accessToken: validToken, - refreshToken: "valid-refresh-token", - expiresInMs: 3600000, - account: { label: "Valid User", id: "valid@example.com" }, - scopes: [], - loginNeeded: false, - }; - - // Setup fetch mock - const fetchMock = fetch as any; - fetchMock.mockClear(); - - // Create mock objects - const mockUriHandler = { event: new EventEmitter(), handleCallback: vi.fn() }; - const mockContext = { - secrets: { store: vi.fn(), get: vi.fn() }, - subscriptions: [], - }; - - // Mock setInterval and setTimeout - const originalSetInterval = global.setInterval; - global.setInterval = vi.fn().mockReturnValue(123 as any); - - const originalSetTimeout = global.setTimeout; - global.setTimeout = vi.fn((callback) => { - callback(); - return 123 as any; - }); - - // Network error followed by success - fetchMock.mockRejectedValueOnce(new Error("Network error")); - fetchMock.mockResolvedValueOnce({ - ok: true, - json: async () => ({ - accessToken: createJwt({ expired: false }), - refreshToken: "new-refresh-token", - }), - text: async () => "", - }); - - // Setup storage - mockSecretStorageGet.mockResolvedValue(JSON.stringify([validSession])); - mockSecretStorageStore.mockClear(); - - // Import and create provider - const { WorkOsAuthProvider } = await import("./WorkOsAuthProvider"); - const provider = new WorkOsAuthProvider(mockContext, mockUriHandler); - - // Wait for promises to resolve - await new Promise(process.nextTick); - - // Check that a non-empty session array was stored (session was preserved) - const storeCall = mockSecretStorageStore.mock.calls[0]; - expect(storeCall[0]).toBe("workos.sessions"); - expect(JSON.parse(storeCall[1])).toHaveLength(1); // Should contain one session - - // Restore originals - global.setTimeout = originalSetTimeout; - global.setInterval = originalSetInterval; - Date.now = originalDateNow; - - // Clean up provider - if (provider._refreshInterval) { - clearInterval(provider._refreshInterval); - provider._refreshInterval = null; - } -}); - -it("should remove expired tokens when refresh fails with a 401 error", async () => { - // Mock Date.now to return a time that makes tokens appear expired - const originalDateNow = Date.now; - const futureTime = Date.now() + 7200000; // 2 hours in the future - Date.now = vi.fn(() => futureTime); - - // Setup with an expired token - const expiredToken = createJwt({ expired: true }); - const expiredSession = { - id: "expired-id", - accessToken: expiredToken, - refreshToken: "expired-refresh-token", - expiresInMs: 3600000, - account: { label: "Expired User", id: "expired@example.com" }, - scopes: [], - loginNeeded: false, - }; - - // Setup fetch mock - const fetchMock = fetch as any; - fetchMock.mockClear(); - - // Create mock objects - const mockUriHandler = { event: new EventEmitter(), handleCallback: vi.fn() }; - const mockContext = { - secrets: { store: vi.fn(), get: vi.fn() }, - subscriptions: [], - }; - - // Mock setInterval - const originalSetInterval = global.setInterval; - global.setInterval = vi.fn().mockReturnValue(123 as any); - - // Refresh will fail with network error - fetchMock.mockRejectedValueOnce(new Error("401")); - - // Setup storage - mockSecretStorageGet.mockResolvedValue(JSON.stringify([expiredSession])); - mockSecretStorageStore.mockClear(); - - // Import and create provider - const { WorkOsAuthProvider } = await import("./WorkOsAuthProvider"); - const provider = new WorkOsAuthProvider(mockContext, mockUriHandler); - - // Wait for promises to resolve - await new Promise(process.nextTick); - - // Check that an empty session array was stored (session was removed) - const storeCall = mockSecretStorageStore.mock.calls[0]; - expect(storeCall[0]).toBe("workos.sessions"); - expect(JSON.parse(storeCall[1])).toHaveLength(0); // Should be empty - - // Restore originals - global.setInterval = originalSetInterval; - Date.now = originalDateNow; - - // Clean up provider - if (provider._refreshInterval) { - clearInterval(provider._refreshInterval); - provider._refreshInterval = null; - } -}); - -it("should implement exponential backoff for failed refresh attempts", async () => { - // Setup existing sessions with a valid token - const validToken = createJwt({ expired: false }); - const mockSession = { - id: "test-id", - accessToken: validToken, - refreshToken: "refresh-token", - expiresInMs: 300000, // 5 minutes - account: { label: "Test User", id: "user@example.com" }, - scopes: [], - loginNeeded: false, - }; - - // Setup fetch mock - const fetchMock = fetch as any; - fetchMock.mockClear(); - - // Setup repeated network errors followed by success - fetchMock.mockRejectedValueOnce(new Error("Network error 1")); - fetchMock.mockRejectedValueOnce(new Error("Network error 2")); - fetchMock.mockResolvedValueOnce({ - ok: true, - json: async () => ({ - accessToken: createJwt({ expired: false }), - refreshToken: "new-refresh-token", - }), - text: async () => "", - }); - - // Create a mock UriHandler - const mockUriHandler = { - event: new EventEmitter(), - handleCallback: vi.fn(), - }; - - // Create a mock ExtensionContext - const mockContext = { - secrets: { - store: vi.fn(), - get: vi.fn(), - }, - subscriptions: [], - }; - - // Mock setInterval to prevent continuous refreshes - const originalSetInterval = global.setInterval; - global.setInterval = vi.fn().mockReturnValue(123 as any); - - // Track setTimeout calls - const setTimeoutSpy = vi.spyOn(global, "setTimeout"); - - // Set up our SecretStorage mock to return the session - mockSecretStorageGet.mockResolvedValue(JSON.stringify([mockSession])); - - // Import WorkOsAuthProvider after setting up all mocks - const { WorkOsAuthProvider } = await import("./WorkOsAuthProvider"); - - // Create provider instance - this will automatically call refreshSessions - const provider = new WorkOsAuthProvider(mockContext, mockUriHandler); - - // Wait for all promises to resolve for the initial refresh attempt - await new Promise(process.nextTick); - - // Verify the first fetch attempt was made - expect(fetchMock).toHaveBeenCalledTimes(1); - - // Trigger first retry - vi.advanceTimersByTime(1000); // Initial backoff - await new Promise(process.nextTick); - - // Verify the second fetch attempt was made - expect(fetchMock).toHaveBeenCalledTimes(2); - - // Trigger second retry - vi.advanceTimersByTime(2000); // Double the backoff - await new Promise(process.nextTick); - - // Verify the third fetch attempt was made - expect(fetchMock).toHaveBeenCalledTimes(3); - - // Verify setTimeout was called with increasing delays - expect(setTimeoutSpy).toHaveBeenCalledTimes(2); - - // Check that the backoff periods increased - const firstDelay = setTimeoutSpy.mock.calls[0][1]; - const secondDelay = setTimeoutSpy.mock.calls[1][1]; - - // Check that backoff increased - expect(secondDelay).toBeGreaterThan(firstDelay); - - // Restore setInterval - global.setInterval = originalSetInterval; - - // Clean up - if (provider._refreshInterval) { - clearInterval(provider._refreshInterval); - provider._refreshInterval = null; - } -}); diff --git a/extensions/vscode/src/stubs/activation.ts b/extensions/vscode/src/stubs/activation.ts deleted file mode 100644 index 60e889c834e..00000000000 --- a/extensions/vscode/src/stubs/activation.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { EXTENSION_NAME } from "core/control-plane/env"; -import * as vscode from "vscode"; - -import { getUserToken } from "./auth"; -import { RemoteConfigSync } from "./remoteConfig"; - -export async function setupRemoteConfigSync(reloadConfig: () => void) { - const settings = vscode.workspace.getConfiguration(EXTENSION_NAME); - const remoteConfigServerUrl = settings.get( - "remoteConfigServerUrl", - null, - ); - if ( - remoteConfigServerUrl === null || - remoteConfigServerUrl === undefined || - remoteConfigServerUrl.trim() === "" - ) { - return; - } - getUserToken().then(async (token) => { - await vscode.workspace - .getConfiguration(EXTENSION_NAME) - .update("userToken", token, vscode.ConfigurationTarget.Global); - try { - const configSync = new RemoteConfigSync(reloadConfig, token); - configSync.setup(); - } catch (e) { - console.warn(`Failed to sync remote config: ${e}`); - } - }); -} diff --git a/extensions/vscode/src/stubs/auth.ts b/extensions/vscode/src/stubs/auth.ts deleted file mode 100644 index 093587b90cf..00000000000 --- a/extensions/vscode/src/stubs/auth.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { EXTENSION_NAME } from "core/control-plane/env"; -import * as vscode from "vscode"; - -export async function getUserToken(): Promise { - // Prefer manual user token first - const settings = vscode.workspace.getConfiguration(EXTENSION_NAME); - const userToken = settings.get("userToken", null); - if (userToken) { - return userToken; - } - - const session = await vscode.authentication.getSession("github", [], { - createIfNone: true, - }); - return session.accessToken; -} diff --git a/extensions/vscode/src/stubs/remoteConfig.ts b/extensions/vscode/src/stubs/remoteConfig.ts deleted file mode 100644 index 4e9338afa5d..00000000000 --- a/extensions/vscode/src/stubs/remoteConfig.ts +++ /dev/null @@ -1,126 +0,0 @@ -import * as fs from "fs"; - -import { ContinueServerClient } from "core/continueServer/stubs/client"; -import { EXTENSION_NAME } from "core/control-plane/env"; -import { getConfigJsonPathForRemote } from "core/util/paths"; -import { canParseUrl } from "core/util/url"; -import * as vscode from "vscode"; - -import { CONTINUE_WORKSPACE_KEY } from "../util/workspaceConfig"; - -export class RemoteConfigSync { - private userToken: string | null; - private remoteConfigServerUrl: string | null; - private remoteConfigSyncPeriod: number; - - private syncInterval: NodeJS.Timer | undefined = undefined; - - constructor( - private triggerReloadConfig: () => void, - userToken: string | null, - ) { - let { - userToken: settingsUserToken, - remoteConfigServerUrl, - remoteConfigSyncPeriod, - } = this.loadVsCodeSettings(); - this.userToken = settingsUserToken || userToken; - this.remoteConfigServerUrl = remoteConfigServerUrl; - this.remoteConfigSyncPeriod = remoteConfigSyncPeriod; - - // Listen for changes to VS Code settings, then trigger a refresh - vscode.workspace.onDidChangeConfiguration(async (event) => { - if (event.affectsConfiguration(CONTINUE_WORKSPACE_KEY)) { - const { userToken, remoteConfigServerUrl, remoteConfigSyncPeriod } = - await this.loadVsCodeSettings(); - if ( - userToken !== this.userToken || - remoteConfigServerUrl !== this.remoteConfigServerUrl || - remoteConfigSyncPeriod !== this.remoteConfigSyncPeriod - ) { - this.userToken = userToken; - this.remoteConfigServerUrl = remoteConfigServerUrl; - this.remoteConfigSyncPeriod = remoteConfigSyncPeriod; - - this.setInterval(); - } - } - }); - } - - private loadVsCodeSettings() { - const settings = vscode.workspace.getConfiguration(EXTENSION_NAME); - const userToken = settings.get("userToken", null); - const remoteConfigServerUrl = settings.get( - "remoteConfigServerUrl", - null, - ); - const remoteConfigSyncPeriod = settings.get( - "remoteConfigSyncPeriod", - 60, - ); - - return { - userToken, - remoteConfigServerUrl, - remoteConfigSyncPeriod, - }; - } - - async setup() { - if ( - this.userToken === null || - this.remoteConfigServerUrl === null || - this.remoteConfigServerUrl.trim() === "" - ) { - return; - } - if (!canParseUrl(this.remoteConfigServerUrl)) { - vscode.window.showErrorMessage( - "The value set for 'remoteConfigServerUrl' is not valid: ", - this.remoteConfigServerUrl, - ); - return; - } - - // Sync once - await this.sync(this.userToken, this.remoteConfigServerUrl); - - // Set timer to sync at user-defined interval - this.setInterval(); - } - - private setInterval() { - if (this.syncInterval !== undefined) { - // @ts-ignore - clearInterval(this.syncInterval); - } - this.syncInterval = setInterval( - () => { - if (!this.userToken || !this.remoteConfigServerUrl) { - return; - } - this.sync(this.userToken, this.remoteConfigServerUrl); - }, - this.remoteConfigSyncPeriod * 1000 * 60, - ); - } - - async sync(userToken: string, remoteConfigServerUrl: string) { - try { - const client = new ContinueServerClient( - remoteConfigServerUrl.toString(), - userToken, - ); - const { configJson } = await client.getConfig(); - - fs.writeFileSync( - getConfigJsonPathForRemote(remoteConfigServerUrl), - configJson, - ); - this.triggerReloadConfig(); - } catch (e) { - vscode.window.showWarningMessage(`Failed to sync remote config: ${e}`); - } - } -} diff --git a/extensions/vscode/src/util/ideUtils.ts b/extensions/vscode/src/util/ideUtils.ts index 6dd9a3aa2b6..5d42c06cc8d 100644 --- a/extensions/vscode/src/util/ideUtils.ts +++ b/extensions/vscode/src/util/ideUtils.ts @@ -1,4 +1,4 @@ -import { EXTENSION_NAME } from "core/control-plane/env"; +import { EXTENSION_NAME } from "core/util/constants"; import { findUriInDirs } from "core/util/uri"; import _ from "lodash"; import * as URI from "uri-js"; diff --git a/gui/src/components/AssistantAndOrgListbox/OrganizationOption.tsx b/gui/src/components/AssistantAndOrgListbox/OrganizationOption.tsx deleted file mode 100644 index 54699d332f6..00000000000 --- a/gui/src/components/AssistantAndOrgListbox/OrganizationOption.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import { Cog6ToothIcon } from "@heroicons/react/24/outline"; -import { - BuildingOfficeIcon, - UserCircleIcon, - UserIcon, -} from "@heroicons/react/24/solid"; -import { useContext } from "react"; -import { useNavigate } from "react-router-dom"; -import { IdeMessengerContext } from "../../context/IdeMessenger"; -import { useAppDispatch, useAppSelector } from "../../redux/hooks"; -import { setSelectedOrgId } from "../../redux/slices/profilesSlice"; -import { CONFIG_ROUTES } from "../../util/navigation"; -import { Button, ListboxOption } from "../ui"; - -interface OrganizationOptionProps { - organization: { id: string; name: string; iconUrl?: string | null }; - onClose: () => void; -} - -function getOrgIcon( - org: { name: string; iconUrl?: string | null }, - size: "sm" | "md" = "sm", -) { - const sizeClasses = size === "sm" ? "h-3.5 w-3.5" : "xs:h-4 xs:w-4 h-3 w-3"; - - if (org.iconUrl) { - return ( - - ); - } - - const IconComponent = - org.name === "Personal" - ? size === "md" - ? UserIcon - : UserCircleIcon - : BuildingOfficeIcon; - return ; -} - -export function OrganizationOption({ - organization, - onClose, -}: OrganizationOptionProps) { - const dispatch = useAppDispatch(); - const navigate = useNavigate(); - const ideMessenger = useContext(IdeMessengerContext); - const selectedOrgId = useAppSelector( - (state) => state.profiles.selectedOrganizationId, - ); - const isSelected = selectedOrgId === organization.id; - - function handleOptionClick() { - dispatch(setSelectedOrgId(organization.id)); - ideMessenger.post("didChangeSelectedOrg", { - id: organization.id, - }); - onClose(); - } - - return ( - -
-
-
- {getOrgIcon(organization, "sm")} -
- - {organization.name} - -
-
- -
-
-
- ); -} diff --git a/gui/src/components/AssistantAndOrgListbox/OrganizationOptions.tsx b/gui/src/components/AssistantAndOrgListbox/OrganizationOptions.tsx deleted file mode 100644 index d374870d44f..00000000000 --- a/gui/src/components/AssistantAndOrgListbox/OrganizationOptions.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { useAuth } from "../../context/Auth"; -import { OrganizationOption } from "./OrganizationOption"; - -interface OrganizationOptionsProps { - onClose: () => void; -} - -export function OrganizationOptions({ onClose }: OrganizationOptionsProps) { - const { organizations } = useAuth(); - - return ( -
- {organizations.map((org) => ( - - ))} -
- ); -} diff --git a/gui/src/components/AssistantAndOrgListbox/index.tsx b/gui/src/components/AssistantAndOrgListbox/index.tsx index cf767c6c3fa..a4770ef1f1b 100644 --- a/gui/src/components/AssistantAndOrgListbox/index.tsx +++ b/gui/src/components/AssistantAndOrgListbox/index.tsx @@ -1,19 +1,14 @@ import { ArrowPathIcon, - ArrowRightStartOnRectangleIcon, Cog6ToothIcon, PlusIcon, } from "@heroicons/react/24/outline"; -import { isOnPremSession } from "core/control-plane/AuthTypes"; import { useContext, useEffect, useRef } from "react"; import { useNavigate } from "react-router-dom"; import { useAuth } from "../../context/Auth"; import { IdeMessengerContext } from "../../context/IdeMessenger"; import { useAppDispatch, useAppSelector } from "../../redux/hooks"; -import { - selectCurrentOrg, - setSelectedProfile, -} from "../../redux/slices/profilesSlice"; +import { setSelectedProfile } from "../../redux/slices/profilesSlice"; import { getMetaKeyLabel, isMetaEquivalentKeyPressed } from "../../util"; import { cn } from "../../util/cn"; import { CONFIG_ROUTES } from "../../util/navigation"; @@ -24,9 +19,7 @@ import { Transition, useFontSize, } from "../ui"; -import { Divider } from "../ui/Divider"; import { AssistantOptions } from "./AssistantOptions"; -import { OrganizationOptions } from "./OrganizationOptions"; import { SelectedAssistantButton } from "./SelectedAssistantButton"; export interface AssistantAndOrgListboxProps { @@ -39,21 +32,10 @@ export function AssistantAndOrgListbox({ const dispatch = useAppDispatch(); const navigate = useNavigate(); const listboxRef = useRef(null); - const currentOrg = useAppSelector(selectCurrentOrg); const ideMessenger = useContext(IdeMessengerContext); - const { - profiles, - selectedProfile, - session, - logout, - login, - organizations, - refreshProfiles, - } = useAuth(); + const { profiles, selectedProfile, refreshProfiles } = useAuth(); const configLoading = useAppSelector((store) => store.config.loading); const tinyFont = useFontSize(-4); - const shouldRenderOrgInfo = - session && organizations.length > 1 && !isOnPremSession(session); function close() { // Close the listbox by clicking outside or programmatically @@ -62,21 +44,7 @@ export function AssistantAndOrgListbox({ } function onNewAssistant() { - if (session) { - void ideMessenger.request("controlPlane/openUrl", { - path: "/new", - orgSlug: currentOrg?.slug, - }); - } else { - void ideMessenger.request("config/newAssistantFile", undefined); - } - close(); - } - - function onNewOrganization() { - void ideMessenger.request("controlPlane/openUrl", { - path: "/organizations/new", - }); + void ideMessenger.request("config/newAssistantFile", undefined); close(); } @@ -85,21 +53,6 @@ export function AssistantAndOrgListbox({ close(); } - function onOrganizationsConfig() { - navigate(CONFIG_ROUTES.ORGANIZATIONS); - close(); - } - - function onRulesConfig() { - navigate(CONFIG_ROUTES.RULES); - close(); - } - - function onToolsConfig() { - navigate(CONFIG_ROUTES.TOOLS); - close(); - } - useEffect(() => { let lastToggleTime = 0; const DEBOUNCE_MS = 800; @@ -139,7 +92,7 @@ export function AssistantAndOrgListbox({ return () => { window.removeEventListener("keydown", handleKeyDown); }; - }, [currentOrg, selectedProfile]); + }, [selectedProfile]); return ( @@ -188,45 +141,6 @@ export function AssistantAndOrgListbox({ onClose={close} /> - {shouldRenderOrgInfo && ( - <> - -
- - Organizations - -
- - -
-
- - - - - - )} - {/* Settings Section */} {variant !== "sidebar" && (
@@ -249,41 +163,6 @@ export function AssistantAndOrgListbox({ Reload
- {session ? ( - - ) : ( - - )} - - )} diff --git a/gui/src/components/BackgroundMode/AgentsList.tsx b/gui/src/components/BackgroundMode/AgentsList.tsx index 8e817834e02..cd9a02b18e1 100644 --- a/gui/src/components/BackgroundMode/AgentsList.tsx +++ b/gui/src/components/BackgroundMode/AgentsList.tsx @@ -1,314 +1,11 @@ -import { ArrowPathIcon, PlayIcon } from "@heroicons/react/24/outline"; -import { useContext, useEffect, useState } from "react"; -import { normalizeRepoUrl } from "core/util/repoUrl"; -import { useAuth } from "../../context/Auth"; -import { IdeMessengerContext } from "../../context/IdeMessenger"; -import { useAppSelector } from "../../redux/hooks"; -import { selectCurrentOrg } from "../../redux/slices/profilesSlice"; -import { Button } from "../ui"; - -interface Agent { - id: string; - name: string | null; - status: string; - repoUrl: string; - createdAt: string; - metadata?: { - github_repo?: string; - }; -} - interface AgentsListProps { isCreatingAgent?: boolean; } -interface WorkspaceInfo { - repoUrl: string; - workspaceDir: string; - workspaceName: string; -} - export function AgentsList({ isCreatingAgent = false }: AgentsListProps) { - const { session } = useAuth(); - const ideMessenger = useContext(IdeMessengerContext); - const currentOrg = useAppSelector(selectCurrentOrg); - const [agents, setAgents] = useState(null); - const [totalCount, setTotalCount] = useState(0); - const [error, setError] = useState(null); - const [workspaces, setWorkspaces] = useState([]); - - // Fetch workspace repo URLs once on mount - useEffect(() => { - async function fetchWorkspaceRepos() { - try { - const workspaceDirs = await ideMessenger.request( - "getWorkspaceDirs", - undefined, - ); - if (workspaceDirs.status === "success" && workspaceDirs.content) { - const workspaceInfos: WorkspaceInfo[] = []; - for (const dir of workspaceDirs.content) { - const repoNameResult = await ideMessenger.request("getRepoName", { - dir, - }); - if (repoNameResult.status === "success" && repoNameResult.content) { - const normalizedUrl = normalizeRepoUrl(repoNameResult.content); - if (normalizedUrl) { - // Extract workspace name from directory path (last segment) - const workspaceName = dir.split("/").pop() || dir; - workspaceInfos.push({ - repoUrl: normalizedUrl, - workspaceDir: dir, - workspaceName, - }); - } - } - } - setWorkspaces(workspaceInfos); - } - } catch (err) { - console.error("Failed to fetch workspace repos:", err); - } - } - void fetchWorkspaceRepos(); - }, [ideMessenger]); - - useEffect(() => { - async function fetchAgents() { - if (!session) { - setAgents([]); - setTotalCount(0); - return; - } - - try { - const organizationId = - currentOrg?.id !== "personal" ? currentOrg?.id : undefined; - const result = await ideMessenger.request("listBackgroundAgents", { - organizationId, - limit: 5, - }); - - if ("status" in result && result.status === "success") { - const content = result.content as { - agents: Agent[]; - totalCount: number; - }; - setAgents(content.agents || []); - setTotalCount(content.totalCount || 0); - setError(null); - } else if ("error" in result) { - setError(result.error); - setAgents([]); - setTotalCount(0); - } else { - setAgents([]); - setTotalCount(0); - } - } catch (err: any) { - console.error("Failed to fetch agents:", err); - setError(err.message || "Failed to load agents"); - setAgents([]); - setTotalCount(0); - } - } - - void fetchAgents(); - - // Poll for updates every 10 seconds - const interval = setInterval(() => { - void fetchAgents(); - }, 10000); - - return () => clearInterval(interval); - }, [session, ideMessenger, currentOrg]); - - // Helper function to check if an agent's repo matches any workspace repo - const isAgentInCurrentWorkspace = (agent: Agent): boolean => { - // Get all possible agent repo URLs (both repoUrl and metadata.github_repo) - const agentUrls = [agent.repoUrl, agent.metadata?.github_repo] - .filter((url): url is string => Boolean(url)) - .map(normalizeRepoUrl) - .filter((url) => url !== ""); - - // Check if any of the agent URLs match any workspace URL - return workspaces.some((workspace) => - agentUrls.some((agentUrl) => agentUrl === workspace.repoUrl), - ); - }; - - // Helper function to get the agent's repository name (for display in tooltips) - const getAgentRepoName = (agent: Agent): string | null => { - // Try to extract repo name from the agent's repository URL - // Handles URLs like: https://github.com/org/repo or git@github.com:org/repo.git - const repoUrl = agent.metadata?.github_repo || agent.repoUrl; - if (!repoUrl) return null; - - try { - // Remove .git suffix if present - const cleanUrl = repoUrl.replace(/\.git$/, ""); - // Extract the last path segment (repo name) - const parts = cleanUrl.split("/"); - const repoName = parts[parts.length - 1]; - return repoName || null; - } catch { - return null; - } - }; - - const handleOpenLocally = (agent: Agent, e: React.MouseEvent) => { - e.stopPropagation(); // Prevent opening the agent detail page - ideMessenger.post("openAgentLocally", { agentSessionId: agent.id }); - }; - - if (error) { - return ( -
- Error loading agents: {error} -
- ); - } - - if (agents === null) { - return ( -
- -
- ); - } - - if (agents.length === 0) { - return ( -
- No background tasks yet. Submit a message above to create one. -
- ); - } - return ( -
-
- Background Tasks -
-
- {agents.map((agent) => { - const isInWorkspace = isAgentInCurrentWorkspace(agent); - const canOpenLocally = isInWorkspace; - const agentRepoName = getAgentRepoName(agent); - - return ( -
{ - // Open agent detail in browser - ideMessenger.post("controlPlane/openUrl", { - path: `agents/${agent.id}`, - }); - }} - > -
-
-
- {agent.name || "Unnamed Agent"} -
-
- {agent.metadata?.github_repo || agent.repoUrl} -
-
-
- - -
-
-
- {formatRelativeTime(agent.createdAt)} -
-
- ); - })} - {totalCount > agents.length && ( -
- -
- )} -
+
+ Background agents are not available.
); } - -function AgentStatusBadge({ status }: { status: string }) { - const statusColors: Record = { - running: "border-success text-success", - pending: "border-warning text-warning", - creating: "border-info text-info", - failure: "border-error text-error", - suspended: "border-description text-description", - }; - - const color = statusColors[status] || "border-description text-description"; - - return ( - - {status} - - ); -} - -function formatRelativeTime(dateString: string): string { - try { - const date = new Date(dateString); - const timestamp = date.getTime(); - - // Guard against invalid dates (NaN) - if (isNaN(timestamp)) { - return dateString; - } - - const now = new Date(); - const diffMs = now.getTime() - timestamp; - const diffMins = Math.floor(diffMs / 60000); - const diffHours = Math.floor(diffMs / 3600000); - const diffDays = Math.floor(diffMs / 86400000); - - if (diffMins < 1) { - return "just now"; - } else if (diffMins < 60) { - return `${diffMins}m ago`; - } else if (diffHours < 24) { - return `${diffHours}h ago`; - } else { - return `${diffDays}d ago`; - } - } catch { - return dateString; - } -} diff --git a/gui/src/components/BackgroundMode/BackgroundModeView.tsx b/gui/src/components/BackgroundMode/BackgroundModeView.tsx index b721d28c3e7..8b3502a228f 100644 --- a/gui/src/components/BackgroundMode/BackgroundModeView.tsx +++ b/gui/src/components/BackgroundMode/BackgroundModeView.tsx @@ -1,13 +1,4 @@ -import { - ArrowPathIcon, - ExclamationTriangleIcon, - RocketLaunchIcon, -} from "@heroicons/react/24/outline"; -import { useCallback, useContext, useEffect, useState } from "react"; -import { useAuth } from "../../context/Auth"; -import { IdeMessengerContext } from "../../context/IdeMessenger"; -import { useAppSelector } from "../../redux/hooks"; -import { selectCurrentOrg } from "../../redux/slices/profilesSlice"; +import { ArrowPathIcon } from "@heroicons/react/24/outline"; import { AgentsList } from "./AgentsList"; interface BackgroundModeViewProps { @@ -17,125 +8,8 @@ interface BackgroundModeViewProps { export function BackgroundModeView({ isCreatingAgent = false, }: BackgroundModeViewProps) { - const { session, login } = useAuth(); - const ideMessenger = useContext(IdeMessengerContext); - const currentOrg = useAppSelector(selectCurrentOrg); - const [isLoggingIn, setIsLoggingIn] = useState(false); - const [showGitHubSetup, setShowGitHubSetup] = useState(false); - const [checkingGitHub, setCheckingGitHub] = useState(true); - - const handleSignIn = useCallback(async () => { - setIsLoggingIn(true); - try { - await login(false); - } catch (error) { - console.error("Login failed:", error); - } finally { - setIsLoggingIn(false); - } - }, [login]); - - const handleOpenGitHubSettings = useCallback(() => { - // Open the hub settings page for GitHub integration - ideMessenger.post("controlPlane/openUrl", { - path: "settings/integrations/github", - orgSlug: currentOrg?.slug, - }); - }, [ideMessenger, currentOrg]); - - // Check if user has GitHub installations when signed in - useEffect(() => { - async function checkGitHubAuth() { - if (!session) { - setCheckingGitHub(false); - return; - } - - try { - setCheckingGitHub(true); - // Try to list agents - if this fails with GitHub token error, - // we know GitHub isn't connected - const organizationId = - currentOrg?.id !== "personal" ? currentOrg?.id : undefined; - const result = await ideMessenger.request("listBackgroundAgents", { - organizationId, - }); - - // Check for error response - if ("error" in result) { - // Check if error is related to GitHub token - if ( - result.error?.includes("GitHub token") || - result.error?.includes("GitHub App") - ) { - setShowGitHubSetup(true); - } - } else { - // If we got here without error, GitHub is connected - setShowGitHubSetup(false); - } - } catch (error: any) { - // Check if error is related to GitHub token - if ( - error?.message?.includes("GitHub token") || - error?.message?.includes("GitHub App") - ) { - setShowGitHubSetup(true); - } - } finally { - setCheckingGitHub(false); - } - } - - void checkGitHubAuth(); - }, [session, ideMessenger, currentOrg]); - - if (!session) { - return ( -
- -
-

Background Agents

-

- Trigger long-running background agents that work on your codebase - autonomously. Sign in to Continue to get started. -

- -
-
- ); - } - return (
- {!checkingGitHub && showGitHubSetup && ( -
-
- -
-

- Connect GitHub -

-

- Background agents need access to your GitHub repositories. - Connect your GitHub account to get started. -

- -
-
-
- )}
Submit a task above to run a background agent. Your task will appear diff --git a/gui/src/components/History/HistoryTableRow.tsx b/gui/src/components/History/HistoryTableRow.tsx index 45c7f130976..4f37929359d 100644 --- a/gui/src/components/History/HistoryTableRow.tsx +++ b/gui/src/components/History/HistoryTableRow.tsx @@ -1,11 +1,9 @@ import { ArrowDownOnSquareIcon, - CloudIcon, PencilSquareIcon, TrashIcon, } from "@heroicons/react/24/outline"; import { BaseSessionMetadata } from "core"; -import type { RemoteSessionMetadata } from "core/control-plane/client"; import { getUriPathBasename } from "core/util/uri"; import React, { useContext, useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; @@ -16,7 +14,6 @@ import { exitEdit } from "../../redux/thunks/edit"; import { deleteSession, getSession, - loadRemoteSession, loadSession, updateSession, } from "../../redux/thunks/session"; @@ -30,7 +27,7 @@ export function HistoryTableRow({ sessionMetadata, index, }: { - sessionMetadata: BaseSessionMetadata | RemoteSessionMetadata; + sessionMetadata: BaseSessionMetadata; index: number; }) { const dispatch = useAppDispatch(); @@ -56,18 +53,10 @@ export function HistoryTableRow({ }); } }; - const isRemote = "isRemote" in sessionMetadata && sessionMetadata.isRemote; const handleKeyUp = async (e: React.KeyboardEvent) => { if (e.key === "Enter") { if (sessionTitleEditValue !== sessionMetadata.title) { - // Don't allow editing remote sessions - if (isRemote) { - setSessionTitleEditValue(sessionMetadata.title); - setEditing(false); - return; - } - // imperfect solution of loading session just to update it // but fine for now, pretty low latency const currentSession = await getSession( @@ -95,21 +84,6 @@ export function HistoryTableRow({ data-testid={`history-row-${index}`} className="hover:bg-input relative mb-2 box-border flex w-full cursor-pointer overflow-hidden rounded-lg p-3" onClick={async () => { - // Handle remote sessions - load remote session data - if (isRemote) { - const remoteSession = sessionMetadata as RemoteSessionMetadata; - await dispatch(exitEdit({})); - await dispatch( - loadRemoteSession({ - remoteId: remoteSession.remoteId, - saveCurrentSession: true, - }), - ); - navigate("/"); - return; - } - - // Handle local sessions - load and navigate as before await dispatch(exitEdit({})); if (sessionMetadata.sessionId !== currentSessionId) { await dispatch( @@ -140,11 +114,6 @@ export function HistoryTableRow({ {sessionMetadata.title} - {isRemote && ( - - Remote - - )} {sessionMetadata.messageCount !== undefined && ( - {isRemote ? ( - { - e.stopPropagation(); - await ideMessenger.request("controlPlane/openUrl", { - path: `/agents/${sessionMetadata.remoteId}`, - }); - }} - > - - - ) : ( + { <> - )} + } )} diff --git a/gui/src/components/History/index.tsx b/gui/src/components/History/index.tsx index db83f85af12..c206778e5d9 100644 --- a/gui/src/components/History/index.tsx +++ b/gui/src/components/History/index.tsx @@ -1,5 +1,4 @@ import { BaseSessionMetadata } from "core"; -import type { RemoteSessionMetadata } from "core/control-plane/client"; import MiniSearch from "minisearch"; import React, { Fragment, @@ -67,10 +66,7 @@ export function History() { const platform = useMemo(() => getPlatform(), []); - const filteredAndSortedSessions: ( - | BaseSessionMetadata - | RemoteSessionMetadata - )[] = useMemo(() => { + const filteredAndSortedSessions: BaseSessionMetadata[] = useMemo(() => { // 1. Exact phrase matching const exactResults = minisearch.search(searchTerm, { fuzzy: false, diff --git a/gui/src/components/History/util.ts b/gui/src/components/History/util.ts index 18c6f7d4213..dd34b868411 100644 --- a/gui/src/components/History/util.ts +++ b/gui/src/components/History/util.ts @@ -1,5 +1,4 @@ import { BaseSessionMetadata } from "core"; -import type { RemoteSessionMetadata } from "core/control-plane/client"; export const parseDate = (date: string): Date => { let dateObj = new Date(date); @@ -11,11 +10,11 @@ export const parseDate = (date: string): Date => { export interface SessionGroup { label: string; - sessions: (BaseSessionMetadata | RemoteSessionMetadata)[]; + sessions: BaseSessionMetadata[]; } export const groupSessionsByDate = ( - sessions: (BaseSessionMetadata | RemoteSessionMetadata)[], + sessions: BaseSessionMetadata[], ): SessionGroup[] => { const yesterday = new Date(Date.now() - 1000 * 60 * 60 * 24); const lastWeek = new Date(Date.now() - 1000 * 60 * 60 * 24 * 7); diff --git a/gui/src/components/Layout.tsx b/gui/src/components/Layout.tsx index 440d8c28031..9283773fdcd 100644 --- a/gui/src/components/Layout.tsx +++ b/gui/src/components/Layout.tsx @@ -1,5 +1,5 @@ import { OnboardingModes } from "core/protocol/core"; -import { useContext, useEffect, useState } from "react"; +import { useContext, useEffect } from "react"; import { Outlet, useLocation, useNavigate } from "react-router-dom"; import styled from "styled-components"; import { CustomScrollbarDiv } from "."; @@ -25,7 +25,6 @@ import { } from "./OnboardingCard"; import OSRContextMenu from "./OSRContextMenu"; - const LayoutTopDiv = styled(CustomScrollbarDiv)` height: 100%; position: relative; @@ -40,7 +39,6 @@ const GridDiv = styled.div` `; const Layout = () => { - const [showStagingIndicator, setShowStagingIndicator] = useState(false); const navigate = useNavigate(); const location = useLocation(); const dispatch = useAppDispatch(); @@ -56,17 +54,6 @@ const Layout = () => { location.pathname === ROUTES.HOME || location.pathname === ROUTES.HOME_INDEX; - useEffect(() => { - (async () => { - const response = await ideMessenger.request( - "controlPlane/getEnvironment", - undefined, - ); - response.status === "success" && - setShowStagingIndicator(response.content.AUTH_TYPE.includes("staging")); - })(); - }, []); - useWebviewListener( "newSession", async () => { @@ -236,44 +223,35 @@ const Layout = () => { return ( - - {showStagingIndicator && ( - - )} - -
+ +
+ { + dispatch(setShowDialog(false)); + }} + onClose={() => { + dispatch(setShowDialog(false)); }} - > - { - dispatch(setShowDialog(false)); - }} - onClose={() => { - dispatch(setShowDialog(false)); - }} - message={dialogMessage} - /> + message={dialogMessage} + /> - - - {/* The fatal error for chat is shown below input */} - {!isHome && } - -
-
- + + + {/* The fatal error for chat is shown below input */} + {!isHome && } + +
+
+ ); diff --git a/gui/src/components/ModeSelect/ModeSelect.tsx b/gui/src/components/ModeSelect/ModeSelect.tsx index 8b69bca2065..b8e5f017b96 100644 --- a/gui/src/components/ModeSelect/ModeSelect.tsx +++ b/gui/src/components/ModeSelect/ModeSelect.tsx @@ -7,7 +7,6 @@ import { import { MessageModes } from "core"; import { isRecommendedAgentModel } from "core/llm/toolSupport"; import { useCallback, useEffect, useMemo } from "react"; -import { useAuth } from "../../context/Auth"; import { useAppDispatch, useAppSelector } from "../../redux/hooks"; import { selectSelectedChatModel } from "../../redux/slices/configSlice"; import { setMode } from "../../redux/slices/sessionSlice"; @@ -21,7 +20,6 @@ export function ModeSelect() { const dispatch = useAppDispatch(); const mode = useAppSelector((store) => store.session.mode); const selectedModel = useAppSelector(selectSelectedChatModel); - const { selectedProfile } = useAuth(); const isGoodAtAgentMode = useMemo(() => { if (!selectedModel) { @@ -30,10 +28,6 @@ export function ModeSelect() { return isRecommendedAgentModel(selectedModel.model); }, [selectedModel]); - const isLocalAgent = useMemo(() => { - return selectedProfile?.profileType === "local"; - }, [selectedProfile]); - const { mainEditor } = useMainEditor(); const metaKeyLabel = useMemo(() => { return getMetaKeyLabel(); @@ -44,9 +38,6 @@ export function ModeSelect() { dispatch(setMode("plan")); } else if (mode === "plan") { dispatch(setMode("agent")); - } else if (mode === "agent") { - // Skip background mode if local agent is selected - dispatch(setMode(isLocalAgent ? "chat" : "background")); } else { dispatch(setMode("chat")); } @@ -54,7 +45,7 @@ export function ModeSelect() { if (!document.activeElement?.classList?.contains("ProseMirror")) { mainEditor?.commands.focus(); } - }, [mode, mainEditor, isLocalAgent]); + }, [mode, mainEditor]); const selectMode = useCallback( (newMode: MessageModes) => { @@ -81,13 +72,6 @@ export function ModeSelect() { return () => document.removeEventListener("keydown", handleKeyDown); }, [cycleMode]); - // Auto-switch from background mode when local agent is selected - useEffect(() => { - if (mode === "background" && isLocalAgent) { - dispatch(setMode("agent")); - } - }, [mode, isLocalAgent, dispatch]); - const notGreatAtAgent = (mode: string) => ( <> - {mode === "chat" - ? "Chat" - : mode === "agent" - ? "Agent" - : mode === "background" - ? "Background" - : "Plan"} + {mode === "chat" ? "Chat" : mode === "agent" ? "Agent" : "Plan"} - -
- - Background - - - -
- {isLocalAgent && ( - - )} - -
-
{`${metaKeyLabel} . for next mode`}
diff --git a/gui/src/components/OnboardingCard/components/OnboardingCardLanding.tsx b/gui/src/components/OnboardingCard/components/OnboardingCardLanding.tsx index e0c20ccde76..02ef41e7bdd 100644 --- a/gui/src/components/OnboardingCard/components/OnboardingCardLanding.tsx +++ b/gui/src/components/OnboardingCard/components/OnboardingCardLanding.tsx @@ -1,12 +1,5 @@ -import { useContext } from "react"; -import { Button, SecondaryButton } from "../.."; -import { useAuth } from "../../../context/Auth"; -import { IdeMessengerContext } from "../../../context/IdeMessenger"; -import { useAppDispatch, useAppSelector } from "../../../redux/hooks"; -import { selectCurrentOrg } from "../../../redux/slices/profilesSlice"; -import { selectFirstHubProfile } from "../../../redux/thunks/selectFirstHubProfile"; +import { SecondaryButton } from "../.."; import ContinueLogo from "../../svg/ContinueLogo"; -import { useOnboardingCard } from "../hooks/useOnboardingCard"; export function OnboardingCardLanding({ onSelectConfigure, @@ -15,54 +8,18 @@ export function OnboardingCardLanding({ onSelectConfigure: () => void; isDialog?: boolean; }) { - const ideMessenger = useContext(IdeMessengerContext); - const onboardingCard = useOnboardingCard(); - const auth = useAuth(); - const currentOrg = useAppSelector(selectCurrentOrg); - const dispatch = useAppDispatch(); - - function onGetStarted() { - void auth.login(true).then((success) => { - if (success) { - onboardingCard.close(isDialog); - - // A new assistant is created when the account is created - // We want to switch to this immediately - void dispatch(selectFirstHubProfile()); - - ideMessenger.post("showTutorial", undefined); - ideMessenger.post("showToast", ["info", "🎉 Welcome to Continue!"]); - } - }); - } - - function openBillingPage() { - ideMessenger.post("controlPlane/openUrl", { - path: "settings/billing", - orgSlug: currentOrg?.slug, - }); - onboardingCard.close(isDialog); - } - return ( -
+

- Log in to Continue Hub to get started with AI-powered coding + Get started with AI-powered coding by configuring your models

- - - Or, configure your own models + Configure your models
); diff --git a/gui/src/components/OnboardingCard/components/OnboardingModelsAddOnTab.tsx b/gui/src/components/OnboardingCard/components/OnboardingModelsAddOnTab.tsx index 0b9f45acef2..b538015ee4a 100644 --- a/gui/src/components/OnboardingCard/components/OnboardingModelsAddOnTab.tsx +++ b/gui/src/components/OnboardingCard/components/OnboardingModelsAddOnTab.tsx @@ -1,49 +1,22 @@ -import { CheckIcon } from "@heroicons/react/24/outline"; import { useContext } from "react"; import { Button } from "../.."; -import { useAuth } from "../../../context/Auth"; import { IdeMessengerContext } from "../../../context/IdeMessenger"; -import { useAppDispatch } from "../../../redux/hooks"; -import { selectFirstHubProfile } from "../../../redux/thunks/selectFirstHubProfile"; import { useOnboardingCard } from "../hooks/useOnboardingCard"; /** - * Models Add-On tab component with two-step onboarding: - * 1. Create Account - Uses auth.login(true) to sign up and redirect back to IDE - * 2. Purchase Credits - Opens /settings/billing to purchase credits + * Models Add-On tab - directs users to configure their own models. */ export function OnboardingModelsAddOnTab() { const ideMessenger = useContext(IdeMessengerContext); const { close } = useOnboardingCard(); - const auth = useAuth(); - const dispatch = useAppDispatch(); - const isLoggedIn = !!auth.session; - - function handleCreateAccount() { - void auth.login(true).then((success) => { - if (success) { - // A new assistant is created when the account is created - // Switch to it immediately - void dispatch(selectFirstHubProfile()); - ideMessenger.post("showToast", ["info", "Account created!"]); - } - }); - } - - function handlePurchaseCredits() { - ideMessenger.post("controlPlane/openUrl", { - path: "settings/billing", + function handleConfigureModels() { + ideMessenger.post("config/openProfile", { + profileId: undefined, }); close(); } - function openPricingPage() { - void ideMessenger.request("controlPlane/openUrl", { - path: "pricing", - }); - } - return (
@@ -52,77 +25,15 @@ export function OnboardingModelsAddOnTab() { - Use a{" "} - - variety of frontier models - {" "} - at cost. + Configure your own model providers to get started.
- {/* Vertical step indicators */}
- {/* Step 1: Create Account */} -
-
-
- {isLoggedIn ? : "1"} -
- - Create Account - -
-
-
- -
-
- - {/* Step 2: Purchase Credits */} -
-
-
- 2 -
- - Purchase Credits - -
-
-
- -
+
+
diff --git a/gui/src/components/StarterCreditsButton.tsx b/gui/src/components/StarterCreditsButton.tsx deleted file mode 100644 index 51f58d16e14..00000000000 --- a/gui/src/components/StarterCreditsButton.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { GiftIcon } from "@heroicons/react/24/outline"; -import { CreditStatus } from "core/control-plane/client"; -import StarterCreditsPopover from "./StarterCreditsPopover"; - -interface FreeTrialButtonProps { - creditStatus?: CreditStatus | null; - refreshCreditStatus?: () => Promise; -} - -/** - * @deprecated Use StarterCreditsPopover with a custom button/icon instead. - * This component is kept for backward compatibility. - */ -export default function StarterCreditsButton({ - creditStatus, - refreshCreditStatus, -}: FreeTrialButtonProps) { - return ( - - - - - - ); -} diff --git a/gui/src/components/StarterCreditsPopover.tsx b/gui/src/components/StarterCreditsPopover.tsx deleted file mode 100644 index c80777199e9..00000000000 --- a/gui/src/components/StarterCreditsPopover.tsx +++ /dev/null @@ -1,196 +0,0 @@ -import { - ArrowPathIcon, - GiftIcon, - XMarkIcon, -} from "@heroicons/react/24/outline"; -import { CreditStatus } from "core/control-plane/client"; -import { useContext, useState } from "react"; -import { Button, SecondaryButton, vscButtonBackground } from "."; -import { useAuth } from "../context/Auth"; -import { IdeMessengerContext } from "../context/IdeMessenger"; -import { cn } from "../util/cn"; -import { setLocalStorage } from "../util/localStorage"; -import { ToolbarButtonWithTooltip } from "./StyledMarkdownPreview/StepContainerPreToolbar/ToolbarButtonWithTooltip"; -import { Listbox, ListboxButton, ListboxOptions, Transition } from "./ui"; - -interface ProgressBarProps { - label: string; - current: number; - total: number; -} - -function ProgressBar({ label, current, total }: ProgressBarProps) { - const percentage = Math.min((current / total) * 100, 100); - - return ( -
-
- {label} - - ${(current / 100).toFixed(2)} / ${(total / 100).toFixed(2)} - -
-
-
-
-
- ); -} - -interface CreditStatusProgressBarsProps { - creditStatus: CreditStatus; -} - -function CreditStatusProgressBar({ - creditStatus, -}: CreditStatusProgressBarsProps) { - const total = 50; - const usage = total - (creditStatus.creditBalance ?? 0); - - return ( - - ); -} - -interface StarterCreditsPopoverProps { - creditStatus?: CreditStatus | null; - refreshCreditStatus?: () => Promise; - children: React.ReactNode; -} - -export default function StarterCreditsPopover({ - creditStatus, - refreshCreditStatus, - children, -}: StarterCreditsPopoverProps) { - const ideMessenger = useContext(IdeMessengerContext); - const { refreshProfiles } = useAuth(); - const [isRefreshing, setIsRefreshing] = useState(false); - - const onSetupApiKeys = async () => { - await ideMessenger.request("controlPlane/openUrl", { - path: "setup-models/api-keys", - orgSlug: undefined, - }); - }; - - const onPurchaseCredits = async () => { - await ideMessenger.request("controlPlane/openUrl", { - path: "settings/billing", - orgSlug: undefined, - }); - }; - - const onHideStarterCreditsUsage = async () => { - // At this point the user doesn't want to see the credit usage UI, so we make sure this is gone right away - setLocalStorage("hasExitedFreeTrial", true); - }; - - const onRefresh = async () => { - if (isRefreshing) { - return; - } - - setIsRefreshing(true); - - const refreshCalls: Promise[] = [ - refreshProfiles("Manual refresh from starter credits button"), - ]; - - if (refreshCreditStatus) { - refreshCalls.push(refreshCreditStatus()); - } - - try { - await Promise.all(refreshCalls); - } finally { - setIsRefreshing(false); - } - }; - - return ( - - - {children} - - - - -
-
- -

Starter credits

-
-
- { - void onRefresh(); - }} - tooltipContent="Refresh credit usage" - > - - - - - -
- -
- - You are currently using starter credits for Continue, which - allows you to use a variety of frontier models at cost. Read - more{" "} - { - await ideMessenger.request("controlPlane/openUrl", { - path: "pricing", - orgSlug: undefined, - }); - ``; - }} - className="cursor-pointer text-blue-400 underline hover:text-blue-300" - > - here - - . - -
- - {!creditStatus ? ( -
- - Loading credit usage... - -
- ) : ( - - )} - -
- - Setup API Keys - - -
-
-
-
-
- ); -} diff --git a/gui/src/components/mainInput/Lump/LumpToolbar/BlockSettingsTopToolbar.tsx b/gui/src/components/mainInput/Lump/LumpToolbar/BlockSettingsTopToolbar.tsx index 2f022ea5a52..e6ed1a0124a 100644 --- a/gui/src/components/mainInput/Lump/LumpToolbar/BlockSettingsTopToolbar.tsx +++ b/gui/src/components/mainInput/Lump/LumpToolbar/BlockSettingsTopToolbar.tsx @@ -1,7 +1,6 @@ import { CubeIcon, ExclamationTriangleIcon, - GiftIcon, PencilIcon, WrenchScrewdriverIcon, } from "@heroicons/react/24/outline"; @@ -14,12 +13,10 @@ import { selectToolCallsByStatus, } from "../../../../redux/selectors/selectToolCalls"; import { setSelectedProfile } from "../../../../redux/slices/profilesSlice"; -import StarterCreditsPopover from "../../../StarterCreditsPopover"; import { ToolTip } from "../../../gui/Tooltip"; import HoverItem from "../../InputToolbar/HoverItem"; import { useAuth } from "../../../../context/Auth"; -import { useCreditStatus } from "../../../../hooks/useCredits"; import { CONFIG_ROUTES } from "../../../../util/navigation"; import { AssistantAndOrgListbox } from "../../../AssistantAndOrgListbox"; @@ -40,9 +37,6 @@ export function BlockSettingsTopToolbar() { const shouldShowError = configError && configError?.length > 0; - const { creditStatus, isUsingFreeTrial, refreshCreditStatus } = - useCreditStatus(); - const handleRulesClick = () => { if (selectedProfile) { dispatch(setSelectedProfile(selectedProfile.id)); @@ -101,19 +95,6 @@ export function BlockSettingsTopToolbar() { {!hasActiveContent && (
- {isUsingFreeTrial && ( - - - - - - - - )} - diff --git a/gui/src/components/mainInput/Lump/useEditBlock.ts b/gui/src/components/mainInput/Lump/useEditBlock.ts index 06065ff60d0..16390daa7b2 100644 --- a/gui/src/components/mainInput/Lump/useEditBlock.ts +++ b/gui/src/components/mainInput/Lump/useEditBlock.ts @@ -9,12 +9,7 @@ export function useEditBlock() { const { selectedProfile } = useAuth(); return (slug?: string, sourceFile?: string) => { - if (slug) { - ideMessenger.post("controlPlane/openUrl", { - path: `${slug}/new-version`, - orgSlug: undefined, - }); - } else if (sourceFile) { + if (sourceFile) { ideMessenger.post("openFile", { path: sourceFile, }); @@ -25,16 +20,7 @@ export function useEditBlock() { ideMessenger.post("openFile", { path: selectedProfile.uri, }); - } else if ( - selectedProfile?.fullSlug?.ownerSlug && - selectedProfile?.fullSlug.packageSlug - ) { - ideMessenger.post("controlPlane/openUrl", { - path: `${selectedProfile.fullSlug.ownerSlug}/${selectedProfile.fullSlug.packageSlug}/new-version`, - orgSlug: undefined, - }); } else { - // Local etc ideMessenger.post("config/openProfile", { profileId: undefined, }); diff --git a/gui/src/context/Auth.tsx b/gui/src/context/Auth.tsx index ee262d78231..a9229f580fc 100644 --- a/gui/src/context/Auth.tsx +++ b/gui/src/context/Auth.tsx @@ -1,34 +1,17 @@ -import { - ProfileDescription, - SerializedOrgWithProfiles, -} from "core/config/ProfileLifecycleManager"; -import { ControlPlaneSessionInfo } from "core/control-plane/AuthTypes"; -import React, { - createContext, - useCallback, - useContext, - useEffect, - useState, -} from "react"; -import { useWebviewListener } from "../hooks/useWebviewListener"; +import { ProfileDescription } from "core/config/ProfileLifecycleManager"; +import React, { createContext, useCallback, useContext } from "react"; import { useAppDispatch, useAppSelector } from "../redux/hooks"; import { setConfigLoading } from "../redux/slices/configSlice"; import { selectCurrentOrg, selectSelectedProfile, - setOrganizations, - setSelectedOrgId, } from "../redux/slices/profilesSlice"; import { IdeMessengerContext } from "./IdeMessenger"; interface AuthContextType { - session: ControlPlaneSessionInfo | undefined; - logout: () => void; - login: (useOnboarding: boolean) => Promise; selectedProfile: ProfileDescription | null; profiles: ProfileDescription[] | null; refreshProfiles: (reason?: string) => Promise; - organizations: SerializedOrgWithProfiles[]; } const AuthContext = createContext(undefined); @@ -38,69 +21,11 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ }) => { const dispatch = useAppDispatch(); const ideMessenger = useContext(IdeMessengerContext); - // Session - const [session, setSession] = useState( - undefined, - ); - - // Orgs - const orgs = useAppSelector((store) => store.profiles.organizations); // Profiles const currentOrg = useAppSelector(selectCurrentOrg); const selectedProfile = useAppSelector(selectSelectedProfile); - const login: AuthContextType["login"] = async (useOnboarding: boolean) => { - try { - const result = await ideMessenger.request("getControlPlaneSessionInfo", { - silent: false, - useOnboarding, - }); - - if (result.status === "error") { - console.error("Login failed:", result.error); - return false; - } - - const session = result.content; - setSession(session); - - return true; - } catch (error: any) { - console.error("Login request failed:", error); - // Let the error propagate so the caller can handle it - throw error; - } - }; - - const logout = () => { - ideMessenger.post("logoutOfControlPlane", undefined); - dispatch(setOrganizations(orgs.filter((org) => org.id === "personal"))); - dispatch(setSelectedOrgId("personal")); - setSession(undefined); - }; - - useEffect(() => { - async function init() { - const result = await ideMessenger.request("getControlPlaneSessionInfo", { - silent: true, - useOnboarding: false, - }); - if (result.status === "success") { - setSession(result.content); - } - } - void init(); - }, []); - - useWebviewListener( - "sessionUpdate", - async (data) => { - setSession(data.sessionInfo); - }, - [], - ); - const refreshProfiles = useCallback( async (reason?: string) => { try { @@ -122,13 +47,9 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ return ( {children} diff --git a/gui/src/context/MockIdeMessenger.ts b/gui/src/context/MockIdeMessenger.ts index e7f78252dc5..7e7f77ea245 100644 --- a/gui/src/context/MockIdeMessenger.ts +++ b/gui/src/context/MockIdeMessenger.ts @@ -1,5 +1,4 @@ import { ChatMessage, IDE, PromptLog } from "core"; -import { AuthType } from "core/control-plane/AuthTypes"; import { FromWebviewProtocol, ToCoreProtocol, @@ -33,12 +32,6 @@ const DEFAULT_MOCK_CORE_RESPONSES: MockResponses = { contents: "Current file contents", path: "file:///Users/user/workspace1/current_file.py", }, - "controlPlane/getCreditStatus": { - optedInToFreeTrial: false, - creditBalance: 0, - hasCredits: false, - hasPurchasedCredits: false, - }, getWorkspaceDirs: [ "file:///Users/user/workspace1", "file:///Users/user/workspace2", @@ -46,14 +39,6 @@ const DEFAULT_MOCK_CORE_RESPONSES: MockResponses = { "history/list": [], "docs/getIndexedPages": [], "history/save": undefined, - getControlPlaneSessionInfo: { - AUTH_TYPE: AuthType.WorkOsStaging, - accessToken: "", - account: { - label: "", - id: "", - }, - }, "config/getSerializedProfileInfo": { organizations: [ { @@ -124,7 +109,6 @@ const DEFAULT_MOCK_CORE_RESPONSES: MockResponses = { }, }, ], - listBackgroundAgents: { agents: [], totalCount: 0 }, }; const DEFAULT_MOCK_CORE_RESPONSE_HANDLERS: MockResponseHandlers = { diff --git a/gui/src/hooks/ParallelListeners.tsx b/gui/src/hooks/ParallelListeners.tsx index bccb7d25bb5..d3e55e052e8 100644 --- a/gui/src/hooks/ParallelListeners.tsx +++ b/gui/src/hooks/ParallelListeners.tsx @@ -227,11 +227,6 @@ function ParallelListeners() { void dispatch(cancelStream()); }); - useWebviewListener("loadAgentSession", async (data) => { - dispatch(newSession(data.session)); - dispatch(setMode("agent")); - }); - useWebviewListener("setTTSActive", async (status) => { dispatch(setTTSActive(status)); }); diff --git a/gui/src/hooks/useCredits.ts b/gui/src/hooks/useCredits.ts deleted file mode 100644 index 8f2a4bb45b7..00000000000 --- a/gui/src/hooks/useCredits.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { usesCreditsBasedApiKey } from "core/config/usesFreeTrialApiKey"; -import { CreditStatus } from "core/control-plane/client"; -import { isOutOfStarterCredits } from "core/llm/utils/starterCredits"; -import { useCallback, useContext, useEffect, useState } from "react"; -import { IdeMessengerContext } from "../context/IdeMessenger"; -import { useAppSelector } from "../redux/hooks"; -import { getLocalStorage } from "../util/localStorage"; - -export function useCreditStatus() { - const config = useAppSelector((state) => state.config.config); - const ideMessenger = useContext(IdeMessengerContext); - const [creditStatus, setCreditStatus] = useState(null); - - const hasExitedFreeTrial = getLocalStorage("hasExitedFreeTrial"); - const usingCreditsBasedApiKey = usesCreditsBasedApiKey(config); - const isUsingFreeTrial = usingCreditsBasedApiKey && !hasExitedFreeTrial; - const outOfStarterCredits = creditStatus - ? isOutOfStarterCredits(usingCreditsBasedApiKey, creditStatus) - : false; - - const refreshCreditStatus = useCallback(async () => { - try { - const resp = await ideMessenger.request( - "controlPlane/getCreditStatus", - undefined, - ); - if (resp.status === "success") { - setCreditStatus(resp.content); - } - } catch (error) { - console.error("Failed to refresh credit status", error); - } - }, [ideMessenger]); - - useEffect(() => { - void refreshCreditStatus(); - - let intervalId: NodeJS.Timeout | null = null; - - if (isUsingFreeTrial) { - intervalId = setInterval(() => { - void refreshCreditStatus(); - }, 15000); - } - - return () => { - if (intervalId) { - clearInterval(intervalId); - } - }; - }, [isUsingFreeTrial, refreshCreditStatus]); - - return { - creditStatus, - outOfStarterCredits, - isUsingFreeTrial, - refreshCreditStatus, - }; -} diff --git a/gui/src/pages/config/components/ContinueFeaturesMenu.tsx b/gui/src/pages/config/components/ContinueFeaturesMenu.tsx deleted file mode 100644 index e8d99c56686..00000000000 --- a/gui/src/pages/config/components/ContinueFeaturesMenu.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import ToggleSwitch from "../../../components/gui/Switch"; -interface ContinueFeaturesMenuProps { - enableStaticContextualization: boolean; - handleEnableStaticContextualizationToggle: (value: boolean) => void; -} - -export function ContinueFeaturesMenu({ - enableStaticContextualization, - handleEnableStaticContextualizationToggle, -}: ContinueFeaturesMenuProps) { - return ( -
-
- 🚧 INTERNAL SETTINGS 🚧 -
-
- - handleEnableStaticContextualizationToggle( - !enableStaticContextualization, - ) - } - text="Use Static Contextualization" - /> -
-
- ); -} diff --git a/gui/src/pages/config/configTabs.tsx b/gui/src/pages/config/configTabs.tsx index b4687f1e33c..2216e671a05 100644 --- a/gui/src/pages/config/configTabs.tsx +++ b/gui/src/pages/config/configTabs.tsx @@ -1,6 +1,5 @@ import { ArrowLeftIcon, - BuildingOfficeIcon, CircleStackIcon, Cog6ToothIcon, CubeIcon, @@ -14,7 +13,6 @@ import { ConfigsSection } from "./sections/ConfigsSection"; import { HelpSection } from "./sections/HelpSection"; import { IndexingSettingsSection } from "./sections/IndexingSettingsSection"; import { ModelsSection } from "./sections/ModelsSection"; -import { OrganizationsSection } from "./sections/OrganizationsSection"; import { RulesSection } from "./sections/RulesSection"; import { ToolsSection } from "./sections/ToolsSection"; import { UserSettingsSection } from "./sections/UserSettingsSection"; @@ -98,18 +96,6 @@ export const topTabSections: TabSection[] = [ ), icon: , }, - { - id: "organizations", - label: "Organizations", - component: ( - - - - ), - icon: ( - - ), - }, ], }, { diff --git a/gui/src/pages/config/features/account/AccountDropdown.tsx b/gui/src/pages/config/features/account/AccountDropdown.tsx index 449988b8990..5f4151635ef 100644 --- a/gui/src/pages/config/features/account/AccountDropdown.tsx +++ b/gui/src/pages/config/features/account/AccountDropdown.tsx @@ -1,107 +1,4 @@ -import { - ArrowRightStartOnRectangleIcon, - Cog6ToothIcon, - UserCircleIcon as UserCircleIconOutline, -} from "@heroicons/react/24/outline"; -import { UserCircleIcon as UserCircleIconSolid } from "@heroicons/react/24/solid"; -import { isOnPremSession } from "core/control-plane/AuthTypes"; -import { useContext } from "react"; -import { ToolTip } from "../../../../components/gui/Tooltip"; -import { - Button, - Listbox, - ListboxButton, - ListboxOption, - ListboxOptions, -} from "../../../../components/ui"; -import { Divider } from "../../../../components/ui/Divider"; -import { useAuth } from "../../../../context/Auth"; -import { IdeMessengerContext } from "../../../../context/IdeMessenger"; - +// Account dropdown removed - no longer needed without Hub auth export function AccountDropdown() { - const { session, logout, login } = useAuth(); - const ideMessenger = useContext(IdeMessengerContext); - - if (isOnPremSession(session)) { - return null; - } - - if (!session) { - return ( - - - - ); - } - - return ( -
- - {({ open }) => ( - <> - - -
- - {session.account.label} - - - {session.account.id} - -
-
- - {/* Account info section for small screens */} -
-
- -
- - {session.account.label} - - - {session.account.id} - -
-
- -
- - - ideMessenger.post("openUrl", "https://continue.dev/settings") - } - value="manage-account" - > -
- - Manage Account -
-
- - -
- - Log out -
-
-
- - )} -
-
- ); + return null; } diff --git a/gui/src/pages/config/index.tsx b/gui/src/pages/config/index.tsx index d672b3d099c..b5cd5cb9610 100644 --- a/gui/src/pages/config/index.tsx +++ b/gui/src/pages/config/index.tsx @@ -1,11 +1,8 @@ -import { isOnPremSession } from "core/control-plane/AuthTypes"; import React from "react"; import { useNavigate, useSearchParams } from "react-router-dom"; -import { AssistantAndOrgListbox } from "../../components/AssistantAndOrgListbox"; import Alert from "../../components/gui/Alert"; import { Divider } from "../../components/ui/Divider"; import { TabGroup } from "../../components/ui/TabGroup"; -import { useAuth } from "../../context/Auth"; import { useNavigationListener } from "../../hooks/useNavigationListener"; import { bottomTabSections, getAllTabs, topTabSections } from "./configTabs"; import { CliInstallBanner } from "../../components/CliInstallBanner"; @@ -16,11 +13,8 @@ function ConfigPage() { const navigate = useNavigate(); const [searchParams] = useSearchParams(); const activeTab = searchParams.get("tab") || "settings"; - const { session, organizations } = useAuth(); const allTabs = getAllTabs(); - const shouldRenderOrgInfo = - session && organizations.length > 1 && !isOnPremSession(session); const handleTabClick = (tabId: string) => { if (tabId === "back") { @@ -35,7 +29,7 @@ function ConfigPage() { {/* Vertical Sidebar - full height */}
- {topTabSections.map((section, index) => ( + {topTabSections.map((section) => ( - {index === 0 && shouldRenderOrgInfo && ( - <> - - - - )} ))} diff --git a/gui/src/pages/config/sections/ModelsSection.tsx b/gui/src/pages/config/sections/ModelsSection.tsx index 316f463a88a..98f03abe735 100644 --- a/gui/src/pages/config/sections/ModelsSection.tsx +++ b/gui/src/pages/config/sections/ModelsSection.tsx @@ -41,25 +41,16 @@ export function ModelsSection() { const handleConfigureModel = useEditModel(); function handleAddModel() { - const isLocal = selectedProfile?.profileType === "local"; - - if (isLocal) { - dispatch(setShowDialog(true)); - dispatch( - setDialogMessage( - { - dispatch(setShowDialog(false)); - }} - />, - ), - ); - } else { - void ideMessenger.request("controlPlane/openUrl", { - path: "?type=models", - orgSlug: undefined, - }); - } + dispatch(setShowDialog(true)); + dispatch( + setDialogMessage( + { + dispatch(setShowDialog(false)); + }} + />, + ), + ); } return ( diff --git a/gui/src/pages/config/sections/OrganizationsSection.tsx b/gui/src/pages/config/sections/OrganizationsSection.tsx deleted file mode 100644 index f7c1258a67a..00000000000 --- a/gui/src/pages/config/sections/OrganizationsSection.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import { Cog6ToothIcon } from "@heroicons/react/24/outline"; -import { BuildingOfficeIcon, UserIcon } from "@heroicons/react/24/solid"; -import { SerializedOrgWithProfiles } from "core/config/ProfileLifecycleManager"; -import { isOnPremSession } from "core/control-plane/AuthTypes"; -import { useContext } from "react"; -import { Button, Card, Divider } from "../../../components/ui"; -import { useAuth } from "../../../context/Auth"; -import { IdeMessengerContext } from "../../../context/IdeMessenger"; -import { ConfigHeader } from "../components/ConfigHeader"; - -function getOrgIcon(org: { name: string; iconUrl?: string | null }) { - if (org.iconUrl) { - return ( - - ); - } - - const IconComponent = org.name === "Personal" ? UserIcon : BuildingOfficeIcon; - return ; -} - -export function OrganizationsSection() { - const { organizations, session } = useAuth(); - const ideMessenger = useContext(IdeMessengerContext); - - const shouldRenderOrgInfo = - session && organizations.length > 1 && !isOnPremSession(session); - - function handleAddOrganization() { - void ideMessenger.request("controlPlane/openUrl", { - path: "/organizations/new", - }); - } - - function handleConfigureOrganization(org: SerializedOrgWithProfiles) { - let path: string; - if (org.id === "personal" || org.slug === undefined) { - path = "/settings"; - } else { - path = `/organizations/${org.slug}/settings`; - } - void ideMessenger.request("controlPlane/openUrl", { - path, - }); - } - - if (!shouldRenderOrgInfo) { - return ( - <> -
-
-

Organizations

-
-
- -
- Organizations are only available with cloud accounts. Sign in to - manage organizations. -
-
- - ); - } - - return ( - <> - - - - {organizations.map((organization, index) => ( -
-
-
-
- {getOrgIcon(organization)} -
-
-

- {organization.name} -

-
-
- -
- {index < organizations.length - 1 && } -
- ))} -
- - ); -} diff --git a/gui/src/pages/config/sections/RulesSection.tsx b/gui/src/pages/config/sections/RulesSection.tsx index 831de5f67ed..a3518817cea 100644 --- a/gui/src/pages/config/sections/RulesSection.tsx +++ b/gui/src/pages/config/sections/RulesSection.tsx @@ -299,16 +299,9 @@ function PromptsSubSection() { }; const handleAddPrompt = () => { - if (isLocal) { - void ideMessenger.request("config/addLocalWorkspaceBlock", { - blockType: "prompts", - }); - } else { - void ideMessenger.request("controlPlane/openUrl", { - path: "?type=prompts", - orgSlug: undefined, - }); - } + void ideMessenger.request("config/addLocalWorkspaceBlock", { + blockType: "prompts", + }); }; const sortedCommands = useMemo(() => { @@ -437,21 +430,14 @@ function RulesSubSection() { const handleAddRule = (mode?: string) => { const currentMode = mode || globalRulesMode; - if (isLocal) { - dispatch(setShowDialog(true)); - dispatch( - setDialogMessage( - , - ), - ); - } else { - void ideMessenger.request("controlPlane/openUrl", { - path: "/hub?type=rules", - orgSlug: undefined, - }); - } + dispatch(setShowDialog(true)); + dispatch( + setDialogMessage( + , + ), + ); }; const handleOptionClick = (value: string) => { diff --git a/gui/src/pages/config/sections/ToolsSection.tsx b/gui/src/pages/config/sections/ToolsSection.tsx index a48bd5df7ac..8a4ee5d4342 100644 --- a/gui/src/pages/config/sections/ToolsSection.tsx +++ b/gui/src/pages/config/sections/ToolsSection.tsx @@ -454,16 +454,9 @@ export function ToolsSection() { }, [servers, selectedProfile]); const handleAddMcpServer = () => { - if (isLocal) { - void ideMessenger.request("config/addLocalWorkspaceBlock", { - blockType: "mcpServers", - }); - } else { - void ideMessenger.request("controlPlane/openUrl", { - path: "?type=mcpServers", - orgSlug: undefined, - }); - } + void ideMessenger.request("config/addLocalWorkspaceBlock", { + blockType: "mcpServers", + }); }; const allToolsOff = useMemo(() => { diff --git a/gui/src/pages/config/sections/UserSettingsSection.tsx b/gui/src/pages/config/sections/UserSettingsSection.tsx index 27b7f9bb183..4085df592e3 100644 --- a/gui/src/pages/config/sections/UserSettingsSection.tsx +++ b/gui/src/pages/config/sections/UserSettingsSection.tsx @@ -2,18 +2,14 @@ import { SharedConfigSchema, modifyAnyConfigWithSharedConfig, } from "core/config/sharedConfig"; -import { HubSessionInfo } from "core/control-plane/AuthTypes"; -import { isContinueTeamMember } from "core/util/isContinueTeamMember"; import { useContext, useEffect, useState } from "react"; import { Card, Toggle, useFontSize } from "../../../components/ui"; -import { useAuth } from "../../../context/Auth"; import { IdeMessengerContext } from "../../../context/IdeMessenger"; import { useAppDispatch, useAppSelector } from "../../../redux/hooks"; import { updateConfig } from "../../../redux/slices/configSlice"; import { selectCurrentOrg } from "../../../redux/slices/profilesSlice"; import { setLocalStorage } from "../../../util/localStorage"; import { ConfigHeader } from "../components/ConfigHeader"; -import { ContinueFeaturesMenu } from "../components/ContinueFeaturesMenu"; import { UserSetting } from "../components/UserSetting"; export function UserSettingsSection() { @@ -24,7 +20,6 @@ export function UserSettingsSection() { const currentOrg = useAppSelector(selectCurrentOrg); const [showExperimental, setShowExperimental] = useState(false); - const { session } = useAuth(); function handleUpdate(sharedConfig: SharedConfigSchema) { // Optimistic update @@ -53,10 +48,6 @@ export function UserSettingsSection() { // Workspace prompts const promptPath = config.experimental?.promptPath || ""; - const handleEnableStaticContextualizationToggle = (value: boolean) => { - handleUpdate({ enableStaticContextualization: value }); - }; - // TODO defaults are in multiple places, should be consolidated and probably not explicit here const showSessionTabs = config.ui?.showSessionTabs ?? false; const continueAfterToolRejection = @@ -74,9 +65,6 @@ export function UserSettingsSection() { config.experimental?.onlyUseSystemMessageTools ?? false; const codebaseToolCallingOnly = config.experimental?.codebaseToolCallingOnly ?? false; - const enableStaticContextualization = - config.experimental?.enableStaticContextualization ?? false; - const allowAnonymousTelemetry = config.allowAnonymousTelemetry ?? true; const useAutocompleteMultilineCompletions = @@ -97,10 +85,6 @@ export function UserSettingsSection() { }); }; - const hasContinueEmail = isContinueTeamMember( - (session as HubSessionInfo)?.account?.id, - ); - const disableTelemetryToggle = currentOrg?.policy?.allowAnonymousTelemetry === false; @@ -321,17 +305,6 @@ export function UserSettingsSection() { handleUpdate({ continueAfterToolRejection: value }) } /> - - {hasContinueEmail && ( - - )}
diff --git a/gui/src/pages/gui/Chat.tsx b/gui/src/pages/gui/Chat.tsx index 45929ec12e2..be42eb5a008 100644 --- a/gui/src/pages/gui/Chat.tsx +++ b/gui/src/pages/gui/Chat.tsx @@ -45,7 +45,6 @@ import { isJetBrains, isMetaEquivalentKeyPressed } from "../../util"; import { ToolCallDiv } from "./ToolCallDiv"; import { useStore } from "react-redux"; -import { BackgroundModeView } from "../../components/BackgroundMode/BackgroundModeView"; import { CliInstallBanner } from "../../components/CliInstallBanner"; import FeedbackDialog from "../../components/dialogs/FeedbackDialog"; @@ -115,7 +114,6 @@ export function Chat() { ); const isStreaming = useAppSelector((state) => state.session.isStreaming); const [stepsOpen] = useState<(boolean | undefined)[]>([]); - const [isCreatingAgent, setIsCreatingAgent] = useState(false); const mainTextInputRef = useRef(null); const stepsDivRef = useRef(null); const tabsRef = useRef(null); @@ -133,7 +131,6 @@ export function Chat() { const hasDismissedExploreDialog = useAppSelector( (state) => state.ui.hasDismissedExploreDialog, ); - const mode = useAppSelector((state) => state.session.mode); const currentOrg = useAppSelector(selectCurrentOrg); const jetbrains = useMemo(() => { return isJetBrains(); @@ -181,56 +178,6 @@ export function Chat() { stateSnapshot.config.config.selectedModelByRole; const currentMode = stateSnapshot.session.mode; - // Handle background mode specially - if (currentMode === "background" && !isCurrentlyInEdit) { - // Background mode triggers agent creation instead of chat - const currentOrg = selectCurrentOrg(stateSnapshot); - const organizationId = - currentOrg?.id !== "personal" ? currentOrg?.id : undefined; - - setIsCreatingAgent(true); - - // Create agent and track loading state - void (async () => { - try { - // Resolve context items from editor content (same as normal chat) - const defaultContextProviders = - stateSnapshot.config.config.experimental?.defaultContext ?? []; - - const { selectedContextItems, selectedCode, content } = - await resolveEditorContent({ - editorState, - modifiers, - ideMessenger, - defaultContextProviders, - availableSlashCommands: - stateSnapshot.config.config.slashCommands, - dispatch, - getState: () => reduxStore.getState(), - }); - - await ideMessenger.request("createBackgroundAgent", { - content, - contextItems: selectedContextItems, - selectedCode, - organizationId, - }); - - // Clear input only after successful API call - if (editorToClearOnSend) { - editorToClearOnSend.commands.clearContent(); - } - - setIsCreatingAgent(false); - } catch (error) { - console.error("Failed to create background agent:", error); - setIsCreatingAgent(false); - } - })(); - - return; - } - // Cancel all pending tool calls latestPendingToolCalls.forEach((toolCallState) => { dispatch( @@ -285,7 +232,7 @@ export function Chat() { setLocalStorage("mainTextEntryCounter", 1); } }, - [dispatch, ideMessenger, reduxStore, setIsCreatingAgent], + [dispatch, ideMessenger, reduxStore], ); useWebviewListener( @@ -502,12 +449,8 @@ export function Chat() {
{!hasDismissedExploreDialog && } - {mode === "background" ? ( - - ) : ( - history.length === 0 && ( - - ) + {history.length === 0 && ( + )}
diff --git a/gui/src/pages/gui/OutOfCreditsDialog.tsx b/gui/src/pages/gui/OutOfCreditsDialog.tsx deleted file mode 100644 index ac10bd9d535..00000000000 --- a/gui/src/pages/gui/OutOfCreditsDialog.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { CreditCardIcon } from "@heroicons/react/24/outline"; -import { useContext } from "react"; -import { SecondaryButton } from "../../components"; -import { IdeMessengerContext } from "../../context/IdeMessenger"; - -export function OutOfCreditsDialog() { - const ideMessenger = useContext(IdeMessengerContext); - - return ( -
-

You're out of credits!

- -
- - To purchase more or set up auto top-up, click below to visit the - billing page: - -
- { - ideMessenger.post("controlPlane/openUrl", { - path: "/settings/billing", - }); - }} - > - - Purchase Credits - -
-
-
- ); -} diff --git a/gui/src/pages/gui/StreamError.tsx b/gui/src/pages/gui/StreamError.tsx index 520d95a42ee..e53dd07a149 100644 --- a/gui/src/pages/gui/StreamError.tsx +++ b/gui/src/pages/gui/StreamError.tsx @@ -7,7 +7,7 @@ import { } from "@heroicons/react/24/outline"; import { DISCUSSIONS_LINK } from "core/util/constants"; import { useContext, useMemo } from "react"; -import { GhostButton, SecondaryButton } from "../../components"; +import { GhostButton } from "../../components"; import { useEditModel } from "../../components/mainInput/Lump/useEditBlock"; import { useMainEditor } from "../../components/mainInput/TipTapEditor"; import { GithubIcon } from "../../components/svg/GithubIcon"; @@ -16,12 +16,9 @@ import { useAuth } from "../../context/Auth"; import { IdeMessengerContext } from "../../context/IdeMessenger"; import { useAppDispatch, useAppSelector } from "../../redux/hooks"; import { selectSelectedChatModel } from "../../redux/slices/configSlice"; -import { selectSelectedProfile } from "../../redux/slices/profilesSlice"; import { setDialogMessage, setShowDialog } from "../../redux/slices/uiSlice"; import { streamResponseThunk } from "../../redux/thunks/streamResponse"; -import { isLocalProfile } from "../../util"; import { analyzeError } from "../../util/errorAnalysis"; -import { OutOfCreditsDialog } from "./OutOfCreditsDialog"; interface StreamErrorProps { error: unknown; @@ -31,8 +28,7 @@ const StreamErrorDialog = ({ error }: StreamErrorProps) => { const dispatch = useAppDispatch(); const ideMessenger = useContext(IdeMessengerContext); const selectedModel = useAppSelector(selectSelectedChatModel); - const selectedProfile = useAppSelector(selectSelectedProfile); - const { session, refreshProfiles } = useAuth(); + const { refreshProfiles } = useAuth(); const { mainEditor } = useMainEditor(); const { @@ -120,10 +116,6 @@ const StreamErrorDialog = ({ error }: StreamErrorProps) => { ); - if (parsedError.includes("You're out of credits!")) { - return ; - } - let errorContent = (
@@ -189,14 +181,6 @@ const StreamErrorDialog = ({ error }: StreamErrorProps) => { if (statusCode === 401) { errorContent = (
- {session && selectedProfile && !isLocalProfile(selectedProfile) && ( -
- {`If your hub secret values may have changed, refresh your agents`} - - Refresh agent secrets - -
- )} {`It's possible that your API key is invalid.`}
{checkKeysButton} @@ -293,7 +277,7 @@ const StreamErrorDialog = ({ error }: StreamErrorProps) => { const issueBody = `**Error Details** Model: ${selectedModel?.title || "Unknown"} -Provider: ${selectedModel ? `${selectedModel.underlyingProviderName}${selectedModel.provider === "continue-proxy" ? " (continue-proxy)" : ""}` : "Unknown"} +Provider: ${selectedModel?.underlyingProviderName || "Unknown"} Status Code: ${statusCode || "N/A"} **Error Output** diff --git a/gui/src/pages/history/history.test.tsx b/gui/src/pages/history/history.test.tsx index 8cc01a7ffd8..12a5c744978 100644 --- a/gui/src/pages/history/history.test.tsx +++ b/gui/src/pages/history/history.test.tsx @@ -16,8 +16,6 @@ mockIdeMessenger.responses["history/list"] = [ sessionId: "remote-agent-123", dateCreated: new Date().toString(), workspaceDirectory: "", - isRemote: true, - remoteId: "agent-123", }, ]; describe("history Page test", () => { diff --git a/gui/src/redux/slices/configSlice.ts b/gui/src/redux/slices/configSlice.ts index 202abbfbbc1..336b690897b 100644 --- a/gui/src/redux/slices/configSlice.ts +++ b/gui/src/redux/slices/configSlice.ts @@ -14,7 +14,6 @@ export const EMPTY_CONFIG: BrowserSerializedContinueConfig = { contextProviders: [], tools: [], mcpServerStatuses: [], - usePlatform: true, modelsByRole: { chat: [], apply: [], diff --git a/gui/src/redux/slices/sessionSlice.ts b/gui/src/redux/slices/sessionSlice.ts index 1a0db2cc441..8784d0c41dc 100644 --- a/gui/src/redux/slices/sessionSlice.ts +++ b/gui/src/redux/slices/sessionSlice.ts @@ -25,7 +25,6 @@ import { ToolCallDelta, ToolCallState, } from "core"; -import type { RemoteSessionMetadata } from "core/control-plane/client"; import { mergeReasoningDetails } from "core/llm/openaiTypeConverters"; import { NEW_SESSION_TITLE } from "core/util/constants"; import { @@ -204,7 +203,7 @@ export type ChatHistoryItemWithMessageId = ChatHistoryItem & { type SessionState = { lastSessionId?: string; isSessionMetadataLoading: boolean; - allSessionMetadata: (BaseSessionMetadata | RemoteSessionMetadata)[]; + allSessionMetadata: BaseSessionMetadata[]; history: ChatHistoryItemWithMessageId[]; isStreaming: boolean; title: string; @@ -721,9 +720,7 @@ export const sessionSlice = createSlice({ }, setAllSessionMetadata: ( state, - { - payload, - }: PayloadAction<(BaseSessionMetadata | RemoteSessionMetadata)[]>, + { payload }: PayloadAction, ) => { state.allSessionMetadata = payload; }, diff --git a/gui/src/redux/thunks/selectFirstHubProfile.ts b/gui/src/redux/thunks/selectFirstHubProfile.ts deleted file mode 100644 index 017d7d783f1..00000000000 --- a/gui/src/redux/thunks/selectFirstHubProfile.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { createAsyncThunk } from "@reduxjs/toolkit"; -import { setSelectedProfile } from "../slices/profilesSlice"; -import { ThunkApiType } from "../store"; - -/** - * If there is a hub profile, select it. - * Used primarily after onboarding when a new profile is created and we - * want to switch to it immediately - */ -export const selectFirstHubProfile = createAsyncThunk< - void, - undefined, - ThunkApiType ->("selectFirstHubProfile", async (_, { dispatch, extra, getState }) => { - let attempts = 0; - const maxAttempts = 3; - - const findAndSelectProfile = () => { - const state = getState(); - - const currentOrgProfiles = state.profiles.organizations.find( - (org) => org.id === (state.profiles.selectedOrganizationId ?? "personal"), - ); - - const firstHubProfile = currentOrgProfiles?.profiles.find( - (profile) => profile.profileType === "platform", - ); - - if (firstHubProfile) { - dispatch(setSelectedProfile(firstHubProfile.id)); - extra.ideMessenger.post("didChangeSelectedProfile", { - id: firstHubProfile.id, - }); - return true; - } - - return false; - }; - - // Try immediately first - if (findAndSelectProfile()) { - return; - } - - // If not found, poll every second for up to maxAttempts - return new Promise((resolve) => { - const interval = setInterval(() => { - attempts++; - - if (findAndSelectProfile() || attempts >= maxAttempts) { - clearInterval(interval); - resolve(); - } - }, 1000); - }); -}); diff --git a/gui/src/redux/thunks/session.ts b/gui/src/redux/thunks/session.ts index ea2cf86cfbc..fd33187c581 100644 --- a/gui/src/redux/thunks/session.ts +++ b/gui/src/redux/thunks/session.ts @@ -1,6 +1,5 @@ import { createAsyncThunk, unwrapResult } from "@reduxjs/toolkit"; import { BaseSessionMetadata, ChatMessage, Session } from "core"; -import { RemoteSessionMetadata } from "core/control-plane/client"; import { NEW_SESSION_TITLE } from "core/util/constants"; import { renderChatMessage } from "core/util/messageContent"; import { IIdeMessenger } from "../../context/IdeMessenger"; @@ -32,19 +31,8 @@ export async function getSession( return result.content; } -export async function getRemoteSession( - ideMessenger: IIdeMessenger, - remoteId: string, -): Promise { - const result = await ideMessenger.request("history/loadRemote", { remoteId }); - if (result.status === "error") { - throw new Error(result.error); - } - return result.content; -} - export const refreshSessionMetadata = createAsyncThunk< - RemoteSessionMetadata[] | BaseSessionMetadata[], + BaseSessionMetadata[], { offset?: number; limit?: number; @@ -125,38 +113,6 @@ export const loadSession = createAsyncThunk< }, ); -export const loadRemoteSession = createAsyncThunk< - void, - { - remoteId: string; - saveCurrentSession: boolean; - }, - ThunkApiType ->( - "session/loadRemote", - async ( - { remoteId, saveCurrentSession: save }, - { extra, dispatch, getState }, - ) => { - if (save) { - const result = await dispatch( - saveCurrentSession({ - openNewSession: false, - generateTitle: true, - }), - ); - unwrapResult(result); - } - const session = await getRemoteSession(extra.ideMessenger, remoteId); - dispatch(newSession(session)); - - // Restore selected chat model from session, if present - if (session.chatModelTitle) { - dispatch(selectChatModelForProfile(session.chatModelTitle)); - } - }, -); - export const selectChatModelForProfile = createAsyncThunk< void, string, diff --git a/gui/src/util/test/utils.ts b/gui/src/util/test/utils.ts index 7473536e567..05c3ff055d5 100644 --- a/gui/src/util/test/utils.ts +++ b/gui/src/util/test/utils.ts @@ -14,7 +14,7 @@ function logDomText() { "", ); cleaned = cleaned?.replace( - /[⌘Ctrl]+⏎ (?:No )?[Aa]ctive file⏎ Enter⏎Log in to access a free trial of theModels Add-OnLog in to Continue HubOr, configure your own models/, + /[⌘Ctrl]+⏎ (?:No )?[Aa]ctive file⏎ Enter⏎(?:Get started with AI-powered coding by configuring your models|Configure your models|Models Add-On|Configure your own model providers to get started\.|Configure Models)/, "", ); cleaned = cleaned.replaceAll("Select model", ""); diff --git a/packages/config-types/src/index.ts b/packages/config-types/src/index.ts index e3a7e3c5823..0cc9fc4a3d7 100644 --- a/packages/config-types/src/index.ts +++ b/packages/config-types/src/index.ts @@ -57,7 +57,6 @@ export const modelDescriptionSchema = z.object({ "cloudflare", "azure", "ovhcloud", - "continue-proxy", "nebius", "scaleway", "watsonx", @@ -111,7 +110,6 @@ export const embeddingsProviderSchema = z.object({ "cohere", "gemini", "ovhcloud", - "continue-proxy", "nebius", "scaleway", "watsonx", @@ -187,7 +185,7 @@ export const contextProviderSchema = z.object({ export type ContextProvider = z.infer; export const rerankerSchema = z.object({ - name: z.enum(["cohere", "voyage", "watsonx", "llm", "continue-proxy"]), + name: z.enum(["cohere", "voyage", "watsonx", "llm"]), params: z.record(z.any()).optional(), }); export type Reranker = z.infer; @@ -200,7 +198,6 @@ export const analyticsSchema = z.object({ "mixpanel", "splunk", "datadog", - "continue-proxy", ]), url: z.string().optional(), clientKey: z.string().optional(), diff --git a/packages/config-yaml/src/__tests__/index.test.ts b/packages/config-yaml/src/__tests__/index.test.ts index cbfcc80b348..46a9277ecc1 100644 --- a/packages/config-yaml/src/__tests__/index.test.ts +++ b/packages/config-yaml/src/__tests__/index.test.ts @@ -1,6 +1,5 @@ import * as fs from "fs"; import { - decodeSecretLocation, FQSN, PackageIdentifier, packageIdentifierToShorthandSlug, @@ -8,7 +7,6 @@ import { PlatformSecretStore, Registry, resolveFQSN, - resolveSecretLocationInProxy, SecretLocation, SecretResult, SecretStore, @@ -115,9 +113,7 @@ describe("E2E Scenarios", () => { { renderSecrets: true, platformClient, - orgScopeId: "test-org", currentUserSlug: "test-user", - onPremProxyUrl: null, }, ); @@ -138,9 +134,7 @@ describe("E2E Scenarios", () => { { renderSecrets: true, platformClient, - orgScopeId: "test-org", currentUserSlug: "test-user", - onPremProxyUrl: null, }, ); @@ -178,41 +172,6 @@ describe("E2E Scenarios", () => { expect(config?.docs?.[0]?.rootUrl).toBe( "https://docs.python.org/release/3.13.1", ); - - // Test that proxy can correctly resolve secrets - const decodedAnthropicSecretLocation = decodeSecretLocation( - anthropicSecretLocation, - ); - const decodedGeminiSecretLocation = - decodeSecretLocation(geminiSecretLocation); - - // With environment - const antSecretValue = await resolveSecretLocationInProxy( - decodedAnthropicSecretLocation, - platformSecretStore, - environmentSecretStore, - ); - expect(antSecretValue).toBe("sk-ant-env"); - const geminiSecretValue = await resolveSecretLocationInProxy( - decodedGeminiSecretLocation, - platformSecretStore, - environmentSecretStore, - ); - expect(geminiSecretValue).toBe("gemini-api-key-env"); - - // Without environment - const antSecretValue2 = await resolveSecretLocationInProxy( - decodedAnthropicSecretLocation, - platformSecretStore, - undefined, - ); - expect(antSecretValue2).toBe("sk-ant"); - const geminiSecretValue2 = await resolveSecretLocationInProxy( - decodedGeminiSecretLocation, - platformSecretStore, - undefined, - ); - expect(geminiSecretValue2).toBe("gemini-api-key"); }); it("should correctly unroll assistant with injected blocks", async () => { @@ -229,9 +188,7 @@ describe("E2E Scenarios", () => { { renderSecrets: true, platformClient, - orgScopeId: "test-org", currentUserSlug: "test-user", - onPremProxyUrl: null, // Add injected blocks injectBlocks: [ { @@ -277,9 +234,7 @@ describe("E2E Scenarios", () => { { renderSecrets: true, platformClient, - orgScopeId: "test-org", currentUserSlug: "test-user", - onPremProxyUrl: null, // Add injected blocks injectBlocks: [ { @@ -324,9 +279,7 @@ describe("E2E Scenarios", () => { { renderSecrets: true, platformClient, - orgScopeId: "test-org", currentUserSlug: "test-user", - onPremProxyUrl: null, blocklistedBlocks: [ { ownerSlug: "test-org", @@ -362,9 +315,7 @@ describe("E2E Scenarios", () => { { renderSecrets: true, platformClient, - orgScopeId: "test-org", currentUserSlug: "test-user", - onPremProxyUrl: null, allowlistedBlocks: [ { ownerSlug: "test-org", @@ -408,9 +359,7 @@ describe("E2E Scenarios", () => { { renderSecrets: true, platformClient, - orgScopeId: "test-org", currentUserSlug: "test-user", - onPremProxyUrl: null, allowlistedBlocks: [ { ownerSlug: "test-org", @@ -448,9 +397,7 @@ describe("E2E Scenarios", () => { { renderSecrets: true, platformClient, - orgScopeId: "test-org", currentUserSlug: "test-user", - onPremProxyUrl: null, blocklistedBlocks: [ { ownerSlug: "test-org", diff --git a/packages/config-yaml/src/browser.ts b/packages/config-yaml/src/browser.ts index 02478264c59..e4add39a4a1 100644 --- a/packages/config-yaml/src/browser.ts +++ b/packages/config-yaml/src/browser.ts @@ -6,7 +6,6 @@ export * from "./interfaces/slugs.js"; export * from "./load/clientRender.js"; export * from "./load/getBlockType.js"; export * from "./load/merge.js"; -export * from "./load/proxySecretResolution.js"; export * from "./load/typeGuards.js"; export * from "./load/unroll.js"; export * from "./markdown/index.js"; diff --git a/packages/config-yaml/src/load/clientRender.ts b/packages/config-yaml/src/load/clientRender.ts index 760ae48a6b7..8650203ea18 100644 --- a/packages/config-yaml/src/load/clientRender.ts +++ b/packages/config-yaml/src/load/clientRender.ts @@ -1,4 +1,3 @@ -import { z } from "zod"; import { PlatformClient, SecretStore } from "../interfaces/index.js"; import { decodeSecretLocation, @@ -22,8 +21,6 @@ export async function renderSecrets( packageIdentifier: PackageIdentifier, unrolledConfigContent: string, clientSecretStore: SecretStore, - orgScopeId: string | null, // The "scope" that the user is logged in with - onPremProxyUrl: string | null, platformClient?: PlatformClient, ): Promise { // 1. First we need to get a list of all the FQSNs that are required to render the config @@ -69,14 +66,18 @@ export async function renderSecrets( // 6. The rendered YAML is parsed and validated again const parsedYaml = parseAssistantUnrolled(renderedYaml); - // 7. We update any of the items with the proxy version if there are un-rendered secrets - const finalConfig = useProxyForUnrenderedSecrets( - parsedYaml, - packageIdentifier, - orgScopeId, - onPremProxyUrl, - ); - return finalConfig; + return parsedYaml; +} + +export function packageIdentifierToShorthandSlug( + id: PackageIdentifier, +): string { + switch (id.uriType) { + case "slug": + return `${id.fullSlug.ownerSlug}/${id.fullSlug.packageSlug}`; + case "file": + return "/"; + } } export function getUnrenderedSecretLocation( @@ -102,87 +103,3 @@ export function getUnrenderedSecretLocation( return undefined; } - -export function packageIdentifierToShorthandSlug( - id: PackageIdentifier, -): string { - switch (id.uriType) { - case "slug": - return `${id.fullSlug.ownerSlug}/${id.fullSlug.packageSlug}`; - case "file": - return "/"; - } -} - -function getContinueProxyModelName( - packageIdentifier: PackageIdentifier, - provider: string, - model: string, -): string { - return `${packageIdentifierToShorthandSlug(packageIdentifier)}/${provider}/${model}`; -} - -export function useProxyForUnrenderedSecrets( - config: AssistantUnrolled, - packageIdentifier: PackageIdentifier, - orgScopeId: string | null, - onPremProxyUrl: string | null, -): AssistantUnrolled { - if (config.models) { - for (let i = 0; i < config.models.length; i++) { - const apiKeyLocation = getUnrenderedSecretLocation( - config.models[i]?.apiKey, - ); - const encodedApiKeyLocation = apiKeyLocation - ? encodeSecretLocation(apiKeyLocation) - : undefined; - - let encodedEnvSecretLocations: Record | undefined = - undefined; - if (config.models[i]?.env) { - Object.entries(config.models[i]?.env!).forEach(([key, value]) => { - if (typeof value === "string") { - const secretLocation = getUnrenderedSecretLocation(value); - if (secretLocation) { - encodedEnvSecretLocations = { - ...encodedEnvSecretLocations, - [key]: encodeSecretLocation(secretLocation), - }; - } - } - }); - } - - if (encodedApiKeyLocation || encodedEnvSecretLocations) { - config.models[i] = { - ...config.models[i], - name: config.models[i]?.name ?? "", - provider: "continue-proxy", - model: getContinueProxyModelName( - packageIdentifier, - config.models[i]?.provider ?? "", - config.models[i]?.model ?? "", - ), - apiKeyLocation: encodedApiKeyLocation, - envSecretLocations: encodedEnvSecretLocations, - orgScopeId, - onPremProxyUrl, - apiKey: undefined, - }; - } - } - } - - return config; -} - -/** The additional properties that are added to the otherwise OpenAI-compatible body when requesting a Continue proxy */ -export const continuePropertiesSchema = z.object({ - apiKeyLocation: z.string().optional(), - envSecretLocations: z.record(z.string(), z.string()).optional(), - apiBase: z.string().optional(), - orgScopeId: z.string().nullable(), - env: z.record(z.string(), z.any()).optional(), -}); - -export type ContinueProperties = z.infer; diff --git a/packages/config-yaml/src/load/proxySecretResolution.ts b/packages/config-yaml/src/load/proxySecretResolution.ts deleted file mode 100644 index ff3b0ec4e64..00000000000 --- a/packages/config-yaml/src/load/proxySecretResolution.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { PlatformSecretStore, SecretStore } from "../interfaces/index.js"; -import { - SecretLocation, - encodeSecretLocation, -} from "../interfaces/SecretResult.js"; - -export async function resolveSecretLocationInProxy( - secretLocaton: SecretLocation, - platformSecretStore: PlatformSecretStore, - environmentSecretStore?: SecretStore, -): Promise { - // 1. Check environment variables (if supported) - if (environmentSecretStore) { - const envSecretValue = await environmentSecretStore.get( - secretLocaton.secretName, - ); - if (envSecretValue) { - return envSecretValue; - } - } - - // 2. Get from secret location - const platformSecret = - await platformSecretStore.getSecretFromSecretLocation(secretLocaton); - if (platformSecret) { - return platformSecret; - } - - throw new Error( - `Could not resolve secret with location ${encodeSecretLocation(secretLocaton)}`, - ); -} diff --git a/packages/config-yaml/src/load/unroll.ts b/packages/config-yaml/src/load/unroll.ts index fd0300a2232..9773668e618 100644 --- a/packages/config-yaml/src/load/unroll.ts +++ b/packages/config-yaml/src/load/unroll.ts @@ -26,10 +26,7 @@ import { } from "../schemas/index.js"; import { ConfigResult, ConfigValidationError } from "../validation.js"; import { BlockDuplicationDetector } from "./blockDuplicationDetector.js"; -import { - packageIdentifierToShorthandSlug, - useProxyForUnrenderedSecrets, -} from "./clientRender.js"; +import { packageIdentifierToShorthandSlug } from "./clientRender.js"; import { BlockType, getBlockType } from "./getBlockType.js"; export function parseConfigYaml(configYaml: string): ConfigYaml { @@ -224,10 +221,8 @@ export interface DoNotRenderSecretsUnrollAssistantOptions export interface RenderSecretsUnrollAssistantOptions extends BaseUnrollAssistantOptions { renderSecrets: true; - orgScopeId: string | null; currentUserSlug: string; platformClient: PlatformClient; - onPremProxyUrl: string | null; alwaysUseProxy?: boolean; } @@ -325,13 +320,7 @@ export async function unrollAssistantFromContent( ); const renderedYaml = renderTemplateData(templatedYaml, { secrets }); - // Parse again and replace models with proxy versions where secrets weren't rendered - const renderedConfig = useProxyForUnrenderedSecrets( - parseAssistantUnrolled(renderedYaml), - id, - options.orgScopeId, - options.onPremProxyUrl, - ); + const renderedConfig = parseAssistantUnrolled(renderedYaml); return { config: renderedConfig, errors, configLoadInterrupted }; } diff --git a/packages/config-yaml/src/registryClient.test.ts b/packages/config-yaml/src/registryClient.test.ts index 618007d1c2c..49d4a51aeea 100644 --- a/packages/config-yaml/src/registryClient.test.ts +++ b/packages/config-yaml/src/registryClient.test.ts @@ -1,6 +1,4 @@ import * as fs from "node:fs"; -import * as http from "node:http"; -import { AddressInfo } from "node:net"; import * as os from "node:os"; import * as path from "node:path"; import { pathToFileURL } from "url"; @@ -10,69 +8,20 @@ import { RegistryClient } from "./registryClient.js"; describe("RegistryClient", () => { // Create a temp directory for test files let tempDir: string; - // Setup a test server - let server: http.Server; - let apiBaseUrl: string; beforeAll(async () => { // Create a temporary directory for test files tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "registry-client-test-")); - - // Create a simple HTTP server for API testing - server = http.createServer((req, res) => { - if (req.url?.startsWith("/registry/v1/")) { - const authHeader = req.headers.authorization; - const data = { - content: authHeader - ? `auth content for ${req.url}` - : `content for ${req.url}`, - }; - - res.writeHead(200, { "Content-Type": "application/json" }); - res.end(JSON.stringify(data)); - } else { - res.writeHead(404); - res.end(); - } - }); - - // Start server on a random port - await new Promise((resolve) => { - server.listen(0, "localhost", () => resolve()); - }); - - const address = server.address() as AddressInfo; - apiBaseUrl = `http://localhost:${address.port}`; }); afterAll(async () => { // Clean up temp directory fs.rmSync(tempDir, { recursive: true, force: true }); - - // Close the test server - await new Promise((resolve) => { - server.close(() => resolve()); - }); }); describe("constructor", () => { - it("should use default apiBase if not provided", () => { + it("should create client with no options", () => { const client = new RegistryClient(); - expect((client as any).apiBase).toBe("https://api.continue.dev/"); - }); - - it("should append trailing slash to apiBase if missing", () => { - const client = new RegistryClient({ apiBase: "https://example.com" }); - expect((client as any).apiBase).toBe("https://example.com/"); - }); - - it("should keep trailing slash if apiBase already has one", () => { - const client = new RegistryClient({ apiBase: "https://example.com/" }); - expect((client as any).apiBase).toBe("https://example.com/"); - }); - - it("should store the accessToken if provided", () => { - const client = new RegistryClient({ accessToken: "test-token" }); - expect((client as any).accessToken).toBe("test-token"); + expect(client).toBeDefined(); }); it("should store the rootPath if provided", () => { @@ -102,10 +51,8 @@ describe("RegistryClient", () => { expect(result).toBe("file content"); }); - it("should get content from slug for slug uriType", async () => { - const client = new RegistryClient({ - apiBase: apiBaseUrl, - }); + it("should throw error for slug uriType", async () => { + const client = new RegistryClient(); const id: PackageIdentifier = { uriType: "slug", @@ -116,8 +63,9 @@ describe("RegistryClient", () => { }, }; - const result = await client.getContent(id); - expect(result).toBe("content for /registry/v1/owner/package/1.0.0"); + await expect(client.getContent(id)).rejects.toThrow( + "Slug-based package resolution is not supported", + ); }); it("should throw error for unknown uriType", async () => { @@ -205,34 +153,4 @@ describe("RegistryClient", () => { }).toThrow("No rootPath provided for relative file path"); }); }); - - describe("getContentFromSlug", () => { - it("should fetch content from API without auth token", async () => { - const client = new RegistryClient({ - apiBase: apiBaseUrl, - }); - const result = await (client as any).getContentFromSlug({ - ownerSlug: "owner", - packageSlug: "package", - versionSlug: "1.0.0", - }); - - expect(result).toBe("content for /registry/v1/owner/package/1.0.0"); - }); - - it("should include auth token in request when provided", async () => { - const client = new RegistryClient({ - apiBase: apiBaseUrl, - accessToken: "test-token", - }); - - const result = await (client as any).getContentFromSlug({ - ownerSlug: "owner", - packageSlug: "package", - versionSlug: "1.0.0", - }); - - expect(result).toBe("auth content for /registry/v1/owner/package/1.0.0"); - }); - }); }); diff --git a/packages/config-yaml/src/registryClient.ts b/packages/config-yaml/src/registryClient.ts index e889f76d7fd..b9e110a6039 100644 --- a/packages/config-yaml/src/registryClient.ts +++ b/packages/config-yaml/src/registryClient.ts @@ -1,26 +1,17 @@ import * as fs from "node:fs"; import * as path from "node:path"; import { Registry } from "./interfaces/index.js"; -import { FullSlug, PackageIdentifier } from "./interfaces/slugs.js"; +import { PackageIdentifier } from "./interfaces/slugs.js"; interface RegistryClientOptions { - accessToken?: string; - apiBase?: string; rootPath?: string; } export class RegistryClient implements Registry { - private readonly accessToken?: string; - private readonly apiBase: string; private readonly rootPath?: string; constructor(options: RegistryClientOptions = {}) { - this.accessToken = options.accessToken; - this.apiBase = options.apiBase ?? "https://api.continue.dev/"; this.rootPath = options.rootPath; - if (!this.apiBase.endsWith("/")) { - this.apiBase += "/"; - } } async getContent(id: PackageIdentifier): Promise { @@ -33,7 +24,7 @@ export class RegistryClient implements Registry { case "file": return this.getContentFromFilePath(id.fileUri); case "slug": - return this.getContentFromSlug(id.fullSlug); + throw new Error("Slug-based package resolution is not supported"); default: throw new Error( `Unknown package identifier type: ${(id as any).uriType}`, @@ -54,19 +45,4 @@ export class RegistryClient implements Registry { throw new Error("No rootPath provided for relative file path"); } } - - private async getContentFromSlug(fullSlug: FullSlug): Promise { - const response = await fetch( - `${this.apiBase}registry/v1/${fullSlug.ownerSlug}/${fullSlug.packageSlug}/${fullSlug.versionSlug}`, - { - headers: { - ...(this.accessToken - ? { Authorization: `Bearer ${this.accessToken}` } - : {}), - }, - }, - ); - const data = await response.json(); - return data.content; - } } diff --git a/packages/config-yaml/src/schemas/models.ts b/packages/config-yaml/src/schemas/models.ts index 89856e35c95..2ea50aa6e27 100644 --- a/packages/config-yaml/src/schemas/models.ts +++ b/packages/config-yaml/src/schemas/models.ts @@ -195,37 +195,17 @@ const baseModelFields = { autocompleteOptions: autocompleteOptionsSchema.optional(), }; -export const modelSchema = z.union([ - z.object({ - ...baseModelFields, - provider: z.literal("continue-proxy"), - apiKeyLocation: z.string().optional(), - envSecretLocations: z.record(z.string(), z.string()).optional(), - orgScopeId: z.string().nullable(), - onPremProxyUrl: z.string().nullable(), - }), - z.object({ - ...baseModelFields, - provider: z.string().refine((val) => val !== "continue-proxy"), - sourceFile: z.string().optional(), - }), -]); +export const modelSchema = z.object({ + ...baseModelFields, + provider: z.string(), + sourceFile: z.string().optional(), +}); -export const partialModelSchema = z.union([ - z - .object({ - ...baseModelFields, - provider: z.literal("continue-proxy"), - apiKeyLocation: z.string().optional(), - envSecretLocations: z.record(z.string(), z.string()).optional(), - }) - .partial(), - z - .object({ - ...baseModelFields, - provider: z.string().refine((val) => val !== "continue-proxy"), - }) - .partial(), -]); +export const partialModelSchema = z + .object({ + ...baseModelFields, + provider: z.string(), + }) + .partial(); export type ModelConfig = z.infer; diff --git a/packages/hub/.npmignore b/packages/hub/.npmignore deleted file mode 100644 index 02818d35231..00000000000 --- a/packages/hub/.npmignore +++ /dev/null @@ -1,2 +0,0 @@ -.env -test \ No newline at end of file diff --git a/packages/hub/jest.config.mjs b/packages/hub/jest.config.mjs deleted file mode 100644 index 2592f3834bf..00000000000 --- a/packages/hub/jest.config.mjs +++ /dev/null @@ -1,20 +0,0 @@ -import path from "path"; -import { fileURLToPath } from "url"; - -export default { - transform: { - "\\.[jt]sx?$": ["ts-jest", { useESM: true }], - }, - - moduleNameMapper: { - "(.+)\\.js": "$1", - }, - extensionsToTreatAsEsm: [".ts"], - preset: "ts-jest/presets/default-esm", - testTimeout: 10000, - testEnvironment: "node", - globals: { - __dirname: path.dirname(fileURLToPath(import.meta.url)), - __filename: path.resolve(fileURLToPath(import.meta.url)), - }, -}; diff --git a/packages/hub/package-lock.json b/packages/hub/package-lock.json deleted file mode 100644 index cc30c7bb211..00000000000 --- a/packages/hub/package-lock.json +++ /dev/null @@ -1,3862 +0,0 @@ -{ - "name": "@continuedev/hub", - "version": "0.0.7", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@continuedev/hub", - "version": "0.0.7", - "license": "Apache-2.0", - "dependencies": { - "@continuedev/config-yaml": "^1.38.0", - "js-yaml": "^4.1.1" - }, - "devDependencies": { - "@types/jest": "^29.5.14", - "cross-env": "^7.0.3", - "jest": "^29.7.0", - "ts-jest": "^29.2.3", - "ts-node": "^10.9.2" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.0.tgz", - "integrity": "sha512-P4fwKI2mjEb3ZU5cnMJzvRsRKGBUcs8jvxIoRmr6ufAY9Xk2Bz7JubRTTivkw55c7WQJfTECeqYVa+HZ0FzREg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", - "integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.9", - "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-module-transforms": "^7.24.9", - "@babel/helpers": "^7.24.8", - "@babel/parser": "^7.24.8", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.8", - "@babel/types": "^7.24.9", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", - "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.25.0", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.8.tgz", - "integrity": "sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.24.8", - "@babel/helper-validator-option": "^7.24.8", - "browserslist": "^4.23.1", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.0.tgz", - "integrity": "sha512-bIkOa2ZJYn7FHnepzr5iX9Kmz8FjIz4UKzJ9zhX3dnYuVW0xul9RuR3skBfoLu+FPTQw90EHW9rJsSZhyLQ3fQ==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", - "@babel/traverse": "^7.25.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", - "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", - "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", - "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.0.tgz", - "integrity": "sha512-CzdIU9jdP0dg7HdyB+bHvDJGagUv+qtzZt5rYCWwW6tITNqV9odjp6Qu41gkG0ca5UfdDUWrKkiAnHHdGRnOrA==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", - "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz", - "integrity": "sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.0.tgz", - "integrity": "sha512-ubALThHQy4GCf6mbb+5ZRNmLLCI7bJ3f8Q6LHBSRlSKSWj5a7dSUzJBLv3VuIhFrFPgjF4IzPF567YG/HSCdZA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.0", - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.0", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.0.tgz", - "integrity": "sha512-LcnxQSsd9aXOIgmmSpvZ/1yo46ra2ESYyqLcryaBZOghxy5qqOBjvCWP5JfkI8yl9rlxRgdLTTMCQQRcN2hdCg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@continuedev/config-types": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/@continuedev/config-types/-/config-types-1.0.14.tgz", - "integrity": "sha512-PVHyHPyRXd2QsaNgnCpiKYU3uHFTlyuQSkqE8OwrBmQqO6/TXUVIr/2EGtyIZGrml4Y+rGMSH40WU4/0t4SGpQ==", - "dependencies": { - "zod": "^3.23.8" - } - }, - "node_modules/@continuedev/config-yaml": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/@continuedev/config-yaml/-/config-yaml-1.38.0.tgz", - "integrity": "sha512-KkkuWdqRVx20sRY0GlntxgrX4El1SrWzOe1nPuW39fH9tDFqRcJtIqvEbCrhD6zJSVYOrfuhBUzBeje/UuDBFA==", - "license": "Apache-2.0", - "dependencies": { - "@continuedev/config-types": "^1.0.14", - "yaml": "^2.8.1", - "zod": "^3.25.76" - }, - "bin": { - "config-yaml": "dist/cli.js" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.14", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", - "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", - "dev": true, - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/node": { - "version": "18.19.39", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.39.tgz", - "integrity": "sha512-nPwTRDKUctxw3di5b4TfT3I0sWDiWoPQCZjXhvdkINntwr8lcoVCKsTgnXeRubKIlfnV+eN/HYk6Jb40tbcEAQ==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", - "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", - "dev": true, - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", - "dev": true - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.23.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", - "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001640", - "electron-to-chromium": "^1.4.820", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.1.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001643", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz", - "integrity": "sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", - "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", - "dev": true - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/cross-env": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", - "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "bin": { - "cross-env": "src/bin/cross-env.js", - "cross-env-shell": "src/bin/cross-env-shell.js" - }, - "engines": { - "node": ">=10.14", - "npm": ">=6", - "yarn": ">=1" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/dedent": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", - "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", - "dev": true, - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "dev": true, - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.2.tgz", - "integrity": "sha512-kc4r3U3V3WLaaZqThjYz/Y6z8tJe+7K0bbjUVo3i+LWIypVdMx5nXCkwRe6SWbY6ILqLdc1rKcKmr3HoH7wjSQ==", - "dev": true - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", - "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-core-module": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", - "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", - "dev": true, - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jake": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", - "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", - "dev": true, - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", - "dev": true, - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ] - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-jest": { - "version": "29.2.3", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.3.tgz", - "integrity": "sha512-yCcfVdiBFngVz9/keHin9EnsrQtQtEu3nRykNy9RVp+FiPFFbPJ3Sg6Qg4+TkmH0vMP5qsTKgXSsk80HRwvdgQ==", - "dev": true, - "dependencies": { - "bs-logger": "0.x", - "ejs": "^3.1.10", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "^7.5.3", - "yargs-parser": "^21.0.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", - "dev": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - } - } -} diff --git a/packages/hub/package.json b/packages/hub/package.json deleted file mode 100644 index 256500e78d2..00000000000 --- a/packages/hub/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "@continuedev/hub", - "version": "0.0.7", - "description": "", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "type": "module", - "scripts": { - "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --passWithNoTests", - "build": "tsc" - }, - "author": "Continue Dev, Inc.", - "license": "Apache-2.0", - "dependencies": { - "@continuedev/config-yaml": "^1.38.0", - "js-yaml": "^4.1.1" - }, - "devDependencies": { - "@types/jest": "^29.5.14", - "cross-env": "^7.0.3", - "jest": "^29.7.0", - "ts-jest": "^29.2.3", - "ts-node": "^10.9.2" - } -} diff --git a/packages/hub/src/ContinueHubClient.ts b/packages/hub/src/ContinueHubClient.ts deleted file mode 100644 index de433867e0f..00000000000 --- a/packages/hub/src/ContinueHubClient.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { - AssistantUnrolled, - ConfigResult, - FQSN, - FullSlug, - SecretResult, -} from "@continuedev/config-yaml"; -import { IContinueHubClient } from "./IContinueHubClient.js"; - -interface ContinueHubClientOptions { - apiKey?: string; - apiBase?: string; - fetchOptions?: RequestInit; -} - -export class ContinueHubClient implements IContinueHubClient { - private readonly apiKey?: string; - private readonly apiBase: string; - private readonly fetchOptions?: RequestInit; - - constructor(options: ContinueHubClientOptions) { - this.apiKey = options.apiKey; - this.apiBase = options.apiBase ?? "https://api.continue.dev"; - this.fetchOptions = options.fetchOptions; - } - - private async request(path: string, init: RequestInit): Promise { - const url = new URL(path, this.apiBase).toString(); - - const finalInit: RequestInit = { - ...this.fetchOptions, - ...init, - headers: { - ...this.fetchOptions?.headers, - ...init.headers, - }, - }; - - if (this.apiKey) { - finalInit.headers = { - ...finalInit.headers, - Authorization: `Bearer ${this.apiKey}`, - }; - } - - const resp = await fetch(url, finalInit); - - if (!resp.ok) { - throw new Error( - `Control plane request failed: ${resp.status} ${await resp.text()}`, - ); - } - - return resp; - } - - async resolveFQSNs( - fqsns: FQSN[], - orgScopeId: string | null, - ): Promise<(SecretResult | undefined)[]> { - const resp = await this.request("ide/sync-secrets", { - method: "POST", - body: JSON.stringify({ fqsns, orgScopeId }), - }); - return (await resp.json()) as any; - } - async listAssistants(options: { - organizationId: string | null; - alwaysUseProxy?: boolean; - }): Promise< - { - configResult: ConfigResult; - ownerSlug: string; - packageSlug: string; - iconUrl: string; - rawYaml: string; - }[] - > { - const organizationId = options.organizationId; - const alwaysUseProxy = options.alwaysUseProxy ?? false; - - try { - const urlObj = new URL("ide/list-assistants", this.apiBase); - if (organizationId) { - urlObj.searchParams.set("organizationId", organizationId); - } - if (alwaysUseProxy) { - urlObj.searchParams.set("alwaysUseProxy", alwaysUseProxy.toString()); - } - const url = urlObj.toString(); - - const resp = await this.request(url, { - method: "GET", - }); - return (await resp.json()) as any; - } catch (e) { - return []; - } - } - - async listAssistantFullSlugs( - organizationId: string | null, - ): Promise { - const url = organizationId - ? `ide/list-assistant-full-slugs?organizationId=${organizationId}` - : "ide/list-assistant-full-slugs"; - - try { - const resp = await this.request(url, { - method: "GET", - }); - const { fullSlugs } = (await resp.json()) as any; - return fullSlugs; - } catch (e) { - return null; - } - } -} diff --git a/packages/hub/src/IContinueHubClient.ts b/packages/hub/src/IContinueHubClient.ts deleted file mode 100644 index 161e36a41db..00000000000 --- a/packages/hub/src/IContinueHubClient.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { - AssistantUnrolled, - ConfigResult, - FQSN, - FullSlug, - SecretResult, -} from "@continuedev/config-yaml"; - -/** - * Interface for the Continue Hub client. - */ -export interface IContinueHubClient { - resolveFQSNs( - fqsns: FQSN[], - orgScopeId: string | null, - ): Promise<(SecretResult | undefined)[]>; - - /** - * Do a full reload of all assistants used in the organization by the user. - */ - listAssistants(options: { - organizationId: string | null; - alwaysUseProxy?: boolean; - }): Promise< - { - configResult: ConfigResult; - ownerSlug: string; - packageSlug: string; - iconUrl: string; - rawYaml: string; - }[] - >; - - /** - * Get the list of FullSlugs (ownerSlug/packageSlug@versionSlug) for all assistant uses in the organization for the user. - * Can be used to poll for changes to assistants and then full reload when needed. - */ - listAssistantFullSlugs( - organizationId: string | null, - ): Promise; -} diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts deleted file mode 100644 index 93a53184228..00000000000 --- a/packages/hub/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./ContinueHubClient.js"; -export * from "./IContinueHubClient.js"; diff --git a/packages/hub/tsconfig.json b/packages/hub/tsconfig.json deleted file mode 100644 index 63587b99595..00000000000 --- a/packages/hub/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "compilerOptions": { - "target": "ESNext", - "module": "NodeNext", - "useDefineForClassFields": true, - "lib": ["DOM", "ESNext"], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "moduleResolution": "NodeNext", - "resolveJsonModule": true, - "isolatedModules": false, - "noEmitOnError": false, - "types": ["node", "jest"], - "outDir": "dist", - "declaration": true - // "sourceMap": true - }, - "include": ["src/**/*"] -} diff --git a/packages/openai-adapters/src/apis/ContinueProxy.ts b/packages/openai-adapters/src/apis/ContinueProxy.ts deleted file mode 100644 index 5ffc638900c..00000000000 --- a/packages/openai-adapters/src/apis/ContinueProxy.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { OpenAI } from "openai/index"; -import { - ChatCompletionCreateParams, - CompletionCreateParamsNonStreaming, - CompletionCreateParamsStreaming, -} from "openai/resources/index"; -import { z } from "zod"; -import { ContinueProxyConfigSchema } from "../types.js"; -import { FimCreateParamsStreaming, RerankCreateParams } from "./base.js"; -import { OpenAIApi } from "./OpenAI.js"; - -export interface ContinueProperties { - apiKeyLocation?: string; - envSecretLocations?: Record; - apiBase?: string; - orgScopeId?: string | null; - env?: Record; -} - -export interface ProxyModelName { - ownerSlug: string; - packageSlug: string; - provider: string; - model: string; -} - -export class ContinueProxyApi extends OpenAIApi { - // The apiKey and apiBase are set to the values for the proxy, - // but we need to keep track of the actual values that the proxy will use - // to call whatever LLM API is chosen - private actualApiBase?: string; - - // Contains extra properties that we pass along to the proxy. Originally from `env` property on LLMOptions - private configEnv?: Record; - - // Store the continue proxy config separately - private continueProxyConfig: z.infer; - - constructor(config: z.infer) { - // Convert ContinueProxyConfigSchema to OpenAIConfigSchema format - const openaiConfig = { - provider: "openai" as const, - apiKey: config.apiKey, - apiBase: config.env?.proxyUrl - ? new URL("model-proxy/v1/", config.env?.proxyUrl).toString() - : "https://api.continue.dev/model-proxy/v1/", - requestOptions: config.requestOptions, - }; - - super(openaiConfig); - - this.continueProxyConfig = config; - this.configEnv = config.env; - this.actualApiBase = config.apiBase; - } - - protected extraBodyProperties(): Record { - const continueProperties: ContinueProperties = { - apiKeyLocation: this.continueProxyConfig.env?.apiKeyLocation, - envSecretLocations: this.continueProxyConfig.env?.envSecretLocations, - apiBase: this.actualApiBase, - orgScopeId: this.continueProxyConfig.env?.orgScopeId ?? null, - env: this.configEnv, - }; - return { - continueProperties, - }; - } - - private modifyBodyWithContinueProperties>( - body: T, - ): T { - return { - ...body, - ...this.extraBodyProperties(), - }; - } - - modifyChatBody(body: T): T { - // First apply OpenAI-specific modifications - const modifiedBody = super.modifyChatBody(body); - // Then add Continue properties - return this.modifyBodyWithContinueProperties(modifiedBody); - } - - modifyCompletionBody< - T extends - | CompletionCreateParamsNonStreaming - | CompletionCreateParamsStreaming, - >(body: T): T { - return this.modifyBodyWithContinueProperties(body); - } - - modifyFimBody(body: T): T { - const modifiedBody = super.modifyFimBody(body); - return this.modifyBodyWithContinueProperties(modifiedBody); - } - - protected getHeaders(): Record { - return { - "Content-Type": "application/json", - Accept: "application/json", - "x-api-key": this.continueProxyConfig.apiKey ?? "", - Authorization: `Bearer ${this.continueProxyConfig.apiKey}`, - }; - } - - modifyEmbedBody( - body: T, - ): T { - return this.modifyBodyWithContinueProperties(body); - } - - modifyRerankBody(body: T): T { - return { - ...body, - ...this.extraBodyProperties(), - }; - } -} diff --git a/packages/openai-adapters/src/index.ts b/packages/openai-adapters/src/index.ts index a00b95b81f7..2462834db23 100644 --- a/packages/openai-adapters/src/index.ts +++ b/packages/openai-adapters/src/index.ts @@ -7,7 +7,7 @@ import { AzureApi } from "./apis/Azure.js"; import { BedrockApi } from "./apis/Bedrock.js"; import { CohereApi } from "./apis/Cohere.js"; import { CometAPIApi } from "./apis/CometAPI.js"; -import { ContinueProxyApi } from "./apis/ContinueProxy.js"; + import { DeepSeekApi } from "./apis/DeepSeek.js"; import { GeminiApi } from "./apis/Gemini.js"; import { InceptionApi } from "./apis/Inception.js"; @@ -125,8 +125,6 @@ export function constructLlmApi(config: LLMConfig): BaseLlmApi | undefined { return new VertexAIApi(config); case "llamastack": return new LlamastackApi(config); - case "continue-proxy": - return new ContinueProxyApi(config); case "xAI": return openAICompatible("https://api.x.ai/v1/", config); case "zAI": diff --git a/packages/openai-adapters/src/types.ts b/packages/openai-adapters/src/types.ts index 868a6e8dfe9..9ac4c64be83 100644 --- a/packages/openai-adapters/src/types.ts +++ b/packages/openai-adapters/src/types.ts @@ -96,17 +96,6 @@ export const LlamastackConfigSchema = OpenAIConfigSchema.extend({ }); export type LlamastackConfig = z.infer; -export const ContinueProxyConfigSchema = BasePlusConfig.extend({ - provider: z.literal("continue-proxy"), - env: z.object({ - apiKeyLocation: z.string().optional(), - envSecretLocations: z.record(z.string(), z.string()).optional(), - orgScopeId: z.string().nullable(), - proxyUrl: z.string().optional(), - }), -}); -export type ContinueProxyConfig = z.infer; - export const MockConfigSchema = BasePlusConfig.extend({ provider: z.literal("mock"), }); @@ -273,7 +262,6 @@ export const LLMConfigSchema = z.discriminatedUnion("provider", [ InceptionConfigSchema, VertexAIConfigSchema, LlamastackConfigSchema, - ContinueProxyConfigSchema, CometAPIConfigSchema, AskSageConfigSchema, AiSdkConfigSchema,