# Install the official Windows x64 CLI. PowerShell 5.1+ and Windows tar.exe are required. # Keep installation inside a complete script block for piped invocation. & { param([string]$Version = 'latest') $ErrorActionPreference = 'Stop' $work = $null try { if ($env:OS -ne 'Windows_NT') { throw 'This installer requires Windows.' } $architecture = $env:PROCESSOR_ARCHITEW6432 if (-not $architecture) { $architecture = $env:PROCESSOR_ARCHITECTURE } if ($architecture -ne 'AMD64') { throw 'The current Windows release supports x64 only.' } if ($Version -ne 'latest' -and $Version -notmatch '^v[0-9]+\.[0-9]+\.[0-9]+([+-][A-Za-z0-9.-]+)?$') { throw 'Use a release tag such as v0.1.0.' } $tar = (Get-Command tar.exe -ErrorAction Stop).Source [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 $repository = 'liusha-com/wasmd' if ($Version -eq 'latest') { $release = Invoke-RestMethod -Uri "https://api.github.com/repos/$repository/releases/latest" -TimeoutSec 60 $Version = $release.tag_name if ($Version -notmatch '^v[0-9]+\.[0-9]+\.[0-9]+([+-][A-Za-z0-9.-]+)?$') { throw 'GitHub returned an invalid release tag.' } } $installRoot = $env:WASMD_INSTALL if (-not $installRoot) { $installRoot = Join-Path $env:USERPROFILE '.wasmd' } if ($installRoot -notmatch '^[A-Za-z]:[\\/]' -or $installRoot -match '[\r\n;]') { throw 'WASMD_INSTALL must be an absolute local drive path without newlines or semicolons.' } $bin = Join-Path ([IO.Path]::GetFullPath($installRoot)) 'bin' New-Item -ItemType Directory -Path $bin -Force | Out-Null $work = Join-Path $bin ('.wasmd-install-' + [Guid]::NewGuid().ToString('N')) New-Item -ItemType Directory -Path $work | Out-Null $archive = 'wasmd-x86_64-pc-windows-msvc.tar.gz' $archivePath = Join-Path $work $archive $checksums = Join-Path $work 'SHA256SUMS' $base = "https://github.com/$repository/releases/download/$Version" Write-Host "Installing Wasmd $Version for Windows x64..." Invoke-WebRequest -UseBasicParsing -Uri "$base/$archive" -OutFile $archivePath -TimeoutSec 600 Invoke-WebRequest -UseBasicParsing -Uri "$base/SHA256SUMS" -OutFile $checksums -TimeoutSec 60 $entries = @(Get-Content -LiteralPath $checksums | Where-Object { $_ -match ('^[0-9a-fA-F]{64}\s+\*?' + [regex]::Escape($archive) + '$') }) if ($entries.Count -ne 1) { throw 'Missing, duplicate, or invalid SHA-256 entry.' } $expected = ($entries[0] -split '\s+')[0] if ((Get-FileHash -LiteralPath $archivePath -Algorithm SHA256).Hash -ne $expected) { throw 'SHA-256 mismatch.' } $listing = @(& $tar -tzf $archivePath) if ($LASTEXITCODE -ne 0) { throw 'Invalid release archive.' } $members = @($listing | Where-Object { $_ -ceq 'wasmd.exe' -or $_ -ceq './wasmd.exe' }) if ($members.Count -ne 1) { throw 'Archive must contain exactly one wasmd.exe.' } $entry = @(& $tar -tvzf $archivePath -- $members[0]) if ($LASTEXITCODE -ne 0 -or $entry.Count -ne 1 -or -not $entry[0].StartsWith('-')) { throw 'Release binary must be a regular file.' } & $tar -xzf $archivePath -C $work -- $members[0] if ($LASTEXITCODE -ne 0) { throw 'Cannot extract release binary.' } $staged = Join-Path $work 'wasmd.exe' $actualVersion = & $staged --version if ($LASTEXITCODE -ne 0 -or $actualVersion -cne "wasmd $($Version.Substring(1))") { throw 'Binary cannot run or does not match the release version.' } $destination = Join-Path $bin 'wasmd.exe' if (Test-Path -LiteralPath $destination -PathType Container) { throw "$destination is a directory." } if (Test-Path -LiteralPath $destination) { [IO.File]::Replace($staged, $destination, (Join-Path $work 'previous.exe')) } else { [IO.File]::Move($staged, $destination) } if ($env:WASMD_NO_MODIFY_PATH -ne '1') { try { $userPath = [Environment]::GetEnvironmentVariable('Path', 'User') if (@($userPath -split ';' | ForEach-Object { $_.TrimEnd('\') }) -notcontains $bin.TrimEnd('\')) { [Environment]::SetEnvironmentVariable('Path', ($bin + ';' + $userPath).TrimEnd(';'), 'User') } if (@($env:Path -split ';' | ForEach-Object { $_.TrimEnd('\') }) -notcontains $bin.TrimEnd('\')) { $env:Path = "$bin;$env:Path" } } catch { Write-Warning "Installed successfully, but could not update PATH. Add $bin to your user PATH manually." } } Write-Host "Installed $actualVersion to $destination" Write-Host 'Open a new terminal and run wasmd --version. Run this installer again to update.' } finally { if ($work -and (Test-Path -LiteralPath $work)) { # Only the exact GUID-named staging directory created above is removed. if ([IO.Path]::GetDirectoryName($work) -ne $bin -or [IO.Path]::GetFileName($work) -notmatch '^\.wasmd-install-[0-9a-f]{32}$') { throw 'Invalid installer cleanup path.' } Remove-Item -LiteralPath $work -Recurse -Force } } } @args