/* eslint-disable no-undef */ "use strict"; /** * Windows PowerShell command helpers for remote SSH hosts. * * Every script is UTF-16LE Base64-encoded and invoked via * powershell -NoProfile -NonInteractive -EncodedCommand * so cmd.exe / OpenSSH-Server quoting cannot break it. */ /** * Encode a PowerShell script string as UTF-16LE bytes, then Base64. * Matches what `powershell -EncodedCommand` expects. */ function encodePowerShellScript(script) { if (typeof Buffer !== "undefined" && Buffer.alloc) { // Node Buffer path: convert UTF-8 → UTF-16LE → Base64. const utf8 = Buffer.from(String(script), "utf8"); const utf16 = Buffer.alloc(utf8.length * 2); for (let i = 0; i < utf8.length; i++) { utf16[i * 2] = utf8[i]; // low byte (ASCII chars: high byte = 0) utf16[i * 2 + 1] = 0; // high byte } return utf16.toString("base64"); } // Browser / pure JS fallback. const chars = String(script); let out = ""; for (let i = 0; i < chars.length; i++) { const code = chars.charCodeAt(i); out += String.fromCharCode(code & 0xff, (code >> 8) & 0xff); } return btoa(out); } /** Build an SSH-exec-friendly PowerShell invocation. */ function wrapPowerShell(encodedBlob) { return `powershell -NoProfile -NonInteractive -EncodedCommand ${encodedBlob}`; } // --------------------------------------------------------------------------- // Process list // --------------------------------------------------------------------------- const PROCESS_LIST_PS = [ // Minimal Windows process list — speed > precision. // Single Get-Process pass; CPU% is a rough estimate (cumulative/uptime/cores). // PPID and command line come from Win32_Process (one WMI query, indexed). '$ErrorActionPreference = "SilentlyContinue";', '$procs = Get-Process;', // Build WMI lookup for PPID + command line '$wmi = @{};', 'Get-CimInstance Win32_Process | ForEach-Object { $wmi[[int]$_.ProcessId] = $_ };', // Get logical core count from first processor (faster than ComputerSystem WMI) '$cores = (Get-CimInstance Win32_Processor | Measure-Object -Property NumberOfLogicalProcessors -Sum).Sum;', 'if (-not $cores -or $cores -le 0) { $cores = 1 };', '$totalMemMB = [math]::Round((Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1MB, 0);', '$now = Get-Date;', '$rows = foreach ($p in $procs) {', ' $cpu = 0;', ' if ($p.CPU -ne $null -and $p.StartTime -ne $null) {', ' $up = ($now - $p.StartTime).TotalSeconds;', ' if ($up -gt 0) { $cpu = [math]::Round([double]$p.CPU / $up / $cores * 100, 2) };', ' if ($cpu -lt 0) { $cpu = 0 };', ' if ($cpu -gt 100) { $cpu = 100 };', ' }', ' $w = $wmi[$p.Id];', ' $ppid = if ($w) { [int]$w.ParentProcessId } else { 0 };', ' $cmd = if ($w -and $w.CommandLine) { $w.CommandLine } else { $p.ProcessName };', ' $el = "";', ' if ($p.StartTime) {', ' $e = $now - $p.StartTime;', ' $el = "{0}:{1}:{2}" -f [int]$e.TotalHours, $e.Minutes, $e.Seconds;', ' };', ' $wsKb = [math]::Round($p.WorkingSet64 / 1024, 0);', ' $mem = if ($totalMemMB -gt 0) { [math]::Round($wsKb / 1024 / $totalMemMB * 100, 2) } else { 0 };', ' [PSCustomObject]@{', ' ProcessId = $p.Id;', ' ParentProcessId = $ppid;', ' Name = $p.ProcessName;', ' CpuPercent = $cpu;', ' MemPercent = $mem;', ' WorkingSetKb = $wsKb;', ' Elapsed = $el;', ' CommandLine = $cmd;', ' }', '}', '$rows | Sort-Object WorkingSetKb -Descending | Select-Object -First 200 | ConvertTo-Json -Compress', ].join(" "); const PROCESS_LIST_PS_COMMAND = wrapPowerShell(encodePowerShellScript(PROCESS_LIST_PS)); // --------------------------------------------------------------------------- // Port list // --------------------------------------------------------------------------- // portOps.cjs already defines LISTEN_PORTS_WINDOWS inline; export it here too // so other ops can re-use the encoding convention. const PORT_LIST_PS_INNER = [ '$rows = @();', '$rows += @(Get-NetTCPConnection -State Listen -ErrorAction SilentlyContinue | ', "Select-Object LocalAddress,LocalPort,OwningProcess,@{Name='Protocol';Expression={'tcp'}});", '$rows += @(Get-NetUDPEndpoint -ErrorAction SilentlyContinue | Where-Object { ', '$a = [string]$_.LocalAddress; ', '$wildcard = ($a -eq "0.0.0.0" -or $a -eq "::" -or $a -eq "*"); ', '$loopback = ($a -eq "127.0.0.1" -or $a -eq "::1"); ', 'if ($wildcard -or $loopback) { $true } else { $_.LocalPort -lt 49152 } ', "} | Select-Object LocalAddress,LocalPort,OwningProcess,@{Name='Protocol';Expression={'udp'}}); ", "if ($rows.Count -gt 0) { $rows | ConvertTo-Json -Compress } else { '[]' }", ].join(""); const PORT_LIST_PS_COMMAND = wrapPowerShell(encodePowerShellScript(PORT_LIST_PS_INNER)); // --------------------------------------------------------------------------- // Service list // --------------------------------------------------------------------------- const SERVICE_LIST_PS_INNER = [ // Force UTF-8 output so Chinese service names don't get mangled over SSH. '[Console]::OutputEncoding = [System.Text.Encoding]::UTF8;', '$OutputEncoding = [System.Text.Encoding]::UTF8;', '$services = Get-Service -ErrorAction SilentlyContinue | ForEach-Object {', ' [PSCustomObject]@{', ' Name = $_.Name;', ' DisplayName = $_.DisplayName;', ' Status = $_.Status.ToString();', ' StartType = $_.StartType.ToString();', ' }', '};', '$services | ConvertTo-Json -Compress', ].join(" "); const SERVICE_LIST_PS_COMMAND = wrapPowerShell(encodePowerShellScript(SERVICE_LIST_PS_INNER)); // --------------------------------------------------------------------------- // Capability probe (mirrors CAPABILITY_SCRIPT_POSIX marker format) // --------------------------------------------------------------------------- const CAPABILITY_PROBE_PS_INNER = [ '$ErrorActionPreference = "SilentlyContinue";', 'Write-Output "__NC_OS__=Windows";', 'if (Get-Command tmux -ErrorAction SilentlyContinue) { Write-Output "__NC_TMUX__=1" };', 'if (Get-Command docker -ErrorAction SilentlyContinue) { Write-Output "__NC_DOCKER__=1" };', 'if (Get-Command nvidia-smi -ErrorAction SilentlyContinue) { Write-Output "__NC_NVIDIA_SMI__=1" };', 'if (Get-Command npu-smi -ErrorAction SilentlyContinue) { Write-Output "__NC_NPU_SMI__=1" };', 'if (Get-Command ss -ErrorAction SilentlyContinue) { Write-Output "__NC_SS__=1" };', // Windows always has netstat.exe 'Write-Output "__NC_NETSTAT__=1";', 'if (Get-Command lsof -ErrorAction SilentlyContinue) { Write-Output "__NC_LSOF__=1" };', // Windows has system services via Get-Service — treat as "systemctl-equivalent" 'Write-Output "__NC_SYSTEMCTL__=1";', ].join(" "); const CAPABILITY_PROBE_PS_COMMAND = wrapPowerShell(encodePowerShellScript(CAPABILITY_PROBE_PS_INNER)); // --------------------------------------------------------------------------- // Service action helper // --------------------------------------------------------------------------- function buildServiceActionPsCommand(action, serviceName) { const safe = String(serviceName).replace(/[^a-zA-Z0-9_\\-]/g, "").slice(0, 256); if (!safe) return null; let verb; switch (action) { case "start": verb = "Start-Service"; break; case "stop": verb = "Stop-Service"; break; case "restart": verb = "Restart-Service"; break; default: return null; } const script = `${verb} -Name "${safe}" -ErrorAction Stop; Write-Output "__NC_OK__"`; return wrapPowerShell(encodePowerShellScript(script)); } // --------------------------------------------------------------------------- // Process kill // --------------------------------------------------------------------------- function buildStopProcessPsCommand(pid, force) { const p = Math.trunc(Number(pid)); if (!Number.isFinite(p) || p <= 0) return null; const cmd = force ? `Stop-Process -Id ${p} -Force -ErrorAction Stop; Write-Output "__NC_OK__"` : `Stop-Process -Id ${p} -ErrorAction Stop; Write-Output "__NC_OK__"`; return wrapPowerShell(encodePowerShellScript(cmd)); } module.exports = { encodePowerShellScript, wrapPowerShell, PROCESS_LIST_PS_COMMAND, PORT_LIST_PS_COMMAND, SERVICE_LIST_PS_COMMAND, CAPABILITY_PROBE_PS_COMMAND, buildServiceActionPsCommand, buildStopProcessPsCommand, };