Detailed changes
@@ -1562,6 +1562,14 @@
"auto_install_extensions": {
"html": true
},
+ // The capabilities granted to extensions.
+ //
+ // This list can be customized to restrict what extensions are able to do.
+ "granted_extension_capabilities": [
+ { "kind": "process:exec", "command": "*", "args": ["**"] },
+ { "kind": "download_file", "host": "*", "path": ["**"] },
+ { "kind": "npm:install", "package": "*" }
+ ],
// Controls how completions are processed for this language.
"completions": {
// Controls how words are completed.
@@ -1,4 +1,7 @@
use collections::HashMap;
+use extension::{
+ DownloadFileCapability, ExtensionCapability, NpmInstallPackageCapability, ProcessExecCapability,
+};
use gpui::App;
use settings::Settings;
use std::sync::Arc;
@@ -13,6 +16,7 @@ pub struct ExtensionSettings {
/// Default: { "html": true }
pub auto_install_extensions: HashMap<Arc<str>, bool>,
pub auto_update_extensions: HashMap<Arc<str>, bool>,
+ pub granted_capabilities: Vec<ExtensionCapability>,
}
impl ExtensionSettings {
@@ -37,6 +41,32 @@ impl Settings for ExtensionSettings {
Self {
auto_install_extensions: content.extension.auto_install_extensions.clone(),
auto_update_extensions: content.extension.auto_update_extensions.clone(),
+ granted_capabilities: content
+ .extension
+ .granted_extension_capabilities
+ .clone()
+ .unwrap_or_default()
+ .into_iter()
+ .map(|capability| match capability {
+ settings::ExtensionCapabilityContent::ProcessExec(capability) => {
+ ExtensionCapability::ProcessExec(ProcessExecCapability {
+ command: capability.command,
+ args: capability.args,
+ })
+ }
+ settings::ExtensionCapabilityContent::DownloadFile(capability) => {
+ ExtensionCapability::DownloadFile(DownloadFileCapability {
+ host: capability.host,
+ path: capability.path,
+ })
+ }
+ settings::ExtensionCapabilityContent::NpmInstallPackage(capability) => {
+ ExtensionCapability::NpmInstallPackage(NpmInstallPackageCapability {
+ package: capability.package,
+ })
+ }
+ })
+ .collect(),
}
}
}
@@ -1,15 +1,15 @@
pub mod wit;
-use crate::ExtensionManifest;
use crate::capability_granter::CapabilityGranter;
+use crate::{ExtensionManifest, ExtensionSettings};
use anyhow::{Context as _, Result, anyhow, bail};
use async_trait::async_trait;
use dap::{DebugRequest, StartDebuggingRequestArgumentsRequest};
use extension::{
CodeLabel, Command, Completion, ContextServerConfiguration, DebugAdapterBinary,
- DebugTaskDefinition, DownloadFileCapability, ExtensionCapability, ExtensionHostProxy,
- KeyValueStoreDelegate, NpmInstallPackageCapability, ProcessExecCapability, ProjectDelegate,
- SlashCommand, SlashCommandArgumentCompletion, SlashCommandOutput, Symbol, WorktreeDelegate,
+ DebugTaskDefinition, ExtensionCapability, ExtensionHostProxy, KeyValueStoreDelegate,
+ ProjectDelegate, SlashCommand, SlashCommandArgumentCompletion, SlashCommandOutput, Symbol,
+ WorktreeDelegate,
};
use fs::{Fs, normalize_path};
use futures::future::LocalBoxFuture;
@@ -29,6 +29,7 @@ use moka::sync::Cache;
use node_runtime::NodeRuntime;
use release_channel::ReleaseChannel;
use semantic_version::SemanticVersion;
+use settings::Settings;
use std::borrow::Cow;
use std::sync::{LazyLock, OnceLock};
use std::time::Duration;
@@ -569,6 +570,9 @@ impl WasmHost {
message(cx).await;
}
});
+
+ let extension_settings = ExtensionSettings::get_global(cx);
+
Arc::new(Self {
engine: wasm_engine(cx.background_executor()),
fs,
@@ -577,19 +581,7 @@ impl WasmHost {
node_runtime,
proxy,
release_channel: ReleaseChannel::global(cx),
- granted_capabilities: vec![
- ExtensionCapability::ProcessExec(ProcessExecCapability {
- command: "*".to_string(),
- args: vec!["**".to_string()],
- }),
- ExtensionCapability::DownloadFile(DownloadFileCapability {
- host: "*".to_string(),
- path: vec!["**".to_string()],
- }),
- ExtensionCapability::NpmInstallPackage(NpmInstallPackageCapability {
- package: "*".to_string(),
- }),
- ],
+ granted_capabilities: extension_settings.granted_capabilities.clone(),
_main_thread_message_task: task,
main_thread_message_tx: tx,
})
@@ -26,7 +26,7 @@ use rpc::{
proto::{self, REMOTE_SERVER_PEER_ID, REMOTE_SERVER_PROJECT_ID},
};
-use settings::initial_server_settings_content;
+use settings::{Settings as _, initial_server_settings_content};
use smol::stream::StreamExt;
use std::{
path::{Path, PathBuf},
@@ -69,6 +69,7 @@ impl HeadlessProject {
settings::init(cx);
language::init(cx);
project::Project::init_settings(cx);
+ extension_host::ExtensionSettings::register(cx);
log_store::init(true, cx);
}
@@ -1,5 +1,6 @@
mod agent;
mod editor;
+mod extension;
mod language;
mod language_model;
mod project;
@@ -9,6 +10,7 @@ mod workspace;
pub use agent::*;
pub use editor::*;
+pub use extension::*;
pub use language::*;
pub use language_model::*;
pub use project::*;
@@ -433,21 +435,6 @@ pub struct CallSettingsContent {
pub share_on_join: Option<bool>,
}
-#[skip_serializing_none]
-#[derive(Deserialize, Serialize, PartialEq, Debug, Default, Clone, JsonSchema, MergeFrom)]
-pub struct ExtensionSettingsContent {
- /// The extensions that should be automatically installed by Zed.
- ///
- /// This is used to make functionality provided by extensions (e.g., language support)
- /// available out-of-the-box.
- ///
- /// Default: { "html": true }
- #[serde(default)]
- pub auto_install_extensions: HashMap<Arc<str>, bool>,
- #[serde(default)]
- pub auto_update_extensions: HashMap<Arc<str>, bool>,
-}
-
#[skip_serializing_none]
#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug)]
pub struct GitPanelSettingsContent {
@@ -0,0 +1,59 @@
+use std::sync::Arc;
+
+use collections::HashMap;
+use schemars::JsonSchema;
+use serde::{Deserialize, Serialize};
+use serde_with::skip_serializing_none;
+use settings_macros::MergeFrom;
+
+#[skip_serializing_none]
+#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
+pub struct ExtensionSettingsContent {
+ /// The extensions that should be automatically installed by Zed.
+ ///
+ /// This is used to make functionality provided by extensions (e.g., language support)
+ /// available out-of-the-box.
+ ///
+ /// Default: { "html": true }
+ #[serde(default)]
+ pub auto_install_extensions: HashMap<Arc<str>, bool>,
+ #[serde(default)]
+ pub auto_update_extensions: HashMap<Arc<str>, bool>,
+ /// The capabilities granted to extensions.
+ #[serde(default)]
+ pub granted_extension_capabilities: Option<Vec<ExtensionCapabilityContent>>,
+}
+
+/// A capability for an extension.
+#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, JsonSchema)]
+#[serde(tag = "kind", rename_all = "snake_case")]
+pub enum ExtensionCapabilityContent {
+ #[serde(rename = "process:exec")]
+ ProcessExec(ProcessExecCapabilityContent),
+ DownloadFile(DownloadFileCapabilityContent),
+ #[serde(rename = "npm:install")]
+ NpmInstallPackage(NpmInstallPackageCapabilityContent),
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub struct ProcessExecCapabilityContent {
+ /// The command to execute.
+ pub command: String,
+ /// The arguments to pass to the command. Use `*` for a single wildcard argument.
+ /// If the last element is `**`, then any trailing arguments are allowed.
+ pub args: Vec<String>,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub struct DownloadFileCapabilityContent {
+ pub host: String,
+ pub path: Vec<String>,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub struct NpmInstallPackageCapabilityContent {
+ pub package: String,
+}