#Requires -Version 5 <# OpenEng AI Engine - one-line installer for Windows (PowerShell). irm https://openeng.app/engine/install.ps1 | iex The engine runs open-weight models on your machine and dials out to reach the web app (app.openeng.app) - it opens no inbound port and is not a server. One engine per machine. This script downloads the prebuilt `openeng.exe` engine binary, checksum-verifies it, installs it to a user directory, and adds that directory to your PATH. No source, no Rust toolchain, no build step. ── SAFETY INVARIANT — why this file exists and must be served as text/plain ── openeng.app is a single-page app behind a catch-all rewrite: a request for a MISSING path returns the SPA's index.html with HTTP 200 (not a 404). Two ways that bites an installer, both defended here: 1. If this very file were missing, `irm .../install.ps1 | iex` would fetch the SPA HTML and execute it as PowerShell. Fix: this file exists AND firebase.json serves /engine/install.ps1 as text/plain. 2. If the Windows binary is missing, a naive download would save the SPA HTML and try to run it. Fix: this script sniffs the PE 'MZ' magic and HTML markers and REFUSES to install or execute anything that is not a genuine native Windows executable. Hard rule: SPA HTML is NEVER written as a binary or piped into execution. Tunables (environment): OPENENG_ENGINE_URL base URL for binaries (default https://openeng.app/engine/bin) OPENENG_BIN_DIR install directory (default %LOCALAPPDATA%\Programs\OpenEng) OPENENG_NO_MODELS=1 skip the default model pull #> Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' # Self-context guard: this must run as PowerShell. If anything other than a real # PowerShell script reached this point (e.g. HTML somehow piped to iex), bail # loudly rather than limp on. (HTML is not valid PowerShell, so iex would throw # before here; this is belt-and-suspenders for the "script's own context".) if ($null -eq $PSVersionTable) { Write-Host "error: this installer must be run with PowerShell (irm ... | iex)." -ForegroundColor Red exit 1 } # Force TLS 1.2+ (Windows PowerShell 5.1 still defaults to TLS 1.0). try { [Net.ServicePointManager]::SecurityProtocol = ` [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 } catch { } function Write-Step($m) { Write-Host "==> $m" -ForegroundColor Cyan } function Write-Ok($m) { Write-Host "ok: $m" -ForegroundColor Green } function Write-Warn($m) { Write-Host "! $m" -ForegroundColor Yellow } function Die($m) { Write-Host "error: $m" -ForegroundColor Red; exit 1 } $base = if ($env:OPENENG_ENGINE_URL) { $env:OPENENG_ENGINE_URL } else { 'https://openeng.app/engine/bin' } $binDir = if ($env:OPENENG_BIN_DIR) { $env:OPENENG_BIN_DIR } else { Join-Path $env:LOCALAPPDATA 'Programs\OpenEng' } $asset = 'openeng-windows-x86_64.exe' $url = "$base/$asset" $dest = Join-Path $binDir 'openeng.exe' # The exact, actionable message shown until the native Windows binary publishes. $wslMessage = "The native Windows build is coming soon — run OpenEng under WSL today: wsl bash -c 'curl -fsSL https://openeng.app/engine/install.sh | bash'" function Show-WindowsComingSoon { # Print the actionable notice verbatim as its own clean line, then exit 0: # a missing native Windows binary is expected today, not a failure. Write-Host "" Write-Host $wslMessage -ForegroundColor Yellow exit 0 } # Real Windows executables begin with the PE/DOS 'MZ' signature (0x4D 0x5A). function Test-IsPeExecutable { param([byte[]]$Bytes) return ($Bytes -and $Bytes.Length -ge 2 -and $Bytes[0] -eq 0x4D -and $Bytes[1] -eq 0x5A) } # HTML sniff over the first bytes — catches the SPA index.html the catch-all # rewrite returns for a missing path (which arrives with HTTP 200). function Test-LooksLikeHtml { param([byte[]]$Bytes) if (-not $Bytes -or $Bytes.Length -eq 0) { return $false } $n = [Math]::Min($Bytes.Length, 512) $head = [System.Text.Encoding]::ASCII.GetString($Bytes, 0, $n).Trim().ToLowerInvariant() if ($head.StartsWith('