Detailed changes
@@ -88,7 +88,7 @@ const FS_WATCH_LATENCY: Duration = Duration::from_millis(100);
/// Extension IDs that are being migrated from hardcoded LLM providers.
/// For backwards compatibility, if the user has the corresponding env var set,
/// we automatically enable env var reading for these extensions on first install.
-const LEGACY_LLM_EXTENSION_IDS: &[&str] = &[
+pub const LEGACY_LLM_EXTENSION_IDS: &[&str] = &[
"anthropic",
"copilot-chat",
"google-ai",
@@ -133,7 +133,7 @@ fn migrate_legacy_llm_provider_env_var(manifest: &ExtensionManifest, cx: &mut Ap
// Check if already enabled in settings
let already_enabled = ExtensionSettings::get_global(cx)
- .allowed_env_vars
+ .allowed_env_var_providers
.contains(settings_key.as_ref());
if already_enabled {
@@ -146,7 +146,7 @@ fn migrate_legacy_llm_provider_env_var(manifest: &ExtensionManifest, cx: &mut Ap
move |settings, _| {
let allowed = settings
.extension
- .allowed_env_vars
+ .allowed_env_var_providers
.get_or_insert_with(Vec::new);
if !allowed
@@ -1192,9 +1192,16 @@ impl ExtensionStore {
}
}
- fs.create_symlink(output_path, extension_source_path)
+ fs.create_symlink(output_path, extension_source_path.clone())
.await?;
+ // Re-load manifest and run migration before reload so settings are updated before providers are registered
+ let manifest_for_migration =
+ ExtensionManifest::load(fs.clone(), &extension_source_path).await?;
+ this.update(cx, |_this, cx| {
+ migrate_legacy_llm_provider_env_var(&manifest_for_migration, cx);
+ })?;
+
this.update(cx, |this, cx| this.reload(None, cx))?.await;
this.update(cx, |this, cx| {
cx.emit(Event::ExtensionInstalled(extension_id.clone()));
@@ -19,7 +19,7 @@ pub struct ExtensionSettings {
/// The extension language model providers that are allowed to read API keys
/// from environment variables. Each entry is in the format
/// "extension_id:provider_id:ENV_VAR_NAME".
- pub allowed_env_vars: HashSet<Arc<str>>,
+ pub allowed_env_var_providers: HashSet<Arc<str>>,
}
impl ExtensionSettings {
@@ -64,9 +64,9 @@ impl Settings for ExtensionSettings {
}
})
.collect(),
- allowed_env_vars: content
+ allowed_env_var_providers: content
.extension
- .allowed_env_vars
+ .allowed_env_var_providers
.clone()
.unwrap_or_default()
.into_iter()
@@ -1,4 +1,5 @@
use crate::ExtensionSettings;
+use crate::LEGACY_LLM_EXTENSION_IDS;
use crate::wasm_host::WasmExtension;
use collections::HashSet;
@@ -69,11 +70,20 @@ impl ExtensionLanguageModelProvider {
// Build set of allowed env vars for this provider
let settings = ExtensionSettings::get_global(cx);
+ let is_legacy_extension =
+ LEGACY_LLM_EXTENSION_IDS.contains(&extension.manifest.id.as_ref());
+
let mut allowed_env_vars = HashSet::default();
if let Some(env_vars) = auth_config.as_ref().and_then(|c| c.env_vars.as_ref()) {
for env_var_name in env_vars {
let key = format!("{}:{}", provider_id_string, env_var_name);
- if settings.allowed_env_vars.contains(key.as_str()) {
+ // For legacy extensions, auto-allow if env var is set (migration will persist this)
+ let env_var_is_set = std::env::var(env_var_name)
+ .map(|v| !v.is_empty())
+ .unwrap_or(false);
+ if settings.allowed_env_var_providers.contains(key.as_str())
+ || (is_legacy_extension && env_var_is_set)
+ {
allowed_env_vars.insert(env_var_name.clone());
}
}
@@ -201,10 +211,18 @@ impl LanguageModelProvider for ExtensionLanguageModelProvider {
if let Some(ref env_vars) = auth_config.env_vars {
let provider_id_string = self.provider_id_string();
let settings = ExtensionSettings::get_global(cx);
+ let is_legacy_extension =
+ LEGACY_LLM_EXTENSION_IDS.contains(&self.extension.manifest.id.as_ref());
for env_var_name in env_vars {
let key = format!("{}:{}", provider_id_string, env_var_name);
- if settings.allowed_env_vars.contains(key.as_str()) {
+ // For legacy extensions, auto-allow if env var is set
+ let env_var_is_set = std::env::var(env_var_name)
+ .map(|v| !v.is_empty())
+ .unwrap_or(false);
+ if settings.allowed_env_var_providers.contains(key.as_str())
+ || (is_legacy_extension && env_var_is_set)
+ {
if let Ok(value) = std::env::var(env_var_name) {
if !value.is_empty() {
return true;
@@ -474,7 +492,7 @@ impl ExtensionProviderConfigurationView {
move |settings, _| {
let allowed = settings
.extension
- .allowed_env_vars
+ .allowed_env_var_providers
.get_or_insert_with(Vec::new);
if currently_allowed {
@@ -1114,6 +1114,7 @@ impl ExtensionImports for WasmState {
impl llm_provider::Host for WasmState {
async fn get_credential(&mut self, provider_id: String) -> wasmtime::Result<Option<String>> {
let extension_id = self.manifest.id.clone();
+ let is_legacy_extension = crate::LEGACY_LLM_EXTENSION_IDS.contains(&extension_id.as_ref());
// Check if this provider has env vars configured and if the user has allowed any of them
let env_vars = self
@@ -1131,6 +1132,11 @@ impl llm_provider::Host for WasmState {
let settings_key: Arc<str> =
format!("{}:{}", full_provider_id, env_var_name).into();
+ // For legacy extensions, auto-allow if env var is set
+ let env_var_is_set = env::var(env_var_name)
+ .map(|v| !v.is_empty())
+ .unwrap_or(false);
+
let is_allowed = self
.on_main_thread({
let settings_key = settings_key.clone();
@@ -1138,7 +1144,7 @@ impl llm_provider::Host for WasmState {
async move {
cx.update(|cx| {
crate::extension_settings::ExtensionSettings::get_global(cx)
- .allowed_env_vars
+ .allowed_env_var_providers
.contains(&settings_key)
})
}
@@ -1148,7 +1154,7 @@ impl llm_provider::Host for WasmState {
.await
.unwrap_or(false);
- if is_allowed {
+ if is_allowed || (is_legacy_extension && env_var_is_set) {
if let Ok(value) = env::var(env_var_name) {
if !value.is_empty() {
return Ok(Some(value));
@@ -1247,6 +1253,11 @@ impl llm_provider::Host for WasmState {
// Check if the user has allowed this specific env var
let settings_key: Arc<str> = format!("{}:{}:{}", extension_id, provider_id, name).into();
+ let is_legacy_extension = crate::LEGACY_LLM_EXTENSION_IDS.contains(&extension_id.as_ref());
+
+ // For legacy extensions, auto-allow if env var is set
+ let env_var_is_set = env::var(&name).map(|v| !v.is_empty()).unwrap_or(false);
+
let is_allowed = self
.on_main_thread({
let settings_key = settings_key.clone();
@@ -1254,7 +1265,7 @@ impl llm_provider::Host for WasmState {
async move {
cx.update(|cx| {
crate::extension_settings::ExtensionSettings::get_global(cx)
- .allowed_env_vars
+ .allowed_env_var_providers
.contains(&settings_key)
})
}
@@ -1264,7 +1275,7 @@ impl llm_provider::Host for WasmState {
.await
.unwrap_or(false);
- if !is_allowed {
+ if !is_allowed && !(is_legacy_extension && env_var_is_set) {
log::debug!(
"Extension {} provider {} is not allowed to read env var {}",
extension_id,
@@ -25,7 +25,7 @@ pub struct ExtensionSettingsContent {
/// "extension_id:provider_id:ENV_VAR_NAME" (e.g., "google-ai:google-ai:GEMINI_API_KEY").
///
/// Default: []
- pub allowed_env_vars: Option<Vec<Arc<str>>>,
+ pub allowed_env_var_providers: Option<Vec<Arc<str>>>,
}
/// A capability for an extension.