diff --git a/src-tauri/src/commands/config.rs b/src-tauri/src/commands/config.rs index d0cca99f..ff7ff6f6 100644 --- a/src-tauri/src/commands/config.rs +++ b/src-tauri/src/commands/config.rs @@ -1891,6 +1891,23 @@ pub fn write_mcp_config(config: Value) -> Result<(), String> { /// macOS: 优先从 npm 包的 package.json 读取(含完整后缀),fallback 到 CLI /// Windows/Linux: 优先读文件系统,fallback 到 CLI async fn get_local_version() -> Option { + // 优先从运行中的 openclaw 实例获取版本(openclaw status --json → runtimeVersion) + // 避免多实例共存时读取到非活跃安装的版本 + if let Ok(output) = crate::utils::openclaw_command_async() + .args(["status", "--json"]) + .output() + .await + { + if output.status.success() { + let stdout = String::from_utf8_lossy(&output.stdout); + if let Some(ver) = crate::commands::skills::extract_json_pub(&stdout) + .and_then(|v| v.get("runtimeVersion")?.as_str().map(String::from)) + { + return Some(ver); + } + } + } + #[cfg(target_os = "macos")] { if let Some(cli_path) = crate::utils::resolve_openclaw_cli_path() { @@ -2638,6 +2655,17 @@ fn read_version_from_installation(cli_path: &std::path::Path) -> Option } } } + // CLI 本体位于包目录中时(如 npm 全局安装:nvm、Homebrew 等), + // 直接读取同目录的 package.json(即该包自身的版本文件) + let own_pkg = dir.join("package.json"); + if let Ok(content) = std::fs::read_to_string(&own_pkg) { + if let Some(ver) = serde_json::from_str::(&content) + .ok() + .and_then(|v| v.get("version")?.as_str().map(String::from)) + { + return Some(ver); + } + } // 根据 CLI 路径判断来源,决定 package.json 检查顺序 // 避免残留的另一来源包被优先读取 let cli_source = crate::utils::classify_cli_source(&cli_path.to_string_lossy()); diff --git a/src-tauri/src/utils.rs b/src-tauri/src/utils.rs index 94939939..993e509d 100644 --- a/src-tauri/src/utils.rs +++ b/src-tauri/src/utils.rs @@ -110,11 +110,8 @@ pub fn resolve_openclaw_cli_path() -> Option { } #[cfg(not(target_os = "windows"))] { - for candidate in common_non_windows_cli_candidates() { - if candidate.exists() { - return Some(candidate.to_string_lossy().to_string()); - } - } + // 优先通过 enhanced_path 搜索:其中 nvm/volta 等版本管理器路径排在 Homebrew 前面, + // 与 `which openclaw` 的优先级一致,避免残留的 Homebrew 旧版本被优先检测到 let path = crate::commands::enhanced_path(); let sep = ':'; for dir in path.split(sep) { @@ -123,6 +120,12 @@ pub fn resolve_openclaw_cli_path() -> Option { return Some(candidate.to_string_lossy().to_string()); } } + // 兜底:检查 enhanced_path 可能未覆盖到的固定路径(如 GUI 环境 PATH 受限时) + for candidate in common_non_windows_cli_candidates() { + if candidate.exists() { + return Some(candidate.to_string_lossy().to_string()); + } + } None } }