extension_host.rs

   1mod anthropic_migration;
   2mod capability_granter;
   3mod copilot_migration;
   4pub mod extension_settings;
   5mod google_ai_migration;
   6pub mod headless_host;
   7mod open_router_migration;
   8mod openai_migration;
   9pub mod wasm_host;
  10
  11#[cfg(test)]
  12mod extension_store_test;
  13
  14use anyhow::{Context as _, Result, anyhow, bail};
  15use async_compression::futures::bufread::GzipDecoder;
  16use async_tar::Archive;
  17use client::ExtensionProvides;
  18use client::{Client, ExtensionMetadata, GetExtensionsResponse, proto, telemetry::Telemetry};
  19use collections::{BTreeMap, BTreeSet, HashSet, btree_map};
  20
  21pub use extension::ExtensionManifest;
  22use extension::extension_builder::{CompileExtensionOptions, ExtensionBuilder};
  23use extension::{
  24    ExtensionContextServerProxy, ExtensionDebugAdapterProviderProxy, ExtensionEvents,
  25    ExtensionGrammarProxy, ExtensionHostProxy, ExtensionLanguageModelProviderProxy,
  26    ExtensionLanguageProxy, ExtensionLanguageServerProxy, ExtensionSlashCommandProxy,
  27    ExtensionSnippetProxy, ExtensionThemeProxy,
  28};
  29use fs::{Fs, RemoveOptions};
  30use futures::future::join_all;
  31use futures::{
  32    AsyncReadExt as _, Future, FutureExt as _, StreamExt as _,
  33    channel::{
  34        mpsc::{UnboundedSender, unbounded},
  35        oneshot,
  36    },
  37    io::BufReader,
  38    select_biased,
  39};
  40use gpui::{
  41    App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Global, SharedString, Task,
  42    WeakEntity, actions,
  43};
  44use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
  45use language::{
  46    LanguageConfig, LanguageMatcher, LanguageName, LanguageQueries, LoadedLanguage,
  47    QUERY_FILENAME_PREFIXES, Rope,
  48};
  49use node_runtime::NodeRuntime;
  50use project::ContextProviderWithTasks;
  51use release_channel::ReleaseChannel;
  52use remote::RemoteClient;
  53use semver::Version;
  54use serde::{Deserialize, Serialize};
  55use settings::Settings;
  56use std::ops::RangeInclusive;
  57use std::str::FromStr;
  58use std::{
  59    cmp::Ordering,
  60    path::{self, Path, PathBuf},
  61    sync::Arc,
  62    time::Duration,
  63};
  64use url::Url;
  65use util::{ResultExt, paths::RemotePathBuf};
  66use wasm_host::llm_provider::ExtensionLanguageModelProvider;
  67use wasm_host::{
  68    WasmExtension, WasmHost,
  69    wit::{LlmModelInfo, LlmProviderInfo, is_supported_wasm_api_version, wasm_api_version_range},
  70};
  71
  72struct LlmProviderWithModels {
  73    provider_info: LlmProviderInfo,
  74    models: Vec<LlmModelInfo>,
  75    is_authenticated: bool,
  76    icon_path: Option<SharedString>,
  77    auth_config: Option<extension::LanguageModelAuthConfig>,
  78}
  79
  80pub use extension::{
  81    ExtensionLibraryKind, GrammarManifestEntry, OldExtensionManifest, SchemaVersion,
  82};
  83pub use extension_settings::ExtensionSettings;
  84
  85pub const RELOAD_DEBOUNCE_DURATION: Duration = Duration::from_millis(200);
  86const FS_WATCH_LATENCY: Duration = Duration::from_millis(100);
  87
  88/// Extension IDs that are being migrated from hardcoded LLM providers.
  89/// For backwards compatibility, if the user has the corresponding env var set,
  90/// we automatically enable env var reading for these extensions on first install.
  91const LEGACY_LLM_EXTENSION_IDS: &[&str] = &[
  92    "anthropic",
  93    "copilot-chat",
  94    "google-ai",
  95    "openrouter",
  96    "openai",
  97];
  98
  99/// Migrates legacy LLM provider extensions by auto-enabling env var reading
 100/// if the env var is currently present in the environment.
 101///
 102/// This is idempotent: if the provider is already in `allowed_env_var_providers`,
 103/// we skip. This means if a user explicitly removes it, it will be re-added on
 104/// next launch if the env var is still set - but that's predictable behavior.
 105fn migrate_legacy_llm_provider_env_var(manifest: &ExtensionManifest, cx: &mut App) {
 106    // Only apply migration to known legacy LLM extensions
 107    if !LEGACY_LLM_EXTENSION_IDS.contains(&manifest.id.as_ref()) {
 108        return;
 109    }
 110
 111    // Check each provider in the manifest
 112    for (provider_id, provider_entry) in &manifest.language_model_providers {
 113        let Some(auth_config) = &provider_entry.auth else {
 114            continue;
 115        };
 116        let Some(env_var_name) = &auth_config.env_var else {
 117            continue;
 118        };
 119
 120        let full_provider_id: Arc<str> = format!("{}:{}", manifest.id, provider_id).into();
 121
 122        // Check if the env var is present and non-empty
 123        let env_var_is_set = std::env::var(env_var_name)
 124            .map(|v| !v.is_empty())
 125            .unwrap_or(false);
 126
 127        // If env var isn't set, no need to do anything
 128        if !env_var_is_set {
 129            continue;
 130        }
 131
 132        // Check if already enabled in settings
 133        let already_enabled = ExtensionSettings::get_global(cx)
 134            .allowed_env_var_providers
 135            .contains(full_provider_id.as_ref());
 136
 137        if already_enabled {
 138            continue;
 139        }
 140
 141        // Enable env var reading since the env var is set
 142        settings::update_settings_file(<dyn fs::Fs>::global(cx), cx, {
 143            let full_provider_id = full_provider_id.clone();
 144            move |settings, _| {
 145                let providers = settings
 146                    .extension
 147                    .allowed_env_var_providers
 148                    .get_or_insert_with(Vec::new);
 149
 150                if !providers
 151                    .iter()
 152                    .any(|id| id.as_ref() == full_provider_id.as_ref())
 153                {
 154                    providers.push(full_provider_id);
 155                }
 156            }
 157        });
 158    }
 159}
 160
 161/// The current extension [`SchemaVersion`] supported by Zed.
 162const CURRENT_SCHEMA_VERSION: SchemaVersion = SchemaVersion(1);
 163
 164/// Extensions that should no longer be loaded or downloaded.
 165///
 166/// These snippets should no longer be downloaded or loaded, because their
 167/// functionality has been integrated into the core editor.
 168const SUPPRESSED_EXTENSIONS: &[&str] = &["snippets", "ruff", "ty", "basedpyright"];
 169
 170/// Returns the [`SchemaVersion`] range that is compatible with this version of Zed.
 171pub fn schema_version_range() -> RangeInclusive<SchemaVersion> {
 172    SchemaVersion::ZERO..=CURRENT_SCHEMA_VERSION
 173}
 174
 175/// Returns whether the given extension version is compatible with this version of Zed.
 176pub fn is_version_compatible(
 177    release_channel: ReleaseChannel,
 178    extension_version: &ExtensionMetadata,
 179) -> bool {
 180    let schema_version = extension_version.manifest.schema_version.unwrap_or(0);
 181    if CURRENT_SCHEMA_VERSION.0 < schema_version {
 182        return false;
 183    }
 184
 185    if let Some(wasm_api_version) = extension_version
 186        .manifest
 187        .wasm_api_version
 188        .as_ref()
 189        .and_then(|wasm_api_version| Version::from_str(wasm_api_version).ok())
 190        && !is_supported_wasm_api_version(release_channel, wasm_api_version)
 191    {
 192        return false;
 193    }
 194
 195    true
 196}
 197
 198pub struct ExtensionStore {
 199    pub proxy: Arc<ExtensionHostProxy>,
 200    pub builder: Arc<ExtensionBuilder>,
 201    pub extension_index: ExtensionIndex,
 202    pub fs: Arc<dyn Fs>,
 203    pub http_client: Arc<HttpClientWithUrl>,
 204    pub telemetry: Option<Arc<Telemetry>>,
 205    pub reload_tx: UnboundedSender<Option<Arc<str>>>,
 206    pub reload_complete_senders: Vec<oneshot::Sender<()>>,
 207    pub installed_dir: PathBuf,
 208    pub outstanding_operations: BTreeMap<Arc<str>, ExtensionOperation>,
 209    pub index_path: PathBuf,
 210    pub modified_extensions: HashSet<Arc<str>>,
 211    pub wasm_host: Arc<WasmHost>,
 212    pub wasm_extensions: Vec<(Arc<ExtensionManifest>, WasmExtension)>,
 213    pub tasks: Vec<Task<()>>,
 214    pub remote_clients: Vec<WeakEntity<RemoteClient>>,
 215    pub ssh_registered_tx: UnboundedSender<()>,
 216}
 217
 218#[derive(Clone, Copy)]
 219pub enum ExtensionOperation {
 220    Upgrade,
 221    Install,
 222    Remove,
 223}
 224
 225#[derive(Clone)]
 226pub enum Event {
 227    ExtensionsUpdated,
 228    StartedReloading,
 229    ExtensionInstalled(Arc<str>),
 230    ExtensionUninstalled(Arc<str>),
 231    ExtensionFailedToLoad(Arc<str>),
 232}
 233
 234impl EventEmitter<Event> for ExtensionStore {}
 235
 236struct GlobalExtensionStore(Entity<ExtensionStore>);
 237
 238impl Global for GlobalExtensionStore {}
 239
 240#[derive(Debug, Deserialize, Serialize, Default, PartialEq, Eq)]
 241pub struct ExtensionIndex {
 242    pub extensions: BTreeMap<Arc<str>, ExtensionIndexEntry>,
 243    pub themes: BTreeMap<Arc<str>, ExtensionIndexThemeEntry>,
 244    #[serde(default)]
 245    pub icon_themes: BTreeMap<Arc<str>, ExtensionIndexIconThemeEntry>,
 246    pub languages: BTreeMap<LanguageName, ExtensionIndexLanguageEntry>,
 247}
 248
 249#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
 250pub struct ExtensionIndexEntry {
 251    pub manifest: Arc<ExtensionManifest>,
 252    pub dev: bool,
 253}
 254
 255#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
 256pub struct ExtensionIndexThemeEntry {
 257    pub extension: Arc<str>,
 258    pub path: PathBuf,
 259}
 260
 261#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
 262pub struct ExtensionIndexIconThemeEntry {
 263    pub extension: Arc<str>,
 264    pub path: PathBuf,
 265}
 266
 267#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
 268pub struct ExtensionIndexLanguageEntry {
 269    pub extension: Arc<str>,
 270    pub path: PathBuf,
 271    pub matcher: LanguageMatcher,
 272    pub hidden: bool,
 273    pub grammar: Option<Arc<str>>,
 274}
 275
 276actions!(
 277    zed,
 278    [
 279        /// Reloads all installed extensions.
 280        ReloadExtensions
 281    ]
 282);
 283
 284pub fn init(
 285    extension_host_proxy: Arc<ExtensionHostProxy>,
 286    fs: Arc<dyn Fs>,
 287    client: Arc<Client>,
 288    node_runtime: NodeRuntime,
 289    cx: &mut App,
 290) {
 291    let store = cx.new(move |cx| {
 292        ExtensionStore::new(
 293            paths::extensions_dir().clone(),
 294            None,
 295            extension_host_proxy,
 296            fs,
 297            client.http_client(),
 298            client.http_client(),
 299            Some(client.telemetry().clone()),
 300            node_runtime,
 301            cx,
 302        )
 303    });
 304
 305    cx.on_action(|_: &ReloadExtensions, cx| {
 306        let store = cx.global::<GlobalExtensionStore>().0.clone();
 307        store.update(cx, |store, cx| drop(store.reload(None, cx)));
 308    });
 309
 310    cx.set_global(GlobalExtensionStore(store));
 311}
 312
 313impl ExtensionStore {
 314    pub fn try_global(cx: &App) -> Option<Entity<Self>> {
 315        cx.try_global::<GlobalExtensionStore>()
 316            .map(|store| store.0.clone())
 317    }
 318
 319    pub fn global(cx: &App) -> Entity<Self> {
 320        cx.global::<GlobalExtensionStore>().0.clone()
 321    }
 322
 323    pub fn new(
 324        extensions_dir: PathBuf,
 325        build_dir: Option<PathBuf>,
 326        extension_host_proxy: Arc<ExtensionHostProxy>,
 327        fs: Arc<dyn Fs>,
 328        http_client: Arc<HttpClientWithUrl>,
 329        builder_client: Arc<dyn HttpClient>,
 330        telemetry: Option<Arc<Telemetry>>,
 331        node_runtime: NodeRuntime,
 332        cx: &mut Context<Self>,
 333    ) -> Self {
 334        let work_dir = extensions_dir.join("work");
 335        let build_dir = build_dir.unwrap_or_else(|| extensions_dir.join("build"));
 336        let installed_dir = extensions_dir.join("installed");
 337        let index_path = extensions_dir.join("index.json");
 338
 339        let (reload_tx, mut reload_rx) = unbounded();
 340        let (connection_registered_tx, mut connection_registered_rx) = unbounded();
 341        let mut this = Self {
 342            proxy: extension_host_proxy.clone(),
 343            extension_index: Default::default(),
 344            installed_dir,
 345            index_path,
 346            builder: Arc::new(ExtensionBuilder::new(builder_client, build_dir)),
 347            outstanding_operations: Default::default(),
 348            modified_extensions: Default::default(),
 349            reload_complete_senders: Vec::new(),
 350            wasm_host: WasmHost::new(
 351                fs.clone(),
 352                http_client.clone(),
 353                node_runtime,
 354                extension_host_proxy,
 355                work_dir,
 356                cx,
 357            ),
 358            wasm_extensions: Vec::new(),
 359            fs,
 360            http_client,
 361            telemetry,
 362            reload_tx,
 363            tasks: Vec::new(),
 364
 365            remote_clients: Default::default(),
 366            ssh_registered_tx: connection_registered_tx,
 367        };
 368
 369        // The extensions store maintains an index file, which contains a complete
 370        // list of the installed extensions and the resources that they provide.
 371        // This index is loaded synchronously on startup.
 372        let (index_content, index_metadata, extensions_metadata) =
 373            cx.background_executor().block(async {
 374                futures::join!(
 375                    this.fs.load(&this.index_path),
 376                    this.fs.metadata(&this.index_path),
 377                    this.fs.metadata(&this.installed_dir),
 378                )
 379            });
 380
 381        // Normally, there is no need to rebuild the index. But if the index file
 382        // is invalid or is out-of-date according to the filesystem mtimes, then
 383        // it must be asynchronously rebuilt.
 384        let mut extension_index = ExtensionIndex::default();
 385        let mut extension_index_needs_rebuild = true;
 386        if let Ok(index_content) = index_content
 387            && let Some(index) = serde_json::from_str(&index_content).log_err()
 388        {
 389            extension_index = index;
 390            if let (Ok(Some(index_metadata)), Ok(Some(extensions_metadata))) =
 391                (index_metadata, extensions_metadata)
 392                && index_metadata
 393                    .mtime
 394                    .bad_is_greater_than(extensions_metadata.mtime)
 395            {
 396                extension_index_needs_rebuild = false;
 397            }
 398        }
 399
 400        // Immediately load all of the extensions in the initial manifest. If the
 401        // index needs to be rebuild, then enqueue
 402        let load_initial_extensions = this.extensions_updated(extension_index, cx);
 403        let mut reload_future = None;
 404        if extension_index_needs_rebuild {
 405            reload_future = Some(this.reload(None, cx));
 406        }
 407
 408        cx.spawn(async move |this, cx| {
 409            if let Some(future) = reload_future {
 410                future.await;
 411            }
 412            this.update(cx, |this, cx| this.auto_install_extensions(cx))
 413                .ok();
 414            this.update(cx, |this, cx| this.check_for_updates(cx)).ok();
 415        })
 416        .detach();
 417
 418        // Perform all extension loading in a single task to ensure that we
 419        // never attempt to simultaneously load/unload extensions from multiple
 420        // parallel tasks.
 421        this.tasks.push(cx.spawn(async move |this, cx| {
 422            async move {
 423                load_initial_extensions.await;
 424
 425                let mut index_changed = false;
 426                let mut debounce_timer = cx.background_spawn(futures::future::pending()).fuse();
 427                loop {
 428                    select_biased! {
 429                        _ = debounce_timer => {
 430                            if index_changed {
 431                                let index = this
 432                                    .update(cx, |this, cx| this.rebuild_extension_index(cx))?
 433                                    .await;
 434                                this.update(cx, |this, cx| this.extensions_updated(index, cx))?
 435                                    .await;
 436                                index_changed = false;
 437                            }
 438
 439                            Self::update_remote_clients(&this, cx).await?;
 440                        }
 441                        _ = connection_registered_rx.next() => {
 442                            debounce_timer = cx
 443                                .background_executor()
 444                                .timer(RELOAD_DEBOUNCE_DURATION)
 445                                .fuse();
 446                        }
 447                        extension_id = reload_rx.next() => {
 448                            let Some(extension_id) = extension_id else { break; };
 449                            this.update(cx, |this, _| {
 450                                this.modified_extensions.extend(extension_id);
 451                            })?;
 452                            index_changed = true;
 453                            debounce_timer = cx
 454                                .background_executor()
 455                                .timer(RELOAD_DEBOUNCE_DURATION)
 456                                .fuse();
 457                        }
 458                    }
 459                }
 460
 461                anyhow::Ok(())
 462            }
 463            .map(drop)
 464            .await;
 465        }));
 466
 467        // Watch the installed extensions directory for changes. Whenever changes are
 468        // detected, rebuild the extension index, and load/unload any extensions that
 469        // have been added, removed, or modified.
 470        this.tasks.push(cx.background_spawn({
 471            let fs = this.fs.clone();
 472            let reload_tx = this.reload_tx.clone();
 473            let installed_dir = this.installed_dir.clone();
 474            async move {
 475                let (mut paths, _) = fs.watch(&installed_dir, FS_WATCH_LATENCY).await;
 476                while let Some(events) = paths.next().await {
 477                    for event in events {
 478                        let Ok(event_path) = event.path.strip_prefix(&installed_dir) else {
 479                            continue;
 480                        };
 481
 482                        if let Some(path::Component::Normal(extension_dir_name)) =
 483                            event_path.components().next()
 484                            && let Some(extension_id) = extension_dir_name.to_str()
 485                        {
 486                            reload_tx.unbounded_send(Some(extension_id.into())).ok();
 487                        }
 488                    }
 489                }
 490            }
 491        }));
 492
 493        this
 494    }
 495
 496    pub fn reload(
 497        &mut self,
 498        modified_extension: Option<Arc<str>>,
 499        cx: &mut Context<Self>,
 500    ) -> impl Future<Output = ()> + use<> {
 501        let (tx, rx) = oneshot::channel();
 502        self.reload_complete_senders.push(tx);
 503        self.reload_tx
 504            .unbounded_send(modified_extension)
 505            .expect("reload task exited");
 506        cx.emit(Event::StartedReloading);
 507
 508        async move {
 509            rx.await.ok();
 510        }
 511    }
 512
 513    fn extensions_dir(&self) -> PathBuf {
 514        self.installed_dir.clone()
 515    }
 516
 517    pub fn outstanding_operations(&self) -> &BTreeMap<Arc<str>, ExtensionOperation> {
 518        &self.outstanding_operations
 519    }
 520
 521    pub fn installed_extensions(&self) -> &BTreeMap<Arc<str>, ExtensionIndexEntry> {
 522        &self.extension_index.extensions
 523    }
 524
 525    pub fn dev_extensions(&self) -> impl Iterator<Item = &Arc<ExtensionManifest>> {
 526        self.extension_index
 527            .extensions
 528            .values()
 529            .filter_map(|extension| extension.dev.then_some(&extension.manifest))
 530    }
 531
 532    pub fn extension_manifest_for_id(&self, extension_id: &str) -> Option<&Arc<ExtensionManifest>> {
 533        self.extension_index
 534            .extensions
 535            .get(extension_id)
 536            .map(|extension| &extension.manifest)
 537    }
 538
 539    /// Returns the names of themes provided by extensions.
 540    pub fn extension_themes<'a>(
 541        &'a self,
 542        extension_id: &'a str,
 543    ) -> impl Iterator<Item = &'a Arc<str>> {
 544        self.extension_index
 545            .themes
 546            .iter()
 547            .filter_map(|(name, theme)| theme.extension.as_ref().eq(extension_id).then_some(name))
 548    }
 549
 550    /// Returns the path to the theme file within an extension, if there is an
 551    /// extension that provides the theme.
 552    pub fn path_to_extension_theme(&self, theme_name: &str) -> Option<PathBuf> {
 553        let entry = self.extension_index.themes.get(theme_name)?;
 554
 555        Some(
 556            self.extensions_dir()
 557                .join(entry.extension.as_ref())
 558                .join(&entry.path),
 559        )
 560    }
 561
 562    /// Returns the names of icon themes provided by extensions.
 563    pub fn extension_icon_themes<'a>(
 564        &'a self,
 565        extension_id: &'a str,
 566    ) -> impl Iterator<Item = &'a Arc<str>> {
 567        self.extension_index
 568            .icon_themes
 569            .iter()
 570            .filter_map(|(name, icon_theme)| {
 571                icon_theme
 572                    .extension
 573                    .as_ref()
 574                    .eq(extension_id)
 575                    .then_some(name)
 576            })
 577    }
 578
 579    /// Returns the path to the icon theme file within an extension, if there is
 580    /// an extension that provides the icon theme.
 581    pub fn path_to_extension_icon_theme(
 582        &self,
 583        icon_theme_name: &str,
 584    ) -> Option<(PathBuf, PathBuf)> {
 585        let entry = self.extension_index.icon_themes.get(icon_theme_name)?;
 586
 587        let icon_theme_path = self
 588            .extensions_dir()
 589            .join(entry.extension.as_ref())
 590            .join(&entry.path);
 591        let icons_root_path = self.extensions_dir().join(entry.extension.as_ref());
 592
 593        Some((icon_theme_path, icons_root_path))
 594    }
 595
 596    pub fn fetch_extensions(
 597        &self,
 598        search: Option<&str>,
 599        provides_filter: Option<&BTreeSet<ExtensionProvides>>,
 600        cx: &mut Context<Self>,
 601    ) -> Task<Result<Vec<ExtensionMetadata>>> {
 602        let version = CURRENT_SCHEMA_VERSION.to_string();
 603        let mut query = vec![("max_schema_version", version.as_str())];
 604        if let Some(search) = search {
 605            query.push(("filter", search));
 606        }
 607
 608        let provides_filter = provides_filter.map(|provides_filter| {
 609            provides_filter
 610                .iter()
 611                .map(|provides| provides.to_string())
 612                .collect::<Vec<_>>()
 613                .join(",")
 614        });
 615        if let Some(provides_filter) = provides_filter.as_deref() {
 616            query.push(("provides", provides_filter));
 617        }
 618
 619        self.fetch_extensions_from_api("/extensions", &query, cx)
 620    }
 621
 622    pub fn fetch_extensions_with_update_available(
 623        &mut self,
 624        cx: &mut Context<Self>,
 625    ) -> Task<Result<Vec<ExtensionMetadata>>> {
 626        let schema_versions = schema_version_range();
 627        let wasm_api_versions = wasm_api_version_range(ReleaseChannel::global(cx));
 628        let extension_settings = ExtensionSettings::get_global(cx);
 629        let extension_ids = self
 630            .extension_index
 631            .extensions
 632            .iter()
 633            .filter(|(id, entry)| !entry.dev && extension_settings.should_auto_update(id))
 634            .map(|(id, _)| id.as_ref())
 635            .collect::<Vec<_>>()
 636            .join(",");
 637        let task = self.fetch_extensions_from_api(
 638            "/extensions/updates",
 639            &[
 640                ("min_schema_version", &schema_versions.start().to_string()),
 641                ("max_schema_version", &schema_versions.end().to_string()),
 642                (
 643                    "min_wasm_api_version",
 644                    &wasm_api_versions.start().to_string(),
 645                ),
 646                ("max_wasm_api_version", &wasm_api_versions.end().to_string()),
 647                ("ids", &extension_ids),
 648            ],
 649            cx,
 650        );
 651        cx.spawn(async move |this, cx| {
 652            let extensions = task.await?;
 653            this.update(cx, |this, _cx| {
 654                extensions
 655                    .into_iter()
 656                    .filter(|extension| {
 657                        this.extension_index
 658                            .extensions
 659                            .get(&extension.id)
 660                            .is_none_or(|installed_extension| {
 661                                installed_extension.manifest.version != extension.manifest.version
 662                            })
 663                    })
 664                    .collect()
 665            })
 666        })
 667    }
 668
 669    pub fn fetch_extension_versions(
 670        &self,
 671        extension_id: &str,
 672        cx: &mut Context<Self>,
 673    ) -> Task<Result<Vec<ExtensionMetadata>>> {
 674        self.fetch_extensions_from_api(&format!("/extensions/{extension_id}"), &[], cx)
 675    }
 676
 677    /// Installs any extensions that should be included with Zed by default.
 678    ///
 679    /// This can be used to make certain functionality provided by extensions
 680    /// available out-of-the-box.
 681    pub fn auto_install_extensions(&mut self, cx: &mut Context<Self>) {
 682        if cfg!(test) {
 683            return;
 684        }
 685
 686        let extension_settings = ExtensionSettings::get_global(cx);
 687
 688        let extensions_to_install = extension_settings
 689            .auto_install_extensions
 690            .keys()
 691            .filter(|extension_id| extension_settings.should_auto_install(extension_id))
 692            .filter(|extension_id| {
 693                let is_already_installed = self
 694                    .extension_index
 695                    .extensions
 696                    .contains_key(extension_id.as_ref());
 697                let dominated = SUPPRESSED_EXTENSIONS.contains(&extension_id.as_ref());
 698                !is_already_installed && !dominated
 699            })
 700            .cloned()
 701            .collect::<Vec<_>>();
 702
 703        cx.spawn(async move |this, cx| {
 704            for extension_id in extensions_to_install {
 705                // When enabled, this checks if an extension exists locally in the repo's extensions/
 706                // directory and installs it as a dev extension instead of fetching from the registry.
 707                // This is useful for testing auto-installed extensions before they've been published.
 708                // Set to `true` only during local development/testing of new auto-install extensions.
 709                #[cfg(debug_assertions)]
 710                const DEBUG_ALLOW_UNPUBLISHED_AUTO_EXTENSIONS: bool = false;
 711
 712                #[cfg(debug_assertions)]
 713                if DEBUG_ALLOW_UNPUBLISHED_AUTO_EXTENSIONS {
 714                    let local_extension_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
 715                        .parent()
 716                        .unwrap()
 717                        .parent()
 718                        .unwrap()
 719                        .join("extensions")
 720                        .join(extension_id.as_ref());
 721
 722                    if local_extension_path.exists() {
 723                        // Force-remove existing extension directory if it exists and isn't a symlink
 724                        // This handles the case where the extension was previously installed from the registry
 725                        if let Some(installed_dir) = this
 726                            .update(cx, |this, _cx| this.installed_dir.clone())
 727                            .ok()
 728                        {
 729                            let existing_path = installed_dir.join(extension_id.as_ref());
 730                            if existing_path.exists() {
 731                                let metadata = std::fs::symlink_metadata(&existing_path);
 732                                let is_symlink = metadata.map(|m| m.is_symlink()).unwrap_or(false);
 733                                if !is_symlink {
 734                                    if let Err(e) = std::fs::remove_dir_all(&existing_path) {
 735                                        log::error!(
 736                                            "Failed to remove existing extension directory {:?}: {}",
 737                                            existing_path,
 738                                            e
 739                                        );
 740                                    }
 741                                }
 742                            }
 743                        }
 744
 745                        if let Some(task) = this
 746                            .update(cx, |this, cx| {
 747                                this.install_dev_extension(local_extension_path, cx)
 748                            })
 749                            .ok()
 750                        {
 751                            task.await.log_err();
 752                        }
 753                        continue;
 754                    }
 755                }
 756
 757                this.update(cx, |this, cx| {
 758                    this.install_latest_extension(extension_id.clone(), cx);
 759                })
 760                .ok();
 761            }
 762        })
 763        .detach();
 764    }
 765
 766    pub fn check_for_updates(&mut self, cx: &mut Context<Self>) {
 767        let task = self.fetch_extensions_with_update_available(cx);
 768        cx.spawn(async move |this, cx| Self::upgrade_extensions(this, task.await?, cx).await)
 769            .detach();
 770    }
 771
 772    async fn upgrade_extensions(
 773        this: WeakEntity<Self>,
 774        extensions: Vec<ExtensionMetadata>,
 775        cx: &mut AsyncApp,
 776    ) -> Result<()> {
 777        for extension in extensions {
 778            let task = this.update(cx, |this, cx| {
 779                if let Some(installed_extension) =
 780                    this.extension_index.extensions.get(&extension.id)
 781                {
 782                    let installed_version =
 783                        Version::from_str(&installed_extension.manifest.version).ok()?;
 784                    let latest_version = Version::from_str(&extension.manifest.version).ok()?;
 785
 786                    if installed_version >= latest_version {
 787                        return None;
 788                    }
 789                }
 790
 791                Some(this.upgrade_extension(extension.id, extension.manifest.version, cx))
 792            })?;
 793
 794            if let Some(task) = task {
 795                task.await.log_err();
 796            }
 797        }
 798        anyhow::Ok(())
 799    }
 800
 801    fn fetch_extensions_from_api(
 802        &self,
 803        path: &str,
 804        query: &[(&str, &str)],
 805        cx: &mut Context<ExtensionStore>,
 806    ) -> Task<Result<Vec<ExtensionMetadata>>> {
 807        let url = self.http_client.build_zed_api_url(path, query);
 808        let http_client = self.http_client.clone();
 809        cx.spawn(async move |_, _| {
 810            let mut response = http_client
 811                .get(url?.as_ref(), AsyncBody::empty(), true)
 812                .await?;
 813
 814            let mut body = Vec::new();
 815            response
 816                .body_mut()
 817                .read_to_end(&mut body)
 818                .await
 819                .context("error reading extensions")?;
 820
 821            if response.status().is_client_error() {
 822                let text = String::from_utf8_lossy(body.as_slice());
 823                bail!(
 824                    "status error {}, response: {text:?}",
 825                    response.status().as_u16()
 826                );
 827            }
 828
 829            let mut response: GetExtensionsResponse = serde_json::from_slice(&body)?;
 830
 831            response
 832                .data
 833                .retain(|extension| !SUPPRESSED_EXTENSIONS.contains(&extension.id.as_ref()));
 834
 835            Ok(response.data)
 836        })
 837    }
 838
 839    pub fn install_extension(
 840        &mut self,
 841        extension_id: Arc<str>,
 842        version: Arc<str>,
 843        cx: &mut Context<Self>,
 844    ) {
 845        self.install_or_upgrade_extension(extension_id, version, ExtensionOperation::Install, cx)
 846            .detach_and_log_err(cx);
 847    }
 848
 849    fn install_or_upgrade_extension_at_endpoint(
 850        &mut self,
 851        extension_id: Arc<str>,
 852        url: Url,
 853        operation: ExtensionOperation,
 854        cx: &mut Context<Self>,
 855    ) -> Task<Result<()>> {
 856        let extension_dir = self.installed_dir.join(extension_id.as_ref());
 857        let http_client = self.http_client.clone();
 858        let fs = self.fs.clone();
 859
 860        match self.outstanding_operations.entry(extension_id.clone()) {
 861            btree_map::Entry::Occupied(_) => return Task::ready(Ok(())),
 862            btree_map::Entry::Vacant(e) => e.insert(operation),
 863        };
 864        cx.notify();
 865
 866        cx.spawn(async move |this, cx| {
 867            let _finish = cx.on_drop(&this, {
 868                let extension_id = extension_id.clone();
 869                move |this, cx| {
 870                    this.outstanding_operations.remove(extension_id.as_ref());
 871                    cx.notify();
 872                }
 873            });
 874
 875            let mut response = http_client
 876                .get(url.as_ref(), Default::default(), true)
 877                .await
 878                .context("downloading extension")?;
 879
 880            fs.remove_dir(
 881                &extension_dir,
 882                RemoveOptions {
 883                    recursive: true,
 884                    ignore_if_not_exists: true,
 885                },
 886            )
 887            .await?;
 888
 889            let content_length = response
 890                .headers()
 891                .get(http_client::http::header::CONTENT_LENGTH)
 892                .and_then(|value| value.to_str().ok()?.parse::<usize>().ok());
 893
 894            let mut body = BufReader::new(response.body_mut());
 895            let mut tar_gz_bytes = Vec::new();
 896            body.read_to_end(&mut tar_gz_bytes).await?;
 897
 898            if let Some(content_length) = content_length {
 899                let actual_len = tar_gz_bytes.len();
 900                if content_length != actual_len {
 901                    bail!(concat!(
 902                        "downloaded extension size {actual_len} ",
 903                        "does not match content length {content_length}"
 904                    ));
 905                }
 906            }
 907            let decompressed_bytes = GzipDecoder::new(BufReader::new(tar_gz_bytes.as_slice()));
 908            let archive = Archive::new(decompressed_bytes);
 909            archive.unpack(extension_dir).await?;
 910            this.update(cx, |this, cx| this.reload(Some(extension_id.clone()), cx))?
 911                .await;
 912
 913            if let ExtensionOperation::Install = operation {
 914                this.update(cx, |this, cx| {
 915                    // Check for legacy LLM provider migration
 916                    if let Some(manifest) = this.extension_manifest_for_id(&extension_id) {
 917                        migrate_legacy_llm_provider_env_var(&manifest, cx);
 918                    }
 919
 920                    cx.emit(Event::ExtensionInstalled(extension_id.clone()));
 921                    if let Some(events) = ExtensionEvents::try_global(cx)
 922                        && let Some(manifest) = this.extension_manifest_for_id(&extension_id)
 923                    {
 924                        events.update(cx, |this, cx| {
 925                            this.emit(extension::Event::ExtensionInstalled(manifest.clone()), cx)
 926                        });
 927                    }
 928
 929                    // Run extension-specific migrations
 930                    copilot_migration::migrate_copilot_credentials_if_needed(&extension_id, cx);
 931                    anthropic_migration::migrate_anthropic_credentials_if_needed(&extension_id, cx);
 932                    google_ai_migration::migrate_google_ai_credentials_if_needed(&extension_id, cx);
 933                    openai_migration::migrate_openai_credentials_if_needed(&extension_id, cx);
 934                    open_router_migration::migrate_open_router_credentials_if_needed(
 935                        &extension_id,
 936                        cx,
 937                    );
 938                })
 939                .ok();
 940            }
 941
 942            anyhow::Ok(())
 943        })
 944    }
 945
 946    pub fn install_latest_extension(&mut self, extension_id: Arc<str>, cx: &mut Context<Self>) {
 947        let schema_versions = schema_version_range();
 948        let wasm_api_versions = wasm_api_version_range(ReleaseChannel::global(cx));
 949
 950        let Some(url) = self
 951            .http_client
 952            .build_zed_api_url(
 953                &format!("/extensions/{extension_id}/download"),
 954                &[
 955                    ("min_schema_version", &schema_versions.start().to_string()),
 956                    ("max_schema_version", &schema_versions.end().to_string()),
 957                    (
 958                        "min_wasm_api_version",
 959                        &wasm_api_versions.start().to_string(),
 960                    ),
 961                    ("max_wasm_api_version", &wasm_api_versions.end().to_string()),
 962                ],
 963            )
 964            .log_err()
 965        else {
 966            return;
 967        };
 968
 969        self.install_or_upgrade_extension_at_endpoint(
 970            extension_id,
 971            url,
 972            ExtensionOperation::Install,
 973            cx,
 974        )
 975        .detach_and_log_err(cx);
 976    }
 977
 978    pub fn upgrade_extension(
 979        &mut self,
 980        extension_id: Arc<str>,
 981        version: Arc<str>,
 982        cx: &mut Context<Self>,
 983    ) -> Task<Result<()>> {
 984        self.install_or_upgrade_extension(extension_id, version, ExtensionOperation::Upgrade, cx)
 985    }
 986
 987    fn install_or_upgrade_extension(
 988        &mut self,
 989        extension_id: Arc<str>,
 990        version: Arc<str>,
 991        operation: ExtensionOperation,
 992        cx: &mut Context<Self>,
 993    ) -> Task<Result<()>> {
 994        let Some(url) = self
 995            .http_client
 996            .build_zed_api_url(
 997                &format!("/extensions/{extension_id}/{version}/download"),
 998                &[],
 999            )
1000            .log_err()
1001        else {
1002            return Task::ready(Ok(()));
1003        };
1004
1005        self.install_or_upgrade_extension_at_endpoint(extension_id, url, operation, cx)
1006    }
1007
1008    pub fn uninstall_extension(
1009        &mut self,
1010        extension_id: Arc<str>,
1011        cx: &mut Context<Self>,
1012    ) -> Task<Result<()>> {
1013        let extension_dir = self.installed_dir.join(extension_id.as_ref());
1014        let work_dir = self.wasm_host.work_dir.join(extension_id.as_ref());
1015        let fs = self.fs.clone();
1016
1017        let extension_manifest = self.extension_manifest_for_id(&extension_id).cloned();
1018
1019        match self.outstanding_operations.entry(extension_id.clone()) {
1020            btree_map::Entry::Occupied(_) => return Task::ready(Ok(())),
1021            btree_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Remove),
1022        };
1023
1024        cx.spawn(async move |extension_store, cx| {
1025            let _finish = cx.on_drop(&extension_store, {
1026                let extension_id = extension_id.clone();
1027                move |this, cx| {
1028                    this.outstanding_operations.remove(extension_id.as_ref());
1029                    cx.notify();
1030                }
1031            });
1032
1033            fs.remove_dir(
1034                &extension_dir,
1035                RemoveOptions {
1036                    recursive: true,
1037                    ignore_if_not_exists: true,
1038                },
1039            )
1040            .await
1041            .with_context(|| format!("Removing extension dir {extension_dir:?}"))?;
1042
1043            extension_store
1044                .update(cx, |extension_store, cx| extension_store.reload(None, cx))?
1045                .await;
1046
1047            // There's a race between wasm extension fully stopping and the directory removal.
1048            // On Windows, it's impossible to remove a directory that has a process running in it.
1049            for i in 0..3 {
1050                cx.background_executor()
1051                    .timer(Duration::from_millis(i * 100))
1052                    .await;
1053                let removal_result = fs
1054                    .remove_dir(
1055                        &work_dir,
1056                        RemoveOptions {
1057                            recursive: true,
1058                            ignore_if_not_exists: true,
1059                        },
1060                    )
1061                    .await;
1062                match removal_result {
1063                    Ok(()) => break,
1064                    Err(e) => {
1065                        if i == 2 {
1066                            log::error!("Failed to remove extension work dir {work_dir:?} : {e}");
1067                        }
1068                    }
1069                }
1070            }
1071
1072            extension_store.update(cx, |_, cx| {
1073                cx.emit(Event::ExtensionUninstalled(extension_id.clone()));
1074                if let Some(events) = ExtensionEvents::try_global(cx)
1075                    && let Some(manifest) = extension_manifest
1076                {
1077                    events.update(cx, |this, cx| {
1078                        this.emit(extension::Event::ExtensionUninstalled(manifest.clone()), cx)
1079                    });
1080                }
1081            })?;
1082
1083            anyhow::Ok(())
1084        })
1085    }
1086
1087    pub fn install_dev_extension(
1088        &mut self,
1089        extension_source_path: PathBuf,
1090        cx: &mut Context<Self>,
1091    ) -> Task<Result<()>> {
1092        let extensions_dir = self.extensions_dir();
1093        let fs = self.fs.clone();
1094        let builder = self.builder.clone();
1095
1096        cx.spawn(async move |this, cx| {
1097            let mut extension_manifest =
1098                ExtensionManifest::load(fs.clone(), &extension_source_path).await?;
1099            let extension_id = extension_manifest.id.clone();
1100
1101            if let Some(uninstall_task) = this
1102                .update(cx, |this, cx| {
1103                    this.extension_index
1104                        .extensions
1105                        .get(extension_id.as_ref())
1106                        .is_some_and(|index_entry| !index_entry.dev)
1107                        .then(|| this.uninstall_extension(extension_id.clone(), cx))
1108                })
1109                .ok()
1110                .flatten()
1111            {
1112                uninstall_task.await.log_err();
1113            }
1114
1115            if !this.update(cx, |this, cx| {
1116                match this.outstanding_operations.entry(extension_id.clone()) {
1117                    btree_map::Entry::Occupied(_) => return false,
1118                    btree_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Install),
1119                };
1120                cx.notify();
1121                true
1122            })? {
1123                return Ok(());
1124            }
1125
1126            let _finish = cx.on_drop(&this, {
1127                let extension_id = extension_id.clone();
1128                move |this, cx| {
1129                    this.outstanding_operations.remove(extension_id.as_ref());
1130                    cx.notify();
1131                }
1132            });
1133
1134            cx.background_spawn({
1135                let extension_source_path = extension_source_path.clone();
1136                let fs = fs.clone();
1137                async move {
1138                    builder
1139                        .compile_extension(
1140                            &extension_source_path,
1141                            &mut extension_manifest,
1142                            CompileExtensionOptions { release: false },
1143                            fs,
1144                        )
1145                        .await
1146                }
1147            })
1148            .await
1149            .inspect_err(|error| {
1150                util::log_err(error);
1151            })?;
1152
1153            let output_path = &extensions_dir.join(extension_id.as_ref());
1154            if let Some(metadata) = fs.metadata(output_path).await? {
1155                if metadata.is_symlink {
1156                    fs.remove_file(
1157                        output_path,
1158                        RemoveOptions {
1159                            recursive: false,
1160                            ignore_if_not_exists: true,
1161                        },
1162                    )
1163                    .await?;
1164                } else {
1165                    bail!("extension {extension_id} is still installed");
1166                }
1167            }
1168
1169            fs.create_symlink(output_path, extension_source_path)
1170                .await?;
1171
1172            this.update(cx, |this, cx| this.reload(None, cx))?.await;
1173            this.update(cx, |this, cx| {
1174                // Run migration for legacy LLM provider env vars
1175                if let Some(manifest) = this.extension_manifest_for_id(&extension_id) {
1176                    migrate_legacy_llm_provider_env_var(&manifest, cx);
1177                }
1178
1179                cx.emit(Event::ExtensionInstalled(extension_id.clone()));
1180                if let Some(events) = ExtensionEvents::try_global(cx)
1181                    && let Some(manifest) = this.extension_manifest_for_id(&extension_id)
1182                {
1183                    events.update(cx, |this, cx| {
1184                        this.emit(extension::Event::ExtensionInstalled(manifest.clone()), cx)
1185                    });
1186                }
1187            })?;
1188
1189            Ok(())
1190        })
1191    }
1192
1193    pub fn rebuild_dev_extension(&mut self, extension_id: Arc<str>, cx: &mut Context<Self>) {
1194        let path = self.installed_dir.join(extension_id.as_ref());
1195        let builder = self.builder.clone();
1196        let fs = self.fs.clone();
1197
1198        match self.outstanding_operations.entry(extension_id.clone()) {
1199            btree_map::Entry::Occupied(_) => return,
1200            btree_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Upgrade),
1201        };
1202
1203        cx.notify();
1204        let compile = cx.background_spawn(async move {
1205            let mut manifest = ExtensionManifest::load(fs.clone(), &path).await?;
1206            builder
1207                .compile_extension(
1208                    &path,
1209                    &mut manifest,
1210                    CompileExtensionOptions { release: true },
1211                    fs,
1212                )
1213                .await
1214        });
1215
1216        cx.spawn(async move |this, cx| {
1217            let result = compile.await;
1218
1219            this.update(cx, |this, cx| {
1220                this.outstanding_operations.remove(&extension_id);
1221                cx.notify();
1222            })?;
1223
1224            if result.is_ok() {
1225                this.update(cx, |this, cx| this.reload(Some(extension_id), cx))?
1226                    .await;
1227            }
1228
1229            result
1230        })
1231        .detach_and_log_err(cx)
1232    }
1233
1234    /// Updates the set of installed extensions.
1235    ///
1236    /// First, this unloads any themes, languages, or grammars that are
1237    /// no longer in the manifest, or whose files have changed on disk.
1238    /// Then it loads any themes, languages, or grammars that are newly
1239    /// added to the manifest, or whose files have changed on disk.
1240    fn extensions_updated(
1241        &mut self,
1242        mut new_index: ExtensionIndex,
1243        cx: &mut Context<Self>,
1244    ) -> Task<()> {
1245        let old_index = &self.extension_index;
1246
1247        new_index
1248            .extensions
1249            .retain(|extension_id, _| !SUPPRESSED_EXTENSIONS.contains(&extension_id.as_ref()));
1250
1251        // Determine which extensions need to be loaded and unloaded, based
1252        // on the changes to the manifest and the extensions that we know have been
1253        // modified.
1254        let mut extensions_to_unload = Vec::default();
1255        let mut extensions_to_load = Vec::default();
1256        {
1257            let mut old_keys = old_index.extensions.iter().peekable();
1258            let mut new_keys = new_index.extensions.iter().peekable();
1259            loop {
1260                match (old_keys.peek(), new_keys.peek()) {
1261                    (None, None) => break,
1262                    (None, Some(_)) => {
1263                        extensions_to_load.push(new_keys.next().unwrap().0.clone());
1264                    }
1265                    (Some(_), None) => {
1266                        extensions_to_unload.push(old_keys.next().unwrap().0.clone());
1267                    }
1268                    (Some((old_key, _)), Some((new_key, _))) => match old_key.cmp(new_key) {
1269                        Ordering::Equal => {
1270                            let (old_key, old_value) = old_keys.next().unwrap();
1271                            let (new_key, new_value) = new_keys.next().unwrap();
1272                            if old_value != new_value || self.modified_extensions.contains(old_key)
1273                            {
1274                                extensions_to_unload.push(old_key.clone());
1275                                extensions_to_load.push(new_key.clone());
1276                            }
1277                        }
1278                        Ordering::Less => {
1279                            extensions_to_unload.push(old_keys.next().unwrap().0.clone());
1280                        }
1281                        Ordering::Greater => {
1282                            extensions_to_load.push(new_keys.next().unwrap().0.clone());
1283                        }
1284                    },
1285                }
1286            }
1287            self.modified_extensions.clear();
1288        }
1289
1290        if extensions_to_load.is_empty() && extensions_to_unload.is_empty() {
1291            self.reload_complete_senders.clear();
1292            return Task::ready(());
1293        }
1294
1295        let extension_ids = extensions_to_load
1296            .iter()
1297            .filter_map(|id| {
1298                Some((
1299                    id.clone(),
1300                    new_index.extensions.get(id)?.manifest.version.clone(),
1301                ))
1302            })
1303            .collect::<Vec<_>>();
1304
1305        telemetry::event!("Extensions Loaded", id_and_versions = extension_ids);
1306
1307        let themes_to_remove = old_index
1308            .themes
1309            .iter()
1310            .filter_map(|(name, entry)| {
1311                if extensions_to_unload.contains(&entry.extension) {
1312                    Some(name.clone().into())
1313                } else {
1314                    None
1315                }
1316            })
1317            .collect::<Vec<_>>();
1318        let icon_themes_to_remove = old_index
1319            .icon_themes
1320            .iter()
1321            .filter_map(|(name, entry)| {
1322                if extensions_to_unload.contains(&entry.extension) {
1323                    Some(name.clone().into())
1324                } else {
1325                    None
1326                }
1327            })
1328            .collect::<Vec<_>>();
1329        let languages_to_remove = old_index
1330            .languages
1331            .iter()
1332            .filter_map(|(name, entry)| {
1333                if extensions_to_unload.contains(&entry.extension) {
1334                    Some(name.clone())
1335                } else {
1336                    None
1337                }
1338            })
1339            .collect::<Vec<_>>();
1340        let mut grammars_to_remove = Vec::new();
1341        let mut server_removal_tasks = Vec::with_capacity(extensions_to_unload.len());
1342        for extension_id in &extensions_to_unload {
1343            let Some(extension) = old_index.extensions.get(extension_id) else {
1344                continue;
1345            };
1346            grammars_to_remove.extend(extension.manifest.grammars.keys().cloned());
1347            for (language_server_name, config) in &extension.manifest.language_servers {
1348                for language in config.languages() {
1349                    server_removal_tasks.push(self.proxy.remove_language_server(
1350                        &language,
1351                        language_server_name,
1352                        cx,
1353                    ));
1354                }
1355            }
1356
1357            for server_id in extension.manifest.context_servers.keys() {
1358                self.proxy.unregister_context_server(server_id.clone(), cx);
1359            }
1360            for adapter in extension.manifest.debug_adapters.keys() {
1361                self.proxy.unregister_debug_adapter(adapter.clone());
1362            }
1363            for locator in extension.manifest.debug_locators.keys() {
1364                self.proxy.unregister_debug_locator(locator.clone());
1365            }
1366            for command_name in extension.manifest.slash_commands.keys() {
1367                self.proxy.unregister_slash_command(command_name.clone());
1368            }
1369            for provider_id in extension.manifest.language_model_providers.keys() {
1370                let full_provider_id: Arc<str> = format!("{}:{}", extension_id, provider_id).into();
1371                self.proxy
1372                    .unregister_language_model_provider(full_provider_id, cx);
1373            }
1374        }
1375
1376        self.wasm_extensions
1377            .retain(|(extension, _)| !extensions_to_unload.contains(&extension.id));
1378        self.proxy.remove_user_themes(themes_to_remove);
1379        self.proxy.remove_icon_themes(icon_themes_to_remove);
1380        self.proxy
1381            .remove_languages(&languages_to_remove, &grammars_to_remove);
1382
1383        let mut grammars_to_add = Vec::new();
1384        let mut themes_to_add = Vec::new();
1385        let mut icon_themes_to_add = Vec::new();
1386        let mut snippets_to_add = Vec::new();
1387        for extension_id in &extensions_to_load {
1388            let Some(extension) = new_index.extensions.get(extension_id) else {
1389                continue;
1390            };
1391
1392            grammars_to_add.extend(extension.manifest.grammars.keys().map(|grammar_name| {
1393                let mut grammar_path = self.installed_dir.clone();
1394                grammar_path.extend([extension_id.as_ref(), "grammars"]);
1395                grammar_path.push(grammar_name.as_ref());
1396                grammar_path.set_extension("wasm");
1397                (grammar_name.clone(), grammar_path)
1398            }));
1399            themes_to_add.extend(extension.manifest.themes.iter().map(|theme_path| {
1400                let mut path = self.installed_dir.clone();
1401                path.extend([Path::new(extension_id.as_ref()), theme_path.as_path()]);
1402                path
1403            }));
1404            icon_themes_to_add.extend(extension.manifest.icon_themes.iter().map(
1405                |icon_theme_path| {
1406                    let mut path = self.installed_dir.clone();
1407                    path.extend([Path::new(extension_id.as_ref()), icon_theme_path.as_path()]);
1408
1409                    let mut icons_root_path = self.installed_dir.clone();
1410                    icons_root_path.extend([Path::new(extension_id.as_ref())]);
1411
1412                    (path, icons_root_path)
1413                },
1414            ));
1415            snippets_to_add.extend(extension.manifest.snippets.iter().map(|snippets_path| {
1416                let mut path = self.installed_dir.clone();
1417                path.extend([Path::new(extension_id.as_ref()), snippets_path.as_path()]);
1418                path
1419            }));
1420        }
1421
1422        self.proxy.register_grammars(grammars_to_add);
1423        let languages_to_add = new_index
1424            .languages
1425            .iter()
1426            .filter(|(_, entry)| extensions_to_load.contains(&entry.extension))
1427            .collect::<Vec<_>>();
1428        for (language_name, language) in languages_to_add {
1429            let mut language_path = self.installed_dir.clone();
1430            language_path.extend([
1431                Path::new(language.extension.as_ref()),
1432                language.path.as_path(),
1433            ]);
1434            self.proxy.register_language(
1435                language_name.clone(),
1436                language.grammar.clone(),
1437                language.matcher.clone(),
1438                language.hidden,
1439                Arc::new(move || {
1440                    let config = std::fs::read_to_string(language_path.join("config.toml"))?;
1441                    let config: LanguageConfig = ::toml::from_str(&config)?;
1442                    let queries = load_plugin_queries(&language_path);
1443                    let context_provider =
1444                        std::fs::read_to_string(language_path.join("tasks.json"))
1445                            .ok()
1446                            .and_then(|contents| {
1447                                let definitions =
1448                                    serde_json_lenient::from_str(&contents).log_err()?;
1449                                Some(Arc::new(ContextProviderWithTasks::new(definitions)) as Arc<_>)
1450                            });
1451
1452                    Ok(LoadedLanguage {
1453                        config,
1454                        queries,
1455                        context_provider,
1456                        toolchain_provider: None,
1457                        manifest_name: None,
1458                    })
1459                }),
1460            );
1461        }
1462
1463        let fs = self.fs.clone();
1464        let wasm_host = self.wasm_host.clone();
1465        let root_dir = self.installed_dir.clone();
1466        let proxy = self.proxy.clone();
1467        let extension_entries = extensions_to_load
1468            .iter()
1469            .filter_map(|name| new_index.extensions.get(name).cloned())
1470            .collect::<Vec<_>>();
1471        self.extension_index = new_index;
1472        cx.notify();
1473        cx.emit(Event::ExtensionsUpdated);
1474
1475        cx.spawn(async move |this, cx| {
1476            cx.background_spawn({
1477                let fs = fs.clone();
1478                async move {
1479                    let _ = join_all(server_removal_tasks).await;
1480                    for theme_path in themes_to_add {
1481                        proxy
1482                            .load_user_theme(theme_path, fs.clone())
1483                            .await
1484                            .log_err();
1485                    }
1486
1487                    for (icon_theme_path, icons_root_path) in icon_themes_to_add {
1488                        proxy
1489                            .load_icon_theme(icon_theme_path, icons_root_path, fs.clone())
1490                            .await
1491                            .log_err();
1492                    }
1493
1494                    for snippets_path in &snippets_to_add {
1495                        match fs
1496                            .load(snippets_path)
1497                            .await
1498                            .with_context(|| format!("Loading snippets from {snippets_path:?}"))
1499                        {
1500                            Ok(snippets_contents) => {
1501                                proxy
1502                                    .register_snippet(snippets_path, &snippets_contents)
1503                                    .log_err();
1504                            }
1505                            Err(e) => log::error!("Cannot load snippets: {e:#}"),
1506                        }
1507                    }
1508                }
1509            })
1510            .await;
1511
1512            let mut wasm_extensions: Vec<(
1513                Arc<ExtensionManifest>,
1514                WasmExtension,
1515                Vec<LlmProviderWithModels>,
1516            )> = Vec::new();
1517            for extension in extension_entries {
1518                if extension.manifest.lib.kind.is_none() {
1519                    continue;
1520                };
1521
1522                let extension_path = root_dir.join(extension.manifest.id.as_ref());
1523                let wasm_extension = WasmExtension::load(
1524                    &extension_path,
1525                    &extension.manifest,
1526                    wasm_host.clone(),
1527                    cx,
1528                )
1529                .await
1530                .with_context(|| format!("Loading extension from {extension_path:?}"));
1531
1532                match wasm_extension {
1533                    Ok(wasm_extension) => {
1534                        // Query for LLM providers if the manifest declares any
1535                        let mut llm_providers_with_models = Vec::new();
1536                        if !extension.manifest.language_model_providers.is_empty() {
1537                            let providers_result = wasm_extension
1538                                .call(|ext, store| {
1539                                    async move { ext.call_llm_providers(store).await }.boxed()
1540                                })
1541                                .await;
1542
1543                            if let Ok(Ok(providers)) = providers_result {
1544                                for provider_info in providers {
1545                                    let models_result = wasm_extension
1546                                        .call({
1547                                            let provider_id = provider_info.id.clone();
1548                                            |ext, store| {
1549                                                async move {
1550                                                    ext.call_llm_provider_models(store, &provider_id)
1551                                                        .await
1552                                                }
1553                                                .boxed()
1554                                            }
1555                                        })
1556                                        .await;
1557
1558                                    let models: Vec<LlmModelInfo> = match models_result {
1559                                        Ok(Ok(Ok(models))) => models,
1560                                        Ok(Ok(Err(e))) => {
1561                                            log::error!(
1562                                                "Failed to get models for LLM provider {} in extension {}: {}",
1563                                                provider_info.id,
1564                                                extension.manifest.id,
1565                                                e
1566                                            );
1567                                            Vec::new()
1568                                        }
1569                                        Ok(Err(e)) => {
1570                                            log::error!(
1571                                                "Wasm error calling llm_provider_models for {} in extension {}: {:?}",
1572                                                provider_info.id,
1573                                                extension.manifest.id,
1574                                                e
1575                                            );
1576                                            Vec::new()
1577                                        }
1578                                        Err(e) => {
1579                                            log::error!(
1580                                                "Extension call failed for llm_provider_models {} in extension {}: {:?}",
1581                                                provider_info.id,
1582                                                extension.manifest.id,
1583                                                e
1584                                            );
1585                                            Vec::new()
1586                                        }
1587                                    };
1588
1589                                    // Query initial authentication state
1590                                    let is_authenticated = wasm_extension
1591                                        .call({
1592                                            let provider_id = provider_info.id.clone();
1593                                            |ext, store| {
1594                                                async move {
1595                                                    ext.call_llm_provider_is_authenticated(
1596                                                        store,
1597                                                        &provider_id,
1598                                                    )
1599                                                    .await
1600                                                }
1601                                                .boxed()
1602                                            }
1603                                        })
1604                                        .await
1605                                        .unwrap_or(Ok(false))
1606                                        .unwrap_or(false);
1607
1608                                    // Resolve icon path if provided
1609                                    let icon_path = provider_info.icon.as_ref().map(|icon| {
1610                                        let icon_file_path = extension_path.join(icon);
1611                                        // Canonicalize to resolve symlinks (dev extensions are symlinked)
1612                                        let absolute_icon_path = icon_file_path
1613                                            .canonicalize()
1614                                            .unwrap_or(icon_file_path)
1615                                            .to_string_lossy()
1616                                            .to_string();
1617                                        SharedString::from(absolute_icon_path)
1618                                    });
1619
1620                                    let provider_id_arc: Arc<str> =
1621                                        provider_info.id.as_str().into();
1622                                    let auth_config = extension
1623                                        .manifest
1624                                        .language_model_providers
1625                                        .get(&provider_id_arc)
1626                                        .and_then(|entry| entry.auth.clone());
1627
1628                                    llm_providers_with_models.push(LlmProviderWithModels {
1629                                        provider_info,
1630                                        models,
1631                                        is_authenticated,
1632                                        icon_path,
1633                                        auth_config,
1634                                    });
1635                                }
1636                            } else {
1637                                log::error!(
1638                                    "Failed to get LLM providers from extension {}: {:?}",
1639                                    extension.manifest.id,
1640                                    providers_result
1641                                );
1642                            }
1643                        }
1644
1645                        wasm_extensions.push((
1646                            extension.manifest.clone(),
1647                            wasm_extension,
1648                            llm_providers_with_models,
1649                        ))
1650                    }
1651                    Err(e) => {
1652                        log::error!(
1653                            "Failed to load extension: {}, {:#}",
1654                            extension.manifest.id,
1655                            e
1656                        );
1657                        this.update(cx, |_, cx| {
1658                            cx.emit(Event::ExtensionFailedToLoad(extension.manifest.id.clone()))
1659                        })
1660                        .ok();
1661                    }
1662                }
1663            }
1664
1665            this.update(cx, |this, cx| {
1666                this.reload_complete_senders.clear();
1667
1668                for (manifest, wasm_extension, llm_providers_with_models) in &wasm_extensions {
1669                    let extension = Arc::new(wasm_extension.clone());
1670
1671                    for (language_server_id, language_server_config) in &manifest.language_servers {
1672                        for language in language_server_config.languages() {
1673                            this.proxy.register_language_server(
1674                                extension.clone(),
1675                                language_server_id.clone(),
1676                                language.clone(),
1677                            );
1678                        }
1679                    }
1680
1681                    for (slash_command_name, slash_command) in &manifest.slash_commands {
1682                        this.proxy.register_slash_command(
1683                            extension.clone(),
1684                            extension::SlashCommand {
1685                                name: slash_command_name.to_string(),
1686                                description: slash_command.description.to_string(),
1687                                // We don't currently expose this as a configurable option, as it currently drives
1688                                // the `menu_text` on the `SlashCommand` trait, which is not used for slash commands
1689                                // defined in extensions, as they are not able to be added to the menu.
1690                                tooltip_text: String::new(),
1691                                requires_argument: slash_command.requires_argument,
1692                            },
1693                        );
1694                    }
1695
1696                    for id in manifest.context_servers.keys() {
1697                        this.proxy
1698                            .register_context_server(extension.clone(), id.clone(), cx);
1699                    }
1700
1701                    for (debug_adapter, meta) in &manifest.debug_adapters {
1702                        let mut path = root_dir.clone();
1703                        path.push(Path::new(manifest.id.as_ref()));
1704                        if let Some(schema_path) = &meta.schema_path {
1705                            path.push(schema_path);
1706                        } else {
1707                            path.push("debug_adapter_schemas");
1708                            path.push(Path::new(debug_adapter.as_ref()).with_extension("json"));
1709                        }
1710
1711                        this.proxy.register_debug_adapter(
1712                            extension.clone(),
1713                            debug_adapter.clone(),
1714                            &path,
1715                        );
1716                    }
1717
1718                    for debug_adapter in manifest.debug_locators.keys() {
1719                        this.proxy
1720                            .register_debug_locator(extension.clone(), debug_adapter.clone());
1721                    }
1722
1723                    // Register LLM providers
1724                    for llm_provider in llm_providers_with_models {
1725                        let provider_id: Arc<str> =
1726                            format!("{}:{}", manifest.id, llm_provider.provider_info.id).into();
1727                        let wasm_ext = extension.as_ref().clone();
1728                        let pinfo = llm_provider.provider_info.clone();
1729                        let mods = llm_provider.models.clone();
1730                        let auth = llm_provider.is_authenticated;
1731                        let icon = llm_provider.icon_path.clone();
1732                        let auth_config = llm_provider.auth_config.clone();
1733
1734                        this.proxy.register_language_model_provider(
1735                            provider_id.clone(),
1736                            Box::new(move |cx: &mut App| {
1737                                let provider = Arc::new(ExtensionLanguageModelProvider::new(
1738                                    wasm_ext, pinfo, mods, auth, icon, auth_config, cx,
1739                                ));
1740                                language_model::LanguageModelRegistry::global(cx).update(
1741                                    cx,
1742                                    |registry, cx| {
1743                                        registry.register_provider(provider, cx);
1744                                    },
1745                                );
1746                            }),
1747                            cx,
1748                        );
1749                    }
1750                }
1751
1752                let wasm_extensions_without_llm: Vec<_> = wasm_extensions
1753                    .into_iter()
1754                    .map(|(manifest, ext, _)| (manifest, ext))
1755                    .collect();
1756                this.wasm_extensions.extend(wasm_extensions_without_llm);
1757                this.proxy.set_extensions_loaded();
1758                this.proxy.reload_current_theme(cx);
1759                this.proxy.reload_current_icon_theme(cx);
1760
1761                if let Some(events) = ExtensionEvents::try_global(cx) {
1762                    events.update(cx, |this, cx| {
1763                        this.emit(extension::Event::ExtensionsInstalledChanged, cx)
1764                    });
1765                }
1766            })
1767            .ok();
1768        })
1769    }
1770
1771    fn rebuild_extension_index(&self, cx: &mut Context<Self>) -> Task<ExtensionIndex> {
1772        let fs = self.fs.clone();
1773        let work_dir = self.wasm_host.work_dir.clone();
1774        let extensions_dir = self.installed_dir.clone();
1775        let index_path = self.index_path.clone();
1776        let proxy = self.proxy.clone();
1777        cx.background_spawn(async move {
1778            let mut index = ExtensionIndex::default();
1779
1780            fs.create_dir(&work_dir).await.log_err();
1781            fs.create_dir(&extensions_dir).await.log_err();
1782
1783            let extension_paths = fs.read_dir(&extensions_dir).await;
1784            if let Ok(mut extension_paths) = extension_paths {
1785                while let Some(extension_dir) = extension_paths.next().await {
1786                    let Ok(extension_dir) = extension_dir else {
1787                        continue;
1788                    };
1789
1790                    if extension_dir
1791                        .file_name()
1792                        .is_some_and(|file_name| file_name == ".DS_Store")
1793                    {
1794                        continue;
1795                    }
1796
1797                    Self::add_extension_to_index(
1798                        fs.clone(),
1799                        extension_dir,
1800                        &mut index,
1801                        proxy.clone(),
1802                    )
1803                    .await
1804                    .log_err();
1805                }
1806            }
1807
1808            if let Ok(index_json) = serde_json::to_string_pretty(&index) {
1809                fs.save(&index_path, &index_json.as_str().into(), Default::default())
1810                    .await
1811                    .context("failed to save extension index")
1812                    .log_err();
1813            }
1814
1815            index
1816        })
1817    }
1818
1819    async fn add_extension_to_index(
1820        fs: Arc<dyn Fs>,
1821        extension_dir: PathBuf,
1822        index: &mut ExtensionIndex,
1823        proxy: Arc<ExtensionHostProxy>,
1824    ) -> Result<()> {
1825        let mut extension_manifest = ExtensionManifest::load(fs.clone(), &extension_dir).await?;
1826        let extension_id = extension_manifest.id.clone();
1827
1828        if SUPPRESSED_EXTENSIONS.contains(&extension_id.as_ref()) {
1829            return Ok(());
1830        }
1831
1832        // TODO: distinguish dev extensions more explicitly, by the absence
1833        // of a checksum file that we'll create when downloading normal extensions.
1834        let is_dev = fs
1835            .metadata(&extension_dir)
1836            .await?
1837            .context("directory does not exist")?
1838            .is_symlink;
1839
1840        if let Ok(mut language_paths) = fs.read_dir(&extension_dir.join("languages")).await {
1841            while let Some(language_path) = language_paths.next().await {
1842                let language_path = language_path?;
1843                let Ok(relative_path) = language_path.strip_prefix(&extension_dir) else {
1844                    continue;
1845                };
1846                let Ok(Some(fs_metadata)) = fs.metadata(&language_path).await else {
1847                    continue;
1848                };
1849                if !fs_metadata.is_dir {
1850                    continue;
1851                }
1852                let config = fs.load(&language_path.join("config.toml")).await?;
1853                let config = ::toml::from_str::<LanguageConfig>(&config)?;
1854
1855                let relative_path = relative_path.to_path_buf();
1856                if !extension_manifest.languages.contains(&relative_path) {
1857                    extension_manifest.languages.push(relative_path.clone());
1858                }
1859
1860                index.languages.insert(
1861                    config.name.clone(),
1862                    ExtensionIndexLanguageEntry {
1863                        extension: extension_id.clone(),
1864                        path: relative_path,
1865                        matcher: config.matcher,
1866                        hidden: config.hidden,
1867                        grammar: config.grammar,
1868                    },
1869                );
1870            }
1871        }
1872
1873        if let Ok(mut theme_paths) = fs.read_dir(&extension_dir.join("themes")).await {
1874            while let Some(theme_path) = theme_paths.next().await {
1875                let theme_path = theme_path?;
1876                let Ok(relative_path) = theme_path.strip_prefix(&extension_dir) else {
1877                    continue;
1878                };
1879
1880                let Some(theme_families) = proxy
1881                    .list_theme_names(theme_path.clone(), fs.clone())
1882                    .await
1883                    .log_err()
1884                else {
1885                    continue;
1886                };
1887
1888                let relative_path = relative_path.to_path_buf();
1889                if !extension_manifest.themes.contains(&relative_path) {
1890                    extension_manifest.themes.push(relative_path.clone());
1891                }
1892
1893                for theme_name in theme_families {
1894                    index.themes.insert(
1895                        theme_name.into(),
1896                        ExtensionIndexThemeEntry {
1897                            extension: extension_id.clone(),
1898                            path: relative_path.clone(),
1899                        },
1900                    );
1901                }
1902            }
1903        }
1904
1905        if let Ok(mut icon_theme_paths) = fs.read_dir(&extension_dir.join("icon_themes")).await {
1906            while let Some(icon_theme_path) = icon_theme_paths.next().await {
1907                let icon_theme_path = icon_theme_path?;
1908                let Ok(relative_path) = icon_theme_path.strip_prefix(&extension_dir) else {
1909                    continue;
1910                };
1911
1912                let Some(icon_theme_families) = proxy
1913                    .list_icon_theme_names(icon_theme_path.clone(), fs.clone())
1914                    .await
1915                    .log_err()
1916                else {
1917                    continue;
1918                };
1919
1920                let relative_path = relative_path.to_path_buf();
1921                if !extension_manifest.icon_themes.contains(&relative_path) {
1922                    extension_manifest.icon_themes.push(relative_path.clone());
1923                }
1924
1925                for icon_theme_name in icon_theme_families {
1926                    index.icon_themes.insert(
1927                        icon_theme_name.into(),
1928                        ExtensionIndexIconThemeEntry {
1929                            extension: extension_id.clone(),
1930                            path: relative_path.clone(),
1931                        },
1932                    );
1933                }
1934            }
1935        }
1936
1937        let extension_wasm_path = extension_dir.join("extension.wasm");
1938        if fs.is_file(&extension_wasm_path).await {
1939            extension_manifest
1940                .lib
1941                .kind
1942                .get_or_insert(ExtensionLibraryKind::Rust);
1943        }
1944
1945        index.extensions.insert(
1946            extension_id.clone(),
1947            ExtensionIndexEntry {
1948                dev: is_dev,
1949                manifest: Arc::new(extension_manifest),
1950            },
1951        );
1952
1953        Ok(())
1954    }
1955
1956    fn prepare_remote_extension(
1957        &mut self,
1958        extension_id: Arc<str>,
1959        is_dev: bool,
1960        tmp_dir: PathBuf,
1961        cx: &mut Context<Self>,
1962    ) -> Task<Result<()>> {
1963        let src_dir = self.extensions_dir().join(extension_id.as_ref());
1964        let Some(loaded_extension) = self.extension_index.extensions.get(&extension_id).cloned()
1965        else {
1966            return Task::ready(Err(anyhow!("extension no longer installed")));
1967        };
1968        let fs = self.fs.clone();
1969        cx.background_spawn(async move {
1970            const EXTENSION_TOML: &str = "extension.toml";
1971            const EXTENSION_WASM: &str = "extension.wasm";
1972            const CONFIG_TOML: &str = "config.toml";
1973
1974            if is_dev {
1975                let manifest_toml = toml::to_string(&loaded_extension.manifest)?;
1976                fs.save(
1977                    &tmp_dir.join(EXTENSION_TOML),
1978                    &Rope::from(manifest_toml),
1979                    language::LineEnding::Unix,
1980                )
1981                .await?;
1982            } else {
1983                fs.copy_file(
1984                    &src_dir.join(EXTENSION_TOML),
1985                    &tmp_dir.join(EXTENSION_TOML),
1986                    fs::CopyOptions::default(),
1987                )
1988                .await?
1989            }
1990
1991            if fs.is_file(&src_dir.join(EXTENSION_WASM)).await {
1992                fs.copy_file(
1993                    &src_dir.join(EXTENSION_WASM),
1994                    &tmp_dir.join(EXTENSION_WASM),
1995                    fs::CopyOptions::default(),
1996                )
1997                .await?
1998            }
1999
2000            for language_path in loaded_extension.manifest.languages.iter() {
2001                if fs
2002                    .is_file(&src_dir.join(language_path).join(CONFIG_TOML))
2003                    .await
2004                {
2005                    fs.create_dir(&tmp_dir.join(language_path)).await?;
2006                    fs.copy_file(
2007                        &src_dir.join(language_path).join(CONFIG_TOML),
2008                        &tmp_dir.join(language_path).join(CONFIG_TOML),
2009                        fs::CopyOptions::default(),
2010                    )
2011                    .await?
2012                }
2013            }
2014
2015            for (adapter_name, meta) in loaded_extension.manifest.debug_adapters.iter() {
2016                let schema_path = &extension::build_debug_adapter_schema_path(adapter_name, meta);
2017
2018                if fs.is_file(&src_dir.join(schema_path)).await {
2019                    if let Some(parent) = schema_path.parent() {
2020                        fs.create_dir(&tmp_dir.join(parent)).await?
2021                    }
2022                    fs.copy_file(
2023                        &src_dir.join(schema_path),
2024                        &tmp_dir.join(schema_path),
2025                        fs::CopyOptions::default(),
2026                    )
2027                    .await?
2028                }
2029            }
2030
2031            Ok(())
2032        })
2033    }
2034
2035    async fn sync_extensions_to_remotes(
2036        this: &WeakEntity<Self>,
2037        client: WeakEntity<RemoteClient>,
2038        cx: &mut AsyncApp,
2039    ) -> Result<()> {
2040        let extensions = this.update(cx, |this, _cx| {
2041            this.extension_index
2042                .extensions
2043                .iter()
2044                .filter_map(|(id, entry)| {
2045                    if !entry.manifest.allow_remote_load() {
2046                        return None;
2047                    }
2048                    Some(proto::Extension {
2049                        id: id.to_string(),
2050                        version: entry.manifest.version.to_string(),
2051                        dev: entry.dev,
2052                    })
2053                })
2054                .collect()
2055        })?;
2056
2057        let response = client
2058            .update(cx, |client, _cx| {
2059                client
2060                    .proto_client()
2061                    .request(proto::SyncExtensions { extensions })
2062            })?
2063            .await?;
2064        let path_style = client.read_with(cx, |client, _| client.path_style())?;
2065
2066        for missing_extension in response.missing_extensions.into_iter() {
2067            let tmp_dir = tempfile::tempdir()?;
2068            this.update(cx, |this, cx| {
2069                this.prepare_remote_extension(
2070                    missing_extension.id.clone().into(),
2071                    missing_extension.dev,
2072                    tmp_dir.path().to_owned(),
2073                    cx,
2074                )
2075            })?
2076            .await?;
2077            let dest_dir = RemotePathBuf::new(
2078                path_style
2079                    .join(&response.tmp_dir, &missing_extension.id)
2080                    .with_context(|| {
2081                        format!(
2082                            "failed to construct destination path: {:?}, {:?}",
2083                            response.tmp_dir, missing_extension.id,
2084                        )
2085                    })?,
2086                path_style,
2087            );
2088
2089            client
2090                .update(cx, |client, cx| {
2091                    client.upload_directory(tmp_dir.path().to_owned(), dest_dir.clone(), cx)
2092                })?
2093                .await?;
2094
2095            let result = client
2096                .update(cx, |client, _cx| {
2097                    client.proto_client().request(proto::InstallExtension {
2098                        tmp_dir: dest_dir.to_proto(),
2099                        extension: Some(missing_extension.clone()),
2100                    })
2101                })?
2102                .await;
2103
2104            if let Err(e) = result {
2105                log::error!(
2106                    "Failed to install extension {}: {}",
2107                    missing_extension.id,
2108                    e
2109                );
2110            }
2111        }
2112
2113        anyhow::Ok(())
2114    }
2115
2116    pub async fn update_remote_clients(this: &WeakEntity<Self>, cx: &mut AsyncApp) -> Result<()> {
2117        let clients = this.update(cx, |this, _cx| {
2118            this.remote_clients.retain(|v| v.upgrade().is_some());
2119            this.remote_clients.clone()
2120        })?;
2121
2122        for client in clients {
2123            Self::sync_extensions_to_remotes(this, client, cx)
2124                .await
2125                .log_err();
2126        }
2127
2128        anyhow::Ok(())
2129    }
2130
2131    pub fn register_remote_client(
2132        &mut self,
2133        client: Entity<RemoteClient>,
2134        _cx: &mut Context<Self>,
2135    ) {
2136        self.remote_clients.push(client.downgrade());
2137        self.ssh_registered_tx.unbounded_send(()).ok();
2138    }
2139}
2140
2141fn load_plugin_queries(root_path: &Path) -> LanguageQueries {
2142    let mut result = LanguageQueries::default();
2143    if let Some(entries) = std::fs::read_dir(root_path).log_err() {
2144        for entry in entries {
2145            let Some(entry) = entry.log_err() else {
2146                continue;
2147            };
2148            let path = entry.path();
2149            if let Some(remainder) = path.strip_prefix(root_path).ok().and_then(|p| p.to_str()) {
2150                if !remainder.ends_with(".scm") {
2151                    continue;
2152                }
2153                for (name, query) in QUERY_FILENAME_PREFIXES {
2154                    if remainder.starts_with(name) {
2155                        if let Some(contents) = std::fs::read_to_string(&path).log_err() {
2156                            match query(&mut result) {
2157                                None => *query(&mut result) = Some(contents.into()),
2158                                Some(r) => r.to_mut().push_str(contents.as_ref()),
2159                            }
2160                        }
2161                        break;
2162                    }
2163                }
2164            }
2165        }
2166    }
2167    result
2168}