Don't block window creation on plugin runtime boot (#616)

This commit is contained in:
Gregory Schier
2026-09-01 09:14:50 -07:00
committed by GitHub
parent 8eebee460e
commit d461c982ec
15 changed files with 183 additions and 105 deletions
+2 -3
View File
@@ -3,11 +3,10 @@ use std::collections::BTreeMap;
use crate::PluginContextExt;
use crate::error::Result;
use KeyAndValueRef::{Ascii, Binary};
use tauri::{Manager, Runtime, WebviewWindow};
use tauri::{Runtime, WebviewWindow};
use yaak_grpc::{KeyAndValueRef, MetadataMap};
use yaak_models::models::GrpcRequest;
use yaak_plugins::events::{CallHttpAuthenticationRequest, HttpHeader};
use yaak_plugins::manager::PluginManager;
pub(crate) fn metadata_to_map(metadata: MetadataMap) -> BTreeMap<String, String> {
let mut entries = BTreeMap::new();
@@ -26,7 +25,7 @@ pub(crate) async fn build_metadata<R: Runtime>(
request: &GrpcRequest,
authentication_context_id: &str,
) -> Result<BTreeMap<String, String>> {
let plugin_manager = window.state::<PluginManager>();
let plugin_manager = crate::plugins_ext::plugin_manager(window).await?;
let mut metadata = BTreeMap::new();
// Add the rest of metadata
@@ -14,7 +14,6 @@ use yaak_http::manager::HttpConnectionManager;
use yaak_models::models::{CookieJar, Environment, HttpRequest, HttpResponse, HttpResponseState};
use yaak_models::util::UpdateSource;
use yaak_plugins::events::PluginContext;
use yaak_plugins::manager::PluginManager;
/// Context for managing response state during HTTP transactions.
/// Handles both persisted responses (stored in DB) and ephemeral responses (in-memory only).
@@ -149,7 +148,7 @@ async fn send_http_request_inner<R: Runtime>(
response_ctx: &mut ResponseContext<R>,
) -> Result<SentHttpRequest> {
let app_handle = window.app_handle().clone();
let plugin_manager = Arc::new((*app_handle.state::<PluginManager>()).clone());
let plugin_manager = Arc::new(crate::plugins_ext::plugin_manager(&app_handle).await?);
let encryption_manager = Arc::new((*app_handle.state::<EncryptionManager>()).clone());
let connection_manager = app_handle.state::<HttpConnectionManager>();
let environment_id = environment.map(|e| e.id);
+1 -2
View File
@@ -7,7 +7,6 @@ use tauri::{Manager, Runtime, WebviewWindow};
use yaak::import::{self, PlanImportDataParams};
use yaak_api::{ApiClientKind, yaak_api_client};
use yaak_models::util::{BatchUpsertResult, ImportDestination, ImportPlan};
use yaak_plugins::manager::PluginManager;
pub(crate) async fn import_data<R: Runtime>(
window: &WebviewWindow<R>,
@@ -40,7 +39,7 @@ async fn plan_import_contents<R: Runtime>(
contents: &str,
destination: ImportDestination,
) -> Result<ImportPlan> {
let plugin_manager = window.state::<PluginManager>();
let plugin_manager = crate::plugins_ext::plugin_manager(window).await?;
let query_manager = window.db_manager();
let plugin_context = window.plugin_context();
+15 -9
View File
@@ -45,7 +45,6 @@ use yaak_plugins::events::{
Color, ErrorResponse, FilterResponse, InternalEvent, InternalEventPayload, PluginContext,
RenderPurpose, ShowToastRequest,
};
use yaak_plugins::manager::PluginManager;
use yaak_plugins::template_callback::PluginTemplateCallback;
use yaak_rpc_schema::{AppMetaData, EphemeralHttpResponse};
use yaak_sse::sse::ServerSentEvent;
@@ -250,7 +249,7 @@ async fn cmd_grpc_reflect<R: Runtime>(
let resolved_settings =
app_handle.db().resolve_settings_for_grpc_request(&unrendered_request)?;
let plugin_manager = Arc::new((*app_handle.state::<PluginManager>()).clone());
let plugin_manager = Arc::new(crate::plugins_ext::plugin_manager(&app_handle).await?);
let encryption_manager = Arc::new((*app_handle.state::<EncryptionManager>()).clone());
let req = render_grpc_request(
&resolved_request,
@@ -310,7 +309,7 @@ async fn cmd_grpc_go<R: Runtime>(
let resolved_settings =
app_handle.db().resolve_settings_for_grpc_request(&unrendered_request)?;
let plugin_manager = Arc::new((*app_handle.state::<PluginManager>()).clone());
let plugin_manager = Arc::new(crate::plugins_ext::plugin_manager(&app_handle).await?);
let encryption_manager = Arc::new((*app_handle.state::<EncryptionManager>()).clone());
let request = render_grpc_request(
&resolved_request,
@@ -962,7 +961,6 @@ async fn cmd_format_graphql(text: &str) -> YaakResult<String> {
async fn cmd_http_response_body<R: Runtime>(
window: WebviewWindow<R>,
plugin_manager: State<'_, PluginManager>,
response_id: &str,
filter: Option<&str>,
) -> YaakResult<FilterResponse> {
@@ -977,7 +975,8 @@ async fn cmd_http_response_body<R: Runtime>(
.ok_or(GenericError("Failed to find response body".to_string()))?;
match filter {
Some(filter) if !filter.is_empty() => Ok(plugin_manager
Some(filter) if !filter.is_empty() => Ok(plugins_ext::plugin_manager(&window)
.await?
.filter_data(&window.plugin_context(), filter, &body, content_type)
.await?),
_ => Ok(FilterResponse { content: body, error: None }),
@@ -1450,7 +1449,10 @@ fn safe_uri(endpoint: &str) -> String {
fn monitor_plugin_events<R: Runtime>(app_handle: &AppHandle<R>) {
let app_handle = app_handle.clone();
tauri::async_runtime::spawn(async move {
let plugin_manager: State<'_, PluginManager> = app_handle.state();
let plugin_manager = match plugins_ext::plugin_manager(&app_handle).await {
Ok(pm) => pm,
Err(_) => return, // The runtime failed to boot; there are no events
};
let (rx_id, mut rx) = plugin_manager.subscribe("app").await;
while let Some(event) = rx.recv().await {
@@ -1491,9 +1493,13 @@ fn monitor_plugin_events<R: Runtime>(app_handle: &AppHandle<R>) {
}
};
let plugin_manager: State<'_, PluginManager> = app_handle.state();
if let Err(e) = plugin_manager.reply(&event, &ev).await {
warn!("Failed to reply to plugin manager: {:?}", e)
match plugins_ext::plugin_manager(&app_handle).await {
Ok(pm) => {
if let Err(e) = pm.reply(&event, &ev).await {
warn!("Failed to reply to plugin manager: {:?}", e)
}
}
Err(e) => warn!("Failed to get plugin manager for reply: {e:?}"),
}
});
}
@@ -32,7 +32,6 @@ use yaak_plugins::events::{
ShowToastRequest, TemplateRenderResponse, WindowInfoResponse, WindowNavigateEvent,
WorkspaceInfo,
};
use yaak_plugins::manager::PluginManager;
use yaak_plugins::plugin_handle::PluginHandle;
use yaak_plugins::template_callback::PluginTemplateCallback;
use yaak_tauri_utils::window::WorkspaceWindowTrait;
@@ -205,7 +204,7 @@ async fn handle_host_plugin_request<R: Runtime>(
req.grpc_request.folder_id.as_deref(),
environment_id.as_deref(),
)?;
let plugin_manager = Arc::new((*app_handle.state::<PluginManager>()).clone());
let plugin_manager = Arc::new(crate::plugins_ext::plugin_manager(app_handle).await?);
let encryption_manager = Arc::new((*app_handle.state::<EncryptionManager>()).clone());
let cb = PluginTemplateCallback::new(
plugin_manager,
@@ -231,7 +230,7 @@ async fn handle_host_plugin_request<R: Runtime>(
req.http_request.folder_id.as_deref(),
environment_id.as_deref(),
)?;
let plugin_manager = Arc::new((*app_handle.state::<PluginManager>()).clone());
let plugin_manager = Arc::new(crate::plugins_ext::plugin_manager(app_handle).await?);
let encryption_manager = Arc::new((*app_handle.state::<EncryptionManager>()).clone());
let cb = PluginTemplateCallback::new(
plugin_manager,
@@ -267,7 +266,7 @@ async fn handle_host_plugin_request<R: Runtime>(
folder_id.as_deref(),
environment_id.as_deref(),
)?;
let plugin_manager = Arc::new((*app_handle.state::<PluginManager>()).clone());
let plugin_manager = Arc::new(crate::plugins_ext::plugin_manager(app_handle).await?);
let encryption_manager = Arc::new((*app_handle.state::<EncryptionManager>()).clone());
let cb = PluginTemplateCallback::new(
plugin_manager,
+77 -18
View File
@@ -31,10 +31,43 @@ use yaak_plugins::api::{
use yaak_plugins::events::{Color, PluginContext, ShowToastRequest};
use yaak_plugins::install::{delete_and_uninstall, download_and_install};
use yaak_plugins::manager::PluginManager;
use yaak_plugins::error::Error::PluginErr;
use yaak_plugins::plugin_meta::get_plugin_meta;
static EXITING: AtomicBool = AtomicBool::new(false);
// ============================================================================
// Plugin Manager Handle
// ============================================================================
/// The plugin runtime boots in the background so startup doesn't wait on it.
/// This handle is the only way to reach the manager: [`PluginManagerHandle::get`]
/// resolves once boot completes, so callers can never observe a
/// partially-initialized runtime.
#[derive(Clone)]
pub struct PluginManagerHandle {
rx: tokio::sync::watch::Receiver<Option<std::result::Result<PluginManager, String>>>,
}
impl PluginManagerHandle {
pub async fn get(&self) -> yaak_plugins::error::Result<PluginManager> {
let mut rx = self.rx.clone();
let result = rx
.wait_for(|v| v.is_some())
.await
.map_err(|_| PluginErr("Plugin runtime boot task died".to_string()))?;
result.clone().unwrap().map_err(PluginErr)
}
}
/// Wait for the plugin runtime to finish booting and return the manager.
pub async fn plugin_manager<R: Runtime>(
manager: &impl Manager<R>,
) -> yaak_plugins::error::Result<PluginManager> {
let handle = manager.state::<PluginManagerHandle>().inner().clone();
handle.get().await
}
// ============================================================================
// Plugin Updater
// ============================================================================
@@ -146,7 +179,7 @@ pub async fn cmd_plugins_install<R: Runtime>(
name: &str,
version: Option<String>,
) -> Result<()> {
let plugin_manager = Arc::new((*window.state::<PluginManager>()).clone());
let plugin_manager = Arc::new(plugin_manager(&window).await?);
let app_version = window.app_handle().package_info().version.to_string();
let http_client = yaak_api_client(ApiClientKind::App, &app_version)?;
let query_manager = window.state::<yaak_models::query_manager::QueryManager>();
@@ -167,6 +200,9 @@ pub async fn cmd_plugins_install_from_directory<R: Runtime>(
window: WebviewWindow<R>,
directory: &str,
) -> Result<Plugin> {
// Resolve the manager before writing the row so startup's plugin snapshot
// can't include it and boot it a second time
let plugin_manager = Arc::new(plugin_manager(&window).await?);
let plugin = window.db().upsert_plugin(
&Plugin {
directory: directory.into(),
@@ -178,7 +214,6 @@ pub async fn cmd_plugins_install_from_directory<R: Runtime>(
&UpdateSource::from_window_label(window.label()),
)?;
let plugin_manager = Arc::new((*window.state::<PluginManager>()).clone());
plugin_manager.add_plugin(&window.plugin_context(), &plugin).await?;
Ok(plugin)
@@ -188,7 +223,7 @@ pub async fn cmd_plugins_uninstall<R: Runtime>(
plugin_id: &str,
window: WebviewWindow<R>,
) -> Result<Plugin> {
let plugin_manager = Arc::new((*window.state::<PluginManager>()).clone());
let plugin_manager = Arc::new(plugin_manager(&window).await?);
let query_manager = window.state::<yaak_models::query_manager::QueryManager>();
let plugin_context = window.plugin_context();
Ok(delete_and_uninstall(plugin_manager, &query_manager, &plugin_context, plugin_id).await?)
@@ -217,7 +252,7 @@ pub async fn cmd_plugins_update_all<R: Runtime>(
return Ok(Vec::new());
}
let plugin_manager = Arc::new((*window.state::<PluginManager>()).clone());
let plugin_manager = Arc::new(plugin_manager(&window).await?);
let query_manager = window.state::<yaak_models::query_manager::QueryManager>();
let plugin_context = window.plugin_context();
@@ -300,20 +335,38 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
let query_manager =
app_handle.state::<yaak_models::query_manager::QueryManager>().inner().clone();
// Create plugin manager asynchronously
// Boot the plugin runtime in the background so the window shows
// immediately. Everything that needs plugins resolves the handle,
// which waits for this task to finish.
let (tx, rx) = tokio::sync::watch::channel(None);
app_handle.manage(PluginManagerHandle { rx });
let app_handle_clone = app_handle.clone();
tauri::async_runtime::block_on(async move {
let manager = PluginManager::new(
vendored_plugin_dir,
installed_plugin_dir,
node_bin_path,
plugin_runtime_main,
&query_manager,
&PluginContext::new_empty(),
dev_mode,
tauri::async_runtime::spawn(async move {
let result = tokio::time::timeout(
Duration::from_secs(60),
PluginManager::new(
vendored_plugin_dir,
installed_plugin_dir,
node_bin_path,
plugin_runtime_main,
&query_manager,
&PluginContext::new_empty(),
dev_mode,
),
)
.await
.expect("Failed to start plugin runtime");
.unwrap_or_else(|_| Err(yaak_plugins::error::Error::PluginErr(
"Timed out starting the plugin runtime".to_string(),
)));
let manager = match result {
Ok(manager) => manager,
Err(e) => {
error!("Failed to start plugin runtime: {e:?}");
let _ = tx.send(Some(Err(e.to_string())));
return;
}
};
// Surface unexpected runtime crashes to the user
let mut crash_rx = manager.runtime_crash_rx();
@@ -339,7 +392,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
}
});
app_handle_clone.manage(manager);
let _ = tx.send(Some(Ok(manager)));
});
let plugin_updater = PluginUpdater::new();
@@ -355,8 +408,14 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
api.prevent_exit();
tauri::async_runtime::block_on(async move {
info!("Exiting plugin runtime due to app exit");
let manager: State<PluginManager> = app.state();
manager.terminate().await;
// Bound the wait in case the exit comes while boot is still
// in flight
let get_manager = plugin_manager(app);
if let Ok(Ok(manager)) =
tokio::time::timeout(Duration::from_secs(5), get_manager).await
{
manager.terminate().await;
}
app.exit(0);
});
}
+46 -35
View File
@@ -109,10 +109,11 @@ impl<R: Runtime> Host for ClientCtx<R> {
}
impl<R: Runtime> ClientCtx<R> {
/// The plugin runtime this window talks to. Only the `PluginHost` impl
/// below uses it; everything else goes through the trait.
fn pm(&self) -> State<'_, PluginManager> {
self.window.state::<PluginManager>()
/// The plugin runtime this window talks to, once it finishes booting.
/// Only the `PluginHost` impl below uses it; everything else goes through
/// the trait.
async fn pm(&self) -> yaak_plugins::error::Result<PluginManager> {
crate::plugins_ext::plugin_manager(&self.window).await
}
}
@@ -122,35 +123,40 @@ impl<R: Runtime> ClientCtx<R> {
/// providing them.
impl<R: Runtime> PluginHost for ClientCtx<R> {
async fn loaded_plugin_metadata(&self, directory: &str) -> Option<PluginMetadata> {
let handle = self.pm().get_plugin_by_dir(directory).await?;
let handle = self.pm().await.ok()?.get_plugin_by_dir(directory).await?;
Some(handle.info())
}
async fn take_plugin_init_errors(&self) -> Vec<(String, String)> {
self.pm().take_init_errors().await
match self.pm().await {
Ok(pm) => pm.take_init_errors().await,
Err(_) => Vec::new(),
}
}
async fn resolve_plugins(&self, plugins: Vec<Plugin>) -> Vec<Plugin> {
self.pm().resolve_plugins_for_runtime_from_db(plugins).await
match self.pm().await {
Ok(pm) => pm.resolve_plugins_for_runtime_from_db(plugins).await,
Err(_) => plugins,
}
}
fn template_callback(&self, purpose: RenderPurpose) -> impl TemplateCallback {
PluginTemplateCallback::new(
Arc::new((*self.pm()).clone()),
async fn template_callback(
&self,
purpose: RenderPurpose,
) -> yaak_commands::Result<impl TemplateCallback> {
Ok(PluginTemplateCallback::new(
Arc::new(self.pm().await?),
Arc::new(self.encryption_manager().clone()),
&self.plugin_context(),
purpose,
)
))
}
async fn template_function_summaries(
&self,
) -> yaak_commands::Result<Vec<GetTemplateFunctionSummaryResponse>> {
Ok(self
.window
.state::<PluginManager>()
.get_template_function_summaries(&self.plugin_context())
.await?)
Ok(self.pm().await?.get_template_function_summaries(&self.plugin_context()).await?)
}
async fn template_function_config(
@@ -160,81 +166,81 @@ impl<R: Runtime> PluginHost for ClientCtx<R> {
model_id: &str,
) -> yaak_commands::Result<GetTemplateFunctionConfigResponse> {
Ok(self
.window
.state::<PluginManager>()
.pm()
.await?
.get_template_function_config(&self.plugin_context(), function_name, values, model_id)
.await?)
}
async fn themes(&self) -> yaak_commands::Result<Vec<GetThemesResponse>> {
Ok(self.pm().get_themes(&self.plugin_context()).await?)
Ok(self.pm().await?.get_themes(&self.plugin_context()).await?)
}
async fn http_request_actions(
&self,
) -> yaak_commands::Result<Vec<GetHttpRequestActionsResponse>> {
Ok(self.pm().get_http_request_actions(&self.plugin_context()).await?)
Ok(self.pm().await?.get_http_request_actions(&self.plugin_context()).await?)
}
async fn websocket_request_actions(
&self,
) -> yaak_commands::Result<Vec<GetWebsocketRequestActionsResponse>> {
Ok(self.pm().get_websocket_request_actions(&self.plugin_context()).await?)
Ok(self.pm().await?.get_websocket_request_actions(&self.plugin_context()).await?)
}
async fn grpc_request_actions(
&self,
) -> yaak_commands::Result<Vec<GetGrpcRequestActionsResponse>> {
Ok(self.pm().get_grpc_request_actions(&self.plugin_context()).await?)
Ok(self.pm().await?.get_grpc_request_actions(&self.plugin_context()).await?)
}
async fn workspace_actions(&self) -> yaak_commands::Result<Vec<GetWorkspaceActionsResponse>> {
Ok(self.pm().get_workspace_actions(&self.plugin_context()).await?)
Ok(self.pm().await?.get_workspace_actions(&self.plugin_context()).await?)
}
async fn folder_actions(&self) -> yaak_commands::Result<Vec<GetFolderActionsResponse>> {
Ok(self.pm().get_folder_actions(&self.plugin_context()).await?)
Ok(self.pm().await?.get_folder_actions(&self.plugin_context()).await?)
}
async fn call_http_request_action(
&self,
req: CallHttpRequestActionRequest,
) -> yaak_commands::Result<()> {
Ok(self.pm().call_http_request_action(&self.plugin_context(), req).await?)
Ok(self.pm().await?.call_http_request_action(&self.plugin_context(), req).await?)
}
async fn call_grpc_request_action(
&self,
req: CallGrpcRequestActionRequest,
) -> yaak_commands::Result<()> {
Ok(self.pm().call_grpc_request_action(&self.plugin_context(), req).await?)
Ok(self.pm().await?.call_grpc_request_action(&self.plugin_context(), req).await?)
}
async fn call_websocket_request_action(
&self,
req: CallWebsocketRequestActionRequest,
) -> yaak_commands::Result<()> {
Ok(self.pm().call_websocket_request_action(&self.plugin_context(), req).await?)
Ok(self.pm().await?.call_websocket_request_action(&self.plugin_context(), req).await?)
}
async fn call_workspace_action(
&self,
req: CallWorkspaceActionRequest,
) -> yaak_commands::Result<()> {
Ok(self.pm().call_workspace_action(&self.plugin_context(), req).await?)
Ok(self.pm().await?.call_workspace_action(&self.plugin_context(), req).await?)
}
async fn call_folder_action(
&self,
req: CallFolderActionRequest,
) -> yaak_commands::Result<()> {
Ok(self.pm().call_folder_action(&self.plugin_context(), req).await?)
Ok(self.pm().await?.call_folder_action(&self.plugin_context(), req).await?)
}
async fn http_authentication_summaries(
&self,
) -> yaak_commands::Result<Vec<GetHttpAuthenticationSummaryResponse>> {
let results = self.pm().get_http_authentication_summaries(&self.plugin_context()).await?;
let results = self.pm().await?.get_http_authentication_summaries(&self.plugin_context()).await?;
Ok(results.into_iter().map(|(_, a)| a).collect())
}
@@ -246,6 +252,7 @@ impl<R: Runtime> PluginHost for ClientCtx<R> {
) -> yaak_commands::Result<GetHttpAuthenticationConfigResponse> {
Ok(self
.pm()
.await?
.get_http_authentication_config(&self.plugin_context(), auth_name, values, model_id)
.await?)
}
@@ -259,6 +266,7 @@ impl<R: Runtime> PluginHost for ClientCtx<R> {
) -> yaak_commands::Result<()> {
Ok(self
.pm()
.await?
.call_http_authentication_action(
&self.plugin_context(),
auth_name,
@@ -270,15 +278,18 @@ impl<R: Runtime> PluginHost for ClientCtx<R> {
}
async fn import_data(&self, content: &str) -> yaak_commands::Result<ImportResponse> {
Ok(self.pm().import_data(&self.plugin_context(), content).await?)
Ok(self.pm().await?.import_data(&self.plugin_context(), content).await?)
}
async fn reload_plugins(&self, plugins: Vec<Plugin>) -> Vec<(String, String)> {
self.pm().initialize_all_plugins(plugins, &self.plugin_context()).await
match self.pm().await {
Ok(pm) => pm.initialize_all_plugins(plugins, &self.plugin_context()).await,
Err(e) => vec![("*".to_string(), e.to_string())],
}
}
async fn encrypt_secure_template(&self, template: &str) -> yaak_commands::Result<String> {
let plugin_manager = Arc::new((*self.pm()).clone());
let plugin_manager = Arc::new(self.pm().await?);
let encryption_manager = Arc::new(self.encryption_manager().clone());
Ok(encrypt_secure_template_function(
plugin_manager,
@@ -422,7 +433,7 @@ async fn cmd_format_graphql<R: Runtime>(_ctx: ClientCtx<R>, req: CmdFormatGraphq
}
async fn cmd_http_response_body<R: Runtime>(ctx: ClientCtx<R>, req: CmdHttpResponseBodyReq) -> Result<FilterResponse> {
Ok(crate::cmd_http_response_body(ctx.window.clone(), ctx.window.app_handle().state::<PluginManager>(), &req.response_id, req.filter.as_deref()).await?)
Ok(crate::cmd_http_response_body(ctx.window.clone(), &req.response_id, req.filter.as_deref()).await?)
}
async fn cmd_http_response_body_path<R: Runtime>(ctx: ClientCtx<R>, req: CmdHttpResponseBodyPathReq) -> Result<Option<String>> {
@@ -817,7 +828,7 @@ async fn cmd_ws_close<R: Runtime>(ctx: ClientCtx<R>, req: CmdWsCloseReq) -> Resu
}
async fn cmd_ws_connect<R: Runtime>(ctx: ClientCtx<R>, req: CmdWsConnectReq) -> Result<WebsocketConnection> {
Ok(crate::ws_ext::cmd_ws_connect(&req.request_id, req.environment_id.as_deref(), req.cookie_jar_id.as_deref(), ctx.window.app_handle().clone(), ctx.window.clone(), ctx.window.app_handle().state::<PluginManager>(), ctx.window.app_handle().state::<Mutex<WebsocketManager>>()).await?)
Ok(crate::ws_ext::cmd_ws_connect(&req.request_id, req.environment_id.as_deref(), req.cookie_jar_id.as_deref(), ctx.window.app_handle().clone(), ctx.window.clone(), ctx.window.app_handle().state::<Mutex<WebsocketManager>>()).await?)
}
async fn cmd_plugins_search<R: Runtime>(ctx: ClientCtx<R>, req: CmdPluginsSearchReq) -> Result<PluginSearchResponse> {
+3 -3
View File
@@ -14,7 +14,6 @@ use tokio::task::block_in_place;
use tokio::time::sleep;
use ts_rs::TS;
use yaak_models::util::generate_id;
use yaak_plugins::manager::PluginManager;
use url::Url;
use yaak_api::get_system_proxy_url;
@@ -98,8 +97,9 @@ impl YaakUpdater {
block_in_place(|| {
tauri::async_runtime::block_on(async move {
info!("Shutting down plugin manager before update");
let plugin_manager = w.state::<PluginManager>();
plugin_manager.terminate().await;
if let Ok(plugin_manager) = crate::plugins_ext::plugin_manager(&w).await {
plugin_manager.terminate().await;
}
});
});
})
@@ -12,7 +12,6 @@ use yaak_api::{ApiClientKind, yaak_api_client};
use yaak_models::util::generate_id;
use yaak_plugins::events::{Color, ShowToastRequest};
use yaak_plugins::install::download_and_install;
use yaak_plugins::manager::PluginManager;
pub(crate) async fn handle_deep_link<R: Runtime>(
app_handle: &AppHandle<R>,
@@ -44,7 +43,7 @@ pub(crate) async fn handle_deep_link<R: Runtime>(
return Ok(());
}
let plugin_manager = Arc::new((*window.state::<PluginManager>()).clone());
let plugin_manager = Arc::new(crate::plugins_ext::plugin_manager(window).await?);
let query_manager = app_handle.db_manager();
let app_version = app_handle.package_info().version.to_string();
let http_client = yaak_api_client(ApiClientKind::App, &app_version)?;
+2 -4
View File
@@ -22,7 +22,6 @@ use yaak_models::models::{
};
use yaak_models::util::UpdateSource;
use yaak_plugins::events::{CallHttpAuthenticationRequest, HttpHeader, RenderPurpose};
use yaak_plugins::manager::PluginManager;
use yaak_plugins::template_callback::PluginTemplateCallback;
use yaak_templates::strip_json_comments::maybe_strip_json_comments;
use yaak_templates::{RenderErrorBehavior, RenderOptions};
@@ -77,7 +76,7 @@ async fn send_websocket_message<R: Runtime>(
)?;
let (resolved_request, _auth_context_id) =
resolve_websocket_request(&window.db(), &unrendered_request)?;
let plugin_manager = Arc::new((*app_handle.state::<PluginManager>()).clone());
let plugin_manager = Arc::new(crate::plugins_ext::plugin_manager(app_handle).await?);
let encryption_manager = Arc::new((*app_handle.state::<EncryptionManager>()).clone());
let request = render_websocket_request(
&resolved_request,
@@ -142,7 +141,6 @@ pub async fn cmd_ws_connect<R: Runtime>(
cookie_jar_id: Option<&str>,
app_handle: AppHandle<R>,
window: WebviewWindow<R>,
_plugin_manager: State<'_, PluginManager>,
ws_manager: State<'_, Mutex<WebsocketManager>>,
) -> Result<WebsocketConnection> {
let unrendered_request = app_handle.db().get_websocket_request(request_id)?;
@@ -156,7 +154,7 @@ pub async fn cmd_ws_connect<R: Runtime>(
let settings = app_handle.db().get_settings();
let (resolved_request, auth_context_id) =
resolve_websocket_request(&window.db(), &unrendered_request)?;
let plugin_manager = Arc::new((*app_handle.state::<PluginManager>()).clone());
let plugin_manager = Arc::new(crate::plugins_ext::plugin_manager(&app_handle).await?);
let encryption_manager = Arc::new((*app_handle.state::<EncryptionManager>()).clone());
let request = render_websocket_request(
&resolved_request,
+6 -1
View File
@@ -121,7 +121,12 @@ pub trait PluginHost: Host {
/// a render — the variables come from the environment chain, which is an
/// ordinary database read — so handing back the callback keeps the rest of
/// rendering shared instead of pushing whole commands behind this trait.
fn template_callback(&self, purpose: RenderPurpose) -> impl TemplateCallback;
/// Async so hosts that finish booting their plugin runtime in the
/// background can wait for it here.
fn template_callback(
&self,
purpose: RenderPurpose,
) -> impl Future<Output = crate::Result<impl TemplateCallback>>;
/// Every template function the installed plugins expose, for the
/// autocomplete menu.
+1 -1
View File
@@ -66,7 +66,7 @@ pub(crate) async fn render_form_values<H: PluginHost>(
let environment_chain =
host.db().resolve_environments(&workspace_id, folder_id.as_deref(), environment_id)?;
let cb = host.template_callback(purpose);
let cb = host.template_callback(purpose).await?;
let rendered =
render_json_value(serde_json::to_value(&values)?, environment_chain, &cb, options).await?;
+2 -2
View File
@@ -21,7 +21,7 @@ pub async fn cmd_render_template<H: PluginHost>(
) -> Result<String> {
let environment_chain =
host.db().resolve_environments(&req.workspace_id, None, req.environment_id.as_deref())?;
let cb = host.template_callback(req.purpose.unwrap_or(RenderPurpose::Preview));
let cb = host.template_callback(req.purpose.unwrap_or(RenderPurpose::Preview)).await?;
let options = RenderOptions {
// A preview that throws would show the user an error where they expect
// to see the value so far, so callers rendering *into the UI* ask for
@@ -41,7 +41,7 @@ pub async fn cmd_template_tokens_to_string<H: PluginHost>(
host: H,
req: CmdTemplateTokensToStringReq,
) -> Result<String> {
let cb = host.template_callback(RenderPurpose::Preview);
let cb = host.template_callback(RenderPurpose::Preview).await?;
Ok(transform_args(req.tokens, &cb)?.to_string())
}
+5 -2
View File
@@ -250,8 +250,11 @@ impl PluginHost for SingleThreadedHost {
Err(yaak_commands::Error::Generic("no plugin runtime on this host".into()))
}
fn template_callback(&self, _purpose: RenderPurpose) -> impl TemplateCallback {
NoTemplateFunctions
async fn template_callback(
&self,
_purpose: RenderPurpose,
) -> yaak_commands::Result<impl TemplateCallback> {
Ok(NoTemplateFunctions)
}
async fn template_function_summaries(
+18 -17
View File
@@ -187,24 +187,25 @@ impl PluginManager {
}
let bundled_dirs = plugin_manager.list_bundled_plugin_dirs().await?;
let db = query_manager.connect();
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,
source: PluginSource::Bundled,
..Default::default()
},
&UpdateSource::Background,
)?;
// Scope the db connection so the future stays Send across the await below
let plugins = {
let db = query_manager.connect();
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,
source: PluginSource::Bundled,
..Default::default()
},
&UpdateSource::Background,
)?;
}
}
}
let plugins = db.list_plugins()?;
drop(db);
db.list_plugins()?
};
let init_errors = plugin_manager.initialize_all_plugins(plugins, plugin_context).await;
if !init_errors.is_empty() {