pyre-cli 9.5.0 → 9.5.2

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.
@@ -1,3 +1,6 @@
1
+ import {
2
+ collectBlenderRenders
3
+ } from "./chunk-25I533OF.js";
1
4
  import {
2
5
  run
3
6
  } from "./chunk-FH5WDJBF.js";
@@ -58,6 +61,25 @@ async function getSmcMetrics() {
58
61
  } catch {
59
62
  }
60
63
  }
64
+ if (result.temps["cpu_die"] === void 0) {
65
+ try {
66
+ const battRaw = await run("ioreg -r -n AppleSmartBattery 2>/dev/null", "");
67
+ if (battRaw) {
68
+ const virtMatch = battRaw.match(/"VirtualTemperature"\s*=\s*(\d+)/);
69
+ const cellMatch = battRaw.match(/"Temperature"\s*=\s*(\d+)/);
70
+ const rawVirt = virtMatch ? parseFloat(virtMatch[1]) : void 0;
71
+ const rawCell = cellMatch ? parseFloat(cellMatch[1]) : void 0;
72
+ const virtC = rawVirt !== void 0 ? Math.round(rawVirt / 10) / 10 : void 0;
73
+ const cellC = rawCell !== void 0 ? Math.round(rawCell / 10) / 10 : void 0;
74
+ if (virtC !== void 0 && virtC >= 15 && virtC <= 115) {
75
+ result.temps["cpu_die"] = virtC;
76
+ } else if (cellC !== void 0 && cellC >= 15 && cellC <= 115) {
77
+ result.temps["cpu_die"] = cellC;
78
+ }
79
+ }
80
+ } catch {
81
+ }
82
+ }
61
83
  if (result.temps["cpu_die"] === void 0) {
62
84
  try {
63
85
  const tz0 = (await run("cat /sys/class/thermal/thermal_zone0/temp 2>/dev/null", "")).trim();
@@ -91,6 +113,43 @@ function parseSuffix(s) {
91
113
  return s.toUpperCase() === "K" ? 1024 : s.toUpperCase() === "M" ? 1024 * 1024 : s.toUpperCase() === "G" ? 1024 * 1024 * 1024 : 1;
92
114
  }
93
115
 
116
+ // src/monitors/dns_cache.ts
117
+ import dns from "dns";
118
+ var cache = /* @__PURE__ */ new Map();
119
+ var pending = /* @__PURE__ */ new Set();
120
+ function resolveIp(ip) {
121
+ let cleanIp = ip;
122
+ if (cleanIp.includes("]:")) {
123
+ cleanIp = cleanIp.substring(1, cleanIp.indexOf("]"));
124
+ } else if (cleanIp.includes(":") && cleanIp.split(":").length === 2) {
125
+ cleanIp = cleanIp.split(":")[0];
126
+ } else if (cleanIp.includes(":") && !cleanIp.includes("]")) {
127
+ const lastColon = cleanIp.lastIndexOf(":");
128
+ const isPort = !cleanIp.includes(":", cleanIp.indexOf(":") + 1);
129
+ }
130
+ if (cleanIp.includes(":") && cleanIp.includes(".")) {
131
+ const lastDot = cleanIp.lastIndexOf(".");
132
+ if (lastDot > cleanIp.lastIndexOf(":")) {
133
+ cleanIp = cleanIp.substring(0, lastDot);
134
+ }
135
+ }
136
+ if (cache.has(cleanIp)) return cache.get(cleanIp);
137
+ if (pending.has(cleanIp)) return ip;
138
+ if (cleanIp.startsWith("127.") || cleanIp.startsWith("192.168.") || cleanIp.startsWith("10.") || cleanIp.startsWith("172.16.") || cleanIp.startsWith("ff02:") || cleanIp === "*:*" || cleanIp === "*" || cleanIp.startsWith("fe80:")) {
139
+ return ip;
140
+ }
141
+ pending.add(cleanIp);
142
+ dns.reverse(cleanIp, (err, hostnames) => {
143
+ if (!err && hostnames && hostnames.length > 0) {
144
+ cache.set(cleanIp, hostnames[0]);
145
+ } else {
146
+ cache.set(cleanIp, cleanIp);
147
+ }
148
+ pending.delete(cleanIp);
149
+ });
150
+ return ip;
151
+ }
152
+
94
153
  // src/monitors/collectors.ts
95
154
  var SP_TTL_MS = 1e4;
96
155
  var NETSTAT_TTL_MS = 1e3;
@@ -103,6 +162,7 @@ var spBatteryCache = null;
103
162
  var netstatIbCache = null;
104
163
  var routeCache = null;
105
164
  var sysctlCache = /* @__PURE__ */ new Map();
165
+ var prevNetSample = null;
106
166
  async function cachedSysctl(key, fallback) {
107
167
  const now = Date.now();
108
168
  const hit = sysctlCache.get(key);
@@ -166,7 +226,7 @@ function estimateBatteryLife(level, state) {
166
226
  return { estimatedTimeToEmpty, dischargeRatePerHour: Math.round(dischargeRate * 10) / 10, powerWatts };
167
227
  }
168
228
  async function collectAll(opts = {}) {
169
- const [cpu, gpu, memory, disk, battery, thermal, network, processes, system, power, packets, tasks] = await Promise.all([
229
+ const [cpu, gpu, memory, disk, battery, thermal, network, processes, system, power, packets, tasks, containers, blenderRenders] = await Promise.all([
170
230
  collectCpu(),
171
231
  collectGpu(opts.detailed),
172
232
  collectMemory(),
@@ -178,7 +238,9 @@ async function collectAll(opts = {}) {
178
238
  collectSystem(),
179
239
  collectPower(),
180
240
  collectPackets(),
181
- collectTasks()
241
+ collectTasks(),
242
+ collectContainers(),
243
+ collectBlenderRenders()
182
244
  ]);
183
245
  return {
184
246
  header: {
@@ -198,9 +260,32 @@ async function collectAll(opts = {}) {
198
260
  power,
199
261
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
200
262
  packets,
201
- tasks
263
+ tasks,
264
+ containers,
265
+ blenderRenders
202
266
  };
203
267
  }
268
+ async function collectContainers() {
269
+ try {
270
+ const raw = (await run('docker stats --no-stream --format "{{.ID}}|{{.Name}}|{{.CPUPerc}}|{{.MemUsage}}|{{.MemPerc}}|{{.NetIO}}|{{.BlockIO}}|{{.PIDs}}" 2>/dev/null', "")).trim();
271
+ if (!raw) return [];
272
+ return raw.split("\n").map((line) => {
273
+ const parts = line.split("|");
274
+ return {
275
+ id: parts[0],
276
+ name: parts[1],
277
+ cpuPercent: parseFloat(parts[2].replace("%", "")) || 0,
278
+ memUsage: parts[3],
279
+ memPercent: parseFloat(parts[4].replace("%", "")) || 0,
280
+ netIO: parts[5],
281
+ blockIO: parts[6],
282
+ pids: parseInt(parts[7], 10) || 0
283
+ };
284
+ });
285
+ } catch {
286
+ return [];
287
+ }
288
+ }
204
289
  async function collectPower() {
205
290
  try {
206
291
  const { power } = await getSmcMetrics();
@@ -730,9 +815,148 @@ async function collectNetwork() {
730
815
  }
731
816
  }
732
817
  }
733
- return { interface: iface, ip, rxBytes, txBytes, rxPackets, txPackets };
818
+ const now = Date.now();
819
+ let rxRate = 0;
820
+ let txRate = 0;
821
+ if (prevNetSample && prevNetSample.ts > 0) {
822
+ const dt = Math.max((now - prevNetSample.ts) / 1e3, 1e-3);
823
+ rxRate = Math.max(0, (rxBytes - prevNetSample.rxBytes) / dt);
824
+ txRate = Math.max(0, (txBytes - prevNetSample.txBytes) / dt);
825
+ }
826
+ prevNetSample = { rxBytes, txBytes, rxPackets, txPackets, ts: now };
827
+ const protocolStats = await getProtocolStats();
828
+ const connectionStates = await getConnectionStates();
829
+ const listeningPorts = await getListeningPorts();
830
+ const establishedConnections = connectionStates.established;
831
+ const topRemoteHosts = await getTopRemoteHosts();
832
+ return {
833
+ interface: iface,
834
+ ip,
835
+ rxBytes,
836
+ txBytes,
837
+ rxPackets,
838
+ txPackets,
839
+ rxRate,
840
+ txRate,
841
+ connections: establishedConnections,
842
+ protocols: protocolStats,
843
+ connectionStates,
844
+ listeningPorts,
845
+ establishedConnections,
846
+ topRemoteHosts
847
+ };
734
848
  } catch {
735
- return { interface: "unknown", ip: "0.0.0.0", rxBytes: 0, txBytes: 0, rxPackets: 0, txPackets: 0 };
849
+ return {
850
+ interface: "unknown",
851
+ ip: "0.0.0.0",
852
+ rxBytes: 0,
853
+ txBytes: 0,
854
+ rxPackets: 0,
855
+ txPackets: 0,
856
+ rxRate: 0,
857
+ txRate: 0,
858
+ connections: 0,
859
+ protocols: { tcp: { connections: 0, rxBytes: 0, txBytes: 0 }, udp: { connections: 0, rxBytes: 0, txBytes: 0 } },
860
+ connectionStates: { established: 0, listening: 0, timeWait: 0, closeWait: 0, other: 0 },
861
+ listeningPorts: [],
862
+ establishedConnections: 0,
863
+ topRemoteHosts: []
864
+ };
865
+ }
866
+ }
867
+ async function getProtocolStats() {
868
+ try {
869
+ const raw = await run("netstat -n -p tcp 2>/dev/null | tail -n +3 | awk '{print $1}' | sort | uniq -c", "");
870
+ const tcpConnections = (await run("netstat -n -p tcp 2>/dev/null | tail -n +3 | wc -l", "0")).trim();
871
+ const udpConnections = (await run("netstat -n -p udp 2>/dev/null | tail -n +3 | wc -l", "0")).trim();
872
+ let tcpRxBytes = 0;
873
+ let tcpTxBytes = 0;
874
+ let udpRxBytes = 0;
875
+ let udpTxBytes = 0;
876
+ try {
877
+ const netstat = await cachedNetstatIb();
878
+ for (const line of netstat.split("\n")) {
879
+ const parts = line.split(/\s+/);
880
+ if (parts.length >= 9 && parts[0] && !parts[0].startsWith("Name")) {
881
+ const iface = parts[0];
882
+ if (iface.includes("lo")) continue;
883
+ const rx = parseInt(parts[5]) || 0;
884
+ const tx = parseInt(parts[7]) || 0;
885
+ tcpRxBytes += rx;
886
+ tcpTxBytes += tx;
887
+ }
888
+ }
889
+ } catch {
890
+ }
891
+ return {
892
+ tcp: {
893
+ connections: parseInt(tcpConnections) || 0,
894
+ rxBytes: tcpRxBytes,
895
+ txBytes: tcpTxBytes
896
+ },
897
+ udp: {
898
+ connections: parseInt(udpConnections) || 0,
899
+ rxBytes: udpRxBytes,
900
+ txBytes: udpTxBytes
901
+ }
902
+ };
903
+ } catch {
904
+ return { tcp: { connections: 0, rxBytes: 0, txBytes: 0 }, udp: { connections: 0, rxBytes: 0, txBytes: 0 } };
905
+ }
906
+ }
907
+ async function getConnectionStates() {
908
+ try {
909
+ const raw = await run("netstat -n -p tcp 2>/dev/null | tail -n +3 | awk '{print $6}' | sort | uniq -c", "");
910
+ const lines = raw.split("\n").filter(Boolean);
911
+ const stats = { established: 0, listening: 0, timeWait: 0, closeWait: 0, other: 0 };
912
+ for (const line of lines) {
913
+ const match = line.match(/(\d+)\s+(\S+)/);
914
+ if (!match) continue;
915
+ const count = parseInt(match[1]);
916
+ const state = match[2].toUpperCase();
917
+ if (state.includes("ESTABLISHED")) stats.established += count;
918
+ else if (state.includes("LISTEN")) stats.listening += count;
919
+ else if (state.includes("TIME_WAIT")) stats.timeWait += count;
920
+ else if (state.includes("CLOSE_WAIT")) stats.closeWait += count;
921
+ else stats.other += count;
922
+ }
923
+ return stats;
924
+ } catch {
925
+ return { established: 0, listening: 0, timeWait: 0, closeWait: 0, other: 0 };
926
+ }
927
+ }
928
+ async function getListeningPorts() {
929
+ try {
930
+ const raw = await run("netstat -n -p tcp 2>/dev/null | grep LISTEN | awk '{print $4}' | rev | cut -d. -f1 | rev", "");
931
+ const ports = raw.split("\n").map((p) => parseInt(p, 10)).filter((p) => !isNaN(p) && p > 0);
932
+ return [...new Set(ports)].sort((a, b) => a - b);
933
+ } catch {
934
+ return [];
935
+ }
936
+ }
937
+ async function getTopRemoteHosts() {
938
+ try {
939
+ const raw = await run("netstat -n -p tcp 2>/dev/null | tail -n +3 | grep ESTABLISHED | awk '{print $5}' | rev | cut -d. -f1 | rev", "");
940
+ const ports = raw.split("\n").filter(Boolean);
941
+ const hostMap = /* @__PURE__ */ new Map();
942
+ for (const port of ports) {
943
+ const host = port.split(":")[0];
944
+ if (!host || host === "127.0.0.1" || host === "::1") continue;
945
+ const existing = hostMap.get(host) || { count: 0, bytes: 0 };
946
+ hostMap.set(host, { count: existing.count + 1, bytes: existing.bytes });
947
+ }
948
+ const result = [];
949
+ for (const [host, info] of hostMap.entries()) {
950
+ result.push({
951
+ host,
952
+ ip: host,
953
+ bytesTransferred: info.bytes,
954
+ connectionCount: info.count
955
+ });
956
+ }
957
+ return result.sort((a, b) => b.connectionCount - a.connectionCount).slice(0, 10);
958
+ } catch {
959
+ return [];
736
960
  }
737
961
  }
738
962
  async function collectProcesses(limit) {
@@ -781,29 +1005,67 @@ async function collectPackets() {
781
1005
  }
782
1006
  }
783
1007
  let connections = 0;
784
- try {
785
- const tcpConns = (await run("netstat -an | grep ESTABLISHED | wc -l", "0", 2e3)).trim();
786
- connections = parseInt(tcpConns) || 0;
787
- } catch {
788
- connections = 0;
789
- }
790
- let topProcesses = [];
791
1008
  let allProcesses = [];
792
1009
  try {
793
- const lsof = (await run("lsof -i -n -P 2>/dev/null | tail -n +2 | awk '{print $1, $2}' | sort | uniq -c | sort -rn", "", 2e3)).trim();
794
- const lsofLines = lsof.split("\n");
795
- topProcesses = lsofLines.map((line) => {
796
- const parts = line.trim().split(/\s+/);
797
- if (parts.length >= 3) {
798
- return { pid: parseInt(parts[2]) || 0, command: parts[1], rxBytes: 0, txBytes: parseInt(parts[0]) || 0 };
1010
+ const nettop = (await run("nettop -L 1 -J bytes_in,bytes_out,state 2>/dev/null", "", 2e3)).trim();
1011
+ const rows = nettop.split("\n");
1012
+ let currentProc = null;
1013
+ for (const row of rows) {
1014
+ if (!row || row.startsWith(",state,")) continue;
1015
+ const cols = row.split(",");
1016
+ const nameCol = cols[0];
1017
+ if (!nameCol.includes("<->")) {
1018
+ if (currentProc) {
1019
+ if (currentProc.connections && currentProc.connections.length > 0) {
1020
+ allProcesses.push(currentProc);
1021
+ }
1022
+ }
1023
+ const lastDot = nameCol.lastIndexOf(".");
1024
+ let pid = 0;
1025
+ let command = nameCol;
1026
+ if (lastDot > 0) {
1027
+ const possiblePid = parseInt(nameCol.substring(lastDot + 1));
1028
+ if (!isNaN(possiblePid)) {
1029
+ pid = possiblePid;
1030
+ command = nameCol.substring(0, lastDot);
1031
+ }
1032
+ }
1033
+ const rx = parseInt(cols[2]) || 0;
1034
+ const tx = parseInt(cols[3]) || 0;
1035
+ currentProc = { pid, command, rxBytes: rx, txBytes: tx, connections: [] };
1036
+ } else {
1037
+ if (currentProc) {
1038
+ const state = cols[1];
1039
+ const rx = parseInt(cols[2]) || 0;
1040
+ const tx = parseInt(cols[3]) || 0;
1041
+ if (state === "Established") connections++;
1042
+ const spaceIdx = nameCol.indexOf(" ");
1043
+ const protocol = spaceIdx > 0 ? nameCol.substring(0, spaceIdx) : "";
1044
+ const endpoints = spaceIdx > 0 ? nameCol.substring(spaceIdx + 1) : nameCol;
1045
+ const arrowIdx = endpoints.indexOf("<->");
1046
+ let local = "";
1047
+ let remote = "";
1048
+ if (arrowIdx > 0) {
1049
+ local = endpoints.substring(0, arrowIdx);
1050
+ remote = endpoints.substring(arrowIdx + 3);
1051
+ remote = resolveIp(remote);
1052
+ } else {
1053
+ local = endpoints;
1054
+ }
1055
+ currentProc.connections.push({ protocol, local, remote, state, rxBytes: rx, txBytes: tx });
1056
+ }
799
1057
  }
800
- return { pid: 0, command: "", rxBytes: 0, txBytes: 0 };
801
- }).filter((p) => p.pid > 0);
802
- allProcesses = topProcesses;
1058
+ }
1059
+ if (currentProc && currentProc.connections && currentProc.connections.length > 0) {
1060
+ allProcesses.push(currentProc);
1061
+ }
803
1062
  } catch {
804
- topProcesses = [];
805
1063
  allProcesses = [];
806
1064
  }
1065
+ const topProcesses = [...allProcesses].sort((a, b) => b.rxBytes + b.txBytes - (a.rxBytes + a.txBytes));
1066
+ const protocolStats = await getProtocolStats();
1067
+ const connectionStates = await getConnectionStates();
1068
+ const listeningPorts = await getListeningPorts();
807
1069
  return {
808
1070
  totalPackets: totalRxPackets + totalTxPackets,
809
1071
  rxPackets: totalRxPackets,
@@ -813,7 +1075,10 @@ async function collectPackets() {
813
1075
  connections,
814
1076
  topProcesses,
815
1077
  interfaces: ifaceStats,
816
- allProcesses
1078
+ allProcesses,
1079
+ protocolStats,
1080
+ connectionStates,
1081
+ listeningPorts
817
1082
  };
818
1083
  } catch {
819
1084
  return null;
@@ -902,6 +1167,7 @@ async function getTimeMachineStatus() {
902
1167
 
903
1168
  export {
904
1169
  collectAll,
1170
+ collectContainers,
905
1171
  collectPower,
906
1172
  collectGpu,
907
1173
  collectCpu,
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  collectAll,
3
3
  collectBattery,
4
+ collectContainers,
4
5
  collectCpu,
5
6
  collectDisk,
6
7
  collectGpu,
@@ -13,11 +14,13 @@ import {
13
14
  collectThermal,
14
15
  getDisplayInfo,
15
16
  getTimeMachineStatus
16
- } from "./chunk-PV4MXTAJ.js";
17
+ } from "./chunk-N4WJVF5M.js";
18
+ import "./chunk-25I533OF.js";
17
19
  import "./chunk-FH5WDJBF.js";
18
20
  export {
19
21
  collectAll,
20
22
  collectBattery,
23
+ collectContainers,
21
24
  collectCpu,
22
25
  collectDisk,
23
26
  collectGpu,
@@ -8,7 +8,7 @@ import {
8
8
  readConfig,
9
9
  saveProfile,
10
10
  writeConfig
11
- } from "./chunk-RVGKEJXT.js";
11
+ } from "./chunk-CXL4GYGQ.js";
12
12
  export {
13
13
  CONFIG_FILE,
14
14
  DEFAULT_CONFIG,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  CONFIG_FILE,
3
3
  readConfig
4
- } from "./chunk-RVGKEJXT.js";
4
+ } from "./chunk-CXL4GYGQ.js";
5
5
  import {
6
6
  run
7
7
  } from "./chunk-FH5WDJBF.js";