From 524490a40937c2a74d7969842a31acaba8d11124 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Thu, 14 May 2026 07:39:13 -0700 Subject: [PATCH] fix(install.ps1): pin uv sync to venv\, verify baseline imports on Windows (#25755) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(cli): allow rotating broken OpenRouter / AI Gateway key in `hermes model` flow Before: when `OPENROUTER_API_KEY` (or `AI_GATEWAY_API_KEY`) was already set in ~/.hermes/.env, `hermes model openrouter` / `hermes model ai-gateway` skipped the API-key prompt entirely and jumped straight to the model picker. Users with a broken / expired / wrong key had no way to replace it without editing ~/.hermes/.env by hand or re-running `hermes setup` from scratch. Both flows now route through the existing `_prompt_api_key()` helper, which surfaces [K]eep / [R]eplace / [C]lear when a key is already configured — the same UX the generic API-key providers (z.ai, MiniMax, Gemini, etc.) and the Daytona setup already use. * fix(install.ps1): pin uv sync target to venv\, verify baseline imports Two related Windows-installer bugs that produce a broken venv with `ModuleNotFoundError: No module named 'dotenv'` on first `hermes` run. ## Bug 1: uv sync ignores VIRTUAL_ENV, syncs into .venv\ instead of venv\ `Install-Dependencies` creates the venv at `venv\` via `uv venv venv`, sets `$env:VIRTUAL_ENV = "$InstallDir\venv"`, then runs `uv sync --extra all --locked`. Modern uv (>=0.5) ignores `VIRTUAL_ENV` for the `sync` subcommand and uses the project default `.venv\` instead. Result: deps land in `$InstallDir\.venv\`, `venv\` stays empty except for the python.exe stub from the earlier `uv venv` call, `hermes.exe` ends up wired to the wrong site-packages. The bash installer (`scripts/install.sh`) already worked around this in `install_deps()` line 1127 by passing `UV_PROJECT_ENVIRONMENT` — that flag tells uv exactly where to put the project env regardless of `VIRTUAL_ENV`. Port the same fix to PowerShell. ## Bug 2: no post-install verification If the sync still misdirects for any other reason (uv version drift, filesystem quirk, user re-run scenarios), the installer reports success and the user only finds out by running `hermes` and getting an unhelpful traceback. Add a baseline-import probe that runs the venv's own python against the four packages every `hermes` invocation needs (`dotenv`, `openai`, `rich`, `prompt_toolkit`). On failure, throw with a recovery command tailored to whether a sibling `.venv\` exists. User report (Windows 11, Python 3.13.5, Hermes v0.13.0): manual repro steps were exactly this — `uv sync` landed in `.venv\`, recovered by junctioning `venv\` → `.venv\` to bridge the path mismatch. --- scripts/install.ps1 | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/scripts/install.ps1 b/scripts/install.ps1 index e2fe765174c..36cdf76ec70 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -813,6 +813,14 @@ function Install-Dependencies { # needs `make` to build from sdist) and the # install fails. # --extra all = just the [all] extra's contents (curated). + # + # UV_PROJECT_ENVIRONMENT pins the sync target to our venv\. + # Without it, modern uv (>=0.5) ignores VIRTUAL_ENV for `sync` + # and creates a sibling .venv\ inside the repo — leaving venv\ + # empty and producing the broken state where `hermes.exe` exists + # in the wrong directory and imports fail with ModuleNotFoundError. + # (Mirrors the same flag in scripts/install.sh::install_deps.) + $env:UV_PROJECT_ENVIRONMENT = "$InstallDir\venv" & $UvCmd sync --extra all --locked if ($LASTEXITCODE -eq 0) { Write-Success "Main package installed (hash-verified via uv.lock)" @@ -902,6 +910,31 @@ except Exception: throw "Failed to install hermes-agent package even with no extras. Inspect the uv pip install output above." } + # Baseline-import gate. Even if a tier reported success above, the + # actual deps may have landed somewhere other than $InstallDir\venv\ + # (e.g. uv 0.5+ syncing into a sibling .venv\ when UV_PROJECT_ENVIRONMENT + # isn't set, leaving venv\ empty and hermes.exe broken with + # `ModuleNotFoundError: No module named 'dotenv'` on first run). + # We probe via the venv's own python so a misdirected sync is caught + # here, not 30 seconds later when the user runs `hermes`. + if (-not $NoVenv) { + $venvPython = "$InstallDir\venv\Scripts\python.exe" + if (-not (Test-Path $venvPython)) { + throw "Install reported success but $venvPython does not exist. The dependency sync likely landed in a sibling .venv\ directory. Re-run the installer; if it persists, manually: cd '$InstallDir'; Remove-Item -Recurse -Force venv,.venv; uv venv venv --python $PythonVersion; `$env:UV_PROJECT_ENVIRONMENT='$InstallDir\venv'; uv sync --extra all --locked" + } + & $venvPython -c "import dotenv, openai, rich, prompt_toolkit" 2>&1 | Out-Null + if ($LASTEXITCODE -ne 0) { + $sibling = "$InstallDir\.venv" + $hint = if (Test-Path $sibling) { + "Detected sibling .venv\ at $sibling — uv synced there instead of venv\. Recover with: cd '$InstallDir'; Remove-Item -Recurse -Force venv; Move-Item .venv venv" + } else { + "Recover with: cd '$InstallDir'; `$env:UV_PROJECT_ENVIRONMENT='$InstallDir\venv'; uv sync --extra all --locked" + } + throw "Baseline imports failed in $InstallDir\venv (dotenv/openai/rich/prompt_toolkit). The install completed but dependencies are not in the venv. $hint" + } + Write-Success "Baseline imports verified in venv" + } + # Verify the dashboard deps specifically — they're the most common thing # users hit and lazy-import errors from `hermes dashboard` are confusing. # If tier 1 failed (the common case), [web] was still picked up by tiers