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