param( [string]$Version = "latest" ) $ErrorActionPreference = "Stop" Set-StrictMode -Version Latest try { [Net.ServicePointManager]::SecurityProtocol = ` [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 } catch { } if ($PSVersionTable.PSVersion.Major -lt 6) { try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch { } } $BaseUrl = "https://downloads.poolside.ai/pool" $InstallDir = if ($env:POOL_INSTALL_DIR) { $env:POOL_INSTALL_DIR } else { Join-Path $env:LOCALAPPDATA "Programs\pool\bin" } $UpdatePath = if ($null -eq $env:POOL_INSTALL_UPDATE_PATH -or $env:POOL_INSTALL_UPDATE_PATH -eq "") { $true } else { $env:POOL_INSTALL_UPDATE_PATH -eq "1" } $ShowSplash = if ($null -eq $env:POOL_INSTALL_SPLASH -or $env:POOL_INSTALL_SPLASH -eq "") { $true } else { $env:POOL_INSTALL_SPLASH -eq "1" } $AcceptEula = $env:POOL_INSTALL_ACCEPT_EULA -eq "1" $PathUpdated = $false function Main { $arch = Get-Arch $resolvedVersion = Resolve-Version $Version $archive = "pool-windows-$arch.tar.gz" $url = "$BaseUrl/$resolvedVersion/$archive" $checksumUrl = "$BaseUrl/$resolvedVersion/checksums.txt" $tempDir = New-TempDir try { Show-Splash $resolvedVersion Confirm-Install Write-Host "Downloading pool $resolvedVersion (windows/$arch)..." $archivePath = Join-Path $tempDir $archive $checksumsPath = Join-Path $tempDir "checksums.txt" Download-File $url $archivePath Download-File $checksumUrl $checksumsPath Assert-Checksum $archivePath $checksumsPath $archive Expand-ArchiveTarGz $archivePath $tempDir $binaryPath = Join-Path $tempDir "pool-windows-$arch.exe" if (-not (Test-Path -LiteralPath $binaryPath)) { Fail "expected binary not found after extraction: $binaryPath" } New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null $targetPath = Join-Path $InstallDir "pool.exe" Move-Item -Force -LiteralPath $binaryPath -Destination $targetPath Write-Host "pool $resolvedVersion installed to $targetPath" Maybe-UpdatePath Maybe-PrintPathHint Write-Host "" Write-Host "Run pool to get started" } finally { if (Test-Path -LiteralPath $tempDir) { Remove-Item -Recurse -Force -LiteralPath $tempDir } } } function Get-Arch { $arch = $env:PROCESSOR_ARCHITECTURE switch ($arch) { "AMD64" { return "amd64" } "ARM64" { return "arm64" } default { Fail "unsupported architecture: $arch" } } } function Resolve-Version([string]$RequestedVersion) { if ($RequestedVersion -eq "latest") { $latest = (Download-String "$BaseUrl/pool-latest-version.txt").Trim() if ([string]::IsNullOrWhiteSpace($latest)) { Fail "empty version from pool-latest-version.txt" } return $latest } return $RequestedVersion } function Download-File([string]$Url, [string]$OutFile) { $prev = $ProgressPreference $ProgressPreference = 'SilentlyContinue' try { Invoke-WebRequest -Uri $Url -OutFile $OutFile -UseBasicParsing } finally { $ProgressPreference = $prev } } function Download-String([string]$Url) { $prev = $ProgressPreference $ProgressPreference = 'SilentlyContinue' try { return (Invoke-WebRequest -Uri $Url -UseBasicParsing).Content } finally { $ProgressPreference = $prev } } function Expand-ArchiveTarGz([string]$ArchivePath, [string]$DestinationDir) { $tar = Get-Command tar -ErrorAction SilentlyContinue if (-not $tar) { Fail "tar is required to extract $ArchivePath" } & $tar.Source -xzf $ArchivePath -C $DestinationDir if ($LASTEXITCODE -ne 0) { Fail "failed to extract $ArchivePath" } } function Assert-Checksum([string]$ArchivePath, [string]$ChecksumsPath, [string]$ArchiveName) { $checksumLine = Select-String -Path $ChecksumsPath -Pattern ([regex]::Escape($ArchiveName)) | Select-Object -First 1 if (-not $checksumLine) { Fail "no checksum found for $ArchiveName" } $expected = ($checksumLine.Line -split '\s+')[0].Trim() if ([string]::IsNullOrWhiteSpace($expected)) { Fail "no checksum found for $ArchiveName" } $actual = (Get-FileHash -Algorithm SHA256 -LiteralPath $ArchivePath).Hash.ToLowerInvariant() if ($actual -ne $expected.ToLowerInvariant()) { Fail "checksum mismatch for ${ArchiveName}: expected $expected, got $actual" } } function Show-Splash([string]$ResolvedVersion) { if (-not $ShowSplash) { return } $s = [char]0x2591 $b = [char]0x2588 $lines = @( ' SBB ', ' SBB ', 'SBBBBBBBB SBBBBBBB SBBBBBBB SBB ', 'SBB SBB SBB SBB SBB SBB SBB ', 'SBB SBB SBB SBB SBB SBB SBB ', 'SBBB SBB SBB SBB SBB SBB SBB ', 'SBBSBBBBB SBBBBBBB SBBBBBBB SBB ', 'SBB ', 'SBB ' ) Write-Host "" foreach ($line in $lines) { Write-Host $line.Replace('S', $s).Replace('B', $b) } Write-Host " $ResolvedVersion loading..." Write-Host "" Start-Sleep -Milliseconds 200 } function Confirm-Install { if ($AcceptEula) { return } if ([Console]::IsInputRedirected) { Fail "interactive confirmation required. Rerun with POOL_INSTALL_ACCEPT_EULA=1 to accept the EULA in headless scenarios." } Write-Host "By installing pool, you agree to the Poolside End User License Agreement: https://poolside.ai/eula" Write-Host "" while ($true) { $reply = Read-Host "Continue? [Y/n]" switch -Regex ($reply) { '^$' { return } '^[Yy]$' { return } '^[Nn]$' { Fail "installation canceled" } default { Write-Host "Please answer y or n." } } } } function Maybe-UpdatePath { if (-not $UpdatePath -or (Test-PathInUserPath $InstallDir)) { return } $currentUserPath = [Environment]::GetEnvironmentVariable("Path", "User") if ([string]::IsNullOrEmpty($currentUserPath)) { $newUserPath = $InstallDir } else { $newUserPath = "$currentUserPath;$InstallDir" } [Environment]::SetEnvironmentVariable("Path", $newUserPath, "User") $script:PathUpdated = $true Write-Host "Added $InstallDir to the user PATH. Restart your shell to pick it up." } function Maybe-PrintPathHint { if (Test-PathInCurrentPath $InstallDir) { return } Write-Host "" if ($PathUpdated -or (Test-PathInUserPath $InstallDir)) { Write-Host "pool was added to your user PATH, but this session has not reloaded it yet." Write-Host "Restart PowerShell, or update this session with:" Write-Host " `$env:Path = [System.Environment]::GetEnvironmentVariable('Path', 'User') + ';' + [System.Environment]::GetEnvironmentVariable('Path', 'Machine')" return } Write-Host "pool is not on your PATH yet." Write-Host "Add it for this session with:" Write-Host " `$env:Path = `"$InstallDir;`$env:Path`"" Write-Host "Or rerun with `$env:POOL_INSTALL_UPDATE_PATH=1 to update your user PATH automatically." } function Test-PathInCurrentPath([string]$Dir) { return Test-PathListContains $env:Path $Dir } function Test-PathInUserPath([string]$Dir) { return Test-PathListContains ([Environment]::GetEnvironmentVariable("Path", "User")) $Dir } function Test-PathListContains([string]$PathValue, [string]$Dir) { if ([string]::IsNullOrWhiteSpace($PathValue)) { return $false } foreach ($entry in $PathValue.Split([char[]]';', [System.StringSplitOptions]::RemoveEmptyEntries)) { if ($entry.TrimEnd('\') -ieq $Dir.TrimEnd('\')) { return $true } } return $false } function New-TempDir { $path = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName()) New-Item -ItemType Directory -Path $path | Out-Null return $path } function Fail([string]$Message) { throw "Error: $Message" } Main