claude-proxy: native C# GUI to force Claude desktop traffic through a proxy

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>
This commit is contained in:
2026-07-10 13:46:23 +03:00
commit ab2d3ed8cd
16 changed files with 2707 additions and 0 deletions
+85
View File
@@ -0,0 +1,85 @@
<#
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."
}