#Requires -Version 5.0 <# .SYNOPSIS MAYROS Installer for Windows .DESCRIPTION Installs Node.js 22+ (if needed) and MAYROS via npm or from source. Usage: iwr -useb https://mayros.apilium.com/install.ps1 | iex .PARAMETER InstallMethod Install method: "npm" (default) or "git" .PARAMETER Tag npm dist-tag or version (default: "latest") .PARAMETER GitDir Checkout directory for git method (default: $env:USERPROFILE\mayros) .PARAMETER NoOnboard Skip onboarding wizard .PARAMETER NoGitUpdate Skip git pull for existing checkout .PARAMETER DryRun Print actions without executing #> param( [ValidateSet("npm", "git")] [string]$InstallMethod = $(if ($env:MAYROS_INSTALL_METHOD) { $env:MAYROS_INSTALL_METHOD } else { "npm" }), [string]$Tag = $(if ($env:MAYROS_VERSION) { $env:MAYROS_VERSION } else { "latest" }), [string]$GitDir = $(if ($env:MAYROS_GIT_DIR) { $env:MAYROS_GIT_DIR } else { "$env:USERPROFILE\mayros" }), [switch]$NoOnboard = $(if ($env:MAYROS_NO_ONBOARD -eq "1") { $true } else { $false }), [switch]$NoGitUpdate = $(if ($env:MAYROS_GIT_UPDATE -eq "0") { $true } else { $false }), [switch]$DryRun = $(if ($env:MAYROS_DRY_RUN -eq "1") { $true } else { $false }) ) $ErrorActionPreference = "Stop" $MayrosPkg = "@apilium/mayros" $MinNodeMajor = 22 # ── Helpers ─────────────────────────────────────────────────────────── function Write-Step { param([string]$Msg) Write-Host "[mayros] -> " -ForegroundColor Cyan -NoNewline Write-Host $Msg } function Write-Info { param([string]$Msg) Write-Host "[mayros] " -ForegroundColor Green -NoNewline Write-Host $Msg } function Write-Warn { param([string]$Msg) Write-Host "[mayros] " -ForegroundColor Yellow -NoNewline Write-Host $Msg } function Write-Err { param([string]$Msg) Write-Host "[mayros] " -ForegroundColor Red -NoNewline Write-Host $Msg } function Test-Command { param([string]$Name) $null -ne (Get-Command $Name -ErrorAction SilentlyContinue) } function Invoke-Run { param([string]$Cmd, [string[]]$Args) if ($DryRun) { Write-Info "[dry-run] $Cmd $($Args -join ' ')" } else { & $Cmd @Args if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) { throw "Command failed: $Cmd $($Args -join ' ') (exit code $LASTEXITCODE)" } } } # ── Step 1: Ensure Node 22+ ────────────────────────────────────────── function Get-NodeMajor { if (Test-Command "node") { try { $ver = (node -e "process.stdout.write(String(process.versions.node.split('.')[0]))" 2>$null) return [int]$ver } catch { return 0 } } return 0 } function Install-Node { $major = Get-NodeMajor if ($major -ge $MinNodeMajor) { $nodeVer = (node -v) Write-Info "Node $nodeVer detected (>= $MinNodeMajor)" return } Write-Step "Installing Node.js $MinNodeMajor..." # Try winget first if (Test-Command "winget") { Write-Step "Installing via winget..." Invoke-Run "winget" @("install", "--id", "OpenJS.NodeJS", "--version", "$MinNodeMajor.12.0", "--accept-package-agreements", "--accept-source-agreements") # Refresh PATH $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User") if (Get-NodeMajor -ge $MinNodeMajor) { Write-Info "Node installed via winget" return } } # Try Chocolatey if (Test-Command "choco") { Write-Step "Installing via Chocolatey..." Invoke-Run "choco" @("install", "nodejs", "--version=$MinNodeMajor.12.0", "-y") $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User") if (Get-NodeMajor -ge $MinNodeMajor) { Write-Info "Node installed via Chocolatey" return } } # Try Scoop if (Test-Command "scoop") { Write-Step "Installing via Scoop..." Invoke-Run "scoop" @("install", "nodejs") if (Get-NodeMajor -ge $MinNodeMajor) { Write-Info "Node installed via Scoop" return } } Write-Err "Could not install Node.js $MinNodeMajor automatically." Write-Err "Please install Node $MinNodeMajor+ from https://nodejs.org and re-run this script." exit 1 } # ── Step 2: Ensure Git ──────────────────────────────────────────────── function Install-Git { if (Test-Command "git") { return } if ($InstallMethod -eq "git") { Write-Err "Git is required for source installs." Write-Err "Install Git for Windows: https://git-scm.com/download/win" exit 1 } # For npm method, git is needed but not critical Write-Warn "Git not found. Some npm packages may fail to install." Write-Warn "Install Git for Windows: https://git-scm.com/download/win" } # ── Step 3a: npm install ───────────────────────────────────────────── function Install-Npm { $env:SHARP_IGNORE_GLOBAL_LIBVIPS = if ($env:SHARP_IGNORE_GLOBAL_LIBVIPS) { $env:SHARP_IGNORE_GLOBAL_LIBVIPS } else { "1" } Write-Step "Installing MAYROS ($Tag) via npm..." Invoke-Run "npm" @("install", "-g", "${MayrosPkg}@${Tag}", "--loglevel", "warn") # Verify $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User") if (Test-Command "mayros") { try { $ver = (mayros --version 2>$null) Write-Info "MAYROS $ver installed via npm" } catch { Write-Info "MAYROS installed via npm" } } else { # Try to find npm prefix and add to PATH $npmPrefix = (npm config get prefix 2>$null) if ($npmPrefix) { Write-Warn "mayros not found in PATH." Write-Warn "Add to PATH: $npmPrefix" Write-Warn "Or run: `$env:PATH += `";$npmPrefix`"" # Try to add to user PATH permanently $userPath = [System.Environment]::GetEnvironmentVariable("PATH", "User") if ($userPath -notlike "*$npmPrefix*") { [System.Environment]::SetEnvironmentVariable("PATH", "$userPath;$npmPrefix", "User") $env:PATH = "$env:PATH;$npmPrefix" Write-Info "Added $npmPrefix to user PATH" } } } } # ── Step 3b: git install ───────────────────────────────────────────── function Install-FromGit { Install-Git # Ensure pnpm if (-not (Test-Command "pnpm")) { Write-Step "Installing pnpm..." Invoke-Run "npm" @("install", "-g", "pnpm") } if (Test-Path "$GitDir\.git") { Write-Info "Found existing checkout at $GitDir" if (-not $NoGitUpdate) { Write-Step "Pulling latest changes..." Invoke-Run "git" @("-C", $GitDir, "pull", "--ff-only") } } else { Write-Step "Cloning MAYROS to $GitDir..." Invoke-Run "git" @("clone", "https://github.com/ApiliumCode/mayros.git", $GitDir) } Write-Step "Installing dependencies..." Push-Location $GitDir try { Invoke-Run "pnpm" @("install") Write-Step "Building..." Invoke-Run "pnpm" @("ui:build") Invoke-Run "pnpm" @("build") } finally { Pop-Location } # Create wrapper .cmd $binDir = "$env:USERPROFILE\.local\bin" New-Item -ItemType Directory -Force -Path $binDir | Out-Null $wrapperContent = "@echo off`r`nnode `"$GitDir\mayros.mjs`" %*" Set-Content -Path "$binDir\mayros.cmd" -Value $wrapperContent -Encoding ASCII # Add to PATH $userPath = [System.Environment]::GetEnvironmentVariable("PATH", "User") if ($userPath -notlike "*$binDir*") { [System.Environment]::SetEnvironmentVariable("PATH", "$userPath;$binDir", "User") $env:PATH = "$env:PATH;$binDir" Write-Info "Added $binDir to user PATH" } Write-Info "MAYROS installed from source at $GitDir" } # ── Step 4: Install AIngle Cortex ──────────────────────────────────── function Install-Cortex { Write-Step "Installing AIngle Cortex (semantic memory)..." $cortexRepo = "apilium/aingle" $installDir = "$env:USERPROFILE\.mayros\bin" # Detect architecture $osArch = if ([Environment]::Is64BitOperatingSystem) { if ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") { "aarch64" } else { "x86_64" } } else { Write-Warn "Cortex: 32-bit Windows not supported, skipping" return } $assetName = "aingle-cortex-windows-${osArch}.zip" $binaryPath = Join-Path $installDir "aingle-cortex.exe" # Already installed? if (Test-Path $binaryPath) { Write-Info "AIngle Cortex already installed at $binaryPath" return } # Fetch latest release try { $releaseUrl = "https://api.github.com/repos/${cortexRepo}/releases/latest" $release = Invoke-RestMethod -Uri $releaseUrl -Headers @{ Accept = "application/vnd.github+json" } -ErrorAction Stop } catch { Write-Warn "Cortex: could not fetch release info. Install later with: mayros update" return } $asset = $release.assets | Where-Object { $_.name -eq $assetName } | Select-Object -First 1 if (-not $asset) { Write-Warn "Cortex: asset '$assetName' not found in release $($release.tag_name). Install later with: mayros update" return } New-Item -ItemType Directory -Force -Path $installDir | Out-Null $archivePath = Join-Path $installDir $asset.name # Download Write-Step "Downloading $($asset.name)..." try { Invoke-WebRequest -Uri $asset.browser_download_url -OutFile $archivePath -ErrorAction Stop } catch { Write-Warn "Cortex: download failed. Install later with: mayros update" return } # Extract Write-Step "Extracting..." try { Expand-Archive -Path $archivePath -DestinationPath $installDir -Force } catch { Write-Warn "Cortex: extraction failed" Remove-Item $archivePath -ErrorAction SilentlyContinue return } Remove-Item $archivePath -ErrorAction SilentlyContinue if (Test-Path $binaryPath) { Write-Info "AIngle Cortex installed at $binaryPath" } else { Write-Warn "Cortex binary not found after extraction" } } # ── Step 5: Post-install ───────────────────────────────────────────── function Invoke-PostInstall { # Doctor check (best-effort) if (Test-Command "mayros") { Write-Step "Running health check..." try { mayros doctor --non-interactive 2>$null } catch {} } # Onboarding if ($NoOnboard) { Write-Info "Skipping onboarding (--NoOnboard)" return } if (Test-Command "mayros") { Write-Step "Starting onboarding wizard..." try { mayros onboard --install-daemon } catch {} } } # ── Main ────────────────────────────────────────────────────────────── function Main { Write-Host "" Write-Host " MAYROS Installer (Windows)" -ForegroundColor White Write-Host " ==========================" -ForegroundColor DarkGray Write-Host "" Install-Node Install-Git if ($InstallMethod -eq "npm") { Install-Npm } else { Install-FromGit } Install-Cortex Invoke-PostInstall Write-Host "" Write-Info "Installation complete!" Write-Info "Open a new PowerShell window, then run: mayros dashboard" Write-Host "" } Main