# ============================================================================== # PasteMesh Official Windows Unified Installer (x64 & ARM64) # Official Product Website: https://pastemesh.net # # Usage: # irm https://pastemesh.net/install.ps1 | iex # & ([scriptblock]::Create((irm https://pastemesh.net/install.ps1))) -Arch arm64 # ============================================================================== param( [string]$Version = "0.13.0", [ValidateSet("auto", "x64", "arm64")] [string]$Arch = "auto", [string]$Engine = "desktop", [string]$InstallDir = "$env:LOCALAPPDATA\PasteMesh", [switch]$NoStartup ) $ErrorActionPreference = "Stop" # Resolve target Windows architecture (x64 or arm64) $ResolvedArch = $Arch.ToLowerInvariant() if ($ResolvedArch -eq "auto") { $OsArch = "" try { $OsArch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString() } catch { } if ($OsArch -eq "Arm64" -or $env:PROCESSOR_ARCHITECTURE -eq "ARM64" -or $env:PROCESSOR_ARCHITEW6432 -eq "ARM64") { $ResolvedArch = "arm64" } else { $ResolvedArch = "x64" } } Write-Host "`n==========================================================" -ForegroundColor Cyan Write-Host " PasteMesh v$Version - Windows ($ResolvedArch) Unified Installer" -ForegroundColor Cyan Write-Host "==========================================================`n" -ForegroundColor Cyan # Stop any running PasteMesh processes before updating binaries Stop-Process -Name "pastemesh-desktop", "pastemesh" -Force -ErrorAction SilentlyContinue Start-Sleep -Milliseconds 300 # Ensure installation directories exist $BinDir = Join-Path $InstallDir "bin" $LibDir = Join-Path $InstallDir "lib" New-Item -ItemType Directory -Force -Path $BinDir | Out-Null New-Item -ItemType Directory -Force -Path $LibDir | Out-Null Write-Host "* Target Installation Dir: $InstallDir" Write-Host "* Target Architecture: windows-$ResolvedArch" Write-Host "* Selected Engine: $Engine" $ZipName = "pastemesh-v$Version-windows-$ResolvedArch.zip" $PrimaryUrl = "https://pastemesh.net/releases/$ZipName" $FallbackUrl = "https://codeberg.org/pastemesh/pastemesh/releases/download/v$Version/$ZipName" $TempZip = Join-Path $env:TEMP $ZipName $Downloaded = $false if ((Test-Path "dist\bundle\pastemesh.exe") -and (Test-Path "dist\bundle\pastemesh-desktop.exe")) { Write-Host "* Using local build from dist\bundle\..." -ForegroundColor Green Copy-Item "dist\bundle\pastemesh.exe" (Join-Path $LibDir "pastemesh.exe") -Force Copy-Item "dist\bundle\pastemesh-desktop.exe" (Join-Path $LibDir "pastemesh-desktop.exe") -Force } elseif (Test-Path "dist\release\$ZipName") { Write-Host "* Using local release archive: dist\release\$ZipName..." -ForegroundColor Green Copy-Item "dist\release\$ZipName" $TempZip -Force $Downloaded = $true } else { try { Write-Host "* Downloading release archive: $ZipName..." Invoke-WebRequest -Uri $PrimaryUrl -OutFile $TempZip -UseBasicParsing -ErrorAction Stop $Downloaded = $true } catch { try { Invoke-WebRequest -Uri $FallbackUrl -OutFile $TempZip -UseBasicParsing -ErrorAction Stop $Downloaded = $true } catch { Write-Error "Failed to download PasteMesh ($ResolvedArch) release bundle from https://pastemesh.net" } } } if ($Downloaded -and (Test-Path $TempZip)) { Write-Host "* Extracting release binaries..." Expand-Archive -Path $TempZip -DestinationPath $LibDir -Force Remove-Item $TempZip -Force -ErrorAction SilentlyContinue } # Copy strictly the 2 canonical binaries to bin foreach ($BinFile in @("pastemesh.exe", "pastemesh-desktop.exe")) { $Src = Join-Path $LibDir $BinFile if (Test-Path $Src) { Copy-Item $Src (Join-Path $BinDir $BinFile) -Force } } # Stage optional VC++ CRT redistributables if present alongside local build $LocalVcCrtDir = "dist\vc_crt_$ResolvedArch" if (Test-Path $LocalVcCrtDir) { Get-ChildItem -Path $LocalVcCrtDir -Filter "*.dll" -ErrorAction SilentlyContinue | ForEach-Object { Copy-Item $_.FullName (Join-Path $BinDir $_.Name) -Force } } # Add $BinDir to User PATH if not already present $UserPath = [Environment]::GetEnvironmentVariable("Path", "User") if ($UserPath -notlike "*$BinDir*") { $NewPath = "$UserPath;$BinDir" [Environment]::SetEnvironmentVariable("Path", $NewPath, "User") $env:Path = "$env:Path;$BinDir" Write-Host "* Added $BinDir to User PATH." -ForegroundColor Green } # Register Windows Startup entry & Start Menu Shortcut for PasteMesh System Tray $DesktopExe = Join-Path $BinDir "pastemesh-desktop.exe" if ((Test-Path $DesktopExe) -and (-not $NoStartup)) { try { $RunKey = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" Set-ItemProperty -Path $RunKey -Name "PasteMesh" -Value "`"$DesktopExe`"" Write-Host "* Registered PasteMesh System Tray in Windows Startup." -ForegroundColor Green $StartMenuDir = Join-Path ([Environment]::GetFolderPath("Programs")) "PasteMesh" New-Item -ItemType Directory -Force -Path $StartMenuDir | Out-Null $WshShell = New-Object -ComObject WScript.Shell $Shortcut = $WshShell.CreateShortcut((Join-Path $StartMenuDir "PasteMesh.lnk")) $Shortcut.TargetPath = $DesktopExe $Shortcut.Arguments = "" $Shortcut.WorkingDirectory = $BinDir $Shortcut.Description = "PasteMesh - Sovereign Encrypted P2P Clipboard Mesh (https://pastemesh.net)" $Shortcut.IconLocation = "$DesktopExe,0" $Shortcut.Save() } catch { } } # Register Windows Defender Firewall rules so PasteMesh and future OTA updates never trigger firewall popups $CliExe = Join-Path $BinDir "pastemesh.exe" $IsAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) $FwCmd = "netsh advfirewall firewall delete rule name=`"PasteMesh Desktop Engine`" >nul 2>&1 & " + "netsh advfirewall firewall delete rule name=`"PasteMesh Desktop`" >nul 2>&1 & " + "netsh advfirewall firewall delete rule name=`"PasteMesh CLI`" >nul 2>&1 & " + "netsh advfirewall firewall delete rule name=`"PasteMesh Mesh & Web UI (TCP 5288)`" >nul 2>&1 & " + "netsh advfirewall firewall delete rule name=`"PasteMesh LAN Discovery (UDP 5289)`" >nul 2>&1 & " + "netsh advfirewall firewall add rule name=`"PasteMesh Desktop`" dir=in action=allow program=`"$DesktopExe`" enable=yes profile=any >nul 2>&1 & " + "netsh advfirewall firewall add rule name=`"PasteMesh CLI`" dir=in action=allow program=`"$CliExe`" enable=yes profile=any >nul 2>&1 & " + "netsh advfirewall firewall add rule name=`"PasteMesh Mesh & Web UI (TCP 5288)`" dir=in action=allow protocol=TCP localport=5286-5299 enable=yes profile=any >nul 2>&1 & " + "netsh advfirewall firewall add rule name=`"PasteMesh LAN Discovery (UDP 5289)`" dir=in action=allow protocol=UDP localport=5289 enable=yes profile=any >nul 2>&1" if ($IsAdmin) { try { & cmd.exe /c $FwCmd | Out-Null Write-Host "* Configured Windows Defender Firewall rules for PasteMesh." -ForegroundColor Green } catch { } } elseif (-not $env:CI -and -not $env:GITHUB_ACTIONS) { try { $ExistingRule = & netsh advfirewall firewall show rule name="PasteMesh Desktop" verbose 2>$null $HasRule = ($ExistingRule -join "`n") -match [Regex]::Escape($DesktopExe) if (-not $HasRule) { Start-Process -FilePath "cmd.exe" -ArgumentList "/c $FwCmd" -Verb RunAs -WindowStyle Hidden -Wait -ErrorAction Stop Write-Host "* Configured Windows Defender Firewall rules for PasteMesh (elevated)." -ForegroundColor Green } } catch { } } if ((Test-Path $DesktopExe) -and (-not $NoStartup)) { try { explorer.exe $DesktopExe Write-Host "* Started PasteMesh System Tray in background." -ForegroundColor Green } catch { } } Write-Host "`n==========================================================" -ForegroundColor Green Write-Host " [OK] PasteMesh v$Version Windows ($ResolvedArch) Installation Complete!" -ForegroundColor Green Write-Host "==========================================================" -ForegroundColor Green Write-Host "* CLI Binary: $BinDir\pastemesh.exe" Write-Host "* Desktop/Tray: $BinDir\pastemesh-desktop.exe" Write-Host "* Quick commands: pastemesh copy, pastemesh paste, pastemesh history, pastemesh pair" Write-Host "* Official site: https://pastemesh.net`n"