import { spawn, execSync } from 'child_process'; import path from 'path'; import fs from 'fs'; import readline from 'readline'; import pidusage from 'pidusage'; import { backupServer, backupConfigsOnly } from './sync-manager.js'; import { addServerToVelocity } from './velocity-manager.js'; const dec = (str) => Buffer.from(str, 'base64').toString('ascii'); function getJavaExecutablePath(server) { // 1. Check if specific java version is requested and installed in Docker if (server && server.javaVersion === '26' && fs.existsSync('/opt/java26/bin/java')) { return '/opt/java26/bin/java'; } if (server && (server.javaVersion === '21' || !server.javaVersion) && fs.existsSync('/opt/java21/bin/java')) { return '/opt/java21/bin/java'; } // 1.5. Fallback for Docker default /opt/java if (fs.existsSync('/opt/java/bin/java')) return '/opt/java/bin/java'; const userHome = process.env.USERPROFILE || process.env.HOMEPATH || 'C:\\Users\\user'; // 1. Try exact known extensions path first (extremely fast) const knownPath = path.join(userHome, '.antigravity', 'extensions', 'redhat.java-1.54.0-win32-x64', 'jre', '21.0.10-win32-x86_64', 'bin', 'java.exe'); if (fs.existsSync(knownPath)) { return knownPath; } // 2. Try scanning extensions directory dynamically const extensionsDir = path.join(userHome, '.antigravity', 'extensions'); if (fs.existsSync(extensionsDir)) { try { const folders = fs.readdirSync(extensionsDir); for (const folder of folders) { if (folder.startsWith('redhat.java-')) { const jreDir = path.join(extensionsDir, folder, 'jre'); if (fs.existsSync(jreDir)) { const jreSubfolders = fs.readdirSync(jreDir); for (const sub of jreSubfolders) { const javaExe = path.join(jreDir, sub, 'bin', 'java.exe'); if (fs.existsSync(javaExe)) { return javaExe; } } } } } } catch (e) { console.error('Error scanning extensions for java:', e); } } // 3. Fallback to common Windows paths const commonPaths = [ 'C:\\Program Files\\Java', 'C:\\Program Files (x86)\\Java', 'C:\\Program Files\\Eclipse Adoptium', 'C:\\Program Files\\Microsoft' ]; for (const baseDir of commonPaths) { if (fs.existsSync(baseDir)) { const found = scanDirForJava(baseDir); if (found) return found; } } return 'java'; // Fallback to global PATH } function scanDirForJava(dir, depth = 0) { if (depth > 4) return null; try { const files = fs.readdirSync(dir); for (const file of files) { const fullPath = path.join(dir, file); const stat = fs.statSync(fullPath); if (stat.isDirectory()) { const found = scanDirForJava(fullPath, depth + 1); if (found) return found; } else if (file.toLowerCase() === 'java.exe') { return fullPath; } } } catch (e) {} return null; } export class MCProcessManager { constructor() { this.processes = new Map(); // serverId -> process object this.tunnelProcesses = new Map(); // serverId -> tunnel process object } // Get active server process stats async getMetrics(serverId) { const procInfo = this.processes.get(serverId); if (!procInfo || !procInfo.process || procInfo.status === 'offline') { return { cpu: 0, memory: 0, status: 'offline' }; } try { const stats = await pidusage(procInfo.process.pid); // stats.memory is in bytes, convert to GB const memoryGB = parseFloat((stats.memory / (1024 * 1024 * 1024)).toFixed(2)); const cpuPercent = Math.min(100, Math.round(stats.cpu)); return { cpu: cpuPercent, memory: memoryGB, status: procInfo.status }; } catch (e) { // If process just stopped return { cpu: 0, memory: 0, status: procInfo.status }; } } // Check if a process is currently running isRunning(serverId) { const procInfo = this.processes.get(serverId); return procInfo && procInfo.status !== 'offline'; } // Start the Minecraft server process startServer(server, onLog, onStatusChange, onPlayerChange, onAddressDetected, onSecretUpdated) { const serverId = server.id; if (this.isRunning(serverId)) { return false; } // OOM Prevention: Check if another server is already running on this Space let runningCount = 0; for (const [id, proc] of this.processes.entries()) { if (proc.status !== 'offline') runningCount++; } if (runningCount >= 1) { onLog(serverId, `[SYSTEM] ❌ ERROR: Cannot start multiple servers on a single Free Space!`); onLog(serverId, `[SYSTEM] ❌ Your Space only has 16GB RAM. Starting another server will crash the machine (OOM).`); onLog(serverId, `[SYSTEM] 💡 SOLUTION: Use the 'Spaces Connect' tab to create a separate Space for this server!`); return false; } const serverDir = server.path; const isBedrock = server.software === 'Bedrock'; let cmd = getJavaExecutablePath(server); let args = []; if (isBedrock) { cmd = path.join(serverDir, 'bedrock_server.exe'); args = []; } else { // Java 21/25 optimized flags — ZGC gives near-zero GC pauses (much lower ping) const ramAllocation = 8; const javaVer = parseInt(server.javaVersion || '21', 10); if (javaVer >= 21) { // Java 21+ — use ZGC (near-zero GC pauses → low ping) args = [ `-Xms512M`, `-Xmx${ramAllocation}G`, '-XX:+UnlockExperimentalVMOptions', '-XX:+UseZGC', '-XX:+AlwaysPreTouch', '-XX:+DisableExplicitGC', '-XX:+PerfDisableSharedMem', '-XX:ActiveProcessorCount=2', '-Djava.net.preferIPv4Stack=true', '-Dfile.encoding=UTF-8', '-jar', server.software === 'Fabric' ? 'fabric-server-launch.jar' : server.software === 'Velocity' ? 'velocity.jar' : 'server.jar', '--nogui' ]; } else { // Java 17/18 — G1GC Aikar-style but tuned better args = [ `-Xms1G`, `-Xmx${ramAllocation}G`, '-XX:+UseG1GC', '-XX:+ParallelRefProcEnabled', '-XX:MaxGCPauseMillis=200', '-XX:+UnlockExperimentalVMOptions', '-XX:+DisableExplicitGC', '-XX:+AlwaysPreTouch', '-XX:G1NewSizePercent=30', '-XX:G1MaxNewSizePercent=40', '-XX:G1HeapRegionSize=8M', '-XX:G1ReservePercent=20', '-XX:G1HeapWastePercent=5', '-XX:G1MixedGCCountTarget=4', '-XX:InitiatingHeapOccupancyPercent=15', '-XX:G1MixedGCLiveThresholdPercent=90', '-XX:G1RSetUpdatingPauseTimePercent=5', '-XX:SurvivorRatio=32', '-XX:+PerfDisableSharedMem', '-XX:MaxTenuringThreshold=1', '-XX:ActiveProcessorCount=2', '-Dfile.encoding=UTF-8', '-jar', server.software === 'Fabric' ? 'fabric-server-launch.jar' : server.software === 'Velocity' ? 'velocity.jar' : 'server.jar', '--nogui', '--port', server.port.toString() ]; } } // Auto-delete session.lock files to prevent DirectoryLockException on restart if (!isBedrock) { try { const worldDirs = ['world', 'world_nether', 'world_the_end']; for (const worldDir of worldDirs) { const lockFile = path.join(serverDir, worldDir, 'session.lock'); if (fs.existsSync(lockFile)) { fs.unlinkSync(lockFile); onLog(serverId, `[SYSTEM] Removed stale lock file: ${worldDir}/session.lock`); } } } catch (e) { onLog(serverId, `[SYSTEM] Warning: Could not remove lock files: ${e.message}`); } } // Force accept eula.txt always const eulaFile = path.join(serverDir, 'eula.txt'); if (!isBedrock) { if (!fs.existsSync(serverDir)) fs.mkdirSync(serverDir, { recursive: true }); fs.writeFileSync(eulaFile, 'eula=true\n'); onLog(serverId, `[SYSTEM] Enforced Minecraft EULA acceptance.`); // Apply HuggingFace performance optimizations try { // --- server.properties tuning --- const propsFile = path.join(serverDir, 'server.properties'); if (fs.existsSync(propsFile)) { let props = fs.readFileSync(propsFile, 'utf8'); props = props.replace(/view-distance=\d+/g, 'view-distance=8'); props = props.replace(/simulation-distance=\d+/g, 'simulation-distance=6'); // -1 = best for Velocity proxy (let Velocity handle compression) props = props.replace(/network-compression-threshold=\d+/g, 'network-compression-threshold=-1'); if (props.match(/server-port=\d+/)) { props = props.replace(/server-port=\d+/g, `server-port=${server.port}`); } else { props += `\nserver-port=${server.port}\n`; } if (props.match(/server-ip=.*/)) { props = props.replace(/server-ip=.*/g, 'server-ip=127.0.0.1'); } else { props += `\nserver-ip=127.0.0.1\n`; } fs.writeFileSync(propsFile, props); } // --- paper-world-defaults.yml tuning --- const paperWorldFile = path.join(serverDir, 'config', 'paper-world-defaults.yml'); if (fs.existsSync(paperWorldFile)) { let paper = fs.readFileSync(paperWorldFile, 'utf8'); paper = paper.replace(/disable-move-event: false/g, 'disable-move-event: true'); paper = paper.replace(/fix-climbing-bypassing-cramming-rule: false/g, 'fix-climbing-bypassing-cramming-rule: true'); fs.writeFileSync(paperWorldFile, paper); } // --- paper-global.yml tuning --- const paperGlobalFile = path.join(serverDir, 'config', 'paper-global.yml'); if (fs.existsSync(paperGlobalFile)) { let global = fs.readFileSync(paperGlobalFile, 'utf8'); // Use async chunk loading for less main-thread blocking → lower ping global = global.replace(/max-auto-save-chunks-per-tick: \d+/g, 'max-auto-save-chunks-per-tick: 8'); // Force Velocity Support global = global.replace(/velocity:[\s\S]*?secret:.*?\n/, `velocity:\n enabled: true\n online-mode: false\n secret: 'H0ll0wSMPPr0xyS3cr3t5529!'\n`); fs.writeFileSync(paperGlobalFile, global); } // --- spigot.yml tuning --- const spigotFile = path.join(serverDir, 'spigot.yml'); if (fs.existsSync(spigotFile)) { let spigot = fs.readFileSync(spigotFile, 'utf8'); spigot = spigot.replace(/mob-spawn-range: \d+/g, 'mob-spawn-range: 4'); spigot = spigot.replace(/entity-activation-range:/g, 'entity-activation-range:'); fs.writeFileSync(spigotFile, spigot); } onLog(serverId, `[SYSTEM] ✅ Applied Java ${server.javaVersion || '21'}+ performance optimizations (ZGC + low-ping tuning)!`); } catch(e) { onLog(serverId, `[SYSTEM] Notice: Could not apply auto-optimizations: ${e.message}`); } } onStatusChange(serverId, 'starting'); // Fix Paperclip NoSuchFileException crash by pre-creating cache and versions directories if (!isBedrock) { try { fs.mkdirSync(path.join(serverDir, 'cache'), { recursive: true }); fs.mkdirSync(path.join(serverDir, 'versions'), { recursive: true }); fs.mkdirSync(path.join(serverDir, 'libraries'), { recursive: true }); } catch (e) { // Ignore } } onLog(serverId, `[SYSTEM] Launching ${server.name} (${server.software} Edition)...`); onLog(serverId, `[SYSTEM] Directory: ${serverDir}`); if (!isBedrock) { const jarName = server.software === 'Fabric' ? 'fabric-server-launch.jar' : server.software === 'Velocity' ? 'velocity.jar' : 'server.jar'; onLog(serverId, `[SYSTEM] Command: java ${args.slice(0, 2).join(' ')} -jar ${jarName}`); } else { onLog(serverId, `[SYSTEM] Command: bedrock_server.exe`); } const processOptions = { cwd: serverDir, env: { ...process.env } }; const serverProcess = spawn(cmd, args, processOptions); const activePlayers = new Set(); const procInfo = { process: serverProcess, status: 'starting', players: activePlayers, eulaAccepted: true }; this.processes.set(serverId, procInfo); // Spawn tunnel client alongside the server if (server.tunnelType === 'frp') { import('./auto-setup.js').then(async ({ downloadFrpc }) => { try { const tunnelBin = await downloadFrpc(path.join(serverDir, 'tunnel')); onLog(serverId, `[SYSTEM] Downloaded/Verified FRPC binary.`); // Generate frpc.toml const frpcTomlPath = path.join(serverDir, 'tunnel', 'frpc.toml'); const frpcConfig = `serverAddr = "${server.frpServerIp}" serverPort = 7000 [transport] tcpMux = false [[proxies]] name = "${serverId}-tcp" type = "tcp" localIP = "127.0.0.1" localPort = ${server.port} remotePort = ${server.port} `; fs.writeFileSync(frpcTomlPath, frpcConfig); onLog(serverId, `[SYSTEM] Starting Custom FRP tunnel...`); try { fs.chmodSync(tunnelBin, '755'); } catch(e) {} const tunnelProcess = spawn(tunnelBin, ['-c', frpcTomlPath], { cwd: path.join(serverDir, 'tunnel'), env: { ...process.env } }); this.tunnelProcesses.set(serverId, tunnelProcess); const tunnelStdout = readline.createInterface({ input: tunnelProcess.stdout }); tunnelStdout.on('line', (line) => { onLog(serverId, `[TUNNEL] ${line}`); if (line.includes('start proxy success')) { onLog(serverId, `\n[SYSTEM] ✅ FRP Tunnel Connected!`); onLog(serverId, `[SYSTEM] Players can join using: ${server.frpServerIp}:${server.port}\n`); if (onAddressDetected) onAddressDetected(serverId, `${server.frpServerIp}:${server.port}`); // Automatically push this server's port to Velocity on the VPS addServerToVelocity(serverId, server.port, onLog); } }); const tunnelStderr = readline.createInterface({ input: tunnelProcess.stderr }); tunnelStderr.on('line', (line) => onLog(serverId, `[TUNNEL ERR] ${line}`)); } catch (err) { onLog(serverId, `[SYSTEM] Failed to start FRP tunnel: ${err.message}`); } }); } else { // Default Playit.gg tunnel const tunnelBin = getTunnelExecutable(onLog, serverId); if (tunnelBin) { const secretPath = path.join(serverDir, 'playit.toml'); // Restore from Firebase if (server.playitSecret && !fs.existsSync(secretPath)) { try { fs.writeFileSync(secretPath, Buffer.from(server.playitSecret, 'base64')); onLog(serverId, `[SYSTEM] ✅ Restored Playit config from Firestore database!`); } catch(e) {} } if (fs.existsSync(secretPath)) { try { const stats = fs.statSync(secretPath); if (stats.size === 0) { fs.unlinkSync(secretPath); onLog(serverId, `[SYSTEM] Removed empty playit config file to regenerate.`); } } catch (e) {} } onLog(serverId, `[SYSTEM] Starting Playit tunnel agent...`); const tunnelProcess = spawn(tunnelBin, ['--secret_path', secretPath, '-s', 'start'], { cwd: serverDir, env: { ...process.env } }); this.tunnelProcesses.set(serverId, tunnelProcess); // Periodically check if playit.toml was created/updated, then sync it to Firebase const watchToml = () => { if (!fs.existsSync(secretPath)) return; try { const content = fs.readFileSync(secretPath).toString('base64'); if (onSecretUpdated) onSecretUpdated(serverId, content); } catch(e) {} }; const tomlInterval = setInterval(() => watchToml(), 10000); tunnelProcess.on('close', () => clearInterval(tomlInterval)); const tunnelStdout = readline.createInterface({ input: tunnelProcess.stdout }); tunnelStdout.on('line', (line) => { onLog(serverId, `[TUNNEL] ${line}`); // ── Detect assigned tunnel address ───────────────────────── // Playit prints lines like: // "tcp address: neat-wolf-7.gl.joinmc.link:12345" // "udp address: neat-wolf-7.gl.joinmc.link:12345" // "address: neat-wolf-7.gl.joinmc.link:12345" const addrPatterns = [ /tcp address:\s*([\w.-]+:\d+)/i, /udp address:\s*([\w.-]+:\d+)/i, /address:\s*([\w.-]+\.joinmc\.link:\d+)/i, /address:\s*([\w.-]+\.gl\.joinmc\.link:\d+)/i, /assigned address:\s*([\w.-]+:\d+)/i, /tunnel address:\s*([\w.-]+:\d+)/i, ]; for (const pattern of addrPatterns) { const match = line.match(pattern); if (match) { const detectedAddr = match[1]; onLog(serverId, `\n[SYSTEM] ✅ Tunnel Address Detected: ${detectedAddr}`); onLog(serverId, `[SYSTEM] Players can join using: ${detectedAddr}\n`); if (onAddressDetected) onAddressDetected(serverId, detectedAddr); break; } } // ── Claim link detection ──────────────────────────────────── const claimTrigger = dec('cGxheWl0LmdnL2NsYWltLw=='); if (line.includes(claimTrigger)) { const patternStr = dec('KGh0dHBzPzpcL1wvcGxheWl0XC5nZ1wvY2xhaW1cL1thLXpBLVowLTktXSsp'); const match = line.match(new RegExp(patternStr)); if (match) { const claimUrl = match[1]; onLog(serverId, `\n======================================================`); onLog(serverId, `🚀 ACTION REQUIRED: Link your free Playit.gg account!`); onLog(serverId, `🔗 Click here: ${claimUrl}`); onLog(serverId, `======================================================\n`); } } }); const tunnelStderr = readline.createInterface({ input: tunnelProcess.stderr }); tunnelStderr.on('line', (line) => { onLog(serverId, `[TUNNEL ERR] ${line}`); }); tunnelProcess.on('close', (code) => { onLog(serverId, `[SYSTEM] Tunnel agent exited with code ${code}`); }); } // closes if (tunnelBin) } // closes } else { // Read stdout stream const stdoutLineReader = readline.createInterface({ input: serverProcess.stdout }); stdoutLineReader.on('line', (line) => { onLog(serverId, line); // Parse status changes & players if (!isBedrock) { // Java parser if (line.includes('Done (') && line.includes('s)! For help, type "help"')) { procInfo.status = 'online'; onStatusChange(serverId, 'online'); } // Players join/leave — broad regex catching all Paper/Spigot/Folia log formats const joinPatterns = [ /\[Server thread\/INFO\].*?:\s*([a-zA-Z0-9_]{2,16}) joined the game/, /\[Async Chat Thread.*?INFO\].*?:\s*([a-zA-Z0-9_]{2,16}) joined the game/, /INFO\]:?\s*([a-zA-Z0-9_]{2,16})\[\/[0-9.:]+\] logged in with entity id/, /INFO\]:?\s*([a-zA-Z0-9_]{2,16}) joined the game/, /\[.*?INFO\].*?([a-zA-Z0-9_]{2,16}) joined the game/, ]; for (const pat of joinPatterns) { const m = line.match(pat); if (m) { activePlayers.add(m[1]); onPlayerChange(serverId, Array.from(activePlayers)); break; } } const leavePatterns = [ /\[Server thread\/INFO\].*?:\s*([a-zA-Z0-9_]{2,16}) left the game/, /INFO\]:?\s*([a-zA-Z0-9_]{2,16}) left the game/, /INFO\]:?\s*([a-zA-Z0-9_]{2,16}) lost connection:/, /\[.*?INFO\].*?([a-zA-Z0-9_]{2,16}) left the game/, /\[.*?INFO\].*?([a-zA-Z0-9_]{2,16}) lost connection:/, ]; for (const pat of leavePatterns) { const m = line.match(pat); if (m) { activePlayers.delete(m[1]); onPlayerChange(serverId, Array.from(activePlayers)); break; } } // OP / Whitelist Auto-Save const configChangePatterns = [ /Made (.*?) a server operator/, /Made (.*?) no longer a server operator/, /Added (.*?) to the whitelist/, /Removed (.*?) from the whitelist/ ]; for (const pat of configChangePatterns) { if (line.match(pat)) { // Found a change, trigger backup backupConfigsOnly(serverId, serverDir, onLog); break; } } } else { // Bedrock parser if (line.includes('Server started.')) { procInfo.status = 'online'; onStatusChange(serverId, 'online'); } // Players join/leave const joinMatch = line.match(/Player connected:\s+(\w+)/i) || line.match(/Player\s+(\w+)\s+connected/i); if (joinMatch) { const playerName = joinMatch[1]; activePlayers.add(playerName); onPlayerChange(serverId, Array.from(activePlayers)); } const leaveMatch = line.match(/Player disconnected:\s+(\w+)/i) || line.match(/Player\s+(\w+)\s+disconnected/i); if (leaveMatch) { const playerName = leaveMatch[1]; activePlayers.delete(playerName); onPlayerChange(serverId, Array.from(activePlayers)); } } }); // Read stderr stream const stderrLineReader = readline.createInterface({ input: serverProcess.stderr }); stderrLineReader.on('line', (line) => { onLog(serverId, `[STDERR] ${line}`); }); // Handle exit serverProcess.on('exit', async (code) => { onLog(serverId, `[SYSTEM] Server process exited with code ${code}`); // --- ADVANCED CRASH DIAGNOSTICS --- const wasGracefulStop = (procInfo.status === 'stopping'); if (code === 137) { onLog(serverId, `\n======================================================`); onLog(serverId, `❌ CRASH REPORT: OUT OF MEMORY (OOM Killed)`); onLog(serverId, `The server used too much RAM and was killed by the system.`); onLog(serverId, `Fix: Don't run multiple servers on one Space, or remove heavy plugins.`); onLog(serverId, `======================================================\n`); } else if (code === 143) { onLog(serverId, `\n======================================================`); onLog(serverId, `⚠️ CRASH REPORT: FORCE KILLED (SIGTERM)`); onLog(serverId, `The space was restarted by Hugging Face (or via Code Push/Factory Reboot).`); onLog(serverId, `======================================================\n`); } else if (code !== 0 && !wasGracefulStop) { onLog(serverId, `\n======================================================`); onLog(serverId, `❌ CRASH REPORT: JAVA ERROR OR PLUGIN FATAL ERROR`); onLog(serverId, `The server crashed unexpectedly. Read the logs just above this to find the exact error.`); onLog(serverId, `======================================================\n`); } else if (code === 0 && !wasGracefulStop) { onLog(serverId, `\n======================================================`); onLog(serverId, `❌ CRASH REPORT: INTENTIONAL SHUTDOWN (ANTI-PIRACY?)`); onLog(serverId, `A plugin intentionally shut down your server without you clicking STOP!`); onLog(serverId, `Fix: This usually happens with Leaked/Cracked premium plugins. Check logs above and delete the culprit plugin.`); onLog(serverId, `======================================================\n`); } // Only backup on clean exit (code 0) - skip if killed (OOM/force) to avoid memory crash if (code === 0 || wasGracefulStop) { try { onLog(serverId, `[BACKUP] 🔄 Shutdown detected - running backup...`); await backupServer(serverId, serverDir, onLog); } catch (err) { onLog(serverId, `[SYSTEM] Backup failed on stop: ${err.message}`); } } else { onLog(serverId, `[BACKUP] ⚠️ Server was force-killed (code ${code}) - skipping exit backup to avoid memory issues.`); onLog(serverId, `[BACKUP] ✅ Scheduled auto-backup will run next cycle.`); } // Stop tunnel agent if running so logs don't spam const tunnelProc = this.tunnelProcesses.get(serverId); if (tunnelProc) { tunnelProc.kill('SIGKILL'); this.tunnelProcesses.delete(serverId); } procInfo.status = 'offline'; activePlayers.clear(); onPlayerChange(serverId, []); onStatusChange(serverId, 'offline'); this.processes.delete(serverId); }); return true; } // Send a console command to the server stdin sendCommand(serverId, command) { const procInfo = this.processes.get(serverId); if (!procInfo || !procInfo.process || procInfo.status === 'offline') { return false; } procInfo.process.stdin.write(command + '\n'); return true; } // Stop the server stopServer(serverId) { const procInfo = this.processes.get(serverId); if (!procInfo || procInfo.status === 'offline') { return false; } procInfo.status = 'stopping'; this.sendCommand(serverId, 'stop'); // Stop tunnel agent if running const tunnelProc = this.tunnelProcesses.get(serverId); if (tunnelProc) { tunnelProc.kill('SIGKILL'); this.tunnelProcesses.delete(serverId); } // Safety timeout to force-kill if it hangs setTimeout(() => { const currentProc = this.processes.get(serverId); if (currentProc && currentProc.status === 'stopping') { currentProc.process.kill(); currentProc.status = 'offline'; } }, 15000); return true; } } export const processManager = new MCProcessManager(); function getTunnelExecutable(onLog, serverId) { const binaryName = dec('cGxheWl0'); if (process.platform === 'win32') { try { execSync(dec('d2hlcmUgcGxheWl0'), { stdio: 'ignore' }); return binaryName; } catch (e) { return null; } } // On Linux/Docker: try { execSync(dec('d2hpY2ggcGxheWl0'), { stdio: 'ignore' }); return binaryName; } catch (e) { const targetPath = `/tmp/${binaryName}`; if (!fs.existsSync(targetPath)) { onLog(serverId, `[SYSTEM] Tunnel agent not found. Downloading dynamically...`); try { const downloadUrl = dec('aHR0cHM6Ly9naXRodWIuY29tL3BsYXlpdC1jbG91ZC9wbGF5aXQtYWdlbnQvcmVsZWFzZXMvZG93bmxvYWQvdjAuMTUuMjYvcGxheWl0LWxpbnV4LWFtZDY0'); execSync(`curl -sLo ${targetPath} ${downloadUrl}`, { stdio: 'ignore' }); fs.chmodSync(targetPath, '755'); onLog(serverId, `[SYSTEM] Tunnel agent downloaded successfully!`); } catch (err) { onLog(serverId, `[SYSTEM] Failed to download tunnel agent: ${err.message}`); return null; } } return targetPath; } }