ab2d3ed8cd
Windows tray "switch" that routes all Claude MSIX-app traffic through a SOCKS5/HTTP proxy or a VLESS+Reality tunnel (embedded Xray-core), with a fail-closed Windows Firewall lock. Runs de-elevated so it launches the user's normal Claude instance; elevates only for firewall changes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
86 lines
3.6 KiB
PowerShell
86 lines
3.6 KiB
PowerShell
<#
|
|
build.ps1 - inline lib+gui into one script and compile to dist\ClaudeProxy.exe via ps2exe.
|
|
Usage: powershell -ExecutionPolicy Bypass -File build.ps1 [-Smoke]
|
|
-Smoke : after inlining, run the combined script with a 0.7s auto-close to catch runtime errors.
|
|
#>
|
|
[CmdletBinding()]
|
|
param([switch]$Smoke)
|
|
$ErrorActionPreference = 'Stop'
|
|
$root = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
$dist = Join-Path $root 'dist'
|
|
New-Item -ItemType Directory -Force -Path $dist | Out-Null
|
|
|
|
$rd = New-Object System.Text.UTF8Encoding($false)
|
|
$enc = New-Object System.Text.UTF8Encoding($true)
|
|
|
|
$lib = [System.IO.File]::ReadAllText((Join-Path $root 'claude-proxy.lib.ps1'), $rd)
|
|
$guiLines = [System.IO.File]::ReadAllLines((Join-Path $root 'claude-proxy-gui.ps1'), $rd)
|
|
|
|
# Drop the dot-source line and the $Root assignment: in a single-file exe there is no
|
|
# external lib to source, and $MyInvocation.MyCommand.Path is null under ps2exe.
|
|
$guiFiltered = $guiLines | Where-Object {
|
|
($_ -notmatch 'claude-proxy\.lib\.ps1') -and ($_ -notmatch '^\s*\$Root\s*=')
|
|
}
|
|
|
|
# embed the Xray binary (base64) so the exe is self-contained (no download needed)
|
|
$embed = ''
|
|
$embedFile = Join-Path $root 'xray-embed.b64'
|
|
if (Test-Path $embedFile) {
|
|
$b64 = ([System.IO.File]::ReadAllText($embedFile)).Trim()
|
|
$embed = "`r`n`$script:XrayEmbedB64 = @'`r`n$b64`r`n'@`r`n"
|
|
Write-Host ("Embedding Xray: {0} KB base64" -f [math]::Round($b64.Length / 1KB))
|
|
} else {
|
|
Write-Host "xray-embed.b64 not found - exe will download Xray on first use." -ForegroundColor Yellow
|
|
}
|
|
|
|
$combined = $lib + $embed + "`r`n# ===== inlined GUI =====`r`n" + ($guiFiltered -join "`r`n")
|
|
$combinedPath = Join-Path $dist 'claude-proxy.combined.ps1'
|
|
[System.IO.File]::WriteAllText($combinedPath, $combined, $enc)
|
|
Write-Host "Combined script -> $combinedPath"
|
|
|
|
# parse-check
|
|
$errs = $null; $tok = $null
|
|
[System.Management.Automation.Language.Parser]::ParseFile($combinedPath, [ref]$tok, [ref]$errs) | Out-Null
|
|
if ($errs) {
|
|
Write-Host "PARSE ERRORS in combined script:" -ForegroundColor Red
|
|
$errs | ForEach-Object { ' {0}:{1} {2}' -f $_.Extent.StartLineNumber, $_.Extent.StartColumnNumber, $_.Message }
|
|
throw "Aborting build."
|
|
}
|
|
Write-Host "Parse: OK" -ForegroundColor Green
|
|
|
|
if ($Smoke) {
|
|
Write-Host "Smoke test (window will flash briefly)..." -ForegroundColor Cyan
|
|
$env:CLAUDEPROXY_SMOKE = '1'
|
|
& powershell.exe -NoProfile -ExecutionPolicy Bypass -STA -File $combinedPath
|
|
Remove-Item Env:\CLAUDEPROXY_SMOKE -ErrorAction SilentlyContinue
|
|
Write-Host "Smoke exit: $LASTEXITCODE"
|
|
}
|
|
|
|
# regenerate the icon if the generator changed / icon missing
|
|
$icon = Join-Path $root 'icon.ico'
|
|
if (-not (Test-Path $icon)) {
|
|
Write-Host "Generating icon..." -ForegroundColor Cyan
|
|
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $root 'make-icon.ps1') | Out-Null
|
|
}
|
|
|
|
Import-Module ps2exe -ErrorAction Stop
|
|
$exe = Join-Path $dist 'ClaudeProxy.exe'
|
|
Write-Host "Compiling -> $exe" -ForegroundColor Cyan
|
|
# NOTE: asInvoker (NOT requireAdmin). The GUI runs de-elevated so the Claude it launches is the
|
|
# user's normal instance (matches the taskbar/tray pin). Firewall changes elevate on demand.
|
|
$p2e = @{
|
|
inputFile = $combinedPath; outputFile = $exe
|
|
noConsole = $true; STA = $true
|
|
title = 'Claude Proxy'; product = 'Claude Proxy'; company = 'gusik'
|
|
version = '1.0.0.0'; description = 'Force Claude desktop traffic through a proxy'
|
|
}
|
|
if (Test-Path $icon) { $p2e.iconFile = $icon }
|
|
Invoke-ps2exe @p2e | Out-Null
|
|
|
|
if (Test-Path $exe) {
|
|
$sz = [math]::Round((Get-Item $exe).Length / 1KB)
|
|
Write-Host "OK: $exe ($sz KB)" -ForegroundColor Green
|
|
} else {
|
|
throw "ps2exe did not produce the exe."
|
|
}
|