pyre-cli 9.0.0 → 9.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  collectAll
4
- } from "./chunk-EQZCK2VC.js";
4
+ } from "./chunk-QOLZ7T32.js";
5
5
  import {
6
6
  CONFIG_FILE,
7
7
  readConfig,
@@ -10,9 +10,9 @@ import {
10
10
  import "./chunk-FH5WDJBF.js";
11
11
 
12
12
  // src/index.ts
13
- import fs8 from "fs";
13
+ import fs9 from "fs";
14
14
  import os3 from "os";
15
- import path5 from "path";
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
- return 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);
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
- const limited = opts2.processLimit ? sorted.slice(0, opts2.processLimit) : sorted;
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(limited);
766
+ return [...trackingLines, ...buildProcessTree(visible)];
733
767
  }
734
- return processTableLines(limited, width - 4, theme.border);
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 availableRows = Math.max(1, targetHeight - headerRowOverhead - tableHeaderOverhead);
879
- const limited = sorted.slice(0, availableRows);
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(limited);
886
- const rightTitle2 = opts2.filter ? `filter: "${opts2.filter}"` : `tree \xB7 sort: ${opts2.sortBy || "cpu"}`;
887
- return panel("P proc", [head, theme.border("\u2500".repeat(contentWidth)), ...treeLines], width, theme.process, theme.border, targetHeight - 2, rightTitle2);
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 = limited.map((p) => {
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
- return `${pidStr} ${cmdStr} ${userStr} ${thrStr} ${memStr} ${cpuStr}`;
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
- const rightTitle = opts2.filter ? `filter: "${opts2.filter}"` : `sort: ${opts2.sortBy || "cpu"}`;
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
- const leftLines = btopLeftColumn(data, width, theme, opts2);
938
- out.push(...leftLines);
997
+ out.push(...btopCpuBox(data, width, theme, opts2));
939
998
  out.push("");
940
- const procLines = btopProcBox(data, width, 12, theme, opts2);
941
- out.push(...procLines);
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,9 @@ var state = {
2415
2490
  anomalyHistory: [],
2416
2491
  splashEnabled: config.splashEnabled,
2417
2492
  splashColorScheme: config.splashColorScheme,
2418
- splashAnimation: config.splashAnimation
2493
+ splashAnimation: config.splashAnimation,
2494
+ menuSelectionIndex: 0,
2495
+ readmeScrollOffset: 0
2419
2496
  };
2420
2497
  function setStatus(msg, ms = 3e3) {
2421
2498
  state.statusMessage = msg;
@@ -2570,6 +2647,9 @@ function writeLogRow(data) {
2570
2647
 
2571
2648
  // src/live/render.ts
2572
2649
  import { execFile, exec } from "child_process";
2650
+ import fs3 from "fs";
2651
+ import path3 from "path";
2652
+ import { fileURLToPath } from "url";
2573
2653
  import chalk4 from "chalk";
2574
2654
 
2575
2655
  // src/anomalies.ts
@@ -2679,6 +2759,9 @@ function tableCacheParams() {
2679
2759
  return [
2680
2760
  state.sortMode,
2681
2761
  state.processFilter || "",
2762
+ state.processSelectionIndex,
2763
+ state.trackedPid || "",
2764
+ state.inspectingProcess ? state.inspectingProcess.pid : "",
2682
2765
  limit,
2683
2766
  state.currentTheme,
2684
2767
  state.activePanel,
@@ -2798,12 +2881,39 @@ function render() {
2798
2881
  writeFrame(out);
2799
2882
  return;
2800
2883
  }
2884
+ if (state.inputMode === "menu") {
2885
+ writeFrame(renderSplashMenu(cols, rows));
2886
+ return;
2887
+ }
2888
+ if (state.inputMode === "readme") {
2889
+ writeFrame(renderReadme(cols, rows));
2890
+ return;
2891
+ }
2892
+ if (state.inputMode === "credits") {
2893
+ writeFrame(renderCredits(cols, rows));
2894
+ return;
2895
+ }
2801
2896
  const lines = [];
2802
2897
  const theme = THEMES[state.currentTheme] || THEMES.default;
2803
2898
  const params = tableCacheParams();
2804
2899
  const anomalies = detectAnomalies(state.lastData, state.history);
2900
+ if (anomalies.length > 0) {
2901
+ for (const a of anomalies) {
2902
+ const now = /* @__PURE__ */ new Date();
2903
+ const alertWithTime = { ...a, timestamp: now };
2904
+ const exists = state.anomalyHistory.some(
2905
+ (h) => h.metric === a.metric && Math.abs(h.zScore - a.zScore) < 0.1 && now.getTime() - (h.timestamp?.getTime() || 0) < 1e4
2906
+ );
2907
+ if (!exists) {
2908
+ state.anomalyHistory.unshift(alertWithTime);
2909
+ }
2910
+ }
2911
+ if (state.anomalyHistory.length > 100) {
2912
+ state.anomalyHistory.length = 100;
2913
+ }
2914
+ }
2805
2915
  const anomalyCacheKey = anomalies.map((a) => `${a.metric}:${a.zScore.toFixed(2)}`).join(",");
2806
- const cacheParams = params + "|" + anomalyCacheKey;
2916
+ const cacheParams = params + "|" + anomalyCacheKey + "|" + state.anomalyHistory.length;
2807
2917
  const tableStr = _tableDataDirty || state.lastData !== _cachedTableData || cacheParams !== _cachedTableParams ? (() => {
2808
2918
  const out = formatTable(state.lastData, {
2809
2919
  width: state.termWidth,
@@ -2819,7 +2929,10 @@ function render() {
2819
2929
  tempUnit: state.tempUnit,
2820
2930
  history: state.history,
2821
2931
  graphMode: state.graphMode,
2822
- panelLayout: state.panelLayout
2932
+ panelLayout: state.panelLayout,
2933
+ selectedProcessIndex: state.processSelectionIndex,
2934
+ trackedPid: state.trackedPid,
2935
+ inspectProcess: state.inspectingProcess
2823
2936
  });
2824
2937
  _cachedTableData = state.lastData;
2825
2938
  _cachedTableParams = cacheParams;
@@ -2991,11 +3104,100 @@ function checkAlerts(data) {
2991
3104
  state.alerted = false;
2992
3105
  }
2993
3106
  }
3107
+ function renderSplashMenu(cols, rows) {
3108
+ const out = [];
3109
+ const contentLines = [];
3110
+ const logo = [
3111
+ 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"),
3112
+ 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"),
3113
+ 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 "),
3114
+ 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 "),
3115
+ chalk4.hex("#ffe004").bold("\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557"),
3116
+ chalk4.hex("#ffff40").bold("\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D")
3117
+ ];
3118
+ const menuTop = Math.max(0, Math.floor(rows / 2) - 8);
3119
+ for (let i = 0; i < menuTop; i++) out.push("");
3120
+ for (const line of logo) contentLines.push(line);
3121
+ contentLines.push("");
3122
+ contentLines.push(chalk4.dim("system monitor v9.0.0"));
3123
+ contentLines.push("");
3124
+ contentLines.push("");
3125
+ for (let i = 0; i < MENU_OPTIONS.length; i++) {
3126
+ const isSelected = i === state.menuSelectionIndex;
3127
+ const opt = MENU_OPTIONS[i];
3128
+ const prefix = isSelected ? chalk4.cyan.bold(" \u25B6 ") : " ";
3129
+ const text = isSelected ? chalk4.bgCyan.black(` ${opt} `) : chalk4.dim(` ${opt} `);
3130
+ contentLines.push(prefix + text);
3131
+ }
3132
+ contentLines.push("");
3133
+ contentLines.push("");
3134
+ contentLines.push(chalk4.dim("Use \u2191/\u2193 to navigate, Enter to select"));
3135
+ while (contentLines.length + menuTop < rows) contentLines.push("");
3136
+ const maxWidth = Math.max(...contentLines.map(visLen));
3137
+ const padding = Math.max(0, Math.floor((cols - maxWidth) / 2));
3138
+ const padStr = " ".repeat(padding);
3139
+ const fit = (l) => padStr + fitVisible(l, cols - padding);
3140
+ for (const line of contentLines) out.push(fit(line));
3141
+ return out;
3142
+ }
3143
+ var cachedReadmeLines = null;
3144
+ function getReadmeLines() {
3145
+ if (cachedReadmeLines) return cachedReadmeLines;
3146
+ try {
3147
+ const __filename = fileURLToPath(import.meta.url);
3148
+ const __dirname = path3.dirname(__filename);
3149
+ const rootDir = path3.resolve(__dirname, "..", "..");
3150
+ const readmePath = path3.join(rootDir, "README.md");
3151
+ const content = fs3.readFileSync(readmePath, "utf8");
3152
+ cachedReadmeLines = content.split("\n");
3153
+ return cachedReadmeLines;
3154
+ } catch (err) {
3155
+ return [chalk4.red("Failed to load README.md")];
3156
+ }
3157
+ }
3158
+ function renderReadme(cols, rows) {
3159
+ const lines = getReadmeLines();
3160
+ const availableLines = Math.max(1, rows - 2);
3161
+ const startIdx = Math.min(state.readmeScrollOffset, Math.max(0, lines.length - availableLines));
3162
+ state.readmeScrollOffset = startIdx;
3163
+ const content = lines.slice(startIdx, startIdx + availableLines).map((l) => {
3164
+ let out2 = l;
3165
+ if (out2.startsWith("#")) {
3166
+ out2 = chalk4.cyan.bold(out2);
3167
+ } else if (out2.startsWith("```")) {
3168
+ out2 = chalk4.dim(out2);
3169
+ } else if (out2.startsWith("- ") || out2.startsWith("* ")) {
3170
+ out2 = " " + chalk4.yellow(out2.substring(0, 2)) + out2.substring(2);
3171
+ }
3172
+ return out2;
3173
+ });
3174
+ const out = [];
3175
+ const pct = Math.round(startIdx / Math.max(1, lines.length - availableLines) * 100);
3176
+ const title = lines.length > availableLines ? `README (${pct}%)` : "README";
3177
+ out.push(...panel(title, content, cols, chalk4.cyan, chalk4.dim, availableLines));
3178
+ return out;
3179
+ }
3180
+ function renderCredits(cols, rows) {
3181
+ const out = [];
3182
+ const content = [
3183
+ chalk4.bold.hex("#ff6004")("Pyre - The Terminal System Monitor"),
3184
+ "",
3185
+ "Created with \u2764\uFE0F using Node.js",
3186
+ "",
3187
+ chalk4.cyan("Libraries used:"),
3188
+ " - chalk (Terminal styling)",
3189
+ " - systeminformation (System metrics)",
3190
+ "",
3191
+ chalk4.dim("Press Esc or Enter to return to menu.")
3192
+ ];
3193
+ out.push(...panel("CREDITS", content, cols, chalk4.magenta, chalk4.dim, rows - 2));
3194
+ return out;
3195
+ }
2994
3196
 
2995
3197
  // src/p2p/server.ts
2996
3198
  import net from "net";
2997
3199
  import tls from "tls";
2998
- import fs3 from "fs";
3200
+ import fs4 from "fs";
2999
3201
  import crypto2 from "crypto";
3000
3202
  import chalk5 from "chalk";
3001
3203
 
@@ -3140,9 +3342,9 @@ var P2PServer = class {
3140
3342
  async start() {
3141
3343
  if (this.options.auditLog) {
3142
3344
  try {
3143
- fs3.mkdirSync(this.options.auditLog, { recursive: true });
3345
+ fs4.mkdirSync(this.options.auditLog, { recursive: true });
3144
3346
  const logFile = `${this.options.auditLog}/p2p-audit.log`;
3145
- this.auditStream = fs3.createWriteStream(logFile, { flags: "a" });
3347
+ this.auditStream = fs4.createWriteStream(logFile, { flags: "a" });
3146
3348
  } catch {
3147
3349
  this.log(chalk5.yellow("Warning: could not open audit log"));
3148
3350
  }
@@ -3151,11 +3353,11 @@ var P2PServer = class {
3151
3353
  const useTLS = !!(this.options.tlsCert && this.options.tlsKey);
3152
3354
  if (useTLS) {
3153
3355
  const tlsOptions = {
3154
- key: fs3.readFileSync(this.options.tlsKey),
3155
- cert: fs3.readFileSync(this.options.tlsCert)
3356
+ key: fs4.readFileSync(this.options.tlsKey),
3357
+ cert: fs4.readFileSync(this.options.tlsCert)
3156
3358
  };
3157
3359
  if (this.options.tlsCA) {
3158
- tlsOptions.ca = fs3.readFileSync(this.options.tlsCA);
3360
+ tlsOptions.ca = fs4.readFileSync(this.options.tlsCA);
3159
3361
  }
3160
3362
  this.server = tls.createServer(tlsOptions, (socket) => this.onConnection(socket));
3161
3363
  } else {
@@ -3423,7 +3625,7 @@ async function startP2PServer(options) {
3423
3625
  // src/p2p/client.ts
3424
3626
  import net2 from "net";
3425
3627
  import tls2 from "tls";
3426
- import fs4 from "fs";
3628
+ import fs5 from "fs";
3427
3629
  import crypto3 from "crypto";
3428
3630
  import chalk6 from "chalk";
3429
3631
  var P2PClient = class {
@@ -3451,9 +3653,9 @@ var P2PClient = class {
3451
3653
  async connect() {
3452
3654
  if (this.options.auditLog) {
3453
3655
  try {
3454
- fs4.mkdirSync(this.options.auditLog, { recursive: true });
3656
+ fs5.mkdirSync(this.options.auditLog, { recursive: true });
3455
3657
  const logFile = `${this.options.auditLog}/p2p-client-audit.log`;
3456
- this.auditStream = fs4.createWriteStream(logFile, { flags: "a" });
3658
+ this.auditStream = fs5.createWriteStream(logFile, { flags: "a" });
3457
3659
  } catch {
3458
3660
  console.log(chalk6.yellow("Warning: could not open audit log"));
3459
3661
  }
@@ -3468,7 +3670,7 @@ var P2PClient = class {
3468
3670
  rejectUnauthorized: !this.options.tlsInsecure
3469
3671
  };
3470
3672
  if (this.options.tlsCA) {
3471
- tlsOptions.ca = fs4.readFileSync(this.options.tlsCA);
3673
+ tlsOptions.ca = fs5.readFileSync(this.options.tlsCA);
3472
3674
  }
3473
3675
  this.socket = tls2.connect(tlsOptions, () => {
3474
3676
  this.running = true;
@@ -3751,6 +3953,68 @@ function handleInputModeKey(str, key) {
3751
3953
  render();
3752
3954
  return;
3753
3955
  }
3956
+ if (state.inputMode === "menu") {
3957
+ if (isEscKey(key, str)) {
3958
+ state.inputMode = null;
3959
+ render();
3960
+ return;
3961
+ }
3962
+ if (isUpKey(key, str)) {
3963
+ state.menuSelectionIndex = (state.menuSelectionIndex - 1 + MENU_OPTIONS.length) % MENU_OPTIONS.length;
3964
+ render();
3965
+ return;
3966
+ }
3967
+ if (isDownKey(key, str)) {
3968
+ state.menuSelectionIndex = (state.menuSelectionIndex + 1) % MENU_OPTIONS.length;
3969
+ render();
3970
+ return;
3971
+ }
3972
+ if (isEnterKey(key, str) || str === " ") {
3973
+ const option = MENU_OPTIONS[state.menuSelectionIndex];
3974
+ switch (option) {
3975
+ case "Resume Dashboard":
3976
+ state.inputMode = null;
3977
+ break;
3978
+ case "Settings (Customizer)":
3979
+ state.inputMode = "customizer";
3980
+ state.customizerIndex = 0;
3981
+ break;
3982
+ case "Readme":
3983
+ state.inputMode = "readme";
3984
+ break;
3985
+ case "Credits":
3986
+ state.inputMode = "credits";
3987
+ break;
3988
+ case "Quit pyre":
3989
+ process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
3990
+ process.exit(0);
3991
+ }
3992
+ render();
3993
+ return;
3994
+ }
3995
+ render();
3996
+ return;
3997
+ }
3998
+ if (state.inputMode === "readme" || state.inputMode === "credits") {
3999
+ if (isEscKey(key, str) || isEnterKey(key, str)) {
4000
+ state.inputMode = "menu";
4001
+ render();
4002
+ return;
4003
+ }
4004
+ if (state.inputMode === "readme") {
4005
+ if (isUpKey(key, str)) {
4006
+ state.readmeScrollOffset = Math.max(0, state.readmeScrollOffset - 1);
4007
+ render();
4008
+ return;
4009
+ }
4010
+ if (isDownKey(key, str)) {
4011
+ state.readmeScrollOffset += 1;
4012
+ render();
4013
+ return;
4014
+ }
4015
+ }
4016
+ return;
4017
+ }
3754
4018
  if (state.inputMode === "signal") {
3755
4019
  if (isUpKey(key, str)) {
3756
4020
  const idx = state.SIGNAL_OPTIONS.indexOf(state.inputBuffer);
@@ -4172,11 +4436,72 @@ async function startLive(opts2, splashPromise) {
4172
4436
  case "tab":
4173
4437
  cycleTab(1);
4174
4438
  break;
4439
+ case "up":
4440
+ if (state.lastData?.processes?.length) {
4441
+ state.trackedPid = null;
4442
+ state.processSelectionIndex = Math.max(0, state.processSelectionIndex - 1);
4443
+ render();
4444
+ }
4445
+ break;
4446
+ case "down":
4447
+ if (state.lastData?.processes?.length) {
4448
+ state.trackedPid = null;
4449
+ const count = state.lastData.processes.length;
4450
+ state.processSelectionIndex = Math.min(count - 1, state.processSelectionIndex + 1);
4451
+ render();
4452
+ }
4453
+ break;
4454
+ case "space":
4455
+ case " ":
4456
+ if (state.lastData?.processes?.length && state.processSelectionIndex >= 0) {
4457
+ const sorted = state.lastData.processes;
4458
+ const proc = sorted[state.processSelectionIndex];
4459
+ if (proc) {
4460
+ if (state.trackedPid === proc.pid) {
4461
+ state.trackedPid = null;
4462
+ setStatus(`Stopped following PID ${proc.pid}`);
4463
+ } else {
4464
+ state.trackedPid = proc.pid;
4465
+ setStatus(`Following PID ${proc.pid} (${proc.command})`);
4466
+ }
4467
+ render();
4468
+ }
4469
+ }
4470
+ break;
4471
+ case "return":
4472
+ case "enter":
4473
+ if (state.inspectingProcess) {
4474
+ state.inspectingProcess = null;
4475
+ render();
4476
+ } else if (state.lastData?.processes?.length && state.processSelectionIndex >= 0) {
4477
+ const sorted = state.lastData.processes;
4478
+ const proc = sorted[state.processSelectionIndex];
4479
+ if (proc) {
4480
+ state.inspectingProcess = proc;
4481
+ state.trackedPid = proc.pid;
4482
+ setStatus(`Inspecting & following PID ${proc.pid}`);
4483
+ render();
4484
+ }
4485
+ }
4486
+ break;
4175
4487
  case "escape":
4176
4488
  case "esc":
4177
- state.activePanel = "grid";
4178
- setStatus("Grid view");
4179
- render();
4489
+ if (state.inspectingProcess) {
4490
+ state.inspectingProcess = null;
4491
+ render();
4492
+ } else if (state.trackedPid !== null) {
4493
+ state.trackedPid = null;
4494
+ setStatus("Stopped following process");
4495
+ render();
4496
+ } else if (state.activePanel !== "grid") {
4497
+ state.activePanel = "grid";
4498
+ setStatus("Grid view");
4499
+ render();
4500
+ } else {
4501
+ state.inputMode = "menu";
4502
+ state.menuSelectionIndex = 0;
4503
+ render();
4504
+ }
4180
4505
  break;
4181
4506
  default:
4182
4507
  if (str && str.length === 1) {
@@ -4193,7 +4518,7 @@ async function startLive(opts2, splashPromise) {
4193
4518
  process.once("SIGINT", () => stopLive());
4194
4519
  }
4195
4520
  function tabKeyToId(str) {
4196
- 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" };
4521
+ 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" };
4197
4522
  return map[str] ?? null;
4198
4523
  }
4199
4524
  function cycleTab(direction) {
@@ -4211,6 +4536,10 @@ function handleNormalMouse(seq) {
4211
4536
  const cy = seq.charCodeAt(5) - 32;
4212
4537
  if (cb === 0 || cb === 1) {
4213
4538
  handleMouseClick(cy, cx);
4539
+ } else if (cb === 64) {
4540
+ scrollProcessSelection(-1);
4541
+ } else if (cb === 65) {
4542
+ scrollProcessSelection(1);
4214
4543
  }
4215
4544
  }
4216
4545
  function handleSGRMouse(seq) {
@@ -4221,6 +4550,18 @@ function handleSGRMouse(seq) {
4221
4550
  const cy = parseInt(match[3]) - 1;
4222
4551
  if (button === 0 || button === 1) {
4223
4552
  handleMouseClick(cy, cx);
4553
+ } else if (button === 64) {
4554
+ scrollProcessSelection(-1);
4555
+ } else if (button === 65) {
4556
+ scrollProcessSelection(1);
4557
+ }
4558
+ }
4559
+ function scrollProcessSelection(dir) {
4560
+ if (state.lastData?.processes?.length) {
4561
+ state.trackedPid = null;
4562
+ const count = state.lastData.processes.length;
4563
+ state.processSelectionIndex = Math.max(0, Math.min(count - 1, state.processSelectionIndex + dir));
4564
+ render();
4224
4565
  }
4225
4566
  }
4226
4567
  function handleMouseClick(y, x) {
@@ -4601,8 +4942,8 @@ async function showSplash(config3) {
4601
4942
  }
4602
4943
 
4603
4944
  // src/historyCmd.ts
4604
- import fs5 from "fs";
4605
- import path3 from "path";
4945
+ import fs6 from "fs";
4946
+ import path4 from "path";
4606
4947
  import chalk8 from "chalk";
4607
4948
  function runHistoryCommand(opts2 = {}) {
4608
4949
  const days = opts2.days || 7;
@@ -4610,11 +4951,11 @@ function runHistoryCommand(opts2 = {}) {
4610
4951
  console.log(chalk8.bold(`
4611
4952
  pyre history \u2014 Past ${days} Days Trend
4612
4953
  `));
4613
- if (!fs5.existsSync(dir)) {
4954
+ if (!fs6.existsSync(dir)) {
4614
4955
  console.log(chalk8.yellow(` No logs directory found at ${dir}`));
4615
4956
  return;
4616
4957
  }
4617
- const files = fs5.readdirSync(dir).filter((f) => f.startsWith("pyre-log-") && f.endsWith(".csv"));
4958
+ const files = fs6.readdirSync(dir).filter((f) => f.startsWith("pyre-log-") && f.endsWith(".csv"));
4618
4959
  if (files.length === 0) {
4619
4960
  console.log(chalk8.yellow(` No CSV logs found in ${dir}`));
4620
4961
  return;
@@ -4624,9 +4965,9 @@ function runHistoryCommand(opts2 = {}) {
4624
4965
  const memPoints = [];
4625
4966
  const tempPoints = [];
4626
4967
  for (const file of files.sort()) {
4627
- const fullPath = path3.join(dir, file);
4968
+ const fullPath = path4.join(dir, file);
4628
4969
  try {
4629
- const content = fs5.readFileSync(fullPath, "utf-8");
4970
+ const content = fs6.readFileSync(fullPath, "utf-8");
4630
4971
  const lines = content.split("\n").slice(1);
4631
4972
  for (const line of lines) {
4632
4973
  if (!line.trim()) continue;
@@ -4661,7 +5002,7 @@ function runHistoryCommand(opts2 = {}) {
4661
5002
  }
4662
5003
 
4663
5004
  // src/diffCmd.ts
4664
- import fs6 from "fs";
5005
+ import fs7 from "fs";
4665
5006
  import chalk9 from "chalk";
4666
5007
  function runDiffCommand(file1, file2) {
4667
5008
  console.log(chalk9.bold(`
@@ -4671,8 +5012,8 @@ function runDiffCommand(file1, file2) {
4671
5012
  console.log(chalk9.dim(` Snapshot B: ${file2}
4672
5013
  `));
4673
5014
  try {
4674
- const dataA = JSON.parse(fs6.readFileSync(file1, "utf-8"));
4675
- const dataB = JSON.parse(fs6.readFileSync(file2, "utf-8"));
5015
+ const dataA = JSON.parse(fs7.readFileSync(file1, "utf-8"));
5016
+ const dataB = JSON.parse(fs7.readFileSync(file2, "utf-8"));
4676
5017
  const diffNum = (label, valA, valB, unit = "") => {
4677
5018
  const delta = valB - valA;
4678
5019
  const sign = delta > 0 ? "+" : "";
@@ -4696,8 +5037,8 @@ function runDiffCommand(file1, file2) {
4696
5037
  }
4697
5038
 
4698
5039
  // src/xbar.ts
4699
- import fs7 from "fs";
4700
- import path4 from "path";
5040
+ import fs8 from "fs";
5041
+ import path5 from "path";
4701
5042
  import os2 from "os";
4702
5043
  import chalk10 from "chalk";
4703
5044
  function generateXbarPlugin() {
@@ -4734,11 +5075,11 @@ echo "\u{1F525} C:\${CPU}% M:\${MEM}% T:\${TEMP}\xB0C | font=Menlo"
4734
5075
  echo "---"
4735
5076
  echo "Open Dashboard | bash='${nodePath}' param1='${pyrePath}' param2='live' terminal=true"
4736
5077
  `;
4737
- const pluginDir = path4.join(os2.homedir(), "Library", "Application Support", "xbar", "plugins");
5078
+ const pluginDir = path5.join(os2.homedir(), "Library", "Application Support", "xbar", "plugins");
4738
5079
  const filename = "pyre-monitor.2s.sh";
4739
- const outPath = path4.join(process.cwd(), filename);
4740
- fs7.writeFileSync(outPath, script);
4741
- fs7.chmodSync(outPath, "755");
5080
+ const outPath = path5.join(process.cwd(), filename);
5081
+ fs8.writeFileSync(outPath, script);
5082
+ fs8.chmodSync(outPath, "755");
4742
5083
  console.log(chalk10.green(`Generated xbar plugin script at ${outPath}`));
4743
5084
  console.log(`To install, move it to your xbar plugins directory:`);
4744
5085
  console.log(chalk10.cyan(` mv ${filename} "${pluginDir}/"`));
@@ -4774,7 +5115,7 @@ function runFleetCommand(hosts) {
4774
5115
 
4775
5116
  // src/index.ts
4776
5117
  var config2 = readConfig();
4777
- var pkg = JSON.parse(fs8.readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
5118
+ var pkg = JSON.parse(fs9.readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
4778
5119
  var program = new Command();
4779
5120
  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");
4780
5121
  program.addHelpText("after", `
@@ -4861,7 +5202,7 @@ async function runServerCommand() {
4861
5202
  async function main() {
4862
5203
  const cmd = program.args[0];
4863
5204
  if (cmd === "anomalies") {
4864
- const { runAnomaliesDigest } = await import("./anomaliesCmd-TO52YY2B.js");
5205
+ const { runAnomaliesDigest } = await import("./anomaliesCmd-25OFKYPK.js");
4865
5206
  runAnomaliesDigest({ since: opts.since || "7d", dir: opts.exportDir });
4866
5207
  return;
4867
5208
  }
@@ -4958,7 +5299,7 @@ async function main() {
4958
5299
  const cfg = readConfig();
4959
5300
  console.log(JSON.stringify(cfg, null, 2));
4960
5301
  } else if (sub === "reset") {
4961
- fs8.rmSync(CONFIG_FILE, { force: true });
5302
+ fs9.rmSync(CONFIG_FILE, { force: true });
4962
5303
  console.log(chalk12.green("Config reset to defaults"));
4963
5304
  } else {
4964
5305
  console.log("Usage: pyre config <show|reset>");
@@ -5055,7 +5396,7 @@ async function main() {
5055
5396
  else output = formatTable(data, { width: process.stdout.columns || 80, sortBy: opts.sort, treeView: opts.tree ?? config2.treeView, visible: { packets: opts.packets ? true : void 0 } });
5056
5397
  console.log(output);
5057
5398
  if (opts.out) {
5058
- fs8.writeFileSync(opts.out, stripAnsi(output));
5399
+ fs9.writeFileSync(opts.out, stripAnsi(output));
5059
5400
  console.log(chalk12.dim(`
5060
5401
  Saved to ${opts.out}`));
5061
5402
  }
@@ -5239,7 +5580,7 @@ async function runP2PConnect() {
5239
5580
  }
5240
5581
  async function runInfoCommand() {
5241
5582
  const data = await collectAll({ detailed: true });
5242
- const { getDisplayInfo, getTimeMachineStatus } = await import("./collectors-KNEN4YJD.js");
5583
+ const { getDisplayInfo, getTimeMachineStatus } = await import("./collectors-VACLPHRP.js");
5243
5584
  const displays = await getDisplayInfo();
5244
5585
  const tmStatus = await getTimeMachineStatus();
5245
5586
  const lines = [];
@@ -5345,9 +5686,9 @@ async function runWebCommand() {
5345
5686
  res.end(getDashboardHtml());
5346
5687
  } else if (url === "/setup") {
5347
5688
  res.writeHead(200, { "Content-Type": "text/html" });
5348
- const indexPath = path5.join(process.cwd(), "index.html");
5349
- if (fs8.existsSync(indexPath)) {
5350
- res.end(fs8.readFileSync(indexPath, "utf-8"));
5689
+ const indexPath = path6.join(process.cwd(), "index.html");
5690
+ if (fs9.existsSync(indexPath)) {
5691
+ res.end(fs9.readFileSync(indexPath, "utf-8"));
5351
5692
  } else {
5352
5693
  res.end(getDashboardHtml());
5353
5694
  }
@@ -5496,8 +5837,8 @@ let delegate = AppDelegate()
5496
5837
  app.delegate = delegate
5497
5838
  app.run()
5498
5839
  `;
5499
- const scriptPath = path5.join(os3.tmpdir(), "pyre-ui.swift");
5500
- fs8.writeFileSync(scriptPath, swiftScript);
5840
+ const scriptPath = path6.join(os3.tmpdir(), "pyre-ui.swift");
5841
+ fs9.writeFileSync(scriptPath, swiftScript);
5501
5842
  const { spawn } = await import("child_process");
5502
5843
  console.log(chalk12.cyan(`
5503
5844
  Launching native UI window...`));
@@ -5514,9 +5855,9 @@ app.run()
5514
5855
  async function runBenchCommand(benchCmd) {
5515
5856
  const { spawn } = await import("child_process");
5516
5857
  const logDir = opts.exportDir || "./pyre-exports";
5517
- const file = path5.join(logDir, `pyre-bench-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.csv`);
5518
- if (!fs8.existsSync(logDir)) fs8.mkdirSync(logDir, { recursive: true });
5519
- const stream = fs8.createWriteStream(file, { flags: "a" });
5858
+ const file = path6.join(logDir, `pyre-bench-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.csv`);
5859
+ if (!fs9.existsSync(logDir)) fs9.mkdirSync(logDir, { recursive: true });
5860
+ const stream = fs9.createWriteStream(file, { flags: "a" });
5520
5861
  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");
5521
5862
  console.log(chalk12.bold(`
5522
5863
  pyre bench`));