PR B of the post-cluster install-hardening pair (PR A: install.sh #444 — merged). The audit found native Windows was the worst path at 2/10 zero-friction: the installer refused on every missing prereq (Python / git / Ollama), didn't pull a model, and gave the user no `jarvis` command after install. This closes those gaps. Changes in deploy/windows/install.ps1: 1. Auto-install Python via winget when missing (was: hard refuse). 2. Auto-install git via the same Install-WithWinget helper. 3. Auto-install Ollama via the official OllamaSetup.exe (NSIS /S flag, $ProgressPreference SilentlyContinue for the 150 MB download). 4. Wait for Ollama daemon health before pulling the model (60s poll on `ollama list`). 5. Pull qwen3.5:2b foreground (~1.5 GB) — banner is honest if pull failed. 6. jarvis.cmd shim at %LOCALAPPDATA%\OpenJarvis\bin\ added to User PATH (deduped against expanded form so re-runs don't append). Shim uses %~dp0..\src to self-locate and uv from PATH so a future uv update can't break it. 7. Pre-check admin for the scheduled-task path; refuse fast in -Service branch if not elevated, default to skip-with-explanation in the interactive branch. 8. Final banner tells the truth: 'jarvis' if all good, 'jarvis doctor' if model missing, 'open a new PowerShell' if User PATH was just updated. Adversarial review caught 5 real bugs before commit: - CRITICAL: GetEnvironmentVariable returns REG_EXPAND_SZ raw → %LOCALAPPDATA% wasn't expanded → just-installed Python invisible. Wrap in ExpandEnvironmentVariables. - HIGH: Ollama NSIS silent flag is /S not /silent (wrong flag opens GUI, hangs install). - MEDIUM: PS 5.1 progress bar makes Invoke-WebRequest 30x slower. - LOW: -Service branch missed isAdmin precheck. - LOW: PATH dedup missed unexpanded %VAR% entries → duplicate every re-run. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
OpenJarvis on native Windows
Phase-1 of the native-Windows-support RFC (#298). Mirrors the Linux
(deploy/systemd/) and macOS (deploy/launchd/) deployments — but for
PowerShell, without WSL2 or Docker.
One-liner install
In an elevated-or-regular PowerShell:
irm https://open-jarvis.github.io/OpenJarvis/install.ps1 | iex
What it does:
- Refuses non-Windows hosts and Windows < 10 1809.
- Checks Python 3.10 – 3.13 (3.14 has no numpy wheels yet — see #432).
- Checks
giton PATH. - Installs
uv(https://astral.sh/uv) if absent. - Clones the OpenJarvis repository to
%LOCALAPPDATA%\OpenJarvis(override with$env:OPENJARVIS_HOME). - Runs
uv sync --extra serverso the FastAPI server entry point is importable. - Optionally prompts to register a scheduled task that auto-starts the server at logon.
Flags (when invoked directly rather than via irm | iex):
| Flag | Effect |
|---|---|
-Service |
Register the scheduled task without prompting |
-SkipService |
Don't prompt; don't register |
-Force |
Re-run all steps even if already done |
irm | iex can't pass param() args into a piped script string, so
the same knobs are honored via env vars when the corresponding flag is
absent:
$env:OPENJARVIS_SKIP_SERVICE = '1'
irm https://open-jarvis.github.io/OpenJarvis/install.ps1 | iex
The available env vars: OPENJARVIS_SKIP_SERVICE, OPENJARVIS_SERVICE,
OPENJARVIS_FORCE. If you need richer control, save the script first
(irm ... -OutFile install.ps1; .\install.ps1 -Force).
Manual scheduled-task setup
If you skipped the prompt during install, you can register / inspect /
remove the task with jarvis-service.ps1:
$srv = "$env:LOCALAPPDATA\OpenJarvis\src\deploy\windows\jarvis-service.ps1"
# install (idempotent — replaces existing)
powershell -ExecutionPolicy Bypass -File $srv install
# status
powershell -ExecutionPolicy Bypass -File $srv status
# remove
powershell -ExecutionPolicy Bypass -File $srv uninstall
The task runs as the current user with LogonType=Interactive and
RunLevel=Limited. It restarts up to 3 times on failure (1-minute
gap), has no execution-time limit, and starts when available (catches
up if missed).
Loopback vs LAN-exposed
By default the scheduled task binds 127.0.0.1 — reachable only from
this machine, no API key required. This matches launchd parity (see
deploy/launchd/com.openjarvis.plist).
To expose on your LAN:
# 1. Generate an API key. The server REFUSES to bind 0.0.0.0 without one.
$env:OPENJARVIS_API_KEY = (uv run jarvis auth generate-key)
# 2. Re-register the task with -ListenHost 0.0.0.0.
powershell -ExecutionPolicy Bypass -File $srv install -ListenHost 0.0.0.0
jarvis-service.ps1 install refuses -ListenHost 0.0.0.0 if
$env:OPENJARVIS_API_KEY is unset — same guard as the systemd unit's
EnvironmentFile=/etc/openjarvis/env.
Parity table
| Concern | systemd | launchd | Windows |
|---|---|---|---|
| Service definition | deploy/systemd/openjarvis.service |
deploy/launchd/com.openjarvis.plist |
deploy/windows/jarvis-service.ps1 (cmdlet-driven) |
| Default bind | 0.0.0.0 (with API key) |
127.0.0.1 (no API key) |
127.0.0.1 (no API key) |
| Restart on failure | Restart=on-failure RestartSec=5 |
KeepAlive=true |
RestartCount=3 RestartInterval=PT1M |
| Auto-start | multi-user.target |
RunAtLoad=true |
AtLogOn trigger |
Updating
To pull the latest:
cd "$env:LOCALAPPDATA\OpenJarvis\src"
git pull --ff-only
uv sync --extra server
Or re-run the installer with -Force:
irm https://open-jarvis.github.io/OpenJarvis/install.ps1 | iex
# (then re-run with the file directly, passing -Force)
Uninstall
powershell -ExecutionPolicy Bypass -File "$env:LOCALAPPDATA\OpenJarvis\src\deploy\windows\jarvis-service.ps1" uninstall
Remove-Item -Recurse -Force "$env:LOCALAPPDATA\OpenJarvis"
Uninstalling does NOT remove uv (it's a separate tool — you may have
other Python projects using it).