pyre-cli 10.1.0 → 10.2.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.
Files changed (46) hide show
  1. package/README.md +195 -494
  2. package/dist/{checkCmd-RKW6QGDR.js → checkCmd-WQAHAUWO.js} +1 -1
  3. package/dist/{chunk-GHRDW646.js → chunk-23YWCI3O.js} +375 -135
  4. package/dist/{service-Q74BBILO.js → chunk-KFPTD266.js} +43 -3
  5. package/dist/{doctor-GYHFTNZQ.js → chunk-KVUMOA6C.js} +7 -6
  6. package/dist/{chunk-53O2RGSL.js → chunk-QM5L43EK.js} +19 -2
  7. package/dist/{chunk-6UHZP4QP.js → chunk-XTOOEKLM.js} +24 -12
  8. package/dist/{collectors-H43PN4PS.js → collectors-JDN5Y7EV.js} +1 -1
  9. package/dist/{completions-JKYEJNGR.js → completions-ZVTNE52B.js} +15 -11
  10. package/dist/{config-GYGJXU2Q.js → config-J4YMADEF.js} +3 -1
  11. package/dist/doctor-LHFW4UJP.js +10 -0
  12. package/dist/firebaseSync-ON3RZ6KX.js +91 -0
  13. package/dist/index.js +4243 -735
  14. package/dist/monitors/platform/windows-ui.ps1 +42 -0
  15. package/dist/{netUsageCmd-WKBTW2YG.js → netUsageCmd-67IBZZYN.js} +2 -2
  16. package/dist/{pipeCmd-7REDBLBU.js → pipeCmd-2N4JPKYE.js} +1 -1
  17. package/dist/{prometheusCmd-PL64BLN5.js → prometheusCmd-LEUM5WCZ.js} +1 -1
  18. package/dist/serveCmd-L7LVJOZN.js +353 -0
  19. package/dist/service-7GIX3YNC.js +16 -0
  20. package/dist/syncCmd-42IRF633.js +246 -0
  21. package/dist/{topoCmd-DU2HHZNH.js → topoCmd-DZ3YS5IL.js} +1 -1
  22. package/dist/{watchdogCmd-UF53JPZB.js → watchdogCmd-6GR4UN6B.js} +7 -7
  23. package/dist/{wizard-TD7BGX6O.js → wizard-JQMQWFAW.js} +1 -1
  24. package/package.json +4 -3
  25. package/parity.md +2 -0
  26. package/src/completions.ts +15 -11
  27. package/src/easy_features.test.ts +149 -0
  28. package/src/firebaseSync.ts +157 -0
  29. package/src/formatters/html_template.ts +3086 -598
  30. package/src/formatters/render.ts +25 -12
  31. package/src/index.ts +516 -23
  32. package/src/live/input.ts +501 -148
  33. package/src/live/render.ts +163 -18
  34. package/src/live/state.ts +52 -2
  35. package/src/live/types.ts +1 -1
  36. package/src/monitors/collectors.ts +3 -10
  37. package/src/monitors/platform/windows-ui.ps1 +42 -0
  38. package/src/monitors/platform/windows.ts +442 -147
  39. package/src/monitors/types.ts +8 -0
  40. package/src/serveCmd.ts +17 -2
  41. package/src/service.ts +49 -0
  42. package/src/state/config.ts +48 -0
  43. package/src/syncCmd.ts +159 -11
  44. package/src/web/dashboard.ts +205 -0
  45. package/dist/serveCmd-2H5VLF3P.js +0 -153
  46. 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
- const brand = cpus[0]?.model || "Windows CPU";
846
- const frequency = cpus[0]?.speed || 0;
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
- if (prevCpuTimesRef.current && prevCpuTimesRef.current.length === current.length) {
882
+ const prevCpuTimes2 = prevCpuTimesRef.current;
883
+ if (prevCpuTimes2 && prevCpuTimes2.length === current.length) {
854
884
  coreUsage = current.map((c, i) => {
855
- const prev = prevCpuTimesRef.current[i];
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 rawWmi = (await run("wmic /namespace:\\\\root\\wmi PATH MSAcpi_ThermalZoneTemperature get CurrentTemperature 2>nul", "", 1500)).trim();
871
- const match = rawWmi.match(/(\d{3,4})/);
872
- if (match) {
873
- const kelvinTenths = parseInt(match[1], 10);
874
- const c = Math.round((kelvinTenths - 2732) / 10 * 10) / 10;
875
- if (c >= 15 && c <= 115) temperature = c;
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
- temperature = Math.round((38 + usage * 0.42) * 10) / 10;
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: cores,
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 = (await run("wmic pagefile get AllocatedBaseSize,CurrentUsage 2>nul", "", 1500)).trim();
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
- if (lines.length >= 2) {
905
- const parts = lines[1].trim().split(/\s+/);
906
- if (parts.length >= 2) {
907
- const allocMb = parseInt(parts[0], 10) || 0;
908
- const usedMb = parseInt(parts[1], 10) || 0;
909
- swapTotal = allocMb * 1024 * 1024;
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 = (await run("wmic logicaldisk get Caption,FileSystem,FreeSpace,Size 2>nul", "", 2500)).trim();
934
- const lines = raw.split("\n").slice(1);
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 lines) {
937
- const parts = line.trim().split(/\s+/);
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 freeBytes = parseInt(parts[2], 10) || 0;
941
- const totalBytes = parseInt(parts[3], 10) || 0;
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 = (await run("wmic path win32_battery get BatteryStatus,EstimatedChargeRemaining,EstimatedRunTime 2>nul", "", 2e3)).trim();
983
- const lines = raw.split("\n").filter((l) => l.trim().length > 0);
984
- if (lines.length >= 2) {
985
- const parts = lines[1].trim().split(/\s+/);
986
- if (parts.length >= 2) {
987
- const statusCode = parseInt(parts[0], 10) || 1;
988
- const level = parseInt(parts[1], 10) || 0;
989
- const runTimeMinutes = parseInt(parts[2], 10);
990
- const powerSource = statusCode === 2 || statusCode === 3 || statusCode === 6 ? "AC" : "Battery";
991
- const state = statusCode === 3 ? "charged" : statusCode === 6 || statusCode === 2 ? "charging" : "discharging";
992
- let timeRemaining = "calculating";
993
- if (state === "charged") timeRemaining = "\u221E";
994
- else if (runTimeMinutes && runTimeMinutes > 0 && runTimeMinutes < 7e4) {
995
- const h = Math.floor(runTimeMinutes / 60);
996
- const m = runTimeMinutes % 60;
997
- timeRemaining = `${h}:${m.toString().padStart(2, "0")}`;
998
- }
999
- return {
1000
- level,
1001
- state,
1002
- timeRemaining,
1003
- health: "Good",
1004
- powerSource
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 rawWmi = (await run("wmic /namespace:\\\\root\\wmi PATH MSAcpi_ThermalZoneTemperature get CurrentTemperature 2>nul", "", 1500)).trim();
1016
- const match = rawWmi.match(/(\d{3,4})/);
1017
- if (match) {
1018
- const kelvinTenths = parseInt(match[1], 10);
1019
- const c = Math.round((kelvinTenths - 2732) / 10 * 10) / 10;
1020
- if (c >= 15 && c <= 115) tempC = c;
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
- const cpus = os2.cpus();
1026
- const loadUsage = os2.loadavg()[0] || 15;
1027
- tempC = Math.round((38 + loadUsage * 0.42) * 10) / 10;
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 (tempC >= 100) {
1032
- state = "Critical";
1033
- pressureLevel = 3;
1034
- } else if (tempC >= 90) {
1035
- state = "Serious";
1036
- pressureLevel = 2;
1037
- } else if (tempC >= 80) {
1038
- state = "Fair";
1039
- pressureLevel = 1;
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} (${tempC}\xB0C)`,
1116
+ detail: cpuTemp !== void 0 ? `${state} (${cpuTemp}\xB0C)` : state,
1044
1117
  pressureLevel,
1045
- temperatures: { cpu: tempC }
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 collectWindowsGpu() {
1052
- const nvRaw = (await run("nvidia-smi --query-gpu=gpu_name,memory.total,utilization.gpu,temperature.gpu --format=csv,noheader,nounits 2>nul", "")).trim();
1053
- if (nvRaw) {
1054
- const parts = nvRaw.split(",").map((s) => s.trim());
1055
- if (parts.length >= 3) {
1056
- const model = parts[0];
1057
- const memory = (parseFloat(parts[1]) || 0) * 1024 * 1024;
1058
- const utilization = Math.min(100, Math.max(0, Math.round(parseFloat(parts[2]) || 0)));
1059
- const temperature = parts[3] ? parseFloat(parts[3]) : void 0;
1060
- return { model, memory, utilization, temperature, processes: 0 };
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 = (await run("wmic path win32_VideoController get Name,AdapterRAM 2>nul", "", 2e3)).trim();
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 parts = lines[1].trim().split(/\s{2,}/);
1068
- if (parts.length >= 2) {
1069
- const ram = parseInt(parts[0], 10) || 0;
1070
- const name = parts[1] || "Windows Display Adapter";
1071
- return { model: name, memory: ram, utilization: 0, processes: 0 };
1072
- } else if (parts.length === 1) {
1073
- return { model: parts[0], memory: 0, utilization: 0, processes: 0 };
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 netstatE = (await run("netstat -e 2>nul", "", 1500)).trim();
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 = (await run("netstat -ano 2>nul", "", 2e3)).trim();
1145
- for (const line of raw.split("\n")) {
1146
- const parts = line.trim().split(/\s+/);
1147
- if (parts.length >= 4 && parts[0].toUpperCase() === "TCP") {
1148
- const state = parts[3].toUpperCase();
1149
- if (state.includes("ESTABLISHED")) stats.established++;
1150
- else if (state.includes("LISTENING")) stats.listening++;
1151
- else if (state.includes("TIME_WAIT")) stats.timeWait++;
1152
- else if (state.includes("CLOSE_WAIT")) stats.closeWait++;
1153
- else stats.other++;
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 = (await run("netstat -e 2>nul", "", 1500)).trim();
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 = (await run("tasklist /fo csv /nh 2>nul", "", 3e3)).trim();
1198
- if (!raw) return [];
1199
- const lines = raw.split("\n");
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('","').map((s) => s.replace(/(^"|"$)/g, "").trim());
1204
- if (parts.length >= 5) {
1205
- const name = parts[0];
1206
- const pid = parseInt(parts[1], 10);
1207
- const memStr = parts[4].replace(/[^\d]/g, "");
1208
- const memKb = parseInt(memStr, 10) || 0;
1209
- const memBytes = memKb * 1024;
1210
- const memPct = totalMem > 0 ? Math.round(memBytes / totalMem * 100 * 10) / 10 : 0;
1211
- const isGpuOrMl = /gpu|vulkan|cuda|ollama|llama|torch|python/i.test(name);
1212
- if (pid > 0) {
1213
- procs.push({
1214
- pid,
1215
- ppid: 0,
1216
- user: parts[2] || "SYSTEM",
1217
- cpu: 0,
1218
- mem: memPct,
1219
- state: "R",
1220
- threads: 0,
1221
- runtime: 0,
1222
- command: name,
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
- return [{ name: "Default Display", resolution: "1920x1080", isMain: true }];
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