param( [string]$Image = "docker.io/qiayanai/kageos:latest", [string]$Version = "", [string]$Name = "kageos", [string]$Volume = "kageos-data", [int]$Port = 8080, [string]$BaseUrl = "", [ValidateSet("auto", "podman", "docker")] [string]$Engine = "auto", [switch]$InstallPodmanDesktop, [switch]$NoInstallRuntime, [switch]$Force, [switch]$DryRun, [int]$StartTimeout = 1200 ) $ErrorActionPreference = "Stop" if ($Version) { $Image = "docker.io/qiayanai/kageos:$Version" } if (-not $BaseUrl) { $BaseUrl = "http://localhost:$Port" } function Write-Step { param([string]$Message) Write-Host "==> $Message" -ForegroundColor Cyan } function Write-Warn { param([string]$Message) Write-Host "WARN: $Message" -ForegroundColor Yellow } function Fail { param([string]$Message) Write-Host "ERROR: $Message" -ForegroundColor Red exit 1 } function Command-Exists { param([string]$Command) return $null -ne (Get-Command $Command -ErrorAction SilentlyContinue) } function Invoke-Engine { param([Parameter(ValueFromRemainingArguments = $true)][string[]]$EngineArgs) & $script:EngineCommand @EngineArgs } function Test-EngineInfo { param([string]$Command) try { & $Command info *> $null return $true } catch { return $false } } function Test-ContainerWithEngine { param([string]$Command) try { & $Command container inspect $Name *> $null return $true } catch { return $false } } function Ensure-PodmanMachine { try { podman machine list *> $null } catch { return $false } try { podman machine inspect *> $null podman machine start *> $null } catch { try { Write-Step "Creating Podman machine" podman machine init podman machine start } catch { return $false } } return (Test-EngineInfo "podman") } function Install-PodmanDesktopIfAllowed { if ($NoInstallRuntime) { return $false } if (-not (Command-Exists "winget")) { return $false } $shouldInstall = $InstallPodmanDesktop if (-not $shouldInstall) { Write-Host "" Write-Host "Podman Desktop is required for the kageos local desktop runtime." Write-Host "Install it now with winget? This installs RedHat.Podman-Desktop. [y/N]" $answer = Read-Host $shouldInstall = $answer -match "^(y|yes)$" } if (-not $shouldInstall) { return $false } Write-Step "Installing Podman Desktop with winget" winget install -e --id RedHat.Podman-Desktop Write-Host "" Write-Host "Podman Desktop was installed or updated." Write-Host "Open Podman Desktop, finish onboarding, then rerun this command:" Write-Host " iwr https://kageos.com/install.ps1 -OutFile install.ps1" Write-Host " powershell -ExecutionPolicy Bypass -File .\install.ps1" exit 0 } function Select-Engine { if ($Engine -eq "podman" -or $Engine -eq "auto") { if (Command-Exists "podman") { if ((Test-EngineInfo "podman") -or (Ensure-PodmanMachine)) { $script:SelectedEngine = "podman" $script:EngineCommand = "podman" Write-Step "Using container engine: podman" return } Write-Warn "Podman is installed but not usable. Open Podman Desktop and complete onboarding." } elseif ($Engine -eq "podman") { Install-PodmanDesktopIfAllowed | Out-Null Fail "Podman is not installed. Install Podman Desktop from https://podman-desktop.io/downloads, then rerun." } } if ($Engine -eq "auto") { Install-PodmanDesktopIfAllowed | Out-Null } if ($Engine -eq "auto" -and (Command-Exists "docker") -and (Test-ContainerWithEngine "docker") -and (Test-EngineInfo "docker")) { Write-Warn "Using existing Docker container. New installs should use Podman Desktop." $script:SelectedEngine = "docker" $script:EngineCommand = "docker" Write-Step "Using container engine: docker" return } if ($Engine -eq "docker") { if (Command-Exists "docker" -and (Test-EngineInfo "docker")) { Write-Warn "Using Docker compatibility fallback. Podman Desktop is the recommended local runtime for kageos." $script:SelectedEngine = "docker" $script:EngineCommand = "docker" Write-Step "Using container engine: docker" return } } if ($Engine -eq "auto" -and (Command-Exists "docker")) { Write-Warn "Docker was detected but is not used automatically. Rerun with -Engine docker to use the compatibility fallback." } Fail "No usable Podman found. Install Podman Desktop, finish onboarding, then rerun this installer." } function Container-Exists { try { Invoke-Engine container inspect $Name *> $null return $true } catch { return $false } } function Container-Running { try { $running = Invoke-Engine inspect -f "{{.State.Running}}" $Name return ($running -eq "true") } catch { return $false } } function Install-Helper { $helperDir = Join-Path $env:USERPROFILE ".kageos\bin" New-Item -ItemType Directory -Force -Path $helperDir *> $null $helper = Join-Path $helperDir "kageos.ps1" @" param([string]`$Command = "status") `$Name = "`$env:KAGEOS_CONTAINER_NAME" if (-not `$Name) { `$Name = "$Name" } `$Engine = "$script:SelectedEngine" switch (`$Command) { "status" { & `$Engine ps -a --filter "name=`$Name"; break } "logs" { & `$Engine logs -f `$Name; break } "password" { & `$Engine exec `$Name sh -lc 'cat /var/lib/kageos/secrets/SYSTEM_USER_PASSWORD'; break } "url" { & `$Engine exec `$Name sh -lc 'printf "%s\n" "`${CANONICAL_BASE_URL:-}"'; break } "start" { & `$Engine start `$Name; break } "stop" { & `$Engine stop `$Name; break } "restart" { & `$Engine restart `$Name; break } default { Write-Host "Usage: kageos.ps1 status|logs|password|url|start|stop|restart" } } "@ | Set-Content -Encoding UTF8 $helper Write-Step "Installed helper: $helper" } function Start-Container { Write-Step "Creating persistent volume: $Volume" Invoke-Engine volume create $Volume *> $null Write-Step "Pulling image: $Image" Invoke-Engine pull $Image Write-Step "Starting kageos container: $Name" Invoke-Engine run -d ` --name $Name ` --privileged ` --restart unless-stopped ` -p "$Port`:80" ` -v "$Volume`:/var/lib/kageos" ` -e "CANONICAL_BASE_URL=$BaseUrl" ` -e "KAGEOS_AIO_PRINT_SECRETS=1" ` $Image *> $null } function Wait-Container { Write-Step "Waiting for kageos to finish first boot. This can take several minutes." $deadline = (Get-Date).AddSeconds($StartTimeout) while ((Get-Date) -lt $deadline) { $logs = Invoke-Engine logs $Name 2>&1 | Out-String if ($logs -match "kageos started successfully") { return } if (-not (Container-Running)) { Invoke-Engine logs $Name 2>&1 | Select-Object -Last 120 Fail "kageos container stopped before startup completed." } Start-Sleep -Seconds 5 } Invoke-Engine logs $Name 2>&1 | Select-Object -Last 120 Fail "Startup did not finish within $StartTimeout seconds." } function Print-Summary { $password = "" try { $password = Invoke-Engine exec $Name sh -lc "cat /var/lib/kageos/secrets/SYSTEM_USER_PASSWORD 2>/dev/null || true" } catch {} Write-Host "" Write-Host "kageos installed successfully" -ForegroundColor Green Write-Host "" Write-Host "URL: $BaseUrl" Write-Host "Username: system" Write-Host "Password: $password" Write-Host "" Write-Host "Useful commands:" Write-Host " $script:SelectedEngine logs -f $Name" Write-Host " $script:SelectedEngine exec $Name cat /var/lib/kageos/secrets/SYSTEM_USER_PASSWORD" Write-Host " $env:USERPROFILE\.kageos\bin\kageos.ps1 password" } Write-Host "kageos Windows local trial installer" Write-Host "" Write-Host "Image: $Image" Write-Host "Container: $Name" Write-Host "Volume: $Volume" Write-Host "Port: ${Port}:80" Write-Host "URL: $BaseUrl" Select-Engine if ($DryRun) { Write-Step "Dry run complete. No container was created." exit 0 } if (Container-Exists) { if ($Force) { Write-Step "Removing existing container: $Name" Invoke-Engine rm -f $Name *> $null } else { if (-not (Container-Running)) { Write-Step "Starting existing container: $Name" Invoke-Engine start $Name *> $null } Install-Helper Print-Summary exit 0 } } Start-Container Install-Helper Wait-Container Print-Summary