pyre-cli 8.2.0 → 9.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{anomaliesCmd-TO52YY2B.js → anomaliesCmd-25OFKYPK.js} +1 -1
- package/dist/{brewHealth-6NE37NAU.js → brewHealth-F2BXF5M6.js} +1 -1
- package/dist/{chunk-GLH7EKAX.js → chunk-FH5WDJBF.js} +1 -1
- package/dist/{chunk-KBKXU2ZV.js → chunk-QOLZ7T32.js} +35 -19
- package/dist/{collectors-4VYRJVRO.js → collectors-VACLPHRP.js} +2 -2
- package/dist/{doctor-LJQAABZ4.js → doctor-L6FZYA7F.js} +1 -1
- package/dist/{extensions-QCFSVCL5.js → extensions-DVA5YLGH.js} +1 -1
- package/dist/index.js +435 -92
- package/package.json +1 -1
- package/src/anomaliesCmd.ts +1 -1
- package/src/formatters/render.ts +127 -38
- package/src/formatters/types.ts +6 -0
- package/src/live/input.ts +149 -5
- package/src/live/render.ts +161 -11
- package/src/live/state.ts +7 -0
- package/src/live/types.ts +1 -1
- package/src/monitors/collectors.ts +33 -14
- package/src/monitors/run.ts +1 -1
- package/src/monitors/smc.ts +6 -9
- package/src/splash.ts +1 -1
package/dist/index.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
collectAll
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-QOLZ7T32.js";
|
|
5
5
|
import {
|
|
6
6
|
CONFIG_FILE,
|
|
7
7
|
readConfig,
|
|
8
8
|
writeConfig
|
|
9
9
|
} from "./chunk-RVGKEJXT.js";
|
|
10
|
-
import "./chunk-
|
|
10
|
+
import "./chunk-FH5WDJBF.js";
|
|
11
11
|
|
|
12
12
|
// src/index.ts
|
|
13
|
-
import
|
|
13
|
+
import fs9 from "fs";
|
|
14
14
|
import os3 from "os";
|
|
15
|
-
import
|
|
15
|
+
import path6 from "path";
|
|
16
16
|
import readline2 from "readline";
|
|
17
17
|
import { Command } from "commander";
|
|
18
18
|
import chalk12 from "chalk";
|
|
@@ -575,17 +575,19 @@ function sortProcesses(processes, sortBy) {
|
|
|
575
575
|
function pctColor(v) {
|
|
576
576
|
return v > 80 ? chalk2.red : v > 50 ? chalk2.yellow : chalk2.dim;
|
|
577
577
|
}
|
|
578
|
-
function processTableLines(processes, contentWidth, borderAccent = THEMES.default.border) {
|
|
578
|
+
function processTableLines(processes, contentWidth, borderAccent = THEMES.default.border, selectedIdx = -1) {
|
|
579
579
|
const pidW = 8, ppidW = 8, userW = 12, cpuW = 8, memW = 8, stateW = 8, thrW = 6, rtW = 10;
|
|
580
580
|
const cmdW = Math.max(10, contentWidth - pidW - ppidW - userW - cpuW - memW - stateW - thrW - rtW - 8);
|
|
581
581
|
const head = chalk2.bold.hex("#f8f8f2").bgHex("#44475a")(" PID ") + chalk2.bold.hex("#f8f8f2").bgHex("#44475a")(" PPID ") + chalk2.bold.hex("#f8f8f2").bgHex("#44475a")(" USER ") + chalk2.bold.hex("#f8f8f2").bgHex("#44475a")(" CPU% ") + chalk2.bold.hex("#f8f8f2").bgHex("#44475a")(" MEM% ") + chalk2.bold.hex("#f8f8f2").bgHex("#44475a")(" STATE ") + chalk2.bold.hex("#f8f8f2").bgHex("#44475a")(" THREADS") + chalk2.bold.hex("#f8f8f2").bgHex("#44475a")(" RUNTIME ") + chalk2.bold.hex("#f8f8f2").bgHex("#44475a")(" COMMAND".padEnd(cmdW + 1));
|
|
582
|
-
const rows = processes.map((p) => {
|
|
582
|
+
const rows = processes.map((p, idx) => {
|
|
583
|
+
const isSelected = idx === selectedIdx;
|
|
583
584
|
const cpuStr = pctColor(p.cpu)(`${p.cpu.toFixed(1)}`.padEnd(cpuW));
|
|
584
585
|
const memStr = pctColor(p.mem)(`${p.mem.toFixed(1)}`.padEnd(memW));
|
|
585
586
|
const stateStr = chalk2.dim(p.state.padEnd(stateW));
|
|
586
587
|
const thrStr = String(p.threads).padEnd(thrW);
|
|
587
588
|
const rtStr = formatRuntime(p.runtime).padEnd(rtW);
|
|
588
|
-
|
|
589
|
+
const line = String(p.pid).padEnd(pidW) + String(p.ppid).padEnd(ppidW) + truncatePlain(p.user, userW - 1).padEnd(userW) + cpuStr + memStr + stateStr + thrStr + rtStr + truncatePlain(p.command, cmdW);
|
|
590
|
+
return isSelected ? chalk2.bgCyan.black(line) : line;
|
|
589
591
|
});
|
|
590
592
|
return [head, borderAccent("\u2500".repeat(contentWidth)), ...rows];
|
|
591
593
|
}
|
|
@@ -654,7 +656,7 @@ function diskTableLines(disks, contentWidth, borderAccent = THEMES.default.borde
|
|
|
654
656
|
});
|
|
655
657
|
return [head, borderAccent("\u2500".repeat(contentWidth)), ...rows];
|
|
656
658
|
}
|
|
657
|
-
function detailPanelTitle(panel2) {
|
|
659
|
+
function detailPanelTitle(panel2, trackedPid) {
|
|
658
660
|
const map = {
|
|
659
661
|
cpu: "CPU",
|
|
660
662
|
mem: "Memory",
|
|
@@ -666,7 +668,7 @@ function detailPanelTitle(panel2) {
|
|
|
666
668
|
packets: "Packets",
|
|
667
669
|
tasks: "Tasks",
|
|
668
670
|
disk: "Disk",
|
|
669
|
-
process: "Processes"
|
|
671
|
+
process: trackedPid !== void 0 && trackedPid !== null ? `Processes (Following PID ${trackedPid})` : "Processes"
|
|
670
672
|
};
|
|
671
673
|
return map[panel2] || panel2;
|
|
672
674
|
}
|
|
@@ -727,11 +729,43 @@ function activeDetailLines(data, activePanel, width, opts2 = {}) {
|
|
|
727
729
|
case "process": {
|
|
728
730
|
const filtered = opts2.filter ? data.processes.filter((p) => p.command.toLowerCase().includes(opts2.filter.toLowerCase())) : data.processes;
|
|
729
731
|
const sorted = sortProcesses(filtered, opts2.sortBy ?? "cpu");
|
|
730
|
-
|
|
732
|
+
let selectedIdx = opts2.selectedProcessIndex ?? -1;
|
|
733
|
+
const isTracking = opts2.trackedPid !== void 0 && opts2.trackedPid !== null;
|
|
734
|
+
if (isTracking) {
|
|
735
|
+
const trackedIdx = sorted.findIndex((p) => p.pid === opts2.trackedPid);
|
|
736
|
+
if (trackedIdx >= 0) {
|
|
737
|
+
selectedIdx = trackedIdx;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
const trackingLines = [];
|
|
741
|
+
if (isTracking) {
|
|
742
|
+
const tp = sorted.find((p) => p.pid === opts2.trackedPid);
|
|
743
|
+
if (tp) {
|
|
744
|
+
const cpuColor = tp.cpu > 50 ? chalk2.red.bold : tp.cpu > 20 ? chalk2.yellow : chalk2.green;
|
|
745
|
+
const memColor = tp.mem > 50 ? chalk2.red.bold : tp.mem > 20 ? chalk2.yellow : chalk2.green;
|
|
746
|
+
trackingLines.push(chalk2.bgHex("#1a1a2e").hex("#e94560").bold(` \u25C9 FOLLOWING PID ${tp.pid} \u2014 ${truncatePlain(tp.command, Math.max(10, contentWidth - 30))} `));
|
|
747
|
+
trackingLines.push(
|
|
748
|
+
` ${chalk2.bold("CPU:")} ${cpuColor(tp.cpu.toFixed(1) + "%")} ${chalk2.bold("MEM:")} ${memColor(tp.mem.toFixed(1) + "%")} ${chalk2.bold("State:")} ${chalk2.dim(tp.state)} ${chalk2.bold("Threads:")} ${tp.threads} ${chalk2.bold("Runtime:")} ${formatRuntime(tp.runtime)} ${chalk2.bold("User:")} ${tp.user}`
|
|
749
|
+
);
|
|
750
|
+
trackingLines.push(chalk2.dim(` Full: ${tp.command}`));
|
|
751
|
+
trackingLines.push(theme.border("\u2500".repeat(contentWidth)));
|
|
752
|
+
} else {
|
|
753
|
+
trackingLines.push(chalk2.yellow(` \u25C9 PID ${opts2.trackedPid} exited \u2014 no longer in process list`));
|
|
754
|
+
trackingLines.push(theme.border("\u2500".repeat(contentWidth)));
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
const availableRows = Math.max(1, (opts2.processLimit || sorted.length) - trackingLines.length);
|
|
758
|
+
let offset = 0;
|
|
759
|
+
if (selectedIdx >= 0) {
|
|
760
|
+
if (selectedIdx >= availableRows) {
|
|
761
|
+
offset = selectedIdx - availableRows + 1;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
const visible = sorted.slice(offset, offset + availableRows);
|
|
731
765
|
if (opts2.treeView) {
|
|
732
|
-
return buildProcessTree(
|
|
766
|
+
return [...trackingLines, ...buildProcessTree(visible)];
|
|
733
767
|
}
|
|
734
|
-
return processTableLines(
|
|
768
|
+
return [...trackingLines, ...processTableLines(visible, width - 4, theme.border, selectedIdx - offset)];
|
|
735
769
|
}
|
|
736
770
|
case "anomalies": {
|
|
737
771
|
if (!opts2.anomalyHistory || opts2.anomalyHistory.length === 0) return ["No anomalies detected yet."];
|
|
@@ -875,18 +909,50 @@ function btopProcBox(data, width, targetHeight, theme, opts2) {
|
|
|
875
909
|
const sorted = sortProcesses(filtered, opts2.sortBy ?? "cpu");
|
|
876
910
|
const headerRowOverhead = 2;
|
|
877
911
|
const tableHeaderOverhead = 2;
|
|
878
|
-
const
|
|
879
|
-
const
|
|
912
|
+
const isTracking = opts2.trackedPid !== void 0 && opts2.trackedPid !== null;
|
|
913
|
+
const trackingLines = [];
|
|
914
|
+
if (isTracking) {
|
|
915
|
+
const tp = sorted.find((p) => p.pid === opts2.trackedPid);
|
|
916
|
+
if (tp) {
|
|
917
|
+
const cpuColor = tp.cpu > 50 ? chalk2.red.bold : tp.cpu > 20 ? chalk2.yellow : chalk2.green;
|
|
918
|
+
const memColor = tp.mem > 50 ? chalk2.red.bold : tp.mem > 20 ? chalk2.yellow : chalk2.green;
|
|
919
|
+
trackingLines.push(chalk2.bgHex("#1a1a2e").hex("#e94560").bold(` \u25C9 PID ${tp.pid} ${truncatePlain(tp.command, Math.max(6, contentWidth - 20))} `));
|
|
920
|
+
trackingLines.push(
|
|
921
|
+
` ${cpuColor("CPU " + tp.cpu.toFixed(1) + "%")} ${memColor("MEM " + tp.mem.toFixed(1) + "%")} ${chalk2.dim(tp.state)} THR:${tp.threads} ${formatRuntime(tp.runtime)}`
|
|
922
|
+
);
|
|
923
|
+
trackingLines.push(theme.border("\u2500".repeat(contentWidth)));
|
|
924
|
+
} else {
|
|
925
|
+
trackingLines.push(chalk2.yellow(` \u25C9 PID ${opts2.trackedPid} exited`));
|
|
926
|
+
trackingLines.push(theme.border("\u2500".repeat(contentWidth)));
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
const availableRows = Math.max(1, targetHeight - headerRowOverhead - tableHeaderOverhead - trackingLines.length);
|
|
930
|
+
let selectedIdx = opts2.selectedProcessIndex ?? -1;
|
|
931
|
+
if (isTracking) {
|
|
932
|
+
const trackedIdx = sorted.findIndex((p) => p.pid === opts2.trackedPid);
|
|
933
|
+
if (trackedIdx >= 0) {
|
|
934
|
+
selectedIdx = trackedIdx;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
let offset = 0;
|
|
938
|
+
if (selectedIdx >= 0) {
|
|
939
|
+
if (selectedIdx >= availableRows) {
|
|
940
|
+
offset = selectedIdx - availableRows + 1;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
const visible = sorted.slice(offset, offset + availableRows);
|
|
880
944
|
const pidW = 6, userW = 7, thrW = 3, memW = 6, cpuW = 6;
|
|
881
945
|
const fixedW = pidW + userW + thrW + memW + cpuW + 5;
|
|
882
946
|
const cmdW = Math.max(8, contentWidth - fixedW);
|
|
883
947
|
const head = chalk2.bold("PID".padEnd(pidW)) + " " + chalk2.bold("PROGRAM".padEnd(cmdW)) + " " + chalk2.bold("USER".padEnd(userW)) + " " + chalk2.bold("THR".padStart(thrW)) + " " + chalk2.bold("MEM%".padStart(memW)) + " " + chalk2.bold("CPU%".padStart(cpuW));
|
|
884
948
|
if (opts2.treeView) {
|
|
885
|
-
const treeLines = buildProcessTree(
|
|
886
|
-
|
|
887
|
-
|
|
949
|
+
const treeLines = buildProcessTree(visible);
|
|
950
|
+
let rightTitle2 = opts2.filter ? `filter: "${opts2.filter}"` : `tree \xB7 sort: ${opts2.sortBy || "cpu"}`;
|
|
951
|
+
if (isTracking) rightTitle2 += ` \xB7 following PID ${opts2.trackedPid}`;
|
|
952
|
+
return panel("P proc", [...trackingLines, head, theme.border("\u2500".repeat(contentWidth)), ...treeLines], width, theme.process, theme.border, targetHeight - 2, rightTitle2);
|
|
888
953
|
}
|
|
889
|
-
const rows =
|
|
954
|
+
const rows = visible.map((p, idx) => {
|
|
955
|
+
const isSelected = offset + idx === selectedIdx;
|
|
890
956
|
const cpuVal = p.cpu.toFixed(1) + "%";
|
|
891
957
|
const cpuStr = p.cpu > 50 ? chalk2.red.bold(cpuVal.padStart(cpuW)) : p.cpu > 20 ? chalk2.yellow(cpuVal.padStart(cpuW)) : chalk2.green(cpuVal.padStart(cpuW));
|
|
892
958
|
const memStr = (p.mem.toFixed(1) + "%").padStart(memW);
|
|
@@ -894,13 +960,17 @@ function btopProcBox(data, width, targetHeight, theme, opts2) {
|
|
|
894
960
|
const thrStr = String(p.threads).padStart(thrW);
|
|
895
961
|
const pidStr = String(p.pid).padEnd(pidW);
|
|
896
962
|
const cmdStr = truncatePlain(p.command, cmdW).padEnd(cmdW);
|
|
897
|
-
|
|
963
|
+
const line = `${pidStr} ${cmdStr} ${userStr} ${thrStr} ${memStr} ${cpuStr}`;
|
|
964
|
+
return isSelected ? chalk2.bgCyan.black(line) : line;
|
|
898
965
|
});
|
|
899
|
-
const lines = [head, theme.border("\u2500".repeat(contentWidth)), ...rows];
|
|
966
|
+
const lines = [...trackingLines, head, theme.border("\u2500".repeat(contentWidth)), ...rows];
|
|
900
967
|
if (rows.length === 0) {
|
|
901
968
|
lines.push(chalk2.dim(`No processes match "${opts2.filter || ""}"`));
|
|
902
969
|
}
|
|
903
|
-
|
|
970
|
+
let rightTitle = opts2.filter ? `filter: "${opts2.filter}"` : `sort: ${opts2.sortBy || "cpu"}`;
|
|
971
|
+
if (isTracking) {
|
|
972
|
+
rightTitle += ` \xB7 following PID ${opts2.trackedPid}`;
|
|
973
|
+
}
|
|
904
974
|
return panel("P proc", lines, width, theme.process, theme.border, targetHeight - 2, rightTitle);
|
|
905
975
|
}
|
|
906
976
|
function formatTable(data, opts2 = {}) {
|
|
@@ -921,24 +991,24 @@ function formatTable(data, opts2 = {}) {
|
|
|
921
991
|
if (activePanel !== "grid") {
|
|
922
992
|
const lines = activeDetailLines(data, activePanel, width, opts2);
|
|
923
993
|
if (lines) {
|
|
924
|
-
out.push(...panel(detailPanelTitle(activePanel), lines, width, theme[activePanel] ?? theme.cpu, theme.border));
|
|
994
|
+
out.push(...panel(detailPanelTitle(activePanel, opts2.trackedPid), lines, width, theme[activePanel] ?? theme.cpu, theme.border));
|
|
925
995
|
}
|
|
926
|
-
return out.join("\n");
|
|
927
|
-
}
|
|
928
|
-
out.push(...btopCpuBox(data, width, theme, opts2));
|
|
929
|
-
out.push("");
|
|
930
|
-
if (width >= 85) {
|
|
931
|
-
const leftColW = Math.max(32, Math.floor(width * 0.38));
|
|
932
|
-
const rightColW = width - leftColW - 2;
|
|
933
|
-
const leftLines = btopLeftColumn(data, leftColW, theme, opts2);
|
|
934
|
-
const procLines = btopProcBox(data, rightColW, leftLines.length, theme, opts2);
|
|
935
|
-
out.push(...hstack([leftLines, procLines], 2, width));
|
|
936
996
|
} else {
|
|
937
|
-
|
|
938
|
-
out.push(...leftLines);
|
|
997
|
+
out.push(...btopCpuBox(data, width, theme, opts2));
|
|
939
998
|
out.push("");
|
|
940
|
-
|
|
941
|
-
|
|
999
|
+
if (width >= 85) {
|
|
1000
|
+
const leftColW = Math.max(32, Math.floor(width * 0.38));
|
|
1001
|
+
const rightColW = width - leftColW - 2;
|
|
1002
|
+
const leftLines = btopLeftColumn(data, leftColW, theme, opts2);
|
|
1003
|
+
const procLines = btopProcBox(data, rightColW, leftLines.length, theme, opts2);
|
|
1004
|
+
out.push(...hstack([leftLines, procLines], 2, width));
|
|
1005
|
+
} else {
|
|
1006
|
+
const leftLines = btopLeftColumn(data, width, theme, opts2);
|
|
1007
|
+
out.push(...leftLines);
|
|
1008
|
+
out.push("");
|
|
1009
|
+
const procLines = btopProcBox(data, width, 12, theme, opts2);
|
|
1010
|
+
out.push(...procLines);
|
|
1011
|
+
}
|
|
942
1012
|
}
|
|
943
1013
|
return out.join("\n");
|
|
944
1014
|
}
|
|
@@ -2311,6 +2381,7 @@ var History = class _History {
|
|
|
2311
2381
|
|
|
2312
2382
|
// src/live/state.ts
|
|
2313
2383
|
var SIGNAL_OPTIONS = ["SIGTERM", "SIGKILL", "SIGINT", "SIGHUP", "SIGSTOP", "SIGCONT"];
|
|
2384
|
+
var MENU_OPTIONS = ["Resume Dashboard", "Settings (Customizer)", "Readme", "Credits", "Quit pyre"];
|
|
2314
2385
|
var config = readConfig();
|
|
2315
2386
|
var state = {
|
|
2316
2387
|
intervalHandle: null,
|
|
@@ -2350,6 +2421,10 @@ var state = {
|
|
|
2350
2421
|
termHeight: process.stdout.rows || 24,
|
|
2351
2422
|
sortMode: config.sortMode,
|
|
2352
2423
|
processFilter: "",
|
|
2424
|
+
processSelectionIndex: 0,
|
|
2425
|
+
processScrollOffset: 0,
|
|
2426
|
+
trackedPid: null,
|
|
2427
|
+
inspectingProcess: null,
|
|
2353
2428
|
inputMode: null,
|
|
2354
2429
|
inputBuffer: "",
|
|
2355
2430
|
targetPid: "",
|
|
@@ -2415,7 +2490,8 @@ var state = {
|
|
|
2415
2490
|
anomalyHistory: [],
|
|
2416
2491
|
splashEnabled: config.splashEnabled,
|
|
2417
2492
|
splashColorScheme: config.splashColorScheme,
|
|
2418
|
-
splashAnimation: config.splashAnimation
|
|
2493
|
+
splashAnimation: config.splashAnimation,
|
|
2494
|
+
readmeScrollOffset: 0
|
|
2419
2495
|
};
|
|
2420
2496
|
function setStatus(msg, ms = 3e3) {
|
|
2421
2497
|
state.statusMessage = msg;
|
|
@@ -2570,6 +2646,9 @@ function writeLogRow(data) {
|
|
|
2570
2646
|
|
|
2571
2647
|
// src/live/render.ts
|
|
2572
2648
|
import { execFile, exec } from "child_process";
|
|
2649
|
+
import fs3 from "fs";
|
|
2650
|
+
import path3 from "path";
|
|
2651
|
+
import { fileURLToPath } from "url";
|
|
2573
2652
|
import chalk4 from "chalk";
|
|
2574
2653
|
|
|
2575
2654
|
// src/anomalies.ts
|
|
@@ -2679,6 +2758,9 @@ function tableCacheParams() {
|
|
|
2679
2758
|
return [
|
|
2680
2759
|
state.sortMode,
|
|
2681
2760
|
state.processFilter || "",
|
|
2761
|
+
state.processSelectionIndex,
|
|
2762
|
+
state.trackedPid || "",
|
|
2763
|
+
state.inspectingProcess ? state.inspectingProcess.pid : "",
|
|
2682
2764
|
limit,
|
|
2683
2765
|
state.currentTheme,
|
|
2684
2766
|
state.activePanel,
|
|
@@ -2697,8 +2779,7 @@ function graphsCacheParams() {
|
|
|
2697
2779
|
}
|
|
2698
2780
|
function processRowBudget() {
|
|
2699
2781
|
let overlayRows = 0;
|
|
2700
|
-
if (state.inputMode === "
|
|
2701
|
-
else if (state.inputMode === "signal") overlayRows = 6;
|
|
2782
|
+
if (state.inputMode === "signal") overlayRows = 6;
|
|
2702
2783
|
else if (state.inputMode === "filter" || state.inputMode === "kill" || state.inputMode === "p2p") overlayRows = 3;
|
|
2703
2784
|
if (state.activePanel === "process") {
|
|
2704
2785
|
return Math.max(10, state.termHeight - 12 - overlayRows);
|
|
@@ -2799,12 +2880,39 @@ function render() {
|
|
|
2799
2880
|
writeFrame(out);
|
|
2800
2881
|
return;
|
|
2801
2882
|
}
|
|
2883
|
+
if (state.inputMode === "menu") {
|
|
2884
|
+
writeFrame(renderSplashMenu(cols, rows));
|
|
2885
|
+
return;
|
|
2886
|
+
}
|
|
2887
|
+
if (state.inputMode === "readme") {
|
|
2888
|
+
writeFrame(renderReadme(cols, rows));
|
|
2889
|
+
return;
|
|
2890
|
+
}
|
|
2891
|
+
if (state.inputMode === "credits") {
|
|
2892
|
+
writeFrame(renderCredits(cols, rows));
|
|
2893
|
+
return;
|
|
2894
|
+
}
|
|
2802
2895
|
const lines = [];
|
|
2803
2896
|
const theme = THEMES[state.currentTheme] || THEMES.default;
|
|
2804
2897
|
const params = tableCacheParams();
|
|
2805
2898
|
const anomalies = detectAnomalies(state.lastData, state.history);
|
|
2899
|
+
if (anomalies.length > 0) {
|
|
2900
|
+
for (const a of anomalies) {
|
|
2901
|
+
const now = /* @__PURE__ */ new Date();
|
|
2902
|
+
const alertWithTime = { ...a, timestamp: now };
|
|
2903
|
+
const exists = state.anomalyHistory.some(
|
|
2904
|
+
(h) => h.metric === a.metric && Math.abs(h.zScore - a.zScore) < 0.1 && now.getTime() - (h.timestamp?.getTime() || 0) < 1e4
|
|
2905
|
+
);
|
|
2906
|
+
if (!exists) {
|
|
2907
|
+
state.anomalyHistory.unshift(alertWithTime);
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
if (state.anomalyHistory.length > 100) {
|
|
2911
|
+
state.anomalyHistory.length = 100;
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2806
2914
|
const anomalyCacheKey = anomalies.map((a) => `${a.metric}:${a.zScore.toFixed(2)}`).join(",");
|
|
2807
|
-
const cacheParams = params + "|" + anomalyCacheKey;
|
|
2915
|
+
const cacheParams = params + "|" + anomalyCacheKey + "|" + state.anomalyHistory.length;
|
|
2808
2916
|
const tableStr = _tableDataDirty || state.lastData !== _cachedTableData || cacheParams !== _cachedTableParams ? (() => {
|
|
2809
2917
|
const out = formatTable(state.lastData, {
|
|
2810
2918
|
width: state.termWidth,
|
|
@@ -2820,7 +2928,10 @@ function render() {
|
|
|
2820
2928
|
tempUnit: state.tempUnit,
|
|
2821
2929
|
history: state.history,
|
|
2822
2930
|
graphMode: state.graphMode,
|
|
2823
|
-
panelLayout: state.panelLayout
|
|
2931
|
+
panelLayout: state.panelLayout,
|
|
2932
|
+
selectedProcessIndex: state.processSelectionIndex,
|
|
2933
|
+
trackedPid: state.trackedPid,
|
|
2934
|
+
inspectProcess: state.inspectingProcess
|
|
2824
2935
|
});
|
|
2825
2936
|
_cachedTableData = state.lastData;
|
|
2826
2937
|
_cachedTableParams = cacheParams;
|
|
@@ -2854,14 +2965,20 @@ function render() {
|
|
|
2854
2965
|
const footerHeight = 3;
|
|
2855
2966
|
const targetBodyHeight = Math.max(1, state.termHeight - footerHeight);
|
|
2856
2967
|
if (state.inputMode === "customizer") {
|
|
2857
|
-
const preOverlayCount = bodyLines.length;
|
|
2858
|
-
const availableForOverlay = targetBodyHeight - preOverlayCount;
|
|
2859
2968
|
const totalItems = state.CUSTOMIZER_OPTIONS.length;
|
|
2860
2969
|
const headerLines = 1;
|
|
2861
|
-
const blankLines =
|
|
2862
|
-
const
|
|
2970
|
+
const blankLines = 1;
|
|
2971
|
+
const idealOverlayHeight = totalItems + headerLines + blankLines;
|
|
2972
|
+
const minOverlayHeight = Math.min(10 + headerLines + blankLines, Math.floor(targetBodyHeight / 2));
|
|
2973
|
+
const overlayHeight = Math.min(idealOverlayHeight, Math.max(minOverlayHeight, Math.floor(targetBodyHeight * 0.6)));
|
|
2974
|
+
const maxBodyForTable = Math.max(3, targetBodyHeight - overlayHeight);
|
|
2975
|
+
if (bodyLines.length > maxBodyForTable) {
|
|
2976
|
+
bodyLines.length = maxBodyForTable;
|
|
2977
|
+
}
|
|
2978
|
+
const availableForOverlay = targetBodyHeight - bodyLines.length;
|
|
2979
|
+
const maxVisibleItems = Math.max(1, availableForOverlay - headerLines - blankLines);
|
|
2863
2980
|
const scrollOffset = totalItems > maxVisibleItems ? Math.max(0, Math.min(state.customizerIndex, totalItems - maxVisibleItems)) : 0;
|
|
2864
|
-
|
|
2981
|
+
bodyLines.push("");
|
|
2865
2982
|
bodyLines.push(...renderCustomizerOverlay(scrollOffset, maxVisibleItems).split("\n"));
|
|
2866
2983
|
} else if (state.inputMode === "filter") {
|
|
2867
2984
|
bodyLines.push(chalk4.cyan(` Filter processes: ${state.inputBuffer}_`));
|
|
@@ -2986,11 +3103,98 @@ function checkAlerts(data) {
|
|
|
2986
3103
|
state.alerted = false;
|
|
2987
3104
|
}
|
|
2988
3105
|
}
|
|
3106
|
+
function renderSplashMenu(cols, rows) {
|
|
3107
|
+
const out = [];
|
|
3108
|
+
const logo = [
|
|
3109
|
+
chalk4.hex("#ff6004").bold(" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557"),
|
|
3110
|
+
chalk4.hex("#ff8004").bold(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D"),
|
|
3111
|
+
chalk4.hex("#ffa004").bold(" \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u255A\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2557 "),
|
|
3112
|
+
chalk4.hex("#ffc004").bold(" \u2588\u2588\u2554\u2550\u2550\u2550\u255D \u255A\u2588\u2588\u2554\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u255D "),
|
|
3113
|
+
chalk4.hex("#ffe004").bold(" \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557"),
|
|
3114
|
+
chalk4.hex("#ffff40").bold(" \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D")
|
|
3115
|
+
];
|
|
3116
|
+
const menuTop = Math.max(0, Math.floor(rows / 2) - 8);
|
|
3117
|
+
for (let i = 0; i < menuTop; i++) out.push("");
|
|
3118
|
+
const padding = Math.max(0, Math.floor((cols - 50) / 2));
|
|
3119
|
+
const padStr = " ".repeat(padding);
|
|
3120
|
+
for (const line of logo) {
|
|
3121
|
+
out.push(padStr + line);
|
|
3122
|
+
}
|
|
3123
|
+
out.push("");
|
|
3124
|
+
out.push(padStr + chalk4.dim(" system monitor v9.0.0"));
|
|
3125
|
+
out.push("");
|
|
3126
|
+
out.push("");
|
|
3127
|
+
for (let i = 0; i < MENU_OPTIONS.length; i++) {
|
|
3128
|
+
const isSelected = i === state.menuSelectionIndex;
|
|
3129
|
+
const opt = MENU_OPTIONS[i];
|
|
3130
|
+
const prefix = isSelected ? chalk4.cyan.bold(" \u25B6 ") : " ";
|
|
3131
|
+
const text = isSelected ? chalk4.bgCyan.black(` ${opt} `) : chalk4.dim(` ${opt} `);
|
|
3132
|
+
out.push(padStr + " " + prefix + text);
|
|
3133
|
+
}
|
|
3134
|
+
out.push("");
|
|
3135
|
+
out.push("");
|
|
3136
|
+
out.push(padStr + chalk4.dim(" Use \u2191/\u2193 to navigate, Enter to select"));
|
|
3137
|
+
while (out.length < rows) out.push("");
|
|
3138
|
+
return out.map((l) => fitVisible(l, cols));
|
|
3139
|
+
}
|
|
3140
|
+
var cachedReadmeLines = null;
|
|
3141
|
+
function getReadmeLines() {
|
|
3142
|
+
if (cachedReadmeLines) return cachedReadmeLines;
|
|
3143
|
+
try {
|
|
3144
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
3145
|
+
const __dirname = path3.dirname(__filename);
|
|
3146
|
+
const rootDir = path3.resolve(__dirname, "..", "..");
|
|
3147
|
+
const readmePath = path3.join(rootDir, "README.md");
|
|
3148
|
+
const content = fs3.readFileSync(readmePath, "utf8");
|
|
3149
|
+
cachedReadmeLines = content.split("\n");
|
|
3150
|
+
return cachedReadmeLines;
|
|
3151
|
+
} catch (err) {
|
|
3152
|
+
return [chalk4.red("Failed to load README.md")];
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
function renderReadme(cols, rows) {
|
|
3156
|
+
const lines = getReadmeLines();
|
|
3157
|
+
const availableLines = Math.max(1, rows - 2);
|
|
3158
|
+
const startIdx = Math.min(state.readmeScrollOffset, Math.max(0, lines.length - availableLines));
|
|
3159
|
+
state.readmeScrollOffset = startIdx;
|
|
3160
|
+
const content = lines.slice(startIdx, startIdx + availableLines).map((l) => {
|
|
3161
|
+
let out2 = l;
|
|
3162
|
+
if (out2.startsWith("#")) {
|
|
3163
|
+
out2 = chalk4.cyan.bold(out2);
|
|
3164
|
+
} else if (out2.startsWith("```")) {
|
|
3165
|
+
out2 = chalk4.dim(out2);
|
|
3166
|
+
} else if (out2.startsWith("- ") || out2.startsWith("* ")) {
|
|
3167
|
+
out2 = " " + chalk4.yellow(out2.substring(0, 2)) + out2.substring(2);
|
|
3168
|
+
}
|
|
3169
|
+
return out2;
|
|
3170
|
+
});
|
|
3171
|
+
const out = [];
|
|
3172
|
+
const pct = Math.round(startIdx / Math.max(1, lines.length - availableLines) * 100);
|
|
3173
|
+
const title = lines.length > availableLines ? `README (${pct}%)` : "README";
|
|
3174
|
+
out.push(...panel(title, content, cols, chalk4.cyan, chalk4.dim, availableLines));
|
|
3175
|
+
return out;
|
|
3176
|
+
}
|
|
3177
|
+
function renderCredits(cols, rows) {
|
|
3178
|
+
const out = [];
|
|
3179
|
+
const content = [
|
|
3180
|
+
chalk4.bold.hex("#ff6004")("Pyre - The Terminal System Monitor"),
|
|
3181
|
+
"",
|
|
3182
|
+
"Created with \u2764\uFE0F using Node.js",
|
|
3183
|
+
"",
|
|
3184
|
+
chalk4.cyan("Libraries used:"),
|
|
3185
|
+
" - chalk (Terminal styling)",
|
|
3186
|
+
" - systeminformation (System metrics)",
|
|
3187
|
+
"",
|
|
3188
|
+
chalk4.dim("Press Esc or Enter to return to menu.")
|
|
3189
|
+
];
|
|
3190
|
+
out.push(...panel("CREDITS", content, cols, chalk4.magenta, chalk4.dim, rows - 2));
|
|
3191
|
+
return out;
|
|
3192
|
+
}
|
|
2989
3193
|
|
|
2990
3194
|
// src/p2p/server.ts
|
|
2991
3195
|
import net from "net";
|
|
2992
3196
|
import tls from "tls";
|
|
2993
|
-
import
|
|
3197
|
+
import fs4 from "fs";
|
|
2994
3198
|
import crypto2 from "crypto";
|
|
2995
3199
|
import chalk5 from "chalk";
|
|
2996
3200
|
|
|
@@ -3135,9 +3339,9 @@ var P2PServer = class {
|
|
|
3135
3339
|
async start() {
|
|
3136
3340
|
if (this.options.auditLog) {
|
|
3137
3341
|
try {
|
|
3138
|
-
|
|
3342
|
+
fs4.mkdirSync(this.options.auditLog, { recursive: true });
|
|
3139
3343
|
const logFile = `${this.options.auditLog}/p2p-audit.log`;
|
|
3140
|
-
this.auditStream =
|
|
3344
|
+
this.auditStream = fs4.createWriteStream(logFile, { flags: "a" });
|
|
3141
3345
|
} catch {
|
|
3142
3346
|
this.log(chalk5.yellow("Warning: could not open audit log"));
|
|
3143
3347
|
}
|
|
@@ -3146,11 +3350,11 @@ var P2PServer = class {
|
|
|
3146
3350
|
const useTLS = !!(this.options.tlsCert && this.options.tlsKey);
|
|
3147
3351
|
if (useTLS) {
|
|
3148
3352
|
const tlsOptions = {
|
|
3149
|
-
key:
|
|
3150
|
-
cert:
|
|
3353
|
+
key: fs4.readFileSync(this.options.tlsKey),
|
|
3354
|
+
cert: fs4.readFileSync(this.options.tlsCert)
|
|
3151
3355
|
};
|
|
3152
3356
|
if (this.options.tlsCA) {
|
|
3153
|
-
tlsOptions.ca =
|
|
3357
|
+
tlsOptions.ca = fs4.readFileSync(this.options.tlsCA);
|
|
3154
3358
|
}
|
|
3155
3359
|
this.server = tls.createServer(tlsOptions, (socket) => this.onConnection(socket));
|
|
3156
3360
|
} else {
|
|
@@ -3418,7 +3622,7 @@ async function startP2PServer(options) {
|
|
|
3418
3622
|
// src/p2p/client.ts
|
|
3419
3623
|
import net2 from "net";
|
|
3420
3624
|
import tls2 from "tls";
|
|
3421
|
-
import
|
|
3625
|
+
import fs5 from "fs";
|
|
3422
3626
|
import crypto3 from "crypto";
|
|
3423
3627
|
import chalk6 from "chalk";
|
|
3424
3628
|
var P2PClient = class {
|
|
@@ -3446,9 +3650,9 @@ var P2PClient = class {
|
|
|
3446
3650
|
async connect() {
|
|
3447
3651
|
if (this.options.auditLog) {
|
|
3448
3652
|
try {
|
|
3449
|
-
|
|
3653
|
+
fs5.mkdirSync(this.options.auditLog, { recursive: true });
|
|
3450
3654
|
const logFile = `${this.options.auditLog}/p2p-client-audit.log`;
|
|
3451
|
-
this.auditStream =
|
|
3655
|
+
this.auditStream = fs5.createWriteStream(logFile, { flags: "a" });
|
|
3452
3656
|
} catch {
|
|
3453
3657
|
console.log(chalk6.yellow("Warning: could not open audit log"));
|
|
3454
3658
|
}
|
|
@@ -3463,7 +3667,7 @@ var P2PClient = class {
|
|
|
3463
3667
|
rejectUnauthorized: !this.options.tlsInsecure
|
|
3464
3668
|
};
|
|
3465
3669
|
if (this.options.tlsCA) {
|
|
3466
|
-
tlsOptions.ca =
|
|
3670
|
+
tlsOptions.ca = fs5.readFileSync(this.options.tlsCA);
|
|
3467
3671
|
}
|
|
3468
3672
|
this.socket = tls2.connect(tlsOptions, () => {
|
|
3469
3673
|
this.running = true;
|
|
@@ -3746,6 +3950,68 @@ function handleInputModeKey(str, key) {
|
|
|
3746
3950
|
render();
|
|
3747
3951
|
return;
|
|
3748
3952
|
}
|
|
3953
|
+
if (state.inputMode === "menu") {
|
|
3954
|
+
if (isEscKey(key, str)) {
|
|
3955
|
+
state.inputMode = null;
|
|
3956
|
+
render();
|
|
3957
|
+
return;
|
|
3958
|
+
}
|
|
3959
|
+
if (isUpKey(key, str)) {
|
|
3960
|
+
state.menuSelectionIndex = (state.menuSelectionIndex - 1 + MENU_OPTIONS.length) % MENU_OPTIONS.length;
|
|
3961
|
+
render();
|
|
3962
|
+
return;
|
|
3963
|
+
}
|
|
3964
|
+
if (isDownKey(key, str)) {
|
|
3965
|
+
state.menuSelectionIndex = (state.menuSelectionIndex + 1) % MENU_OPTIONS.length;
|
|
3966
|
+
render();
|
|
3967
|
+
return;
|
|
3968
|
+
}
|
|
3969
|
+
if (isEnterKey(key, str) || str === " ") {
|
|
3970
|
+
const option = MENU_OPTIONS[state.menuSelectionIndex];
|
|
3971
|
+
switch (option) {
|
|
3972
|
+
case "Resume Dashboard":
|
|
3973
|
+
state.inputMode = null;
|
|
3974
|
+
break;
|
|
3975
|
+
case "Settings (Customizer)":
|
|
3976
|
+
state.inputMode = "customizer";
|
|
3977
|
+
state.customizerIndex = 0;
|
|
3978
|
+
break;
|
|
3979
|
+
case "Readme":
|
|
3980
|
+
state.inputMode = "readme";
|
|
3981
|
+
break;
|
|
3982
|
+
case "Credits":
|
|
3983
|
+
state.inputMode = "credits";
|
|
3984
|
+
break;
|
|
3985
|
+
case "Quit pyre":
|
|
3986
|
+
process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
3987
|
+
process.exit(0);
|
|
3988
|
+
}
|
|
3989
|
+
render();
|
|
3990
|
+
return;
|
|
3991
|
+
}
|
|
3992
|
+
render();
|
|
3993
|
+
return;
|
|
3994
|
+
}
|
|
3995
|
+
if (state.inputMode === "readme" || state.inputMode === "credits") {
|
|
3996
|
+
if (isEscKey(key, str) || isEnterKey(key, str)) {
|
|
3997
|
+
state.inputMode = "menu";
|
|
3998
|
+
render();
|
|
3999
|
+
return;
|
|
4000
|
+
}
|
|
4001
|
+
if (state.inputMode === "readme") {
|
|
4002
|
+
if (isUpKey(key, str)) {
|
|
4003
|
+
state.readmeScrollOffset = Math.max(0, state.readmeScrollOffset - 1);
|
|
4004
|
+
render();
|
|
4005
|
+
return;
|
|
4006
|
+
}
|
|
4007
|
+
if (isDownKey(key, str)) {
|
|
4008
|
+
state.readmeScrollOffset += 1;
|
|
4009
|
+
render();
|
|
4010
|
+
return;
|
|
4011
|
+
}
|
|
4012
|
+
}
|
|
4013
|
+
return;
|
|
4014
|
+
}
|
|
3749
4015
|
if (state.inputMode === "signal") {
|
|
3750
4016
|
if (isUpKey(key, str)) {
|
|
3751
4017
|
const idx = state.SIGNAL_OPTIONS.indexOf(state.inputBuffer);
|
|
@@ -4167,11 +4433,72 @@ async function startLive(opts2, splashPromise) {
|
|
|
4167
4433
|
case "tab":
|
|
4168
4434
|
cycleTab(1);
|
|
4169
4435
|
break;
|
|
4436
|
+
case "up":
|
|
4437
|
+
if (state.lastData?.processes?.length) {
|
|
4438
|
+
state.trackedPid = null;
|
|
4439
|
+
state.processSelectionIndex = Math.max(0, state.processSelectionIndex - 1);
|
|
4440
|
+
render();
|
|
4441
|
+
}
|
|
4442
|
+
break;
|
|
4443
|
+
case "down":
|
|
4444
|
+
if (state.lastData?.processes?.length) {
|
|
4445
|
+
state.trackedPid = null;
|
|
4446
|
+
const count = state.lastData.processes.length;
|
|
4447
|
+
state.processSelectionIndex = Math.min(count - 1, state.processSelectionIndex + 1);
|
|
4448
|
+
render();
|
|
4449
|
+
}
|
|
4450
|
+
break;
|
|
4451
|
+
case "space":
|
|
4452
|
+
case " ":
|
|
4453
|
+
if (state.lastData?.processes?.length && state.processSelectionIndex >= 0) {
|
|
4454
|
+
const sorted = state.lastData.processes;
|
|
4455
|
+
const proc = sorted[state.processSelectionIndex];
|
|
4456
|
+
if (proc) {
|
|
4457
|
+
if (state.trackedPid === proc.pid) {
|
|
4458
|
+
state.trackedPid = null;
|
|
4459
|
+
setStatus(`Stopped following PID ${proc.pid}`);
|
|
4460
|
+
} else {
|
|
4461
|
+
state.trackedPid = proc.pid;
|
|
4462
|
+
setStatus(`Following PID ${proc.pid} (${proc.command})`);
|
|
4463
|
+
}
|
|
4464
|
+
render();
|
|
4465
|
+
}
|
|
4466
|
+
}
|
|
4467
|
+
break;
|
|
4468
|
+
case "return":
|
|
4469
|
+
case "enter":
|
|
4470
|
+
if (state.inspectingProcess) {
|
|
4471
|
+
state.inspectingProcess = null;
|
|
4472
|
+
render();
|
|
4473
|
+
} else if (state.lastData?.processes?.length && state.processSelectionIndex >= 0) {
|
|
4474
|
+
const sorted = state.lastData.processes;
|
|
4475
|
+
const proc = sorted[state.processSelectionIndex];
|
|
4476
|
+
if (proc) {
|
|
4477
|
+
state.inspectingProcess = proc;
|
|
4478
|
+
state.trackedPid = proc.pid;
|
|
4479
|
+
setStatus(`Inspecting & following PID ${proc.pid}`);
|
|
4480
|
+
render();
|
|
4481
|
+
}
|
|
4482
|
+
}
|
|
4483
|
+
break;
|
|
4170
4484
|
case "escape":
|
|
4171
4485
|
case "esc":
|
|
4172
|
-
state.
|
|
4173
|
-
|
|
4174
|
-
|
|
4486
|
+
if (state.inspectingProcess) {
|
|
4487
|
+
state.inspectingProcess = null;
|
|
4488
|
+
render();
|
|
4489
|
+
} else if (state.trackedPid !== null) {
|
|
4490
|
+
state.trackedPid = null;
|
|
4491
|
+
setStatus("Stopped following process");
|
|
4492
|
+
render();
|
|
4493
|
+
} else if (state.activePanel !== "grid") {
|
|
4494
|
+
state.activePanel = "grid";
|
|
4495
|
+
setStatus("Grid view");
|
|
4496
|
+
render();
|
|
4497
|
+
} else {
|
|
4498
|
+
state.inputMode = "menu";
|
|
4499
|
+
state.menuSelectionIndex = 0;
|
|
4500
|
+
render();
|
|
4501
|
+
}
|
|
4175
4502
|
break;
|
|
4176
4503
|
default:
|
|
4177
4504
|
if (str && str.length === 1) {
|
|
@@ -4188,7 +4515,7 @@ async function startLive(opts2, splashPromise) {
|
|
|
4188
4515
|
process.once("SIGINT", () => stopLive());
|
|
4189
4516
|
}
|
|
4190
4517
|
function tabKeyToId(str) {
|
|
4191
|
-
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" };
|
|
4518
|
+
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" };
|
|
4192
4519
|
return map[str] ?? null;
|
|
4193
4520
|
}
|
|
4194
4521
|
function cycleTab(direction) {
|
|
@@ -4206,6 +4533,10 @@ function handleNormalMouse(seq) {
|
|
|
4206
4533
|
const cy = seq.charCodeAt(5) - 32;
|
|
4207
4534
|
if (cb === 0 || cb === 1) {
|
|
4208
4535
|
handleMouseClick(cy, cx);
|
|
4536
|
+
} else if (cb === 64) {
|
|
4537
|
+
scrollProcessSelection(-1);
|
|
4538
|
+
} else if (cb === 65) {
|
|
4539
|
+
scrollProcessSelection(1);
|
|
4209
4540
|
}
|
|
4210
4541
|
}
|
|
4211
4542
|
function handleSGRMouse(seq) {
|
|
@@ -4216,6 +4547,18 @@ function handleSGRMouse(seq) {
|
|
|
4216
4547
|
const cy = parseInt(match[3]) - 1;
|
|
4217
4548
|
if (button === 0 || button === 1) {
|
|
4218
4549
|
handleMouseClick(cy, cx);
|
|
4550
|
+
} else if (button === 64) {
|
|
4551
|
+
scrollProcessSelection(-1);
|
|
4552
|
+
} else if (button === 65) {
|
|
4553
|
+
scrollProcessSelection(1);
|
|
4554
|
+
}
|
|
4555
|
+
}
|
|
4556
|
+
function scrollProcessSelection(dir) {
|
|
4557
|
+
if (state.lastData?.processes?.length) {
|
|
4558
|
+
state.trackedPid = null;
|
|
4559
|
+
const count = state.lastData.processes.length;
|
|
4560
|
+
state.processSelectionIndex = Math.max(0, Math.min(count - 1, state.processSelectionIndex + dir));
|
|
4561
|
+
render();
|
|
4219
4562
|
}
|
|
4220
4563
|
}
|
|
4221
4564
|
function handleMouseClick(y, x) {
|
|
@@ -4591,13 +4934,13 @@ async function showSplash(config3) {
|
|
|
4591
4934
|
process.stdin.setRawMode(true);
|
|
4592
4935
|
process.stdin.resume();
|
|
4593
4936
|
process.stdin.on("data", onEnter);
|
|
4594
|
-
setTimeout(onEnter,
|
|
4937
|
+
setTimeout(onEnter, 1500);
|
|
4595
4938
|
});
|
|
4596
4939
|
}
|
|
4597
4940
|
|
|
4598
4941
|
// src/historyCmd.ts
|
|
4599
|
-
import
|
|
4600
|
-
import
|
|
4942
|
+
import fs6 from "fs";
|
|
4943
|
+
import path4 from "path";
|
|
4601
4944
|
import chalk8 from "chalk";
|
|
4602
4945
|
function runHistoryCommand(opts2 = {}) {
|
|
4603
4946
|
const days = opts2.days || 7;
|
|
@@ -4605,11 +4948,11 @@ function runHistoryCommand(opts2 = {}) {
|
|
|
4605
4948
|
console.log(chalk8.bold(`
|
|
4606
4949
|
pyre history \u2014 Past ${days} Days Trend
|
|
4607
4950
|
`));
|
|
4608
|
-
if (!
|
|
4951
|
+
if (!fs6.existsSync(dir)) {
|
|
4609
4952
|
console.log(chalk8.yellow(` No logs directory found at ${dir}`));
|
|
4610
4953
|
return;
|
|
4611
4954
|
}
|
|
4612
|
-
const files =
|
|
4955
|
+
const files = fs6.readdirSync(dir).filter((f) => f.startsWith("pyre-log-") && f.endsWith(".csv"));
|
|
4613
4956
|
if (files.length === 0) {
|
|
4614
4957
|
console.log(chalk8.yellow(` No CSV logs found in ${dir}`));
|
|
4615
4958
|
return;
|
|
@@ -4619,9 +4962,9 @@ function runHistoryCommand(opts2 = {}) {
|
|
|
4619
4962
|
const memPoints = [];
|
|
4620
4963
|
const tempPoints = [];
|
|
4621
4964
|
for (const file of files.sort()) {
|
|
4622
|
-
const fullPath =
|
|
4965
|
+
const fullPath = path4.join(dir, file);
|
|
4623
4966
|
try {
|
|
4624
|
-
const content =
|
|
4967
|
+
const content = fs6.readFileSync(fullPath, "utf-8");
|
|
4625
4968
|
const lines = content.split("\n").slice(1);
|
|
4626
4969
|
for (const line of lines) {
|
|
4627
4970
|
if (!line.trim()) continue;
|
|
@@ -4656,7 +4999,7 @@ function runHistoryCommand(opts2 = {}) {
|
|
|
4656
4999
|
}
|
|
4657
5000
|
|
|
4658
5001
|
// src/diffCmd.ts
|
|
4659
|
-
import
|
|
5002
|
+
import fs7 from "fs";
|
|
4660
5003
|
import chalk9 from "chalk";
|
|
4661
5004
|
function runDiffCommand(file1, file2) {
|
|
4662
5005
|
console.log(chalk9.bold(`
|
|
@@ -4666,8 +5009,8 @@ function runDiffCommand(file1, file2) {
|
|
|
4666
5009
|
console.log(chalk9.dim(` Snapshot B: ${file2}
|
|
4667
5010
|
`));
|
|
4668
5011
|
try {
|
|
4669
|
-
const dataA = JSON.parse(
|
|
4670
|
-
const dataB = JSON.parse(
|
|
5012
|
+
const dataA = JSON.parse(fs7.readFileSync(file1, "utf-8"));
|
|
5013
|
+
const dataB = JSON.parse(fs7.readFileSync(file2, "utf-8"));
|
|
4671
5014
|
const diffNum = (label, valA, valB, unit = "") => {
|
|
4672
5015
|
const delta = valB - valA;
|
|
4673
5016
|
const sign = delta > 0 ? "+" : "";
|
|
@@ -4691,8 +5034,8 @@ function runDiffCommand(file1, file2) {
|
|
|
4691
5034
|
}
|
|
4692
5035
|
|
|
4693
5036
|
// src/xbar.ts
|
|
4694
|
-
import
|
|
4695
|
-
import
|
|
5037
|
+
import fs8 from "fs";
|
|
5038
|
+
import path5 from "path";
|
|
4696
5039
|
import os2 from "os";
|
|
4697
5040
|
import chalk10 from "chalk";
|
|
4698
5041
|
function generateXbarPlugin() {
|
|
@@ -4729,11 +5072,11 @@ echo "\u{1F525} C:\${CPU}% M:\${MEM}% T:\${TEMP}\xB0C | font=Menlo"
|
|
|
4729
5072
|
echo "---"
|
|
4730
5073
|
echo "Open Dashboard | bash='${nodePath}' param1='${pyrePath}' param2='live' terminal=true"
|
|
4731
5074
|
`;
|
|
4732
|
-
const pluginDir =
|
|
5075
|
+
const pluginDir = path5.join(os2.homedir(), "Library", "Application Support", "xbar", "plugins");
|
|
4733
5076
|
const filename = "pyre-monitor.2s.sh";
|
|
4734
|
-
const outPath =
|
|
4735
|
-
|
|
4736
|
-
|
|
5077
|
+
const outPath = path5.join(process.cwd(), filename);
|
|
5078
|
+
fs8.writeFileSync(outPath, script);
|
|
5079
|
+
fs8.chmodSync(outPath, "755");
|
|
4737
5080
|
console.log(chalk10.green(`Generated xbar plugin script at ${outPath}`));
|
|
4738
5081
|
console.log(`To install, move it to your xbar plugins directory:`);
|
|
4739
5082
|
console.log(chalk10.cyan(` mv ${filename} "${pluginDir}/"`));
|
|
@@ -4769,7 +5112,7 @@ function runFleetCommand(hosts) {
|
|
|
4769
5112
|
|
|
4770
5113
|
// src/index.ts
|
|
4771
5114
|
var config2 = readConfig();
|
|
4772
|
-
var pkg = JSON.parse(
|
|
5115
|
+
var pkg = JSON.parse(fs9.readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
|
|
4773
5116
|
var program = new Command();
|
|
4774
5117
|
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("--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("--webhook-url <url>", "URL to POST to on alert").option("--alert-cmd <cmd>", "Command to execute on alert").option("--port <port>", "Port number for web server mode", "3000");
|
|
4775
5118
|
program.addHelpText("after", `
|
|
@@ -4856,17 +5199,17 @@ async function runServerCommand() {
|
|
|
4856
5199
|
async function main() {
|
|
4857
5200
|
const cmd = program.args[0];
|
|
4858
5201
|
if (cmd === "anomalies") {
|
|
4859
|
-
const { runAnomaliesDigest } = await import("./anomaliesCmd-
|
|
5202
|
+
const { runAnomaliesDigest } = await import("./anomaliesCmd-25OFKYPK.js");
|
|
4860
5203
|
runAnomaliesDigest({ since: opts.since || "7d", dir: opts.exportDir });
|
|
4861
5204
|
return;
|
|
4862
5205
|
}
|
|
4863
5206
|
if (cmd === "extensions") {
|
|
4864
|
-
const { printExtensionsReport } = await import("./extensions-
|
|
5207
|
+
const { printExtensionsReport } = await import("./extensions-DVA5YLGH.js");
|
|
4865
5208
|
await printExtensionsReport();
|
|
4866
5209
|
return;
|
|
4867
5210
|
}
|
|
4868
5211
|
if (cmd === "brew") {
|
|
4869
|
-
const { printBrewHealthReport } = await import("./brewHealth-
|
|
5212
|
+
const { printBrewHealthReport } = await import("./brewHealth-F2BXF5M6.js");
|
|
4870
5213
|
await printBrewHealthReport();
|
|
4871
5214
|
return;
|
|
4872
5215
|
}
|
|
@@ -4953,7 +5296,7 @@ async function main() {
|
|
|
4953
5296
|
const cfg = readConfig();
|
|
4954
5297
|
console.log(JSON.stringify(cfg, null, 2));
|
|
4955
5298
|
} else if (sub === "reset") {
|
|
4956
|
-
|
|
5299
|
+
fs9.rmSync(CONFIG_FILE, { force: true });
|
|
4957
5300
|
console.log(chalk12.green("Config reset to defaults"));
|
|
4958
5301
|
} else {
|
|
4959
5302
|
console.log("Usage: pyre config <show|reset>");
|
|
@@ -4969,7 +5312,7 @@ async function main() {
|
|
|
4969
5312
|
return;
|
|
4970
5313
|
}
|
|
4971
5314
|
if (cmd === "doctor") {
|
|
4972
|
-
const { runDoctor, printDoctorReport } = await import("./doctor-
|
|
5315
|
+
const { runDoctor, printDoctorReport } = await import("./doctor-L6FZYA7F.js");
|
|
4973
5316
|
const checks = await runDoctor();
|
|
4974
5317
|
printDoctorReport(checks);
|
|
4975
5318
|
return;
|
|
@@ -5050,7 +5393,7 @@ async function main() {
|
|
|
5050
5393
|
else output = formatTable(data, { width: process.stdout.columns || 80, sortBy: opts.sort, treeView: opts.tree ?? config2.treeView, visible: { packets: opts.packets ? true : void 0 } });
|
|
5051
5394
|
console.log(output);
|
|
5052
5395
|
if (opts.out) {
|
|
5053
|
-
|
|
5396
|
+
fs9.writeFileSync(opts.out, stripAnsi(output));
|
|
5054
5397
|
console.log(chalk12.dim(`
|
|
5055
5398
|
Saved to ${opts.out}`));
|
|
5056
5399
|
}
|
|
@@ -5234,7 +5577,7 @@ async function runP2PConnect() {
|
|
|
5234
5577
|
}
|
|
5235
5578
|
async function runInfoCommand() {
|
|
5236
5579
|
const data = await collectAll({ detailed: true });
|
|
5237
|
-
const { getDisplayInfo, getTimeMachineStatus } = await import("./collectors-
|
|
5580
|
+
const { getDisplayInfo, getTimeMachineStatus } = await import("./collectors-VACLPHRP.js");
|
|
5238
5581
|
const displays = await getDisplayInfo();
|
|
5239
5582
|
const tmStatus = await getTimeMachineStatus();
|
|
5240
5583
|
const lines = [];
|
|
@@ -5340,9 +5683,9 @@ async function runWebCommand() {
|
|
|
5340
5683
|
res.end(getDashboardHtml());
|
|
5341
5684
|
} else if (url === "/setup") {
|
|
5342
5685
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
5343
|
-
const indexPath =
|
|
5344
|
-
if (
|
|
5345
|
-
res.end(
|
|
5686
|
+
const indexPath = path6.join(process.cwd(), "index.html");
|
|
5687
|
+
if (fs9.existsSync(indexPath)) {
|
|
5688
|
+
res.end(fs9.readFileSync(indexPath, "utf-8"));
|
|
5346
5689
|
} else {
|
|
5347
5690
|
res.end(getDashboardHtml());
|
|
5348
5691
|
}
|
|
@@ -5491,8 +5834,8 @@ let delegate = AppDelegate()
|
|
|
5491
5834
|
app.delegate = delegate
|
|
5492
5835
|
app.run()
|
|
5493
5836
|
`;
|
|
5494
|
-
const scriptPath =
|
|
5495
|
-
|
|
5837
|
+
const scriptPath = path6.join(os3.tmpdir(), "pyre-ui.swift");
|
|
5838
|
+
fs9.writeFileSync(scriptPath, swiftScript);
|
|
5496
5839
|
const { spawn } = await import("child_process");
|
|
5497
5840
|
console.log(chalk12.cyan(`
|
|
5498
5841
|
Launching native UI window...`));
|
|
@@ -5509,9 +5852,9 @@ app.run()
|
|
|
5509
5852
|
async function runBenchCommand(benchCmd) {
|
|
5510
5853
|
const { spawn } = await import("child_process");
|
|
5511
5854
|
const logDir = opts.exportDir || "./pyre-exports";
|
|
5512
|
-
const file =
|
|
5513
|
-
if (!
|
|
5514
|
-
const stream =
|
|
5855
|
+
const file = path6.join(logDir, `pyre-bench-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.csv`);
|
|
5856
|
+
if (!fs9.existsSync(logDir)) fs9.mkdirSync(logDir, { recursive: true });
|
|
5857
|
+
const stream = fs9.createWriteStream(file, { flags: "a" });
|
|
5515
5858
|
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");
|
|
5516
5859
|
console.log(chalk12.bold(`
|
|
5517
5860
|
pyre bench`));
|