添加qiming-rcoder模块

This commit is contained in:
Codex
2026-06-01 13:54:52 +08:00
parent 8092c4b1f8
commit 4b1a580132
539 changed files with 151650 additions and 0 deletions

View File

@@ -0,0 +1,120 @@
//! 子进程环境变量管理 - 供 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<String> {
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<String> {
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<String> {
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<String> {
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<String> = 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
}