mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-13 14:02:16 +00:00
tests: pin ink engine in _make_tui_argv npm-bootstrap tests (post-merge semantic fix)
Main's rewritten test_tui_npm_install.py tests call _make_tui_argv expecting the Ink/npm flow unconditionally; with the dual-engine dispatch merged in, _resolve_tui_engine() auto-selects opentui whenever ui-opentui/dist is built in the repo, routing the call away from the path under test (first subprocess became 'node --version' instead of 'npm run build'). Pin the engine to ink via an autouse fixture, mirroring the existing pinning precedent in test_tui_resume_flow.py.
This commit is contained in:
parent
ab37440ce6
commit
e1067dbbe5
756 changed files with 79874 additions and 19585 deletions
|
|
@ -892,6 +892,42 @@ function Test-Node {
|
|||
return $true
|
||||
}
|
||||
|
||||
function Update-ProcessPathForPackages {
|
||||
# Make freshly-installed shims (rg.exe, ffmpeg.exe) visible to Get-Command in
|
||||
# THIS process without spawning a new shell, by folding the persisted
|
||||
# User+Machine hives plus winget's alias-shim directory into $env:Path.
|
||||
# Called after every package-manager attempt (winget/choco/scoop): previously
|
||||
# PATH was only refreshed inside the winget branch, so a successful
|
||||
# choco/scoop fallback -- or any install on a box without winget -- could be
|
||||
# misreported as "not installed".
|
||||
#
|
||||
# MERGE rather than overwrite: start from the existing process PATH so any
|
||||
# process-only entries added earlier in this installer run survive, then
|
||||
# APPEND hive/winget-Links entries not already present (case-insensitive,
|
||||
# order-preserving dedupe). A wholesale replace would silently drop those
|
||||
# process-only entries.
|
||||
$candidates = @()
|
||||
$candidates += $env:Path
|
||||
$candidates += [Environment]::GetEnvironmentVariable("Path", "User")
|
||||
$candidates += [Environment]::GetEnvironmentVariable("Path", "Machine")
|
||||
$wingetLinks = Join-Path $env:LOCALAPPDATA "Microsoft\WinGet\Links"
|
||||
if (Test-Path $wingetLinks) {
|
||||
$candidates += $wingetLinks
|
||||
}
|
||||
$seen = New-Object System.Collections.Generic.HashSet[string] ([StringComparer]::OrdinalIgnoreCase)
|
||||
$ordered = New-Object System.Collections.Generic.List[string]
|
||||
foreach ($chunk in $candidates) {
|
||||
if ([string]::IsNullOrEmpty($chunk)) { continue }
|
||||
foreach ($entry in $chunk.Split(';')) {
|
||||
$trimmed = $entry.Trim()
|
||||
if ($trimmed -and $seen.Add($trimmed)) {
|
||||
$ordered.Add($trimmed)
|
||||
}
|
||||
}
|
||||
}
|
||||
$env:Path = [string]::Join(';', $ordered)
|
||||
}
|
||||
|
||||
function Install-SystemPackages {
|
||||
$script:HasRipgrep = $false
|
||||
$script:HasFfmpeg = $false
|
||||
|
|
@ -961,25 +997,33 @@ function Install-SystemPackages {
|
|||
try {
|
||||
$output = winget install --exact --id $pkg --source winget --silent `
|
||||
--accept-package-agreements --accept-source-agreements 2>&1
|
||||
$code = $LASTEXITCODE
|
||||
$output | Out-File -FilePath $log -Encoding utf8
|
||||
"winget exit: $LASTEXITCODE" | Out-File -FilePath $log -Encoding utf8 -Append
|
||||
"winget exit: $code" | Out-File -FilePath $log -Encoding utf8 -Append
|
||||
# 0x8A15002B (-1978335189) = APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE.
|
||||
# winget treats `install` on a package it already has registered as
|
||||
# an *upgrade*, finds no newer version, and bails with this code --
|
||||
# even when the binary is gone from disk/PATH (stale registration,
|
||||
# files removed outside winget, or a missing alias shim). We KNOW the
|
||||
# command was missing (that's why we're here), so a plain install
|
||||
# dead-ends forever. Force a reinstall to repair the registration so
|
||||
# the shim reappears.
|
||||
if ($code -eq -1978335189) {
|
||||
"-> already-installed/no-upgrade; retrying with --force" | Out-File -FilePath $log -Encoding utf8 -Append
|
||||
$output = winget install --exact --id $pkg --source winget --silent --force `
|
||||
--accept-package-agreements --accept-source-agreements 2>&1
|
||||
$output | Out-File -FilePath $log -Encoding utf8 -Append
|
||||
"winget exit (force): $LASTEXITCODE" | Out-File -FilePath $log -Encoding utf8 -Append
|
||||
}
|
||||
} catch {
|
||||
$_ | Out-File -FilePath $log -Encoding utf8 -Append
|
||||
"winget exit: <exception>" | Out-File -FilePath $log -Encoding utf8 -Append
|
||||
}
|
||||
}
|
||||
# Refresh PATH from both env-var hives AND winget's alias shim directory.
|
||||
# winget exposes packages via "command line aliases" in %LOCALAPPDATA%\
|
||||
# Microsoft\WinGet\Links, which is added to PATH by the AppExecutionAlias
|
||||
# machinery only in *newly-spawned* shells -- not the current process.
|
||||
# Without this addition, Get-Command rg below would falsely return null
|
||||
# immediately after a successful install.
|
||||
$wingetLinks = Join-Path $env:LOCALAPPDATA "Microsoft\WinGet\Links"
|
||||
$envPath = [Environment]::GetEnvironmentVariable("Path", "User") + ";" + [Environment]::GetEnvironmentVariable("Path", "Machine")
|
||||
if (Test-Path $wingetLinks) {
|
||||
$envPath = "$envPath;$wingetLinks"
|
||||
}
|
||||
$env:Path = $envPath
|
||||
# Refresh PATH so packages winget exposed via "command line aliases" in
|
||||
# %LOCALAPPDATA%\Microsoft\WinGet\Links (added to PATH only in
|
||||
# newly-spawned shells, not this process) are visible to Get-Command below.
|
||||
Update-ProcessPathForPackages
|
||||
if ($needRipgrep -and (Get-Command rg -ErrorAction SilentlyContinue)) {
|
||||
Write-Success "ripgrep installed"
|
||||
$script:HasRipgrep = $true
|
||||
|
|
@ -1005,6 +1049,7 @@ function Install-SystemPackages {
|
|||
foreach ($pkg in $chocoPkgs) {
|
||||
try { choco install $pkg -y 2>&1 | Out-Null } catch { }
|
||||
}
|
||||
Update-ProcessPathForPackages
|
||||
if ($needRipgrep -and (Get-Command rg -ErrorAction SilentlyContinue)) {
|
||||
Write-Success "ripgrep installed via chocolatey"
|
||||
$script:HasRipgrep = $true
|
||||
|
|
@ -1023,6 +1068,7 @@ function Install-SystemPackages {
|
|||
foreach ($pkg in $scoopPkgs) {
|
||||
try { scoop install $pkg 2>&1 | Out-Null } catch { }
|
||||
}
|
||||
Update-ProcessPathForPackages
|
||||
if ($needRipgrep -and (Get-Command rg -ErrorAction SilentlyContinue)) {
|
||||
Write-Success "ripgrep installed via scoop"
|
||||
$script:HasRipgrep = $true
|
||||
|
|
@ -1060,9 +1106,10 @@ function Install-Repository {
|
|||
# directory OR a symlink OR a submodule-style gitfile -- and also when
|
||||
# it's a broken stub left over from a failed previous install (e.g.
|
||||
# a partial Remove-Item that couldn't delete a locked index.lock).
|
||||
# Validate the repo properly by asking git itself. Two checks
|
||||
# belt-and-braces: rev-parse AND git status. If either fails the
|
||||
# repo is broken and we fall through to a fresh clone.
|
||||
# Validate the repo properly by asking git itself. Three checks
|
||||
# belt-and-braces: rev-parse (work tree), git status, and a resolvable
|
||||
# HEAD (an initial commit). If any fails the repo is broken and we
|
||||
# fall through to a fresh clone.
|
||||
$repoValid = $false
|
||||
if (Test-Path "$InstallDir\.git") {
|
||||
Push-Location $InstallDir
|
||||
|
|
@ -1077,7 +1124,17 @@ function Install-Repository {
|
|||
$null = & git -c windows.appendAtomically=false status --short 2>&1
|
||||
$statusOk = ($LASTEXITCODE -eq 0)
|
||||
|
||||
if ($revParseOk -and $statusOk) {
|
||||
# An interrupted previous clone leaves a repo with NO initial
|
||||
# commit. rev-parse/status still succeed there, but the update
|
||||
# path's `git stash` (and later `git checkout`) abort with
|
||||
# "You do not have the initial commit yet" and fail the install
|
||||
# (#40998). Require a resolvable HEAD so such partial checkouts
|
||||
# are treated as broken and re-cloned fresh below.
|
||||
$global:LASTEXITCODE = 0
|
||||
$null = & git -c windows.appendAtomically=false rev-parse --verify HEAD 2>&1
|
||||
$hasCommit = ($LASTEXITCODE -eq 0)
|
||||
|
||||
if ($revParseOk -and $statusOk -and $hasCommit) {
|
||||
$repoValid = $true
|
||||
}
|
||||
} catch {}
|
||||
|
|
@ -1119,7 +1176,7 @@ function Install-Repository {
|
|||
git -c windows.appendAtomically=false stash push --include-untracked -m "$stashName"
|
||||
if ($LASTEXITCODE -eq 0) { $autostashRef = "stash@{0}" }
|
||||
}
|
||||
git -c windows.appendAtomically=false fetch origin
|
||||
git -c windows.appendAtomically=false fetch origin $Branch
|
||||
if ($LASTEXITCODE -ne 0) { throw "git fetch failed (exit $LASTEXITCODE)" }
|
||||
# Precedence: Commit > Tag > Branch. Commit and Tag check
|
||||
# out as detached HEAD intentionally -- they're meant to be
|
||||
|
|
@ -1198,16 +1255,19 @@ function Install-Repository {
|
|||
}
|
||||
$didUpdate = $true
|
||||
} else {
|
||||
# Directory exists but isn't a usable git repo. Wipe it and
|
||||
# fall through to a fresh clone. A leftover ``.git`` stub from
|
||||
# a partial uninstall used to lock the installer into the
|
||||
# "update" branch forever, emitting three ``fatal: not a git
|
||||
# repository`` errors and failing with "not in a git directory".
|
||||
Write-Warn "Existing directory at $InstallDir is not a valid git repo -- replacing it."
|
||||
# Directory exists but isn't a usable git repo -- e.g. an
|
||||
# interrupted clone with no initial commit (#40998), or a leftover
|
||||
# ``.git`` stub from a partial uninstall that used to lock the
|
||||
# installer into the "update" branch forever. Move it aside rather
|
||||
# than deleting it -- never destroy a directory the user might still
|
||||
# want -- and fall through to a fresh clone.
|
||||
$backupDir = "$InstallDir.broken-" + (Get-Date -Format "yyyyMMdd-HHmmss")
|
||||
Write-Warn "Existing directory at $InstallDir is not a valid git repo."
|
||||
Write-Warn "Moving it aside to $backupDir before re-cloning."
|
||||
try {
|
||||
Remove-Item -Recurse -Force $InstallDir -ErrorAction Stop
|
||||
Move-Item -LiteralPath $InstallDir -Destination $backupDir -ErrorAction Stop
|
||||
} catch {
|
||||
Write-Err "Could not remove $InstallDir : $_"
|
||||
Write-Err "Could not move $InstallDir aside : $_"
|
||||
Write-Info "Close any programs that might be using files in $InstallDir (editors,"
|
||||
Write-Info "terminals, running hermes processes) and try again."
|
||||
throw
|
||||
|
|
@ -1711,7 +1771,7 @@ function Write-BootstrapMarker {
|
|||
function Copy-ConfigTemplates {
|
||||
Write-Info "Setting up configuration files..."
|
||||
|
||||
# Create ~/.hermes directory structure
|
||||
# Create the HERMES_HOME directory structure ($HermesHome, default %LOCALAPPDATA%\hermes)
|
||||
New-Item -ItemType Directory -Force -Path "$HermesHome\cron" | Out-Null
|
||||
New-Item -ItemType Directory -Force -Path "$HermesHome\sessions" | Out-Null
|
||||
New-Item -ItemType Directory -Force -Path "$HermesHome\logs" | Out-Null
|
||||
|
|
@ -1729,13 +1789,13 @@ function Copy-ConfigTemplates {
|
|||
$examplePath = "$InstallDir\.env.example"
|
||||
if (Test-Path $examplePath) {
|
||||
Copy-Item $examplePath $envPath
|
||||
Write-Success "Created ~/.hermes/.env from template"
|
||||
Write-Success "Created $envPath from template"
|
||||
} else {
|
||||
New-Item -ItemType File -Force -Path $envPath | Out-Null
|
||||
Write-Success "Created ~/.hermes/.env"
|
||||
Write-Success "Created $envPath"
|
||||
}
|
||||
} else {
|
||||
Write-Info "~/.hermes/.env already exists, keeping it"
|
||||
Write-Info "$envPath already exists, keeping it"
|
||||
}
|
||||
|
||||
# Create config.yaml
|
||||
|
|
@ -1744,10 +1804,10 @@ function Copy-ConfigTemplates {
|
|||
$examplePath = "$InstallDir\cli-config.yaml.example"
|
||||
if (Test-Path $examplePath) {
|
||||
Copy-Item $examplePath $configPath
|
||||
Write-Success "Created ~/.hermes/config.yaml from template"
|
||||
Write-Success "Created $configPath from template"
|
||||
}
|
||||
} else {
|
||||
Write-Info "~/.hermes/config.yaml already exists, keeping it"
|
||||
Write-Info "$configPath already exists, keeping it"
|
||||
}
|
||||
|
||||
# Create SOUL.md if it doesn't exist (global persona file).
|
||||
|
|
@ -1780,25 +1840,25 @@ Delete the contents (or this file) to use the default personality.
|
|||
"@
|
||||
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
|
||||
[System.IO.File]::WriteAllText($soulPath, $soulContent, $utf8NoBom)
|
||||
Write-Success "Created ~/.hermes/SOUL.md (edit to customize personality)"
|
||||
Write-Success "Created $soulPath (edit to customize personality)"
|
||||
}
|
||||
|
||||
Write-Success "Configuration directory ready: ~/.hermes/"
|
||||
Write-Success "Configuration directory ready: $HermesHome"
|
||||
|
||||
# Seed bundled skills into ~/.hermes/skills/ (manifest-based, one-time per skill)
|
||||
Write-Info "Syncing bundled skills to ~/.hermes/skills/ ..."
|
||||
# Seed bundled skills into $HermesHome\skills (manifest-based, one-time per skill)
|
||||
Write-Info "Syncing bundled skills to $HermesHome\skills ..."
|
||||
$pythonExe = "$InstallDir\venv\Scripts\python.exe"
|
||||
if (Test-Path $pythonExe) {
|
||||
try {
|
||||
& $pythonExe "$InstallDir\tools\skills_sync.py" 2>$null
|
||||
Write-Success "Skills synced to ~/.hermes/skills/"
|
||||
Write-Success "Skills synced to $HermesHome\skills"
|
||||
} catch {
|
||||
# Fallback: simple directory copy
|
||||
$bundledSkills = "$InstallDir\skills"
|
||||
$userSkills = "$HermesHome\skills"
|
||||
if ((Test-Path $bundledSkills) -and -not (Get-ChildItem $userSkills -Exclude '.bundled_manifest' -ErrorAction SilentlyContinue)) {
|
||||
Copy-Item -Path "$bundledSkills\*" -Destination $userSkills -Recurse -Force -ErrorAction SilentlyContinue
|
||||
Write-Success "Skills copied to ~/.hermes/skills/"
|
||||
Write-Success "Skills copied to $HermesHome\skills"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2234,6 +2294,24 @@ function Install-Desktop {
|
|||
$code = $LASTEXITCODE
|
||||
}
|
||||
}
|
||||
# Still failing and the user hasn't pinned their own mirror: GitHub's
|
||||
# Electron release host is likely blocked/throttled (the repeating
|
||||
# "retrying" log). Retry once via npmmirror.com — the de-facto Electron
|
||||
# community mirror (Alibaba). @electron/get SHASUM-checks the download,
|
||||
# but the SHASUMS come from the same mirror, so that guards against a
|
||||
# corrupt/partial download, NOT a compromised mirror: an explicit trust
|
||||
# trade-off we only make AFTER the canonical GitHub download has failed,
|
||||
# and we never override a user-pinned ELECTRON_MIRROR.
|
||||
if ($code -ne 0 -and -not $env:ELECTRON_MIRROR) {
|
||||
$prevMirror = $env:ELECTRON_MIRROR
|
||||
$env:ELECTRON_MIRROR = "https://npmmirror.com/mirrors/electron/"
|
||||
Write-Warn "Desktop build still failing - the Electron download from GitHub looks blocked."
|
||||
Write-Warn "Retrying once via a public Electron mirror ($($env:ELECTRON_MIRROR)):"
|
||||
Write-Info " (set ELECTRON_MIRROR yourself to use a different/trusted mirror)"
|
||||
& $npmExe run pack 2>&1 | ForEach-Object { "$_" } | Tee-Object -FilePath $buildLog
|
||||
$code = $LASTEXITCODE
|
||||
$env:ELECTRON_MIRROR = $prevMirror
|
||||
}
|
||||
$ErrorActionPreference = $prevEAP
|
||||
if ($code -ne 0) {
|
||||
$errText = Get-Content $buildLog -Raw -ErrorAction SilentlyContinue
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue