# install.ps1 — iria-monitor Windows installer # Usage: irm https://ai.obsly.io/install.ps1 | iex # or: powershell -ExecutionPolicy Bypass -File install.ps1 Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" $TelemetryUrl = "http://plexus-monitor.iria.tech/api/otel/v1/logs" $PackageName = "obsly-ai" $BinaryName = "iria-monitor" $MinPython = [version]"3.10" $MaxPython = [version]"3.13.99" # --- Helpers --------------------------------------------------------------- function Write-Step($msg) { Write-Host "=> $msg" -ForegroundColor Cyan } function Write-Ok($msg) { Write-Host " $msg" -ForegroundColor Green } function Write-Err($msg) { Write-Host " $msg" -ForegroundColor Red } function Get-OsVersion { try { [System.Environment]::OSVersion.Version.ToString() } catch { "unknown" } } function Send-Telemetry { param( [int]$ExitCode, [string]$Message, [string]$PythonVersion, [string]$PythonSource, [bool]$PipxPreinstalled, [string]$FailedStep ) $ctx = @{ os_version = Get-OsVersion python_source = $PythonSource pipx_preinstalled = $PipxPreinstalled extras = "core" } if ($FailedStep) { $ctx["failed_step"] = $FailedStep } $attrs = @( @{ key = "event.name"; value = @{ stringValue = "cli.install" } }, @{ key = "platform"; value = @{ stringValue = "windows" } }, @{ key = "python.version"; value = @{ stringValue = $PythonVersion } }, @{ key = "install.kind"; value = @{ stringValue = "pipx" } }, @{ key = "exit.code"; value = @{ intValue = [string]$ExitCode } }, @{ key = "context.os_version"; value = @{ stringValue = $ctx["os_version"] } }, @{ key = "context.python_source"; value = @{ stringValue = $ctx["python_source"] } }, @{ key = "context.pipx_preinstalled"; value = @{ boolValue = $ctx["pipx_preinstalled"] } }, @{ key = "context.extras"; value = @{ stringValue = $ctx["extras"] } } ) if ($Message) { $attrs += @{ key = "event.message"; value = @{ stringValue = $Message } } } if ($FailedStep) { $attrs += @{ key = "context.failed_step"; value = @{ stringValue = $FailedStep } } } $payload = @{ resourceLogs = @(@{ resource = @{ attributes = @( @{ key = "service.name"; value = @{ stringValue = "iria-monitor-cli" } }, @{ key = "telemetry.sdk.language"; value = @{ stringValue = "powershell" } }, @{ key = "deployment.environment"; value = @{ stringValue = "cli" } } ) } scopeLogs = @(@{ scope = @{ name = "iria-monitor-cli.telemetry" } logRecords = @(@{ timeUnixNano = [string]([DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() * 1000000) severityText = $(if ($ExitCode -eq 0) { "INFO" } else { "ERROR" }) severityNumber = $(if ($ExitCode -eq 0) { 9 } else { 17 }) body = @{ stringValue = "cli.install" } attributes = $attrs }) }) }) } | ConvertTo-Json -Depth 8 try { Invoke-RestMethod -Uri $TelemetryUrl -Method Post ` -ContentType "application/json" -Body $payload ` -TimeoutSec 5 | Out-Null } catch { # telemetry is best-effort — never block install } } # --- Step 1: Find Python 3.10-3.13 ---------------------------------------- Write-Step "Looking for Python 3.10-3.13..." $PythonCmd = $null $PythonVer = $null $PythonSource = "unknown" # Try the py launcher first (preferred on Windows) $pyLauncher = Get-Command py -ErrorAction SilentlyContinue if ($pyLauncher) { foreach ($minor in @(13, 12, 11, 10)) { try { $out = & py "-3.$minor" -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}')" 2>$null if ($LASTEXITCODE -eq 0 -and $out) { $ver = [version]$out.Trim() if ($ver -ge $MinPython -and $ver -le $MaxPython) { $PythonCmd = @("py", "-3.$minor") $PythonVer = $out.Trim() $PythonSource = "py_launcher" break } } } catch { continue } } } # Fallback: python / python3 in PATH if (-not $PythonCmd) { foreach ($candidate in @("python3", "python")) { $cmd = Get-Command $candidate -ErrorAction SilentlyContinue if ($cmd) { try { $out = & $candidate -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}')" 2>$null if ($LASTEXITCODE -eq 0 -and $out) { $ver = [version]$out.Trim() if ($ver -ge $MinPython -and $ver -le $MaxPython) { $PythonCmd = @($candidate) $PythonVer = $out.Trim() $PythonSource = "path" break } } } catch { continue } } } } if (-not $PythonCmd) { Write-Err "No Python 3.10-3.13 found." Write-Err "Python 3.14+ is not supported yet (numpy/matplotlib won't build)." Write-Host "" Write-Host "Install Python 3.13 from: https://www.python.org/downloads/" -ForegroundColor Yellow Send-Telemetry -ExitCode 1 -Message "no compatible python" ` -PythonVersion "none" -PythonSource "none" ` -PipxPreinstalled $false -FailedStep "python_detection" exit 1 } Write-Ok "Found Python $PythonVer ($PythonSource)" # --- Step 2: Install pipx if needed --------------------------------------- Write-Step "Checking for pipx..." $PipxPreinstalled = $false $pipxCheck = & @PythonCmd -m pipx --version 2>$null if ($LASTEXITCODE -eq 0) { $PipxPreinstalled = $true Write-Ok "pipx already installed ($($pipxCheck.Trim()))" } else { Write-Step "Installing pipx..." & @PythonCmd -m pip install --user pipx 2>&1 | Out-Null if ($LASTEXITCODE -ne 0) { Write-Err "Failed to install pipx." Write-Host "Try manually: $($PythonCmd -join ' ') -m pip install --user pipx" -ForegroundColor Yellow Send-Telemetry -ExitCode 2 -Message "pipx install failed" ` -PythonVersion $PythonVer -PythonSource $PythonSource ` -PipxPreinstalled $false -FailedStep "pipx_install" exit 1 } # verify it works now & @PythonCmd -m pipx --version 2>$null | Out-Null if ($LASTEXITCODE -ne 0) { Write-Err "pipx installed but not reachable via '$($PythonCmd -join ' ') -m pipx'." Send-Telemetry -ExitCode 2 -Message "pipx not reachable after install" ` -PythonVersion $PythonVer -PythonSource $PythonSource ` -PipxPreinstalled $false -FailedStep "pipx_verify" exit 1 } Write-Ok "pipx installed" } # --- Step 3: Install obsly-ai --------------------------------------------- Write-Step "Installing $PackageName..." $installOutput = & @PythonCmd -m pipx install $PackageName --python (@PythonCmd -join ' ') 2>&1 if ($LASTEXITCODE -ne 0) { # check if already installed if ($installOutput -match "already (installed|exists)") { Write-Ok "$PackageName already installed, upgrading..." & @PythonCmd -m pipx upgrade $PackageName 2>&1 | Out-Null } else { Write-Err "Failed to install $PackageName." Write-Host ($installOutput | Out-String) -ForegroundColor Yellow Send-Telemetry -ExitCode 3 -Message "package install failed" ` -PythonVersion $PythonVer -PythonSource $PythonSource ` -PipxPreinstalled $PipxPreinstalled -FailedStep "package_install" exit 1 } } else { Write-Ok "$PackageName installed" } # --- Step 4: Ensure PATH -------------------------------------------------- Write-Step "Configuring PATH..." & @PythonCmd -m pipx ensurepath 2>&1 | Out-Null Write-Ok "PATH updated (restart your terminal to pick it up)" # --- Step 5: Verify ------------------------------------------------------- Write-Step "Verifying installation..." # pipx puts binaries here — check directly without relying on PATH refresh $pipxBinDir = & @PythonCmd -c "import sysconfig; print(sysconfig.get_path('scripts', 'nt_user'))" 2>$null if (-not $pipxBinDir) { $pipxBinDir = "$env:USERPROFILE\.local\bin" } $binaryPath = Join-Path $pipxBinDir "$BinaryName.exe" if (Test-Path $binaryPath) { $versionOut = & $binaryPath version 2>$null if ($LASTEXITCODE -eq 0) { Write-Ok "$BinaryName $($versionOut.Trim()) is ready" } else { Write-Ok "$BinaryName installed at $binaryPath (could not read version)" } } else { # try from PATH anyway $fromPath = Get-Command $BinaryName -ErrorAction SilentlyContinue if ($fromPath) { Write-Ok "$BinaryName found in PATH" } else { Write-Err "$BinaryName not found. You may need to restart your terminal." Write-Host "Expected location: $binaryPath" -ForegroundColor Yellow } } # --- Step 6: Telemetry (success) ------------------------------------------ Send-Telemetry -ExitCode 0 -Message "ok" ` -PythonVersion $PythonVer -PythonSource $PythonSource ` -PipxPreinstalled $PipxPreinstalled -FailedStep "" # --- Done ------------------------------------------------------------------ Write-Host "" Write-Host "Installation complete!" -ForegroundColor Green Write-Host "" Write-Host "Quick start:" -ForegroundColor Cyan Write-Host " $BinaryName init # Set up in current repo" Write-Host " $BinaryName status # Check configuration" Write-Host " $BinaryName version # Show version" Write-Host "" Write-Host "Note:" -ForegroundColor Yellow Write-Host " AI code attribution (git hooks) requires Git for Windows," Write-Host " which includes Git Bash. Download from: https://git-scm.com/download/win" Write-Host " Most standard Git installations already include it." Write-Host "" Write-Host "If '$BinaryName' is not recognized, restart your terminal." -ForegroundColor Yellow