# Vigthoria CLI Installer for Windows # Usage: irm https://extension.vigthoria.io/downloads/install.ps1 | iex # Alternative: PowerShell -ExecutionPolicy Bypass -File install.ps1 $ErrorActionPreference = "Stop" # Configuration $CLI_VERSION = "1.13.42" $INSTALL_DIR = "$env:USERPROFILE\.vigthoria" $MANIFEST_URL = "https://extension.vigthoria.io/downloads/manifest.json" $HOSTED_TARBALL_URL = "https://extension.vigthoria.io/downloads/vigthoria-cli-$CLI_VERSION.tgz" # Packaged installs resolve the signed manifest. The standalone hosted script # is pinned after packing to the immutable archive digest; a pipe-to-iex copy # stays on the same canonical extension archive. $HOSTED_TARBALL_SHA256 = "eea27c6c0f5d935feb4c8c87d16ce25f0597216f7c7188b6fe9eaf22facd1bdd" $RELEASE_RESOLVER = if ($PSScriptRoot) { Join-Path $PSScriptRoot "scripts\release\resolve-release-manifest.mjs" } else { "" } $RELEASE_INSTALLER = if ($PSScriptRoot) { Join-Path $PSScriptRoot "scripts\release\install-release.mjs" } else { "" } function Assert-TrustedReleaseUrl([string]$Value) { $allowed = @("extension.vigthoria.io") try { $uri = [Uri]$Value } catch { throw "Invalid release URL" } if ($uri.Scheme -ne "https" -or -not $uri.IsDefaultPort -or $uri.UserInfo -or $allowed -notcontains $uri.DnsSafeHost.ToLowerInvariant() -or $Value.Contains("\")) { throw "Release URL must use HTTPS on an exact approved host and port" } return $uri.AbsoluteUri } function Resolve-ReleaseManifest { # The hosted path is enabled only when the packaged shared policy verifier # can validate strict metadata and the detached Ed25519 signature. if (-not $RELEASE_RESOLVER -or -not (Test-Path -LiteralPath $RELEASE_RESOLVER)) { return } try { $trustedManifest = Assert-TrustedReleaseUrl $MANIFEST_URL $resolvedJson = & node $RELEASE_RESOLVER $trustedManifest stable if ($LASTEXITCODE -ne 0) { throw "release resolver exited with status $LASTEXITCODE" } $resolved = $resolvedJson | ConvertFrom-Json if ($resolved.version) { $script:CLI_VERSION = [string]$resolved.version } if ($resolved.url) { $script:HOSTED_TARBALL_URL = Assert-TrustedReleaseUrl ([string]$resolved.url) } if ($resolved.sha256 -and ([string]$resolved.sha256) -match '^[a-fA-F0-9]{64}$') { $script:HOSTED_TARBALL_SHA256 = ([string]$resolved.sha256).ToLowerInvariant() } } catch { Write-Host "Signed release manifest unavailable, using exact pinned fallback $CLI_VERSION" -ForegroundColor Yellow } } Resolve-ReleaseManifest function Write-Banner { Write-Host "" Write-Host " +-----------------------------------------------------------+" -ForegroundColor Cyan Write-Host " | |" -ForegroundColor Cyan Write-Host " | VIGTHORIA CLI INSTALLER v$CLI_VERSION |" -ForegroundColor Cyan Write-Host " | AI-Powered Terminal Coding Assistant |" -ForegroundColor Cyan Write-Host " | |" -ForegroundColor Cyan Write-Host " +-----------------------------------------------------------+" -ForegroundColor Cyan Write-Host "" } function Test-NetworkConnectivity { Write-Host "[CHECK] Checking network connectivity..." -ForegroundColor Yellow try { $trustedTarball = Assert-TrustedReleaseUrl $HOSTED_TARBALL_URL $response = Invoke-WebRequest -Uri $trustedTarball -Method Head -UseBasicParsing -MaximumRedirection 0 -TimeoutSec 10 -ErrorAction Stop Write-Host "[OK] Hosted CLI package is reachable" -ForegroundColor Green return $true } catch { Write-Host "[ERROR] Cannot reach the canonical extension package" -ForegroundColor Red return $false } } function Test-Prerequisites { Write-Host "[CHECK] Checking prerequisites..." -ForegroundColor Yellow # Check Node.js try { $nodeVersion = node -v 2>$null if ($nodeVersion) { $versionText = $nodeVersion.TrimStart('v') $parsedVersion = [version]$versionText if ($parsedVersion -ge [version]"20.19.0") { Write-Host "[OK] Node.js $nodeVersion" -ForegroundColor Green } else { Write-Host "[ERROR] Node.js 20.19+ required (found $nodeVersion)" -ForegroundColor Red Write-Host " Download from: https://nodejs.org/" -ForegroundColor Yellow return $false } } } catch { Write-Host "[ERROR] Node.js is not installed" -ForegroundColor Red Write-Host "" Write-Host "Please install Node.js first:" -ForegroundColor Yellow Write-Host " Option 1: winget install OpenJS.NodeJS" -ForegroundColor White Write-Host " Option 2: Download from https://nodejs.org/" -ForegroundColor White Write-Host "" return $false } # Check npm try { $npmVersion = npm -v 2>$null if ($npmVersion) { Write-Host "[OK] npm v$npmVersion" -ForegroundColor Green } } catch { Write-Host "[ERROR] npm is not installed" -ForegroundColor Red return $false } return $true } function Get-VigthoriaTempRoot { $explicitRoot = $env:VIGTHORIA_TEMP_DIR $candidate = if ($explicitRoot) { $explicitRoot } else { Join-Path $env:USERPROFILE ".vigthoria\tmp" } if (-not [IO.Path]::IsPathRooted($candidate)) { throw "VIGTHORIA_TEMP_DIR must be an absolute local path" } if ($candidate.StartsWith("\\") -or $candidate.StartsWith("\\?\") -or $candidate.StartsWith("\\.\")) { throw "VIGTHORIA_TEMP_DIR cannot use a UNC or Windows device path" } $fullRoot = [IO.Path]::GetFullPath($candidate).TrimEnd([IO.Path]::DirectorySeparatorChar) $volumeRoot = [IO.Path]::GetPathRoot($fullRoot).TrimEnd([IO.Path]::DirectorySeparatorChar) if ($fullRoot -eq $volumeRoot -or $fullRoot -eq [IO.Path]::GetTempPath().TrimEnd([IO.Path]::DirectorySeparatorChar)) { throw "Refusing shared or filesystem-root temporary storage" } New-Item -ItemType Directory -Path $fullRoot -Force -ErrorAction Stop | Out-Null $cursor = Get-Item -LiteralPath $fullRoot -Force -ErrorAction Stop while ($cursor) { if (($cursor.Attributes -band [IO.FileAttributes]::ReparsePoint) -ne 0) { throw "Refusing a reparse-point temporary storage path" } if ($cursor.FullName -eq [IO.Path]::GetPathRoot($cursor.FullName)) { break } $cursor = $cursor.Parent } if (-not $explicitRoot) { $homeRoot = [IO.Path]::GetFullPath($env:USERPROFILE).TrimEnd([IO.Path]::DirectorySeparatorChar) if (-not ($fullRoot.Equals($homeRoot, [StringComparison]::OrdinalIgnoreCase) -or $fullRoot.StartsWith($homeRoot + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase))) { throw "Default temporary storage escaped the user profile" } } return $fullRoot } function Invoke-VigthoriaTransactionalInstaller { param( [Parameter(Mandatory = $true)][string]$ArchivePath, [Parameter(Mandatory = $true)][string]$ExtractionRoot ) $installer = $RELEASE_INSTALLER if (-not ($installer -and (Test-Path -LiteralPath $installer))) { $tarCommand = Get-Command tar.exe -ErrorAction SilentlyContinue if (-not $tarCommand) { throw "tar.exe is required to activate the verified release" } $bootstrapRoot = Join-Path $ExtractionRoot "bootstrap" New-Item -ItemType Directory -Path $bootstrapRoot -Force -ErrorAction Stop | Out-Null & $tarCommand.Source -xf $ArchivePath -C $bootstrapRoot if ($LASTEXITCODE -ne 0) { throw "verified release bootstrap extraction exited with status $LASTEXITCODE" } $installer = Join-Path $bootstrapRoot "package\scripts\release\install-release.mjs" if (-not (Test-Path -LiteralPath $installer -PathType Leaf)) { throw "Verified release does not contain its transactional installer" } } & node $installer $ArchivePath $CLI_VERSION if ($LASTEXITCODE -ne 0) { throw "transactional installer exited with status $LASTEXITCODE" } } function Install-VigthoriaCLI-Package { Write-Host "" Write-Host "[INSTALL] Installing Vigthoria CLI..." -ForegroundColor Cyan if ($HOSTED_TARBALL_SHA256) { $releaseTemp = Join-Path (Get-VigthoriaTempRoot) ("install-" + [Guid]::NewGuid().ToString("N")) $releaseArchive = Join-Path $releaseTemp "candidate.tgz" try { New-Item -ItemType Directory -Path $releaseTemp -Force | Out-Null $trustedTarball = Assert-TrustedReleaseUrl $HOSTED_TARBALL_URL Invoke-WebRequest -Uri $trustedTarball -OutFile $releaseArchive -UseBasicParsing -MaximumRedirection 0 -TimeoutSec 60 -ErrorAction Stop $actualSha256 = (Get-FileHash -Path $releaseArchive -Algorithm SHA256).Hash.ToLowerInvariant() if ($actualSha256 -ne $HOSTED_TARBALL_SHA256) { throw "Release checksum mismatch" } Invoke-VigthoriaTransactionalInstaller $releaseArchive $releaseTemp Write-Host "[OK] Vigthoria CLI installed from the checksum-verified release!" -ForegroundColor Green return $true } catch { Write-Host "[ERROR] Verified extension package install failed: $_" -ForegroundColor Red return $false } finally { if (Test-Path $releaseTemp) { Remove-Item -Recurse -Force $releaseTemp } } } else { Write-Host "[WARN] Hosted package has no trusted manifest SHA-256; skipping direct URL installation." -ForegroundColor Yellow } return $false } function Install-VigthoriaCLI-Direct { Write-Host "" Write-Host "[INSTALL] Installing Vigthoria CLI (Direct Download)..." -ForegroundColor Cyan $releaseTemp = Join-Path (Get-VigthoriaTempRoot) ("direct-" + [Guid]::NewGuid().ToString("N")) $tarballPath = Join-Path $releaseTemp "candidate.tgz" try { if (-not $HOSTED_TARBALL_SHA256) { throw "Direct installation requires a signed manifest SHA-256" } New-Item -ItemType Directory -Path $releaseTemp -ErrorAction Stop | Out-Null $tarballUrl = Assert-TrustedReleaseUrl $HOSTED_TARBALL_URL Invoke-WebRequest -Uri $tarballUrl -OutFile $tarballPath -UseBasicParsing -MaximumRedirection 0 -TimeoutSec 60 -ErrorAction Stop $actualSha256 = (Get-FileHash -Path $tarballPath -Algorithm SHA256).Hash.ToLowerInvariant() if ($actualSha256 -ne $HOSTED_TARBALL_SHA256) { throw "Release checksum mismatch" } Invoke-VigthoriaTransactionalInstaller $tarballPath $releaseTemp Write-Host "[OK] Vigthoria CLI installed successfully!" -ForegroundColor Green return $true } catch { Write-Host "[ERROR] Direct install failed: $_" -ForegroundColor Red return $false } finally { if (Test-Path $releaseTemp) { Remove-Item -Recurse -Force $releaseTemp } } } function Show-NetworkTroubleshooting { Write-Host "" Write-Host "===============================================================" -ForegroundColor Yellow Write-Host " NETWORK TROUBLESHOOTING" -ForegroundColor Yellow Write-Host "===============================================================" -ForegroundColor Yellow Write-Host "" Write-Host "The canonical extension package could not be reached. Try these solutions:" -ForegroundColor White Write-Host "" Write-Host "1. Check your internet connection" -ForegroundColor Cyan Write-Host " Test-NetConnection extension.vigthoria.io -Port 443" -ForegroundColor Gray Write-Host "" Write-Host "2. If behind a corporate proxy, configure npm:" -ForegroundColor Cyan Write-Host " npm config set proxy http://proxy.company.com:8080" -ForegroundColor Gray Write-Host " npm config set https-proxy http://proxy.company.com:8080" -ForegroundColor Gray Write-Host "" Write-Host "3. Check Windows Firewall/Antivirus settings" -ForegroundColor Cyan Write-Host "" Write-Host "4. Try using a VPN" -ForegroundColor Cyan Write-Host "" Write-Host "5. Canonical manual download:" -ForegroundColor Cyan Write-Host " Hosted package: $HOSTED_TARBALL_URL" -ForegroundColor Gray Write-Host "" } function Show-Success { Write-Host "" Write-Host "===============================================================" -ForegroundColor Green Write-Host " INSTALLATION COMPLETE!" -ForegroundColor Green Write-Host "===============================================================" -ForegroundColor Green Write-Host "" Write-Host " Get started:" -ForegroundColor White Write-Host " 1. Open a new terminal" -ForegroundColor Gray Write-Host " 2. Login: vigthoria login" -ForegroundColor Gray Write-Host " 3. Start: vigthoria chat" -ForegroundColor Gray Write-Host " Or local project start: npx vigthoria-chat" -ForegroundColor Gray Write-Host "" Write-Host " Commands:" -ForegroundColor White Write-Host " vigthoria chat - Start AI chat" -ForegroundColor Gray Write-Host " vigthoria-chat - Start chat directly" -ForegroundColor Gray Write-Host " vigthoria edit - Edit files with AI" -ForegroundColor Gray Write-Host " vigthoria repo - Manage cloud repos" -ForegroundColor Gray Write-Host " vig --help - Show all commands" -ForegroundColor Gray Write-Host "" Write-Host " Documentation: https://docs.vigthoria.io/cli" -ForegroundColor Cyan Write-Host "" } # Main installation flow Write-Banner if (-not (Test-Prerequisites)) { exit 1 } $networkOk = Test-NetworkConnectivity if ($networkOk) { if (Install-VigthoriaCLI-Package) { Show-Success exit 0 } } # Network or canonical package installation failed Show-NetworkTroubleshooting Write-Host "Would you like to try the checksum-verified direct download?" -ForegroundColor Yellow $choice = Read-Host "Enter 'y' to try direct download or 'n' to exit" switch ($choice.ToLower()) { 'y' { if (Install-VigthoriaCLI-Direct) { Show-Success exit 0 } exit 1 } default { Write-Host "Installation cancelled." -ForegroundColor Gray exit 1 } }