mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-05-10 17:42:49 +08:00
修复:nvm 用户 Node.js/CLI 检测失败的问题
Bug1: check_node_at_path 参数名不匹配(snake_case vs camelCase)
- Tauri v2 默认期望 camelCase,前端发送的 node_dir 改为 nodeDir
- 同步修复 save_custom_node_path 和所有 memory 函数的 agent_id → agentId
Bug2: Windows 上 OpenClaw CLI 已安装但检测显示 ❌
- is_cli_installed() 仅检查 %APPDATA%\npm\openclaw.cmd
- 增加 PATH 查找兜底,兼容 nvm、自定义 prefix 等安装方式
增强: enhanced_path() 扫描 nvm 版本目录
- macOS/Linux: 扫描 ~/.nvm/versions/node/*/bin
- Windows: 扫描 %APPDATA%\nvm\* 和 %NVM_HOME%\*
This commit is contained in:
@@ -36,7 +36,7 @@ pub fn enhanced_path() -> String {
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
let extra: Vec<String> = vec![
|
||||
let mut extra: Vec<String> = vec![
|
||||
"/usr/local/bin".into(),
|
||||
"/opt/homebrew/bin".into(),
|
||||
format!("{}/.nvm/current/bin", home.display()),
|
||||
@@ -45,6 +45,18 @@ pub fn enhanced_path() -> String {
|
||||
format!("{}/.fnm/current/bin", home.display()),
|
||||
format!("{}/n/bin", home.display()),
|
||||
];
|
||||
// 扫描 nvm 实际安装的版本目录(兼容无 current 符号链接的情况)
|
||||
let nvm_versions = home.join(".nvm/versions/node");
|
||||
if nvm_versions.is_dir() {
|
||||
if let Ok(entries) = std::fs::read_dir(&nvm_versions) {
|
||||
for entry in entries.flatten() {
|
||||
let bin = entry.path().join("bin");
|
||||
if bin.is_dir() {
|
||||
extra.push(bin.to_string_lossy().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut parts: Vec<&str> = vec![];
|
||||
if let Some(ref cp) = custom_path {
|
||||
parts.push(cp.as_str());
|
||||
@@ -74,6 +86,32 @@ pub fn enhanced_path() -> String {
|
||||
if !appdata.is_empty() {
|
||||
extra.push(format!(r"{}\npm", appdata));
|
||||
extra.push(format!(r"{}\nvm", appdata));
|
||||
// 扫描 nvm-windows 实际安装的版本目录
|
||||
let nvm_dir = std::path::Path::new(&appdata).join("nvm");
|
||||
if nvm_dir.is_dir() {
|
||||
if let Ok(entries) = std::fs::read_dir(&nvm_dir) {
|
||||
for entry in entries.flatten() {
|
||||
let p = entry.path();
|
||||
if p.is_dir() && p.join("node.exe").exists() {
|
||||
extra.push(p.to_string_lossy().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// NVM_HOME 环境变量(用户可能自定义了 nvm 安装目录)
|
||||
if let Ok(nvm_home) = std::env::var("NVM_HOME") {
|
||||
let nvm_path = std::path::Path::new(&nvm_home);
|
||||
if nvm_path.is_dir() {
|
||||
if let Ok(entries) = std::fs::read_dir(nvm_path) {
|
||||
for entry in entries.flatten() {
|
||||
let p = entry.path();
|
||||
if p.is_dir() && p.join("node.exe").exists() {
|
||||
extra.push(p.to_string_lossy().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
extra.push(format!(r"{}\.volta\bin", home.display()));
|
||||
|
||||
|
||||
@@ -213,8 +213,9 @@ mod platform {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
/// 检测 openclaw CLI 是否已安装(文件系统检测,避免 spawn 进程)
|
||||
/// 检测 openclaw CLI 是否已安装
|
||||
pub fn is_cli_installed() -> bool {
|
||||
// 方式1: 检查常见文件路径
|
||||
if let Ok(appdata) = std::env::var("APPDATA") {
|
||||
let cmd_path = std::path::Path::new(&appdata)
|
||||
.join("npm")
|
||||
@@ -223,6 +224,17 @@ mod platform {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// 方式2: 通过 PATH 查找(兼容 nvm、自定义 prefix 等)
|
||||
let mut cmd = std::process::Command::new("cmd");
|
||||
cmd.args(["/c", "openclaw", "--version"]);
|
||||
cmd.env("PATH", crate::commands::enhanced_path());
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
cmd.creation_flags(CREATE_NO_WINDOW);
|
||||
if let Ok(o) = cmd.output() {
|
||||
if o.status.success() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
@@ -278,19 +278,19 @@ export const api = {
|
||||
searchLog: (logName, query, maxResults = 50) => invoke('search_log', { logName, query, maxResults }),
|
||||
|
||||
// 记忆文件
|
||||
listMemoryFiles: (category, agentId) => cachedInvoke('list_memory_files', { category, agent_id: agentId || null }),
|
||||
readMemoryFile: (path, agentId) => cachedInvoke('read_memory_file', { path, agent_id: agentId || null }, 5000),
|
||||
writeMemoryFile: (path, content, category, agentId) => { invalidate('list_memory_files', 'read_memory_file'); return invoke('write_memory_file', { path, content, category: category || 'memory', agent_id: agentId || null }) },
|
||||
deleteMemoryFile: (path, agentId) => { invalidate('list_memory_files'); return invoke('delete_memory_file', { path, agent_id: agentId || null }) },
|
||||
exportMemoryZip: (category, agentId) => invoke('export_memory_zip', { category, agent_id: agentId || null }),
|
||||
listMemoryFiles: (category, agentId) => cachedInvoke('list_memory_files', { category, agentId: agentId || null }),
|
||||
readMemoryFile: (path, agentId) => cachedInvoke('read_memory_file', { path, agentId: agentId || null }, 5000),
|
||||
writeMemoryFile: (path, content, category, agentId) => { invalidate('list_memory_files', 'read_memory_file'); return invoke('write_memory_file', { path, content, category: category || 'memory', agentId: agentId || null }) },
|
||||
deleteMemoryFile: (path, agentId) => { invalidate('list_memory_files'); return invoke('delete_memory_file', { path, agentId: agentId || null }) },
|
||||
exportMemoryZip: (category, agentId) => invoke('export_memory_zip', { category, agentId: agentId || null }),
|
||||
|
||||
// 安装/部署
|
||||
checkInstallation: () => cachedInvoke('check_installation', {}, 60000),
|
||||
initOpenclawConfig: () => { invalidate('check_installation'); return invoke('init_openclaw_config') },
|
||||
checkNode: () => cachedInvoke('check_node', {}, 60000),
|
||||
checkNodeAtPath: (nodeDir) => invoke('check_node_at_path', { node_dir: nodeDir }),
|
||||
checkNodeAtPath: (nodeDir) => invoke('check_node_at_path', { nodeDir }),
|
||||
scanNodePaths: () => invoke('scan_node_paths'),
|
||||
saveCustomNodePath: (nodeDir) => invoke('save_custom_node_path', { node_dir: nodeDir }),
|
||||
saveCustomNodePath: (nodeDir) => invoke('save_custom_node_path', { nodeDir }),
|
||||
getDeployConfig: () => cachedInvoke('get_deploy_config'),
|
||||
patchModelVision: () => invoke('patch_model_vision'),
|
||||
checkPanelUpdate: () => invoke('check_panel_update'),
|
||||
|
||||
Reference in New Issue
Block a user