Repository navigation
Expand file tree
/
Copy pathinstall.ps1
More file actions
350 lines (311 loc) · 14.5 KB
/
Copy pathinstall.ps1
File metadata and controls
350 lines (311 loc) · 14.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
#Requires -Version 5.1
<#
.SYNOPSIS
StackFlow - one-command install script for Windows.
.DESCRIPTION
Installs everything: Python, Node.js, Git, Docker Desktop, infra services.
API + editor run locally; Langfuse + PostgreSQL run in Docker.
.EXAMPLE
.\install.ps1 # from inside a clone
irm <raw-url> | iex # from anywhere (clones the repo)
#>
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
# ── Colours ────────────────────────────────────────────────────────────
function Write-Info { param([string]$Msg) Write-Host " -> " -ForegroundColor Cyan -NoNewline; Write-Host $Msg }
function Write-Ok { param([string]$Msg) Write-Host " [ok] " -ForegroundColor Green -NoNewline; Write-Host $Msg }
function Write-Warn { param([string]$Msg) Write-Host " [!] " -ForegroundColor Yellow -NoNewline; Write-Host $Msg }
function Write-Fail { param([string]$Msg) Write-Host " [x] " -ForegroundColor Red -NoNewline; Write-Host $Msg; exit 1 }
# ── Banner ─────────────────────────────────────────────────────────────
function Show-Banner {
Write-Host ""
Write-Host " +-++-++-++-++-++-++-++-++-+" -ForegroundColor Cyan
Write-Host " |S||t||a||c||k||F||l||o||w|" -ForegroundColor Cyan
Write-Host " +-++-++-++-++-++-++-++-++-+" -ForegroundColor Cyan
Write-Host ""
Write-Host " One-command installer (Windows)" -ForegroundColor DarkGray
Write-Host ""
}
# ── Prompt helper ──────────────────────────────────────────────────────
function Read-Default {
param([string]$Prompt, [string]$Default)
if ($Default) {
$value = Read-Host " $Prompt [$Default]"
if ([string]::IsNullOrWhiteSpace($value)) { return $Default }
return $value
}
return Read-Host " $Prompt"
}
# ── Refresh PATH within current session ────────────────────────────────
function Update-SessionPath {
$machinePath = [Environment]::GetEnvironmentVariable('Path', 'Machine')
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
$env:Path = "$machinePath;$userPath"
}
# ── Check for winget ───────────────────────────────────────────────────
function Ensure-Winget {
if (Get-Command winget -ErrorAction SilentlyContinue) {
Write-Ok "winget"
return
}
Write-Fail "winget is not available. Please install App Installer from the Microsoft Store."
}
# ── Prerequisites ──────────────────────────────────────────────────────
function Ensure-Prereqs {
Write-Info "Checking prerequisites..."
# Python 3.11+
$needPython = $true
if (Get-Command python -ErrorAction SilentlyContinue) {
try {
$pyver = & python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" 2>$null
if ($pyver) {
$parts = $pyver.Split('.')
$major = [int]$parts[0]; $minor = [int]$parts[1]
if ($major -ge 3 -and $minor -ge 11) {
Write-Ok "Python $pyver"
$needPython = $false
}
}
} catch {}
}
if ($needPython) {
Write-Info "Installing Python 3.13..."
winget install --id Python.Python.3.13 --accept-source-agreements --accept-package-agreements --silent
Update-SessionPath
if (-not (Get-Command python -ErrorAction SilentlyContinue)) {
Write-Fail "Python installation failed. Please install Python 3.11+ manually."
}
Write-Ok "Python 3.13"
}
# Node.js
if (Get-Command node -ErrorAction SilentlyContinue) {
Write-Ok "Node.js $(node --version)"
} else {
Write-Info "Installing Node.js..."
winget install --id OpenJS.NodeJS.LTS --accept-source-agreements --accept-package-agreements --silent
Update-SessionPath
if (-not (Get-Command node -ErrorAction SilentlyContinue)) {
Write-Fail "Node.js installation failed. Please install Node.js manually."
}
Write-Ok "Node.js"
}
# Git
if (Get-Command git -ErrorAction SilentlyContinue) {
Write-Ok "git"
} else {
Write-Info "Installing Git..."
winget install --id Git.Git --accept-source-agreements --accept-package-agreements --silent
Update-SessionPath
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
Write-Fail "Git installation failed. Please install Git manually."
}
Write-Ok "git"
}
# Docker Desktop (replaces Docker CLI + Colima on macOS)
if (Get-Command docker -ErrorAction SilentlyContinue) {
Write-Ok "Docker CLI"
} else {
Write-Info "Installing Docker Desktop..."
winget install --id Docker.DockerDesktop --accept-source-agreements --accept-package-agreements --silent
Update-SessionPath
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) {
Write-Warn "Docker Desktop installed but may require a restart."
Write-Warn "Please restart your computer, then re-run this script."
exit 0
}
Write-Ok "Docker Desktop"
}
# Docker Compose (bundled with Docker Desktop, but verify)
try {
$null = & docker compose version 2>$null
Write-Ok "Docker Compose"
} catch {
Write-Warn "Docker Compose not found. It should be bundled with Docker Desktop."
Write-Warn "Please ensure Docker Desktop is installed and running."
}
Write-Host ""
}
# ── Docker runtime ─────────────────────────────────────────────────────
function Test-DockerReady {
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) { return $false }
$savedPref = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
$null = & docker info 2>$null
$result = $LASTEXITCODE -eq 0
$ErrorActionPreference = $savedPref
return $result
}
function Ensure-DockerRuntime {
# Check if Docker daemon is responsive
if (Test-DockerReady) {
Write-Ok "Docker runtime already running"
return
}
Write-Info "Docker is not running. Attempting to start Docker Desktop..."
# Try to start Docker Desktop
$dockerDesktop = "${env:ProgramFiles}\Docker\Docker\Docker Desktop.exe"
if (Test-Path $dockerDesktop) {
Start-Process $dockerDesktop
Write-Info "Waiting for Docker Desktop to start (this may take a minute)..."
$maxWait = 60
$waited = 0
while ($waited -lt $maxWait) {
Start-Sleep -Seconds 3
$waited += 3
if (Test-DockerReady) {
Write-Ok "Docker Desktop is running"
Write-Host ""
return
}
Write-Host "." -NoNewline
}
Write-Host ""
Write-Fail "Docker Desktop did not start in time. Please start it manually and re-run this script."
} else {
Write-Fail "Docker Desktop not found. Please install and start Docker Desktop, then re-run this script."
}
}
# ── Determine STACKFLOW_HOME ──────────────────────────────────────────
function Resolve-Home {
$scriptDir = Split-Path -Parent $PSCommandPath
if (Test-Path (Join-Path $scriptDir "src\api_server.py")) {
$script:STACKFLOW_HOME = $scriptDir
Write-Info "Using existing repo at $($script:STACKFLOW_HOME)"
} else {
$defaultHome = Join-Path $env:USERPROFILE ".stackflow"
$script:STACKFLOW_HOME = Read-Default "Install location" $defaultHome
if (Test-Path (Join-Path $script:STACKFLOW_HOME "src")) {
Write-Info "Existing installation found - updating."
Push-Location $script:STACKFLOW_HOME
try { & git pull --ff-only } catch { Write-Warn "git pull failed - continuing with current version." }
Pop-Location
} else {
Write-Info "Cloning StackFlow into $($script:STACKFLOW_HOME)..."
& git clone --depth=1 https://github.com/StackAdapt/StackFlow.git $script:STACKFLOW_HOME
}
}
Set-Location $script:STACKFLOW_HOME
Write-Host ""
}
# ── Python venv + deps ─────────────────────────────────────────────────
function Setup-Python {
Write-Info "Setting up Python environment..."
if (-not (Test-Path "venv")) {
& python -m venv venv
Write-Ok "Created venv"
} else {
Write-Ok "venv already exists"
}
Write-Info "Installing Python dependencies (this may take a minute)..."
& venv\Scripts\pip.exe install --quiet --upgrade pip
& venv\Scripts\pip.exe install --quiet -r requirements.txt
Write-Ok "Python dependencies installed"
Write-Host ""
}
# ── Node deps ──────────────────────────────────────────────────────────
function Setup-Node {
Write-Info "Installing Node.js dependencies..."
Push-Location litegraph-editor
& npm install --silent 2>$null
Pop-Location
Write-Ok "Node.js dependencies installed"
Write-Host ""
}
# ── .env setup ─────────────────────────────────────────────────────────
function Setup-Env {
if (Test-Path ".env") {
Write-Warn ".env already exists - skipping interactive setup."
Write-Host " To reconfigure, delete .env and re-run install.ps1" -ForegroundColor DarkGray
Write-Host ""
return
}
Write-Info "Writing .env..."
@"
# Langfuse Configuration (local Docker instance)
LANGFUSE_PUBLIC_KEY=pk-lf-local
LANGFUSE_SECRET_KEY=sk-lf-local
LANGFUSE_HOST=http://localhost:3000
# Database Configuration (Docker PostgreSQL - shared with Langfuse)
LANGGRAPH_DB_USER=postgres
LANGGRAPH_DB_PASSWORD=postgres
LANGGRAPH_DB_NAME=stackflow
LANGGRAPH_DB_HOST=localhost
LANGGRAPH_DB_PORT=5432
# Langgraph config
MAX_CONCURRENCY=3
RECURSION_LIMIT=10000
"@ | Set-Content -Path ".env" -Encoding UTF8
Write-Ok ".env created"
Write-Host ""
}
# ── Docker infra services ─────────────────────────────────────────────
function Setup-Infra {
Write-Info "Starting infrastructure services (Langfuse + PostgreSQL)..."
$savedPref = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
& docker compose up -d 2>&1 | ForEach-Object {
Write-Host " $_" -ForegroundColor DarkGray
}
$ErrorActionPreference = $savedPref
Write-Ok "Infrastructure services running"
Write-Host " Langfuse: http://localhost:3000 (admin@stackflow.local / adminadmin)" -ForegroundColor DarkGray
Write-Host " PostgreSQL: localhost:5432" -ForegroundColor DarkGray
Write-Host ""
}
# ── Install global CLI ─────────────────────────────────────────────────
function Install-Cli {
Write-Info "Installing stackflow command..."
# Create a stackflow.cmd wrapper in a directory on PATH
$binDir = Join-Path $env:USERPROFILE ".local\bin"
if (-not (Test-Path $binDir)) { New-Item -ItemType Directory -Path $binDir -Force | Out-Null }
# Create .cmd wrapper for use from cmd.exe / PowerShell
$cmdWrapper = Join-Path $binDir "stackflow.cmd"
@"
@echo off
REM Auto-generated by StackFlow installer - $(Get-Date -Format 'yyyy-MM-dd')
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$($script:STACKFLOW_HOME)\stackflow.ps1" %*
"@ | Set-Content -Path $cmdWrapper -Encoding ASCII
# Create .ps1 wrapper for direct PowerShell use
$ps1Wrapper = Join-Path $binDir "stackflow.ps1"
@"
# Auto-generated by StackFlow installer - $(Get-Date -Format 'yyyy-MM-dd')
& "$($script:STACKFLOW_HOME)\stackflow.ps1" @args
"@ | Set-Content -Path $ps1Wrapper -Encoding UTF8
# Add to user PATH if not already there
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
if ($userPath -notlike "*$binDir*") {
[Environment]::SetEnvironmentVariable('Path', "$userPath;$binDir", 'User')
$env:Path = "$env:Path;$binDir"
Write-Ok "Added $binDir to user PATH"
Write-Warn "You may need to restart your terminal for PATH changes to take effect."
} else {
Write-Ok "stackflow command installed to $binDir"
}
Write-Host ""
}
# ── Success ────────────────────────────────────────────────────────────
function Show-Done {
Write-Host ""
Write-Host " [ok] Setup complete!" -ForegroundColor Green
Write-Host ""
Write-Host " Commands:" -ForegroundColor White
Write-Host " stackflow " -ForegroundColor Cyan -NoNewline; Write-Host "Start API + editor"
Write-Host " stackflow stop " -ForegroundColor Cyan -NoNewline; Write-Host "Stop API + editor"
Write-Host " stackflow docker " -ForegroundColor Cyan -NoNewline; Write-Host "Show infra container status"
Write-Host " stackflow logs -f " -ForegroundColor Cyan -NoNewline; Write-Host "Tail API logs"
Write-Host " stackflow pm list " -ForegroundColor Cyan -NoNewline; Write-Host "List modules"
Write-Host " stackflow install " -ForegroundColor Cyan -NoNewline; Write-Host "Re-run setup"
Write-Host ""
}
# ── Main ───────────────────────────────────────────────────────────────
Show-Banner
Ensure-Winget
Ensure-Prereqs
Resolve-Home
Setup-Python
Setup-Node
Setup-Env
Ensure-DockerRuntime
Setup-Infra
Install-Cli
Show-Done