mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-05 08:10:14 +00:00
Merge c9cf493d5c into 7adbfec2a4
This commit is contained in:
commit
7a3be88323
7 changed files with 297 additions and 80 deletions
|
|
@ -536,6 +536,7 @@ export interface WebviewMessage {
|
|||
| "checkRulesDirectory"
|
||||
| "checkRulesDirectoryResult"
|
||||
| "saveCodeIndexSettingsAtomic"
|
||||
| "setUseWorkspaceConfig"
|
||||
| "requestCodeIndexSecretStatus"
|
||||
| "requestCommands"
|
||||
| "openCommandFile"
|
||||
|
|
@ -648,6 +649,8 @@ export interface WebviewMessage {
|
|||
organizationId?: string | null // For organization switching
|
||||
useProviderSignup?: boolean // For rooCloudSignIn to use provider signup flow
|
||||
codeIndexSettings?: {
|
||||
// Whether to save as workspace-specific config
|
||||
useWorkspaceConfig?: boolean
|
||||
// Global state settings
|
||||
codebaseIndexEnabled: boolean
|
||||
codebaseIndexQdrantUrl: string
|
||||
|
|
@ -750,6 +753,7 @@ export interface IndexingStatus {
|
|||
workspacePath?: string
|
||||
workspaceEnabled?: boolean
|
||||
autoEnableDefault?: boolean
|
||||
useWorkspaceConfig?: boolean
|
||||
}
|
||||
|
||||
export interface IndexingStatusUpdateMessage {
|
||||
|
|
|
|||
|
|
@ -2321,20 +2321,25 @@ export class ClineProvider
|
|||
organizationSettingsVersion,
|
||||
customCondensingPrompt,
|
||||
codebaseIndexModels: codebaseIndexModels ?? EMBEDDING_MODEL_PROFILES,
|
||||
codebaseIndexConfig: {
|
||||
codebaseIndexEnabled: codebaseIndexConfig?.codebaseIndexEnabled ?? false,
|
||||
codebaseIndexQdrantUrl: codebaseIndexConfig?.codebaseIndexQdrantUrl ?? "http://localhost:6333",
|
||||
codebaseIndexEmbedderProvider: codebaseIndexConfig?.codebaseIndexEmbedderProvider ?? "openai",
|
||||
codebaseIndexEmbedderBaseUrl: codebaseIndexConfig?.codebaseIndexEmbedderBaseUrl ?? "",
|
||||
codebaseIndexEmbedderModelId: codebaseIndexConfig?.codebaseIndexEmbedderModelId ?? "",
|
||||
codebaseIndexEmbedderModelDimension: codebaseIndexConfig?.codebaseIndexEmbedderModelDimension ?? 1536,
|
||||
codebaseIndexOpenAiCompatibleBaseUrl: codebaseIndexConfig?.codebaseIndexOpenAiCompatibleBaseUrl,
|
||||
codebaseIndexSearchMaxResults: codebaseIndexConfig?.codebaseIndexSearchMaxResults,
|
||||
codebaseIndexSearchMinScore: codebaseIndexConfig?.codebaseIndexSearchMinScore,
|
||||
codebaseIndexBedrockRegion: codebaseIndexConfig?.codebaseIndexBedrockRegion,
|
||||
codebaseIndexBedrockProfile: codebaseIndexConfig?.codebaseIndexBedrockProfile,
|
||||
codebaseIndexOpenRouterSpecificProvider: codebaseIndexConfig?.codebaseIndexOpenRouterSpecificProvider,
|
||||
},
|
||||
codebaseIndexConfig: (() => {
|
||||
// Use workspace-specific config if the current workspace has it enabled
|
||||
const currentManager = this.getCurrentWorkspaceCodeIndexManager()
|
||||
const effectiveConfig = currentManager?.getEffectiveConfig() ?? codebaseIndexConfig
|
||||
return {
|
||||
codebaseIndexEnabled: effectiveConfig?.codebaseIndexEnabled ?? false,
|
||||
codebaseIndexQdrantUrl: effectiveConfig?.codebaseIndexQdrantUrl ?? "http://localhost:6333",
|
||||
codebaseIndexEmbedderProvider: effectiveConfig?.codebaseIndexEmbedderProvider ?? "openai",
|
||||
codebaseIndexEmbedderBaseUrl: effectiveConfig?.codebaseIndexEmbedderBaseUrl ?? "",
|
||||
codebaseIndexEmbedderModelId: effectiveConfig?.codebaseIndexEmbedderModelId ?? "",
|
||||
codebaseIndexEmbedderModelDimension: effectiveConfig?.codebaseIndexEmbedderModelDimension ?? 1536,
|
||||
codebaseIndexOpenAiCompatibleBaseUrl: effectiveConfig?.codebaseIndexOpenAiCompatibleBaseUrl,
|
||||
codebaseIndexSearchMaxResults: effectiveConfig?.codebaseIndexSearchMaxResults,
|
||||
codebaseIndexSearchMinScore: effectiveConfig?.codebaseIndexSearchMinScore,
|
||||
codebaseIndexBedrockRegion: effectiveConfig?.codebaseIndexBedrockRegion,
|
||||
codebaseIndexBedrockProfile: effectiveConfig?.codebaseIndexBedrockProfile,
|
||||
codebaseIndexOpenRouterSpecificProvider: effectiveConfig?.codebaseIndexOpenRouterSpecificProvider,
|
||||
}
|
||||
})(),
|
||||
// Only set mdmCompliant if there's an actual MDM policy
|
||||
// undefined means no MDM policy, true means compliant, false means non-compliant
|
||||
mdmCompliant: this.mdmService?.requiresCloudAuth() ? this.checkMdmCompliance() : undefined,
|
||||
|
|
@ -2542,25 +2547,25 @@ export class ClineProvider
|
|||
organizationSettingsVersion,
|
||||
customCondensingPrompt: stateValues.customCondensingPrompt,
|
||||
codebaseIndexModels: stateValues.codebaseIndexModels ?? EMBEDDING_MODEL_PROFILES,
|
||||
codebaseIndexConfig: {
|
||||
codebaseIndexEnabled: stateValues.codebaseIndexConfig?.codebaseIndexEnabled ?? false,
|
||||
codebaseIndexQdrantUrl:
|
||||
stateValues.codebaseIndexConfig?.codebaseIndexQdrantUrl ?? "http://localhost:6333",
|
||||
codebaseIndexEmbedderProvider:
|
||||
stateValues.codebaseIndexConfig?.codebaseIndexEmbedderProvider ?? "openai",
|
||||
codebaseIndexEmbedderBaseUrl: stateValues.codebaseIndexConfig?.codebaseIndexEmbedderBaseUrl ?? "",
|
||||
codebaseIndexEmbedderModelId: stateValues.codebaseIndexConfig?.codebaseIndexEmbedderModelId ?? "",
|
||||
codebaseIndexEmbedderModelDimension:
|
||||
stateValues.codebaseIndexConfig?.codebaseIndexEmbedderModelDimension,
|
||||
codebaseIndexOpenAiCompatibleBaseUrl:
|
||||
stateValues.codebaseIndexConfig?.codebaseIndexOpenAiCompatibleBaseUrl,
|
||||
codebaseIndexSearchMaxResults: stateValues.codebaseIndexConfig?.codebaseIndexSearchMaxResults,
|
||||
codebaseIndexSearchMinScore: stateValues.codebaseIndexConfig?.codebaseIndexSearchMinScore,
|
||||
codebaseIndexBedrockRegion: stateValues.codebaseIndexConfig?.codebaseIndexBedrockRegion,
|
||||
codebaseIndexBedrockProfile: stateValues.codebaseIndexConfig?.codebaseIndexBedrockProfile,
|
||||
codebaseIndexOpenRouterSpecificProvider:
|
||||
stateValues.codebaseIndexConfig?.codebaseIndexOpenRouterSpecificProvider,
|
||||
},
|
||||
codebaseIndexConfig: (() => {
|
||||
// Use workspace-specific config if the current workspace has it enabled
|
||||
const wsManager = this.getCurrentWorkspaceCodeIndexManager()
|
||||
const effectiveCfg = wsManager?.getEffectiveConfig() ?? stateValues.codebaseIndexConfig
|
||||
return {
|
||||
codebaseIndexEnabled: effectiveCfg?.codebaseIndexEnabled ?? false,
|
||||
codebaseIndexQdrantUrl: effectiveCfg?.codebaseIndexQdrantUrl ?? "http://localhost:6333",
|
||||
codebaseIndexEmbedderProvider: effectiveCfg?.codebaseIndexEmbedderProvider ?? "openai",
|
||||
codebaseIndexEmbedderBaseUrl: effectiveCfg?.codebaseIndexEmbedderBaseUrl ?? "",
|
||||
codebaseIndexEmbedderModelId: effectiveCfg?.codebaseIndexEmbedderModelId ?? "",
|
||||
codebaseIndexEmbedderModelDimension: effectiveCfg?.codebaseIndexEmbedderModelDimension,
|
||||
codebaseIndexOpenAiCompatibleBaseUrl: effectiveCfg?.codebaseIndexOpenAiCompatibleBaseUrl,
|
||||
codebaseIndexSearchMaxResults: effectiveCfg?.codebaseIndexSearchMaxResults,
|
||||
codebaseIndexSearchMinScore: effectiveCfg?.codebaseIndexSearchMinScore,
|
||||
codebaseIndexBedrockRegion: effectiveCfg?.codebaseIndexBedrockRegion,
|
||||
codebaseIndexBedrockProfile: effectiveCfg?.codebaseIndexBedrockProfile,
|
||||
codebaseIndexOpenRouterSpecificProvider: effectiveCfg?.codebaseIndexOpenRouterSpecificProvider,
|
||||
}
|
||||
})(),
|
||||
profileThresholds: stateValues.profileThresholds ?? {},
|
||||
lockApiConfigAcrossModes: this.context.workspaceState.get("lockApiConfigAcrossModes", false),
|
||||
includeDiagnosticMessages: stateValues.includeDiagnosticMessages ?? true,
|
||||
|
|
|
|||
|
|
@ -2511,16 +2511,15 @@ export const webviewMessageHandler = async (
|
|||
}
|
||||
|
||||
const settings = message.codeIndexSettings
|
||||
const saveToWorkspace = settings.useWorkspaceConfig === true
|
||||
|
||||
try {
|
||||
// Check if embedder provider has changed
|
||||
const currentConfig = getGlobalState("codebaseIndexConfig") || {}
|
||||
const embedderProviderChanged =
|
||||
currentConfig.codebaseIndexEmbedderProvider !== settings.codebaseIndexEmbedderProvider
|
||||
const currentCodeIndexManager = provider.getCurrentWorkspaceCodeIndexManager()
|
||||
|
||||
// Save global state settings atomically
|
||||
const globalStateConfig = {
|
||||
...currentConfig,
|
||||
// Build the config object (used for both global and workspace saves)
|
||||
const configObject = {
|
||||
codebaseIndexEnabled: settings.codebaseIndexEnabled,
|
||||
codebaseIndexQdrantUrl: settings.codebaseIndexQdrantUrl,
|
||||
codebaseIndexEmbedderProvider: settings.codebaseIndexEmbedderProvider,
|
||||
|
|
@ -2535,59 +2534,71 @@ export const webviewMessageHandler = async (
|
|||
codebaseIndexOpenRouterSpecificProvider: settings.codebaseIndexOpenRouterSpecificProvider,
|
||||
}
|
||||
|
||||
// Save global state first
|
||||
await updateGlobalState("codebaseIndexConfig", globalStateConfig)
|
||||
|
||||
// Save secrets directly using context proxy
|
||||
// Build secrets map
|
||||
const secretEntries: Record<string, string> = {}
|
||||
if (settings.codeIndexOpenAiKey !== undefined) {
|
||||
await provider.contextProxy.storeSecret("codeIndexOpenAiKey", settings.codeIndexOpenAiKey)
|
||||
secretEntries.codeIndexOpenAiKey = settings.codeIndexOpenAiKey
|
||||
}
|
||||
if (settings.codeIndexQdrantApiKey !== undefined) {
|
||||
await provider.contextProxy.storeSecret("codeIndexQdrantApiKey", settings.codeIndexQdrantApiKey)
|
||||
secretEntries.codeIndexQdrantApiKey = settings.codeIndexQdrantApiKey
|
||||
}
|
||||
if (settings.codebaseIndexOpenAiCompatibleApiKey !== undefined) {
|
||||
await provider.contextProxy.storeSecret(
|
||||
"codebaseIndexOpenAiCompatibleApiKey",
|
||||
settings.codebaseIndexOpenAiCompatibleApiKey,
|
||||
)
|
||||
secretEntries.codebaseIndexOpenAiCompatibleApiKey = settings.codebaseIndexOpenAiCompatibleApiKey
|
||||
}
|
||||
if (settings.codebaseIndexGeminiApiKey !== undefined) {
|
||||
await provider.contextProxy.storeSecret(
|
||||
"codebaseIndexGeminiApiKey",
|
||||
settings.codebaseIndexGeminiApiKey,
|
||||
)
|
||||
secretEntries.codebaseIndexGeminiApiKey = settings.codebaseIndexGeminiApiKey
|
||||
}
|
||||
if (settings.codebaseIndexMistralApiKey !== undefined) {
|
||||
await provider.contextProxy.storeSecret(
|
||||
"codebaseIndexMistralApiKey",
|
||||
settings.codebaseIndexMistralApiKey,
|
||||
)
|
||||
secretEntries.codebaseIndexMistralApiKey = settings.codebaseIndexMistralApiKey
|
||||
}
|
||||
if (settings.codebaseIndexVercelAiGatewayApiKey !== undefined) {
|
||||
await provider.contextProxy.storeSecret(
|
||||
"codebaseIndexVercelAiGatewayApiKey",
|
||||
settings.codebaseIndexVercelAiGatewayApiKey,
|
||||
)
|
||||
secretEntries.codebaseIndexVercelAiGatewayApiKey = settings.codebaseIndexVercelAiGatewayApiKey
|
||||
}
|
||||
if (settings.codebaseIndexOpenRouterApiKey !== undefined) {
|
||||
await provider.contextProxy.storeSecret(
|
||||
"codebaseIndexOpenRouterApiKey",
|
||||
settings.codebaseIndexOpenRouterApiKey,
|
||||
)
|
||||
secretEntries.codebaseIndexOpenRouterApiKey = settings.codebaseIndexOpenRouterApiKey
|
||||
}
|
||||
|
||||
// Determine effective previous config for change detection
|
||||
const effectivePreviousProvider = saveToWorkspace
|
||||
? currentCodeIndexManager?.getWorkspaceConfig()?.codebaseIndexEmbedderProvider
|
||||
: currentConfig.codebaseIndexEmbedderProvider
|
||||
const embedderProviderChanged = effectivePreviousProvider !== settings.codebaseIndexEmbedderProvider
|
||||
|
||||
if (saveToWorkspace && currentCodeIndexManager) {
|
||||
// Save to workspace-specific storage
|
||||
await currentCodeIndexManager.saveWorkspaceConfig(configObject)
|
||||
|
||||
// Merge new secrets with existing workspace secrets
|
||||
const existingSecrets = currentCodeIndexManager.getWorkspaceSecrets()
|
||||
await currentCodeIndexManager.saveWorkspaceSecrets({
|
||||
...existingSecrets,
|
||||
...secretEntries,
|
||||
})
|
||||
} else {
|
||||
// Save to global state (existing behavior)
|
||||
const globalStateConfig = {
|
||||
...currentConfig,
|
||||
...configObject,
|
||||
}
|
||||
await updateGlobalState("codebaseIndexConfig", globalStateConfig)
|
||||
|
||||
// Save secrets to global secret store
|
||||
for (const [key, value] of Object.entries(secretEntries)) {
|
||||
await provider.contextProxy.storeSecret(key as any, value)
|
||||
}
|
||||
}
|
||||
|
||||
// Send success response first - settings are saved regardless of validation
|
||||
await provider.postMessageToWebview({
|
||||
type: "codeIndexSettingsSaved",
|
||||
success: true,
|
||||
settings: globalStateConfig,
|
||||
settings: configObject,
|
||||
})
|
||||
|
||||
// Update webview state
|
||||
await provider.postStateToWebview()
|
||||
|
||||
// Then handle validation and initialization for the current workspace
|
||||
const currentCodeIndexManager = provider.getCurrentWorkspaceCodeIndexManager()
|
||||
if (currentCodeIndexManager) {
|
||||
// If embedder provider changed, perform proactive validation
|
||||
if (embedderProviderChanged) {
|
||||
|
|
@ -2812,6 +2823,42 @@ export const webviewMessageHandler = async (
|
|||
}
|
||||
break
|
||||
}
|
||||
case "setUseWorkspaceConfig": {
|
||||
try {
|
||||
const manager = provider.getCurrentWorkspaceCodeIndexManager()
|
||||
if (!manager) {
|
||||
provider.log("Cannot toggle workspace config: No workspace folder open")
|
||||
return
|
||||
}
|
||||
const enabled = message.bool ?? false
|
||||
await manager.setUseWorkspaceConfig(enabled)
|
||||
|
||||
if (enabled && !manager.getWorkspaceConfig()) {
|
||||
// When enabling workspace config for the first time, copy global config as starting point
|
||||
const globalConfig = getGlobalState("codebaseIndexConfig") || {}
|
||||
await manager.saveWorkspaceConfig(globalConfig)
|
||||
}
|
||||
|
||||
// Force config manager re-creation to pick up the new config source
|
||||
await manager.recoverFromError()
|
||||
if (manager.isFeatureEnabled && manager.isFeatureConfigured && manager.isWorkspaceEnabled) {
|
||||
await manager.initialize(provider.contextProxy)
|
||||
}
|
||||
|
||||
provider.postMessageToWebview({
|
||||
type: "indexingStatusUpdate",
|
||||
values: manager.getCurrentStatus(),
|
||||
})
|
||||
|
||||
// Refresh webview state so it picks up the effective config
|
||||
await provider.postStateToWebview()
|
||||
} catch (error) {
|
||||
provider.log(
|
||||
`Error toggling workspace config: ${error instanceof Error ? error.message : String(error)}`,
|
||||
)
|
||||
}
|
||||
break
|
||||
}
|
||||
case "setAutoEnableDefault": {
|
||||
try {
|
||||
const manager = provider.getCurrentWorkspaceCodeIndexManager()
|
||||
|
|
|
|||
|
|
@ -5,9 +5,20 @@ import { CodeIndexConfig, PreviousConfigSnapshot } from "./interfaces/config"
|
|||
import { DEFAULT_SEARCH_MIN_SCORE, DEFAULT_MAX_SEARCH_RESULTS } from "./constants"
|
||||
import { getDefaultModelId, getModelDimension, getModelScoreThreshold } from "../../shared/embeddingModels"
|
||||
|
||||
/**
|
||||
* A function that resolves workspace-specific config and secrets.
|
||||
* When it returns undefined, the global config is used instead.
|
||||
*/
|
||||
export type WorkspaceConfigResolver = () => { config: Record<string, any>; secrets: Record<string, string> } | undefined
|
||||
|
||||
/**
|
||||
* Manages configuration state and validation for the code indexing feature.
|
||||
* Handles loading, validating, and providing access to configuration values.
|
||||
*
|
||||
* Supports both global configuration (via ContextProxy) and per-workspace
|
||||
* configuration overrides (via WorkspaceConfigResolver). When a workspace
|
||||
* config resolver is provided and returns a value, it takes priority over
|
||||
* the global configuration.
|
||||
*/
|
||||
export class CodeIndexConfigManager {
|
||||
private codebaseIndexEnabled: boolean = false
|
||||
|
|
@ -27,7 +38,10 @@ export class CodeIndexConfigManager {
|
|||
private searchMinScore?: number
|
||||
private searchMaxResults?: number
|
||||
|
||||
constructor(private readonly contextProxy: ContextProxy) {
|
||||
constructor(
|
||||
private readonly contextProxy: ContextProxy,
|
||||
private readonly workspaceConfigResolver?: WorkspaceConfigResolver,
|
||||
) {
|
||||
// Initialize with current configuration to avoid false restart triggers
|
||||
this._loadAndSetConfiguration()
|
||||
}
|
||||
|
|
@ -44,8 +58,10 @@ export class CodeIndexConfigManager {
|
|||
* This eliminates code duplication between initializeWithCurrentConfig() and loadConfiguration().
|
||||
*/
|
||||
private _loadAndSetConfiguration(): void {
|
||||
// Load configuration from storage
|
||||
const codebaseIndexConfig = this.contextProxy?.getGlobalState("codebaseIndexConfig") ?? {
|
||||
// Check for workspace-specific config first, fall back to global config
|
||||
const workspaceSource = this.workspaceConfigResolver?.()
|
||||
|
||||
const defaultConfig: Record<string, any> = {
|
||||
codebaseIndexEnabled: false,
|
||||
codebaseIndexQdrantUrl: "http://localhost:6333",
|
||||
codebaseIndexEmbedderProvider: "openai",
|
||||
|
|
@ -57,6 +73,12 @@ export class CodeIndexConfigManager {
|
|||
codebaseIndexBedrockProfile: "",
|
||||
}
|
||||
|
||||
// Load configuration from workspace source or global state.
|
||||
// Use Record<string, any> to allow flexible property access for both
|
||||
// workspace config (which is a plain object) and global config.
|
||||
const codebaseIndexConfig: Record<string, any> =
|
||||
workspaceSource?.config ?? this.contextProxy?.getGlobalState("codebaseIndexConfig") ?? defaultConfig
|
||||
|
||||
const {
|
||||
codebaseIndexEnabled,
|
||||
codebaseIndexQdrantUrl,
|
||||
|
|
@ -67,17 +89,25 @@ export class CodeIndexConfigManager {
|
|||
codebaseIndexSearchMaxResults,
|
||||
} = codebaseIndexConfig
|
||||
|
||||
const openAiKey = this.contextProxy?.getSecret("codeIndexOpenAiKey") ?? ""
|
||||
const qdrantApiKey = this.contextProxy?.getSecret("codeIndexQdrantApiKey") ?? ""
|
||||
// Helper to resolve secrets: workspace source takes priority over global secrets
|
||||
const getSecret = (key: string): string => {
|
||||
if (workspaceSource?.secrets[key] !== undefined) {
|
||||
return workspaceSource.secrets[key]
|
||||
}
|
||||
return this.contextProxy?.getSecret(key as any) ?? ""
|
||||
}
|
||||
|
||||
const openAiKey = getSecret("codeIndexOpenAiKey")
|
||||
const qdrantApiKey = getSecret("codeIndexQdrantApiKey")
|
||||
// Fix: Read OpenAI Compatible settings from the correct location within codebaseIndexConfig
|
||||
const openAiCompatibleBaseUrl = codebaseIndexConfig.codebaseIndexOpenAiCompatibleBaseUrl ?? ""
|
||||
const openAiCompatibleApiKey = this.contextProxy?.getSecret("codebaseIndexOpenAiCompatibleApiKey") ?? ""
|
||||
const geminiApiKey = this.contextProxy?.getSecret("codebaseIndexGeminiApiKey") ?? ""
|
||||
const mistralApiKey = this.contextProxy?.getSecret("codebaseIndexMistralApiKey") ?? ""
|
||||
const vercelAiGatewayApiKey = this.contextProxy?.getSecret("codebaseIndexVercelAiGatewayApiKey") ?? ""
|
||||
const openAiCompatibleApiKey = getSecret("codebaseIndexOpenAiCompatibleApiKey")
|
||||
const geminiApiKey = getSecret("codebaseIndexGeminiApiKey")
|
||||
const mistralApiKey = getSecret("codebaseIndexMistralApiKey")
|
||||
const vercelAiGatewayApiKey = getSecret("codebaseIndexVercelAiGatewayApiKey")
|
||||
const bedrockRegion = codebaseIndexConfig.codebaseIndexBedrockRegion ?? "us-east-1"
|
||||
const bedrockProfile = codebaseIndexConfig.codebaseIndexBedrockProfile ?? ""
|
||||
const openRouterApiKey = this.contextProxy?.getSecret("codebaseIndexOpenRouterApiKey") ?? ""
|
||||
const openRouterApiKey = getSecret("codebaseIndexOpenRouterApiKey")
|
||||
const openRouterSpecificProvider = codebaseIndexConfig.codebaseIndexOpenRouterSpecificProvider ?? ""
|
||||
|
||||
// Update instance variables with configuration
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import * as vscode from "vscode"
|
|||
import { ContextProxy } from "../../core/config/ContextProxy"
|
||||
import { VectorStoreSearchResult } from "./interfaces"
|
||||
import { IndexingState } from "./interfaces/manager"
|
||||
import { CodeIndexConfigManager } from "./config-manager"
|
||||
import { CodeIndexConfigManager, WorkspaceConfigResolver } from "./config-manager"
|
||||
import { CodeIndexStateManager } from "./state-manager"
|
||||
import { CodeIndexServiceFactory } from "./service-factory"
|
||||
import { CodeIndexSearchService } from "./search-service"
|
||||
|
|
@ -120,6 +120,103 @@ export class CodeIndexManager {
|
|||
await this.context.globalState.update("codeIndexAutoEnableDefault", enabled)
|
||||
}
|
||||
|
||||
// --- Per-Workspace Config ---
|
||||
|
||||
/**
|
||||
* Returns the workspaceState key for the per-workspace config toggle.
|
||||
*/
|
||||
private _useWorkspaceConfigKey(): string {
|
||||
return "codeIndexUseWorkspaceConfig:" + this._folderUri.toString(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the workspaceState key for the per-workspace config object.
|
||||
*/
|
||||
private _workspaceConfigKey(): string {
|
||||
return "codeIndexWorkspaceConfig:" + this._folderUri.toString(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the workspaceState key for the per-workspace secrets.
|
||||
*/
|
||||
private _workspaceSecretsKey(): string {
|
||||
return "codeIndexWorkspaceSecrets:" + this._folderUri.toString(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this workspace uses workspace-specific config instead of global.
|
||||
*/
|
||||
public get useWorkspaceConfig(): boolean {
|
||||
return this.context.workspaceState.get<boolean>(this._useWorkspaceConfigKey(), false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle workspace-specific config for this workspace folder.
|
||||
*/
|
||||
public async setUseWorkspaceConfig(enabled: boolean): Promise<void> {
|
||||
await this.context.workspaceState.update(this._useWorkspaceConfigKey(), enabled)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the workspace-specific config object, if any.
|
||||
*/
|
||||
public getWorkspaceConfig(): Record<string, any> | undefined {
|
||||
return this.context.workspaceState.get<Record<string, any>>(this._workspaceConfigKey())
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves workspace-specific config.
|
||||
*/
|
||||
public async saveWorkspaceConfig(config: Record<string, any>): Promise<void> {
|
||||
await this.context.workspaceState.update(this._workspaceConfigKey(), config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets workspace-specific secrets.
|
||||
*/
|
||||
public getWorkspaceSecrets(): Record<string, string> {
|
||||
return this.context.workspaceState.get<Record<string, string>>(this._workspaceSecretsKey(), {})
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves workspace-specific secrets.
|
||||
*/
|
||||
public async saveWorkspaceSecrets(secrets: Record<string, string>): Promise<void> {
|
||||
await this.context.workspaceState.update(this._workspaceSecretsKey(), secrets)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the effective config for this workspace (workspace-specific or global).
|
||||
* Used to populate the webview state with the correct config values.
|
||||
*/
|
||||
public getEffectiveConfig(): Record<string, any> | undefined {
|
||||
if (this.useWorkspaceConfig) {
|
||||
return this.getWorkspaceConfig()
|
||||
}
|
||||
return undefined // caller should fall back to global config
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WorkspaceConfigResolver for this manager instance.
|
||||
* The resolver lazily loads workspace config when called.
|
||||
*/
|
||||
private _createWorkspaceConfigResolver(): WorkspaceConfigResolver | undefined {
|
||||
// Only create a resolver if workspace config is enabled
|
||||
if (!this.useWorkspaceConfig) {
|
||||
return undefined
|
||||
}
|
||||
return () => {
|
||||
const config = this.getWorkspaceConfig()
|
||||
if (!config) {
|
||||
return undefined
|
||||
}
|
||||
return {
|
||||
config,
|
||||
secrets: this.getWorkspaceSecrets(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public get onProgressUpdate() {
|
||||
return this._stateManager.onProgressUpdate
|
||||
}
|
||||
|
|
@ -163,7 +260,7 @@ export class CodeIndexManager {
|
|||
public async initialize(contextProxy: ContextProxy): Promise<{ requiresRestart: boolean }> {
|
||||
// 1. ConfigManager Initialization and Configuration Loading
|
||||
if (!this._configManager) {
|
||||
this._configManager = new CodeIndexConfigManager(contextProxy)
|
||||
this._configManager = new CodeIndexConfigManager(contextProxy, this._createWorkspaceConfigResolver())
|
||||
}
|
||||
// Load configuration once to get current state and restart requirements
|
||||
const { requiresRestart } = await this._configManager.loadConfiguration()
|
||||
|
|
@ -331,6 +428,7 @@ export class CodeIndexManager {
|
|||
workspacePath: this.workspacePath,
|
||||
workspaceEnabled: this.isWorkspaceEnabled,
|
||||
autoEnableDefault: this.autoEnableDefault,
|
||||
useWorkspaceConfig: this.useWorkspaceConfig,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -556,6 +556,9 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
|
|||
// Always include codebaseIndexEnabled to ensure it's persisted
|
||||
settingsToSave.codebaseIndexEnabled = currentSettings.codebaseIndexEnabled
|
||||
|
||||
// Include workspace config flag so the backend knows where to save
|
||||
settingsToSave.useWorkspaceConfig = indexingStatus.useWorkspaceConfig ?? false
|
||||
|
||||
// Save settings to backend
|
||||
vscode.postMessage({
|
||||
type: "saveCodeIndexSettingsAtomic",
|
||||
|
|
@ -1615,7 +1618,7 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
|
|||
|
||||
{/* Workspace Toggle */}
|
||||
{currentSettings.codebaseIndexEnabled && (
|
||||
<div className="flex items-center gap-2 pt-1 pb-2">
|
||||
<div className="flex items-center gap-2 pt-1 pb-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="workspace-indexing-toggle"
|
||||
|
|
@ -1636,6 +1639,34 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* Use Workspace-Specific Settings Toggle */}
|
||||
{currentSettings.codebaseIndexEnabled && (
|
||||
<div className="flex items-center gap-2 pt-1 pb-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="use-workspace-config-toggle"
|
||||
checked={indexingStatus.useWorkspaceConfig ?? false}
|
||||
onChange={(e) =>
|
||||
vscode.postMessage({
|
||||
type: "setUseWorkspaceConfig",
|
||||
bool: e.target.checked,
|
||||
})
|
||||
}
|
||||
className="accent-vscode-focusBorder"
|
||||
/>
|
||||
<label
|
||||
htmlFor="use-workspace-config-toggle"
|
||||
className="text-xs text-vscode-foreground cursor-pointer">
|
||||
{t("settings:codeIndex.useWorkspaceConfigLabel")}
|
||||
</label>
|
||||
{indexingStatus.useWorkspaceConfig && (
|
||||
<span className="text-xs text-vscode-descriptionForeground italic">
|
||||
{t("settings:codeIndex.workspaceConfigActive")}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{currentSettings.codebaseIndexEnabled && !indexingStatus.workspaceEnabled && (
|
||||
<p className="text-xs text-vscode-descriptionForeground pb-2">
|
||||
{t("settings:codeIndex.workspaceDisabledMessage")}
|
||||
|
|
|
|||
|
|
@ -279,7 +279,9 @@
|
|||
"stoppingButton": "Stopping...",
|
||||
"workspaceToggleLabel": "Enable indexing for this workspace",
|
||||
"workspaceDisabledMessage": "Indexing is configured but not enabled for this workspace.",
|
||||
"autoEnableDefaultLabel": "Auto-enable indexing for new workspaces"
|
||||
"autoEnableDefaultLabel": "Auto-enable indexing for new workspaces",
|
||||
"useWorkspaceConfigLabel": "Use workspace-specific settings",
|
||||
"workspaceConfigActive": "(settings scoped to this workspace)"
|
||||
},
|
||||
"autoApprove": {
|
||||
"description": "Run these actions without asking for permission. Only enable for actions you fully trust and if you understand the security risks.",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue