Restore extension_host.rs to origin/main

Richard Feldman created

Change summary

crates/extension_host/src/extension_host.rs | 465 ++--------------------
1 file changed, 47 insertions(+), 418 deletions(-)

Detailed changes

crates/extension_host/src/extension_host.rs 🔗

@@ -1,11 +1,6 @@
-mod anthropic_migration;
 mod capability_granter;
-mod copilot_migration;
 pub mod extension_settings;
-mod google_ai_migration;
 pub mod headless_host;
-mod open_router_migration;
-mod openai_migration;
 pub mod wasm_host;
 
 #[cfg(test)]
@@ -17,14 +12,13 @@ use async_tar::Archive;
 use client::ExtensionProvides;
 use client::{Client, ExtensionMetadata, GetExtensionsResponse, proto, telemetry::Telemetry};
 use collections::{BTreeMap, BTreeSet, HashSet, btree_map};
-
 pub use extension::ExtensionManifest;
 use extension::extension_builder::{CompileExtensionOptions, ExtensionBuilder};
 use extension::{
     ExtensionContextServerProxy, ExtensionDebugAdapterProviderProxy, ExtensionEvents,
-    ExtensionGrammarProxy, ExtensionHostProxy, ExtensionLanguageModelProviderProxy,
-    ExtensionLanguageProxy, ExtensionLanguageServerProxy, ExtensionSlashCommandProxy,
-    ExtensionSnippetProxy, ExtensionThemeProxy,
+    ExtensionGrammarProxy, ExtensionHostProxy, ExtensionLanguageProxy,
+    ExtensionLanguageServerProxy, ExtensionSlashCommandProxy, ExtensionSnippetProxy,
+    ExtensionThemeProxy,
 };
 use fs::{Fs, RemoveOptions};
 use futures::future::join_all;
@@ -38,8 +32,8 @@ use futures::{
     select_biased,
 };
 use gpui::{
-    App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Global, SharedString, Task,
-    WeakEntity, actions,
+    App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Global, Task, WeakEntity,
+    actions,
 };
 use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
 use language::{
@@ -59,28 +53,15 @@ use std::{
     cmp::Ordering,
     path::{self, Path, PathBuf},
     sync::Arc,
-    time::Duration,
+    time::{Duration, Instant},
 };
 use url::Url;
 use util::{ResultExt, paths::RemotePathBuf};
-use wasm_host::llm_provider::ExtensionLanguageModelProvider;
 use wasm_host::{
     WasmExtension, WasmHost,
-    wit::{
-        LlmCacheConfiguration, LlmModelInfo, LlmProviderInfo, is_supported_wasm_api_version,
-        wasm_api_version_range,
-    },
+    wit::{is_supported_wasm_api_version, wasm_api_version_range},
 };
 
-struct LlmProviderWithModels {
-    provider_info: LlmProviderInfo,
-    models: Vec<LlmModelInfo>,
-    cache_configs: collections::HashMap<String, LlmCacheConfiguration>,
-    is_authenticated: bool,
-    icon_path: Option<SharedString>,
-    auth_config: Option<extension::LanguageModelAuthConfig>,
-}
-
 pub use extension::{
     ExtensionLibraryKind, GrammarManifestEntry, OldExtensionManifest, SchemaVersion,
 };
@@ -89,82 +70,6 @@ pub use extension_settings::ExtensionSettings;
 pub const RELOAD_DEBOUNCE_DURATION: Duration = Duration::from_millis(200);
 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.
-pub const LEGACY_LLM_EXTENSION_IDS: &[&str] = &[
-    "anthropic",
-    "copilot-chat",
-    "google-ai",
-    "openrouter",
-    "openai",
-];
-
-/// Migrates legacy LLM provider extensions by auto-enabling env var reading
-/// if the env var is currently present in the environment.
-///
-/// This is idempotent: if the env var is already in `allowed_env_vars`,
-/// we skip. This means if a user explicitly removes it, it will be re-added on
-/// next launch if the env var is still set - but that's predictable behavior.
-fn migrate_legacy_llm_provider_env_var(manifest: &ExtensionManifest, cx: &mut App) {
-    // Only apply migration to known legacy LLM extensions
-    if !LEGACY_LLM_EXTENSION_IDS.contains(&manifest.id.as_ref()) {
-        return;
-    }
-
-    // Check each provider in the manifest
-    for (provider_id, provider_entry) in &manifest.language_model_providers {
-        let Some(auth_config) = &provider_entry.auth else {
-            continue;
-        };
-        let Some(env_vars) = &auth_config.env_vars else {
-            continue;
-        };
-
-        let full_provider_id = format!("{}:{}", manifest.id, provider_id);
-
-        // For each env var, check if it's set and enable it if so
-        for env_var_name in env_vars {
-            let env_var_is_set = std::env::var(env_var_name)
-                .map(|v| !v.is_empty())
-                .unwrap_or(false);
-
-            if !env_var_is_set {
-                continue;
-            }
-
-            let settings_key: Arc<str> = format!("{}:{}", full_provider_id, env_var_name).into();
-
-            // Check if already enabled in settings
-            let already_enabled = ExtensionSettings::get_global(cx)
-                .allowed_env_var_providers
-                .contains(settings_key.as_ref());
-
-            if already_enabled {
-                continue;
-            }
-
-            // Enable env var reading since the env var is set
-            settings::update_settings_file(<dyn fs::Fs>::global(cx), cx, {
-                let settings_key = settings_key.clone();
-                move |settings, _| {
-                    let allowed = settings
-                        .extension
-                        .allowed_env_var_providers
-                        .get_or_insert_with(Vec::new);
-
-                    if !allowed
-                        .iter()
-                        .any(|id| id.as_ref() == settings_key.as_ref())
-                    {
-                        allowed.push(settings_key);
-                    }
-                }
-            });
-        }
-    }
-}
-
 /// The current extension [`SchemaVersion`] supported by Zed.
 const CURRENT_SCHEMA_VERSION: SchemaVersion = SchemaVersion(1);
 
@@ -226,8 +131,6 @@ pub struct ExtensionStore {
 pub enum ExtensionOperation {
     Upgrade,
     Install,
-    /// Auto-install from settings - triggers legacy LLM provider migrations
-    AutoInstall,
     Remove,
 }
 
@@ -710,60 +613,8 @@ impl ExtensionStore {
 
         cx.spawn(async move |this, cx| {
             for extension_id in extensions_to_install {
-                // When enabled, this checks if an extension exists locally in the repo's extensions/
-                // directory and installs it as a dev extension instead of fetching from the registry.
-                // This is useful for testing auto-installed extensions before they've been published.
-                // Set to `true` only during local development/testing of new auto-install extensions.
-                #[cfg(debug_assertions)]
-                const DEBUG_ALLOW_UNPUBLISHED_AUTO_EXTENSIONS: bool = false;
-
-                #[cfg(debug_assertions)]
-                if DEBUG_ALLOW_UNPUBLISHED_AUTO_EXTENSIONS {
-                    let local_extension_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
-                        .parent()
-                        .unwrap()
-                        .parent()
-                        .unwrap()
-                        .join("extensions")
-                        .join(extension_id.as_ref());
-
-                    if local_extension_path.exists() {
-                        // Force-remove existing extension directory if it exists and isn't a symlink
-                        // This handles the case where the extension was previously installed from the registry
-                        if let Some(installed_dir) = this
-                            .update(cx, |this, _cx| this.installed_dir.clone())
-                            .ok()
-                        {
-                            let existing_path = installed_dir.join(extension_id.as_ref());
-                            if existing_path.exists() {
-                                let metadata = std::fs::symlink_metadata(&existing_path);
-                                let is_symlink = metadata.map(|m| m.is_symlink()).unwrap_or(false);
-                                if !is_symlink {
-                                    if let Err(e) = std::fs::remove_dir_all(&existing_path) {
-                                        log::error!(
-                                            "Failed to remove existing extension directory {:?}: {}",
-                                            existing_path,
-                                            e
-                                        );
-                                    }
-                                }
-                            }
-                        }
-
-                        if let Some(task) = this
-                            .update(cx, |this, cx| {
-                                this.install_dev_extension(local_extension_path, cx)
-                            })
-                            .ok()
-                        {
-                            task.await.log_err();
-                        }
-                        continue;
-                    }
-                }
-
                 this.update(cx, |this, cx| {
-                    this.auto_install_latest_extension(extension_id.clone(), cx);
+                    this.install_latest_extension(extension_id.clone(), cx);
                 })
                 .ok();
             }
@@ -918,10 +769,7 @@ impl ExtensionStore {
             this.update(cx, |this, cx| this.reload(Some(extension_id.clone()), cx))?
                 .await;
 
-            if matches!(
-                operation,
-                ExtensionOperation::Install | ExtensionOperation::AutoInstall
-            ) {
+            if let ExtensionOperation::Install = operation {
                 this.update(cx, |this, cx| {
                     cx.emit(Event::ExtensionInstalled(extension_id.clone()));
                     if let Some(events) = ExtensionEvents::try_global(cx)
@@ -931,27 +779,6 @@ impl ExtensionStore {
                             this.emit(extension::Event::ExtensionInstalled(manifest.clone()), cx)
                         });
                     }
-
-                    // Run legacy LLM provider migrations only for auto-installed extensions
-                    if matches!(operation, ExtensionOperation::AutoInstall) {
-                        if let Some(manifest) = this.extension_manifest_for_id(&extension_id) {
-                            migrate_legacy_llm_provider_env_var(&manifest, cx);
-                        }
-                        copilot_migration::migrate_copilot_credentials_if_needed(&extension_id, cx);
-                        anthropic_migration::migrate_anthropic_credentials_if_needed(
-                            &extension_id,
-                            cx,
-                        );
-                        google_ai_migration::migrate_google_ai_credentials_if_needed(
-                            &extension_id,
-                            cx,
-                        );
-                        openai_migration::migrate_openai_credentials_if_needed(&extension_id, cx);
-                        open_router_migration::migrate_open_router_credentials_if_needed(
-                            &extension_id,
-                            cx,
-                        );
-                    }
                 })
                 .ok();
             }
@@ -961,24 +788,8 @@ impl ExtensionStore {
     }
 
     pub fn install_latest_extension(&mut self, extension_id: Arc<str>, cx: &mut Context<Self>) {
-        self.install_latest_extension_with_operation(extension_id, ExtensionOperation::Install, cx);
-    }
-
-    /// Auto-install an extension, triggering legacy LLM provider migrations.
-    fn auto_install_latest_extension(&mut self, extension_id: Arc<str>, cx: &mut Context<Self>) {
-        self.install_latest_extension_with_operation(
-            extension_id,
-            ExtensionOperation::AutoInstall,
-            cx,
-        );
-    }
+        log::info!("installing extension {extension_id} latest version");
 
-    fn install_latest_extension_with_operation(
-        &mut self,
-        extension_id: Arc<str>,
-        operation: ExtensionOperation,
-        cx: &mut Context<Self>,
-    ) {
         let schema_versions = schema_version_range();
         let wasm_api_versions = wasm_api_version_range(ReleaseChannel::global(cx));
 
@@ -1001,8 +812,13 @@ impl ExtensionStore {
             return;
         };
 
-        self.install_or_upgrade_extension_at_endpoint(extension_id, url, operation, cx)
-            .detach_and_log_err(cx);
+        self.install_or_upgrade_extension_at_endpoint(
+            extension_id,
+            url,
+            ExtensionOperation::Install,
+            cx,
+        )
+        .detach_and_log_err(cx);
     }
 
     pub fn upgrade_extension(
@@ -1021,6 +837,7 @@ impl ExtensionStore {
         operation: ExtensionOperation,
         cx: &mut Context<Self>,
     ) -> Task<Result<()>> {
+        log::info!("installing extension {extension_id} {version}");
         let Some(url) = self
             .http_client
             .build_zed_api_url(
@@ -1196,37 +1013,9 @@ impl ExtensionStore {
                 }
             }
 
-            fs.create_symlink(output_path, extension_source_path.clone())
+            fs.create_symlink(output_path, extension_source_path)
                 .await?;
 
-            // Re-load manifest and run migrations 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);
-                // Also run credential migrations for dev extensions
-                copilot_migration::migrate_copilot_credentials_if_needed(
-                    manifest_for_migration.id.as_ref(),
-                    cx,
-                );
-                anthropic_migration::migrate_anthropic_credentials_if_needed(
-                    manifest_for_migration.id.as_ref(),
-                    cx,
-                );
-                google_ai_migration::migrate_google_ai_credentials_if_needed(
-                    manifest_for_migration.id.as_ref(),
-                    cx,
-                );
-                openai_migration::migrate_openai_credentials_if_needed(
-                    manifest_for_migration.id.as_ref(),
-                    cx,
-                );
-                open_router_migration::migrate_open_router_credentials_if_needed(
-                    manifest_for_migration.id.as_ref(),
-                    cx,
-                );
-            })?;
-
             this.update(cx, |this, cx| this.reload(None, cx))?.await;
             this.update(cx, |this, cx| {
                 cx.emit(Event::ExtensionInstalled(extension_id.clone()));
@@ -1345,6 +1134,18 @@ impl ExtensionStore {
             return Task::ready(());
         }
 
+        let reload_count = extensions_to_unload
+            .iter()
+            .filter(|id| extensions_to_load.contains(id))
+            .count();
+
+        log::info!(
+            "extensions updated. loading {}, reloading {}, unloading {}",
+            extensions_to_load.len() - reload_count,
+            reload_count,
+            extensions_to_unload.len() - reload_count
+        );
+
         let extension_ids = extensions_to_load
             .iter()
             .filter_map(|id| {
@@ -1419,11 +1220,6 @@ impl ExtensionStore {
             for command_name in extension.manifest.slash_commands.keys() {
                 self.proxy.unregister_slash_command(command_name.clone());
             }
-            for provider_id in extension.manifest.language_model_providers.keys() {
-                let full_provider_id: Arc<str> = format!("{}:{}", extension_id, provider_id).into();
-                self.proxy
-                    .unregister_language_model_provider(full_provider_id, cx);
-            }
         }
 
         self.wasm_extensions
@@ -1562,11 +1358,7 @@ impl ExtensionStore {
             })
             .await;
 
-            let mut wasm_extensions: Vec<(
-                Arc<ExtensionManifest>,
-                WasmExtension,
-                Vec<LlmProviderWithModels>,
-            )> = Vec::new();
+            let mut wasm_extensions = Vec::new();
             for extension in extension_entries {
                 if extension.manifest.lib.kind.is_none() {
                     continue;
@@ -1584,149 +1376,7 @@ impl ExtensionStore {
 
                 match wasm_extension {
                     Ok(wasm_extension) => {
-                        // Query for LLM providers if the manifest declares any
-                        let mut llm_providers_with_models = Vec::new();
-                        if !extension.manifest.language_model_providers.is_empty() {
-                            let providers_result = wasm_extension
-                                .call(|ext, store| {
-                                    async move { ext.call_llm_providers(store).await }.boxed()
-                                })
-                                .await;
-
-                            if let Ok(Ok(providers)) = providers_result {
-                                for provider_info in providers {
-                                    let models_result = wasm_extension
-                                        .call({
-                                            let provider_id = provider_info.id.clone();
-                                            |ext, store| {
-                                                async move {
-                                                    ext.call_llm_provider_models(store, &provider_id)
-                                                        .await
-                                                }
-                                                .boxed()
-                                            }
-                                        })
-                                        .await;
-
-                                    let models: Vec<LlmModelInfo> = match models_result {
-                                        Ok(Ok(Ok(models))) => models,
-                                        Ok(Ok(Err(e))) => {
-                                            log::error!(
-                                                "Failed to get models for LLM provider {} in extension {}: {}",
-                                                provider_info.id,
-                                                extension.manifest.id,
-                                                e
-                                            );
-                                            Vec::new()
-                                        }
-                                        Ok(Err(e)) => {
-                                            log::error!(
-                                                "Wasm error calling llm_provider_models for {} in extension {}: {:?}",
-                                                provider_info.id,
-                                                extension.manifest.id,
-                                                e
-                                            );
-                                            Vec::new()
-                                        }
-                                        Err(e) => {
-                                            log::error!(
-                                                "Extension call failed for llm_provider_models {} in extension {}: {:?}",
-                                                provider_info.id,
-                                                extension.manifest.id,
-                                                e
-                                            );
-                                            Vec::new()
-                                        }
-                                    };
-
-                                    // Query cache configurations for each model
-                                    let mut cache_configs = collections::HashMap::default();
-                                    for model in &models {
-                                        let cache_config_result = wasm_extension
-                                            .call({
-                                                let provider_id = provider_info.id.clone();
-                                                let model_id = model.id.clone();
-                                                |ext, store| {
-                                                    async move {
-                                                        ext.call_llm_cache_configuration(
-                                                            store,
-                                                            &provider_id,
-                                                            &model_id,
-                                                        )
-                                                        .await
-                                                    }
-                                                    .boxed()
-                                                }
-                                            })
-                                            .await;
-
-                                        if let Ok(Ok(Some(config))) = cache_config_result {
-                                            cache_configs.insert(model.id.clone(), config);
-                                        }
-                                    }
-
-                                    // Query initial authentication state
-                                    let is_authenticated = wasm_extension
-                                        .call({
-                                            let provider_id = provider_info.id.clone();
-                                            |ext, store| {
-                                                async move {
-                                                    ext.call_llm_provider_is_authenticated(
-                                                        store,
-                                                        &provider_id,
-                                                    )
-                                                    .await
-                                                }
-                                                .boxed()
-                                            }
-                                        })
-                                        .await
-                                        .unwrap_or(Ok(false))
-                                        .unwrap_or(false);
-
-                                    // Resolve icon path if provided
-                                    let icon_path = provider_info.icon.as_ref().map(|icon| {
-                                        let icon_file_path = extension_path.join(icon);
-                                        // Canonicalize to resolve symlinks (dev extensions are symlinked)
-                                        let absolute_icon_path = icon_file_path
-                                            .canonicalize()
-                                            .unwrap_or(icon_file_path)
-                                            .to_string_lossy()
-                                            .to_string();
-                                        SharedString::from(absolute_icon_path)
-                                    });
-
-                                    let provider_id_arc: Arc<str> =
-                                        provider_info.id.as_str().into();
-                                    let auth_config = extension
-                                        .manifest
-                                        .language_model_providers
-                                        .get(&provider_id_arc)
-                                        .and_then(|entry| entry.auth.clone());
-
-                                    llm_providers_with_models.push(LlmProviderWithModels {
-                                        provider_info,
-                                        models,
-                                        cache_configs,
-                                        is_authenticated,
-                                        icon_path,
-                                        auth_config,
-                                    });
-                                }
-                            } else {
-                                log::error!(
-                                    "Failed to get LLM providers from extension {}: {:?}",
-                                    extension.manifest.id,
-                                    providers_result
-                                );
-                            }
-                        }
-
-                        wasm_extensions.push((
-                            extension.manifest.clone(),
-                            wasm_extension,
-                            llm_providers_with_models,
-                        ))
+                        wasm_extensions.push((extension.manifest.clone(), wasm_extension))
                     }
                     Err(e) => {
                         log::error!(
@@ -1745,7 +1395,7 @@ impl ExtensionStore {
             this.update(cx, |this, cx| {
                 this.reload_complete_senders.clear();
 
-                for (manifest, wasm_extension, llm_providers_with_models) in &wasm_extensions {
+                for (manifest, wasm_extension) in &wasm_extensions {
                     let extension = Arc::new(wasm_extension.clone());
 
                     for (language_server_id, language_server_config) in &manifest.language_servers {
@@ -1799,42 +1449,9 @@ impl ExtensionStore {
                         this.proxy
                             .register_debug_locator(extension.clone(), debug_adapter.clone());
                     }
-
-                    // Register LLM providers
-                    for llm_provider in llm_providers_with_models {
-                        let provider_id: Arc<str> =
-                            format!("{}:{}", manifest.id, llm_provider.provider_info.id).into();
-                        let wasm_ext = extension.as_ref().clone();
-                        let pinfo = llm_provider.provider_info.clone();
-                        let mods = llm_provider.models.clone();
-                        let cache_cfgs = llm_provider.cache_configs.clone();
-                        let auth = llm_provider.is_authenticated;
-                        let icon = llm_provider.icon_path.clone();
-                        let auth_config = llm_provider.auth_config.clone();
-
-                        this.proxy.register_language_model_provider(
-                            provider_id.clone(),
-                            Box::new(move |cx: &mut App| {
-                                let provider = Arc::new(ExtensionLanguageModelProvider::new(
-                                    wasm_ext, pinfo, mods, cache_cfgs, auth, icon, auth_config, cx,
-                                ));
-                                language_model::LanguageModelRegistry::global(cx).update(
-                                    cx,
-                                    |registry, cx| {
-                                        registry.register_provider(provider, cx);
-                                    },
-                                );
-                            }),
-                            cx,
-                        );
-                    }
                 }
 
-                let wasm_extensions_without_llm: Vec<_> = wasm_extensions
-                    .into_iter()
-                    .map(|(manifest, ext, _)| (manifest, ext))
-                    .collect();
-                this.wasm_extensions.extend(wasm_extensions_without_llm);
+                this.wasm_extensions.extend(wasm_extensions);
                 this.proxy.set_extensions_loaded();
                 this.proxy.reload_current_theme(cx);
                 this.proxy.reload_current_icon_theme(cx);
@@ -1856,6 +1473,7 @@ impl ExtensionStore {
         let index_path = self.index_path.clone();
         let proxy = self.proxy.clone();
         cx.background_spawn(async move {
+            let start_time = Instant::now();
             let mut index = ExtensionIndex::default();
 
             fs.create_dir(&work_dir).await.log_err();
@@ -1893,6 +1511,7 @@ impl ExtensionStore {
                     .log_err();
             }
 
+            log::info!("rebuilt extension index in {:?}", start_time.elapsed());
             index
         })
     }
@@ -2166,6 +1785,11 @@ impl ExtensionStore {
                     })?,
                 path_style,
             );
+            log::info!(
+                "Uploading extension {} to {:?}",
+                missing_extension.clone().id,
+                dest_dir
+            );
 
             client
                 .update(cx, |client, cx| {
@@ -2173,6 +1797,11 @@ impl ExtensionStore {
                 })?
                 .await?;
 
+            log::info!(
+                "Finished uploading extension {}",
+                missing_extension.clone().id
+            );
+
             let result = client
                 .update(cx, |client, _cx| {
                     client.proto_client().request(proto::InstallExtension {