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.
134 lines
6.2 KiB
PowerShell
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 ''
|