[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
198
electron/bridges/systemManager/windowsPowerShell.cjs
Normal file
198
electron/bridges/systemManager/windowsPowerShell.cjs
Normal file
@@ -0,0 +1,198 @@
|
||||
/* 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 <blob>
|
||||
* 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,
|
||||
};
|
||||
Reference in New Issue
Block a user