Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 8 additions & 173 deletions core/config/ConfigHandler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ConfigResult, ConfigValidationError } from "@continuedev/config-yaml";

import { ControlPlaneClient } from "../control-plane/client.js";
import {
BrowserSerializedContinueConfig,
ContinueConfig,
Expand All @@ -12,20 +11,13 @@ 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 {
getAllDotContinueDefinitionFiles,
LoadAssistantFilesOptions,
} from "./loadLocalAssistants.js";
import LocalProfileLoader from "./profile/LocalProfileLoader.js";
import PlatformProfileLoader from "./profile/PlatformProfileLoader.js";
import {
OrganizationDescription,
OrgWithProfiles,
Expand All @@ -39,7 +31,6 @@ export type { ProfileDescription };
type ConfigUpdateFunction = (payload: ConfigResult<ContinueConfig>) => void;

export class ConfigHandler {
controlPlaneClient: ControlPlaneClient;
private readonly globalContext = new GlobalContext();
private globalLocalProfileManager: ProfileLifecycleManager;

Expand All @@ -60,16 +51,10 @@ export class ConfigHandler {
constructor(
private readonly ide: IDE,
private llmLogger: ILLMLogger,
initialSessionInfoPromise: Promise<ControlPlaneSessionInfo | undefined>,
) {
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,
);

Expand Down Expand Up @@ -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,
);

Expand All @@ -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,
Expand All @@ -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", {
Expand Down Expand Up @@ -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 };
Expand All @@ -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<OrgWithProfiles> {
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({
Expand Down Expand Up @@ -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) {
Expand All @@ -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),
Expand All @@ -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) {
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion core/config/default.ts
Original file line number Diff line number Diff line change
@@ -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: [],
Expand Down
12 changes: 1 addition & 11 deletions core/config/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
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";
Expand All @@ -60,7 +59,6 @@

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";
Expand Down Expand Up @@ -240,7 +238,6 @@
ideInfo,
uniqueId,
llmLogger,
workOsAccessToken,
loadPromptFiles = true,
}: {
config: Config;
Expand All @@ -249,7 +246,6 @@
ideInfo: IdeInfo;
uniqueId: string;
llmLogger: ILLMLogger;
workOsAccessToken: string | undefined;
loadPromptFiles?: boolean;
}): Promise<{ config: ContinueConfig; errors: ConfigValidationError[] }> {
const errors: ConfigValidationError[] = [];
Expand Down Expand Up @@ -464,7 +460,7 @@
}
if (name === "llm") {
const llm = models.find((model) => model.title === params?.modelTitle);
if (!llm) {

Check warning on line 463 in core/config/load.ts

View workflow job for this annotation

GitHub Actions / core-checks

Unexpected negated condition
errors.push({
fatal: false,
message: `Unknown reranking model ${params?.modelTitle}`,
Expand Down Expand Up @@ -550,17 +546,14 @@
// 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) => ({
id: `continue-mcp-server-${index + 1}`,
name: `MCP Server`,
requestOptions: mergeConfigYamlRequestOptions(
server.transport.type !== "stdio"

Check warning on line 556 in core/config/load.ts

View workflow job for this annotation

GitHub Actions / core-checks

Unexpected negated condition
? server.transport.requestOptions
: undefined,
config.requestOptions,
Expand Down Expand Up @@ -677,7 +670,6 @@
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,
Expand Down Expand Up @@ -828,7 +820,6 @@
ideInfo: IdeInfo,
uniqueId: string,
llmLogger: ILLMLogger,
workOsAccessToken: string | undefined,
overrideConfigJson: SerializedContinueConfig | undefined,
): Promise<ConfigResult<ContinueConfig>> {
const workspaceConfigs = await getWorkspaceRcConfigs(ide);
Expand Down Expand Up @@ -924,7 +915,6 @@
ideInfo,
uniqueId,
llmLogger,
workOsAccessToken,
});
return {
config: finalConfig,
Expand Down
Loading
Loading