diff --git a/v2/src-tauri/src/sidecar.rs b/v2/src-tauri/src/sidecar.rs index 3f073b5..19e5bb4 100644 --- a/v2/src-tauri/src/sidecar.rs +++ b/v2/src-tauri/src/sidecar.rs @@ -1,5 +1,5 @@ -// Node.js sidecar lifecycle management -// Spawns agent-runner.ts (compiled), communicates via stdio NDJSON +// Sidecar lifecycle management (Deno-first, Node.js fallback) +// Spawns agent-runner-deno.ts (or agent-runner.mjs), communicates via stdio NDJSON use serde::{Deserialize, Serialize}; use std::io::{BufRead, BufReader, Write}; @@ -18,6 +18,11 @@ pub struct AgentQueryOptions { pub resume_session_id: Option, } +struct SidecarCommand { + program: String, + args: Vec, +} + pub struct SidecarManager { child: Arc>>, stdin_writer: Arc>>>, @@ -39,13 +44,13 @@ impl SidecarManager { return Err("Sidecar already running".to_string()); } - // Resolve sidecar binary path relative to the app - let sidecar_path = Self::resolve_sidecar_path(app)?; + // Resolve sidecar command (Deno-first, Node.js fallback) + let cmd = Self::resolve_sidecar_command(app)?; - log::info!("Starting sidecar: node {}", sidecar_path.display()); + log::info!("Starting sidecar: {} {}", cmd.program, cmd.args.join(" ")); - let mut child = Command::new("node") - .arg(&sidecar_path) + let mut child = Command::new(&cmd.program) + .args(&cmd.args) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) @@ -184,35 +189,63 @@ impl SidecarManager { *self.ready.lock().unwrap() } - fn resolve_sidecar_path(app: &AppHandle) -> Result { - // In dev mode, use the sidecar source directory - // In production, the built sidecar is bundled with the app + fn resolve_sidecar_command(app: &AppHandle) -> Result { let resource_dir = app .path() .resource_dir() .map_err(|e| format!("Failed to get resource dir: {e}"))?; - let prod_path = resource_dir.join("sidecar").join("dist").join("agent-runner.mjs"); - if prod_path.exists() { - return Ok(prod_path); - } - - // Dev fallback: look relative to the Cargo project root - let dev_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + let dev_root = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) .parent() .unwrap() - .join("sidecar") - .join("dist") - .join("agent-runner.mjs"); + .to_path_buf(); - if dev_path.exists() { - return Ok(dev_path); + // Try Deno first (runs TypeScript directly, no build step needed) + let deno_paths = [ + resource_dir.join("sidecar").join("agent-runner-deno.ts"), + dev_root.join("sidecar").join("agent-runner-deno.ts"), + ]; + + for path in &deno_paths { + if path.exists() { + // Check if deno is available + if Command::new("deno").arg("--version").output().is_ok() { + return Ok(SidecarCommand { + program: "deno".to_string(), + args: vec![ + "run".to_string(), + "--allow-run".to_string(), + "--allow-env".to_string(), + "--allow-read".to_string(), + path.to_string_lossy().to_string(), + ], + }); + } + log::warn!("Deno sidecar found at {} but deno not in PATH, falling back to Node.js", path.display()); + } + } + + // Fallback to Node.js + let node_paths = [ + resource_dir.join("sidecar").join("dist").join("agent-runner.mjs"), + dev_root.join("sidecar").join("dist").join("agent-runner.mjs"), + ]; + + for path in &node_paths { + if path.exists() { + return Ok(SidecarCommand { + program: "node".to_string(), + args: vec![path.to_string_lossy().to_string()], + }); + } } Err(format!( - "Sidecar not found at {} or {}", - prod_path.display(), - dev_path.display() + "Sidecar not found. Checked Deno ({}, {}) and Node.js ({}, {})", + deno_paths[0].display(), + deno_paths[1].display(), + node_paths[0].display(), + node_paths[1].display(), )) } } diff --git a/v2/src-tauri/tauri.conf.json b/v2/src-tauri/tauri.conf.json index 368ef76..f7e3ea7 100644 --- a/v2/src-tauri/tauri.conf.json +++ b/v2/src-tauri/tauri.conf.json @@ -30,7 +30,7 @@ "https://github.com/DexterFromLab/BTerminal/releases/latest/download/latest.json" ], "dialog": true, - "pubkey": "" + "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEJCRkZEMERDMTUwMzY5MjIKUldRaWFRTVYzTkQvdTYwRDh6YStaSE9rWUZYYkRGd3UvVUcydE1IQVdTM29uNTRPTlpjQmFqVFEK" } }, "bundle": { @@ -43,6 +43,10 @@ "icons/icon.icns", "icons/icon.ico" ], + "resources": [ + "../sidecar/agent-runner-deno.ts", + "../sidecar/dist/agent-runner.mjs" + ], "category": "DeveloperTool", "shortDescription": "Multi-session Claude agent dashboard", "longDescription": "BTerminal is a terminal emulator with integrated Claude AI agent sessions, SSH management, and a tiling pane layout. Built with Tauri, Svelte 5, and xterm.js.",