diff --git a/crates/gyandoot-agent/src/browser_manager.rs b/crates/gyandoot-agent/src/browser_manager.rs index b5a511b..573f396 100644 --- a/crates/gyandoot-agent/src/browser_manager.rs +++ b/crates/gyandoot-agent/src/browser_manager.rs @@ -10,6 +10,7 @@ //! - Both packages share the same cache directory (~/Library/Caches/camoufox/). //! We use the pinned `camoufox-js` for fetch, `camoufox-browser` for daemon. +use std::process::Stdio; use std::time::Duration; use tokio::process::Command; @@ -95,9 +96,38 @@ fn check_known_errors(log: &str) -> Option<&'static str> { if lower.contains("error while loading shared libraries") { return Some("Missing Linux system libraries."); } + if lower.contains("sigkill") { + return Some("macOS Gatekeeper killed the browser (SIGKILL). Run: xattr -dr com.apple.quarantine ~/Library/Caches/camoufox"); + } None } +/// Remove macOS quarantine attribute from the Camoufox cache directory. +/// Gatekeeper kills unsigned headless browsers with SIGKILL on Apple Silicon. +async fn remove_macos_quarantine() { + if !cfg!(target_os = "macos") { + return; + } + let cache = browser_cache_dir(); + if !cache.exists() { + return; + } + let output = Command::new("xattr") + .args(&["-dr", "com.apple.quarantine", &cache.to_string_lossy()]) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .output() + .await; + match output { + Ok(o) if o.status.success() => { + info!("Removed macOS quarantine from {}", cache.display()); + } + _ => { + warn!("Could not remove quarantine (xattr not available or failed)"); + } + } +} + /// Initialize the browser daemon and return a ready client. /// /// Steps: @@ -122,7 +152,11 @@ pub async fn init_browser(node_path: Option<&str>) -> BrowserInitResult { if !is_browser_installed() { info!("Downloading Camoufox browser using camoufox-js@{}...", CAMOUFOX_JS_VERSION); match run_fetch(npx, CAMOUFOX_JS_VERSION).await { - Ok(msg) => info!("Camoufox browser fetch: {}", msg), + Ok(msg) => { + info!("Camoufox browser fetch: {}", msg); + // On macOS, remove Gatekeeper quarantine immediately after download + remove_macos_quarantine().await; + } Err(e) => { return BrowserInitResult::Unavailable(format!( "Failed to download Camoufox browser: {}\n\n\ @@ -133,6 +167,10 @@ pub async fn init_browser(node_path: Option<&str>) -> BrowserInitResult { } } + // On macOS, always try to remove Gatekeeper quarantine from cached binary + // (even if it was already installed — prior npx downloads may have set it) + remove_macos_quarantine().await; + // 3. Ensure the `camoufox-browser` npm package is globally installed for daemon if !is_npm_package_installed(npx, "camoufox-browser", CAMOUFOX_BROWSER_VERSION).await { info!("Installing camoufox-browser@{} (daemon)...", CAMOUFOX_BROWSER_VERSION);