pyre-cli 9.5.1 → 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.
- package/README.md +15 -2
- package/dist/blender-7UT5J7IP.js +7 -0
- package/dist/{checkCmd-EWFNLBWE.js → checkCmd-T7O4KXXA.js} +4 -3
- package/dist/chunk-25I533OF.js +133 -0
- package/dist/{chunk-MOAEGMFP.js → chunk-3TE7MJLS.js} +168 -36
- package/dist/{chunk-RVGKEJXT.js → chunk-CXL4GYGQ.js} +3 -0
- package/dist/{chunk-HGRBBPYX.js → chunk-N4WJVF5M.js} +270 -23
- package/dist/{collectors-WD6VKH6M.js → collectors-JB4OGUEF.js} +4 -1
- package/dist/{config-4ZLF4HTS.js → config-KS5ZZ3VN.js} +1 -1
- package/dist/{doctor-L6FZYA7F.js → doctor-UV7TWIHX.js} +1 -1
- package/dist/index.js +419 -156
- package/dist/{pipeCmd-7THXEGDH.js → pipeCmd-TYLJHQ5B.js} +3 -2
- package/dist/{wizard-GVIAHIVV.js → wizard-Z3RGA3VB.js} +1 -1
- package/package.json +1 -1
- package/src/anomalies.ts +114 -60
- package/src/formatters/index.ts +1 -1
- package/src/formatters/output.ts +100 -13
- package/src/formatters/render.ts +240 -28
- package/src/formatters/themes.ts +8 -0
- package/src/formatters/types.ts +4 -1
- package/src/index.ts +53 -26
- package/src/live/input.ts +62 -4
- package/src/live/render.ts +31 -0
- package/src/live/state.ts +65 -52
- package/src/live/types.ts +2 -2
- package/src/monitors/blender.ts +144 -0
- package/src/monitors/collectors.ts +287 -40
- package/src/monitors/dns_cache.ts +48 -0
- package/src/monitors/index.ts +3 -2
- package/src/monitors/types.ts +97 -14
- package/src/state/config.ts +6 -0
- /package/dist/{chunk-SG26Y3WF.js → chunk-IM3IQMMT.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
CONFIG_FILE,
|
|
4
4
|
readConfig,
|
|
5
5
|
writeConfig
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-CXL4GYGQ.js";
|
|
7
7
|
import {
|
|
8
8
|
TAB_BAR_ROW,
|
|
9
9
|
THEMES,
|
|
@@ -15,15 +15,16 @@ import {
|
|
|
15
15
|
panel,
|
|
16
16
|
sparkline,
|
|
17
17
|
visLen
|
|
18
|
-
} from "./chunk-
|
|
19
|
-
import "./chunk-
|
|
18
|
+
} from "./chunk-3TE7MJLS.js";
|
|
19
|
+
import "./chunk-IM3IQMMT.js";
|
|
20
20
|
import {
|
|
21
21
|
collectAll
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-N4WJVF5M.js";
|
|
23
|
+
import "./chunk-25I533OF.js";
|
|
23
24
|
import "./chunk-FH5WDJBF.js";
|
|
24
25
|
|
|
25
26
|
// src/index.ts
|
|
26
|
-
import
|
|
27
|
+
import fs9 from "fs";
|
|
27
28
|
import os2 from "os";
|
|
28
29
|
import path5 from "path";
|
|
29
30
|
import readline2 from "readline";
|
|
@@ -124,19 +125,39 @@ function formatHtml(data) {
|
|
|
124
125
|
<table>
|
|
125
126
|
${rows([
|
|
126
127
|
{ label: "Interface", value: `${data.network.interface} (${data.network.ip})` },
|
|
127
|
-
{ label: "RX", value: `${formatBytes(data.network.rxBytes)}
|
|
128
|
-
{ label: "TX", value: `${formatBytes(data.network.txBytes)}
|
|
128
|
+
{ label: "RX", value: `${formatBytes(data.network.rxBytes)} \xB7 ${formatBytes(data.network.rxRate)}/s` },
|
|
129
|
+
{ label: "TX", value: `${formatBytes(data.network.txBytes)} \xB7 ${formatBytes(data.network.txRate)}/s` },
|
|
130
|
+
...data.network.protocols ? [
|
|
131
|
+
{ label: "TCP", value: `${data.network.protocols.tcp.connections} conns` },
|
|
132
|
+
{ label: "UDP", value: `${data.network.protocols.udp.connections} conns` }
|
|
133
|
+
] : [],
|
|
134
|
+
...data.network.connectionStates ? [
|
|
135
|
+
{ label: "Active", value: `${data.network.connectionStates.established} ESTABLISHED` },
|
|
136
|
+
{ label: "Listening", value: `${data.network.connectionStates.listening}` }
|
|
137
|
+
] : [],
|
|
138
|
+
...data.network.listeningPorts && data.network.listeningPorts.length ? [
|
|
139
|
+
{ label: "Ports", value: data.network.listeningPorts.slice(0, 8).join(", ") }
|
|
140
|
+
] : []
|
|
129
141
|
]).trim()}
|
|
130
142
|
</table>`);
|
|
131
143
|
if (data.packets) {
|
|
132
|
-
|
|
133
|
-
<table>
|
|
134
|
-
${rows([
|
|
144
|
+
const packetRows = [
|
|
135
145
|
{ label: "Total", value: `${data.packets.totalPackets} pkt` },
|
|
136
146
|
{ label: "RX", value: `${data.packets.rxPackets} pkt` },
|
|
137
147
|
{ label: "TX", value: `${data.packets.txPackets} pkt` },
|
|
138
148
|
{ label: "Connections", value: `${data.packets.connections} TCP` }
|
|
139
|
-
]
|
|
149
|
+
];
|
|
150
|
+
if (data.packets.protocolStats) {
|
|
151
|
+
packetRows.push({ label: "TCP", value: `${data.packets.protocolStats.tcp.connections} conns` });
|
|
152
|
+
packetRows.push({ label: "UDP", value: `${data.packets.protocolStats.udp.connections} conns` });
|
|
153
|
+
}
|
|
154
|
+
if (data.packets.connectionStates) {
|
|
155
|
+
packetRows.push({ label: "ESTABLISHED", value: `${data.packets.connectionStates.established}` });
|
|
156
|
+
packetRows.push({ label: "LISTENING", value: `${data.packets.connectionStates.listening}` });
|
|
157
|
+
}
|
|
158
|
+
sections.push(`<h2>Packets</h2>
|
|
159
|
+
<table>
|
|
160
|
+
${rows(packetRows).trim()}
|
|
140
161
|
</table>`);
|
|
141
162
|
}
|
|
142
163
|
if (data.tasks.length) {
|
|
@@ -167,6 +188,16 @@ function formatHtml(data) {
|
|
|
167
188
|
sections.push(`<h2>Processes</h2>
|
|
168
189
|
<table>
|
|
169
190
|
${rows(procRows).trim()}
|
|
191
|
+
</table>`);
|
|
192
|
+
}
|
|
193
|
+
if (data.blenderRenders && data.blenderRenders.length) {
|
|
194
|
+
const blenderRows = data.blenderRenders.map((r) => ({
|
|
195
|
+
label: `PID ${r.pid}`,
|
|
196
|
+
value: `${r.blendFile} \xB7 ${r.renderEngine} \xB7 ${r.currentFrame}/${r.totalFrames} frames \xB7 ${r.completionPercent}% \xB7 ${r.status} \xB7 ${r.elapsedSec}s`
|
|
197
|
+
}));
|
|
198
|
+
sections.push(`<h2>Blender Renders</h2>
|
|
199
|
+
<table>
|
|
200
|
+
${rows(blenderRows).trim()}
|
|
170
201
|
</table>`);
|
|
171
202
|
}
|
|
172
203
|
return `<!DOCTYPE html>
|
|
@@ -388,8 +419,19 @@ function formatMarkdown(data) {
|
|
|
388
419
|
lines.push("| Property | Value |");
|
|
389
420
|
lines.push("|---|---|");
|
|
390
421
|
lines.push(`| Interface | ${mdCell(`${data.network.interface} (${data.network.ip})`)} |`);
|
|
391
|
-
lines.push(`| RX | ${mdCell(`${formatBytes(data.network.rxBytes)}
|
|
392
|
-
lines.push(`| TX | ${mdCell(`${formatBytes(data.network.txBytes)}
|
|
422
|
+
lines.push(`| RX | ${mdCell(`${formatBytes(data.network.rxBytes)} \xB7 ${formatBytes(data.network.rxRate)}/s`)} |`);
|
|
423
|
+
lines.push(`| TX | ${mdCell(`${formatBytes(data.network.txBytes)} \xB7 ${formatBytes(data.network.txRate)}/s`)} |`);
|
|
424
|
+
if (data.network.protocols) {
|
|
425
|
+
lines.push(`| TCP | ${mdCell(`${data.network.protocols.tcp.connections} conns`)} |`);
|
|
426
|
+
lines.push(`| UDP | ${mdCell(`${data.network.protocols.udp.connections} conns`)} |`);
|
|
427
|
+
}
|
|
428
|
+
if (data.network.connectionStates) {
|
|
429
|
+
lines.push(`| Active | ${mdCell(`${data.network.connectionStates.established} ESTABLISHED`)} |`);
|
|
430
|
+
lines.push(`| Listening | ${mdCell(`${data.network.connectionStates.listening}`)} |`);
|
|
431
|
+
}
|
|
432
|
+
if (data.network.listeningPorts && data.network.listeningPorts.length) {
|
|
433
|
+
lines.push(`| Ports | ${mdCell(data.network.listeningPorts.slice(0, 8).join(", "))} |`);
|
|
434
|
+
}
|
|
393
435
|
lines.push("");
|
|
394
436
|
if (data.packets) {
|
|
395
437
|
lines.push("## Packets");
|
|
@@ -400,6 +442,14 @@ function formatMarkdown(data) {
|
|
|
400
442
|
lines.push(`| RX | ${mdCell(`${data.packets.rxPackets} pkt`)} |`);
|
|
401
443
|
lines.push(`| TX | ${mdCell(`${data.packets.txPackets} pkt`)} |`);
|
|
402
444
|
lines.push(`| Connections | ${mdCell(`${data.packets.connections} TCP`)} |`);
|
|
445
|
+
if (data.packets.protocolStats) {
|
|
446
|
+
lines.push(`| TCP | ${mdCell(`${data.packets.protocolStats.tcp.connections} conns`)} |`);
|
|
447
|
+
lines.push(`| UDP | ${mdCell(`${data.packets.protocolStats.udp.connections} conns`)} |`);
|
|
448
|
+
}
|
|
449
|
+
if (data.packets.connectionStates) {
|
|
450
|
+
lines.push(`| ESTABLISHED | ${mdCell(`${data.packets.connectionStates.established}`)} |`);
|
|
451
|
+
lines.push(`| LISTENING | ${mdCell(`${data.packets.connectionStates.listening}`)} |`);
|
|
452
|
+
}
|
|
403
453
|
lines.push("");
|
|
404
454
|
}
|
|
405
455
|
if (data.tasks.length) {
|
|
@@ -432,6 +482,16 @@ function formatMarkdown(data) {
|
|
|
432
482
|
}
|
|
433
483
|
lines.push("");
|
|
434
484
|
}
|
|
485
|
+
if (data.blenderRenders && data.blenderRenders.length) {
|
|
486
|
+
lines.push("## Blender Renders");
|
|
487
|
+
lines.push("");
|
|
488
|
+
lines.push("| PID | Blend File | Engine | Frames | Completion | Status | Elapsed |");
|
|
489
|
+
lines.push("|---|---|---|---|---|---|---|");
|
|
490
|
+
for (const r of data.blenderRenders) {
|
|
491
|
+
lines.push(`| ${r.pid} | ${mdCell(r.blendFile)} | ${mdCell(r.renderEngine)} | ${r.currentFrame}/${r.totalFrames} | ${r.completionPercent}% | ${r.status} | ${r.elapsedSec}s |`);
|
|
492
|
+
}
|
|
493
|
+
lines.push("");
|
|
494
|
+
}
|
|
435
495
|
return lines.join("\n");
|
|
436
496
|
}
|
|
437
497
|
function escapeHtml(s) {
|
|
@@ -461,9 +521,16 @@ function formatCsv(data) {
|
|
|
461
521
|
["thermal", "cpuTempF", data.thermal.temperatures?.cpu_die != null ? String(celsiusToFahrenheit(data.thermal.temperatures.cpu_die)) : ""],
|
|
462
522
|
["network", "rxBytes", String(data.network.rxBytes)],
|
|
463
523
|
["network", "txBytes", String(data.network.txBytes)],
|
|
524
|
+
["network", "rxRate", String(data.network.rxRate)],
|
|
525
|
+
["network", "txRate", String(data.network.txRate)],
|
|
464
526
|
["network", "rxPackets", String(data.network.rxPackets)],
|
|
465
527
|
["network", "txPackets", String(data.network.txPackets)],
|
|
466
528
|
["network", "connections", data.network.connections ? String(data.network.connections) : ""],
|
|
529
|
+
["network", "tcpConns", data.network.protocols ? String(data.network.protocols.tcp.connections) : ""],
|
|
530
|
+
["network", "udpConns", data.network.protocols ? String(data.network.protocols.udp.connections) : ""],
|
|
531
|
+
["network", "established", data.network.connectionStates ? String(data.network.connectionStates.established) : ""],
|
|
532
|
+
["network", "listening", data.network.connectionStates ? String(data.network.connectionStates.listening) : ""],
|
|
533
|
+
["network", "listeningPorts", data.network.listeningPorts ? data.network.listeningPorts.slice(0, 10).join(";") : ""],
|
|
467
534
|
["battery", "level", data.battery ? String(data.battery.level) : ""],
|
|
468
535
|
["battery", "powerSource", data.battery ? data.battery.powerSource : ""],
|
|
469
536
|
["battery", "condition", data.battery?.condition ?? ""],
|
|
@@ -487,6 +554,16 @@ function formatCsv(data) {
|
|
|
487
554
|
rows.push(["packets", "rxPackets", String(data.packets.rxPackets)]);
|
|
488
555
|
rows.push(["packets", "txPackets", String(data.packets.txPackets)]);
|
|
489
556
|
rows.push(["packets", "connections", String(data.packets.connections)]);
|
|
557
|
+
if (data.packets.protocolStats) {
|
|
558
|
+
rows.push(["packets", "tcpConns", String(data.packets.protocolStats.tcp.connections)]);
|
|
559
|
+
rows.push(["packets", "udpConns", String(data.packets.protocolStats.udp.connections)]);
|
|
560
|
+
}
|
|
561
|
+
if (data.packets.connectionStates) {
|
|
562
|
+
rows.push(["packets", "established", String(data.packets.connectionStates.established)]);
|
|
563
|
+
rows.push(["packets", "listening", String(data.packets.connectionStates.listening)]);
|
|
564
|
+
rows.push(["packets", "timeWait", String(data.packets.connectionStates.timeWait)]);
|
|
565
|
+
rows.push(["packets", "closeWait", String(data.packets.connectionStates.closeWait)]);
|
|
566
|
+
}
|
|
490
567
|
for (const iface of data.packets.interfaces || []) {
|
|
491
568
|
rows.push(["packets_iface", iface.iface, `${iface.rxPackets},${iface.txPackets},${iface.rxBytes},${iface.txBytes}`]);
|
|
492
569
|
}
|
|
@@ -512,6 +589,20 @@ function formatCsv(data) {
|
|
|
512
589
|
rows.push(["process", `runtime=${p.pid}`, String(p.runtime)]);
|
|
513
590
|
rows.push(["process", `command=${p.pid}`, p.command]);
|
|
514
591
|
}
|
|
592
|
+
for (const r of data.blenderRenders || []) {
|
|
593
|
+
rows.push(["blender", `pid=${r.pid}`, `${r.pid}`]);
|
|
594
|
+
rows.push(["blender", `blend_file=${r.pid}`, r.blendFile]);
|
|
595
|
+
rows.push(["blender", `engine=${r.pid}`, r.renderEngine]);
|
|
596
|
+
rows.push(["blender", `frame_start=${r.pid}`, String(r.frameStart)]);
|
|
597
|
+
rows.push(["blender", `frame_end=${r.pid}`, String(r.frameEnd)]);
|
|
598
|
+
rows.push(["blender", `current_frame=${r.pid}`, String(r.currentFrame)]);
|
|
599
|
+
rows.push(["blender", `total_frames=${r.pid}`, String(r.totalFrames)]);
|
|
600
|
+
rows.push(["blender", `completion_pct=${r.pid}`, String(r.completionPercent)]);
|
|
601
|
+
rows.push(["blender", `status=${r.pid}`, r.status]);
|
|
602
|
+
rows.push(["blender", `elapsed_sec=${r.pid}`, String(r.elapsedSec)]);
|
|
603
|
+
rows.push(["blender", `output_path=${r.pid}`, r.outputPath]);
|
|
604
|
+
if (r.sampleCount !== void 0) rows.push(["blender", `samples=${r.pid}`, String(r.sampleCount)]);
|
|
605
|
+
}
|
|
515
606
|
return rows.map((r) => r.map((item) => `"${String(item).replace(/"/g, '""')}"`).join(",")).join("\n");
|
|
516
607
|
}
|
|
517
608
|
function formatTsv(data) {
|
|
@@ -1237,6 +1328,9 @@ document.addEventListener('keydown', e => { if(keyMap[e.key.toLowerCase()]) swit
|
|
|
1237
1328
|
// src/live/input.ts
|
|
1238
1329
|
import readline from "readline";
|
|
1239
1330
|
|
|
1331
|
+
// src/live/state.ts
|
|
1332
|
+
import fs from "fs";
|
|
1333
|
+
|
|
1240
1334
|
// src/history.ts
|
|
1241
1335
|
var History = class _History {
|
|
1242
1336
|
/** Maximum number of samples retained in the rolling window. */
|
|
@@ -1396,6 +1490,7 @@ var History = class _History {
|
|
|
1396
1490
|
var SIGNAL_OPTIONS = ["SIGTERM", "SIGKILL", "SIGINT", "SIGHUP", "SIGSTOP", "SIGCONT"];
|
|
1397
1491
|
var MENU_OPTIONS = ["Resume Dashboard", "Settings (Customizer)", "Readme", "Credits", "Quit pyre"];
|
|
1398
1492
|
var config = readConfig();
|
|
1493
|
+
var isDockerEnv = fs.existsSync("/.dockerenv") || fs.existsSync("/run/.containerenv");
|
|
1399
1494
|
var state = {
|
|
1400
1495
|
intervalHandle: null,
|
|
1401
1496
|
uiIntervalHandle: null,
|
|
@@ -1420,7 +1515,9 @@ var state = {
|
|
|
1420
1515
|
packets: config.visiblePanels.packets,
|
|
1421
1516
|
tasks: config.visiblePanels.tasks,
|
|
1422
1517
|
disk: config.visiblePanels.disk,
|
|
1423
|
-
process: config.visiblePanels.process
|
|
1518
|
+
process: config.visiblePanels.process,
|
|
1519
|
+
containers: config.visiblePanels.containers,
|
|
1520
|
+
blender: config.visiblePanels.blender
|
|
1424
1521
|
},
|
|
1425
1522
|
panelLayout: config.panelLayout || ["mem", "disk", "net"],
|
|
1426
1523
|
logging: false,
|
|
@@ -1438,7 +1535,7 @@ var state = {
|
|
|
1438
1535
|
processScrollOffset: 0,
|
|
1439
1536
|
trackedPid: null,
|
|
1440
1537
|
inspectingProcess: null,
|
|
1441
|
-
inputMode: null,
|
|
1538
|
+
inputMode: isDockerEnv && !config.dockerModeConfirmed ? "docker-alert" : null,
|
|
1442
1539
|
inputBuffer: "",
|
|
1443
1540
|
targetPid: "",
|
|
1444
1541
|
selectedSignal: "SIGTERM",
|
|
@@ -1460,10 +1557,12 @@ var state = {
|
|
|
1460
1557
|
"Toggle Battery",
|
|
1461
1558
|
"Toggle Thermal",
|
|
1462
1559
|
"Toggle Network",
|
|
1463
|
-
"Toggle
|
|
1560
|
+
"Toggle Conns",
|
|
1464
1561
|
"Toggle Tasks",
|
|
1465
1562
|
"Toggle Disk",
|
|
1466
1563
|
"Toggle Processes",
|
|
1564
|
+
"Toggle Containers",
|
|
1565
|
+
"Toggle Blender",
|
|
1467
1566
|
"Toggle Tree View"
|
|
1468
1567
|
],
|
|
1469
1568
|
CPU_ALERT_PCT: config.cpuAlertPct,
|
|
@@ -1485,12 +1584,14 @@ var state = {
|
|
|
1485
1584
|
{ id: "battery", label: "Battery", key: "5" },
|
|
1486
1585
|
{ id: "thermal", label: "Thermal", key: "6" },
|
|
1487
1586
|
{ id: "network", label: "Network", key: "7" },
|
|
1488
|
-
{ id: "packets", label: "
|
|
1587
|
+
{ id: "packets", label: "Conns", key: "8" },
|
|
1489
1588
|
{ id: "tasks", label: "Tasks", key: "9" },
|
|
1490
1589
|
{ id: "disk", label: "Disk", key: "0" },
|
|
1491
1590
|
{ id: "process", label: "Process", key: "P" },
|
|
1591
|
+
{ id: "containers", label: "Containers", key: "C" },
|
|
1492
1592
|
{ id: "p2p", label: "P2P", key: "R" },
|
|
1493
|
-
{ id: "anomalies", label: "Anomalies", key: "A" }
|
|
1593
|
+
{ id: "anomalies", label: "Anomalies", key: "A" },
|
|
1594
|
+
{ id: "blender", label: "Blender", key: "B" }
|
|
1494
1595
|
],
|
|
1495
1596
|
p2pServer: null,
|
|
1496
1597
|
p2pClient: null,
|
|
@@ -1505,7 +1606,10 @@ var state = {
|
|
|
1505
1606
|
splashColorScheme: config.splashColorScheme,
|
|
1506
1607
|
splashAnimation: config.splashAnimation,
|
|
1507
1608
|
menuSelectionIndex: 0,
|
|
1508
|
-
readmeScrollOffset: 0
|
|
1609
|
+
readmeScrollOffset: 0,
|
|
1610
|
+
isDocker: isDockerEnv,
|
|
1611
|
+
dockerModeConfirmed: config.dockerModeConfirmed || false,
|
|
1612
|
+
dockerSelectionIndex: 0
|
|
1509
1613
|
};
|
|
1510
1614
|
function setStatus(msg, ms = 3e3) {
|
|
1511
1615
|
state.statusMessage = msg;
|
|
@@ -1523,20 +1627,22 @@ function getToggleKey(opt) {
|
|
|
1523
1627
|
"Toggle Battery": "battery",
|
|
1524
1628
|
"Toggle Thermal": "thermal",
|
|
1525
1629
|
"Toggle Network": "network",
|
|
1526
|
-
"Toggle
|
|
1630
|
+
"Toggle Conns": "packets",
|
|
1527
1631
|
"Toggle Tasks": "tasks",
|
|
1528
1632
|
"Toggle Disk": "disk",
|
|
1529
1633
|
"Toggle Processes": "process",
|
|
1634
|
+
"Toggle Containers": "containers",
|
|
1635
|
+
"Toggle Blender": "blender",
|
|
1530
1636
|
"Toggle Tree View": "tree"
|
|
1531
1637
|
};
|
|
1532
1638
|
return map[opt] ?? null;
|
|
1533
1639
|
}
|
|
1534
1640
|
|
|
1535
1641
|
// src/live/export.ts
|
|
1536
|
-
import
|
|
1642
|
+
import fs2 from "fs";
|
|
1537
1643
|
import path from "path";
|
|
1538
1644
|
function ensureDir(dir) {
|
|
1539
|
-
if (!
|
|
1645
|
+
if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
|
|
1540
1646
|
}
|
|
1541
1647
|
function timestampForFile() {
|
|
1542
1648
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
@@ -1568,7 +1674,7 @@ function exportSnapshot() {
|
|
|
1568
1674
|
content = formatMarkdown(state.lastData);
|
|
1569
1675
|
break;
|
|
1570
1676
|
}
|
|
1571
|
-
|
|
1677
|
+
fs2.writeFileSync(file, content);
|
|
1572
1678
|
setStatus(`Exported snapshot -> ${file}`);
|
|
1573
1679
|
} catch (err) {
|
|
1574
1680
|
setStatus(`Export failed: ${err.message}`);
|
|
@@ -1579,7 +1685,7 @@ function startLogging() {
|
|
|
1579
1685
|
ensureDir(state.exportDir);
|
|
1580
1686
|
rotateLogs();
|
|
1581
1687
|
const file = path.join(state.exportDir, `pyre-log-${timestampForFile()}.csv`);
|
|
1582
|
-
state.logStream =
|
|
1688
|
+
state.logStream = fs2.createWriteStream(file, { flags: "a" });
|
|
1583
1689
|
state.logStream.write("timestamp,cpu_usage,mem_usage_percent,temp_c,net_rx_bytes,net_tx_bytes,net_rx_packets,net_tx_packets,connections,thermal_state\n");
|
|
1584
1690
|
state.logging = true;
|
|
1585
1691
|
setStatus(`Logging every tick -> ${file}`);
|
|
@@ -1593,37 +1699,37 @@ var MAX_LOG_BYTES = 50 * 1024 * 1024;
|
|
|
1593
1699
|
function rotateLogs() {
|
|
1594
1700
|
try {
|
|
1595
1701
|
const dir = state.exportDir;
|
|
1596
|
-
if (!
|
|
1597
|
-
const files =
|
|
1702
|
+
if (!fs2.existsSync(dir)) return;
|
|
1703
|
+
const files = fs2.readdirSync(dir).filter((f) => f.startsWith("pyre-log-") && f.endsWith(".csv")).map((f) => ({ name: f, path: path.join(dir, f), mtime: fs2.statSync(path.join(dir, f)).mtimeMs, size: fs2.statSync(path.join(dir, f)).size })).sort((a, b) => a.mtime - b.mtime);
|
|
1598
1704
|
const now = Date.now();
|
|
1599
1705
|
for (const f of files) {
|
|
1600
1706
|
if (now - f.mtime > MAX_LOG_AGE_MS) {
|
|
1601
1707
|
try {
|
|
1602
|
-
|
|
1708
|
+
fs2.unlinkSync(f.path);
|
|
1603
1709
|
} catch {
|
|
1604
1710
|
}
|
|
1605
1711
|
}
|
|
1606
1712
|
}
|
|
1607
|
-
const remaining =
|
|
1713
|
+
const remaining = fs2.readdirSync(dir).filter((f) => f.startsWith("pyre-log-") && f.endsWith(".csv"));
|
|
1608
1714
|
if (remaining.length > MAX_LOG_COUNT) {
|
|
1609
1715
|
const toDelete = remaining.sort().slice(0, remaining.length - MAX_LOG_COUNT);
|
|
1610
1716
|
for (const f of toDelete) {
|
|
1611
1717
|
try {
|
|
1612
|
-
|
|
1718
|
+
fs2.unlinkSync(path.join(dir, f));
|
|
1613
1719
|
} catch {
|
|
1614
1720
|
}
|
|
1615
1721
|
}
|
|
1616
1722
|
}
|
|
1617
1723
|
let totalSize = 0;
|
|
1618
1724
|
for (const f of remaining) {
|
|
1619
|
-
totalSize +=
|
|
1725
|
+
totalSize += fs2.statSync(path.join(dir, f)).size;
|
|
1620
1726
|
}
|
|
1621
1727
|
if (totalSize > MAX_LOG_BYTES) {
|
|
1622
1728
|
const sorted = remaining.sort();
|
|
1623
1729
|
for (const f of sorted) {
|
|
1624
|
-
const sz =
|
|
1730
|
+
const sz = fs2.statSync(path.join(dir, f)).size;
|
|
1625
1731
|
try {
|
|
1626
|
-
|
|
1732
|
+
fs2.unlinkSync(path.join(dir, f));
|
|
1627
1733
|
} catch {
|
|
1628
1734
|
}
|
|
1629
1735
|
totalSize -= sz;
|
|
@@ -1660,7 +1766,7 @@ function writeLogRow(data) {
|
|
|
1660
1766
|
|
|
1661
1767
|
// src/live/render.ts
|
|
1662
1768
|
import { execFile, exec } from "child_process";
|
|
1663
|
-
import
|
|
1769
|
+
import fs3 from "fs";
|
|
1664
1770
|
import path2 from "path";
|
|
1665
1771
|
import { fileURLToPath } from "url";
|
|
1666
1772
|
import chalk2 from "chalk";
|
|
@@ -1668,76 +1774,127 @@ import chalk2 from "chalk";
|
|
|
1668
1774
|
// src/anomalies.ts
|
|
1669
1775
|
var ZSCORE_THRESHOLD = 2.5;
|
|
1670
1776
|
var CRITICAL_ZSCORE = 3.5;
|
|
1671
|
-
var MIN_SAMPLES =
|
|
1777
|
+
var MIN_SAMPLES = 10;
|
|
1778
|
+
var MIN_DEV_CPU = 10;
|
|
1779
|
+
var MIN_DEV_MEM = 5;
|
|
1780
|
+
var MIN_DEV_TEMP = 5;
|
|
1781
|
+
var MIN_DEV_NET = 5e5;
|
|
1782
|
+
var MIN_DEV_POWER = 5;
|
|
1783
|
+
var MIN_DEV_GPU = 10;
|
|
1672
1784
|
function detectAnomalies(data, history) {
|
|
1673
1785
|
const alerts = [];
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1786
|
+
if (history.cpuUsage.length >= MIN_SAMPLES) {
|
|
1787
|
+
const cpuMean = History.mean(history.cpuUsage);
|
|
1788
|
+
if (Math.abs(data.cpu.usage - cpuMean) > MIN_DEV_CPU) {
|
|
1789
|
+
const cpuZ = History.zScore(data.cpu.usage, history.cpuUsage);
|
|
1790
|
+
if (Math.abs(cpuZ) > ZSCORE_THRESHOLD) {
|
|
1791
|
+
alerts.push({
|
|
1792
|
+
metric: "CPU",
|
|
1793
|
+
value: data.cpu.usage,
|
|
1794
|
+
zScore: cpuZ,
|
|
1795
|
+
severity: Math.abs(cpuZ) > CRITICAL_ZSCORE ? "critical" : "warning"
|
|
1796
|
+
});
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1682
1799
|
}
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1800
|
+
if (history.memUsage.length >= MIN_SAMPLES) {
|
|
1801
|
+
const memMean = History.mean(history.memUsage);
|
|
1802
|
+
if (Math.abs(data.memory.usagePercent - memMean) > MIN_DEV_MEM) {
|
|
1803
|
+
const memZ = History.zScore(data.memory.usagePercent, history.memUsage);
|
|
1804
|
+
if (Math.abs(memZ) > ZSCORE_THRESHOLD) {
|
|
1805
|
+
alerts.push({
|
|
1806
|
+
metric: "Memory",
|
|
1807
|
+
value: data.memory.usagePercent,
|
|
1808
|
+
zScore: memZ,
|
|
1809
|
+
severity: Math.abs(memZ) > CRITICAL_ZSCORE ? "critical" : "warning"
|
|
1810
|
+
});
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1691
1813
|
}
|
|
1692
|
-
if (data.cpu.temperature !== void 0) {
|
|
1693
|
-
const
|
|
1694
|
-
if (Math.abs(
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1814
|
+
if (data.cpu.temperature !== void 0 && history.temp.length >= MIN_SAMPLES) {
|
|
1815
|
+
const tempMean = History.mean(history.temp);
|
|
1816
|
+
if (Math.abs(data.cpu.temperature - tempMean) > MIN_DEV_TEMP) {
|
|
1817
|
+
const tempZ = History.zScore(data.cpu.temperature, history.temp);
|
|
1818
|
+
if (Math.abs(tempZ) > ZSCORE_THRESHOLD) {
|
|
1819
|
+
alerts.push({
|
|
1820
|
+
metric: "Temp",
|
|
1821
|
+
value: data.cpu.temperature,
|
|
1822
|
+
zScore: tempZ,
|
|
1823
|
+
severity: Math.abs(tempZ) > CRITICAL_ZSCORE ? "critical" : "warning"
|
|
1824
|
+
});
|
|
1825
|
+
}
|
|
1701
1826
|
}
|
|
1702
1827
|
}
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1828
|
+
if (history.netRxRate.length >= MIN_SAMPLES) {
|
|
1829
|
+
const currentRxRate = history.netRxRate[history.netRxRate.length - 1];
|
|
1830
|
+
const netRxMean = History.mean(history.netRxRate);
|
|
1831
|
+
if (Math.abs(currentRxRate - netRxMean) > MIN_DEV_NET) {
|
|
1832
|
+
const netRxZ = History.zScore(currentRxRate, history.netRxRate);
|
|
1833
|
+
if (Math.abs(netRxZ) > ZSCORE_THRESHOLD) {
|
|
1834
|
+
alerts.push({
|
|
1835
|
+
metric: "Net RX",
|
|
1836
|
+
value: currentRxRate,
|
|
1837
|
+
zScore: netRxZ,
|
|
1838
|
+
severity: Math.abs(netRxZ) > CRITICAL_ZSCORE ? "critical" : "warning"
|
|
1839
|
+
});
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1711
1842
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1843
|
+
if (history.netTxRate.length >= MIN_SAMPLES) {
|
|
1844
|
+
const currentTxRate = history.netTxRate[history.netTxRate.length - 1];
|
|
1845
|
+
const netTxMean = History.mean(history.netTxRate);
|
|
1846
|
+
if (Math.abs(currentTxRate - netTxMean) > MIN_DEV_NET) {
|
|
1847
|
+
const netTxZ = History.zScore(currentTxRate, history.netTxRate);
|
|
1848
|
+
if (Math.abs(netTxZ) > ZSCORE_THRESHOLD) {
|
|
1849
|
+
alerts.push({
|
|
1850
|
+
metric: "Net TX",
|
|
1851
|
+
value: currentTxRate,
|
|
1852
|
+
zScore: netTxZ,
|
|
1853
|
+
severity: Math.abs(netTxZ) > CRITICAL_ZSCORE ? "critical" : "warning"
|
|
1854
|
+
});
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1720
1857
|
}
|
|
1721
|
-
if (data.power?.combinedWatts !== void 0) {
|
|
1722
|
-
const
|
|
1723
|
-
if (Math.abs(
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1858
|
+
if (data.power?.combinedWatts !== void 0 && history.powerWatts.length >= MIN_SAMPLES) {
|
|
1859
|
+
const powerMean = History.mean(history.powerWatts);
|
|
1860
|
+
if (Math.abs(data.power.combinedWatts - powerMean) > MIN_DEV_POWER) {
|
|
1861
|
+
const powerZ = History.zScore(data.power.combinedWatts, history.powerWatts);
|
|
1862
|
+
if (Math.abs(powerZ) > ZSCORE_THRESHOLD) {
|
|
1863
|
+
alerts.push({
|
|
1864
|
+
metric: "Power",
|
|
1865
|
+
value: data.power.combinedWatts,
|
|
1866
|
+
zScore: powerZ,
|
|
1867
|
+
severity: Math.abs(powerZ) > CRITICAL_ZSCORE ? "critical" : "warning"
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1730
1870
|
}
|
|
1731
1871
|
}
|
|
1732
|
-
if (
|
|
1733
|
-
const
|
|
1734
|
-
if (Math.abs(
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1872
|
+
if (history.connections.length >= MIN_SAMPLES) {
|
|
1873
|
+
const connMean = History.mean(history.connections);
|
|
1874
|
+
if (Math.abs((data.packets?.connections || data.network.connections || 0) - connMean) > 5) {
|
|
1875
|
+
const connZ = History.zScore(data.packets?.connections || data.network.connections || 0, history.connections);
|
|
1876
|
+
if (Math.abs(connZ) > ZSCORE_THRESHOLD) {
|
|
1877
|
+
alerts.push({
|
|
1878
|
+
metric: "Connections",
|
|
1879
|
+
value: data.packets?.connections || data.network.connections || 0,
|
|
1880
|
+
zScore: connZ,
|
|
1881
|
+
severity: Math.abs(connZ) > CRITICAL_ZSCORE ? "critical" : "warning"
|
|
1882
|
+
});
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
if (data.gpu && history.gpuUtil.length >= MIN_SAMPLES) {
|
|
1887
|
+
const gpuMean = History.mean(history.gpuUtil);
|
|
1888
|
+
if (Math.abs(data.gpu.utilization - gpuMean) > MIN_DEV_GPU) {
|
|
1889
|
+
const gpuZ = History.zScore(data.gpu.utilization, history.gpuUtil);
|
|
1890
|
+
if (Math.abs(gpuZ) > ZSCORE_THRESHOLD) {
|
|
1891
|
+
alerts.push({
|
|
1892
|
+
metric: "GPU",
|
|
1893
|
+
value: data.gpu.utilization,
|
|
1894
|
+
zScore: gpuZ,
|
|
1895
|
+
severity: Math.abs(gpuZ) > CRITICAL_ZSCORE ? "critical" : "warning"
|
|
1896
|
+
});
|
|
1897
|
+
}
|
|
1741
1898
|
}
|
|
1742
1899
|
}
|
|
1743
1900
|
return alerts;
|
|
@@ -1906,6 +2063,10 @@ function render() {
|
|
|
1906
2063
|
writeFrame(renderCredits(cols, rows));
|
|
1907
2064
|
return;
|
|
1908
2065
|
}
|
|
2066
|
+
if (state.inputMode === "docker-alert") {
|
|
2067
|
+
writeFrame(renderDockerAlert(cols, rows));
|
|
2068
|
+
return;
|
|
2069
|
+
}
|
|
1909
2070
|
const lines = [];
|
|
1910
2071
|
const theme = THEMES[state.currentTheme] || THEMES.default;
|
|
1911
2072
|
const params = tableCacheParams();
|
|
@@ -2161,7 +2322,7 @@ function getReadmeLines() {
|
|
|
2161
2322
|
const __dirname = path2.dirname(__filename);
|
|
2162
2323
|
const rootDir = path2.resolve(__dirname, "..", "..");
|
|
2163
2324
|
const readmePath = path2.join(rootDir, "README.md");
|
|
2164
|
-
const content =
|
|
2325
|
+
const content = fs3.readFileSync(readmePath, "utf8");
|
|
2165
2326
|
cachedReadmeLines = content.split("\n");
|
|
2166
2327
|
return cachedReadmeLines;
|
|
2167
2328
|
} catch (err) {
|
|
@@ -2206,11 +2367,34 @@ function renderCredits(cols, rows) {
|
|
|
2206
2367
|
out.push(...panel("CREDITS", content, cols, chalk2.magenta, chalk2.dim, rows - 2));
|
|
2207
2368
|
return out;
|
|
2208
2369
|
}
|
|
2370
|
+
function renderDockerAlert(cols, rows) {
|
|
2371
|
+
const out = [];
|
|
2372
|
+
const content = [
|
|
2373
|
+
chalk2.bold.red("Docker Container Detected"),
|
|
2374
|
+
"",
|
|
2375
|
+
"Pyre is running inside a container. Some hardware sensors",
|
|
2376
|
+
"(battery, thermal, power) may not be available or relevant.",
|
|
2377
|
+
"",
|
|
2378
|
+
chalk2.cyan("Do you wish to continue in Docker mode?"),
|
|
2379
|
+
""
|
|
2380
|
+
];
|
|
2381
|
+
const options = ["Yes, use Docker mode (hide hardware sensors)", "No, launch regular TUI with all features"];
|
|
2382
|
+
for (let i = 0; i < options.length; i++) {
|
|
2383
|
+
const isSelected = i === state.dockerSelectionIndex;
|
|
2384
|
+
const prefix = isSelected ? chalk2.cyan.bold(" \u25B6 ") : " ";
|
|
2385
|
+
const text = isSelected ? chalk2.bgCyan.black(` ${options[i]} `) : chalk2.dim(` ${options[i]} `);
|
|
2386
|
+
content.push(prefix + text);
|
|
2387
|
+
}
|
|
2388
|
+
content.push("");
|
|
2389
|
+
content.push(chalk2.dim("Press \u2191/\u2193 to select, Enter to confirm, Esc to skip."));
|
|
2390
|
+
out.push(...panel("DOCKER DETECTED", content, cols, chalk2.red, chalk2.dim, rows - 2));
|
|
2391
|
+
return out;
|
|
2392
|
+
}
|
|
2209
2393
|
|
|
2210
2394
|
// src/p2p/server.ts
|
|
2211
2395
|
import net from "net";
|
|
2212
2396
|
import tls from "tls";
|
|
2213
|
-
import
|
|
2397
|
+
import fs4 from "fs";
|
|
2214
2398
|
import crypto2 from "crypto";
|
|
2215
2399
|
import chalk3 from "chalk";
|
|
2216
2400
|
|
|
@@ -2355,9 +2539,9 @@ var P2PServer = class {
|
|
|
2355
2539
|
async start() {
|
|
2356
2540
|
if (this.options.auditLog) {
|
|
2357
2541
|
try {
|
|
2358
|
-
|
|
2542
|
+
fs4.mkdirSync(this.options.auditLog, { recursive: true });
|
|
2359
2543
|
const logFile = `${this.options.auditLog}/p2p-audit.log`;
|
|
2360
|
-
this.auditStream =
|
|
2544
|
+
this.auditStream = fs4.createWriteStream(logFile, { flags: "a" });
|
|
2361
2545
|
} catch {
|
|
2362
2546
|
this.log(chalk3.yellow("Warning: could not open audit log"));
|
|
2363
2547
|
}
|
|
@@ -2366,11 +2550,11 @@ var P2PServer = class {
|
|
|
2366
2550
|
const useTLS = !!(this.options.tlsCert && this.options.tlsKey);
|
|
2367
2551
|
if (useTLS) {
|
|
2368
2552
|
const tlsOptions = {
|
|
2369
|
-
key:
|
|
2370
|
-
cert:
|
|
2553
|
+
key: fs4.readFileSync(this.options.tlsKey),
|
|
2554
|
+
cert: fs4.readFileSync(this.options.tlsCert)
|
|
2371
2555
|
};
|
|
2372
2556
|
if (this.options.tlsCA) {
|
|
2373
|
-
tlsOptions.ca =
|
|
2557
|
+
tlsOptions.ca = fs4.readFileSync(this.options.tlsCA);
|
|
2374
2558
|
}
|
|
2375
2559
|
this.server = tls.createServer(tlsOptions, (socket) => this.onConnection(socket));
|
|
2376
2560
|
} else {
|
|
@@ -2638,7 +2822,7 @@ async function startP2PServer(options) {
|
|
|
2638
2822
|
// src/p2p/client.ts
|
|
2639
2823
|
import net2 from "net";
|
|
2640
2824
|
import tls2 from "tls";
|
|
2641
|
-
import
|
|
2825
|
+
import fs5 from "fs";
|
|
2642
2826
|
import crypto3 from "crypto";
|
|
2643
2827
|
import chalk4 from "chalk";
|
|
2644
2828
|
var P2PClient = class {
|
|
@@ -2666,9 +2850,9 @@ var P2PClient = class {
|
|
|
2666
2850
|
async connect() {
|
|
2667
2851
|
if (this.options.auditLog) {
|
|
2668
2852
|
try {
|
|
2669
|
-
|
|
2853
|
+
fs5.mkdirSync(this.options.auditLog, { recursive: true });
|
|
2670
2854
|
const logFile = `${this.options.auditLog}/p2p-client-audit.log`;
|
|
2671
|
-
this.auditStream =
|
|
2855
|
+
this.auditStream = fs5.createWriteStream(logFile, { flags: "a" });
|
|
2672
2856
|
} catch {
|
|
2673
2857
|
console.log(chalk4.yellow("Warning: could not open audit log"));
|
|
2674
2858
|
}
|
|
@@ -2683,7 +2867,7 @@ var P2PClient = class {
|
|
|
2683
2867
|
rejectUnauthorized: !this.options.tlsInsecure
|
|
2684
2868
|
};
|
|
2685
2869
|
if (this.options.tlsCA) {
|
|
2686
|
-
tlsOptions.ca =
|
|
2870
|
+
tlsOptions.ca = fs5.readFileSync(this.options.tlsCA);
|
|
2687
2871
|
}
|
|
2688
2872
|
this.socket = tls2.connect(tlsOptions, () => {
|
|
2689
2873
|
this.running = true;
|
|
@@ -2893,7 +3077,8 @@ function persistConfig() {
|
|
|
2893
3077
|
splashColorScheme: state.splashColorScheme,
|
|
2894
3078
|
splashAnimation: state.splashAnimation,
|
|
2895
3079
|
visiblePanels: { ...state.visiblePanels },
|
|
2896
|
-
notificationsEnabled: state.notificationsEnabled
|
|
3080
|
+
notificationsEnabled: state.notificationsEnabled,
|
|
3081
|
+
dockerModeConfirmed: state.dockerModeConfirmed
|
|
2897
3082
|
});
|
|
2898
3083
|
}
|
|
2899
3084
|
function isUpKey(key, str) {
|
|
@@ -3028,6 +3213,46 @@ function handleInputModeKey(str, key) {
|
|
|
3028
3213
|
}
|
|
3029
3214
|
return;
|
|
3030
3215
|
}
|
|
3216
|
+
if (state.inputMode === "docker-alert") {
|
|
3217
|
+
if (isEscKey(key, str)) {
|
|
3218
|
+
state.inputMode = null;
|
|
3219
|
+
state.dockerModeConfirmed = true;
|
|
3220
|
+
render();
|
|
3221
|
+
return;
|
|
3222
|
+
}
|
|
3223
|
+
if (isUpKey(key, str)) {
|
|
3224
|
+
state.dockerSelectionIndex = Math.max(0, state.dockerSelectionIndex - 1);
|
|
3225
|
+
render();
|
|
3226
|
+
return;
|
|
3227
|
+
}
|
|
3228
|
+
if (isDownKey(key, str)) {
|
|
3229
|
+
state.dockerSelectionIndex = Math.min(1, state.dockerSelectionIndex + 1);
|
|
3230
|
+
render();
|
|
3231
|
+
return;
|
|
3232
|
+
}
|
|
3233
|
+
if (isEnterKey(key, str) || str === " ") {
|
|
3234
|
+
state.dockerModeConfirmed = true;
|
|
3235
|
+
state.inputMode = null;
|
|
3236
|
+
if (state.dockerSelectionIndex === 0) {
|
|
3237
|
+
state.visiblePanels.battery = false;
|
|
3238
|
+
state.visiblePanels.thermal = false;
|
|
3239
|
+
state.visiblePanels.power = false;
|
|
3240
|
+
state.visiblePanels.gpu = false;
|
|
3241
|
+
state.detailed = false;
|
|
3242
|
+
} else {
|
|
3243
|
+
state.visiblePanels.battery = true;
|
|
3244
|
+
state.visiblePanels.thermal = true;
|
|
3245
|
+
state.visiblePanels.power = true;
|
|
3246
|
+
state.visiblePanels.gpu = true;
|
|
3247
|
+
}
|
|
3248
|
+
invalidateTableCache();
|
|
3249
|
+
invalidateFrame();
|
|
3250
|
+
persistConfig();
|
|
3251
|
+
render();
|
|
3252
|
+
return;
|
|
3253
|
+
}
|
|
3254
|
+
return;
|
|
3255
|
+
}
|
|
3031
3256
|
if (state.inputMode === "signal") {
|
|
3032
3257
|
if (isUpKey(key, str)) {
|
|
3033
3258
|
const idx = state.SIGNAL_OPTIONS.indexOf(state.inputBuffer);
|
|
@@ -3358,6 +3583,18 @@ async function startLive(opts2, splashPromise) {
|
|
|
3358
3583
|
render();
|
|
3359
3584
|
return;
|
|
3360
3585
|
}
|
|
3586
|
+
case "C": {
|
|
3587
|
+
state.activePanel = state.activePanel === "containers" ? "grid" : "containers";
|
|
3588
|
+
setStatus(state.activePanel === "grid" ? "Grid view" : "Containers panel");
|
|
3589
|
+
render();
|
|
3590
|
+
return;
|
|
3591
|
+
}
|
|
3592
|
+
case "B": {
|
|
3593
|
+
state.activePanel = state.activePanel === "blender" ? "grid" : "blender";
|
|
3594
|
+
setStatus(state.activePanel === "grid" ? "Grid view" : "Blender panel");
|
|
3595
|
+
render();
|
|
3596
|
+
return;
|
|
3597
|
+
}
|
|
3361
3598
|
}
|
|
3362
3599
|
switch (key.name) {
|
|
3363
3600
|
case "r":
|
|
@@ -3531,7 +3768,7 @@ async function startLive(opts2, splashPromise) {
|
|
|
3531
3768
|
process.once("SIGINT", () => stopLive());
|
|
3532
3769
|
}
|
|
3533
3770
|
function tabKeyToId(str) {
|
|
3534
|
-
const map = { "1": "cpu", "2": "mem", "3": "gpu", "4": "power", "5": "battery", "6": "thermal", "7": "network", "8": "packets", "9": "tasks", "0": "disk", "p": "process", "r": "p2p", "A": "anomalies" };
|
|
3771
|
+
const map = { "1": "cpu", "2": "mem", "3": "gpu", "4": "power", "5": "battery", "6": "thermal", "7": "network", "8": "packets", "9": "tasks", "0": "disk", "p": "process", "C": "containers", "r": "p2p", "A": "anomalies", "B": "blender" };
|
|
3535
3772
|
return map[str] ?? null;
|
|
3536
3773
|
}
|
|
3537
3774
|
function cycleTab(direction) {
|
|
@@ -3955,7 +4192,7 @@ async function showSplash(config3) {
|
|
|
3955
4192
|
}
|
|
3956
4193
|
|
|
3957
4194
|
// src/historyCmd.ts
|
|
3958
|
-
import
|
|
4195
|
+
import fs6 from "fs";
|
|
3959
4196
|
import path3 from "path";
|
|
3960
4197
|
import chalk6 from "chalk";
|
|
3961
4198
|
function runHistoryCommand(opts2 = {}) {
|
|
@@ -3964,11 +4201,11 @@ function runHistoryCommand(opts2 = {}) {
|
|
|
3964
4201
|
console.log(chalk6.bold(`
|
|
3965
4202
|
pyre history \u2014 Past ${days} Days Trend
|
|
3966
4203
|
`));
|
|
3967
|
-
if (!
|
|
4204
|
+
if (!fs6.existsSync(dir)) {
|
|
3968
4205
|
console.log(chalk6.yellow(` No logs directory found at ${dir}`));
|
|
3969
4206
|
return;
|
|
3970
4207
|
}
|
|
3971
|
-
const files =
|
|
4208
|
+
const files = fs6.readdirSync(dir).filter((f) => f.startsWith("pyre-log-") && f.endsWith(".csv"));
|
|
3972
4209
|
if (files.length === 0) {
|
|
3973
4210
|
console.log(chalk6.yellow(` No CSV logs found in ${dir}`));
|
|
3974
4211
|
return;
|
|
@@ -3980,7 +4217,7 @@ function runHistoryCommand(opts2 = {}) {
|
|
|
3980
4217
|
for (const file of files.sort()) {
|
|
3981
4218
|
const fullPath = path3.join(dir, file);
|
|
3982
4219
|
try {
|
|
3983
|
-
const content =
|
|
4220
|
+
const content = fs6.readFileSync(fullPath, "utf-8");
|
|
3984
4221
|
const lines = content.split("\n").slice(1);
|
|
3985
4222
|
for (const line of lines) {
|
|
3986
4223
|
if (!line.trim()) continue;
|
|
@@ -4015,7 +4252,7 @@ function runHistoryCommand(opts2 = {}) {
|
|
|
4015
4252
|
}
|
|
4016
4253
|
|
|
4017
4254
|
// src/diffCmd.ts
|
|
4018
|
-
import
|
|
4255
|
+
import fs7 from "fs";
|
|
4019
4256
|
import chalk7 from "chalk";
|
|
4020
4257
|
function runDiffCommand(file1, file2) {
|
|
4021
4258
|
console.log(chalk7.bold(`
|
|
@@ -4025,8 +4262,8 @@ function runDiffCommand(file1, file2) {
|
|
|
4025
4262
|
console.log(chalk7.dim(` Snapshot B: ${file2}
|
|
4026
4263
|
`));
|
|
4027
4264
|
try {
|
|
4028
|
-
const dataA = JSON.parse(
|
|
4029
|
-
const dataB = JSON.parse(
|
|
4265
|
+
const dataA = JSON.parse(fs7.readFileSync(file1, "utf-8"));
|
|
4266
|
+
const dataB = JSON.parse(fs7.readFileSync(file2, "utf-8"));
|
|
4030
4267
|
const diffNum = (label, valA, valB, unit = "") => {
|
|
4031
4268
|
const delta = valB - valA;
|
|
4032
4269
|
const sign = delta > 0 ? "+" : "";
|
|
@@ -4050,7 +4287,7 @@ function runDiffCommand(file1, file2) {
|
|
|
4050
4287
|
}
|
|
4051
4288
|
|
|
4052
4289
|
// src/xbar.ts
|
|
4053
|
-
import
|
|
4290
|
+
import fs8 from "fs";
|
|
4054
4291
|
import path4 from "path";
|
|
4055
4292
|
import os from "os";
|
|
4056
4293
|
import chalk8 from "chalk";
|
|
@@ -4091,8 +4328,8 @@ echo "Open Dashboard | bash='${nodePath}' param1='${pyrePath}' param2='live' ter
|
|
|
4091
4328
|
const pluginDir = path4.join(os.homedir(), "Library", "Application Support", "xbar", "plugins");
|
|
4092
4329
|
const filename = "pyre-monitor.2s.sh";
|
|
4093
4330
|
const outPath = path4.join(process.cwd(), filename);
|
|
4094
|
-
|
|
4095
|
-
|
|
4331
|
+
fs8.writeFileSync(outPath, script);
|
|
4332
|
+
fs8.chmodSync(outPath, "755");
|
|
4096
4333
|
console.log(chalk8.green(`Generated xbar plugin script at ${outPath}`));
|
|
4097
4334
|
console.log(`To install, move it to your xbar plugins directory:`);
|
|
4098
4335
|
console.log(chalk8.cyan(` mv ${filename} "${pluginDir}/"`));
|
|
@@ -4130,36 +4367,37 @@ async function runFleetCommand(hosts) {
|
|
|
4130
4367
|
|
|
4131
4368
|
// src/index.ts
|
|
4132
4369
|
var config2 = readConfig();
|
|
4133
|
-
var pkg = JSON.parse(
|
|
4370
|
+
var pkg = JSON.parse(fs9.readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
|
|
4134
4371
|
var program = new Command();
|
|
4135
4372
|
program.name("pyre").version(pkg.version).description("Mac system monitoring CLI: interactive live dashboard, stats, graphs, export, packet monitor, battery predictor").option("-j, --json", "Output as JSON").option("--html", "Output as HTML").option("--md", "Output as Markdown").option("-c, --csv", "Output as CSV").option("-t, --tsv", "Output as TSV").option("--detailed", "Include detailed system info and sensor readings").option("--theme <name>", "Default theme for live mode (default, dracula, cyberpunk, monochrome, nord, gruvbox)", config2.theme).option("--interval <seconds>", "Refresh interval for live mode", String(config2.interval)).option("--once", "Show a single static snapshot instead of live feed").option("--out <file>", "Also write the snapshot output to a file (--once/--json/--csv/--tsv modes)").option("--export-dir <dir>", "Directory used for live-mode snapshot exports and logs", config2.exportDir).option("--log", "Start continuous CSV logging immediately when live mode starts").option("--tree", "Show process tree view instead of flat list").option("--sort <key>", "Sort processes by: cpu, mem, pid, user, command, state, threads, runtime", config2.sortMode).option("--packets", "Include packet monitor panel in output").option("--limit <n>", "Max number of processes to include in --once/--json/--csv/--tsv snapshots (0 = all)", "10").option("--alert-cpu <pct>", "CPU usage alert threshold (default: 90)", String(config2.cpuAlertPct)).option("--alert-temp <c>", "CPU temperature alert threshold in Celsius (default: 95)", String(config2.tempAlertC)).option("--minimal", "Render minimal macmon-style glanceable view (CPU, GPU, Memory, Temp)").option("--compact", "Launch pyre ui in compact widget mode").option("--ontop", "Keep pyre ui window always on top").option("--temp-unit <unit>", "Temperature display unit: c or f (default: c)", "c").option("--p2p-host <host>", "P2P host address (server: bind address, client: server address)").option("--p2p-port <port>", "P2P port number", "9876").option("--p2p-password <password>", "Password for P2P authentication").option("--p2p-tls", "Enable TLS encryption for P2P connections").option("--p2p-cert <file>", "TLS certificate file (PEM) for P2P server").option("--p2p-key <file>", "TLS private key file (PEM) for P2P server").option("--p2p-ca <file>", "TLS CA certificate file (PEM) for P2P client").option("--p2p-insecure", "Skip TLS certificate verification (client only)").option("--p2p-rate-limit <n>", "Max auth attempts per IP per minute (default: 5)", "5").option("--p2p-allow <ips>", "Comma-separated list of allowed IPs (empty = all)").option("--p2p-deny <ips>", "Comma-separated list of denied IPs").option("--p2p-audit-log <dir>", "Directory for P2P audit logs").option("--p2p-hmac-key <key>", "HMAC key for message signing (default: derived from password)").option("--since <range>", "Time range for anomalies digest (e.g. 1d, 7d, yesterday)").option("--plain", "Output in plain accessible text without ANSI colors or line-drawing characters").option("--a11y", "Alias for --plain").option("-d, --duration <seconds>", "Duration in seconds for stress/bench commands", "30").option("--cores <n>", "Number of worker threads for stress command").option("--exit-code", "Exit non-zero if system health checks trigger alerts (for check command)").option("--no-wizard", "Skip first-run interactive setup wizard").option("--install", "Install pyre web as a background launchd agent").option("--uninstall", "Uninstall pyre web launchd agent").option("--port <port>", "Port number for web server mode", "3000");
|
|
4136
4373
|
program.addHelpText("after", `
|
|
4137
|
-
Commands:
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4374
|
+
Commands:
|
|
4375
|
+
live Interactive live dashboard
|
|
4376
|
+
check One-line plain-English health summary
|
|
4377
|
+
pipe Continuous newline-delimited JSON stream for scripting
|
|
4378
|
+
stress Synthetic CPU/GPU load generator
|
|
4379
|
+
ui Launch the native macOS UI dashboard
|
|
4380
|
+
web Serve an auto-refreshing live web portal
|
|
4381
|
+
ssh <host> Stream live stats from a remote Mac over SSH
|
|
4382
|
+
fleet <host1> [host2]... Multi-host live dashboard monitoring multiple Macs
|
|
4383
|
+
bench <cmd> Log CPU, memory, network, power draw, and estimate kWh cost
|
|
4384
|
+
benchmark Run a 1-minute CPU benchmark calculating digits of PI
|
|
4385
|
+
anomalies Compute z-score anomalies and print plain-language digest
|
|
4386
|
+
doctor Run system diagnostics
|
|
4387
|
+
extensions System Extensions inspector
|
|
4388
|
+
brew Homebrew health panel
|
|
4389
|
+
update Check for pyre-cli updates
|
|
4390
|
+
profile <save|load|list> Atomic configuration profile management
|
|
4391
|
+
config <show|reset> View or reset persistent configuration file
|
|
4392
|
+
history Graph historical resource trends from CSV logs
|
|
4393
|
+
diff <file1> <file2> Compare two saved snapshot files side-by-side
|
|
4394
|
+
info Concise hardware summary
|
|
4395
|
+
completions <shell> Generate shell auto-completion scripts
|
|
4396
|
+
xbar Generate an xbar / SwiftBar menu bar plugin script
|
|
4397
|
+
p2p <server|connect> Start a P2P server or connect to one
|
|
4398
|
+
server Print commands for starting P2P server/client
|
|
4399
|
+
blender Track active Blender render jobs and their progress
|
|
4400
|
+
`);
|
|
4163
4401
|
program.parse(process.argv);
|
|
4164
4402
|
var opts = program.opts();
|
|
4165
4403
|
if (opts.plain || opts.a11y) {
|
|
@@ -4219,11 +4457,11 @@ async function runServerCommand() {
|
|
|
4219
4457
|
console.log();
|
|
4220
4458
|
}
|
|
4221
4459
|
async function main() {
|
|
4222
|
-
const { runSetupWizardIfNeeded } = await import("./wizard-
|
|
4460
|
+
const { runSetupWizardIfNeeded } = await import("./wizard-Z3RGA3VB.js");
|
|
4223
4461
|
await runSetupWizardIfNeeded(!opts.wizard);
|
|
4224
4462
|
const cmd = program.args[0];
|
|
4225
4463
|
if (cmd === "check") {
|
|
4226
|
-
const { runCheckCommand } = await import("./checkCmd-
|
|
4464
|
+
const { runCheckCommand } = await import("./checkCmd-T7O4KXXA.js");
|
|
4227
4465
|
await runCheckCommand({
|
|
4228
4466
|
exitCode: !!opts.exitCode,
|
|
4229
4467
|
cpuAlertPct: config2.cpuAlertPct,
|
|
@@ -4233,7 +4471,7 @@ async function main() {
|
|
|
4233
4471
|
return;
|
|
4234
4472
|
}
|
|
4235
4473
|
if (cmd === "pipe") {
|
|
4236
|
-
const { runPipeCommand } = await import("./pipeCmd-
|
|
4474
|
+
const { runPipeCommand } = await import("./pipeCmd-TYLJHQ5B.js");
|
|
4237
4475
|
const intervalSec = parseFloat(opts.interval) || 1;
|
|
4238
4476
|
const samples = parseInt(opts.samples, 10) || 0;
|
|
4239
4477
|
await runPipeCommand({
|
|
@@ -4260,6 +4498,31 @@ async function main() {
|
|
|
4260
4498
|
await printExtensionsReport();
|
|
4261
4499
|
return;
|
|
4262
4500
|
}
|
|
4501
|
+
if (cmd === "blender") {
|
|
4502
|
+
const { collectBlenderRenders } = await import("./blender-7UT5J7IP.js");
|
|
4503
|
+
const renders = await collectBlenderRenders();
|
|
4504
|
+
if (!renders.length) {
|
|
4505
|
+
console.log(chalk10.dim("No active Blender renders detected."));
|
|
4506
|
+
return;
|
|
4507
|
+
}
|
|
4508
|
+
const lines = [];
|
|
4509
|
+
lines.push(chalk10.bold(" Blender Render Tracker"));
|
|
4510
|
+
lines.push("");
|
|
4511
|
+
for (const r of renders) {
|
|
4512
|
+
const statusColor = r.status === "rendering" ? chalk10.green : r.status === "finalizing" ? chalk10.yellow : r.status === "loading" ? chalk10.cyan : chalk10.dim;
|
|
4513
|
+
lines.push(` PID: ${r.pid}`);
|
|
4514
|
+
lines.push(` Blend File: ${r.blendFile}`);
|
|
4515
|
+
lines.push(` Engine: ${r.renderEngine}`);
|
|
4516
|
+
lines.push(` Frames: ${r.currentFrame} / ${r.totalFrames} (${r.completionPercent}%)`);
|
|
4517
|
+
lines.push(` Elapsed: ${r.elapsedSec}s`);
|
|
4518
|
+
lines.push(` Status: ${statusColor(r.status)}`);
|
|
4519
|
+
lines.push(` Output: ${r.outputPath}`);
|
|
4520
|
+
if (r.sampleCount !== void 0) lines.push(` Samples: ${r.sampleCount}`);
|
|
4521
|
+
lines.push("");
|
|
4522
|
+
}
|
|
4523
|
+
console.log(lines.join("\n"));
|
|
4524
|
+
return;
|
|
4525
|
+
}
|
|
4263
4526
|
if (cmd === "brew") {
|
|
4264
4527
|
const { printBrewHealthReport } = await import("./brewHealth-F2BXF5M6.js");
|
|
4265
4528
|
await printBrewHealthReport();
|
|
@@ -4273,7 +4536,7 @@ async function main() {
|
|
|
4273
4536
|
if (cmd === "profile") {
|
|
4274
4537
|
const sub = program.args[1];
|
|
4275
4538
|
const name = program.args[2];
|
|
4276
|
-
const { saveProfile, loadProfile, listProfiles } = await import("./config-
|
|
4539
|
+
const { saveProfile, loadProfile, listProfiles } = await import("./config-KS5ZZ3VN.js");
|
|
4277
4540
|
if (sub === "save" && name) {
|
|
4278
4541
|
saveProfile(name);
|
|
4279
4542
|
console.log(chalk10.green(` \u2714 Profile '${name}' saved successfully.`));
|
|
@@ -4364,7 +4627,7 @@ async function main() {
|
|
|
4364
4627
|
const cfg = readConfig();
|
|
4365
4628
|
console.log(JSON.stringify(cfg, null, 2));
|
|
4366
4629
|
} else if (sub === "reset") {
|
|
4367
|
-
|
|
4630
|
+
fs9.rmSync(CONFIG_FILE, { force: true });
|
|
4368
4631
|
console.log(chalk10.green("Config reset to defaults"));
|
|
4369
4632
|
} else {
|
|
4370
4633
|
console.log("Usage: pyre config <show|reset>");
|
|
@@ -4380,7 +4643,7 @@ async function main() {
|
|
|
4380
4643
|
return;
|
|
4381
4644
|
}
|
|
4382
4645
|
if (cmd === "doctor") {
|
|
4383
|
-
const { runDoctor, printDoctorReport } = await import("./doctor-
|
|
4646
|
+
const { runDoctor, printDoctorReport } = await import("./doctor-UV7TWIHX.js");
|
|
4384
4647
|
const checks = await runDoctor();
|
|
4385
4648
|
printDoctorReport(checks);
|
|
4386
4649
|
return;
|
|
@@ -4461,7 +4724,7 @@ async function main() {
|
|
|
4461
4724
|
else output = formatTable(data, { width: process.stdout.columns || 80, sortBy: opts.sort, treeView: opts.tree ?? config2.treeView, minimal: !!opts.minimal, visible: { packets: opts.packets ? true : void 0 } });
|
|
4462
4725
|
console.log(output);
|
|
4463
4726
|
if (opts.out) {
|
|
4464
|
-
|
|
4727
|
+
fs9.writeFileSync(opts.out, stripAnsi(output));
|
|
4465
4728
|
console.log(chalk10.dim(`
|
|
4466
4729
|
Saved to ${opts.out}`));
|
|
4467
4730
|
}
|
|
@@ -4645,7 +4908,7 @@ async function runP2PConnect() {
|
|
|
4645
4908
|
}
|
|
4646
4909
|
async function runInfoCommand() {
|
|
4647
4910
|
const data = await collectAll({ detailed: true });
|
|
4648
|
-
const { getDisplayInfo, getTimeMachineStatus } = await import("./collectors-
|
|
4911
|
+
const { getDisplayInfo, getTimeMachineStatus } = await import("./collectors-JB4OGUEF.js");
|
|
4649
4912
|
const displays = await getDisplayInfo();
|
|
4650
4913
|
const tmStatus = await getTimeMachineStatus();
|
|
4651
4914
|
const lines = [];
|
|
@@ -4759,8 +5022,8 @@ async function runWebCommand() {
|
|
|
4759
5022
|
} else if (url === "/setup") {
|
|
4760
5023
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
4761
5024
|
const indexPath = path5.join(process.cwd(), "index.html");
|
|
4762
|
-
if (
|
|
4763
|
-
res.end(
|
|
5025
|
+
if (fs9.existsSync(indexPath)) {
|
|
5026
|
+
res.end(fs9.readFileSync(indexPath, "utf-8"));
|
|
4764
5027
|
} else {
|
|
4765
5028
|
res.end(getDashboardHtml());
|
|
4766
5029
|
}
|
|
@@ -4916,7 +5179,7 @@ app.delegate = delegate
|
|
|
4916
5179
|
app.run()
|
|
4917
5180
|
`;
|
|
4918
5181
|
const scriptPath = path5.join(os2.tmpdir(), "pyre-ui.swift");
|
|
4919
|
-
|
|
5182
|
+
fs9.writeFileSync(scriptPath, swiftScript);
|
|
4920
5183
|
const { spawn } = await import("child_process");
|
|
4921
5184
|
console.log(chalk10.cyan(`
|
|
4922
5185
|
Launching native UI window...`));
|
|
@@ -4934,8 +5197,8 @@ async function runBenchCommand(benchCmd) {
|
|
|
4934
5197
|
const { spawn } = await import("child_process");
|
|
4935
5198
|
const logDir = opts.exportDir || "./pyre-exports";
|
|
4936
5199
|
const file = path5.join(logDir, `pyre-bench-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.csv`);
|
|
4937
|
-
if (!
|
|
4938
|
-
const stream =
|
|
5200
|
+
if (!fs9.existsSync(logDir)) fs9.mkdirSync(logDir, { recursive: true });
|
|
5201
|
+
const stream = fs9.createWriteStream(file, { flags: "a" });
|
|
4939
5202
|
stream.write("timestamp,cpu_usage,mem_usage_percent,temp_c,net_rx_bytes,net_tx_bytes,net_rx_packets,net_tx_packets,connections,thermal_state,power_watts\n");
|
|
4940
5203
|
console.log(chalk10.bold(`
|
|
4941
5204
|
pyre bench`));
|