From 5e9aebda6f074a6e7fa1c5ff50551f5b8b19dca7 Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Sun, 22 Feb 2026 14:44:40 -0800 Subject: [PATCH] Embed CLI plugin assets and share bundled plugin registration --- Cargo.lock | 1 + crates-cli/yaak-cli/Cargo.toml | 1 + crates-cli/yaak-cli/src/context.rs | 37 ++++++++++++++++++++++-- crates-tauri/yaak-app/src/plugins_ext.rs | 26 +++-------------- crates/yaak-plugins/src/manager.rs | 30 ++++++++++++++++++- 5 files changed, 70 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d27fe7b7..24c736a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10149,6 +10149,7 @@ dependencies = [ "env_logger", "futures", "hex", + "include_dir", "keyring", "log 0.4.29", "oxc_resolver", diff --git a/crates-cli/yaak-cli/Cargo.toml b/crates-cli/yaak-cli/Cargo.toml index 6ef74cb8..17da495f 100644 --- a/crates-cli/yaak-cli/Cargo.toml +++ b/crates-cli/yaak-cli/Cargo.toml @@ -16,6 +16,7 @@ dirs = "6" env_logger = "0.11" futures = "0.3" hex = { workspace = true } +include_dir = "0.7" keyring = { workspace = true, features = ["apple-native", "windows-native", "sync-secret-service"] } log = { workspace = true } rand = "0.8" diff --git a/crates-cli/yaak-cli/src/context.rs b/crates-cli/yaak-cli/src/context.rs index e03cee73..e21dd4c3 100644 --- a/crates-cli/yaak-cli/src/context.rs +++ b/crates-cli/yaak-cli/src/context.rs @@ -1,4 +1,6 @@ use crate::plugin_events::CliPluginEventBridge; +use include_dir::{Dir, include_dir}; +use std::fs; use std::path::{Path, PathBuf}; use std::sync::Arc; use tokio::sync::Mutex; @@ -9,6 +11,13 @@ use yaak_models::query_manager::QueryManager; use yaak_plugins::events::PluginContext; use yaak_plugins::manager::PluginManager; +const EMBEDDED_PLUGIN_RUNTIME: &str = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../../crates-tauri/yaak-app/vendored/plugin-runtime/index.cjs" +)); +static EMBEDDED_VENDORED_PLUGINS: Dir<'_> = + include_dir!("$CARGO_MANIFEST_DIR/../../crates-tauri/yaak-app/vendored/plugins"); + pub struct CliContext { data_dir: PathBuf, query_manager: QueryManager, @@ -33,10 +42,13 @@ impl CliContext { 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"); + let plugin_runtime_main = std::env::var("YAAK_PLUGIN_RUNTIME").map(PathBuf::from).unwrap_or_else(|_| { - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("../../crates-tauri/yaak-app/vendored/plugin-runtime/index.cjs") + prepare_embedded_plugin_runtime(&data_dir) + .expect("Failed to prepare embedded plugin runtime") }); let plugin_manager = Arc::new( @@ -50,6 +62,13 @@ impl CliContext { .await, ); + { + let db = query_manager.connect(); + if let Err(err) = plugin_manager.ensure_bundled_plugins_registered(&db).await { + eprintln!("Warning: Failed to register bundled plugins: {err}"); + } + } + let plugins = query_manager.connect().list_plugins().unwrap_or_default(); if !plugins.is_empty() { let errors = plugin_manager @@ -113,3 +132,17 @@ impl CliContext { } } } + +fn prepare_embedded_plugin_runtime(data_dir: &Path) -> std::io::Result { + let runtime_dir = data_dir.join("vendored").join("plugin-runtime"); + fs::create_dir_all(&runtime_dir)?; + let runtime_main = runtime_dir.join("index.cjs"); + fs::write(&runtime_main, EMBEDDED_PLUGIN_RUNTIME)?; + Ok(runtime_main) +} + +fn prepare_embedded_vendored_plugins(vendored_plugin_dir: &Path) -> std::io::Result<()> { + fs::create_dir_all(vendored_plugin_dir)?; + EMBEDDED_VENDORED_PLUGINS.extract(vendored_plugin_dir)?; + Ok(()) +} diff --git a/crates-tauri/yaak-app/src/plugins_ext.rs b/crates-tauri/yaak-app/src/plugins_ext.rs index bc252e86..6ca1e951 100644 --- a/crates-tauri/yaak-app/src/plugins_ext.rs +++ b/crates-tauri/yaak-app/src/plugins_ext.rs @@ -23,7 +23,6 @@ use tokio::sync::Mutex; use ts_rs::TS; use yaak_api::yaak_api_client; use yaak_models::models::Plugin; -use yaak_models::util::UpdateSource; use yaak_plugins::api::{ PluginNameVersion, PluginSearchResponse, PluginUpdatesResponse, check_plugin_updates, search_plugins, @@ -281,28 +280,11 @@ pub fn init() -> TauriPlugin { ) .await; - // Initialize all plugins after manager is created - let bundled_dirs = manager - .list_bundled_plugin_dirs() - .await - .expect("Failed to list bundled plugins"); - - // Ensure all bundled plugins make it into the database let db = app_handle_clone.db(); - for dir in &bundled_dirs { - if db.get_plugin_by_directory(dir).is_none() { - db.upsert_plugin( - &Plugin { - directory: dir.clone(), - enabled: true, - url: None, - ..Default::default() - }, - &UpdateSource::Background, - ) - .expect("Failed to upsert bundled plugin"); - } - } + manager + .ensure_bundled_plugins_registered(&db) + .await + .expect("Failed to register bundled plugins"); // Get all plugins from database and initialize let plugins = db.list_plugins().expect("Failed to list plugins from database"); diff --git a/crates/yaak-plugins/src/manager.rs b/crates/yaak-plugins/src/manager.rs index 66c854d6..e1399c67 100644 --- a/crates/yaak-plugins/src/manager.rs +++ b/crates/yaak-plugins/src/manager.rs @@ -33,8 +33,9 @@ use tokio::net::TcpListener; use tokio::sync::mpsc::error::TrySendError; use tokio::sync::{Mutex, mpsc, oneshot}; use tokio::time::{Instant, timeout}; +use yaak_models::db_context::DbContext; use yaak_models::models::Plugin; -use yaak_models::util::generate_id; +use yaak_models::util::{UpdateSource, generate_id}; use yaak_templates::error::Error::RenderError; use yaak_templates::error::Result as TemplateResult; @@ -180,6 +181,33 @@ impl PluginManager { read_plugins_dir(&plugins_dir).await } + /// Ensure all bundled plugin directories are present in the plugins table. + /// Returns a list of newly registered plugin directories. + pub async fn ensure_bundled_plugins_registered( + &self, + db: &DbContext<'_>, + ) -> Result> { + let bundled_dirs = self.list_bundled_plugin_dirs().await?; + let mut registered = Vec::new(); + + for dir in bundled_dirs { + if db.get_plugin_by_directory(&dir).is_none() { + db.upsert_plugin( + &Plugin { + directory: dir.clone(), + enabled: true, + url: None, + ..Default::default() + }, + &UpdateSource::Background, + )?; + registered.push(dir); + } + } + + Ok(registered) + } + pub async fn uninstall(&self, plugin_context: &PluginContext, dir: &str) -> Result<()> { let plugin = self.get_plugin_by_dir(dir).await.ok_or(PluginNotFoundErr(dir.to_string()))?; self.remove_plugin(plugin_context, &plugin).await