fix(install-windows): pull models last, so Ctrl+C can't abort the install

Ctrl+C in PS 5.1 kills the whole script, and the multi-GB model pull sat in the
middle -- skipping the download also skipped the ncp PATH registration and the
desktop shortcut. Moves the pull after them. Also drops the pipe that buffered
ollama's progress bar (a running download looked like a hang) and replaces a
try/catch that native commands never trigger with $LASTEXITCODE checks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jon
2026-07-22 14:30:09 -05:00
co-authored by Claude Opus 4.8
parent 6137710c25
commit 4961d765c8
2 changed files with 78 additions and 60 deletions
+68 -60
View File
@@ -106,64 +106,6 @@ if (-not (Test-Path (Join-Path $RepoRoot "interface\web\dist\index.html"))) {
}
Write-OK "Web UI built (interface\web\dist)"
# -- Pull models (best-effort) -------------------------------------------------
# Which models ship is decided in ONE place - DEFAULT_CHAT_MODEL and
# DEFAULT_MEMORY_MODEL in synapse\nexus_config.py (rationale documented there).
# Read them instead of hardcoding, so the installer can never pull one model
# while the backend defaults to another.
Push-Location $RepoRoot
$ChatModel = (& $VenvPy -c "from synapse.nexus_config import DEFAULT_CHAT_MODEL as m; print(m)")
$MemModel = (& $VenvPy -c "from synapse.nexus_config import DEFAULT_MEMORY_MODEL as m; print(m)")
Pop-Location
if ($LASTEXITCODE -ne 0 -or -not $ChatModel -or -not $MemModel) {
Write-Fail "Could not read the default models from synapse\nexus_config.py - the venv install is broken"
}
Write-Step "Pulling models ($ChatModel for chat, $MemModel for memory)"
Write-Host " Downloads a few GB; press Ctrl+C to skip and pull them later from the Models tab." -ForegroundColor DarkGray
try {
ollama pull $ChatModel | Out-Host
Write-OK "$ChatModel ready (default chat model)"
} catch {
Write-Warn "$ChatModel pull skipped/failed - pull it from the Models tab later."
}
try {
ollama pull $MemModel | Out-Host
Write-OK "$MemModel ready (memory curator)"
} catch {
Write-Warn "$MemModel pull skipped/failed - the memory service will fall back to the chat model."
}
# Pin it as the default chat model. Runs from the repo root so the synapse
# package imports; only writes the 'model' setting in the shared DB.
Write-Step "Setting $ChatModel as the default model"
Push-Location $RepoRoot
& $VenvPy -c "from synapse.memory.store import store; from synapse.nexus_config import DEFAULT_CHAT_MODEL; store.update_settings({'model': DEFAULT_CHAT_MODEL})"
$seedOk = ($LASTEXITCODE -eq 0)
Pop-Location
if ($seedOk) { Write-OK "Default model set to $ChatModel" }
else { Write-Warn "Could not persist default model - pick it at the top of the chat instead." }
# -- Make Ollama manual-start (NexusOS owns the lifecycle) ----------------------
Write-Step "Setting Ollama to manual start"
# The Ollama desktop app autostarts a server at every login, and the elevated
# 'ollama pull' above leaves an elevated server the user-level app cannot stop -
# which makes the Start/Stop AI button get stuck. Remove the login autostart and
# stop the running server so NexusOS controls Ollama via its Start AI button.
try {
$ollamaAutostart = Join-Path ([Environment]::GetFolderPath("Startup")) "Ollama.lnk"
if (Test-Path $ollamaAutostart) {
Remove-Item $ollamaAutostart -Force
Write-OK "Removed Ollama login autostart"
}
foreach ($img in @("ollama app.exe", "ollama.exe")) {
taskkill /F /T /IM $img 2>$null | Out-Null
}
Write-OK "Ollama set to manual start"
} catch {
Write-Warn "Could not adjust Ollama autostart - you can still Start/Stop AI from the app."
}
# -- ncp command ---------------------------------------------------------------
# management\ncp.cmd goes on the machine PATH rather than a `function ncp` in the
# PowerShell profile. Three reasons the profile route kept biting:
@@ -220,6 +162,72 @@ try {
Write-Warn "Could not create desktop shortcut - launch with: powershell -File launch_nexus.ps1"
}
# -- Pull models (best-effort, LAST on purpose) --------------------------------
# This is the only multi-GB step, and the only one a user is invited to Ctrl+C.
# Ctrl+C in PowerShell 5.1 terminates the whole SCRIPT, not just the running
# native command - so with this block in the middle, skipping the download also
# skipped the PATH registration and the desktop shortcut, and left an install
# that looked finished but had no working `ncp`. Everything that makes NexusOS
# usable now runs before we get here; abort at this point and you lose only the
# models, which the Models tab can pull later.
#
# Which models ship is decided in ONE place - DEFAULT_CHAT_MODEL and
# DEFAULT_MEMORY_MODEL in synapse\nexus_config.py (rationale documented there).
# Read them instead of hardcoding, so the installer can never pull one model
# while the backend defaults to another.
Push-Location $RepoRoot
$ChatModel = (& $VenvPy -c "from synapse.nexus_config import DEFAULT_CHAT_MODEL as m; print(m)")
$MemModel = (& $VenvPy -c "from synapse.nexus_config import DEFAULT_MEMORY_MODEL as m; print(m)")
Pop-Location
if ($LASTEXITCODE -ne 0 -or -not $ChatModel -or -not $MemModel) {
Write-Fail "Could not read the default models from synapse\nexus_config.py - the venv install is broken"
}
Write-Step "Pulling models ($ChatModel for chat, $MemModel for memory)"
Write-Host " Downloads a few GB; press Ctrl+C to skip and pull them later from the Models tab." -ForegroundColor DarkGray
# No pipe: 'ollama pull' draws a progress bar with cursor control, and piping it
# (to Out-Host or anything else) buffers the redraws - the download then shows no
# output for minutes and reads as a hang. Let it own the console.
# No try/catch either: a native command that exits non-zero does not throw, so
# the catch never fired and a failed pull was reported as success.
ollama pull $ChatModel
if ($LASTEXITCODE -eq 0) { Write-OK "$ChatModel ready (default chat model)" }
else { Write-Warn "$ChatModel pull skipped/failed - pull it from the Models tab later." }
ollama pull $MemModel
if ($LASTEXITCODE -eq 0) { Write-OK "$MemModel ready (memory curator)" }
else { Write-Warn "$MemModel pull skipped/failed - the memory service will fall back to the chat model." }
# Pin it as the default chat model. Runs from the repo root so the synapse
# package imports; only writes the 'model' setting in the shared DB.
Write-Step "Setting $ChatModel as the default model"
Push-Location $RepoRoot
& $VenvPy -c "from synapse.memory.store import store; from synapse.nexus_config import DEFAULT_CHAT_MODEL; store.update_settings({'model': DEFAULT_CHAT_MODEL})"
$seedOk = ($LASTEXITCODE -eq 0)
Pop-Location
if ($seedOk) { Write-OK "Default model set to $ChatModel" }
else { Write-Warn "Could not persist default model - pick it at the top of the chat instead." }
# -- Make Ollama manual-start (NexusOS owns the lifecycle) ----------------------
Write-Step "Setting Ollama to manual start"
# The Ollama desktop app autostarts a server at every login, and the elevated
# 'ollama pull' above leaves an elevated server the user-level app cannot stop -
# which makes the Start/Stop AI button get stuck. Remove the login autostart and
# stop the running server so NexusOS controls Ollama via its Start AI button.
try {
$ollamaAutostart = Join-Path ([Environment]::GetFolderPath("Startup")) "Ollama.lnk"
if (Test-Path $ollamaAutostart) {
Remove-Item $ollamaAutostart -Force
Write-OK "Removed Ollama login autostart"
}
foreach ($img in @("ollama app.exe", "ollama.exe")) {
taskkill /F /T /IM $img 2>$null | Out-Null
}
Write-OK "Ollama set to manual start"
} catch {
Write-Warn "Could not adjust Ollama autostart - you can still Start/Stop AI from the app."
}
# -- Done ----------------------------------------------------------------------
Write-Host ""
Write-Host " ==========================================================" -ForegroundColor Green
@@ -231,8 +239,8 @@ Write-Host ""
Write-Host " The app opens at http://localhost:8000" -ForegroundColor White
Write-Host " The AI starts OFF - click 'Start AI' in the sidebar to turn it on." -ForegroundColor White
Write-Host ""
Write-Host " Terminal: open a NEW PowerShell window, then run ncp help" -ForegroundColor White
Write-Host " (the profile that defines ncp is only read at startup)" -ForegroundColor DarkGray
Write-Host " Terminal: open a NEW terminal (PowerShell or cmd), then run ncp help" -ForegroundColor White
Write-Host " (PATH is read at process start, so open windows lack it)" -ForegroundColor DarkGray
Write-Host " ==========================================================" -ForegroundColor Green
Write-Host ""
Read-Host "Press Enter to close"
+10
View File
@@ -86,6 +86,16 @@ def test_ncp_is_registered_on_path_not_in_a_shell_profile():
assert "setx" not in code.lower()
assert 'SetEnvironmentVariable("Path"' in code
# The model pull is the only multi-GB step and the only one the script
# invites a Ctrl+C on -- which in PS 5.1 kills the whole script. Anything
# after it is lost, so it has to come last. It used to sit in the middle,
# and skipping the download silently skipped the ncp registration too.
assert ps1.index("ollama pull") > ps1.index("Registering the ncp command"), \
"model pull must come after ncp registration - a Ctrl+C there aborts the installer"
assert ps1.index("ollama pull") > ps1.index("Creating desktop shortcut"), \
"model pull must come after the desktop shortcut"
def test_first_playbook_is_the_system_prompt(tmp_path, monkeypatch):
store = PlaybookFileStore(tmp_path)