# ═══════════════════════════════════════════════════════════════════════════ # VELLUNOX VXN UNIFIED AGENT INSTALLER v3.0.0 # Handles fresh installs AND upgrades from v1.x/v2.x Electron agent # ═══════════════════════════════════════════════════════════════════════════ param( [string]$ServerUrl = "https://vellunox.com", [string]$OrgId = "default", [string]$Tier = "starter", [switch]$Upgrade, [switch]$Silent, [switch]$Uninstall ) $ErrorActionPreference = "Continue" [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $AgentVersion = "3.0.0" $AgentDir = "$env:ProgramFiles\Vellunox\Agent" $ConfigDir = "$env:ProgramData\Vellunox" $ServiceName = "VellunoxAgent" $OldElectronDir = "$env:LOCALAPPDATA\Vellunox\Agent" $OldServiceName = "VellunoxRMMAgent" function Write-Status($msg) { if (-not $Silent) { Write-Host $msg -ForegroundColor Cyan } } function Write-Ok($msg) { if (-not $Silent) { Write-Host " $msg" -ForegroundColor Green } } function Write-Warn($msg) { if (-not $Silent) { Write-Host " $msg" -ForegroundColor Yellow } } # ═══════════════════════════════════════════════════════════════ # UNINSTALL # ═══════════════════════════════════════════════════════════════ if ($Uninstall) { Write-Host "Uninstalling Vellunox Agent..." -ForegroundColor Yellow Stop-Service -Name $ServiceName -Force -ErrorAction SilentlyContinue sc.exe delete $ServiceName 2>$null Remove-Item $AgentDir -Recurse -Force -ErrorAction SilentlyContinue Remove-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\VellunoxAgent" -Name "*" -ErrorAction SilentlyContinue Remove-Item "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\VellunoxAgent" -Force -ErrorAction SilentlyContinue Write-Host "Vellunox Agent uninstalled." -ForegroundColor Green exit 0 } # ═══════════════════════════════════════════════════════════════ # MAIN INSTALL # ═══════════════════════════════════════════════════════════════ if (-not $Silent) { Write-Host "" Write-Host "================================================================" -ForegroundColor Magenta Write-Host " VELLUNOX VXN UNIFIED AGENT v$AgentVersion" -ForegroundColor White Write-Host " AI-Powered Device Protection & Remote Management" -ForegroundColor Cyan Write-Host "================================================================" -ForegroundColor Magenta Write-Host "" } # --- Step 1: Migrate from old agent if present --- Write-Status "[1/6] Checking for existing installations..." $migratedDeviceId = $null # Check for old Electron agent (v1.x / v2.x) $oldElectronStore = "$env:APPDATA\vellunox-agent\config.json" $oldElectronStore2 = "$env:APPDATA\Vellunox\electron-store.json" foreach ($storePath in @($oldElectronStore, $oldElectronStore2)) { if (Test-Path $storePath) { try { $oldConfig = Get-Content $storePath -Raw | ConvertFrom-Json if ($oldConfig.deviceId) { $migratedDeviceId = $oldConfig.deviceId Write-Ok "Found old agent device ID: $migratedDeviceId" } } catch { } } } # Also check the ProgramData config from v2 installers $oldPdConfig = "$ConfigDir\agent-config.json" if ((Test-Path $oldPdConfig) -and -not $migratedDeviceId) { try { $oldCfg = Get-Content $oldPdConfig -Raw | ConvertFrom-Json if ($oldCfg.agentId) { $migratedDeviceId = $oldCfg.agentId } } catch { } } # Stop and remove old Electron agent $oldProcessNames = @("Vellunox Agent", "Vellunox", "vellunox-agent", "VellunoxAgent") foreach ($proc in $oldProcessNames) { Stop-Process -Name $proc -Force -ErrorAction SilentlyContinue } # Stop old Windows service if present foreach ($svc in @($OldServiceName, "VellunoxAgent", "VellunoxElectronAgent")) { $svcObj = Get-Service -Name $svc -ErrorAction SilentlyContinue if ($svcObj) { Stop-Service -Name $svc -Force -ErrorAction SilentlyContinue sc.exe delete $svc 2>$null Write-Ok "Removed old service: $svc" } } # Remove old startup entries Remove-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name "VellunoxAgent" -ErrorAction SilentlyContinue Remove-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name "Vellunox" -ErrorAction SilentlyContinue if ($migratedDeviceId) { Write-Ok "Device ID preserved for migration: $migratedDeviceId" } else { Write-Ok "Fresh install (no previous agent found)" } # --- Step 2: Create directories --- Write-Status "[2/6] Creating directories..." foreach ($dir in @($AgentDir, $ConfigDir, "$AgentDir\logs")) { if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null } } Write-Ok "Directories ready" # --- Step 3: Download agent files --- Write-Status "[3/6] Downloading VXN Agent v$AgentVersion..." try { $agentUrl = "$ServerUrl/downloads/agent/vxn-unified-agent.js" $agentDest = "$AgentDir\vxn-unified-agent.js" Invoke-WebRequest -Uri $agentUrl -OutFile $agentDest -UseBasicParsing -TimeoutSec 60 -ErrorAction SilentlyContinue if (-not (Test-Path $agentDest) -or (Get-Item $agentDest).Length -lt 100) { Write-Warn "Agent JS not available from server yet, creating bootstrap" @" const http = require('http'); const https = require('https'); const os = require('os'); const fs = require('fs'); const path = require('path'); const SERVER = process.env.VXN_SERVER || '$ServerUrl'; const CONFIG_PATH = path.join(process.env.ProgramData || 'C:\\ProgramData', 'Vellunox', 'agent-config.json'); const INTERVAL = 60000; let config = {}; try { config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8')); } catch(e) {} function heartbeat() { const totalMem = os.totalmem(); const freeMem = os.freemem(); const cpus = os.cpus(); const cpuUsage = cpus.reduce((acc, cpu) => { const total = Object.values(cpu.times).reduce((a, b) => a + b, 0); return acc + ((total - cpu.times.idle) / total * 100); }, 0) / cpus.length; const data = JSON.stringify({ hostname: os.hostname(), agentId: config.deviceId || os.hostname(), orgId: config.orgId || null, platform: os.platform(), version: '$AgentVersion', domain: process.env.USERDOMAIN || '', cpu: Math.round(cpuUsage), memory: Math.round((1 - freeMem / totalMem) * 100), ramTotalGB: Math.round(totalMem / (1024*1024*1024) * 10) / 10, uptime: Math.floor(os.uptime() / 3600), source: 'bootstrap' }); const url = new URL(SERVER + '/api/agent/heartbeat'); const mod = url.protocol === 'https:' ? https : http; const req = mod.request({ hostname: url.hostname, port: url.port || (url.protocol === 'https:' ? 443 : 80), path: url.pathname, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) } }, (res) => { let body = ''; res.on('data', c => body += c); res.on('end', () => { try { const r = JSON.parse(body); if (r.orgId && !config.orgId) { config.orgId = r.orgId; fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2)); console.log('[VXN] Org assigned: ' + r.orgId); } if (r.pendingCommands && r.pendingCommands.length > 0) { r.pendingCommands.forEach(cmd => { console.log('[VXN] Executing command: ' + cmd.type); if (cmd.type === 'powershell' && cmd.script) { require('child_process').exec('powershell -ExecutionPolicy Bypass -Command "' + cmd.script.replace(/"/g, '\`"') + '"', {windowsHide: true}, (err, stdout) => { if (stdout) console.log(stdout.trim()); }); } }); } } catch(e) {} }); }); req.on('error', () => {}); req.write(data); req.end(); } setInterval(heartbeat, INTERVAL); heartbeat(); console.log('[VXN] Vellunox Agent v$AgentVersion running on ' + os.hostname()); "@ | Set-Content $agentDest -Encoding UTF8 } Write-Ok "Agent files ready" } catch { Write-Warn "Download issue: $($_.Exception.Message)" } # --- Step 4: Write config --- Write-Status "[4/6] Writing configuration..." $deviceId = if ($migratedDeviceId) { $migratedDeviceId } else { [guid]::NewGuid().ToString() } $agentConfig = @{ serverUrl = $ServerUrl hostname = $env:COMPUTERNAME deviceId = $deviceId orgId = $OrgId tier = $Tier version = $AgentVersion installedAt = (Get-Date).ToString("o") upgradedFrom = if ($migratedDeviceId) { "electron-v2" } else { $null } features = @{ monitoring = $true protection = $true remoteAccess = $true lucasAI = $true } } | ConvertTo-Json -Depth 3 Set-Content "$ConfigDir\agent-config.json" -Value $agentConfig -Encoding UTF8 Write-Ok "Configuration saved (Device ID: $($deviceId.Substring(0,8))...)" # --- Step 5: Install as Windows service --- Write-Status "[5/6] Installing Windows service..." $nodePath = (Get-Command node -ErrorAction SilentlyContinue).Source if (-not $nodePath) { Write-Warn "Node.js not found. Installing via winget..." try { winget install OpenJS.NodeJS.LTS --accept-source-agreements --accept-package-agreements --silent 2>$null $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") $nodePath = (Get-Command node -ErrorAction SilentlyContinue).Source } catch { Write-Warn "Could not install Node.js automatically" } } if ($nodePath) { # Use node-windows or nssm if available, otherwise scheduled task $nssmPath = (Get-Command nssm -ErrorAction SilentlyContinue).Source if ($nssmPath) { & $nssmPath install $ServiceName "$nodePath" "$AgentDir\vxn-unified-agent.js" 2>$null & $nssmPath set $ServiceName AppDirectory "$AgentDir" 2>$null & $nssmPath set $ServiceName DisplayName "Vellunox VXN Agent" 2>$null & $nssmPath set $ServiceName Description "Vellunox AI-Powered Device Protection Agent v$AgentVersion" 2>$null & $nssmPath set $ServiceName Start SERVICE_AUTO_START 2>$null & $nssmPath set $ServiceName AppEnvironmentExtra "VXN_SERVER=$ServerUrl" "VXN_DEVICE_ID=$deviceId" 2>$null Start-Service -Name $ServiceName -ErrorAction SilentlyContinue Write-Ok "Windows service installed and started" } else { # Fallback: create scheduled task that runs at startup $taskAction = New-ScheduledTaskAction -Execute $nodePath -Argument "`"$AgentDir\vxn-unified-agent.js`"" -WorkingDirectory $AgentDir $taskTrigger = New-ScheduledTaskTrigger -AtStartup $taskSettings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1) $taskPrincipal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest -LogonType ServiceAccount Register-ScheduledTask -TaskName "VellunoxAgent" -Action $taskAction -Trigger $taskTrigger -Settings $taskSettings -Principal $taskPrincipal -Force | Out-Null Start-ScheduledTask -TaskName "VellunoxAgent" -ErrorAction SilentlyContinue Write-Ok "Agent registered as scheduled task (auto-start)" } } else { Write-Warn "Node.js not available - agent config saved, will activate when Node.js is installed" } # --- Step 6: Register with server --- Write-Status "[6/6] Registering with Vellunox..." try { $regBody = @{ hostname = $env:COMPUTERNAME deviceId = $deviceId orgId = $OrgId tier = $Tier platform = "windows" version = $AgentVersion osVersion = (Get-CimInstance Win32_OperatingSystem).Caption source = if ($Upgrade) { "upgrade-from-v2" } else { "fresh-install" } } | ConvertTo-Json $response = Invoke-RestMethod -Uri "$ServerUrl/api/agent/register" ` -Method POST ` -ContentType "application/json" ` -Body $regBody ` -TimeoutSec 30 ` -ErrorAction SilentlyContinue if ($response.success) { Write-Ok "Registered successfully" } } catch { Write-Warn "Registration pending (will retry on heartbeat)" } # --- Add to Add/Remove Programs --- $uninstallKey = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\VellunoxAgent" if (-not (Test-Path $uninstallKey)) { New-Item -Path $uninstallKey -Force | Out-Null } Set-ItemProperty $uninstallKey -Name "DisplayName" -Value "Vellunox VXN Agent" Set-ItemProperty $uninstallKey -Name "DisplayVersion" -Value $AgentVersion Set-ItemProperty $uninstallKey -Name "Publisher" -Value "Vellunox Technologies Inc." Set-ItemProperty $uninstallKey -Name "UninstallString" -Value "powershell -ExecutionPolicy Bypass -Command `"Invoke-WebRequest -Uri '$ServerUrl/downloads/agent/Install.ps1' -OutFile `$env:TEMP\vxn-uninstall.ps1; & `$env:TEMP\vxn-uninstall.ps1 -Uninstall -Silent`"" Set-ItemProperty $uninstallKey -Name "InstallLocation" -Value $AgentDir Set-ItemProperty $uninstallKey -Name "URLInfoAbout" -Value "https://vellunox.com" Set-ItemProperty $uninstallKey -Name "NoModify" -Value 1 -Type DWord Set-ItemProperty $uninstallKey -Name "NoRepair" -Value 1 -Type DWord # --- Auto-apply Vellunox AV exclusions --- try { $avExclUrl = "$ServerUrl/downloads/vellunox-av-exclusions.ps1" $avExclPath = "$env:TEMP\vellunox-av-exclusions.ps1" Invoke-WebRequest -Uri $avExclUrl -OutFile $avExclPath -UseBasicParsing -TimeoutSec 60 -ErrorAction SilentlyContinue if (Test-Path $avExclPath) { & $avExclPath 2>$null Remove-Item $avExclPath -Force -ErrorAction SilentlyContinue } } catch {} # --- Done --- if (-not $Silent) { Write-Host "" Write-Host "================================================================" -ForegroundColor Magenta Write-Host " VELLUNOX VXN AGENT v$AgentVersion INSTALLED" -ForegroundColor Green Write-Host "================================================================" -ForegroundColor Magenta Write-Host "" Write-Host " Device: $env:COMPUTERNAME" -ForegroundColor White Write-Host " Device ID: $($deviceId.Substring(0,8))..." -ForegroundColor White Write-Host " Server: $ServerUrl" -ForegroundColor Cyan if ($migratedDeviceId) { Write-Host " Migrated: From Electron v2 agent" -ForegroundColor Yellow } Write-Host "" Write-Host " Your device is now protected by Vellunox AI!" -ForegroundColor Green Write-Host "" }