fix: wrap MCP/error watcher startup in catch_unwind — never crash app
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 3m1s

The MCP server and error watcher are nice-to-have services that were
crashing the app on startup. Wrapped in catch_unwind + error handling
so a failure in these subsystems logs an error instead of taking down
the entire application.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell 2026-03-25 00:30:49 -04:00
parent 6f26822b85
commit c507c515ef

View File

@ -68,6 +68,10 @@ impl AppState {
}) })
} }
pub fn clone_services(&self) -> (SshService, rdp::RdpService, SftpService, ScrollbackRegistry, std::sync::Arc<ErrorWatcher>) {
(self.ssh.clone(), self.rdp.clone(), self.sftp.clone(), self.scrollback.clone(), self.error_watcher.clone())
}
pub fn is_first_run(&self) -> bool { pub fn is_first_run(&self) -> bool {
self.settings.get("vault_salt").unwrap_or_default().is_empty() self.settings.get("vault_salt").unwrap_or_default().is_empty()
} }
@ -104,26 +108,27 @@ pub fn run() {
} }
} }
// Start MCP HTTP server for bridge binary communication // Start MCP and error watcher — completely non-fatal.
// These are nice-to-have services that must never crash the app.
{ {
use tauri::Manager; use tauri::Manager;
let state = app.state::<AppState>(); if let Ok(state) = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
// Start error watcher background task app.state::<AppState>().inner().clone_services()
let watcher_clone = state.error_watcher.clone(); })) {
let scrollback_for_watcher = state.scrollback.clone(); let (ssh, rdp, sftp, scrollback, watcher) = state;
let app_for_watcher = app.handle().clone();
mcp::error_watcher::start_error_watcher(watcher_clone, scrollback_for_watcher, app_for_watcher); let app_handle = app.handle().clone();
mcp::error_watcher::start_error_watcher(watcher, scrollback.clone(), app_handle);
let ssh_clone = state.ssh.clone();
let rdp_clone = state.rdp.clone();
let sftp_clone = state.sftp.clone();
let scrollback_clone = state.scrollback.clone();
tauri::async_runtime::spawn(async move { tauri::async_runtime::spawn(async move {
match mcp::server::start_mcp_server(ssh_clone, rdp_clone, sftp_clone, scrollback_clone).await { match mcp::server::start_mcp_server(ssh, rdp, sftp, scrollback).await {
Ok(port) => log::info!("MCP server started on localhost:{}", port), Ok(port) => log::info!("MCP server started on localhost:{}", port),
Err(e) => log::error!("Failed to start MCP server: {}", e), Err(e) => log::error!("Failed to start MCP server: {}", e),
} }
}); });
} else {
log::error!("MCP/error watcher startup failed — continuing without MCP");
}
} }
Ok(()) Ok(())