# Vex installer for Windows. # # irm https://www.projectvex.ai/install.ps1 | iex # # What this script does, and nothing else: # 1. Reads the release manifest (latest.yml) from GitHub Releases of # Vex-Foundation/Vex, the only place Vex builds live. # 2. Downloads the Windows installer and checks its size and SHA-512 against # that manifest. # 3. Checks its Authenticode signature is valid and issued to # "VEX LABS PTE. LTD.", the same publisher check Vex's own updater makes. # 4. Runs the installer silently. It installs for your user only, with no # admin prompt, into %LOCALAPPDATA%\Programs\Vex. # # It never asks for a seed phrase or a master password and never touches your # wallet data. # # To install a specific release instead of the latest, first run: # $env:VEX_VERSION = '0.3.1' # # Everything runs inside one script block, so nothing it defines is left behind # in your session. & { $ErrorActionPreference = 'Stop' # Invoke-WebRequest's progress bar slows downloads to a crawl on Windows PowerShell 5.1. $ProgressPreference = 'SilentlyContinue' [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 $Repo = 'Vex-Foundation/Vex' $Publisher = 'VEX LABS PTE. LTD.' $InstallDir = Join-Path $env:LOCALAPPDATA 'Programs\Vex' function Step([string]$Message) { Write-Host '==> ' -ForegroundColor Blue -NoNewline Write-Host $Message } function Download([string]$Url, [string]$OutFile, [switch]$Progress) { # curl.exe ships with Windows 10 1803+ and shows progress; Invoke-WebRequest is the fallback. if ($Progress -and (Get-Command curl.exe -ErrorAction SilentlyContinue)) { & curl.exe -fL --retry 2 --progress-bar -o $OutFile $Url if ($LASTEXITCODE -ne 0) { throw "download failed: $Url" } } else { Invoke-WebRequest -UseBasicParsing -Uri $Url -OutFile $OutFile } } $tmp = Join-Path ([IO.Path]::GetTempPath()) ('vex-install-' + [guid]::NewGuid().ToString('N')) try { Write-Host '' Write-Host 'Vex' -NoNewline Write-Host ' Your keys. Your machine. Your agent.' -ForegroundColor DarkGray Write-Host '' if ([Environment]::OSVersion.Platform -ne 'Win32NT') { throw 'this is the Windows installer. On macOS or Linux run: curl -fsSL https://www.projectvex.ai/install.sh | bash' } if (-not [Environment]::Is64BitOperatingSystem) { throw 'Vex needs 64-bit Windows 10 or 11.' } New-Item -ItemType Directory -Path $tmp | Out-Null Step 'Reading the latest Vex release' if ($env:VEX_VERSION) { $base = "https://github.com/$Repo/releases/download/v$($env:VEX_VERSION.TrimStart('v'))" } else { $base = "https://github.com/$Repo/releases/latest/download" } $manifestPath = Join-Path $tmp 'latest.yml' try { Download "$base/latest.yml" $manifestPath } catch { throw "could not read latest.yml from GitHub Releases. The Windows build may still be uploading; try again in a few minutes, or see https://github.com/$Repo/releases" } $manifest = Get-Content -LiteralPath $manifestPath -Raw # The version goes into a download URL and the file name onto disk, so both # have to look exactly like what the release pipeline produces. $v = [regex]::Match($manifest, '(?m)^version:\s*([0-9]+\.[0-9]+\.[0-9]+(?:[-+][0-9A-Za-z.-]+)?)\s*$') if (-not $v.Success) { throw 'unexpected version in the release manifest.' } $version = $v.Groups[1].Value $entry = [regex]::Match($manifest, '(?m)^\s*-\s*url:\s*(Vex-[0-9A-Za-z.+-]+-win-x64\.exe)\s*\r?\n\s*sha512:\s*(\S+)\s*\r?\n\s*size:\s*([0-9]+)') if (-not $entry.Success) { throw "release $version has no Windows x64 installer." } $name = $entry.Groups[1].Value $sha512 = $entry.Groups[2].Value $size = [int64]$entry.Groups[3].Value $installedExe = Join-Path $InstallDir 'Vex.exe' if ((Test-Path -LiteralPath $installedExe) -and (Get-Item -LiteralPath $installedExe).VersionInfo.ProductVersion -eq $version) { Write-Host "Vex $version is already installed at $InstallDir." return } Step "Downloading $name ($([math]::Round($size / 1MB)) MB)" $exe = Join-Path $tmp $name Download "https://github.com/$Repo/releases/download/v$version/$name" $exe -Progress Step 'Checking SHA-512 against the release manifest' if ((Get-Item -LiteralPath $exe).Length -ne $size) { throw "size mismatch for $name (expected $size bytes). Not installing." } $expected = [BitConverter]::ToString([Convert]::FromBase64String($sha512)) -replace '-', '' $actual = (Get-FileHash -LiteralPath $exe -Algorithm SHA512).Hash if ($actual -ne $expected) { throw "SHA-512 mismatch for $name. The file is not the published build. Not installing." } Step "Verifying the Authenticode signature ($Publisher)" $sig = Get-AuthenticodeSignature -LiteralPath $exe if ($sig.Status -ne 'Valid') { throw "the installer's signature is not valid ($($sig.Status)). Not installing." } $signer = $sig.SignerCertificate.GetNameInfo([Security.Cryptography.X509Certificates.X509NameType]::SimpleName, $false) if ($signer -cne $Publisher) { throw "the installer is signed by '$signer', not '$Publisher'. Not installing." } if (Get-Process -Name 'Vex' -ErrorAction SilentlyContinue) { throw 'Vex is running. Quit it and run this installer again.' } Step "Installing to $InstallDir" $proc = Start-Process -FilePath $exe -ArgumentList '/S' -Wait -PassThru if ($proc.ExitCode -ne 0) { throw "the installer exited with code $($proc.ExitCode)." } Write-Host '' Write-Host "Vex $version is installed." -NoNewline Write-Host ' Open it from the Start menu.' Write-Host '' Write-Host 'Before first run' if ((Get-Command docker -ErrorAction SilentlyContinue) -or (Test-Path -LiteralPath (Join-Path $env:ProgramFiles 'Docker\Docker\Docker Desktop.exe'))) { Write-Host ' ok Docker found. Make sure Docker Desktop uses the WSL2 backend.' -ForegroundColor DarkGray } else { Write-Host ' todo Install Docker Desktop with the WSL2 backend. Vex runs its local stack in it.' -ForegroundColor Yellow } Write-Host ' todo Have an OpenRouter API key ready. Inference runs on your own account.' -ForegroundColor Yellow Write-Host '' Write-Host 'Vex will never DM you first or ask for your seed phrase. Official sources:' -ForegroundColor DarkGray Write-Host "projectvex.ai, x.com/ProjectVEXai, github.com/$Repo" -ForegroundColor DarkGray } catch { Write-Host "error: $($_.Exception.Message)" -ForegroundColor Red } finally { Remove-Item -LiteralPath $tmp -Recurse -Force -ErrorAction SilentlyContinue } }