# llamay installer for Windows. # # irm https://llamay.com/install.ps1 | iex # # Installs per user by default, which is what makes the one-liner work without # an elevation prompt: %LOCALAPPDATA%\Programs\llamay is writable by the user # who is running the shell. -AllInUsers installs to Program Files and needs an # administrator shell. # # Overridable: # # $env:LLAMAY_VERSION a specific version, e.g. 1.2.3 (default: latest) # $env:LLAMAY_INSTALL_DIR where the binary goes # $env:LLAMAY_NO_SERVICE set to 1 to install the CLI and no startup task [CmdletBinding()] param( [switch]$AllUsers, [string]$Version = $env:LLAMAY_VERSION, [string]$InstallDir = $env:LLAMAY_INSTALL_DIR ) $ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' # the progress bar makes iwr crawl # The artifacts live in a public repository of their own; the engine's source # is private, and a private repository answers 404 to an unauthenticated # download -- which is every user of this script. $Repo = 'AzmxAI/llamay-releases' $Base = "https://github.com/$Repo/releases" function Say { param($m) Write-Host $m } function Die { param($m) Write-Host "error: $m" -ForegroundColor Red; exit 1 } # Windows on arm64 is a real target and `uname -m` has no equivalent here; # PROCESSOR_ARCHITECTURE is what the shell knows, and under an x86 shell on an # arm64 machine it is the emulated one, so PROCESSOR_ARCHITEW6432 wins if set. $archRaw = if ($env:PROCESSOR_ARCHITEW6432) { $env:PROCESSOR_ARCHITEW6432 } else { $env:PROCESSOR_ARCHITECTURE } $arch = switch ($archRaw) { 'AMD64' { 'amd64' } 'ARM64' { 'arm64' } default { Die "unsupported architecture: $archRaw" } } if (-not $Version) { # The redirect on /releases/latest names the tag, which avoids the API and # its rate limit for what is one lookup. $resp = Invoke-WebRequest -Uri "$Base/latest" -MaximumRedirection 0 -ErrorAction SilentlyContinue $loc = $resp.Headers.Location if (-not $loc) { $loc = (Invoke-WebRequest -Uri "$Base/latest" -UseBasicParsing).BaseResponse.ResponseUri.AbsoluteUri } $Version = ([string]$loc) -replace '.*/tag/v', '' if (-not $Version) { Die 'could not determine the latest version; set $env:LLAMAY_VERSION' } } if (-not $InstallDir) { $InstallDir = if ($AllUsers) { "$env:ProgramFiles\llamay" } else { "$env:LOCALAPPDATA\Programs\llamay" } } if ($AllUsers) { $admin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $admin) { Die '-AllUsers needs an administrator PowerShell' } } $asset = "llamay_${Version}_windows_${arch}.zip" $url = "$Base/download/v$Version/$asset" Say "llamay $Version - windows/$arch" $tmp = Join-Path ([IO.Path]::GetTempPath()) ("llamay-" + [Guid]::NewGuid()) New-Item -ItemType Directory -Path $tmp -Force | Out-Null try { Say "downloading $asset" Invoke-WebRequest -Uri $url -OutFile "$tmp\$asset" -UseBasicParsing # The checksum is not optional. A release publishes SHA256SUMS over the # artifacts as uploaded; an installer that skips the check runs whatever it # was handed. Invoke-WebRequest -Uri "$Base/download/v$Version/SHA256SUMS" -OutFile "$tmp\SHA256SUMS" -UseBasicParsing $want = (Select-String -Path "$tmp\SHA256SUMS" -Pattern ([regex]::Escape($asset) + '$') | Select-Object -First 1).Line -split '\s+' | Select-Object -First 1 if (-not $want) { Die "$asset is not listed in SHA256SUMS" } $got = (Get-FileHash "$tmp\$asset" -Algorithm SHA256).Hash.ToLower() if ($got -ne $want.ToLower()) { Die "checksum mismatch for ${asset}: got $got, expected $want" } Say 'checksum ok' Expand-Archive -Path "$tmp\$asset" -DestinationPath "$tmp\x" -Force $exe = Get-ChildItem -Path "$tmp\x" -Filter 'llamay.exe' -Recurse | Select-Object -First 1 if (-not $exe) { Die 'the archive does not contain llamay.exe' } New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null # A running server holds the file open, so stop it before replacing the # binary. Without this an upgrade fails with a sharing violation and leaves # the old version in place while reporting success. Get-Process -Name 'llamay' -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue Start-Sleep -Milliseconds 300 Copy-Item $exe.FullName (Join-Path $InstallDir 'llamay.exe') -Force Say "installed $InstallDir\llamay.exe" # PATH, on the right hive for the scope this installed into. $scope = if ($AllUsers) { 'Machine' } else { 'User' } $path = [Environment]::GetEnvironmentVariable('Path', $scope) if ($path -notlike "*$InstallDir*") { [Environment]::SetEnvironmentVariable('Path', "$path;$InstallDir", $scope) Say "added $InstallDir to the $scope PATH - open a new terminal for it to take effect" } $env:Path = "$env:Path;$InstallDir" if ($env:LLAMAY_NO_SERVICE -ne '1') { # A logon scheduled task rather than a Windows service: llamay is a # plain console program with no service control handler, and a real # service would need one. The task gives the same result -- it is # running when the user is logged in -- without pretending. $task = 'llamay server' schtasks /Query /TN $task 2>$null | Out-Null if ($LASTEXITCODE -eq 0) { schtasks /Delete /TN $task /F | Out-Null } schtasks /Create /TN $task /TR "`"$InstallDir\llamay.exe`" serve" /SC ONLOGON /RL LIMITED /F | Out-Null if ($LASTEXITCODE -eq 0) { schtasks /Run /TN $task | Out-Null Say 'service running - schtasks /Query /TN "llamay server"' } else { Write-Host 'the startup task could not be created; run: llamay serve' -ForegroundColor Yellow } } Say '' Say "llamay $Version is installed" Say ' llamay pull hf:Qwen/Qwen2.5-0.5B-Instruct-GGUF/qwen2.5-0.5b-instruct-q4_k_m.gguf' Say " llamay run -m qwen2.5-0.5b-instruct:q4_k_m -p 'hello'" Say ' the server is on http://127.0.0.1:11435' } finally { Remove-Item -Recurse -Force $tmp -ErrorAction SilentlyContinue }