param( [string]$OutputDir = (Join-Path $PSScriptRoot '..\src-tauri\bundled\proxifyre'), [ValidateSet('x64', 'x86', 'ARM64')] [string[]]$Architectures = @('x64'), [switch]$SkipVcRuntime ) $ErrorActionPreference = 'Stop' Set-StrictMode -Version Latest $ProgressPreference = 'SilentlyContinue' function Invoke-JsonApi([string]$Uri) { Invoke-RestMethod -Uri $Uri -Headers @{ 'User-Agent' = 'proxywarden-bundle-updater' 'Accept' = 'application/vnd.github+json' } -TimeoutSec 60 -MaximumRedirection 10 } function Invoke-FileDownload([string]$Uri, [string]$Path) { $partialPath = "$Path.part" Remove-Item -LiteralPath $partialPath -Force -ErrorAction SilentlyContinue try { Invoke-WebRequest -UseBasicParsing -Uri $Uri -OutFile $partialPath -Headers @{ 'User-Agent' = 'proxywarden-bundle-updater' 'Accept' = 'application/octet-stream,*/*' } -TimeoutSec 240 -MaximumRedirection 10 } catch { Remove-Item -LiteralPath $partialPath -Force -ErrorAction SilentlyContinue throw } $item = Get-Item -LiteralPath $partialPath if ($item.Length -le 0) { Remove-Item -LiteralPath $partialPath -Force -ErrorAction SilentlyContinue throw "Downloaded file is empty: $Uri" } Move-Item -LiteralPath $partialPath -Destination $Path -Force } function Select-ReleaseAsset($Release, [string]$Pattern, [string]$Label) { $asset = $Release.assets | Where-Object { $_.name -match $Pattern } | Select-Object -First 1 if ($null -eq $asset) { throw "No asset found for $Label using pattern $Pattern" } $asset } function Save-Asset([string]$Id, [string]$Name, [string]$Url, [string]$ExpectedDigest = '') { $path = Join-Path $OutputDir $Name if (Test-Path -LiteralPath $path) { $existing = Get-Item -LiteralPath $path if ($existing.Length -gt 0) { $existingHash = (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash.ToLowerInvariant() $expectedHash = '' if (-not [string]::IsNullOrWhiteSpace($ExpectedDigest) -and $ExpectedDigest -match '^sha256:(.+)$') { $expectedHash = $Matches[1].ToLowerInvariant() } if ([string]::IsNullOrWhiteSpace($expectedHash) -or $existingHash -eq $expectedHash) { Write-Host "Using existing $Name" return [PSCustomObject]@{ id = $Id name = $Name sha256 = $existingHash size = $existing.Length sourceUrl = $Url } } } } Write-Host "Downloading $Name" Invoke-FileDownload $Url $path $hash = (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash.ToLowerInvariant() if (-not [string]::IsNullOrWhiteSpace($ExpectedDigest) -and $ExpectedDigest -match '^sha256:(.+)$') { $expected = $Matches[1].ToLowerInvariant() if ($hash -ne $expected) { throw "SHA256 mismatch for $Name. Expected $expected, got $hash." } } [PSCustomObject]@{ id = $Id name = $Name sha256 = $hash size = (Get-Item -LiteralPath $path).Length sourceUrl = $Url } } $resolvedOutputDir = [System.IO.Path]::GetFullPath($OutputDir) New-Item -ItemType Directory -Force -Path $resolvedOutputDir | Out-Null $OutputDir = $resolvedOutputDir $selectedArchitectures = $Architectures | ForEach-Object { if ($_ -eq 'ARM64') { 'ARM64' } elseif ($_ -eq 'x86') { 'x86' } else { 'x64' } } | Select-Object -Unique $proxifyreRelease = Invoke-JsonApi 'https://api.github.com/repos/wiresock/proxifyre/releases/latest' $ndisapiRelease = Invoke-JsonApi 'https://api.github.com/repos/wiresock/ndisapi/releases/latest' $files = New-Object System.Collections.Generic.List[object] foreach ($arch in $selectedArchitectures) { $proxifyreAsset = Select-ReleaseAsset $proxifyreRelease "ProxiFyre-.*-$arch-signed\.zip$" "ProxiFyre $arch" $files.Add((Save-Asset "proxifyre-$($arch.ToLowerInvariant())" $proxifyreAsset.name $proxifyreAsset.browser_download_url $proxifyreAsset.digest)) $ndisAsset = Select-ReleaseAsset $ndisapiRelease "Windows\.Packet\.Filter\..*\.$arch\.msi$" "Windows Packet Filter $arch" $files.Add((Save-Asset "packet-filter-$($arch.ToLowerInvariant())" $ndisAsset.name $ndisAsset.browser_download_url $ndisAsset.digest)) } if (-not $SkipVcRuntime) { if ($selectedArchitectures | Where-Object { $_ -ne 'x86' }) { $files.Add((Save-Asset 'vc-runtime-x64' 'vc_redist.x64.exe' 'https://aka.ms/vc14/vc_redist.x64.exe')) } if ($selectedArchitectures -contains 'x86') { $files.Add((Save-Asset 'vc-runtime-x86' 'vc_redist.x86.exe' 'https://aka.ms/vc14/vc_redist.x86.exe')) } } $manifest = [PSCustomObject]@{ generatedAt = (Get-Date).ToUniversalTime().ToString('o') architectures = @($selectedArchitectures) proxifyreRelease = $proxifyreRelease.tag_name windowsPacketFilterRelease = $ndisapiRelease.tag_name files = $files } $manifestPath = Join-Path $OutputDir 'manifest.json' $manifest | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath $manifestPath -Encoding UTF8 $keepNames = @($files | ForEach-Object { $_.name }) + 'manifest.json' Get-ChildItem -LiteralPath $OutputDir -File | Where-Object { $keepNames -notcontains $_.Name } | ForEach-Object { Remove-Item -LiteralPath $_.FullName -Force } Write-Host "Bundle updated: $OutputDir"