Files
hermes-agent/setup-hermes.ps1
ethernet 3d12e86ef1 feat(pm): unified package manager — pm store foundation
Introduce the pm store: a unified, hash-verified package store that
replaces lazy_deps and the old installer's ad-hoc tool downloads.
Store tools are provisioned on PATH (ffmpeg, node/npm via pinned uv),
with a resumable 8-way downloader, verify() returning failure reasons,
and adopt() made EPERM-safe. chromium ships in the payload for every
target. The 3600-line install.sh is replaced by a staged bootstrapper
(heavy deps are pm's job after this); setup-hermes.sh, Dockerfile and
nix pin tables are rewired onto the store. Old install-script tests,
lazy_deps/managed_uv/build_info, and the ps1/bash installer test
batteries are removed with the machinery they tested.

Rebuilt from ethie/pm onto upstream/main (ac6c8028e0) after the
utf-8-sig sweep. 16 hot files (main also churned them) hand-merged:
platform adapters, main.py, electron/main.ts, tui_gateway/server.py,
cua_backend, installer-tests workflow, install.sh (full rewrite),
setup-hermes.sh, plugins doc.
2026-08-31 18:00:48 -04:00

134 lines
6.2 KiB
PowerShell

# ============================================================================
# Hermes Agent Setup Script (Windows) — THE dev-environment entry point.
# ============================================================================
# Sets up the pm-managed development environment from a fresh clone:
# 1. Stage the pinned uv from pm/lock.json (sha256-verified, into the pm
# store slot) - pm needs uv to bootstrap, so it cannot stage uv itself.
# 2. Provision Python + the venv + hash-verified dependency sync by running
# `python -m pm.cli install` through that uv (the same code path
# `hermes pm install` uses). pyproject.toml + uv.lock are the single
# authority for pins.
# 3. Point you at `.\activate.ps1` - the venv-style way to put the pm env
# (PATH + tool vars) into your current session.
# ============================================================================
$ErrorActionPreference = 'Stop'
Write-Host ''
Write-Host 'Hermes Agent Setup' -ForegroundColor Cyan
Write-Host ''
$repo = $PSScriptRoot
$lockPath = Join-Path $repo 'pm/lock.json'
if (-not (Test-Path $lockPath)) { throw 'pm/lock.json not found' }
$lock = Get-Content -Raw $lockPath | ConvertFrom-Json
$machineArch = (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment').PROCESSOR_ARCHITECTURE
$arch = if ($machineArch -eq 'ARM64') { 'arm64' } else { 'x64' }
$target = "win32-$arch"
# ---------------------------------------------------------------------------
# Stage the pinned uv from pm/lock.json into the pm store slot
# ---------------------------------------------------------------------------
$uvPin = $lock.packages.uv
if (-not $uvPin) { throw 'no uv pin in pm/lock.json' }
$artifact = $uvPin.artifacts.$target
if (-not $artifact) { $artifact = $uvPin.artifacts.any }
if (-not $artifact) { throw "no uv artifact for $target" }
$pyPin = $lock.packages.python
$pyVersion = if ($pyPin) { ($pyPin.version -split '\+')[0] -replace '^(\d+\.\d+).*', '$1' } else { '3.11' }
$store = if ($env:HERMES_RUNTIME_DIR) { $env:HERMES_RUNTIME_DIR } else { Join-Path $HOME '.hermes/tools' }
$entry = Join-Path $store "uv-$($uvPin.version)-$target"
$uv = Join-Path $entry 'uv.exe'
if (Test-Path $uv) {
Write-Host ("pinned uv found: " + (& $uv --version)) -ForegroundColor Green
} else {
Write-Host "Staging pinned uv $($uvPin.version) ($target) into the pm store..." -ForegroundColor Cyan
$tmp = Join-Path ([System.IO.Path]::GetTempPath()) ("hermes-setup-" + [guid]::NewGuid().ToString('n'))
New-Item -ItemType Directory -Path $tmp | Out-Null
try {
$archive = Join-Path $tmp ([uri]$artifact.url).Segments[-1]
Invoke-WebRequest -Uri $artifact.url -OutFile $archive
$got = (Get-FileHash -Algorithm SHA256 $archive).Hash.ToLowerInvariant()
if ($got -ne $artifact.sha256) {
throw "sha256 mismatch for uv (got $got, pinned $($artifact.sha256))"
}
$tree = Join-Path $tmp 'tree'
Expand-Archive -Path $archive -DestinationPath $tree
# flatten a single wrapping dir
$inner = @(Get-ChildItem $tree)
$src = if ($inner.Count -eq 1 -and $inner[0].PSIsContainer) { $inner[0].FullName } else { $tree }
New-Item -ItemType Directory -Force -Path $store | Out-Null
if (Test-Path $entry) { Remove-Item -Recurse -Force $entry }
Move-Item $src $entry
} finally {
Remove-Item -Recurse -Force $tmp -ErrorAction SilentlyContinue
}
Write-Host ("uv installed: " + (& $uv --version)) -ForegroundColor Green
}
# ---------------------------------------------------------------------------
# Delegate to pm: python + venv + tool store + hash-verified venv sync
# ---------------------------------------------------------------------------
Write-Host 'Installing python + tools + dependencies via pm (hash-verified via uv.lock)...' -ForegroundColor Cyan
Write-Host '(first run on a fresh checkout can take 1-5 minutes)'
Push-Location $repo
try {
& $uv run --no-project --python $pyVersion python -m pm.cli install
if ($LASTEXITCODE -ne 0) { throw 'pm install failed - see output above.' }
} finally {
Pop-Location
}
Write-Host 'Tools + dependencies installed (hash-verified via pm + uv.lock)' -ForegroundColor Green
# ---------------------------------------------------------------------------
# Environment file
# ---------------------------------------------------------------------------
$envFile = Join-Path $repo '.env'
if (-not (Test-Path $envFile)) {
if (Test-Path (Join-Path $repo '.env.example')) {
Copy-Item (Join-Path $repo '.env.example') $envFile
Write-Host 'Created .env from template' -ForegroundColor Green
}
} else {
Write-Host '.env exists' -ForegroundColor Green
}
# ---------------------------------------------------------------------------
# Seed bundled skills into ~/.hermes/skills/
# ---------------------------------------------------------------------------
$skillsDir = if ($env:HERMES_HOME) { Join-Path $env:HERMES_HOME 'skills' } else { Join-Path $HOME '.hermes/skills' }
New-Item -ItemType Directory -Force -Path $skillsDir | Out-Null
$sync = Join-Path $repo 'tools/skills_sync.py'
$venvPy = Join-Path $repo 'venv/Scripts/python.exe'
if ((Test-Path $sync) -and (Test-Path $venvPy)) {
& $venvPy $sync 2>$null
Write-Host 'Skills synced' -ForegroundColor Green
}
# ---------------------------------------------------------------------------
# Done
# ---------------------------------------------------------------------------
Write-Host ''
Write-Host 'Setup complete!' -ForegroundColor Green
Write-Host ''
Write-Host 'Next steps:'
Write-Host ''
Write-Host ' 1. Activate the dev environment (venv-style, in THIS session):'
Write-Host ' .\activate.ps1'
Write-Host ''
Write-Host ' 2. Run the setup wizard to configure API keys:'
Write-Host ' hermes setup'
Write-Host ''
Write-Host ' 3. Start chatting:'
Write-Host ' hermes'
Write-Host ''
Write-Host 'Other commands:'
Write-Host ' hermes pm install # Re-run the tool + dependency install'
Write-Host ' hermes status # Check configuration'
Write-Host ' hermes doctor # Diagnose issues'
Write-Host ' deactivate # Undo the activation (restore PATH etc.)'
Write-Host ''