diff --git a/crates-cli/yaak-cli/src/context.rs b/crates-cli/yaak-cli/src/context.rs index af3753f41..fabe9b04a 100644 --- a/crates-cli/yaak-cli/src/context.rs +++ b/crates-cli/yaak-cli/src/context.rs @@ -38,12 +38,16 @@ impl CliContext { let encryption_manager = Arc::new(EncryptionManager::new(query_manager.clone(), app_id)); let plugin_manager = if with_plugins { - let vendored_plugin_dir = data_dir.join("vendored-plugins"); + let embedded_vendored_plugin_dir = data_dir.join("vendored-plugins"); + let bundled_plugin_dir = + resolve_bundled_plugin_dir_for_cli(&embedded_vendored_plugin_dir); let installed_plugin_dir = data_dir.join("installed-plugins"); let node_bin_path = PathBuf::from("node"); - prepare_embedded_vendored_plugins(&vendored_plugin_dir) - .expect("Failed to prepare bundled plugins"); + if bundled_plugin_dir == embedded_vendored_plugin_dir { + prepare_embedded_vendored_plugins(&embedded_vendored_plugin_dir) + .expect("Failed to prepare bundled plugins"); + } let plugin_runtime_main = std::env::var("YAAK_PLUGIN_RUNTIME").map(PathBuf::from).unwrap_or_else(|_| { @@ -52,13 +56,13 @@ impl CliContext { }); match PluginManager::new( - vendored_plugin_dir, + bundled_plugin_dir, + embedded_vendored_plugin_dir, installed_plugin_dir, node_bin_path, plugin_runtime_main, &query_manager, &PluginContext::new_empty(), - false, ) .await { @@ -131,3 +135,20 @@ fn prepare_embedded_vendored_plugins(vendored_plugin_dir: &Path) -> std::io::Res EMBEDDED_VENDORED_PLUGINS.extract(vendored_plugin_dir)?; Ok(()) } + +fn resolve_bundled_plugin_dir_for_cli(embedded_vendored_plugin_dir: &Path) -> PathBuf { + if !cfg!(debug_assertions) { + return embedded_vendored_plugin_dir.to_path_buf(); + } + + let plugins_dir = match std::env::current_dir() { + Ok(cwd) => cwd.join("plugins"), + Err(_) => return embedded_vendored_plugin_dir.to_path_buf(), + }; + + if !plugins_dir.is_dir() { + return embedded_vendored_plugin_dir.to_path_buf(); + } + + plugins_dir.canonicalize().unwrap_or(plugins_dir) +} diff --git a/crates-tauri/yaak-app/src/plugins_ext.rs b/crates-tauri/yaak-app/src/plugins_ext.rs index ced6bca85..7bcf822b0 100644 --- a/crates-tauri/yaak-app/src/plugins_ext.rs +++ b/crates-tauri/yaak-app/src/plugins_ext.rs @@ -10,6 +10,7 @@ use crate::error::Result; use crate::models_ext::QueryManagerExt; use log::{error, info, warn}; use serde::Serialize; +use std::path::PathBuf; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use std::time::{Duration, Instant}; @@ -243,6 +244,11 @@ pub fn init() -> TauriPlugin { .path() .resolve("vendored/plugins", BaseDirectory::Resource) .expect("failed to resolve plugin directory resource"); + let bundled_plugin_dir = if is_dev() { + resolve_workspace_plugins_dir().unwrap_or_else(|| vendored_plugin_dir.clone()) + } else { + vendored_plugin_dir.clone() + }; let installed_plugin_dir = app_handle .path() @@ -266,7 +272,6 @@ pub fn init() -> TauriPlugin { .expect("failed to resolve plugin runtime") .join("index.cjs"); - let dev_mode = is_dev(); let query_manager = app_handle.state::().inner().clone(); @@ -274,13 +279,13 @@ pub fn init() -> TauriPlugin { let app_handle_clone = app_handle.clone(); tauri::async_runtime::block_on(async move { let manager = PluginManager::new( + bundled_plugin_dir, vendored_plugin_dir, installed_plugin_dir, node_bin_path, plugin_runtime_main, &query_manager, &PluginContext::new_empty(), - dev_mode, ) .await .expect("Failed to initialize plugins"); @@ -322,3 +327,11 @@ pub fn init() -> TauriPlugin { }) .build() } + +fn resolve_workspace_plugins_dir() -> Option { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../..") + .join("plugins") + .canonicalize() + .ok() +} diff --git a/crates/yaak-plugins/src/manager.rs b/crates/yaak-plugins/src/manager.rs index d7b30a08d..f7e6f2abc 100644 --- a/crates/yaak-plugins/src/manager.rs +++ b/crates/yaak-plugins/src/manager.rs @@ -24,7 +24,6 @@ use crate::plugin_handle::PluginHandle; use crate::server_ws::PluginRuntimeServerWebsocket; use log::{error, info, warn}; use std::collections::HashMap; -use std::env; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::time::Duration; @@ -46,9 +45,9 @@ pub struct PluginManager { kill_tx: tokio::sync::watch::Sender, killed_rx: Arc>>>, ws_service: Arc, + bundled_plugin_dir: PathBuf, vendored_plugin_dir: PathBuf, pub(crate) installed_plugin_dir: PathBuf, - dev_mode: bool, } /// Callback for plugin initialization events (e.g., toast notifications) @@ -58,21 +57,21 @@ impl PluginManager { /// Create a new PluginManager with the given paths. /// /// # Arguments + /// * `bundled_plugin_dir` - Directory to scan for bundled plugins /// * `vendored_plugin_dir` - Path to vendored plugins directory /// * `installed_plugin_dir` - Path to installed plugins directory /// * `node_bin_path` - Path to the yaaknode binary /// * `plugin_runtime_main` - Path to the plugin runtime index.cjs /// * `query_manager` - Query manager for bundled plugin registration and loading /// * `plugin_context` - Context to use while initializing plugins - /// * `dev_mode` - Whether the app is in dev mode (affects plugin loading) pub async fn new( + bundled_plugin_dir: PathBuf, vendored_plugin_dir: PathBuf, installed_plugin_dir: PathBuf, node_bin_path: PathBuf, plugin_runtime_main: PathBuf, query_manager: &QueryManager, plugin_context: &PluginContext, - dev_mode: bool, ) -> Result { let (events_tx, mut events_rx) = mpsc::channel(2048); let (kill_server_tx, kill_server_rx) = tokio::sync::watch::channel(false); @@ -89,9 +88,9 @@ impl PluginManager { ws_service: Arc::new(ws_service.clone()), kill_tx: kill_server_tx, killed_rx: Arc::new(Mutex::new(Some(killed_rx))), + bundled_plugin_dir, vendored_plugin_dir, installed_plugin_dir, - dev_mode, }; // Forward events to subscribers @@ -192,25 +191,11 @@ impl PluginManager { Ok(plugin_manager) } - /// Get the vendored plugin directory path (resolves dev mode path if applicable) - pub fn get_plugins_dir(&self) -> PathBuf { - if self.dev_mode { - // Use plugins directly for easy development - // Tauri runs from crates-tauri/yaak-app/, so go up two levels to reach project root - env::current_dir() - .map(|cwd| cwd.join("../../plugins").canonicalize().unwrap()) - .unwrap_or_else(|_| self.vendored_plugin_dir.clone()) - } else { - self.vendored_plugin_dir.clone() - } - } - /// Read plugin directories from disk and return their paths. /// This is useful for discovering bundled plugins. pub async fn list_bundled_plugin_dirs(&self) -> Result> { - let plugins_dir = self.get_plugins_dir(); - info!("Loading bundled plugins from {plugins_dir:?}"); - read_plugins_dir(&plugins_dir).await + info!("Loading bundled plugins from {:?}", self.bundled_plugin_dir); + read_plugins_dir(&self.bundled_plugin_dir).await } pub async fn uninstall(&self, plugin_context: &PluginContext, dir: &str) -> Result<()> {