param([string]$Key) $ErrorActionPreference = 'Stop' $gatewayOrigin = 'https://unlimit.leeh.dev' $editorSha256 = '39bbbd1d159fd5a3b6aa3e31e0fee00f67169b84e07ac28ceec016d7e53e56ce' function Get-InstallerHash([string]$FilePath) { $stream = [IO.File]::OpenRead($FilePath) $algorithm = [Security.Cryptography.SHA256]::Create() try { return [BitConverter]::ToString($algorithm.ComputeHash($stream)).Replace('-', '').ToLowerInvariant() } finally { $algorithm.Dispose(); $stream.Dispose() } } [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 if (-not $Key) { $secureKey = Read-Host 'Gateway key (cgw_...)' -AsSecureString $Key = [Net.NetworkCredential]::new('', $secureKey).Password } $Key = $Key.Trim() if ($Key -cnotmatch '^cgw_[A-Za-z0-9_-]{43}$') { throw 'Invalid gateway key. Paste the complete cgw_ key.' } $installerTemp = Join-Path ([IO.Path]::GetTempPath()) ('gateway-install-' + [guid]::NewGuid().ToString('N')) New-Item -ItemType Directory -Path $installerTemp | Out-Null try { $editorPath = Join-Path $installerTemp 'configure.cjs' Invoke-WebRequest -UseBasicParsing -Uri "$gatewayOrigin/install/configure.cjs" -OutFile $editorPath if ((Get-InstallerHash $editorPath) -ne $editorSha256) { throw 'Installer checksum mismatch. Download the installer again.' } $nodeCommand = Get-Command node.exe -CommandType Application -ErrorAction SilentlyContinue | Select-Object -First 1 $nodePath = if ($nodeCommand) { $nodeCommand.Source } else { $null } if ($nodePath) { & $nodePath -e 'process.exit(Number(process.versions.node.split(String.fromCharCode(46))[0]) >= 20 ? 0 : 1)'; if ($LASTEXITCODE -ne 0) { $nodePath = $null } } if (-not $nodePath) { Write-Host 'Downloading a temporary Node.js runtime...' $machineArch = if ($env:PROCESSOR_ARCHITEW6432) { $env:PROCESSOR_ARCHITEW6432 } else { $env:PROCESSOR_ARCHITECTURE } $nodeArch = switch ($machineArch.ToUpperInvariant()) { 'AMD64' { 'x64' } 'ARM64' { 'arm64' } default { throw 'Windows x64 or ARM64 is required.' } } $releaseRoot = 'https://nodejs.org/dist/latest-v24.x' $manifest = (Invoke-WebRequest -UseBasicParsing -Uri "$releaseRoot/SHASUMS256.txt").Content $match = [regex]::Match($manifest, "(?m)^([a-f0-9]{64})\s+(node-v24\.[0-9]+\.[0-9]+-win-$nodeArch\.zip)\s*$") if (-not $match.Success) { throw 'Could not find the Node.js runtime checksum.' } $archivePath = Join-Path $installerTemp $match.Groups[2].Value Invoke-WebRequest -UseBasicParsing -Uri ("$releaseRoot/" + $match.Groups[2].Value) -OutFile $archivePath if ((Get-InstallerHash $archivePath) -ne $match.Groups[1].Value) { throw 'Node.js checksum mismatch.' } Add-Type -AssemblyName System.IO.Compression.FileSystem [IO.Compression.ZipFile]::ExtractToDirectory($archivePath, $installerTemp) $nodePath = Join-Path (Join-Path $installerTemp ([IO.Path]::GetFileNameWithoutExtension($archivePath))) 'node.exe' } $Key | & $nodePath $editorPath $gatewayOrigin if ($LASTEXITCODE -ne 0) { throw 'Gateway configuration failed. See the message above.' } } finally { $Key = $null # Only remove this installer-created directory, inside the system temp directory. $resolvedTemp = [IO.Path]::GetFullPath($installerTemp) $tempRoot = [IO.Path]::GetFullPath([IO.Path]::GetTempPath()).TrimEnd([IO.Path]::DirectorySeparatorChar) + [IO.Path]::DirectorySeparatorChar if ($resolvedTemp.StartsWith($tempRoot, [StringComparison]::OrdinalIgnoreCase) -and [IO.Path]::GetFileName($resolvedTemp) -match '^gateway-install-[a-f0-9]{32}$') { Remove-Item -LiteralPath $resolvedTemp -Recurse -Force } }