Browse Source

Simplify the process of setting the "active" provider profile (#3366)

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
Chris Estreich 7 months ago
parent
commit
55b72a530c

+ 1 - 0
.gitignore

@@ -4,6 +4,7 @@ out
 out-*
 node_modules
 coverage/
+mock/
 
 .DS_Store
 

+ 0 - 0
git


+ 34 - 26
src/core/config/ProviderSettingsManager.ts

@@ -265,52 +265,60 @@ export class ProviderSettingsManager {
 		}
 	}
 
-	/**
-	 * Load a config by name and set it as the current config.
-	 */
-	public async loadConfig(name: string) {
+	public async getProfile(params: { name: string } | { id: string }) {
 		try {
 			return await this.lock(async () => {
 				const providerProfiles = await this.load()
-				const providerSettings = providerProfiles.apiConfigs[name]
+				let name: string
+				let providerSettings: ProviderSettingsWithId
 
-				if (!providerSettings) {
-					throw new Error(`Config '${name}' not found`)
-				}
+				if ("name" in params) {
+					name = params.name
 
-				providerProfiles.currentApiConfigName = name
-				await this.store(providerProfiles)
+					if (!providerProfiles.apiConfigs[name]) {
+						throw new Error(`Config with name '${name}' not found`)
+					}
+
+					providerSettings = providerProfiles.apiConfigs[name]
+				} else {
+					const id = params.id
+
+					const entry = Object.entries(providerProfiles.apiConfigs).find(
+						([_, apiConfig]) => apiConfig.id === id,
+					)
 
-				return providerSettings
+					if (!entry) {
+						throw new Error(`Config with ID '${id}' not found`)
+					}
+
+					name = entry[0]
+					providerSettings = entry[1]
+				}
+
+				return { name, ...providerSettings }
 			})
 		} catch (error) {
-			throw new Error(`Failed to load config: ${error}`)
+			throw new Error(`Failed to get profile: ${error instanceof Error ? error.message : error}`)
 		}
 	}
 
 	/**
-	 * Load a config by ID and set it as the current config.
+	 * Activate a profile by name or ID.
 	 */
-	public async loadConfigById(id: string) {
+	public async activateProfile(
+		params: { name: string } | { id: string },
+	): Promise<ProviderSettingsWithId & { name: string }> {
+		const { name, ...providerSettings } = await this.getProfile(params)
+
 		try {
 			return await this.lock(async () => {
 				const providerProfiles = await this.load()
-				const providerSettings = Object.entries(providerProfiles.apiConfigs).find(
-					([_, apiConfig]) => apiConfig.id === id,
-				)
-
-				if (!providerSettings) {
-					throw new Error(`Config with ID '${id}' not found`)
-				}
-
-				const [name, apiConfig] = providerSettings
 				providerProfiles.currentApiConfigName = name
 				await this.store(providerProfiles)
-
-				return { config: apiConfig, name }
+				return { name, ...providerSettings }
 			})
 		} catch (error) {
-			throw new Error(`Failed to load config by ID: ${error}`)
+			throw new Error(`Failed to activate profile: ${error instanceof Error ? error.message : error}`)
 		}
 	}
 

+ 15 - 47
src/core/config/__tests__/ProviderSettingsManager.test.ts

@@ -438,17 +438,15 @@ describe("ProviderSettingsManager", () => {
 			mockGlobalState.get.mockResolvedValue(42)
 			mockSecrets.get.mockResolvedValue(JSON.stringify(existingConfig))
 
-			const config = await providerSettingsManager.loadConfig("test")
+			const { name, ...providerSettings } = await providerSettingsManager.activateProfile({ name: "test" })
 
-			expect(config).toEqual({
-				apiProvider: "anthropic",
-				apiKey: "test-key",
-				id: "test-id",
-			})
+			expect(name).toBe("test")
+			expect(providerSettings).toEqual({ apiProvider: "anthropic", apiKey: "test-key", id: "test-id" })
 
-			// Get the stored config to check the structure
+			// Get the stored config to check the structure.
 			const storedConfig = JSON.parse(mockSecrets.store.mock.calls[1][1])
 			expect(storedConfig.currentApiConfigName).toBe("test")
+
 			expect(storedConfig.apiConfigs.test).toEqual({
 				apiProvider: "anthropic",
 				apiKey: "test-key",
@@ -460,17 +458,12 @@ describe("ProviderSettingsManager", () => {
 			mockSecrets.get.mockResolvedValue(
 				JSON.stringify({
 					currentApiConfigName: "default",
-					apiConfigs: {
-						default: {
-							config: {},
-							id: "default",
-						},
-					},
+					apiConfigs: { default: { config: {}, id: "default" } },
 				}),
 			)
 
-			await expect(providerSettingsManager.loadConfig("nonexistent")).rejects.toThrow(
-				"Config 'nonexistent' not found",
+			await expect(providerSettingsManager.activateProfile({ name: "nonexistent" })).rejects.toThrow(
+				"Config with name 'nonexistent' not found",
 			)
 		})
 
@@ -478,20 +471,13 @@ describe("ProviderSettingsManager", () => {
 			mockSecrets.get.mockResolvedValue(
 				JSON.stringify({
 					currentApiConfigName: "default",
-					apiConfigs: {
-						test: {
-							config: {
-								apiProvider: "anthropic",
-							},
-							id: "test-id",
-						},
-					},
+					apiConfigs: { test: { config: { apiProvider: "anthropic" }, id: "test-id" } },
 				}),
 			)
 			mockSecrets.store.mockRejectedValueOnce(new Error("Storage failed"))
 
-			await expect(providerSettingsManager.loadConfig("test")).rejects.toThrow(
-				"Failed to load config: Error: Failed to write provider profiles to secrets: Error: Storage failed",
+			await expect(providerSettingsManager.activateProfile({ name: "test" })).rejects.toThrow(
+				"Failed to activate profile: Failed to write provider profiles to secrets: Error: Storage failed",
 			)
 		})
 
@@ -541,12 +527,7 @@ describe("ProviderSettingsManager", () => {
 			mockSecrets.get.mockResolvedValue(
 				JSON.stringify({
 					currentApiConfigName: "test",
-					apiConfigs: {
-						test: {
-							apiProvider: "anthropic",
-							id: "test-id",
-						},
-					},
+					apiConfigs: { test: { apiProvider: "anthropic", id: "test-id" } },
 				}),
 			)
 
@@ -561,18 +542,8 @@ describe("ProviderSettingsManager", () => {
 		it("should return true for existing config", async () => {
 			const existingConfig: ProviderProfiles = {
 				currentApiConfigName: "default",
-				apiConfigs: {
-					default: {
-						id: "default",
-					},
-					test: {
-						apiProvider: "anthropic",
-						id: "test-id",
-					},
-				},
-				migrations: {
-					rateLimitSecondsMigrated: false,
-				},
+				apiConfigs: { default: { id: "default" }, test: { apiProvider: "anthropic", id: "test-id" } },
+				migrations: { rateLimitSecondsMigrated: false },
 			}
 
 			mockSecrets.get.mockResolvedValue(JSON.stringify(existingConfig))
@@ -583,10 +554,7 @@ describe("ProviderSettingsManager", () => {
 
 		it("should return false for non-existent config", async () => {
 			mockSecrets.get.mockResolvedValue(
-				JSON.stringify({
-					currentApiConfigName: "default",
-					apiConfigs: { default: {} },
-				}),
+				JSON.stringify({ currentApiConfigName: "default", apiConfigs: { default: {} } }),
 			)
 
 			const hasConfig = await providerSettingsManager.hasConfig("nonexistent")

+ 18 - 12
src/core/webview/ClineProvider.ts

@@ -762,7 +762,6 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
 	 * @param newMode The mode to switch to
 	 */
 	public async handleModeSwitch(newMode: Mode) {
-		// Capture mode switch telemetry event
 		const cline = this.getCurrentCline()
 
 		if (cline) {
@@ -779,24 +778,19 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
 		// Update listApiConfigMeta first to ensure UI has latest data
 		await this.updateGlobalState("listApiConfigMeta", listApiConfig)
 
-		// If this mode has a saved config, use it
+		// If this mode has a saved config, use it.
 		if (savedConfigId) {
-			const config = listApiConfig?.find((c) => c.id === savedConfigId)
+			const profile = listApiConfig.find(({ id }) => id === savedConfigId)
 
-			if (config?.name) {
-				const apiConfig = await this.providerSettingsManager.loadConfig(config.name)
-
-				await Promise.all([
-					this.updateGlobalState("currentApiConfigName", config.name),
-					this.updateApiConfiguration(apiConfig),
-				])
+			if (profile?.name) {
+				await this.activateProviderProfile({ name: profile.name })
 			}
 		} else {
-			// If no saved config for this mode, save current config as default
+			// If no saved config for this mode, save current config as default.
 			const currentApiConfigName = this.getGlobalState("currentApiConfigName")
 
 			if (currentApiConfigName) {
-				const config = listApiConfig?.find((c) => c.name === currentApiConfigName)
+				const config = listApiConfig.find((c) => c.name === currentApiConfigName)
 
 				if (config?.id) {
 					await this.providerSettingsManager.setModeConfig(newMode, config.id)
@@ -807,6 +801,18 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
 		await this.postStateToWebview()
 	}
 
+	async activateProviderProfile(args: { name: string } | { id: string }) {
+		const { name, ...providerSettings } = await this.providerSettingsManager.activateProfile(args)
+
+		await Promise.all([
+			this.contextProxy.setValue("listApiConfigMeta", await this.providerSettingsManager.listConfig()),
+			this.contextProxy.setValue("currentApiConfigName", name),
+		])
+
+		await this.updateApiConfiguration(providerSettings)
+		await this.postStateToWebview()
+	}
+
 	async updateApiConfiguration(providerSettings: ProviderSettings) {
 		// Update mode's default config.
 		const { mode } = await this.getState()

+ 17 - 22
src/core/webview/__tests__/ClineProvider.test.ts

@@ -5,7 +5,7 @@ import * as vscode from "vscode"
 import axios from "axios"
 
 import { ClineProvider } from "../ClineProvider"
-import { ClineMessage, ExtensionMessage, ExtensionState } from "../../../shared/ExtensionMessage"
+import { ApiConfigMeta, ClineMessage, ExtensionMessage, ExtensionState } from "../../../shared/ExtensionMessage"
 import { setSoundEnabled } from "../../../utils/sound"
 import { setTtsEnabled } from "../../../utils/tts"
 import { defaultModeSlug } from "../../../shared/modes"
@@ -227,12 +227,6 @@ jest.mock("../../../integrations/misc/extract-text", () => ({
 	}),
 }))
 
-// Spy on console.error and console.log to suppress expected messages
-beforeAll(() => {
-	jest.spyOn(console, "error").mockImplementation(() => {})
-	jest.spyOn(console, "log").mockImplementation(() => {})
-})
-
 afterAll(() => {
 	jest.restoreAllMocks()
 })
@@ -596,14 +590,16 @@ describe("ClineProvider", () => {
 		expect(state.alwaysApproveResubmit).toBe(false)
 	})
 
-	test("loads saved API config when switching modes", async () => {
+	it("loads saved API config when switching modes", async () => {
 		await provider.resolveWebviewView(mockWebviewView)
 		const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
 
+		const profile: ApiConfigMeta = { name: "test-config", id: "test-id", apiProvider: "anthropic" }
+
 		;(provider as any).providerSettingsManager = {
 			getModeConfigId: jest.fn().mockResolvedValue("test-id"),
-			listConfig: jest.fn().mockResolvedValue([{ name: "test-config", id: "test-id", apiProvider: "anthropic" }]),
-			loadConfig: jest.fn().mockResolvedValue({ apiProvider: "anthropic" }),
+			listConfig: jest.fn().mockResolvedValue([profile]),
+			activateProfile: jest.fn().mockResolvedValue(profile),
 			setModeConfig: jest.fn(),
 		} as any
 
@@ -612,7 +608,7 @@ describe("ClineProvider", () => {
 
 		// Should load the saved config for architect mode
 		expect(provider.providerSettingsManager.getModeConfigId).toHaveBeenCalledWith("architect")
-		expect(provider.providerSettingsManager.loadConfig).toHaveBeenCalledWith("test-config")
+		expect(provider.providerSettingsManager.activateProfile).toHaveBeenCalledWith({ name: "test-config" })
 		expect(mockContext.globalState.update).toHaveBeenCalledWith("currentApiConfigName", "test-config")
 	})
 
@@ -642,8 +638,7 @@ describe("ClineProvider", () => {
 		const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
 
 		;(provider as any).providerSettingsManager = {
-			loadConfig: jest.fn().mockResolvedValue({ apiProvider: "anthropic", id: "new-id" }),
-			loadConfigById: jest
+			activateProfile: jest
 				.fn()
 				.mockResolvedValue({ config: { apiProvider: "anthropic", id: "new-id" }, name: "new-config" }),
 			listConfig: jest.fn().mockResolvedValue([{ name: "new-config", id: "new-id", apiProvider: "anthropic" }]),
@@ -666,7 +661,7 @@ describe("ClineProvider", () => {
 		const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
 
 		;(provider as any).providerSettingsManager = {
-			loadConfigById: jest.fn().mockResolvedValue({
+			activateProfile: jest.fn().mockResolvedValue({
 				config: { apiProvider: "anthropic", id: "config-id-123" },
 				name: "config-by-id",
 			}),
@@ -686,8 +681,8 @@ describe("ClineProvider", () => {
 		// Should save new config as default for architect mode
 		expect(provider.providerSettingsManager.setModeConfig).toHaveBeenCalledWith("architect", "config-id-123")
 
-		// Ensure the loadConfigById method was called with the correct ID
-		expect(provider.providerSettingsManager.loadConfigById).toHaveBeenCalledWith("config-id-123")
+		// Ensure the `activateProfile` method was called with the correct ID
+		expect(provider.providerSettingsManager.activateProfile).toHaveBeenCalledWith({ id: "config-id-123" })
 	})
 
 	test("handles browserToolEnabled setting", async () => {
@@ -1542,13 +1537,13 @@ describe("ClineProvider", () => {
 			await provider.resolveWebviewView(mockWebviewView)
 		})
 
-		test("loads saved API config when switching modes", async () => {
+		it("loads saved API config when switching modes", async () => {
+			const profile: ApiConfigMeta = { name: "saved-config", id: "saved-config-id", apiProvider: "anthropic" }
+
 			;(provider as any).providerSettingsManager = {
 				getModeConfigId: jest.fn().mockResolvedValue("saved-config-id"),
-				listConfig: jest
-					.fn()
-					.mockResolvedValue([{ name: "saved-config", id: "saved-config-id", apiProvider: "anthropic" }]),
-				loadConfig: jest.fn().mockResolvedValue({ apiProvider: "anthropic" }),
+				listConfig: jest.fn().mockResolvedValue([profile]),
+				activateProfile: jest.fn().mockResolvedValue(profile),
 				setModeConfig: jest.fn(),
 			} as any
 
@@ -1560,7 +1555,7 @@ describe("ClineProvider", () => {
 
 			// Verify saved config was loaded
 			expect(provider.providerSettingsManager.getModeConfigId).toHaveBeenCalledWith("architect")
-			expect(provider.providerSettingsManager.loadConfig).toHaveBeenCalledWith("saved-config")
+			expect(provider.providerSettingsManager.activateProfile).toHaveBeenCalledWith({ name: "saved-config" })
 			expect(mockContext.globalState.update).toHaveBeenCalledWith("currentApiConfigName", "saved-config")
 
 			// Verify state was posted to webview

+ 48 - 92
src/core/webview/webviewMessageHandler.ts

@@ -4,9 +4,9 @@ import pWaitFor from "p-wait-for"
 import * as vscode from "vscode"
 
 import { ClineProvider } from "./ClineProvider"
-import { Language, ApiConfigMeta } from "../../schemas"
+import { Language, ApiConfigMeta, ProviderSettings } from "../../schemas"
 import { changeLanguage, t } from "../../i18n"
-import { ApiConfiguration, RouterName, toRouterName } from "../../shared/api"
+import { RouterName, toRouterName } from "../../shared/api"
 import { supportPrompt } from "../../shared/support-prompt"
 
 import { checkoutDiffPayloadSchema, checkoutRestorePayloadSchema, WebviewMessage } from "../../shared/WebviewMessage"
@@ -89,19 +89,12 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
 
 					if (currentConfigName) {
 						if (!(await provider.providerSettingsManager.hasConfig(currentConfigName))) {
-							// current config name not valid, get first config in list
-							await updateGlobalState("currentApiConfigName", listApiConfig?.[0]?.name)
-							if (listApiConfig?.[0]?.name) {
-								const apiConfig = await provider.providerSettingsManager.loadConfig(
-									listApiConfig?.[0]?.name,
-								)
+							// Current config name not valid, get first config in list.
+							const name = listApiConfig[0]?.name
+							await updateGlobalState("currentApiConfigName", name)
 
-								await Promise.all([
-									updateGlobalState("listApiConfigMeta", listApiConfig),
-									provider.postMessageToWebview({ type: "listApiConfig", listApiConfig }),
-									provider.updateApiConfiguration(apiConfig),
-								])
-								await provider.postStateToWebview()
+							if (name) {
+								await provider.activateProviderProfile({ name })
 								return
 							}
 						}
@@ -939,45 +932,39 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
 					const { apiConfiguration, customSupportPrompts, listApiConfigMeta, enhancementApiConfigId } =
 						await provider.getState()
 
-					// Try to get enhancement config first, fall back to current config
-					let configToUse: ApiConfiguration = apiConfiguration
-					if (enhancementApiConfigId) {
-						const config = listApiConfigMeta?.find((c: ApiConfigMeta) => c.id === enhancementApiConfigId)
-						if (config?.name) {
-							const loadedConfig = await provider.providerSettingsManager.loadConfig(config.name)
-							if (loadedConfig.apiProvider) {
-								configToUse = loadedConfig
-							}
+					// Try to get enhancement config first, fall back to current config.
+					let configToUse: ProviderSettings = apiConfiguration
+
+					if (
+						enhancementApiConfigId &&
+						!!listApiConfigMeta.find((c: ApiConfigMeta) => c.id === enhancementApiConfigId)
+					) {
+						const { name: _, ...providerSettings } = await provider.providerSettingsManager.getProfile({
+							id: enhancementApiConfigId,
+						})
+
+						if (providerSettings.apiProvider) {
+							configToUse = providerSettings
 						}
 					}
 
 					const enhancedPrompt = await singleCompletionHandler(
 						configToUse,
-						supportPrompt.create(
-							"ENHANCE",
-							{
-								userInput: message.text,
-							},
-							customSupportPrompts,
-						),
+						supportPrompt.create("ENHANCE", { userInput: message.text }, customSupportPrompts),
 					)
 
-					// Capture telemetry for prompt enhancement
+					// Capture telemetry for prompt enhancement.
 					const currentCline = provider.getCurrentCline()
 					telemetryService.capturePromptEnhanced(currentCline?.taskId)
 
-					await provider.postMessageToWebview({
-						type: "enhancedPrompt",
-						text: enhancedPrompt,
-					})
+					await provider.postMessageToWebview({ type: "enhancedPrompt", text: enhancedPrompt })
 				} catch (error) {
 					provider.log(
 						`Error enhancing prompt: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
 					)
+
 					vscode.window.showErrorMessage(t("common:errors.enhance_prompt"))
-					await provider.postMessageToWebview({
-						type: "enhancedPrompt",
-					})
+					await provider.postMessageToWebview({ type: "enhancedPrompt" })
 				}
 			}
 			break
@@ -1096,30 +1083,23 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
 						break
 					}
 
-					// Load the old configuration to get its ID
-					const oldConfig = await provider.providerSettingsManager.loadConfig(oldName)
+					// Load the old configuration to get its ID.
+					const { id } = await provider.providerSettingsManager.getProfile({ name: oldName })
 
-					// Create a new configuration with the same ID
-					const newConfig = {
-						...message.apiConfiguration,
-						id: oldConfig.id, // Preserve the ID
-					}
+					// Create a new configuration with the new name and old ID.
+					await provider.providerSettingsManager.saveConfig(newName, { ...message.apiConfiguration, id })
 
-					// Save with the new name but same ID
-					await provider.providerSettingsManager.saveConfig(newName, newConfig)
+					// Delete the old configuration.
 					await provider.providerSettingsManager.deleteConfig(oldName)
 
-					const listApiConfig = await provider.providerSettingsManager.listConfig()
-
-					// Update listApiConfigMeta first to ensure UI has latest data
-					await updateGlobalState("listApiConfigMeta", listApiConfig)
-					await updateGlobalState("currentApiConfigName", newName)
-
-					await provider.postStateToWebview()
+					// Re-activate to update the global settings related to the
+					// currently activated provider profile.
+					await provider.activateProviderProfile({ name: newName })
 				} catch (error) {
 					provider.log(
 						`Error rename api configuration: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
 					)
+
 					vscode.window.showErrorMessage(t("common:errors.rename_api_config"))
 				}
 			}
@@ -1127,16 +1107,7 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
 		case "loadApiConfiguration":
 			if (message.text) {
 				try {
-					const apiConfig = await provider.providerSettingsManager.loadConfig(message.text)
-					const listApiConfig = await provider.providerSettingsManager.listConfig()
-
-					await Promise.all([
-						updateGlobalState("listApiConfigMeta", listApiConfig),
-						updateGlobalState("currentApiConfigName", message.text),
-						provider.updateApiConfiguration(apiConfig),
-					])
-
-					await provider.postStateToWebview()
+					await provider.activateProviderProfile({ name: message.text })
 				} catch (error) {
 					provider.log(
 						`Error load api configuration: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
@@ -1148,18 +1119,7 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
 		case "loadApiConfigurationById":
 			if (message.text) {
 				try {
-					const { config: apiConfig, name } = await provider.providerSettingsManager.loadConfigById(
-						message.text,
-					)
-					const listApiConfig = await provider.providerSettingsManager.listConfig()
-
-					await Promise.all([
-						updateGlobalState("listApiConfigMeta", listApiConfig),
-						updateGlobalState("currentApiConfigName", name),
-						provider.updateApiConfiguration(apiConfig),
-					])
-
-					await provider.postStateToWebview()
+					await provider.activateProviderProfile({ id: message.text })
 				} catch (error) {
 					provider.log(
 						`Error load api configuration by ID: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
@@ -1180,29 +1140,25 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
 					break
 				}
 
-				try {
-					await provider.providerSettingsManager.deleteConfig(message.text)
-					const listApiConfig = await provider.providerSettingsManager.listConfig()
+				const oldName = message.text
 
-					// Update listApiConfigMeta first to ensure UI has latest data
-					await updateGlobalState("listApiConfigMeta", listApiConfig)
-
-					// If this was the current config, switch to first available
-					const currentApiConfigName = getGlobalState("currentApiConfigName")
+				const newName = (await provider.providerSettingsManager.listConfig()).filter(
+					(c) => c.name !== oldName,
+				)[0]?.name
 
-					if (message.text === currentApiConfigName && listApiConfig?.[0]?.name) {
-						const apiConfig = await provider.providerSettingsManager.loadConfig(listApiConfig[0].name)
-						await Promise.all([
-							updateGlobalState("currentApiConfigName", listApiConfig[0].name),
-							provider.updateApiConfiguration(apiConfig),
-						])
-					}
+				if (!newName) {
+					vscode.window.showErrorMessage(t("common:errors.delete_api_config"))
+					return
+				}
 
-					await provider.postStateToWebview()
+				try {
+					await provider.providerSettingsManager.deleteConfig(oldName)
+					await provider.activateProviderProfile({ name: newName })
 				} catch (error) {
 					provider.log(
 						`Error delete api configuration: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
 					)
+
 					vscode.window.showErrorMessage(t("common:errors.delete_api_config"))
 				}
 			}

+ 20 - 21
src/exports/api.ts

@@ -217,21 +217,24 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
 		return id
 	}
 
-	public getProfiles() {
-		return (this.getConfiguration().listApiConfigMeta || []).map((profile) => profile.name)
+	private getProfilesMeta() {
+		return this.getConfiguration().listApiConfigMeta || []
 	}
 
-	public async setActiveProfile(name: string) {
-		const currentSettings = this.getConfiguration()
-		const profiles = currentSettings.listApiConfigMeta || []
+	public getProfiles() {
+		return this.getProfilesMeta().map((profile) => profile.name)
+	}
 
-		const profile = profiles.find((p) => p.name === name)
+	public hasProfile(name: string): boolean {
+		return !!(this.getConfiguration().listApiConfigMeta || []).find((profile) => profile.name === name)
+	}
 
-		if (!profile) {
+	public async setActiveProfile(name: string) {
+		if (!this.hasProfile(name)) {
 			throw new Error(`Profile with name "${name}" does not exist`)
 		}
 
-		await this.setConfiguration({ ...currentSettings, currentApiConfigName: profile.name })
+		await this.sidebarProvider.activateProviderProfile({ name })
 	}
 
 	public getActiveProfile() {
@@ -240,27 +243,23 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
 
 	public async deleteProfile(name: string) {
 		const currentSettings = this.getConfiguration()
-		const profiles = currentSettings.listApiConfigMeta || []
-		const targetIndex = profiles.findIndex((p) => p.name === name)
+		const listApiConfigMeta = this.getProfilesMeta()
+		const targetIndex = listApiConfigMeta.findIndex((p) => p.name === name)
 
 		if (targetIndex === -1) {
 			throw new Error(`Profile with name "${name}" does not exist`)
 		}
 
-		const profileToDelete = profiles[targetIndex]
-		profiles.splice(targetIndex, 1)
+		const profileToDelete = listApiConfigMeta[targetIndex]
+		listApiConfigMeta.splice(targetIndex, 1)
 
 		// If we're deleting the active profile, clear the currentApiConfigName.
-		const newSettings: RooCodeSettings = {
-			...currentSettings,
-			listApiConfigMeta: profiles,
-			currentApiConfigName:
-				currentSettings.currentApiConfigName === profileToDelete.name
-					? undefined
-					: currentSettings.currentApiConfigName,
-		}
+		const currentApiConfigName =
+			currentSettings.currentApiConfigName === profileToDelete.name
+				? undefined
+				: currentSettings.currentApiConfigName
 
-		await this.setConfiguration(newSettings)
+		await this.setConfiguration({ ...currentSettings, listApiConfigMeta, currentApiConfigName })
 	}
 
 	public isReady() {