Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@

## [Unreleased]

### Added

- **`start.ps1`: native Windows launcher** — PowerShell equivalent of `start.sh` that bypasses `bootstrap.py`'s `ensure_supported_platform()` refusal and invokes `server.py` directly on native Windows. Mirrors `start.sh`'s discovery (load optional `.env` with the same readonly-var filter for `UID`/`GID`/`EUID`/`EGID`/`PPID`, find Python via `HERMES_WEBUI_PYTHON` env → `python3` → `python` → `py`, locate the hermes-agent dir at `%USERPROFILE%\.hermes\hermes-agent` or `../hermes-agent`, prefer the agent's `venv\Scripts\python.exe` if present, set `HERMES_WEBUI_HOST` / `HERMES_WEBUI_PORT` / `HERMES_WEBUI_STATE_DIR` / `HERMES_HOME` defaults). Closes the launcher half of #1952; complements the README community-guide link below. Assumes Python + agent venv are already set up — for first-time setup, use WSL2 once to create the venv, then `start.ps1` works natively.

### Documentation

- **README: link @markwang2658's native Windows community guide** — adds a paragraph below the existing "Native Windows is not supported by this bootstrap" line pointing Windows users at the community-maintained no-Docker / no-WSL2 setup ([hermes-windows-native-guide](https://github.com/markwang2658/hermes-windows-native-guide), companion setup repo [hermes-windows-native](https://github.com/markwang2658/hermes-windows-native)) tracked in #1952, including the memory delta vs containerized setups (~330 MB native vs ~1080 MB with WSL2+Docker). Closes the docs half of #1952.

## [v0.51.118] — 2026-05-22 — Release CP (stage-pr2773 — 1-PR hotfix — v0.51.117 brick fix: chat input restored)

### Fixed
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,13 @@ The bootstrap will:

> Native Windows is not supported for this bootstrap yet. Use Linux, macOS, or WSL2.
> For Windows / WSL auto-start at login, see [`docs/wsl-autostart.md`](docs/wsl-autostart.md).
> A community-maintained native Windows guide is tracked in [#1952](https://github.com/nesquena/hermes-webui/issues/1952).

A community-maintained native Windows setup is documented at [@markwang2658/hermes-windows-native-guide](https://github.com/markwang2658/hermes-windows-native-guide) (companion setup repo: [@markwang2658/hermes-windows-native](https://github.com/markwang2658/hermes-windows-native)). Notes from the community report in [#1952](https://github.com/nesquena/hermes-webui/issues/1952):

- **Memory:** community-measured ~330 MB native vs ~1080 MB with WSL2+Docker (varies by configuration).
- **What works:** chat, workspace browser, session management, all themes.
- **Known limitations:** some POSIX-style file paths surface in the workspace browser; bash-assuming agent tools may not work natively.
- **WSL2 relationship:** WSL2 is recommended *once* for first-time venv creation (since `bootstrap.py` currently refuses on native Windows). After the venv exists, `start.ps1` at the repo root runs the WebUI natively by invoking `server.py` directly — no WSL2 needed for day-to-day use.

If provider setup is still incomplete after install, the onboarding wizard will point you to finish it with `hermes model` instead of trying to replicate the full CLI setup in-browser.
For a step-by-step walkthrough of the wizard, provider choices, local model server Base URLs, and safe re-runs, see [`docs/onboarding.md`](docs/onboarding.md).
Expand Down
156 changes: 156 additions & 0 deletions start.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
<#
.SYNOPSIS
Native Windows launcher for Hermes WebUI - PowerShell equivalent
of start.sh, bypassing bootstrap.py's platform refusal.

.DESCRIPTION
Mirrors start.sh's discovery: load optional .env, find Python,
locate the hermes-agent install, set sensible env defaults, then
invoke server.py directly. The bootstrap.py path is skipped
because it currently raises on platform.system() == 'Windows';
server.py itself runs cleanly on native Windows.

Assumes Python + hermes-agent + the WebUI Python deps are already
installed - same assumption start.sh makes when invoked outside
a fresh bootstrap. For first-time setup, run bootstrap.py inside
WSL2 once to create the venv, then this script can use that venv.

.PARAMETER Port
TCP port the WebUI binds to. Overrides HERMES_WEBUI_PORT env.
Default: 8787.

.PARAMETER BindHost
Bind address. Overrides HERMES_WEBUI_HOST env.
Default: 127.0.0.1.

.EXAMPLE
.\start.ps1
# Bind to 127.0.0.1:8787, foreground.

.EXAMPLE
.\start.ps1 -Port 9000
# Bind to 127.0.0.1:9000.

.EXAMPLE
$env:HERMES_WEBUI_HOST = '0.0.0.0'
.\start.ps1
# Bind to all interfaces (set a password first via env or Settings).

.LINK
https://github.com/nesquena/hermes-webui/issues/1952
#>

[CmdletBinding()]
param(
[int]$Port = 0,
[string]$BindHost = ''
)

$ErrorActionPreference = 'Stop'
$RepoRoot = Split-Path -Parent $PSCommandPath

# === Load .env (mirroring start.sh's filtering) ========================
$envFile = Join-Path $RepoRoot '.env'
if (Test-Path $envFile) {
foreach ($line in Get-Content $envFile -Encoding UTF8) {
$trimmed = $line.Trim()
if (-not $trimmed -or $trimmed.StartsWith('#') -or -not $trimmed.Contains('=')) { continue }
$kv = $trimmed -split '=', 2
$key = ($kv[0].Trim() -replace '^export\s+', '')
# Filter out shell-readonly vars (UID, GID, EUID, EGID, PPID) per start.sh
if ($key -in @('UID', 'GID', 'EUID', 'EGID', 'PPID')) { continue }
if ($key -notmatch '^[A-Za-z_][A-Za-z0-9_]*$') { continue }
# Explicit $null check — an env var explicitly set to '' should still
# be considered "set" and NOT overridden by .env (empty string is
# falsey in PowerShell, so a plain truthy check would mis-skip).
if ($null -ne [Environment]::GetEnvironmentVariable($key)) { continue }
$val = $kv[1]
if ($val -match '^"(.*)"$') { $val = $Matches[1] }
elseif ($val -match "^'(.*)'$") { $val = $Matches[1] }
[Environment]::SetEnvironmentVariable($key, $val)
}
}

# === Find Python (matches start.sh order) ==============================
$Python = $env:HERMES_WEBUI_PYTHON
if (-not $Python) {
foreach ($candidate in @('python3', 'python', 'py')) {
$cmd = Get-Command $candidate -ErrorAction SilentlyContinue
if ($cmd) { $Python = $cmd.Source; break }
}
}
if (-not $Python) {
Write-Error 'Python 3 is required to run server.py (set HERMES_WEBUI_PYTHON or add python to PATH).'
exit 1
}

# === Find Hermes Agent dir (server.py imports from it) =================
# When HERMES_WEBUI_AGENT_DIR is set we still validate it on disk —
# an explicit override pointing at a missing dir should fail FAST
# with a clear message, not silently progress into a python3 launch
# that's about to crash on missing imports. Smoke-test feedback on
# PR #2783: nesquena/hermes-webui requested this guard.
$AgentDir = $env:HERMES_WEBUI_AGENT_DIR
if ($AgentDir -and -not (Test-Path (Join-Path $AgentDir 'hermes_cli'))) {
Write-Error "HERMES_WEBUI_AGENT_DIR is set to '$AgentDir' but no hermes_cli/ folder exists there. Unset the variable to fall back to auto-discovery, or fix the path."
exit 1
}
if (-not $AgentDir) {
$candidates = @(
(Join-Path $env:USERPROFILE '.hermes\hermes-agent'),
(Join-Path (Split-Path -Parent $RepoRoot) 'hermes-agent')
)
foreach ($c in $candidates) {
if (Test-Path (Join-Path $c 'hermes_cli')) { $AgentDir = $c; break }
}
}
if (-not $AgentDir) {
$expectedPrimary = Join-Path $env:USERPROFILE '.hermes\hermes-agent'
$expectedSibling = Join-Path (Split-Path -Parent $RepoRoot) 'hermes-agent'
Write-Error "hermes-agent not found at $expectedPrimary or $expectedSibling. Set HERMES_WEBUI_AGENT_DIR explicitly."
exit 1
}

# === Prefer the agent's venv Python if available =======================
$agentVenvPython = Join-Path $AgentDir 'venv\Scripts\python.exe'
if (Test-Path $agentVenvPython) {
$Python = $agentVenvPython
}

# === Resolve bind + state defaults =====================================
$BindHostFinal = if ($BindHost) { $BindHost } elseif ($env:HERMES_WEBUI_HOST) { $env:HERMES_WEBUI_HOST } else { '127.0.0.1' }
$PortFinal = if ($Port) { $Port } elseif ($env:HERMES_WEBUI_PORT) { [int]$env:HERMES_WEBUI_PORT } else { 8787 }
$env:HERMES_WEBUI_HOST = $BindHostFinal
$env:HERMES_WEBUI_PORT = "$PortFinal"
if (-not $env:HERMES_WEBUI_STATE_DIR) {
$env:HERMES_WEBUI_STATE_DIR = Join-Path $env:USERPROFILE '.hermes\webui'
}
if (-not $env:HERMES_HOME) {
$env:HERMES_HOME = Join-Path $env:USERPROFILE '.hermes'
}

# === Ensure dirs exist =================================================
New-Item -ItemType Directory -Force -Path $env:HERMES_HOME | Out-Null
New-Item -ItemType Directory -Force -Path $env:HERMES_WEBUI_STATE_DIR | Out-Null

# === Launch (foreground, matches start.sh) =============================
Write-Host "[start.ps1] Hermes WebUI native Windows launcher" -ForegroundColor Cyan
Write-Host "[start.ps1] Python: $Python"
Write-Host "[start.ps1] Agent dir: $AgentDir"
Write-Host "[start.ps1] State dir: $env:HERMES_WEBUI_STATE_DIR"
Write-Host "[start.ps1] Binding: ${BindHostFinal}:${PortFinal}"
Write-Host ""

$serverPath = Join-Path $RepoRoot 'server.py'
if (-not (Test-Path $serverPath)) {
Write-Error "server.py not found at $serverPath - is this the hermes-webui repo root?"
exit 1
}

Push-Location $RepoRoot
try {
& $Python $serverPath @args
exit $LASTEXITCODE
} finally {
Pop-Location
}