diff --git a/app/src-tauri/Cargo.lock b/app/src-tauri/Cargo.lock index 7f9afd923..397255d70 100644 --- a/app/src-tauri/Cargo.lock +++ b/app/src-tauri/Cargo.lock @@ -41,6 +41,7 @@ dependencies = [ "tauri-plugin-global-shortcut", "tauri-plugin-notification", "tauri-plugin-opener", + "tauri-plugin-single-instance", "tauri-plugin-updater", "tauri-runtime-cef", "tempfile", @@ -7261,6 +7262,20 @@ dependencies = [ "zbus", ] +[[package]] +name = "tauri-plugin-single-instance" +version = "2.4.0" +source = "git+https://github.com/tauri-apps/plugins-workspace?rev=c6561ab6b4f9e7f650d4fc8c53fd8acc9b65b9b2#c6561ab6b4f9e7f650d4fc8c53fd8acc9b65b9b2" +dependencies = [ + "serde", + "serde_json", + "tauri", + "thiserror 2.0.18", + "tracing", + "windows-sys 0.60.2", + "zbus", +] + [[package]] name = "tauri-plugin-updater" version = "2.10.1" diff --git a/app/src-tauri/Cargo.toml b/app/src-tauri/Cargo.toml index e5fb5a136..299781891 100644 --- a/app/src-tauri/Cargo.toml +++ b/app/src-tauri/Cargo.toml @@ -46,6 +46,12 @@ tauri-plugin-deep-link = "2.0.0" tauri-plugin-global-shortcut = "2" tauri-plugin-notification = { path = "vendor/tauri-plugin-notification" } tauri-plugin-opener = "2" +# Prevents a second launch from racing into CEF init and hitting the +# `cef::initialize(...) != 1` cache-lock panic seen in production +# (Sentry OPENHUMAN-TAURI-A). The plugin acquires a per-identifier +# lock before any tauri::Builder work happens, so the secondary +# process exits cleanly after handing its argv to the primary. +tauri-plugin-single-instance = "2" # Auto-update for the Tauri shell itself. The core sidecar already has its own # updater (see `core_update.rs`); this plugin handles the .app/.exe/.AppImage # bundle. Both are needed because shipping a new RPC method requires both @@ -188,6 +194,7 @@ tauri-plugin = { path = "vendor/tauri-cef/crates/tauri-plugin" } tauri-plugin-opener = { git = "https://github.com/tauri-apps/plugins-workspace", rev = "c6561ab6b4f9e7f650d4fc8c53fd8acc9b65b9b2" } tauri-plugin-deep-link = { git = "https://github.com/tauri-apps/plugins-workspace", rev = "c6561ab6b4f9e7f650d4fc8c53fd8acc9b65b9b2" } tauri-plugin-global-shortcut = { git = "https://github.com/tauri-apps/plugins-workspace", rev = "c6561ab6b4f9e7f650d4fc8c53fd8acc9b65b9b2" } +tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", rev = "c6561ab6b4f9e7f650d4fc8c53fd8acc9b65b9b2" } tauri-plugin-notification = { path = "vendor/tauri-plugin-notification" } [dev-dependencies] diff --git a/app/src-tauri/src/lib.rs b/app/src-tauri/src/lib.rs index 968ee7ebd..7b15471b4 100644 --- a/app/src-tauri/src/lib.rs +++ b/app/src-tauri/src/lib.rs @@ -1362,6 +1362,31 @@ pub fn run() { }; let builder = builder + // Single-instance guard — MUST be the first plugin registered so the + // secondary-process exit path triggers before any other plugin setup + // (and before `Builder::build()` reaches `CefRuntime::init`). Without + // this, launching a second instance races into CEF init while the + // primary still holds the cache lock; `cef::initialize` returns 0 and + // the vendored runtime asserts (Sentry OPENHUMAN-TAURI-A — 442 events + // across Win10/11 + Linux, all releases). The callback receives the + // secondary's argv/cwd; we forward deep-link args and focus the main + // window. Deep-link payloads stay handled by `tauri-plugin-deep-link` + // — we just need to wake the primary so it observes them. + .plugin(tauri_plugin_single_instance::init( + |app: &AppHandle, args, cwd| { + // Don't log raw argv/cwd: deep-link callbacks (OAuth codes, + // magic links) can carry auth tokens that would otherwise leak + // into desktop logs and Sentry breadcrumbs. CodeRabbit on #1510. + log::info!( + "[single-instance] secondary launch detected, focusing primary (argc={}, cwd_present={})", + args.len(), + !cwd.is_empty() + ); + if let Err(err) = show_main_window(app) { + log::warn!("[single-instance] failed to focus main window: {err}"); + } + }, + )) // Explicitly disable `open_js_links_on_click`: tauri-plugin-opener // defaults to injecting `init-iife.js` into *every* webview — a // global click listener that invokes `plugin:opener|open_url` via diff --git a/app/src-tauri/vendor/tauri-cef b/app/src-tauri/vendor/tauri-cef index a57470231..2e1ae997d 160000 --- a/app/src-tauri/vendor/tauri-cef +++ b/app/src-tauri/vendor/tauri-cef @@ -1 +1 @@ -Subproject commit a57470231f8c1037d49f9913ec316d6bca325f74 +Subproject commit 2e1ae997dd6b9c90ca635b4fd076ebf646116ca9