feat(plugins): allow changing the plugin's /host folder (under a new permission) (#3827)

* working without notifying plugins

* permissions and events

* cleanups and formatting

* style(fmt): rustfmt
This commit is contained in:
Aram Drevekenin 2024-12-01 11:06:44 +01:00 committed by GitHub
parent 90ecd8fb4b
commit 0c21eae664
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 301 additions and 37 deletions

View file

@ -158,6 +158,7 @@ pub enum PluginInstruction {
}, },
WatchFilesystem, WatchFilesystem,
ListClientsToPlugin(SessionLayoutMetadata, PluginId, ClientId), ListClientsToPlugin(SessionLayoutMetadata, PluginId, ClientId),
ChangePluginHostDir(PathBuf, PluginId, ClientId),
Exit, Exit,
} }
@ -204,6 +205,7 @@ impl From<&PluginInstruction> for PluginContext {
PluginContext::FailedToWriteConfigToDisk PluginContext::FailedToWriteConfigToDisk
}, },
PluginInstruction::ListClientsToPlugin(..) => PluginContext::ListClientsToPlugin, PluginInstruction::ListClientsToPlugin(..) => PluginContext::ListClientsToPlugin,
PluginInstruction::ChangePluginHostDir(..) => PluginContext::ChangePluginHostDir,
} }
} }
} }
@ -886,6 +888,11 @@ pub(crate) fn plugin_thread_main(
PluginInstruction::WatchFilesystem => { PluginInstruction::WatchFilesystem => {
wasm_bridge.start_fs_watcher_if_not_started(); wasm_bridge.start_fs_watcher_if_not_started();
}, },
PluginInstruction::ChangePluginHostDir(new_host_folder, plugin_id, client_id) => {
wasm_bridge
.change_plugin_host_dir(new_host_folder, plugin_id, client_id)
.non_fatal();
},
PluginInstruction::Exit => { PluginInstruction::Exit => {
break; break;
}, },

View file

@ -14,7 +14,7 @@ use std::{
}; };
use url::Url; use url::Url;
use wasmtime::{Engine, Instance, Linker, Module, Store}; use wasmtime::{Engine, Instance, Linker, Module, Store};
use wasmtime_wasi::{DirPerms, FilePerms, WasiCtxBuilder}; use wasmtime_wasi::{preview1::WasiP1Ctx, DirPerms, FilePerms, WasiCtxBuilder};
use zellij_utils::consts::ZELLIJ_PLUGIN_ARTIFACT_DIR; use zellij_utils::consts::ZELLIJ_PLUGIN_ARTIFACT_DIR;
use zellij_utils::prost::Message; use zellij_utils::prost::Message;
@ -64,7 +64,7 @@ pub struct PluginLoader<'a> {
size: Size, size: Size,
wasm_blob_on_hd: Option<(Vec<u8>, PathBuf)>, wasm_blob_on_hd: Option<(Vec<u8>, PathBuf)>,
path_to_default_shell: PathBuf, path_to_default_shell: PathBuf,
zellij_cwd: PathBuf, plugin_cwd: PathBuf,
capabilities: PluginCapabilities, capabilities: PluginCapabilities,
client_attributes: ClientAttributes, client_attributes: ClientAttributes,
default_shell: Option<TerminalAction>, default_shell: Option<TerminalAction>,
@ -85,7 +85,7 @@ impl<'a> PluginLoader<'a> {
connected_clients: Arc<Mutex<Vec<ClientId>>>, connected_clients: Arc<Mutex<Vec<ClientId>>>,
loading_indication: &mut LoadingIndication, loading_indication: &mut LoadingIndication,
path_to_default_shell: PathBuf, path_to_default_shell: PathBuf,
zellij_cwd: PathBuf, plugin_cwd: PathBuf,
capabilities: PluginCapabilities, capabilities: PluginCapabilities,
client_attributes: ClientAttributes, client_attributes: ClientAttributes,
default_shell: Option<TerminalAction>, default_shell: Option<TerminalAction>,
@ -110,7 +110,7 @@ impl<'a> PluginLoader<'a> {
engine, engine,
&plugin_dir, &plugin_dir,
path_to_default_shell, path_to_default_shell,
Some(zellij_cwd), Some(plugin_cwd),
capabilities, capabilities,
client_attributes, client_attributes,
default_shell, default_shell,
@ -145,7 +145,7 @@ impl<'a> PluginLoader<'a> {
connected_clients: Arc<Mutex<Vec<ClientId>>>, connected_clients: Arc<Mutex<Vec<ClientId>>>,
loading_indication: &mut LoadingIndication, loading_indication: &mut LoadingIndication,
path_to_default_shell: PathBuf, path_to_default_shell: PathBuf,
zellij_cwd: PathBuf, plugin_cwd: PathBuf,
capabilities: PluginCapabilities, capabilities: PluginCapabilities,
client_attributes: ClientAttributes, client_attributes: ClientAttributes,
default_shell: Option<TerminalAction>, default_shell: Option<TerminalAction>,
@ -168,7 +168,7 @@ impl<'a> PluginLoader<'a> {
tab_index, tab_index,
size, size,
path_to_default_shell, path_to_default_shell,
zellij_cwd, plugin_cwd,
capabilities, capabilities,
client_attributes, client_attributes,
default_shell, default_shell,
@ -222,7 +222,7 @@ impl<'a> PluginLoader<'a> {
connected_clients: Arc<Mutex<Vec<ClientId>>>, connected_clients: Arc<Mutex<Vec<ClientId>>>,
loading_indication: &mut LoadingIndication, loading_indication: &mut LoadingIndication,
path_to_default_shell: PathBuf, path_to_default_shell: PathBuf,
zellij_cwd: PathBuf, plugin_cwd: PathBuf,
capabilities: PluginCapabilities, capabilities: PluginCapabilities,
client_attributes: ClientAttributes, client_attributes: ClientAttributes,
default_shell: Option<TerminalAction>, default_shell: Option<TerminalAction>,
@ -246,7 +246,7 @@ impl<'a> PluginLoader<'a> {
engine.clone(), engine.clone(),
&plugin_dir, &plugin_dir,
path_to_default_shell.clone(), path_to_default_shell.clone(),
zellij_cwd.clone(), plugin_cwd.clone(),
capabilities.clone(), capabilities.clone(),
client_attributes.clone(), client_attributes.clone(),
default_shell.clone(), default_shell.clone(),
@ -333,7 +333,7 @@ impl<'a> PluginLoader<'a> {
tab_index: Option<usize>, tab_index: Option<usize>,
size: Size, size: Size,
path_to_default_shell: PathBuf, path_to_default_shell: PathBuf,
zellij_cwd: PathBuf, plugin_cwd: PathBuf,
capabilities: PluginCapabilities, capabilities: PluginCapabilities,
client_attributes: ClientAttributes, client_attributes: ClientAttributes,
default_shell: Option<TerminalAction>, default_shell: Option<TerminalAction>,
@ -366,7 +366,7 @@ impl<'a> PluginLoader<'a> {
size, size,
wasm_blob_on_hd: None, wasm_blob_on_hd: None,
path_to_default_shell, path_to_default_shell,
zellij_cwd, plugin_cwd,
capabilities, capabilities,
client_attributes, client_attributes,
default_shell, default_shell,
@ -412,7 +412,7 @@ impl<'a> PluginLoader<'a> {
// prefer the explicitly given cwd, otherwise copy it from the running plugin // prefer the explicitly given cwd, otherwise copy it from the running plugin
// (when reloading a plugin, we want to copy it, when starting a new plugin instance from // (when reloading a plugin, we want to copy it, when starting a new plugin instance from
// meomory, we want to reset it) // meomory, we want to reset it)
let zellij_cwd = cwd.unwrap_or_else(|| running_plugin.store.data().plugin_cwd.clone()); let plugin_cwd = cwd.unwrap_or_else(|| running_plugin.store.data().plugin_cwd.clone());
loading_indication.set_name(running_plugin.store.data().name()); loading_indication.set_name(running_plugin.store.data().name());
PluginLoader::new( PluginLoader::new(
plugin_cache, plugin_cache,
@ -426,7 +426,7 @@ impl<'a> PluginLoader<'a> {
tab_index, tab_index,
size, size,
path_to_default_shell, path_to_default_shell,
zellij_cwd, plugin_cwd,
capabilities, capabilities,
client_attributes, client_attributes,
default_shell, default_shell,
@ -446,7 +446,7 @@ impl<'a> PluginLoader<'a> {
engine: Engine, engine: Engine,
plugin_dir: &'a PathBuf, plugin_dir: &'a PathBuf,
path_to_default_shell: PathBuf, path_to_default_shell: PathBuf,
zellij_cwd: PathBuf, plugin_cwd: PathBuf,
capabilities: PluginCapabilities, capabilities: PluginCapabilities,
client_attributes: ClientAttributes, client_attributes: ClientAttributes,
default_shell: Option<TerminalAction>, default_shell: Option<TerminalAction>,
@ -483,7 +483,7 @@ impl<'a> PluginLoader<'a> {
tab_index, tab_index,
size, size,
path_to_default_shell, path_to_default_shell,
zellij_cwd, plugin_cwd,
capabilities, capabilities,
client_attributes, client_attributes,
default_shell, default_shell,
@ -736,7 +736,7 @@ impl<'a> PluginLoader<'a> {
self.engine.clone(), self.engine.clone(),
&self.plugin_dir, &self.plugin_dir,
self.path_to_default_shell.clone(), self.path_to_default_shell.clone(),
self.zellij_cwd.clone(), self.plugin_cwd.clone(),
self.capabilities.clone(), self.capabilities.clone(),
self.client_attributes.clone(), self.client_attributes.clone(),
self.default_shell.clone(), self.default_shell.clone(),
@ -784,18 +784,22 @@ impl<'a> PluginLoader<'a> {
}, },
} }
} }
fn create_plugin_instance_env(&self, module: &Module) -> Result<(Store<PluginEnv>, Instance)> { pub fn create_wasi_ctx(
let err_context = || { host_dir: &PathBuf,
format!( data_dir: &PathBuf,
"Failed to create instance, plugin env and subscriptions for plugin {}", cache_dir: &PathBuf,
self.plugin_id tmp_dir: &PathBuf,
) plugin_url: &String,
}; plugin_id: PluginId,
stdin_pipe: Arc<Mutex<VecDeque<u8>>>,
stdout_pipe: Arc<Mutex<VecDeque<u8>>>,
) -> Result<WasiP1Ctx> {
let err_context = || format!("Failed to create wasi_ctx");
let dirs = vec![ let dirs = vec![
("/host".to_owned(), self.zellij_cwd.clone()), ("/host".to_owned(), host_dir.clone()),
("/data".to_owned(), self.plugin_own_data_dir.clone()), ("/data".to_owned(), data_dir.clone()),
("/cache".to_owned(), self.plugin_own_cache_dir.clone()), ("/cache".to_owned(), cache_dir.clone()),
("/tmp".to_owned(), ZELLIJ_TMP_DIR.clone()), ("/tmp".to_owned(), tmp_dir.clone()),
]; ];
let dirs = dirs.into_iter().filter(|(_dir_name, dir)| { let dirs = dirs.into_iter().filter(|(_dir_name, dir)| {
// note that this does not protect against TOCTOU errors // note that this does not protect against TOCTOU errors
@ -812,16 +816,35 @@ impl<'a> PluginLoader<'a> {
.preopened_dir(host_path, guest_path, DirPerms::all(), FilePerms::all()) .preopened_dir(host_path, guest_path, DirPerms::all(), FilePerms::all())
.with_context(err_context)?; .with_context(err_context)?;
} }
let stdin_pipe = Arc::new(Mutex::new(VecDeque::new()));
let stdout_pipe = Arc::new(Mutex::new(VecDeque::new()));
wasi_ctx_builder wasi_ctx_builder
.stdin(VecDequeInputStream(stdin_pipe.clone())) .stdin(VecDequeInputStream(stdin_pipe.clone()))
.stdout(WriteOutputStream(stdout_pipe.clone())) .stdout(WriteOutputStream(stdout_pipe.clone()))
.stderr(WriteOutputStream(Arc::new(Mutex::new(LoggingPipe::new( .stderr(WriteOutputStream(Arc::new(Mutex::new(LoggingPipe::new(
&self.plugin.location.to_string(), plugin_url, plugin_id,
self.plugin_id,
))))); )))));
let wasi_ctx = wasi_ctx_builder.build_p1(); let wasi_ctx = wasi_ctx_builder.build_p1();
Ok(wasi_ctx)
}
fn create_plugin_instance_env(&self, module: &Module) -> Result<(Store<PluginEnv>, Instance)> {
let err_context = || {
format!(
"Failed to create instance, plugin env and subscriptions for plugin {}",
self.plugin_id
)
};
let stdin_pipe = Arc::new(Mutex::new(VecDeque::new()));
let stdout_pipe = Arc::new(Mutex::new(VecDeque::new()));
let wasi_ctx = PluginLoader::create_wasi_ctx(
&self.plugin_cwd,
&self.plugin_own_data_dir,
&self.plugin_own_cache_dir,
&ZELLIJ_TMP_DIR,
&self.plugin.location.to_string(),
self.plugin_id,
stdin_pipe.clone(),
stdout_pipe.clone(),
)?;
let plugin = self.plugin.clone(); let plugin = self.plugin.clone();
let plugin_env = PluginEnv { let plugin_env = PluginEnv {
plugin_id: self.plugin_id, plugin_id: self.plugin_id,
@ -838,7 +861,7 @@ impl<'a> PluginLoader<'a> {
client_attributes: self.client_attributes.clone(), client_attributes: self.client_attributes.clone(),
default_shell: self.default_shell.clone(), default_shell: self.default_shell.clone(),
default_layout: self.default_layout.clone(), default_layout: self.default_layout.clone(),
plugin_cwd: self.zellij_cwd.clone(), plugin_cwd: self.plugin_cwd.clone(),
input_pipes_to_unblock: Arc::new(Mutex::new(HashSet::new())), input_pipes_to_unblock: Arc::new(Mutex::new(HashSet::new())),
input_pipes_to_block: Arc::new(Mutex::new(HashSet::new())), input_pipes_to_block: Arc::new(Mutex::new(HashSet::new())),
layout_dir: self.layout_dir.clone(), layout_dir: self.layout_dir.clone(),

View file

@ -4,6 +4,7 @@ use crate::plugins::pipes::{
}; };
use crate::plugins::plugin_loader::PluginLoader; use crate::plugins::plugin_loader::PluginLoader;
use crate::plugins::plugin_map::{AtomicEvent, PluginEnv, PluginMap, RunningPlugin, Subscriptions}; use crate::plugins::plugin_map::{AtomicEvent, PluginEnv, PluginMap, RunningPlugin, Subscriptions};
use crate::plugins::plugin_worker::MessageToWorker; use crate::plugins::plugin_worker::MessageToWorker;
use crate::plugins::watch_filesystem::watch_filesystem; use crate::plugins::watch_filesystem::watch_filesystem;
use crate::plugins::zellij_exports::{wasi_read_string, wasi_write_object}; use crate::plugins::zellij_exports::{wasi_read_string, wasi_write_object};
@ -18,7 +19,7 @@ use std::{
use wasmtime::{Engine, Module}; use wasmtime::{Engine, Module};
use zellij_utils::async_channel::Sender; use zellij_utils::async_channel::Sender;
use zellij_utils::async_std::task::{self, JoinHandle}; use zellij_utils::async_std::task::{self, JoinHandle};
use zellij_utils::consts::ZELLIJ_CACHE_DIR; use zellij_utils::consts::{ZELLIJ_CACHE_DIR, ZELLIJ_TMP_DIR};
use zellij_utils::data::{InputMode, PermissionStatus, PermissionType, PipeMessage, PipeSource}; use zellij_utils::data::{InputMode, PermissionStatus, PermissionType, PipeMessage, PipeSource};
use zellij_utils::downloader::Downloader; use zellij_utils::downloader::Downloader;
use zellij_utils::input::keybinds::Keybinds; use zellij_utils::input::keybinds::Keybinds;
@ -736,6 +737,107 @@ impl WasmBridge {
} }
Ok(()) Ok(())
} }
pub fn change_plugin_host_dir(
&mut self,
new_host_dir: PathBuf,
plugin_id_to_update: PluginId,
client_id_to_update: ClientId,
) -> Result<()> {
let plugins_to_change: Vec<(
PluginId,
ClientId,
Arc<Mutex<RunningPlugin>>,
Arc<Mutex<Subscriptions>>,
)> = self
.plugin_map
.lock()
.unwrap()
.running_plugins_and_subscriptions()
.iter()
.cloned()
.filter(|(plugin_id, _client_id, _running_plugin, _subscriptions)| {
// TODO: cache this somehow in this case...
!&self
.cached_events_for_pending_plugins
.contains_key(&plugin_id)
})
.collect();
task::spawn({
let senders = self.senders.clone();
async move {
match new_host_dir.try_exists() {
Ok(false) => {
log::error!(
"Failed to change folder to {},: folder does not exist",
new_host_dir.display()
);
let _ = senders.send_to_plugin(PluginInstruction::Update(vec![(
Some(plugin_id_to_update),
Some(client_id_to_update),
Event::FailedToChangeHostFolder(Some(format!(
"Folder {} does not exist",
new_host_dir.display()
))),
)]));
return;
},
Err(e) => {
log::error!(
"Failed to change folder to {},: {}",
new_host_dir.display(),
e
);
let _ = senders.send_to_plugin(PluginInstruction::Update(vec![(
Some(plugin_id_to_update),
Some(client_id_to_update),
Event::FailedToChangeHostFolder(Some(e.to_string())),
)]));
return;
},
_ => {},
}
for (plugin_id, client_id, running_plugin, _subscriptions) in &plugins_to_change {
if plugin_id == &plugin_id_to_update && client_id == &client_id_to_update {
let mut running_plugin = running_plugin.lock().unwrap();
let plugin_env = running_plugin.store.data_mut();
let stdin_pipe = plugin_env.stdin_pipe.clone();
let stdout_pipe = plugin_env.stdout_pipe.clone();
let wasi_ctx = PluginLoader::create_wasi_ctx(
&new_host_dir,
&plugin_env.plugin_own_data_dir,
&plugin_env.plugin_own_cache_dir,
&ZELLIJ_TMP_DIR,
&plugin_env.plugin.location.to_string(),
plugin_env.plugin_id,
stdin_pipe.clone(),
stdout_pipe.clone(),
);
match wasi_ctx {
Ok(wasi_ctx) => {
drop(std::mem::replace(&mut plugin_env.wasi_ctx, wasi_ctx));
plugin_env.plugin_cwd = new_host_dir.clone();
let _ = senders.send_to_plugin(PluginInstruction::Update(vec![(
Some(*plugin_id),
Some(*client_id),
Event::HostFolderChanged(new_host_dir.clone()),
)]));
},
Err(e) => {
let _ = senders.send_to_plugin(PluginInstruction::Update(vec![(
Some(*plugin_id),
Some(*client_id),
Event::FailedToChangeHostFolder(Some(e.to_string())),
)]));
log::error!("Failed to create wasi ctx: {}", e);
},
}
}
}
}
});
Ok(())
}
pub fn pipe_messages( pub fn pipe_messages(
&mut self, &mut self,
mut messages: Vec<(Option<PluginId>, Option<ClientId>, PipeMessage)>, mut messages: Vec<(Option<PluginId>, Option<ClientId>, PipeMessage)>,

View file

@ -352,6 +352,9 @@ fn host_run_plugin_command(caller: Caller<'_, PluginEnv>) {
write_config_to_disk, write_config_to_disk,
} => rebind_keys(env, keys_to_rebind, keys_to_unbind, write_config_to_disk)?, } => rebind_keys(env, keys_to_rebind, keys_to_unbind, write_config_to_disk)?,
PluginCommand::ListClients => list_clients(env), PluginCommand::ListClients => list_clients(env),
PluginCommand::ChangeHostFolder(new_host_folder) => {
change_host_folder(env, new_host_folder)
},
}, },
(PermissionStatus::Denied, permission) => { (PermissionStatus::Denied, permission) => {
log::error!( log::error!(
@ -1484,6 +1487,16 @@ fn list_clients(env: &PluginEnv) {
}); });
} }
fn change_host_folder(env: &PluginEnv, new_host_folder: PathBuf) {
let _ = env.senders.to_plugin.as_ref().map(|sender| {
sender.send(PluginInstruction::ChangePluginHostDir(
new_host_folder,
env.plugin_id,
env.client_id,
))
});
}
fn scan_host_folder(env: &PluginEnv, folder_to_scan: PathBuf) { fn scan_host_folder(env: &PluginEnv, folder_to_scan: PathBuf) {
if !folder_to_scan.starts_with("/host") { if !folder_to_scan.starts_with("/host") {
log::error!( log::error!(
@ -1913,6 +1926,7 @@ fn check_command_permission(
PluginCommand::RebindKeys { .. } | PluginCommand::Reconfigure(..) => { PluginCommand::RebindKeys { .. } | PluginCommand::Reconfigure(..) => {
PermissionType::Reconfigure PermissionType::Reconfigure
}, },
PluginCommand::ChangeHostFolder(..) => PermissionType::FullHdAccess,
_ => return (PermissionStatus::Granted, None), _ => return (PermissionStatus::Granted, None),
}; };

View file

@ -1128,6 +1128,13 @@ pub fn rebind_keys(
unsafe { host_run_plugin_command() }; unsafe { host_run_plugin_command() };
} }
pub fn change_host_folder(new_host_folder: PathBuf) {
let plugin_command = PluginCommand::ChangeHostFolder(new_host_folder);
let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
object_to_stdout(&protobuf_plugin_command.encode_to_vec());
unsafe { host_run_plugin_command() };
}
// Utility Functions // Utility Functions
#[allow(unused)] #[allow(unused)]

View file

@ -11,7 +11,7 @@ pub struct Event {
pub name: i32, pub name: i32,
#[prost( #[prost(
oneof = "event::Payload", oneof = "event::Payload",
tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23" tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25"
)] )]
pub payload: ::core::option::Option<event::Payload>, pub payload: ::core::option::Option<event::Payload>,
} }
@ -64,10 +64,26 @@ pub mod event {
FailedToWriteConfigToDiskPayload(super::FailedToWriteConfigToDiskPayload), FailedToWriteConfigToDiskPayload(super::FailedToWriteConfigToDiskPayload),
#[prost(message, tag = "23")] #[prost(message, tag = "23")]
ListClientsPayload(super::ListClientsPayload), ListClientsPayload(super::ListClientsPayload),
#[prost(message, tag = "24")]
HostFolderChangedPayload(super::HostFolderChangedPayload),
#[prost(message, tag = "25")]
FailedToChangeHostFolderPayload(super::FailedToChangeHostFolderPayload),
} }
} }
#[allow(clippy::derive_partial_eq_without_eq)] #[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)] #[derive(Clone, PartialEq, ::prost::Message)]
pub struct FailedToChangeHostFolderPayload {
#[prost(string, optional, tag = "1")]
pub error_message: ::core::option::Option<::prost::alloc::string::String>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct HostFolderChangedPayload {
#[prost(string, tag = "1")]
pub new_host_folder_path: ::prost::alloc::string::String,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ListClientsPayload { pub struct ListClientsPayload {
#[prost(message, repeated, tag = "1")] #[prost(message, repeated, tag = "1")]
pub client_info: ::prost::alloc::vec::Vec<ClientInfo>, pub client_info: ::prost::alloc::vec::Vec<ClientInfo>,
@ -468,6 +484,8 @@ pub enum EventType {
CommandPaneReRun = 24, CommandPaneReRun = 24,
FailedToWriteConfigToDisk = 25, FailedToWriteConfigToDisk = 25,
ListClients = 26, ListClients = 26,
HostFolderChanged = 27,
FailedToChangeHostFolder = 28,
} }
impl EventType { impl EventType {
/// String value of the enum field names used in the ProtoBuf definition. /// String value of the enum field names used in the ProtoBuf definition.
@ -503,6 +521,8 @@ impl EventType {
EventType::CommandPaneReRun => "CommandPaneReRun", EventType::CommandPaneReRun => "CommandPaneReRun",
EventType::FailedToWriteConfigToDisk => "FailedToWriteConfigToDisk", EventType::FailedToWriteConfigToDisk => "FailedToWriteConfigToDisk",
EventType::ListClients => "ListClients", EventType::ListClients => "ListClients",
EventType::HostFolderChanged => "HostFolderChanged",
EventType::FailedToChangeHostFolder => "FailedToChangeHostFolder",
} }
} }
/// Creates an enum from field names used in the ProtoBuf definition. /// Creates an enum from field names used in the ProtoBuf definition.
@ -535,6 +555,8 @@ impl EventType {
"CommandPaneReRun" => Some(Self::CommandPaneReRun), "CommandPaneReRun" => Some(Self::CommandPaneReRun),
"FailedToWriteConfigToDisk" => Some(Self::FailedToWriteConfigToDisk), "FailedToWriteConfigToDisk" => Some(Self::FailedToWriteConfigToDisk),
"ListClients" => Some(Self::ListClients), "ListClients" => Some(Self::ListClients),
"HostFolderChanged" => Some(Self::HostFolderChanged),
"FailedToChangeHostFolder" => Some(Self::FailedToChangeHostFolder),
_ => None, _ => None,
} }
} }

View file

@ -5,7 +5,7 @@ pub struct PluginCommand {
pub name: i32, pub name: i32,
#[prost( #[prost(
oneof = "plugin_command::Payload", oneof = "plugin_command::Payload",
tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88" tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89"
)] )]
pub payload: ::core::option::Option<plugin_command::Payload>, pub payload: ::core::option::Option<plugin_command::Payload>,
} }
@ -174,10 +174,18 @@ pub mod plugin_command {
LoadNewPluginPayload(super::LoadNewPluginPayload), LoadNewPluginPayload(super::LoadNewPluginPayload),
#[prost(message, tag = "88")] #[prost(message, tag = "88")]
RebindKeysPayload(super::RebindKeysPayload), RebindKeysPayload(super::RebindKeysPayload),
#[prost(message, tag = "89")]
ChangeHostFolderPayload(super::ChangeHostFolderPayload),
} }
} }
#[allow(clippy::derive_partial_eq_without_eq)] #[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)] #[derive(Clone, PartialEq, ::prost::Message)]
pub struct ChangeHostFolderPayload {
#[prost(string, tag = "1")]
pub new_host_folder: ::prost::alloc::string::String,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct RebindKeysPayload { pub struct RebindKeysPayload {
#[prost(message, repeated, tag = "1")] #[prost(message, repeated, tag = "1")]
pub keys_to_rebind: ::prost::alloc::vec::Vec<KeyToRebind>, pub keys_to_rebind: ::prost::alloc::vec::Vec<KeyToRebind>,
@ -716,6 +724,7 @@ pub enum CommandName {
LoadNewPlugin = 111, LoadNewPlugin = 111,
RebindKeys = 112, RebindKeys = 112,
ListClients = 113, ListClients = 113,
ChangeHostFolder = 114,
} }
impl CommandName { impl CommandName {
/// String value of the enum field names used in the ProtoBuf definition. /// String value of the enum field names used in the ProtoBuf definition.
@ -840,6 +849,7 @@ impl CommandName {
CommandName::LoadNewPlugin => "LoadNewPlugin", CommandName::LoadNewPlugin => "LoadNewPlugin",
CommandName::RebindKeys => "RebindKeys", CommandName::RebindKeys => "RebindKeys",
CommandName::ListClients => "ListClients", CommandName::ListClients => "ListClients",
CommandName::ChangeHostFolder => "ChangeHostFolder",
} }
} }
/// Creates an enum from field names used in the ProtoBuf definition. /// Creates an enum from field names used in the ProtoBuf definition.
@ -961,6 +971,7 @@ impl CommandName {
"LoadNewPlugin" => Some(Self::LoadNewPlugin), "LoadNewPlugin" => Some(Self::LoadNewPlugin),
"RebindKeys" => Some(Self::RebindKeys), "RebindKeys" => Some(Self::RebindKeys),
"ListClients" => Some(Self::ListClients), "ListClients" => Some(Self::ListClients),
"ChangeHostFolder" => Some(Self::ChangeHostFolder),
_ => None, _ => None,
} }
} }

View file

@ -11,6 +11,7 @@ pub enum PermissionType {
ReadCliPipes = 7, ReadCliPipes = 7,
MessageAndLaunchOtherPlugins = 8, MessageAndLaunchOtherPlugins = 8,
Reconfigure = 9, Reconfigure = 9,
FullHdAccess = 10,
} }
impl PermissionType { impl PermissionType {
/// String value of the enum field names used in the ProtoBuf definition. /// String value of the enum field names used in the ProtoBuf definition.
@ -31,6 +32,7 @@ impl PermissionType {
"MessageAndLaunchOtherPlugins" "MessageAndLaunchOtherPlugins"
} }
PermissionType::Reconfigure => "Reconfigure", PermissionType::Reconfigure => "Reconfigure",
PermissionType::FullHdAccess => "FullHdAccess",
} }
} }
/// Creates an enum from field names used in the ProtoBuf definition. /// Creates an enum from field names used in the ProtoBuf definition.
@ -46,6 +48,7 @@ impl PermissionType {
"ReadCliPipes" => Some(Self::ReadCliPipes), "ReadCliPipes" => Some(Self::ReadCliPipes),
"MessageAndLaunchOtherPlugins" => Some(Self::MessageAndLaunchOtherPlugins), "MessageAndLaunchOtherPlugins" => Some(Self::MessageAndLaunchOtherPlugins),
"Reconfigure" => Some(Self::Reconfigure), "Reconfigure" => Some(Self::Reconfigure),
"FullHdAccess" => Some(Self::FullHdAccess),
_ => None, _ => None,
} }
} }

View file

@ -912,6 +912,8 @@ pub enum Event {
CommandPaneReRun(u32, Context), // u32 - terminal_pane_id, Option<i32> - CommandPaneReRun(u32, Context), // u32 - terminal_pane_id, Option<i32> -
FailedToWriteConfigToDisk(Option<String>), // String -> the file path we failed to write FailedToWriteConfigToDisk(Option<String>), // String -> the file path we failed to write
ListClients(Vec<ClientInfo>), ListClients(Vec<ClientInfo>),
HostFolderChanged(PathBuf), // PathBuf -> new host folder
FailedToChangeHostFolder(Option<String>), // String -> the error we got when changing
} }
#[derive( #[derive(
@ -942,6 +944,7 @@ pub enum Permission {
ReadCliPipes, ReadCliPipes,
MessageAndLaunchOtherPlugins, MessageAndLaunchOtherPlugins,
Reconfigure, Reconfigure,
FullHdAccess,
} }
impl PermissionType { impl PermissionType {
@ -963,6 +966,7 @@ impl PermissionType {
"Send messages to and launch other plugins".to_owned() "Send messages to and launch other plugins".to_owned()
}, },
PermissionType::Reconfigure => "Change Zellij runtime configuration".to_owned(), PermissionType::Reconfigure => "Change Zellij runtime configuration".to_owned(),
PermissionType::FullHdAccess => "Full access to the hard-drive".to_owned(),
} }
} }
} }
@ -1893,4 +1897,5 @@ pub enum PluginCommand {
write_config_to_disk: bool, write_config_to_disk: bool,
}, },
ListClients, ListClients,
ChangeHostFolder(PathBuf),
} }

View file

@ -435,6 +435,7 @@ pub enum PluginContext {
Reconfigure, Reconfigure,
FailedToWriteConfigToDisk, FailedToWriteConfigToDisk,
ListClientsToPlugin, ListClientsToPlugin,
ChangePluginHostDir,
} }
/// Stack call representations corresponding to the different types of [`ClientInstruction`]s. /// Stack call representations corresponding to the different types of [`ClientInstruction`]s.

View file

@ -50,6 +50,8 @@ enum EventType {
CommandPaneReRun = 24; CommandPaneReRun = 24;
FailedToWriteConfigToDisk = 25; FailedToWriteConfigToDisk = 25;
ListClients = 26; ListClients = 26;
HostFolderChanged = 27;
FailedToChangeHostFolder = 28;
} }
message EventNameList { message EventNameList {
@ -81,9 +83,19 @@ message Event {
CommandPaneReRunPayload command_pane_rerun_payload = 21; CommandPaneReRunPayload command_pane_rerun_payload = 21;
FailedToWriteConfigToDiskPayload failed_to_write_config_to_disk_payload = 22; FailedToWriteConfigToDiskPayload failed_to_write_config_to_disk_payload = 22;
ListClientsPayload list_clients_payload = 23; ListClientsPayload list_clients_payload = 23;
HostFolderChangedPayload host_folder_changed_payload = 24;
FailedToChangeHostFolderPayload failed_to_change_host_folder_payload = 25;
} }
} }
message FailedToChangeHostFolderPayload {
optional string error_message = 1;
}
message HostFolderChangedPayload {
string new_host_folder_path = 1;
}
message ListClientsPayload { message ListClientsPayload {
repeated ClientInfo client_info = 1; repeated ClientInfo client_info = 1;
} }

View file

@ -333,6 +333,22 @@ impl TryFrom<ProtobufEvent> for Event {
}, },
_ => Err("Malformed payload for the FailedToWriteConfigToDisk Event"), _ => Err("Malformed payload for the FailedToWriteConfigToDisk Event"),
}, },
Some(ProtobufEventType::HostFolderChanged) => match protobuf_event.payload {
Some(ProtobufEventPayload::HostFolderChangedPayload(
host_folder_changed_payload,
)) => Ok(Event::HostFolderChanged(PathBuf::from(
host_folder_changed_payload.new_host_folder_path,
))),
_ => Err("Malformed payload for the HostFolderChanged Event"),
},
Some(ProtobufEventType::FailedToChangeHostFolder) => match protobuf_event.payload {
Some(ProtobufEventPayload::FailedToChangeHostFolderPayload(
failed_to_change_host_folder_payload,
)) => Ok(Event::FailedToChangeHostFolder(
failed_to_change_host_folder_payload.error_message,
)),
_ => Err("Malformed payload for the FailedToChangeHostFolder Event"),
},
None => Err("Unknown Protobuf Event"), None => Err("Unknown Protobuf Event"),
} }
} }
@ -683,6 +699,20 @@ impl TryFrom<Event> for ProtobufEvent {
.collect(), .collect(),
})), })),
}), }),
Event::HostFolderChanged(new_host_folder_path) => Ok(ProtobufEvent {
name: ProtobufEventType::HostFolderChanged as i32,
payload: Some(event::Payload::HostFolderChangedPayload(
HostFolderChangedPayload {
new_host_folder_path: new_host_folder_path.display().to_string(),
},
)),
}),
Event::FailedToChangeHostFolder(error_message) => Ok(ProtobufEvent {
name: ProtobufEventType::FailedToChangeHostFolder as i32,
payload: Some(event::Payload::FailedToChangeHostFolderPayload(
FailedToChangeHostFolderPayload { error_message },
)),
}),
} }
} }
} }
@ -1228,6 +1258,8 @@ impl TryFrom<ProtobufEventType> for EventType {
ProtobufEventType::CommandPaneReRun => EventType::CommandPaneReRun, ProtobufEventType::CommandPaneReRun => EventType::CommandPaneReRun,
ProtobufEventType::FailedToWriteConfigToDisk => EventType::FailedToWriteConfigToDisk, ProtobufEventType::FailedToWriteConfigToDisk => EventType::FailedToWriteConfigToDisk,
ProtobufEventType::ListClients => EventType::ListClients, ProtobufEventType::ListClients => EventType::ListClients,
ProtobufEventType::HostFolderChanged => EventType::HostFolderChanged,
ProtobufEventType::FailedToChangeHostFolder => EventType::FailedToChangeHostFolder,
}) })
} }
} }
@ -1263,6 +1295,8 @@ impl TryFrom<EventType> for ProtobufEventType {
EventType::CommandPaneReRun => ProtobufEventType::CommandPaneReRun, EventType::CommandPaneReRun => ProtobufEventType::CommandPaneReRun,
EventType::FailedToWriteConfigToDisk => ProtobufEventType::FailedToWriteConfigToDisk, EventType::FailedToWriteConfigToDisk => ProtobufEventType::FailedToWriteConfigToDisk,
EventType::ListClients => ProtobufEventType::ListClients, EventType::ListClients => ProtobufEventType::ListClients,
EventType::HostFolderChanged => ProtobufEventType::HostFolderChanged,
EventType::FailedToChangeHostFolder => ProtobufEventType::FailedToChangeHostFolder,
}) })
} }
} }

View file

@ -127,6 +127,7 @@ enum CommandName {
LoadNewPlugin = 111; LoadNewPlugin = 111;
RebindKeys = 112; RebindKeys = 112;
ListClients = 113; ListClients = 113;
ChangeHostFolder = 114;
} }
message PluginCommand { message PluginCommand {
@ -210,9 +211,14 @@ message PluginCommand {
ReloadPluginPayload reload_plugin_payload = 86; ReloadPluginPayload reload_plugin_payload = 86;
LoadNewPluginPayload load_new_plugin_payload = 87; LoadNewPluginPayload load_new_plugin_payload = 87;
RebindKeysPayload rebind_keys_payload = 88; RebindKeysPayload rebind_keys_payload = 88;
ChangeHostFolderPayload change_host_folder_payload = 89;
} }
} }
message ChangeHostFolderPayload {
string new_host_folder = 1;
}
message RebindKeysPayload { message RebindKeysPayload {
repeated KeyToRebind keys_to_rebind = 1; repeated KeyToRebind keys_to_rebind = 1;
repeated KeyToUnbind keys_to_unbind = 2; repeated KeyToUnbind keys_to_unbind = 2;

View file

@ -4,9 +4,9 @@ pub use super::generated_api::api::{
input_mode::InputMode as ProtobufInputMode, input_mode::InputMode as ProtobufInputMode,
plugin_command::{ plugin_command::{
plugin_command::Payload, BreakPanesToNewTabPayload, BreakPanesToTabWithIndexPayload, plugin_command::Payload, BreakPanesToNewTabPayload, BreakPanesToTabWithIndexPayload,
ClearScreenForPaneIdPayload, CliPipeOutputPayload, CloseTabWithIndexPayload, CommandName, ChangeHostFolderPayload, ClearScreenForPaneIdPayload, CliPipeOutputPayload,
ContextItem, EditScrollbackForPaneWithIdPayload, EnvVariable, ExecCmdPayload, CloseTabWithIndexPayload, CommandName, ContextItem, EditScrollbackForPaneWithIdPayload,
FixedOrPercent as ProtobufFixedOrPercent, EnvVariable, ExecCmdPayload, FixedOrPercent as ProtobufFixedOrPercent,
FixedOrPercentValue as ProtobufFixedOrPercentValue, FixedOrPercentValue as ProtobufFixedOrPercentValue,
FloatingPaneCoordinates as ProtobufFloatingPaneCoordinates, HidePaneWithIdPayload, FloatingPaneCoordinates as ProtobufFloatingPaneCoordinates, HidePaneWithIdPayload,
HttpVerb as ProtobufHttpVerb, IdAndNewName, KeyToRebind, KeyToUnbind, KillSessionsPayload, HttpVerb as ProtobufHttpVerb, IdAndNewName, KeyToRebind, KeyToUnbind, KillSessionsPayload,
@ -1303,6 +1303,14 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
Some(_) => Err("ListClients should have no payload, found a payload"), Some(_) => Err("ListClients should have no payload, found a payload"),
None => Ok(PluginCommand::ListClients), None => Ok(PluginCommand::ListClients),
}, },
Some(CommandName::ChangeHostFolder) => match protobuf_plugin_command.payload {
Some(Payload::ChangeHostFolderPayload(change_host_folder_payload)) => {
Ok(PluginCommand::ChangeHostFolder(PathBuf::from(
change_host_folder_payload.new_host_folder,
)))
},
_ => Err("Mismatched payload for ChangeHostFolder"),
},
None => Err("Unrecognized plugin command"), None => Err("Unrecognized plugin command"),
} }
} }
@ -2130,6 +2138,12 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
name: CommandName::ListClients as i32, name: CommandName::ListClients as i32,
payload: None, payload: None,
}), }),
PluginCommand::ChangeHostFolder(new_host_folder) => Ok(ProtobufPluginCommand {
name: CommandName::ChangeHostFolder as i32,
payload: Some(Payload::ChangeHostFolderPayload(ChangeHostFolderPayload {
new_host_folder: new_host_folder.display().to_string(), // TODO: not accurate?
})),
}),
} }
} }
} }

View file

@ -13,4 +13,5 @@ enum PermissionType {
ReadCliPipes = 7; ReadCliPipes = 7;
MessageAndLaunchOtherPlugins = 8; MessageAndLaunchOtherPlugins = 8;
Reconfigure = 9; Reconfigure = 9;
FullHdAccess = 10;
} }

View file

@ -25,6 +25,7 @@ impl TryFrom<ProtobufPermissionType> for PermissionType {
Ok(PermissionType::MessageAndLaunchOtherPlugins) Ok(PermissionType::MessageAndLaunchOtherPlugins)
}, },
ProtobufPermissionType::Reconfigure => Ok(PermissionType::Reconfigure), ProtobufPermissionType::Reconfigure => Ok(PermissionType::Reconfigure),
ProtobufPermissionType::FullHdAccess => Ok(PermissionType::FullHdAccess),
} }
} }
} }
@ -51,6 +52,7 @@ impl TryFrom<PermissionType> for ProtobufPermissionType {
Ok(ProtobufPermissionType::MessageAndLaunchOtherPlugins) Ok(ProtobufPermissionType::MessageAndLaunchOtherPlugins)
}, },
PermissionType::Reconfigure => Ok(ProtobufPermissionType::Reconfigure), PermissionType::Reconfigure => Ok(ProtobufPermissionType::Reconfigure),
PermissionType::FullHdAccess => Ok(ProtobufPermissionType::FullHdAccess),
} }
} }
} }