From 30691c68cb5ededa157f50a4821f4409cfdfbc82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=99=B4=E5=A4=A9?= Date: Thu, 28 May 2026 09:16:58 +0800 Subject: [PATCH] fix(hermes): make hermes_venv_python aware of uv-tool default install path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hermes_venv_python() only looked at \HERMES_PYTHON\ and \~/.hermes-venv\ (the uv-pip layout), but ClawPanel's default \install_hermes\ runs \uv tool install\ and lands the Python at \/hermes-agent/{Scripts,bin}/python\. Result: any code path that funnels through \ un_venv_python_json\ — most importantly the '可选依赖管理 / Optional Dependencies' page (hermes_lazy_deps_*) — refused to start with 'Hermes venv 未找到(~/.hermes-venv 不存在)。请先安装 Hermes。', even on machines where Hermes was already installed and Profile / Channels / Dashboard pages worked fine. Refactor: - Extract \hermes_uv_tool_root()\ (resolves \/hermes-agent\). - Add \hermes_uv_tool_python()\ (returns Scripts/python.exe on Windows, bin/python on Unix). - Reuse \hermes_uv_tool_root()\ from the existing \locate_hermes_cli_package_dir()\ so the two helpers share one source of truth. - \hermes_venv_python()\ now falls through to the uv-tool path after \HERMES_PYTHON\ and \~/.hermes-venv\. Error message updated to list every probed location so users on truly-uninstalled machines still get actionable feedback. Verified end-to-end: the actual installed venv at \%APPDATA%/uv/tools/hermes-agent/Scripts/python.exe\ runs \ rom tools.lazy_deps import LAZY_DEPS\ cleanly and reports 25 features. --- src-tauri/src/commands/hermes.rs | 82 ++++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 25 deletions(-) diff --git a/src-tauri/src/commands/hermes.rs b/src-tauri/src/commands/hermes.rs index 5d6c8a5..8d5da33 100644 --- a/src-tauri/src/commands/hermes.rs +++ b/src-tauri/src/commands/hermes.rs @@ -1762,14 +1762,10 @@ const HERMES_DASHBOARD_WEB_DIST_INDEX_HTML: &str = r#" "#; -/// Locate the installed `hermes_cli` package directory inside the uv tool venv. -/// -/// Layouts vary by platform: -/// - Windows: `/hermes-agent/Lib/site-packages/hermes_cli` -/// - macOS / Linux: `/hermes-agent/lib/python3.X/site-packages/hermes_cli` -/// -/// Returns `None` if uv is unavailable or hermes-agent is not installed. -fn locate_hermes_cli_package_dir() -> Option { +/// Resolve `/hermes-agent` — the venv root that `uv tool install` +/// creates. Returns `None` if `uv` is unavailable or hermes-agent isn't installed +/// via the uv-tool path (e.g. user is on the legacy `~/.hermes-venv` uv-pip path). +fn hermes_uv_tool_root() -> Option { let uv_path = uv_bin_path(); let uv_cmd = if uv_path.exists() { uv_path.to_string_lossy().to_string() @@ -1792,10 +1788,41 @@ fn locate_hermes_cli_package_dir() -> Option { if stdout.is_empty() { return None; } - let hermes_root = std::path::PathBuf::from(&stdout).join("hermes-agent"); - if !hermes_root.exists() { - return None; + let root = std::path::PathBuf::from(&stdout).join("hermes-agent"); + if root.exists() { + Some(root) + } else { + None } +} + +/// Locate the Python interpreter inside the uv-tool hermes-agent venv. +/// +/// Layouts vary by platform: +/// - Windows: `/hermes-agent/Scripts/python.exe` +/// - macOS / Linux: `/hermes-agent/bin/python` +fn hermes_uv_tool_python() -> Option { + let root = hermes_uv_tool_root()?; + #[cfg(target_os = "windows")] + let py = root.join("Scripts").join("python.exe"); + #[cfg(not(target_os = "windows"))] + let py = root.join("bin").join("python"); + if py.exists() { + Some(py) + } else { + None + } +} + +/// Locate the installed `hermes_cli` package directory inside the uv tool venv. +/// +/// Layouts vary by platform: +/// - Windows: `/hermes-agent/Lib/site-packages/hermes_cli` +/// - macOS / Linux: `/hermes-agent/lib/python3.X/site-packages/hermes_cli` +/// +/// Returns `None` if uv is unavailable or hermes-agent is not installed. +fn locate_hermes_cli_package_dir() -> Option { + let hermes_root = hermes_uv_tool_root()?; let windows_path = hermes_root .join("Lib") @@ -12769,9 +12796,12 @@ pub async fn hermes_read_config_full() -> Result { /// 找到 Hermes venv 的 Python 解释器路径 /// -/// 优先级(P1-3 优化): -/// 1. 环境变量 `HERMES_PYTHON` — 适配自定义 venv(brew / uv tool / 容器等非默认路径) -/// 2. ~/.hermes-venv/bin/python (Unix) 或 ~/.hermes-venv/Scripts/python.exe (Windows) +/// 优先级: +/// 1. 环境变量 `HERMES_PYTHON` — 适配自定义 venv(brew / 容器 / 任何非默认布局) +/// 2. `~/.hermes-venv/{Scripts,bin}/python` — `uv pip install` 备选安装路径 +/// 3. `/hermes-agent/{Scripts,bin}/python` — `uv tool install` 默认路径 +/// (ClawPanel `install_hermes` 默认走此分支,所以这里的 fallback 必不可少; +/// 早期实现只查路径 #2 导致「可选依赖管理」等页面对绝大多数用户都误报「未安装」) fn hermes_venv_python() -> Option { // 1. HERMES_PYTHON 环境变量优先 if let Ok(custom) = std::env::var("HERMES_PYTHON") { @@ -12780,23 +12810,25 @@ fn hermes_venv_python() -> Option { return Some(p); } } - // 2. 默认 venv 位置 - let venv_dir = dirs::home_dir()?.join(".hermes-venv"); - #[cfg(target_os = "windows")] - let py = venv_dir.join("Scripts").join("python.exe"); - #[cfg(not(target_os = "windows"))] - let py = venv_dir.join("bin").join("python"); - if py.exists() { - Some(py) - } else { - None + // 2. 旧的 ~/.hermes-venv 位置(uv pip install 路径) + if let Some(home) = dirs::home_dir() { + let venv_dir = home.join(".hermes-venv"); + #[cfg(target_os = "windows")] + let py = venv_dir.join("Scripts").join("python.exe"); + #[cfg(not(target_os = "windows"))] + let py = venv_dir.join("bin").join("python"); + if py.exists() { + return Some(py); + } } + // 3. uv tool 默认路径(ClawPanel 默认安装方式) + hermes_uv_tool_python() } /// 统一跑 venv python -c "