Skip to content

Commit 8079137

Browse files
committed
add scripts
1 parent 92b94f9 commit 8079137

1 file changed

Lines changed: 206 additions & 0 deletions

File tree

scripts/install-worker.ps1

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
#Requires -RunAsAdministrator
2+
<#
3+
.SYNOPSIS
4+
Installs exo worker node on a second Windows host.
5+
6+
.DESCRIPTION
7+
- Clones the ubisoft/exo repo (peter/wip branch)
8+
- Installs Python dependencies via uv
9+
- Builds the dashboard
10+
- Downloads llama.cpp b7836 CUDA binaries
11+
- Opens firewall ports 52415 (API) and 4001 (libp2p)
12+
- Creates a startup script
13+
14+
.PARAMETER InstallDir
15+
Directory to install exo into. Default: C:\exo
16+
17+
.PARAMETER NoApi
18+
Start as worker-only node (no dashboard/API).
19+
20+
.PARAMETER SkipDashboard
21+
Skip the npm dashboard build step.
22+
23+
.PARAMETER CudaVersion
24+
CUDA version suffix for llama.cpp download (e.g. cu12.4, cu12.2, cu11.7).
25+
Default: cu12.4
26+
27+
.EXAMPLE
28+
.\install-worker.ps1
29+
.\install-worker.ps1 -InstallDir D:\exo -NoApi -CudaVersion cu12.2
30+
#>
31+
param(
32+
[string]$InstallDir = "C:\exo",
33+
[switch]$NoApi,
34+
[switch]$SkipDashboard,
35+
[string]$CudaVersion = "cu12.4"
36+
)
37+
38+
Set-StrictMode -Version Latest
39+
$ErrorActionPreference = "Stop"
40+
41+
$LlamaBuild = "b7836"
42+
$LlamaZip = "llama-$LlamaBuild-bin-win-cuda-$CudaVersion-x64.zip"
43+
$LlamaUrl = "https://github.com/ggerganov/llama.cpp/releases/download/$LlamaBuild/$LlamaZip"
44+
$RepoUrl = "git@github.com:ubisoft/exo.git"
45+
$Branch = "supportWindows"
46+
$LibP2PPort = 4001
47+
$ApiPort = 52415
48+
49+
# ── helpers ──────────────────────────────────────────────────────────────────
50+
51+
function Write-Step([string]$msg) {
52+
Write-Host "`n==> $msg" -ForegroundColor Cyan
53+
}
54+
55+
function Assert-Command([string]$cmd, [string]$install) {
56+
if (-not (Get-Command $cmd -ErrorAction SilentlyContinue)) {
57+
Write-Host "[ERROR] '$cmd' not found. $install" -ForegroundColor Red
58+
exit 1
59+
}
60+
}
61+
62+
# ── prerequisite checks ───────────────────────────────────────────────────────
63+
64+
Write-Step "Checking prerequisites"
65+
66+
Assert-Command "git" "Install Git from https://git-scm.com"
67+
Assert-Command "uv" "Install uv: irm https://astral.sh/uv/install.ps1 | iex"
68+
Assert-Command "python" "Install Python 3.10 from https://python.org"
69+
70+
$pyVer = python --version 2>&1
71+
Write-Host " Python : $pyVer"
72+
Write-Host " uv : $(uv --version)"
73+
Write-Host " git : $(git --version)"
74+
75+
if (-not (Get-Command "npm" -ErrorAction SilentlyContinue) -and -not $SkipDashboard) {
76+
Write-Host "[WARN] npm not found — dashboard build will be skipped." -ForegroundColor Yellow
77+
$SkipDashboard = $true
78+
}
79+
80+
# ── clone ─────────────────────────────────────────────────────────────────────
81+
82+
Write-Step "Cloning repository into $InstallDir"
83+
84+
if (Test-Path $InstallDir) {
85+
Write-Host " Directory exists — pulling latest changes"
86+
git -C $InstallDir fetch ubisoft
87+
git -C $InstallDir checkout $Branch
88+
git -C $InstallDir pull ubisoft $Branch
89+
} else {
90+
git clone $RepoUrl $InstallDir
91+
git -C $InstallDir checkout $Branch
92+
}
93+
94+
Set-Location $InstallDir
95+
96+
# ── python deps ───────────────────────────────────────────────────────────────
97+
98+
Write-Step "Installing Python dependencies"
99+
$env:UV_SKIP_WHEEL_FILENAME_CHECK = "1"
100+
uv sync
101+
102+
# ── dashboard ─────────────────────────────────────────────────────────────────
103+
104+
if (-not $SkipDashboard) {
105+
Write-Step "Building dashboard"
106+
Push-Location "$InstallDir\dashboard"
107+
npm install
108+
npm run build
109+
Pop-Location
110+
}
111+
112+
# ── llama.cpp binaries ────────────────────────────────────────────────────────
113+
114+
Write-Step "Downloading llama.cpp $LlamaBuild ($CudaVersion)"
115+
116+
$tmpZip = "$env:TEMP\$LlamaZip"
117+
$tmpDir = "$env:TEMP\llama-extract"
118+
119+
if (-not (Test-Path $tmpZip)) {
120+
Write-Host " Downloading $LlamaUrl"
121+
Invoke-WebRequest -Uri $LlamaUrl -OutFile $tmpZip -UseBasicParsing
122+
} else {
123+
Write-Host " Archive already cached at $tmpZip"
124+
}
125+
126+
Write-Host " Extracting..."
127+
if (Test-Path $tmpDir) { Remove-Item $tmpDir -Recurse -Force }
128+
Expand-Archive -Path $tmpZip -DestinationPath $tmpDir
129+
130+
# Copy only the needed files
131+
$needed = @(
132+
"llama-server.exe",
133+
"ggml.dll", "ggml-base.dll", "ggml-cuda.dll", "ggml-rpc.dll",
134+
"llama.dll", "mtmd.dll", "libomp140.x86_64.dll"
135+
)
136+
$cpuDlls = Get-ChildItem $tmpDir -Filter "ggml-cpu-*.dll" -Recurse
137+
138+
foreach ($f in $needed) {
139+
$src = Get-ChildItem $tmpDir -Filter $f -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
140+
if ($src) {
141+
Copy-Item $src.FullName "$InstallDir\" -Force
142+
Write-Host " Copied $f"
143+
} else {
144+
Write-Host " [WARN] $f not found in archive" -ForegroundColor Yellow
145+
}
146+
}
147+
foreach ($dll in $cpuDlls) {
148+
Copy-Item $dll.FullName "$InstallDir\" -Force
149+
Write-Host " Copied $($dll.Name)"
150+
}
151+
152+
# ── firewall rules ────────────────────────────────────────────────────────────
153+
154+
Write-Step "Configuring firewall"
155+
156+
$rules = @(
157+
@{ Name = "exo API"; Port = $ApiPort; Proto = "TCP" },
158+
@{ Name = "exo libp2p"; Port = $LibP2PPort; Proto = "TCP" }
159+
)
160+
foreach ($r in $rules) {
161+
$existing = Get-NetFirewallRule -DisplayName $r.Name -ErrorAction SilentlyContinue
162+
if ($existing) {
163+
Write-Host " Rule '$($r.Name)' already exists — skipping"
164+
} else {
165+
New-NetFirewallRule -DisplayName $r.Name `
166+
-Direction Inbound -Action Allow `
167+
-Protocol $r.Proto -LocalPort $r.Port `
168+
-Profile Private | Out-Null
169+
Write-Host " Created rule: $($r.Name) ($($r.Proto)/$($r.Port))"
170+
}
171+
}
172+
173+
# ── startup script ────────────────────────────────────────────────────────────
174+
175+
Write-Step "Writing startup script"
176+
177+
$startScript = @"
178+
# exo worker startup script — generated by install-worker.ps1
179+
Set-Location "$InstallDir"
180+
181+
`$env:UV_SKIP_WHEEL_FILENAME_CHECK = "1"
182+
`$env:EXO_LLAMA_SERVER_PATH = "$InstallDir\llama-server.exe"
183+
`$env:EXO_LIBP2P_LISTEN_PORT = "$LibP2PPort"
184+
`$env:EXO_LIBP2P_LISTEN_ADDR = "0.0.0.0"
185+
186+
$(if ($NoApi) { 'uv run exo --no-api' } else { 'uv run exo' })
187+
"@
188+
189+
$startPath = "$InstallDir\start-exo.ps1"
190+
Set-Content -Path $startPath -Value $startScript -Encoding UTF8
191+
Write-Host " Written to $startPath"
192+
193+
# ── done ─────────────────────────────────────────────────────────────────────
194+
195+
Write-Host @"
196+
197+
==========================================================
198+
Installation complete!
199+
200+
Start exo:
201+
powershell -ExecutionPolicy Bypass -File "$startPath"
202+
203+
Cluster will form automatically via mDNS once both
204+
nodes are running on the same LAN.
205+
==========================================================
206+
"@ -ForegroundColor Green

0 commit comments

Comments
 (0)