pyre-cli 10.1.0 → 10.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +211 -494
- package/dist/{checkCmd-RKW6QGDR.js → checkCmd-WQAHAUWO.js} +1 -1
- package/dist/{chunk-GHRDW646.js → chunk-23YWCI3O.js} +375 -135
- package/dist/{service-Q74BBILO.js → chunk-KFPTD266.js} +43 -3
- package/dist/{doctor-GYHFTNZQ.js → chunk-KVUMOA6C.js} +7 -6
- package/dist/{chunk-53O2RGSL.js → chunk-QM5L43EK.js} +19 -2
- package/dist/{chunk-6UHZP4QP.js → chunk-XTOOEKLM.js} +24 -12
- package/dist/{collectors-H43PN4PS.js → collectors-JDN5Y7EV.js} +1 -1
- package/dist/{completions-JKYEJNGR.js → completions-ZVTNE52B.js} +15 -11
- package/dist/{config-GYGJXU2Q.js → config-J4YMADEF.js} +3 -1
- package/dist/doctor-LHFW4UJP.js +10 -0
- package/dist/firebaseSync-ON3RZ6KX.js +91 -0
- package/dist/index.js +4245 -736
- package/dist/monitors/platform/windows-ui.ps1 +42 -0
- package/dist/{netUsageCmd-WKBTW2YG.js → netUsageCmd-67IBZZYN.js} +2 -2
- package/dist/{pipeCmd-7REDBLBU.js → pipeCmd-2N4JPKYE.js} +1 -1
- package/dist/{prometheusCmd-PL64BLN5.js → prometheusCmd-LEUM5WCZ.js} +1 -1
- package/dist/serveCmd-L7LVJOZN.js +353 -0
- package/dist/service-7GIX3YNC.js +16 -0
- package/dist/syncCmd-42IRF633.js +246 -0
- package/dist/{topoCmd-DU2HHZNH.js → topoCmd-DZ3YS5IL.js} +1 -1
- package/dist/{watchdogCmd-UF53JPZB.js → watchdogCmd-6GR4UN6B.js} +7 -7
- package/dist/{wizard-TD7BGX6O.js → wizard-JQMQWFAW.js} +1 -1
- package/package.json +4 -3
- package/parity.md +2 -0
- package/src/completions.ts +15 -11
- package/src/easy_features.test.ts +149 -0
- package/src/firebaseSync.ts +157 -0
- package/src/formatters/html_template.ts +3086 -598
- package/src/formatters/render.ts +25 -12
- package/src/index.ts +518 -24
- package/src/live/input.ts +501 -148
- package/src/live/render.ts +163 -18
- package/src/live/state.ts +52 -2
- package/src/live/types.ts +1 -1
- package/src/monitors/collectors.ts +3 -10
- package/src/monitors/platform/windows-ui.ps1 +42 -0
- package/src/monitors/platform/windows.ts +442 -147
- package/src/monitors/types.ts +8 -0
- package/src/serveCmd.ts +17 -2
- package/src/service.ts +49 -0
- package/src/state/config.ts +48 -0
- package/src/syncCmd.ts +159 -11
- package/src/web/dashboard.ts +205 -0
- package/dist/serveCmd-2H5VLF3P.js +0 -153
- package/dist/syncCmd-A2W3QB3S.js +0 -126
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
collectBlenderRenders
|
|
6
6
|
} from "./chunk-VIACN2RU.js";
|
|
7
7
|
import {
|
|
8
|
-
run
|
|
8
|
+
run,
|
|
9
|
+
runPowerShell
|
|
9
10
|
} from "./chunk-MZO5QKUR.js";
|
|
10
11
|
|
|
11
12
|
// src/monitors/collectors.ts
|
|
@@ -818,6 +819,26 @@ async function getLinuxDisplayInfo() {
|
|
|
818
819
|
// src/monitors/platform/windows.ts
|
|
819
820
|
import os2 from "os";
|
|
820
821
|
var prevNetSample2 = null;
|
|
822
|
+
function safeParseInt(s, def = 0) {
|
|
823
|
+
if (!s) return def;
|
|
824
|
+
const n = parseInt(s, 10);
|
|
825
|
+
return Number.isFinite(n) ? n : def;
|
|
826
|
+
}
|
|
827
|
+
async function getCimRow(className) {
|
|
828
|
+
const raw = await runPowerShell(
|
|
829
|
+
`Get-CimInstance -ClassName ${className} -ErrorAction SilentlyContinue | Select-Object -Property * -ExcludeProperty CIM* | ConvertTo-Csv -NoTypeInformation`,
|
|
830
|
+
""
|
|
831
|
+
);
|
|
832
|
+
const lines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
833
|
+
if (lines.length < 2) return {};
|
|
834
|
+
const headers = lines[0].split(",").map((h) => h.trim().replace(/^"|"$/g, ""));
|
|
835
|
+
const values = lines[1].split(",").map((v) => v.trim().replace(/^"|"$/g, ""));
|
|
836
|
+
const row = {};
|
|
837
|
+
headers.forEach((h, i) => {
|
|
838
|
+
row[h] = values[i] || "";
|
|
839
|
+
});
|
|
840
|
+
return row;
|
|
841
|
+
}
|
|
821
842
|
async function collectWindowsSystem() {
|
|
822
843
|
const hostname = os2.hostname();
|
|
823
844
|
const rel = os2.release();
|
|
@@ -842,17 +863,26 @@ async function collectWindowsSystem() {
|
|
|
842
863
|
async function collectWindowsCpu(prevCpuTimesRef) {
|
|
843
864
|
const cpus = os2.cpus();
|
|
844
865
|
const cores = cpus.length || 1;
|
|
845
|
-
|
|
846
|
-
|
|
866
|
+
let brand = cpus[0]?.model || "Windows CPU";
|
|
867
|
+
let physicalCores = cores;
|
|
868
|
+
let frequency = cpus[0]?.speed || 0;
|
|
869
|
+
try {
|
|
870
|
+
const cpuRow = await getCimRow("Win32_Processor");
|
|
871
|
+
if (cpuRow.Name) brand = cpuRow.Name.trim();
|
|
872
|
+
if (cpuRow.NumberOfCores) physicalCores = safeParseInt(cpuRow.NumberOfCores, cores);
|
|
873
|
+
if (cpuRow.MaxClockSpeed) frequency = safeParseInt(cpuRow.MaxClockSpeed, frequency);
|
|
874
|
+
} catch {
|
|
875
|
+
}
|
|
847
876
|
const current = cpus.map((cpu) => ({
|
|
848
877
|
total: cpu.times.user + cpu.times.nice + cpu.times.sys + cpu.times.idle + cpu.times.irq,
|
|
849
878
|
idle: cpu.times.idle
|
|
850
879
|
}));
|
|
851
880
|
let coreUsage = [];
|
|
852
881
|
let usage = 0;
|
|
853
|
-
|
|
882
|
+
const prevCpuTimes2 = prevCpuTimesRef.current;
|
|
883
|
+
if (prevCpuTimes2 && prevCpuTimes2.length === current.length) {
|
|
854
884
|
coreUsage = current.map((c, i) => {
|
|
855
|
-
const prev =
|
|
885
|
+
const prev = prevCpuTimes2[i];
|
|
856
886
|
const totalDelta = c.total - prev.total;
|
|
857
887
|
const idleDelta = c.idle - prev.idle;
|
|
858
888
|
if (totalDelta <= 0) return 0;
|
|
@@ -867,22 +897,32 @@ async function collectWindowsCpu(prevCpuTimesRef) {
|
|
|
867
897
|
const loadAvg = [activeLoad, activeLoad, activeLoad];
|
|
868
898
|
let temperature;
|
|
869
899
|
try {
|
|
870
|
-
const
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
900
|
+
const raw = await runPowerShell(
|
|
901
|
+
`(Get-Counter 'Thermal Zone Information(*)*' -ErrorAction SilentlyContinue).CounterSamples | Where-Object { $_.CookedValue -gt 200 -and $_.CookedValue -lt 400 } | Select-Object -First 1 -ExpandProperty CookedValue`,
|
|
902
|
+
""
|
|
903
|
+
).catch(() => "");
|
|
904
|
+
const tempK = parseFloat(raw);
|
|
905
|
+
if (tempK > 200 && tempK < 400) {
|
|
906
|
+
temperature = Math.round((tempK - 273.15) * 10) / 10;
|
|
876
907
|
}
|
|
877
908
|
} catch {
|
|
878
909
|
}
|
|
879
910
|
if (temperature === void 0) {
|
|
880
|
-
|
|
911
|
+
try {
|
|
912
|
+
const rawWmi = await run("wmic /namespace:\\\\root\\wmi PATH MSAcpi_ThermalZoneTemperature get CurrentTemperature 2>nul", "", 1500);
|
|
913
|
+
const match = rawWmi.match(/(\d{3,4})/);
|
|
914
|
+
if (match) {
|
|
915
|
+
const kelvinTenths = parseInt(match[1], 10);
|
|
916
|
+
const c = Math.round((kelvinTenths - 2732) / 10 * 10) / 10;
|
|
917
|
+
if (c >= 15 && c <= 115) temperature = c;
|
|
918
|
+
}
|
|
919
|
+
} catch {
|
|
920
|
+
}
|
|
881
921
|
}
|
|
882
922
|
return {
|
|
883
923
|
brand,
|
|
884
924
|
cores,
|
|
885
|
-
physicalCores
|
|
925
|
+
physicalCores,
|
|
886
926
|
frequency,
|
|
887
927
|
usage,
|
|
888
928
|
loadAvg,
|
|
@@ -899,18 +939,19 @@ async function collectWindowsMemory() {
|
|
|
899
939
|
let swapUsed = 0;
|
|
900
940
|
let swapFree = 0;
|
|
901
941
|
try {
|
|
902
|
-
const rawPage =
|
|
942
|
+
const rawPage = await runPowerShell(
|
|
943
|
+
`Get-CimInstance Win32_PageFileUsage | ForEach-Object { "$($_.AllocatedBaseSize)|$($_.CurrentUsage)" }`,
|
|
944
|
+
""
|
|
945
|
+
);
|
|
903
946
|
const lines = rawPage.split("\n").filter((l) => l.trim().length > 0);
|
|
904
|
-
|
|
905
|
-
const
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
swapUsed = usedMb * 1024 * 1024;
|
|
911
|
-
swapFree = Math.max(0, swapTotal - swapUsed);
|
|
912
|
-
}
|
|
947
|
+
for (const line of lines) {
|
|
948
|
+
const [allocStr, usedStr] = line.split("|");
|
|
949
|
+
const allocMb = safeParseInt(allocStr);
|
|
950
|
+
const usedMb = safeParseInt(usedStr);
|
|
951
|
+
swapTotal += allocMb * 1024 * 1024;
|
|
952
|
+
swapUsed += usedMb * 1024 * 1024;
|
|
913
953
|
}
|
|
954
|
+
swapFree = Math.max(0, swapTotal - swapUsed);
|
|
914
955
|
} catch {
|
|
915
956
|
}
|
|
916
957
|
let pressureLevel = "Normal";
|
|
@@ -930,15 +971,17 @@ async function collectWindowsMemory() {
|
|
|
930
971
|
}
|
|
931
972
|
async function collectWindowsDisk() {
|
|
932
973
|
try {
|
|
933
|
-
const raw =
|
|
934
|
-
|
|
974
|
+
const raw = await runPowerShell(
|
|
975
|
+
`Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | ForEach-Object { "$($_.DeviceID)|$($_.FileSystem)|$($_.Size)|$($_.FreeSpace)" }`,
|
|
976
|
+
""
|
|
977
|
+
);
|
|
935
978
|
const disks = [];
|
|
936
|
-
for (const line of
|
|
937
|
-
const parts = line.trim().split(
|
|
979
|
+
for (const line of raw.split("\n")) {
|
|
980
|
+
const parts = line.trim().split("|");
|
|
938
981
|
if (parts.length >= 4) {
|
|
939
982
|
const drive = parts[0];
|
|
940
|
-
const
|
|
941
|
-
const
|
|
983
|
+
const totalBytes = safeParseInt(parts[2]);
|
|
984
|
+
const freeBytes = safeParseInt(parts[3]);
|
|
942
985
|
if (totalBytes > 0) {
|
|
943
986
|
const usedBytes = Math.max(0, totalBytes - freeBytes);
|
|
944
987
|
const capPct = Math.round(usedBytes / totalBytes * 100);
|
|
@@ -979,104 +1022,249 @@ function formatBytesStr(bytes) {
|
|
|
979
1022
|
}
|
|
980
1023
|
async function collectWindowsBattery() {
|
|
981
1024
|
try {
|
|
982
|
-
const raw =
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1025
|
+
const raw = await runPowerShell(
|
|
1026
|
+
`$bat = Get-CimInstance Win32_Battery -ErrorAction SilentlyContinue; if ($bat) { "$($bat.EstimatedChargeRemaining)|$($bat.BatteryStatus)|$($bat.EstimatedRunTime)|$($bat.DischargeRate)" } else { '' }`,
|
|
1027
|
+
""
|
|
1028
|
+
);
|
|
1029
|
+
if (!raw) return null;
|
|
1030
|
+
const parts = raw.split("|");
|
|
1031
|
+
const level = safeParseInt(parts[0], 0);
|
|
1032
|
+
const statusCode = safeParseInt(parts[1], 1);
|
|
1033
|
+
const runTimeMinutes = safeParseInt(parts[2], 0);
|
|
1034
|
+
const dischargeRate = safeParseInt(parts[3], 0);
|
|
1035
|
+
const powerSource = statusCode === 2 || statusCode === 3 || statusCode === 6 ? "AC" : "Battery";
|
|
1036
|
+
const state = statusCode === 3 ? "charged" : statusCode === 6 || statusCode === 2 ? "charging" : "discharging";
|
|
1037
|
+
let timeRemaining = "calculating";
|
|
1038
|
+
if (state === "charged") timeRemaining = "\u221E";
|
|
1039
|
+
else if (runTimeMinutes && runTimeMinutes > 0 && runTimeMinutes < 7e4) {
|
|
1040
|
+
const h = Math.floor(runTimeMinutes / 60);
|
|
1041
|
+
const m = runTimeMinutes % 60;
|
|
1042
|
+
timeRemaining = `${h}:${m.toString().padStart(2, "0")}`;
|
|
1043
|
+
}
|
|
1044
|
+
const powerWatts = dischargeRate > 0 ? Math.round(dischargeRate / 1e3 * 100) / 100 : void 0;
|
|
1045
|
+
return {
|
|
1046
|
+
level,
|
|
1047
|
+
state,
|
|
1048
|
+
timeRemaining,
|
|
1049
|
+
health: "Good",
|
|
1050
|
+
powerSource,
|
|
1051
|
+
powerWatts
|
|
1052
|
+
};
|
|
1008
1053
|
} catch {
|
|
1054
|
+
return null;
|
|
1009
1055
|
}
|
|
1010
|
-
return null;
|
|
1011
1056
|
}
|
|
1012
1057
|
async function collectWindowsThermal() {
|
|
1058
|
+
const temperatures = {};
|
|
1013
1059
|
let tempC;
|
|
1014
1060
|
try {
|
|
1015
|
-
const
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1061
|
+
const raw = await runPowerShell(
|
|
1062
|
+
`(Get-Counter 'Thermal Zone Information(*)*' -ErrorAction SilentlyContinue).CounterSamples | Where-Object { $_.CookedValue -gt 200 -and $_.CookedValue -lt 400 } | Select-Object -First 1 -ExpandProperty CookedValue`,
|
|
1063
|
+
""
|
|
1064
|
+
).catch(() => "");
|
|
1065
|
+
const tempK = parseFloat(raw);
|
|
1066
|
+
if (tempK > 200 && tempK < 400) {
|
|
1067
|
+
tempC = Math.round((tempK - 273.15) * 10) / 10;
|
|
1068
|
+
temperatures["cpu"] = tempC;
|
|
1021
1069
|
}
|
|
1022
1070
|
} catch {
|
|
1023
1071
|
}
|
|
1024
1072
|
if (tempC === void 0) {
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1073
|
+
try {
|
|
1074
|
+
const rawWmi = await run("wmic /namespace:\\\\root\\wmi PATH MSAcpi_ThermalZoneTemperature get CurrentTemperature 2>nul", "", 1500);
|
|
1075
|
+
const match = rawWmi.match(/(\d{3,4})/);
|
|
1076
|
+
if (match) {
|
|
1077
|
+
const kelvinTenths = parseInt(match[1], 10);
|
|
1078
|
+
const c = Math.round((kelvinTenths - 2732) / 10 * 10) / 10;
|
|
1079
|
+
if (c >= 15 && c <= 115) {
|
|
1080
|
+
tempC = c;
|
|
1081
|
+
temperatures["cpu"] = c;
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
} catch {
|
|
1085
|
+
}
|
|
1028
1086
|
}
|
|
1087
|
+
let gpuTemp;
|
|
1088
|
+
try {
|
|
1089
|
+
const gpuRaw = await runPowerShell(
|
|
1090
|
+
`(Get-Counter 'GPU(*)*' -ErrorAction SilentlyContinue).CounterSamples | Where-Object { $_.CounterName -match 'Temperature' -and $_.CookedValue -gt 0 } | Select-Object -First 1 -ExpandProperty CookedValue`,
|
|
1091
|
+
""
|
|
1092
|
+
).catch(() => "");
|
|
1093
|
+
gpuTemp = parseFloat(gpuRaw);
|
|
1094
|
+
if (gpuTemp > 0 && gpuTemp < 150) {
|
|
1095
|
+
temperatures["gpu"] = Math.round(gpuTemp * 10) / 10;
|
|
1096
|
+
}
|
|
1097
|
+
} catch {
|
|
1098
|
+
}
|
|
1099
|
+
const cpuTemp = tempC ?? gpuTemp;
|
|
1029
1100
|
let state = "Nominal";
|
|
1030
1101
|
let pressureLevel = 0;
|
|
1031
|
-
if (
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1102
|
+
if (cpuTemp !== void 0) {
|
|
1103
|
+
if (cpuTemp >= 100) {
|
|
1104
|
+
state = "Critical";
|
|
1105
|
+
pressureLevel = 3;
|
|
1106
|
+
} else if (cpuTemp >= 90) {
|
|
1107
|
+
state = "Serious";
|
|
1108
|
+
pressureLevel = 2;
|
|
1109
|
+
} else if (cpuTemp >= 80) {
|
|
1110
|
+
state = "Fair";
|
|
1111
|
+
pressureLevel = 1;
|
|
1112
|
+
}
|
|
1040
1113
|
}
|
|
1041
1114
|
return {
|
|
1042
1115
|
state,
|
|
1043
|
-
detail: `${state} (${
|
|
1116
|
+
detail: cpuTemp !== void 0 ? `${state} (${cpuTemp}\xB0C)` : state,
|
|
1044
1117
|
pressureLevel,
|
|
1045
|
-
temperatures:
|
|
1118
|
+
temperatures: Object.keys(temperatures).length ? temperatures : void 0
|
|
1046
1119
|
};
|
|
1047
1120
|
}
|
|
1048
1121
|
async function collectWindowsPower() {
|
|
1122
|
+
const power = {};
|
|
1123
|
+
try {
|
|
1124
|
+
const batRaw = await runPowerShell(
|
|
1125
|
+
`$bat = Get-CimInstance Win32_Battery -ErrorAction SilentlyContinue; if ($bat) { "$($bat.DischargeRate)|$($bat.BatteryStatus)" } else { '|0' }`,
|
|
1126
|
+
""
|
|
1127
|
+
);
|
|
1128
|
+
const [dischargeStr, statusStr] = batRaw.split("|");
|
|
1129
|
+
const dischargeRate = safeParseInt(dischargeStr);
|
|
1130
|
+
const statusCode = safeParseInt(statusStr, 1);
|
|
1131
|
+
if (dischargeRate > 0 && (statusCode === 1 || statusCode === 2)) {
|
|
1132
|
+
power.combinedWatts = Math.round(dischargeRate / 1e3 * 100) / 100;
|
|
1133
|
+
}
|
|
1134
|
+
} catch {
|
|
1135
|
+
}
|
|
1136
|
+
try {
|
|
1137
|
+
const cpu = os2.cpus();
|
|
1138
|
+
const usage = cpu.length > 0 ? Math.round(cpu.reduce((a, c) => a + (c.times.user + c.times.nice + c.times.sys + c.times.irq - c.times.idle) / (c.times.user + c.times.nice + c.times.sys + c.times.idle + c.times.irq), 0) / cpu.length * 100) : 0;
|
|
1139
|
+
const tdpWatts = 65;
|
|
1140
|
+
if (usage > 0) {
|
|
1141
|
+
power.cpuWatts = Math.round(usage / 100 * tdpWatts * 100) / 100;
|
|
1142
|
+
}
|
|
1143
|
+
} catch {
|
|
1144
|
+
}
|
|
1145
|
+
try {
|
|
1146
|
+
const nvRaw = await run("nvidia-smi --query-gpu=power.draw,utilization.gpu --format=csv,noheader,nounits 2>nul", "").catch(() => "");
|
|
1147
|
+
const nvLines = nvRaw.split("\n").filter((l) => l.trim());
|
|
1148
|
+
for (const line of nvLines) {
|
|
1149
|
+
const parts = line.trim().split(",").map((s) => s.trim());
|
|
1150
|
+
const pwr = parseFloat(parts[0]);
|
|
1151
|
+
if (pwr > 0) {
|
|
1152
|
+
power.gpuWatts = Math.round(pwr * 100) / 100;
|
|
1153
|
+
if (!power.combinedWatts) power.combinedWatts = power.gpuWatts;
|
|
1154
|
+
else power.combinedWatts = Math.round((power.combinedWatts + power.gpuWatts) * 100) / 100;
|
|
1155
|
+
break;
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
} catch {
|
|
1159
|
+
}
|
|
1160
|
+
if (power.combinedWatts || power.cpuWatts || power.gpuWatts) {
|
|
1161
|
+
if (!power.combinedWatts) {
|
|
1162
|
+
power.combinedWatts = Math.round(((power.cpuWatts || 0) + (power.gpuWatts || 0)) * 100) / 100;
|
|
1163
|
+
}
|
|
1164
|
+
return power;
|
|
1165
|
+
}
|
|
1049
1166
|
return null;
|
|
1050
1167
|
}
|
|
1051
|
-
async function
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
const
|
|
1055
|
-
if (
|
|
1056
|
-
const
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1168
|
+
async function getNvidiaGpu() {
|
|
1169
|
+
try {
|
|
1170
|
+
const nvRaw = await run("nvidia-smi --query-gpu=gpu_name,memory.total,utilization.gpu,temperature.gpu,power.draw --format=csv,noheader,nounits 2>nul", "");
|
|
1171
|
+
const nvLines = nvRaw.split("\n").filter((l) => l.trim());
|
|
1172
|
+
if (nvLines.length > 0) {
|
|
1173
|
+
const parts = nvLines[0].trim().split(",").map((s) => s.trim());
|
|
1174
|
+
if (parts.length >= 3) {
|
|
1175
|
+
const model = parts[0];
|
|
1176
|
+
const memory = (parseFloat(parts[1]) || 0) * 1024 * 1024;
|
|
1177
|
+
const utilization = Math.min(100, Math.max(0, Math.round(parseFloat(parts[2]) || 0)));
|
|
1178
|
+
const temperature = parts[3] ? parseFloat(parts[3]) : void 0;
|
|
1179
|
+
const powerDraw = parts[4] ? parseFloat(parts[4]) : void 0;
|
|
1180
|
+
return {
|
|
1181
|
+
model,
|
|
1182
|
+
memory,
|
|
1183
|
+
utilization,
|
|
1184
|
+
temperature: Number.isFinite(temperature) ? temperature : void 0,
|
|
1185
|
+
processes: 0,
|
|
1186
|
+
powerDraw: Number.isFinite(powerDraw) ? powerDraw : void 0
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1061
1189
|
}
|
|
1190
|
+
} catch {
|
|
1062
1191
|
}
|
|
1192
|
+
return null;
|
|
1193
|
+
}
|
|
1194
|
+
async function getWmiGpu() {
|
|
1063
1195
|
try {
|
|
1064
|
-
const raw =
|
|
1196
|
+
const raw = await runPowerShell(
|
|
1197
|
+
`Get-CimInstance Win32_VideoController | Select-Object Name, AdapterRAM, DriverVersion, VideoProcessor | ConvertTo-Csv -NoTypeInformation`,
|
|
1198
|
+
""
|
|
1199
|
+
);
|
|
1065
1200
|
const lines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
1066
1201
|
if (lines.length >= 2) {
|
|
1067
|
-
const
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1202
|
+
const headers = lines[0].split(",").map((h) => h.trim().replace(/^"|"$/g, ""));
|
|
1203
|
+
const values = lines[1].split(",").map((v) => v.trim().replace(/^"|"$/g, ""));
|
|
1204
|
+
const row = {};
|
|
1205
|
+
headers.forEach((h, i) => {
|
|
1206
|
+
row[h] = values[i] || "";
|
|
1207
|
+
});
|
|
1208
|
+
const name = row.Name || row.VideoProcessor || "Windows Display Adapter";
|
|
1209
|
+
const ram = safeParseInt(row.AdapterRAM);
|
|
1210
|
+
return {
|
|
1211
|
+
model: name,
|
|
1212
|
+
memory: ram,
|
|
1213
|
+
utilization: 0,
|
|
1214
|
+
processes: 0
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1217
|
+
} catch {
|
|
1218
|
+
}
|
|
1219
|
+
return null;
|
|
1220
|
+
}
|
|
1221
|
+
async function getPerfCounterGpu() {
|
|
1222
|
+
try {
|
|
1223
|
+
const raw = await runPowerShell(
|
|
1224
|
+
`$engines = Get-Counter 'GPU Engine(*)*' -ErrorAction SilentlyContinue; if ($engines) { $engines.CounterSamples | Where-Object { $_.CounterName -match 'Utilization Percentage|GPU Time' -and $_.CookedValue -gt 0 } | ForEach-Object { "$($_.InstanceName)|$($_.CounterName)|$($_.CookedValue)" } }`,
|
|
1225
|
+
""
|
|
1226
|
+
).catch(() => "");
|
|
1227
|
+
if (!raw) return null;
|
|
1228
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
1229
|
+
if (lines.length === 0) return null;
|
|
1230
|
+
const gpuGroups = /* @__PURE__ */ new Map();
|
|
1231
|
+
for (const line of lines) {
|
|
1232
|
+
const [instanceName, counterName, valueStr] = line.split("|");
|
|
1233
|
+
const baseName = instanceName.replace(/\s*\(\d+\)\s*$/, "").trim();
|
|
1234
|
+
if (!gpuGroups.has(baseName)) {
|
|
1235
|
+
gpuGroups.set(baseName, { name: baseName, values: [] });
|
|
1236
|
+
}
|
|
1237
|
+
gpuGroups.get(baseName).values.push(parseFloat(valueStr) || 0);
|
|
1238
|
+
}
|
|
1239
|
+
let bestGpu = null;
|
|
1240
|
+
for (const group of gpuGroups.values()) {
|
|
1241
|
+
const total = group.values.reduce((a, b) => a + b, 0);
|
|
1242
|
+
const util = Math.min(100, Math.round(total));
|
|
1243
|
+
if (!bestGpu || util > bestGpu.util) {
|
|
1244
|
+
bestGpu = { name: group.name, util };
|
|
1074
1245
|
}
|
|
1075
1246
|
}
|
|
1247
|
+
if (bestGpu) {
|
|
1248
|
+
return {
|
|
1249
|
+
model: bestGpu.name,
|
|
1250
|
+
memory: 0,
|
|
1251
|
+
utilization: bestGpu.util,
|
|
1252
|
+
processes: 0
|
|
1253
|
+
};
|
|
1254
|
+
}
|
|
1076
1255
|
} catch {
|
|
1077
1256
|
}
|
|
1078
1257
|
return null;
|
|
1079
1258
|
}
|
|
1259
|
+
async function collectWindowsGpu() {
|
|
1260
|
+
const nv = await getNvidiaGpu();
|
|
1261
|
+
if (nv) return nv;
|
|
1262
|
+
const perf = await getPerfCounterGpu();
|
|
1263
|
+
if (perf) return perf;
|
|
1264
|
+
const wmi = await getWmiGpu();
|
|
1265
|
+
if (wmi) return wmi;
|
|
1266
|
+
return null;
|
|
1267
|
+
}
|
|
1080
1268
|
async function collectWindowsNetwork() {
|
|
1081
1269
|
let iface = "Ethernet";
|
|
1082
1270
|
let ip = "127.0.0.1";
|
|
@@ -1094,7 +1282,15 @@ async function collectWindowsNetwork() {
|
|
|
1094
1282
|
let rxPackets = 0;
|
|
1095
1283
|
let txPackets = 0;
|
|
1096
1284
|
try {
|
|
1097
|
-
const
|
|
1285
|
+
const raw = await runPowerShell(
|
|
1286
|
+
`Get-NetIPConfiguration -AddressFamily IPv4 -ErrorAction SilentlyContinue | Where-Object { $_.NetAdapter.Status -eq 'Up' } | Select-Object -First 1 -ExpandProperty NetAdapter.Name`,
|
|
1287
|
+
""
|
|
1288
|
+
);
|
|
1289
|
+
if (raw) iface = raw;
|
|
1290
|
+
} catch {
|
|
1291
|
+
}
|
|
1292
|
+
try {
|
|
1293
|
+
const netstatE = await run("netstat -e 2>nul", "", 1500);
|
|
1098
1294
|
const lines = netstatE.split("\n");
|
|
1099
1295
|
for (const line of lines) {
|
|
1100
1296
|
const parts = line.trim().split(/\s+/);
|
|
@@ -1141,18 +1337,26 @@ async function collectWindowsNetwork() {
|
|
|
1141
1337
|
async function getWindowsConnectionStates() {
|
|
1142
1338
|
const stats = { established: 0, listening: 0, timeWait: 0, closeWait: 0, other: 0 };
|
|
1143
1339
|
try {
|
|
1144
|
-
const raw =
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1340
|
+
const raw = await runPowerShell(
|
|
1341
|
+
`Get-NetTCPConnection -State Established -ErrorAction SilentlyContinue | Measure-Object | Select-Object -ExpandProperty Count`,
|
|
1342
|
+
"0"
|
|
1343
|
+
);
|
|
1344
|
+
stats.established = safeParseInt(raw, 0);
|
|
1345
|
+
const listenRaw = await runPowerShell(
|
|
1346
|
+
`Get-NetTCPConnection -State Listen -ErrorAction SilentlyContinue | Measure-Object | Select-Object -ExpandProperty Count`,
|
|
1347
|
+
"0"
|
|
1348
|
+
);
|
|
1349
|
+
stats.listening = safeParseInt(listenRaw, 0);
|
|
1350
|
+
const twRaw = await runPowerShell(
|
|
1351
|
+
`Get-NetTCPConnection -State TimeWait -ErrorAction SilentlyContinue | Measure-Object | Select-Object -ExpandProperty Count`,
|
|
1352
|
+
"0"
|
|
1353
|
+
);
|
|
1354
|
+
stats.timeWait = safeParseInt(twRaw, 0);
|
|
1355
|
+
const cwRaw = await runPowerShell(
|
|
1356
|
+
`Get-NetTCPConnection -State CloseWait -ErrorAction SilentlyContinue | Measure-Object | Select-Object -ExpandProperty Count`,
|
|
1357
|
+
"0"
|
|
1358
|
+
);
|
|
1359
|
+
stats.closeWait = safeParseInt(cwRaw, 0);
|
|
1156
1360
|
} catch {
|
|
1157
1361
|
}
|
|
1158
1362
|
return stats;
|
|
@@ -1163,7 +1367,7 @@ async function collectWindowsPackets() {
|
|
|
1163
1367
|
let rxPackets = 0;
|
|
1164
1368
|
let txPackets = 0;
|
|
1165
1369
|
try {
|
|
1166
|
-
const netstatE =
|
|
1370
|
+
const netstatE = await run("netstat -e 2>nul", "", 1500);
|
|
1167
1371
|
const lines = netstatE.split("\n");
|
|
1168
1372
|
for (const line of lines) {
|
|
1169
1373
|
const parts = line.trim().split(/\s+/);
|
|
@@ -1192,40 +1396,57 @@ async function collectWindowsPackets() {
|
|
|
1192
1396
|
listeningPorts: []
|
|
1193
1397
|
};
|
|
1194
1398
|
}
|
|
1399
|
+
var winProcCache = /* @__PURE__ */ new Map();
|
|
1195
1400
|
async function collectWindowsProcesses(limit) {
|
|
1196
1401
|
try {
|
|
1197
|
-
const raw =
|
|
1198
|
-
|
|
1199
|
-
|
|
1402
|
+
const raw = await runPowerShell(
|
|
1403
|
+
`Get-CimInstance Win32_Process | Select-Object Name, ProcessId, ThreadCount, WorkingSetSize, UserModeTime, KernelModeTime | ConvertTo-Csv -NoTypeInformation`,
|
|
1404
|
+
""
|
|
1405
|
+
);
|
|
1406
|
+
const lines = raw.split("\n").filter((l) => l.trim().length > 0 && !l.startsWith('"Name"'));
|
|
1407
|
+
const now = Date.now();
|
|
1200
1408
|
const procs = [];
|
|
1201
1409
|
const totalMem = os2.totalmem();
|
|
1202
1410
|
for (const line of lines) {
|
|
1203
|
-
const parts = line.split(
|
|
1204
|
-
if (parts.length
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
isGpuOrMlAttributed: isGpuOrMl
|
|
1224
|
-
});
|
|
1411
|
+
const parts = line.split(",").map((s) => s.trim().replace(/^"|"$/g, ""));
|
|
1412
|
+
if (parts.length < 6) continue;
|
|
1413
|
+
const name = parts[0];
|
|
1414
|
+
const pid = safeParseInt(parts[1], 0);
|
|
1415
|
+
const threads = safeParseInt(parts[2], 0);
|
|
1416
|
+
const memBytes = safeParseInt(parts[3], 0);
|
|
1417
|
+
const userTime = safeParseInt(parts[4], 0);
|
|
1418
|
+
const kernelTime = safeParseInt(parts[5], 0);
|
|
1419
|
+
if (!pid || pid <= 0) continue;
|
|
1420
|
+
const totalTime = userTime + kernelTime;
|
|
1421
|
+
let cpu = 0;
|
|
1422
|
+
const prev = winProcCache.get(pid);
|
|
1423
|
+
if (prev) {
|
|
1424
|
+
const dt = now - prev.ts;
|
|
1425
|
+
if (dt > 0) {
|
|
1426
|
+
const timeDelta = totalTime - (prev.user + prev.kernel);
|
|
1427
|
+
if (timeDelta > 0) {
|
|
1428
|
+
const cpuSeconds = timeDelta / 1e7;
|
|
1429
|
+
cpu = Math.min(100, Math.round(cpuSeconds / dt * 1e4) / 100);
|
|
1430
|
+
}
|
|
1225
1431
|
}
|
|
1226
1432
|
}
|
|
1433
|
+
winProcCache.set(pid, { user: userTime, kernel: kernelTime, ts: now });
|
|
1434
|
+
const memPct = totalMem > 0 ? Math.round(memBytes / totalMem * 1e3) / 10 : 0;
|
|
1435
|
+
const isGpuOrMl = /gpu|vulkan|cuda|ollama|llama|torch|python|nvidia|amd|radeon|intel.*graphics/i.test(name);
|
|
1436
|
+
procs.push({
|
|
1437
|
+
pid,
|
|
1438
|
+
ppid: 0,
|
|
1439
|
+
user: "SYSTEM",
|
|
1440
|
+
cpu,
|
|
1441
|
+
mem: memPct,
|
|
1442
|
+
state: "R",
|
|
1443
|
+
threads,
|
|
1444
|
+
runtime: 0,
|
|
1445
|
+
command: name,
|
|
1446
|
+
isGpuOrMlAttributed: isGpuOrMl
|
|
1447
|
+
});
|
|
1227
1448
|
}
|
|
1228
|
-
procs.sort((a, b) => b.mem - a.mem);
|
|
1449
|
+
procs.sort((a, b) => b.cpu - a.cpu || b.mem - a.mem);
|
|
1229
1450
|
if (limit && limit > 0) return procs.slice(0, limit);
|
|
1230
1451
|
return procs;
|
|
1231
1452
|
} catch {
|
|
@@ -1245,7 +1466,26 @@ async function collectWindowsTasks(limit = 12) {
|
|
|
1245
1466
|
}));
|
|
1246
1467
|
}
|
|
1247
1468
|
async function getWindowsDisplayInfo() {
|
|
1248
|
-
|
|
1469
|
+
try {
|
|
1470
|
+
const raw = await runPowerShell(
|
|
1471
|
+
`Get-CimInstance Win32_VideoController | ForEach-Object { "$($_.Name)|$($_.VideoModeDescription)|$($_.VideoProcessor)" }`,
|
|
1472
|
+
""
|
|
1473
|
+
);
|
|
1474
|
+
const displays = [];
|
|
1475
|
+
for (const line of raw.split("\n")) {
|
|
1476
|
+
const parts = line.trim().split("|");
|
|
1477
|
+
if (parts[0]) {
|
|
1478
|
+
displays.push({
|
|
1479
|
+
name: parts[0],
|
|
1480
|
+
resolution: parts[1] || "Unknown",
|
|
1481
|
+
isMain: displays.length === 0
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
return displays.length > 0 ? displays : [{ name: "Default Display", resolution: "1920x1080", isMain: true }];
|
|
1486
|
+
} catch {
|
|
1487
|
+
return [{ name: "Default Display", resolution: "1920x1080", isMain: true }];
|
|
1488
|
+
}
|
|
1249
1489
|
}
|
|
1250
1490
|
|
|
1251
1491
|
// src/monitors/batteryPredictor.ts
|