//! 子进程环境变量管理 - 供 Agent 子进程使用 //! //! 提供 PATH 环境变量构建函数,供 rcoder 启动 Claude Code 等 Agent 时使用。 use std::path::PathBuf; /// Tauri 桌面客户端在 APPDATA 下的应用数据目录名 /// Windows: %APPDATA%/{TAURI_APP_DATA_DIR} pub const TAURI_APP_DATA_DIR: &str = "com.nuwax.agent-tauri-client"; /// 在系统 PATH 中查找可执行文件 fn find_in_path(executable: &str) -> Option { let output = if cfg!(windows) { std::process::Command::new("where").arg(executable).output() } else { std::process::Command::new("which").arg(executable).output() }; match output { Ok(o) if o.status.success() => { let binding = String::from_utf8_lossy(&o.stdout); binding .lines() .next() .map(str::trim) .filter(|s| !s.is_empty()) .map(str::to_string) } _ => None, } } /// 在系统 PATH 中查找可执行文件并返回其父目录 fn find_bin_dir(executable: &str) -> Option { find_in_path(executable).and_then(|path| { let parent = PathBuf::from(&path).parent()?.to_path_buf(); let parent_str = parent.to_string_lossy().to_string(); // 如果父目录是 "cmd",尝试找同级的 "bin" 目录 // 例如: D:\Program Files\Git\cmd -> D:\Program Files\Git\bin if parent_str.to_lowercase().ends_with("cmd") { let bin_dir = parent .file_name() .and_then(|n| n.to_str()) .map(|n| { if n.eq_ignore_ascii_case("cmd") { parent .parent() .map(|p| p.join("bin").to_string_lossy().to_string()) } else { None } }) .flatten(); if let Some(bin) = bin_dir { if PathBuf::from(&bin).join("bash.exe").exists() { return Some(bin); } } } Some(parent_str) }) } /// Windows: 查找 Git Bash 路径 /// /// 查找系统 PATH 中的 bash.exe,返回其 bin 目录路径 /// 如果 bash.exe 在 Git\cmd 目录下,会自动查找同级的 Git\bin 目录 #[cfg(windows)] pub fn find_git_bash_path() -> Option { find_bin_dir("bash.exe") } /// 构建适用于 rcoder 进程的 PATH 环境变量。 /// /// 优先级: /// 1. NUWAX_APP_RUNTIME_PATH(优先) /// 2. APPDATA 中的 node bin 和 npm bin(回退) /// /// 返回 PATH 字符串,如果都未找到则返回 None #[cfg(windows)] pub fn build_rcoder_path_env() -> Option { use tracing::info; let sep = ";"; // 1. 优先从 NUWAX_APP_RUNTIME_PATH 构建 if let Ok(runtime_path) = std::env::var("NUWAX_APP_RUNTIME_PATH") { let runtime_path = runtime_path.trim().to_string(); if !runtime_path.is_empty() { info!("[Env] Using NUWAX_APP_RUNTIME_PATH to build PATH"); return Some(runtime_path); } } // 2. 从 APPDATA 推导默认路径 if let Ok(appdata) = std::env::var("APPDATA") { let app_base = PathBuf::from(&appdata).join(TAURI_APP_DATA_DIR); let mut paths: Vec = Vec::new(); let node_bin = app_base.join("runtime").join("node").join("bin"); if node_bin.exists() { paths.push(node_bin.to_string_lossy().to_string()); } let npm_bin = app_base.join("node_modules").join(".bin"); if npm_bin.exists() { paths.push(npm_bin.to_string_lossy().to_string()); } if !paths.is_empty() { let path_value = paths.join(sep); info!("[Env] APPDATA built PATH: {}", path_value); return Some(path_value); } } None }