pyre-cli 9.5.1 → 10.0.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/README.md +84 -14
- package/dist/alertChannels-TPSQIJ7S.js +8 -0
- package/dist/blender-FGH3ASEQ.js +7 -0
- package/dist/brewHealth-P2GW5Y5C.js +176 -0
- package/dist/buildCmd-CDPZ2ZFA.js +38 -0
- package/dist/{checkCmd-EWFNLBWE.js → checkCmd-RKW6QGDR.js} +10 -11
- package/dist/{chunk-RVGKEJXT.js → chunk-53O2RGSL.js} +47 -17
- package/dist/{chunk-MOAEGMFP.js → chunk-6UHZP4QP.js} +204 -46
- package/dist/chunk-GHRDW646.js +2506 -0
- package/dist/{prometheus-LFYHF6VO.js → chunk-HT4VQ4OH.js} +1 -0
- package/dist/chunk-M73HCQD2.js +95 -0
- package/dist/chunk-MZO5QKUR.js +32 -0
- package/dist/chunk-VIACN2RU.js +133 -0
- package/dist/chunk-Z2YV5HKV.js +87 -0
- package/dist/chunk-ZLVIVFSO.js +110 -0
- package/dist/{collectors-WD6VKH6M.js → collectors-H43PN4PS.js} +8 -2
- package/dist/{completions-VQMHQCXE.js → completions-JKYEJNGR.js} +34 -1
- package/dist/{config-4ZLF4HTS.js → config-GYGJXU2Q.js} +9 -1
- package/dist/doctor-GYHFTNZQ.js +387 -0
- package/dist/explainCmd-MF6PIMLW.js +222 -0
- package/dist/extensions-XJGHG7X3.js +134 -0
- package/dist/index.js +841 -206
- package/dist/ioreport.py +72 -0
- package/dist/netUsageCmd-WKBTW2YG.js +90 -0
- package/dist/{pipeCmd-7THXEGDH.js → pipeCmd-7REDBLBU.js} +5 -3
- package/dist/profileProcCmd-N4H6MCBT.js +107 -0
- package/dist/prometheus-5BL5PVLC.js +6 -0
- package/dist/prometheusCmd-PL64BLN5.js +93 -0
- package/dist/replayCmd-Z3PYXY7V.js +164 -0
- package/dist/run-HOGQDDTA.js +8 -0
- package/dist/serveCmd-2H5VLF3P.js +153 -0
- package/dist/service-Q74BBILO.js +181 -0
- package/dist/smartCmd-ODWKSFNG.js +166 -0
- package/dist/syncCmd-A2W3QB3S.js +126 -0
- package/dist/topoCmd-DU2HHZNH.js +148 -0
- package/dist/watchdogCmd-UF53JPZB.js +161 -0
- package/dist/{wizard-GVIAHIVV.js → wizard-TD7BGX6O.js} +1 -1
- package/install.ps1 +29 -0
- package/install.sh +10 -9
- package/package.json +7 -3
- package/src/ai/models.ts +138 -0
- package/src/alertChannels.ts +119 -0
- package/src/anomalies.ts +114 -60
- package/src/brewHealth.ts +157 -40
- package/src/buildCmd.ts +48 -0
- package/src/checkCmd.ts +5 -5
- package/src/completions.ts +35 -0
- package/src/doctor.ts +235 -7
- package/src/easy_features.test.ts +40 -0
- package/src/explainCmd.ts +261 -0
- package/src/extensions.ts +71 -7
- package/src/formatters/index.ts +1 -1
- package/src/formatters/output.ts +100 -13
- package/src/formatters/render.ts +274 -38
- package/src/formatters/themes.ts +8 -0
- package/src/formatters/types.ts +6 -1
- package/src/index.ts +298 -56
- package/src/live/input.ts +122 -4
- package/src/live/render.ts +98 -0
- package/src/live/state.ts +70 -52
- package/src/live/types.ts +2 -2
- package/src/monitors/batteryPredictor.ts +143 -0
- package/src/monitors/blender.ts +144 -0
- package/src/monitors/buildTracker.ts +121 -0
- package/src/monitors/collectors.ts +558 -147
- package/src/monitors/dns_cache.ts +48 -0
- package/src/monitors/index.ts +3 -2
- package/src/monitors/ioreport.py +72 -0
- package/src/monitors/platform/linux.ts +756 -0
- package/src/monitors/platform/windows.ts +519 -0
- package/src/monitors/run.ts +27 -3
- package/src/monitors/smc.ts +21 -1
- package/src/monitors/types.ts +112 -14
- package/src/netUsageCmd.ts +121 -0
- package/src/platform.test.ts +99 -0
- package/src/plugins/index.ts +143 -0
- package/src/profileProcCmd.ts +133 -0
- package/src/prometheusCmd.ts +107 -0
- package/src/replayCmd.ts +186 -0
- package/src/serveCmd.ts +180 -0
- package/src/service.ts +125 -7
- package/src/smartCmd.ts +205 -0
- package/src/state/config.ts +64 -17
- package/src/syncCmd.ts +166 -0
- package/src/topoCmd.ts +176 -0
- package/src/watchdogCmd.ts +198 -0
- package/src/xbar.ts +22 -11
- package/dist/brewHealth-F2BXF5M6.js +0 -74
- package/dist/chunk-FH5WDJBF.js +0 -15
- package/dist/chunk-HGRBBPYX.js +0 -937
- package/dist/doctor-L6FZYA7F.js +0 -184
- package/dist/extensions-DVA5YLGH.js +0 -74
- package/dist/service-WTS3NBQU.js +0 -73
- /package/dist/{chunk-SG26Y3WF.js → chunk-IM3IQMMT.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
CONFIG_FILE,
|
|
4
|
-
readConfig,
|
|
5
|
-
writeConfig
|
|
6
|
-
} from "./chunk-RVGKEJXT.js";
|
|
7
2
|
import {
|
|
8
3
|
TAB_BAR_ROW,
|
|
9
4
|
THEMES,
|
|
@@ -15,17 +10,31 @@ import {
|
|
|
15
10
|
panel,
|
|
16
11
|
sparkline,
|
|
17
12
|
visLen
|
|
18
|
-
} from "./chunk-
|
|
19
|
-
import
|
|
13
|
+
} from "./chunk-6UHZP4QP.js";
|
|
14
|
+
import {
|
|
15
|
+
AVAILABLE_AI_MODELS
|
|
16
|
+
} from "./chunk-ZLVIVFSO.js";
|
|
17
|
+
import {
|
|
18
|
+
sendAlert
|
|
19
|
+
} from "./chunk-Z2YV5HKV.js";
|
|
20
|
+
import {
|
|
21
|
+
CONFIG_FILE,
|
|
22
|
+
getConfigDir,
|
|
23
|
+
readConfig,
|
|
24
|
+
writeConfig
|
|
25
|
+
} from "./chunk-53O2RGSL.js";
|
|
26
|
+
import "./chunk-IM3IQMMT.js";
|
|
20
27
|
import {
|
|
21
28
|
collectAll
|
|
22
|
-
} from "./chunk-
|
|
23
|
-
import "./chunk-
|
|
29
|
+
} from "./chunk-GHRDW646.js";
|
|
30
|
+
import "./chunk-M73HCQD2.js";
|
|
31
|
+
import "./chunk-VIACN2RU.js";
|
|
32
|
+
import "./chunk-MZO5QKUR.js";
|
|
24
33
|
|
|
25
34
|
// src/index.ts
|
|
26
|
-
import
|
|
35
|
+
import fs10 from "fs";
|
|
27
36
|
import os2 from "os";
|
|
28
|
-
import
|
|
37
|
+
import path6 from "path";
|
|
29
38
|
import readline2 from "readline";
|
|
30
39
|
import { Command } from "commander";
|
|
31
40
|
import chalk10 from "chalk";
|
|
@@ -124,19 +133,39 @@ function formatHtml(data) {
|
|
|
124
133
|
<table>
|
|
125
134
|
${rows([
|
|
126
135
|
{ 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)}
|
|
136
|
+
{ label: "RX", value: `${formatBytes(data.network.rxBytes)} \xB7 ${formatBytes(data.network.rxRate)}/s` },
|
|
137
|
+
{ label: "TX", value: `${formatBytes(data.network.txBytes)} \xB7 ${formatBytes(data.network.txRate)}/s` },
|
|
138
|
+
...data.network.protocols ? [
|
|
139
|
+
{ label: "TCP", value: `${data.network.protocols.tcp.connections} conns` },
|
|
140
|
+
{ label: "UDP", value: `${data.network.protocols.udp.connections} conns` }
|
|
141
|
+
] : [],
|
|
142
|
+
...data.network.connectionStates ? [
|
|
143
|
+
{ label: "Active", value: `${data.network.connectionStates.established} ESTABLISHED` },
|
|
144
|
+
{ label: "Listening", value: `${data.network.connectionStates.listening}` }
|
|
145
|
+
] : [],
|
|
146
|
+
...data.network.listeningPorts && data.network.listeningPorts.length ? [
|
|
147
|
+
{ label: "Ports", value: data.network.listeningPorts.slice(0, 8).join(", ") }
|
|
148
|
+
] : []
|
|
129
149
|
]).trim()}
|
|
130
150
|
</table>`);
|
|
131
151
|
if (data.packets) {
|
|
132
|
-
|
|
133
|
-
<table>
|
|
134
|
-
${rows([
|
|
152
|
+
const packetRows = [
|
|
135
153
|
{ label: "Total", value: `${data.packets.totalPackets} pkt` },
|
|
136
154
|
{ label: "RX", value: `${data.packets.rxPackets} pkt` },
|
|
137
155
|
{ label: "TX", value: `${data.packets.txPackets} pkt` },
|
|
138
156
|
{ label: "Connections", value: `${data.packets.connections} TCP` }
|
|
139
|
-
]
|
|
157
|
+
];
|
|
158
|
+
if (data.packets.protocolStats) {
|
|
159
|
+
packetRows.push({ label: "TCP", value: `${data.packets.protocolStats.tcp.connections} conns` });
|
|
160
|
+
packetRows.push({ label: "UDP", value: `${data.packets.protocolStats.udp.connections} conns` });
|
|
161
|
+
}
|
|
162
|
+
if (data.packets.connectionStates) {
|
|
163
|
+
packetRows.push({ label: "ESTABLISHED", value: `${data.packets.connectionStates.established}` });
|
|
164
|
+
packetRows.push({ label: "LISTENING", value: `${data.packets.connectionStates.listening}` });
|
|
165
|
+
}
|
|
166
|
+
sections.push(`<h2>Packets</h2>
|
|
167
|
+
<table>
|
|
168
|
+
${rows(packetRows).trim()}
|
|
140
169
|
</table>`);
|
|
141
170
|
}
|
|
142
171
|
if (data.tasks.length) {
|
|
@@ -167,6 +196,16 @@ function formatHtml(data) {
|
|
|
167
196
|
sections.push(`<h2>Processes</h2>
|
|
168
197
|
<table>
|
|
169
198
|
${rows(procRows).trim()}
|
|
199
|
+
</table>`);
|
|
200
|
+
}
|
|
201
|
+
if (data.blenderRenders && data.blenderRenders.length) {
|
|
202
|
+
const blenderRows = data.blenderRenders.map((r) => ({
|
|
203
|
+
label: `PID ${r.pid}`,
|
|
204
|
+
value: `${r.blendFile} \xB7 ${r.renderEngine} \xB7 ${r.currentFrame}/${r.totalFrames} frames \xB7 ${r.completionPercent}% \xB7 ${r.status} \xB7 ${r.elapsedSec}s`
|
|
205
|
+
}));
|
|
206
|
+
sections.push(`<h2>Blender Renders</h2>
|
|
207
|
+
<table>
|
|
208
|
+
${rows(blenderRows).trim()}
|
|
170
209
|
</table>`);
|
|
171
210
|
}
|
|
172
211
|
return `<!DOCTYPE html>
|
|
@@ -388,8 +427,19 @@ function formatMarkdown(data) {
|
|
|
388
427
|
lines.push("| Property | Value |");
|
|
389
428
|
lines.push("|---|---|");
|
|
390
429
|
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)}
|
|
430
|
+
lines.push(`| RX | ${mdCell(`${formatBytes(data.network.rxBytes)} \xB7 ${formatBytes(data.network.rxRate)}/s`)} |`);
|
|
431
|
+
lines.push(`| TX | ${mdCell(`${formatBytes(data.network.txBytes)} \xB7 ${formatBytes(data.network.txRate)}/s`)} |`);
|
|
432
|
+
if (data.network.protocols) {
|
|
433
|
+
lines.push(`| TCP | ${mdCell(`${data.network.protocols.tcp.connections} conns`)} |`);
|
|
434
|
+
lines.push(`| UDP | ${mdCell(`${data.network.protocols.udp.connections} conns`)} |`);
|
|
435
|
+
}
|
|
436
|
+
if (data.network.connectionStates) {
|
|
437
|
+
lines.push(`| Active | ${mdCell(`${data.network.connectionStates.established} ESTABLISHED`)} |`);
|
|
438
|
+
lines.push(`| Listening | ${mdCell(`${data.network.connectionStates.listening}`)} |`);
|
|
439
|
+
}
|
|
440
|
+
if (data.network.listeningPorts && data.network.listeningPorts.length) {
|
|
441
|
+
lines.push(`| Ports | ${mdCell(data.network.listeningPorts.slice(0, 8).join(", "))} |`);
|
|
442
|
+
}
|
|
393
443
|
lines.push("");
|
|
394
444
|
if (data.packets) {
|
|
395
445
|
lines.push("## Packets");
|
|
@@ -400,6 +450,14 @@ function formatMarkdown(data) {
|
|
|
400
450
|
lines.push(`| RX | ${mdCell(`${data.packets.rxPackets} pkt`)} |`);
|
|
401
451
|
lines.push(`| TX | ${mdCell(`${data.packets.txPackets} pkt`)} |`);
|
|
402
452
|
lines.push(`| Connections | ${mdCell(`${data.packets.connections} TCP`)} |`);
|
|
453
|
+
if (data.packets.protocolStats) {
|
|
454
|
+
lines.push(`| TCP | ${mdCell(`${data.packets.protocolStats.tcp.connections} conns`)} |`);
|
|
455
|
+
lines.push(`| UDP | ${mdCell(`${data.packets.protocolStats.udp.connections} conns`)} |`);
|
|
456
|
+
}
|
|
457
|
+
if (data.packets.connectionStates) {
|
|
458
|
+
lines.push(`| ESTABLISHED | ${mdCell(`${data.packets.connectionStates.established}`)} |`);
|
|
459
|
+
lines.push(`| LISTENING | ${mdCell(`${data.packets.connectionStates.listening}`)} |`);
|
|
460
|
+
}
|
|
403
461
|
lines.push("");
|
|
404
462
|
}
|
|
405
463
|
if (data.tasks.length) {
|
|
@@ -432,6 +490,16 @@ function formatMarkdown(data) {
|
|
|
432
490
|
}
|
|
433
491
|
lines.push("");
|
|
434
492
|
}
|
|
493
|
+
if (data.blenderRenders && data.blenderRenders.length) {
|
|
494
|
+
lines.push("## Blender Renders");
|
|
495
|
+
lines.push("");
|
|
496
|
+
lines.push("| PID | Blend File | Engine | Frames | Completion | Status | Elapsed |");
|
|
497
|
+
lines.push("|---|---|---|---|---|---|---|");
|
|
498
|
+
for (const r of data.blenderRenders) {
|
|
499
|
+
lines.push(`| ${r.pid} | ${mdCell(r.blendFile)} | ${mdCell(r.renderEngine)} | ${r.currentFrame}/${r.totalFrames} | ${r.completionPercent}% | ${r.status} | ${r.elapsedSec}s |`);
|
|
500
|
+
}
|
|
501
|
+
lines.push("");
|
|
502
|
+
}
|
|
435
503
|
return lines.join("\n");
|
|
436
504
|
}
|
|
437
505
|
function escapeHtml(s) {
|
|
@@ -461,9 +529,16 @@ function formatCsv(data) {
|
|
|
461
529
|
["thermal", "cpuTempF", data.thermal.temperatures?.cpu_die != null ? String(celsiusToFahrenheit(data.thermal.temperatures.cpu_die)) : ""],
|
|
462
530
|
["network", "rxBytes", String(data.network.rxBytes)],
|
|
463
531
|
["network", "txBytes", String(data.network.txBytes)],
|
|
532
|
+
["network", "rxRate", String(data.network.rxRate)],
|
|
533
|
+
["network", "txRate", String(data.network.txRate)],
|
|
464
534
|
["network", "rxPackets", String(data.network.rxPackets)],
|
|
465
535
|
["network", "txPackets", String(data.network.txPackets)],
|
|
466
536
|
["network", "connections", data.network.connections ? String(data.network.connections) : ""],
|
|
537
|
+
["network", "tcpConns", data.network.protocols ? String(data.network.protocols.tcp.connections) : ""],
|
|
538
|
+
["network", "udpConns", data.network.protocols ? String(data.network.protocols.udp.connections) : ""],
|
|
539
|
+
["network", "established", data.network.connectionStates ? String(data.network.connectionStates.established) : ""],
|
|
540
|
+
["network", "listening", data.network.connectionStates ? String(data.network.connectionStates.listening) : ""],
|
|
541
|
+
["network", "listeningPorts", data.network.listeningPorts ? data.network.listeningPorts.slice(0, 10).join(";") : ""],
|
|
467
542
|
["battery", "level", data.battery ? String(data.battery.level) : ""],
|
|
468
543
|
["battery", "powerSource", data.battery ? data.battery.powerSource : ""],
|
|
469
544
|
["battery", "condition", data.battery?.condition ?? ""],
|
|
@@ -487,6 +562,16 @@ function formatCsv(data) {
|
|
|
487
562
|
rows.push(["packets", "rxPackets", String(data.packets.rxPackets)]);
|
|
488
563
|
rows.push(["packets", "txPackets", String(data.packets.txPackets)]);
|
|
489
564
|
rows.push(["packets", "connections", String(data.packets.connections)]);
|
|
565
|
+
if (data.packets.protocolStats) {
|
|
566
|
+
rows.push(["packets", "tcpConns", String(data.packets.protocolStats.tcp.connections)]);
|
|
567
|
+
rows.push(["packets", "udpConns", String(data.packets.protocolStats.udp.connections)]);
|
|
568
|
+
}
|
|
569
|
+
if (data.packets.connectionStates) {
|
|
570
|
+
rows.push(["packets", "established", String(data.packets.connectionStates.established)]);
|
|
571
|
+
rows.push(["packets", "listening", String(data.packets.connectionStates.listening)]);
|
|
572
|
+
rows.push(["packets", "timeWait", String(data.packets.connectionStates.timeWait)]);
|
|
573
|
+
rows.push(["packets", "closeWait", String(data.packets.connectionStates.closeWait)]);
|
|
574
|
+
}
|
|
490
575
|
for (const iface of data.packets.interfaces || []) {
|
|
491
576
|
rows.push(["packets_iface", iface.iface, `${iface.rxPackets},${iface.txPackets},${iface.rxBytes},${iface.txBytes}`]);
|
|
492
577
|
}
|
|
@@ -512,6 +597,20 @@ function formatCsv(data) {
|
|
|
512
597
|
rows.push(["process", `runtime=${p.pid}`, String(p.runtime)]);
|
|
513
598
|
rows.push(["process", `command=${p.pid}`, p.command]);
|
|
514
599
|
}
|
|
600
|
+
for (const r of data.blenderRenders || []) {
|
|
601
|
+
rows.push(["blender", `pid=${r.pid}`, `${r.pid}`]);
|
|
602
|
+
rows.push(["blender", `blend_file=${r.pid}`, r.blendFile]);
|
|
603
|
+
rows.push(["blender", `engine=${r.pid}`, r.renderEngine]);
|
|
604
|
+
rows.push(["blender", `frame_start=${r.pid}`, String(r.frameStart)]);
|
|
605
|
+
rows.push(["blender", `frame_end=${r.pid}`, String(r.frameEnd)]);
|
|
606
|
+
rows.push(["blender", `current_frame=${r.pid}`, String(r.currentFrame)]);
|
|
607
|
+
rows.push(["blender", `total_frames=${r.pid}`, String(r.totalFrames)]);
|
|
608
|
+
rows.push(["blender", `completion_pct=${r.pid}`, String(r.completionPercent)]);
|
|
609
|
+
rows.push(["blender", `status=${r.pid}`, r.status]);
|
|
610
|
+
rows.push(["blender", `elapsed_sec=${r.pid}`, String(r.elapsedSec)]);
|
|
611
|
+
rows.push(["blender", `output_path=${r.pid}`, r.outputPath]);
|
|
612
|
+
if (r.sampleCount !== void 0) rows.push(["blender", `samples=${r.pid}`, String(r.sampleCount)]);
|
|
613
|
+
}
|
|
515
614
|
return rows.map((r) => r.map((item) => `"${String(item).replace(/"/g, '""')}"`).join(",")).join("\n");
|
|
516
615
|
}
|
|
517
616
|
function formatTsv(data) {
|
|
@@ -1237,6 +1336,9 @@ document.addEventListener('keydown', e => { if(keyMap[e.key.toLowerCase()]) swit
|
|
|
1237
1336
|
// src/live/input.ts
|
|
1238
1337
|
import readline from "readline";
|
|
1239
1338
|
|
|
1339
|
+
// src/live/state.ts
|
|
1340
|
+
import fs from "fs";
|
|
1341
|
+
|
|
1240
1342
|
// src/history.ts
|
|
1241
1343
|
var History = class _History {
|
|
1242
1344
|
/** Maximum number of samples retained in the rolling window. */
|
|
@@ -1396,6 +1498,7 @@ var History = class _History {
|
|
|
1396
1498
|
var SIGNAL_OPTIONS = ["SIGTERM", "SIGKILL", "SIGINT", "SIGHUP", "SIGSTOP", "SIGCONT"];
|
|
1397
1499
|
var MENU_OPTIONS = ["Resume Dashboard", "Settings (Customizer)", "Readme", "Credits", "Quit pyre"];
|
|
1398
1500
|
var config = readConfig();
|
|
1501
|
+
var isDockerEnv = fs.existsSync("/.dockerenv") || fs.existsSync("/run/.containerenv");
|
|
1399
1502
|
var state = {
|
|
1400
1503
|
intervalHandle: null,
|
|
1401
1504
|
uiIntervalHandle: null,
|
|
@@ -1420,7 +1523,9 @@ var state = {
|
|
|
1420
1523
|
packets: config.visiblePanels.packets,
|
|
1421
1524
|
tasks: config.visiblePanels.tasks,
|
|
1422
1525
|
disk: config.visiblePanels.disk,
|
|
1423
|
-
process: config.visiblePanels.process
|
|
1526
|
+
process: config.visiblePanels.process,
|
|
1527
|
+
containers: config.visiblePanels.containers,
|
|
1528
|
+
blender: config.visiblePanels.blender
|
|
1424
1529
|
},
|
|
1425
1530
|
panelLayout: config.panelLayout || ["mem", "disk", "net"],
|
|
1426
1531
|
logging: false,
|
|
@@ -1438,7 +1543,7 @@ var state = {
|
|
|
1438
1543
|
processScrollOffset: 0,
|
|
1439
1544
|
trackedPid: null,
|
|
1440
1545
|
inspectingProcess: null,
|
|
1441
|
-
inputMode: null,
|
|
1546
|
+
inputMode: isDockerEnv && !config.dockerModeConfirmed ? "docker-alert" : null,
|
|
1442
1547
|
inputBuffer: "",
|
|
1443
1548
|
targetPid: "",
|
|
1444
1549
|
selectedSignal: "SIGTERM",
|
|
@@ -1453,6 +1558,8 @@ var state = {
|
|
|
1453
1558
|
"Splash Animation",
|
|
1454
1559
|
"Notifications",
|
|
1455
1560
|
"Temperature Unit",
|
|
1561
|
+
"AI Model",
|
|
1562
|
+
"Grid Panel Order",
|
|
1456
1563
|
"Toggle CPU",
|
|
1457
1564
|
"Toggle Memory",
|
|
1458
1565
|
"Toggle GPU",
|
|
@@ -1460,10 +1567,12 @@ var state = {
|
|
|
1460
1567
|
"Toggle Battery",
|
|
1461
1568
|
"Toggle Thermal",
|
|
1462
1569
|
"Toggle Network",
|
|
1463
|
-
"Toggle
|
|
1570
|
+
"Toggle Conns",
|
|
1464
1571
|
"Toggle Tasks",
|
|
1465
1572
|
"Toggle Disk",
|
|
1466
1573
|
"Toggle Processes",
|
|
1574
|
+
"Toggle Containers",
|
|
1575
|
+
"Toggle Blender",
|
|
1467
1576
|
"Toggle Tree View"
|
|
1468
1577
|
],
|
|
1469
1578
|
CPU_ALERT_PCT: config.cpuAlertPct,
|
|
@@ -1485,12 +1594,14 @@ var state = {
|
|
|
1485
1594
|
{ id: "battery", label: "Battery", key: "5" },
|
|
1486
1595
|
{ id: "thermal", label: "Thermal", key: "6" },
|
|
1487
1596
|
{ id: "network", label: "Network", key: "7" },
|
|
1488
|
-
{ id: "packets", label: "
|
|
1597
|
+
{ id: "packets", label: "Conns", key: "8" },
|
|
1489
1598
|
{ id: "tasks", label: "Tasks", key: "9" },
|
|
1490
1599
|
{ id: "disk", label: "Disk", key: "0" },
|
|
1491
1600
|
{ id: "process", label: "Process", key: "P" },
|
|
1601
|
+
{ id: "containers", label: "Containers", key: "C" },
|
|
1492
1602
|
{ id: "p2p", label: "P2P", key: "R" },
|
|
1493
|
-
{ id: "anomalies", label: "Anomalies", key: "A" }
|
|
1603
|
+
{ id: "anomalies", label: "Anomalies", key: "A" },
|
|
1604
|
+
{ id: "blender", label: "Blender", key: "B" }
|
|
1494
1605
|
],
|
|
1495
1606
|
p2pServer: null,
|
|
1496
1607
|
p2pClient: null,
|
|
@@ -1505,7 +1616,13 @@ var state = {
|
|
|
1505
1616
|
splashColorScheme: config.splashColorScheme,
|
|
1506
1617
|
splashAnimation: config.splashAnimation,
|
|
1507
1618
|
menuSelectionIndex: 0,
|
|
1508
|
-
readmeScrollOffset: 0
|
|
1619
|
+
readmeScrollOffset: 0,
|
|
1620
|
+
isDocker: isDockerEnv,
|
|
1621
|
+
dockerModeConfirmed: config.dockerModeConfirmed || false,
|
|
1622
|
+
dockerSelectionIndex: 0,
|
|
1623
|
+
aiModel: config.aiModel || "expert-rules-v1",
|
|
1624
|
+
aiBackend: config.aiBackend || "builtin",
|
|
1625
|
+
modelSelectionIndex: 0
|
|
1509
1626
|
};
|
|
1510
1627
|
function setStatus(msg, ms = 3e3) {
|
|
1511
1628
|
state.statusMessage = msg;
|
|
@@ -1523,20 +1640,22 @@ function getToggleKey(opt) {
|
|
|
1523
1640
|
"Toggle Battery": "battery",
|
|
1524
1641
|
"Toggle Thermal": "thermal",
|
|
1525
1642
|
"Toggle Network": "network",
|
|
1526
|
-
"Toggle
|
|
1643
|
+
"Toggle Conns": "packets",
|
|
1527
1644
|
"Toggle Tasks": "tasks",
|
|
1528
1645
|
"Toggle Disk": "disk",
|
|
1529
1646
|
"Toggle Processes": "process",
|
|
1647
|
+
"Toggle Containers": "containers",
|
|
1648
|
+
"Toggle Blender": "blender",
|
|
1530
1649
|
"Toggle Tree View": "tree"
|
|
1531
1650
|
};
|
|
1532
1651
|
return map[opt] ?? null;
|
|
1533
1652
|
}
|
|
1534
1653
|
|
|
1535
1654
|
// src/live/export.ts
|
|
1536
|
-
import
|
|
1655
|
+
import fs2 from "fs";
|
|
1537
1656
|
import path from "path";
|
|
1538
1657
|
function ensureDir(dir) {
|
|
1539
|
-
if (!
|
|
1658
|
+
if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
|
|
1540
1659
|
}
|
|
1541
1660
|
function timestampForFile() {
|
|
1542
1661
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
@@ -1568,7 +1687,7 @@ function exportSnapshot() {
|
|
|
1568
1687
|
content = formatMarkdown(state.lastData);
|
|
1569
1688
|
break;
|
|
1570
1689
|
}
|
|
1571
|
-
|
|
1690
|
+
fs2.writeFileSync(file, content);
|
|
1572
1691
|
setStatus(`Exported snapshot -> ${file}`);
|
|
1573
1692
|
} catch (err) {
|
|
1574
1693
|
setStatus(`Export failed: ${err.message}`);
|
|
@@ -1579,7 +1698,7 @@ function startLogging() {
|
|
|
1579
1698
|
ensureDir(state.exportDir);
|
|
1580
1699
|
rotateLogs();
|
|
1581
1700
|
const file = path.join(state.exportDir, `pyre-log-${timestampForFile()}.csv`);
|
|
1582
|
-
state.logStream =
|
|
1701
|
+
state.logStream = fs2.createWriteStream(file, { flags: "a" });
|
|
1583
1702
|
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
1703
|
state.logging = true;
|
|
1585
1704
|
setStatus(`Logging every tick -> ${file}`);
|
|
@@ -1593,37 +1712,37 @@ var MAX_LOG_BYTES = 50 * 1024 * 1024;
|
|
|
1593
1712
|
function rotateLogs() {
|
|
1594
1713
|
try {
|
|
1595
1714
|
const dir = state.exportDir;
|
|
1596
|
-
if (!
|
|
1597
|
-
const files =
|
|
1715
|
+
if (!fs2.existsSync(dir)) return;
|
|
1716
|
+
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
1717
|
const now = Date.now();
|
|
1599
1718
|
for (const f of files) {
|
|
1600
1719
|
if (now - f.mtime > MAX_LOG_AGE_MS) {
|
|
1601
1720
|
try {
|
|
1602
|
-
|
|
1721
|
+
fs2.unlinkSync(f.path);
|
|
1603
1722
|
} catch {
|
|
1604
1723
|
}
|
|
1605
1724
|
}
|
|
1606
1725
|
}
|
|
1607
|
-
const remaining =
|
|
1726
|
+
const remaining = fs2.readdirSync(dir).filter((f) => f.startsWith("pyre-log-") && f.endsWith(".csv"));
|
|
1608
1727
|
if (remaining.length > MAX_LOG_COUNT) {
|
|
1609
1728
|
const toDelete = remaining.sort().slice(0, remaining.length - MAX_LOG_COUNT);
|
|
1610
1729
|
for (const f of toDelete) {
|
|
1611
1730
|
try {
|
|
1612
|
-
|
|
1731
|
+
fs2.unlinkSync(path.join(dir, f));
|
|
1613
1732
|
} catch {
|
|
1614
1733
|
}
|
|
1615
1734
|
}
|
|
1616
1735
|
}
|
|
1617
1736
|
let totalSize = 0;
|
|
1618
1737
|
for (const f of remaining) {
|
|
1619
|
-
totalSize +=
|
|
1738
|
+
totalSize += fs2.statSync(path.join(dir, f)).size;
|
|
1620
1739
|
}
|
|
1621
1740
|
if (totalSize > MAX_LOG_BYTES) {
|
|
1622
1741
|
const sorted = remaining.sort();
|
|
1623
1742
|
for (const f of sorted) {
|
|
1624
|
-
const sz =
|
|
1743
|
+
const sz = fs2.statSync(path.join(dir, f)).size;
|
|
1625
1744
|
try {
|
|
1626
|
-
|
|
1745
|
+
fs2.unlinkSync(path.join(dir, f));
|
|
1627
1746
|
} catch {
|
|
1628
1747
|
}
|
|
1629
1748
|
totalSize -= sz;
|
|
@@ -1660,7 +1779,7 @@ function writeLogRow(data) {
|
|
|
1660
1779
|
|
|
1661
1780
|
// src/live/render.ts
|
|
1662
1781
|
import { execFile, exec } from "child_process";
|
|
1663
|
-
import
|
|
1782
|
+
import fs3 from "fs";
|
|
1664
1783
|
import path2 from "path";
|
|
1665
1784
|
import { fileURLToPath } from "url";
|
|
1666
1785
|
import chalk2 from "chalk";
|
|
@@ -1668,76 +1787,127 @@ import chalk2 from "chalk";
|
|
|
1668
1787
|
// src/anomalies.ts
|
|
1669
1788
|
var ZSCORE_THRESHOLD = 2.5;
|
|
1670
1789
|
var CRITICAL_ZSCORE = 3.5;
|
|
1671
|
-
var MIN_SAMPLES =
|
|
1790
|
+
var MIN_SAMPLES = 10;
|
|
1791
|
+
var MIN_DEV_CPU = 10;
|
|
1792
|
+
var MIN_DEV_MEM = 5;
|
|
1793
|
+
var MIN_DEV_TEMP = 5;
|
|
1794
|
+
var MIN_DEV_NET = 5e5;
|
|
1795
|
+
var MIN_DEV_POWER = 5;
|
|
1796
|
+
var MIN_DEV_GPU = 10;
|
|
1672
1797
|
function detectAnomalies(data, history) {
|
|
1673
1798
|
const alerts = [];
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1799
|
+
if (history.cpuUsage.length >= MIN_SAMPLES) {
|
|
1800
|
+
const cpuMean = History.mean(history.cpuUsage);
|
|
1801
|
+
if (Math.abs(data.cpu.usage - cpuMean) > MIN_DEV_CPU) {
|
|
1802
|
+
const cpuZ = History.zScore(data.cpu.usage, history.cpuUsage);
|
|
1803
|
+
if (Math.abs(cpuZ) > ZSCORE_THRESHOLD) {
|
|
1804
|
+
alerts.push({
|
|
1805
|
+
metric: "CPU",
|
|
1806
|
+
value: data.cpu.usage,
|
|
1807
|
+
zScore: cpuZ,
|
|
1808
|
+
severity: Math.abs(cpuZ) > CRITICAL_ZSCORE ? "critical" : "warning"
|
|
1809
|
+
});
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1682
1812
|
}
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1813
|
+
if (history.memUsage.length >= MIN_SAMPLES) {
|
|
1814
|
+
const memMean = History.mean(history.memUsage);
|
|
1815
|
+
if (Math.abs(data.memory.usagePercent - memMean) > MIN_DEV_MEM) {
|
|
1816
|
+
const memZ = History.zScore(data.memory.usagePercent, history.memUsage);
|
|
1817
|
+
if (Math.abs(memZ) > ZSCORE_THRESHOLD) {
|
|
1818
|
+
alerts.push({
|
|
1819
|
+
metric: "Memory",
|
|
1820
|
+
value: data.memory.usagePercent,
|
|
1821
|
+
zScore: memZ,
|
|
1822
|
+
severity: Math.abs(memZ) > CRITICAL_ZSCORE ? "critical" : "warning"
|
|
1823
|
+
});
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1691
1826
|
}
|
|
1692
|
-
if (data.cpu.temperature !== void 0) {
|
|
1693
|
-
const
|
|
1694
|
-
if (Math.abs(
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1827
|
+
if (data.cpu.temperature !== void 0 && history.temp.length >= MIN_SAMPLES) {
|
|
1828
|
+
const tempMean = History.mean(history.temp);
|
|
1829
|
+
if (Math.abs(data.cpu.temperature - tempMean) > MIN_DEV_TEMP) {
|
|
1830
|
+
const tempZ = History.zScore(data.cpu.temperature, history.temp);
|
|
1831
|
+
if (Math.abs(tempZ) > ZSCORE_THRESHOLD) {
|
|
1832
|
+
alerts.push({
|
|
1833
|
+
metric: "Temp",
|
|
1834
|
+
value: data.cpu.temperature,
|
|
1835
|
+
zScore: tempZ,
|
|
1836
|
+
severity: Math.abs(tempZ) > CRITICAL_ZSCORE ? "critical" : "warning"
|
|
1837
|
+
});
|
|
1838
|
+
}
|
|
1701
1839
|
}
|
|
1702
1840
|
}
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1841
|
+
if (history.netRxRate.length >= MIN_SAMPLES) {
|
|
1842
|
+
const currentRxRate = history.netRxRate[history.netRxRate.length - 1];
|
|
1843
|
+
const netRxMean = History.mean(history.netRxRate);
|
|
1844
|
+
if (Math.abs(currentRxRate - netRxMean) > MIN_DEV_NET) {
|
|
1845
|
+
const netRxZ = History.zScore(currentRxRate, history.netRxRate);
|
|
1846
|
+
if (Math.abs(netRxZ) > ZSCORE_THRESHOLD) {
|
|
1847
|
+
alerts.push({
|
|
1848
|
+
metric: "Net RX",
|
|
1849
|
+
value: currentRxRate,
|
|
1850
|
+
zScore: netRxZ,
|
|
1851
|
+
severity: Math.abs(netRxZ) > CRITICAL_ZSCORE ? "critical" : "warning"
|
|
1852
|
+
});
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1711
1855
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1856
|
+
if (history.netTxRate.length >= MIN_SAMPLES) {
|
|
1857
|
+
const currentTxRate = history.netTxRate[history.netTxRate.length - 1];
|
|
1858
|
+
const netTxMean = History.mean(history.netTxRate);
|
|
1859
|
+
if (Math.abs(currentTxRate - netTxMean) > MIN_DEV_NET) {
|
|
1860
|
+
const netTxZ = History.zScore(currentTxRate, history.netTxRate);
|
|
1861
|
+
if (Math.abs(netTxZ) > ZSCORE_THRESHOLD) {
|
|
1862
|
+
alerts.push({
|
|
1863
|
+
metric: "Net TX",
|
|
1864
|
+
value: currentTxRate,
|
|
1865
|
+
zScore: netTxZ,
|
|
1866
|
+
severity: Math.abs(netTxZ) > CRITICAL_ZSCORE ? "critical" : "warning"
|
|
1867
|
+
});
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1720
1870
|
}
|
|
1721
|
-
if (data.power?.combinedWatts !== void 0) {
|
|
1722
|
-
const
|
|
1723
|
-
if (Math.abs(
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1871
|
+
if (data.power?.combinedWatts !== void 0 && history.powerWatts.length >= MIN_SAMPLES) {
|
|
1872
|
+
const powerMean = History.mean(history.powerWatts);
|
|
1873
|
+
if (Math.abs(data.power.combinedWatts - powerMean) > MIN_DEV_POWER) {
|
|
1874
|
+
const powerZ = History.zScore(data.power.combinedWatts, history.powerWatts);
|
|
1875
|
+
if (Math.abs(powerZ) > ZSCORE_THRESHOLD) {
|
|
1876
|
+
alerts.push({
|
|
1877
|
+
metric: "Power",
|
|
1878
|
+
value: data.power.combinedWatts,
|
|
1879
|
+
zScore: powerZ,
|
|
1880
|
+
severity: Math.abs(powerZ) > CRITICAL_ZSCORE ? "critical" : "warning"
|
|
1881
|
+
});
|
|
1882
|
+
}
|
|
1730
1883
|
}
|
|
1731
1884
|
}
|
|
1732
|
-
if (
|
|
1733
|
-
const
|
|
1734
|
-
if (Math.abs(
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1885
|
+
if (history.connections.length >= MIN_SAMPLES) {
|
|
1886
|
+
const connMean = History.mean(history.connections);
|
|
1887
|
+
if (Math.abs((data.packets?.connections || data.network.connections || 0) - connMean) > 5) {
|
|
1888
|
+
const connZ = History.zScore(data.packets?.connections || data.network.connections || 0, history.connections);
|
|
1889
|
+
if (Math.abs(connZ) > ZSCORE_THRESHOLD) {
|
|
1890
|
+
alerts.push({
|
|
1891
|
+
metric: "Connections",
|
|
1892
|
+
value: data.packets?.connections || data.network.connections || 0,
|
|
1893
|
+
zScore: connZ,
|
|
1894
|
+
severity: Math.abs(connZ) > CRITICAL_ZSCORE ? "critical" : "warning"
|
|
1895
|
+
});
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
if (data.gpu && history.gpuUtil.length >= MIN_SAMPLES) {
|
|
1900
|
+
const gpuMean = History.mean(history.gpuUtil);
|
|
1901
|
+
if (Math.abs(data.gpu.utilization - gpuMean) > MIN_DEV_GPU) {
|
|
1902
|
+
const gpuZ = History.zScore(data.gpu.utilization, history.gpuUtil);
|
|
1903
|
+
if (Math.abs(gpuZ) > ZSCORE_THRESHOLD) {
|
|
1904
|
+
alerts.push({
|
|
1905
|
+
metric: "GPU",
|
|
1906
|
+
value: data.gpu.utilization,
|
|
1907
|
+
zScore: gpuZ,
|
|
1908
|
+
severity: Math.abs(gpuZ) > CRITICAL_ZSCORE ? "critical" : "warning"
|
|
1909
|
+
});
|
|
1910
|
+
}
|
|
1741
1911
|
}
|
|
1742
1912
|
}
|
|
1743
1913
|
return alerts;
|
|
@@ -1781,7 +1951,8 @@ function tableCacheParams() {
|
|
|
1781
1951
|
state.treeView ? "1" : "0",
|
|
1782
1952
|
state.history.version,
|
|
1783
1953
|
JSON.stringify(state.visiblePanels),
|
|
1784
|
-
JSON.stringify(state.panelLayout)
|
|
1954
|
+
JSON.stringify(state.panelLayout),
|
|
1955
|
+
state.aiModel
|
|
1785
1956
|
].join("|");
|
|
1786
1957
|
}
|
|
1787
1958
|
function graphsCacheParams() {
|
|
@@ -1850,6 +2021,11 @@ function renderCustomizerOverlay(scrollOffset, maxVisibleItems) {
|
|
|
1850
2021
|
lines.push(`${prefix}${opt}: ${status}`);
|
|
1851
2022
|
} else if (opt === "Temperature Unit") {
|
|
1852
2023
|
lines.push(`${prefix}${opt}: ${chalk2.bold.green(state.tempUnit.toUpperCase())} [C, F]`);
|
|
2024
|
+
} else if (opt === "AI Model") {
|
|
2025
|
+
const curModel = AVAILABLE_AI_MODELS.find((m) => m.id === state.aiModel) || AVAILABLE_AI_MODELS[0];
|
|
2026
|
+
lines.push(`${prefix}${opt}: ${chalk2.bold.cyan(curModel.name)} ${chalk2.dim(`[${curModel.badge}]`)}`);
|
|
2027
|
+
} else if (opt === "Grid Panel Order") {
|
|
2028
|
+
lines.push(`${prefix}${opt}: ${chalk2.bold.green(state.panelLayout.join(" \u2192 "))}`);
|
|
1853
2029
|
} else {
|
|
1854
2030
|
const toggleKey = getToggleKey(opt);
|
|
1855
2031
|
const isVisible = toggleKey ? toggleKey === "tree" ? state.treeView : state.visiblePanels[toggleKey] !== false : true;
|
|
@@ -1906,6 +2082,14 @@ function render() {
|
|
|
1906
2082
|
writeFrame(renderCredits(cols, rows));
|
|
1907
2083
|
return;
|
|
1908
2084
|
}
|
|
2085
|
+
if (state.inputMode === "docker-alert") {
|
|
2086
|
+
writeFrame(renderDockerAlert(cols, rows));
|
|
2087
|
+
return;
|
|
2088
|
+
}
|
|
2089
|
+
if (state.inputMode === "ai-model") {
|
|
2090
|
+
writeFrame(renderAiModelSelector(cols, rows));
|
|
2091
|
+
return;
|
|
2092
|
+
}
|
|
1909
2093
|
const lines = [];
|
|
1910
2094
|
const theme = THEMES[state.currentTheme] || THEMES.default;
|
|
1911
2095
|
const params = tableCacheParams();
|
|
@@ -1945,7 +2129,8 @@ function render() {
|
|
|
1945
2129
|
panelLayout: state.panelLayout,
|
|
1946
2130
|
selectedProcessIndex: state.processSelectionIndex,
|
|
1947
2131
|
trackedPid: state.trackedPid,
|
|
1948
|
-
inspectProcess: state.inspectingProcess
|
|
2132
|
+
inspectProcess: state.inspectingProcess,
|
|
2133
|
+
aiModel: state.aiModel
|
|
1949
2134
|
});
|
|
1950
2135
|
_cachedTableData = state.lastData;
|
|
1951
2136
|
_cachedTableParams = cacheParams;
|
|
@@ -2042,7 +2227,8 @@ function footerLine() {
|
|
|
2042
2227
|
["l", state.logging ? "stop log" : "log"],
|
|
2043
2228
|
["f", state.exportFormat],
|
|
2044
2229
|
["+/-", `${state.interval}s`],
|
|
2045
|
-
["r", state.p2pServerRunning ? "stop p2p" : "start p2p"]
|
|
2230
|
+
["r", state.p2pServerRunning ? "stop p2p" : "start p2p"],
|
|
2231
|
+
["m", `ai:${state.aiModel.split("-")[0]}`]
|
|
2046
2232
|
];
|
|
2047
2233
|
const fmt = (list) => list.map(([k, label]) => `${chalk2.hex("#50fa7b").bold(k)} ${chalk2.dim(label)}`).join(chalk2.dim(" | "));
|
|
2048
2234
|
const str1 = fmt(row1);
|
|
@@ -2052,6 +2238,7 @@ function footerLine() {
|
|
|
2052
2238
|
if (state.logging) badges.push(chalk2.red.bold("\u25CF REC"));
|
|
2053
2239
|
if (state.activePanel !== "grid") badges.push(chalk2.cyan.bold(`\u25C9 ${state.activePanel.toUpperCase()}`));
|
|
2054
2240
|
if (state.p2pServerRunning) badges.push(chalk2.green.bold(`P2P ${state.p2pBind || "0.0.0.0"}:${state.p2pPort}`));
|
|
2241
|
+
badges.push(chalk2.magenta.bold(`AI:${state.aiModel}`));
|
|
2055
2242
|
const badgeStr = badges.join(" ") || " ";
|
|
2056
2243
|
return [str1, str2, badgeStr];
|
|
2057
2244
|
}
|
|
@@ -2113,6 +2300,28 @@ function checkAlerts(data) {
|
|
|
2113
2300
|
exec(state.alertCmd, { env: { ...process.env, PYRE_ALERT: alertMsg } }, () => {
|
|
2114
2301
|
});
|
|
2115
2302
|
}
|
|
2303
|
+
const cfg = readConfig();
|
|
2304
|
+
const channelConfig = {
|
|
2305
|
+
slackUrl: cfg.slackAlertUrl || void 0,
|
|
2306
|
+
discordUrl: cfg.discordAlertUrl || void 0,
|
|
2307
|
+
pushoverToken: cfg.pushoverToken || void 0,
|
|
2308
|
+
pushoverUser: cfg.pushoverUser || void 0,
|
|
2309
|
+
ntfyUrl: cfg.ntfyUrl || void 0
|
|
2310
|
+
};
|
|
2311
|
+
const hasChannels = Object.values(channelConfig).some((v) => !!v);
|
|
2312
|
+
if (hasChannels) {
|
|
2313
|
+
const severity = hasCriticalAnomaly ? "critical" : "warning";
|
|
2314
|
+
sendAlert({
|
|
2315
|
+
metric: reasons[0]?.split(" ")[0] ?? "System",
|
|
2316
|
+
value: data.cpu.usage,
|
|
2317
|
+
threshold: state.CPU_ALERT_PCT,
|
|
2318
|
+
unit: "%",
|
|
2319
|
+
host: data.header.hostname,
|
|
2320
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2321
|
+
severity
|
|
2322
|
+
}, channelConfig).catch(() => {
|
|
2323
|
+
});
|
|
2324
|
+
}
|
|
2116
2325
|
} else if (!hot && !anomalyTriggered && !watchdogTriggered) {
|
|
2117
2326
|
state.alerted = false;
|
|
2118
2327
|
}
|
|
@@ -2161,7 +2370,7 @@ function getReadmeLines() {
|
|
|
2161
2370
|
const __dirname = path2.dirname(__filename);
|
|
2162
2371
|
const rootDir = path2.resolve(__dirname, "..", "..");
|
|
2163
2372
|
const readmePath = path2.join(rootDir, "README.md");
|
|
2164
|
-
const content =
|
|
2373
|
+
const content = fs3.readFileSync(readmePath, "utf8");
|
|
2165
2374
|
cachedReadmeLines = content.split("\n");
|
|
2166
2375
|
return cachedReadmeLines;
|
|
2167
2376
|
} catch (err) {
|
|
@@ -2206,11 +2415,57 @@ function renderCredits(cols, rows) {
|
|
|
2206
2415
|
out.push(...panel("CREDITS", content, cols, chalk2.magenta, chalk2.dim, rows - 2));
|
|
2207
2416
|
return out;
|
|
2208
2417
|
}
|
|
2418
|
+
function renderDockerAlert(cols, rows) {
|
|
2419
|
+
const out = [];
|
|
2420
|
+
const content = [
|
|
2421
|
+
chalk2.bold.red("Docker Container Detected"),
|
|
2422
|
+
"",
|
|
2423
|
+
"Pyre is running inside a container. Some hardware sensors",
|
|
2424
|
+
"(battery, thermal, power) may not be available or relevant.",
|
|
2425
|
+
"",
|
|
2426
|
+
chalk2.cyan("Do you wish to continue in Docker mode?"),
|
|
2427
|
+
""
|
|
2428
|
+
];
|
|
2429
|
+
const options = ["Yes, use Docker mode (hide hardware sensors)", "No, launch regular TUI with all features"];
|
|
2430
|
+
for (let i = 0; i < options.length; i++) {
|
|
2431
|
+
const isSelected = i === state.dockerSelectionIndex;
|
|
2432
|
+
const prefix = isSelected ? chalk2.cyan.bold(" \u25B6 ") : " ";
|
|
2433
|
+
const text = isSelected ? chalk2.bgCyan.black(` ${options[i]} `) : chalk2.dim(` ${options[i]} `);
|
|
2434
|
+
content.push(prefix + text);
|
|
2435
|
+
}
|
|
2436
|
+
content.push("");
|
|
2437
|
+
content.push(chalk2.dim("Press \u2191/\u2193 to select, Enter to confirm, Esc to skip."));
|
|
2438
|
+
out.push(...panel("DOCKER DETECTED", content, cols, chalk2.red, chalk2.dim, rows - 2));
|
|
2439
|
+
return out;
|
|
2440
|
+
}
|
|
2441
|
+
function renderAiModelSelector(cols, rows) {
|
|
2442
|
+
const out = [];
|
|
2443
|
+
const content = [
|
|
2444
|
+
chalk2.bold.hex("#ff6004")("Choose AI Anomaly Explanation Model"),
|
|
2445
|
+
chalk2.dim("Select your preferred diagnosis engine. Offline models run entirely in-process."),
|
|
2446
|
+
""
|
|
2447
|
+
];
|
|
2448
|
+
for (let i = 0; i < AVAILABLE_AI_MODELS.length; i++) {
|
|
2449
|
+
const m = AVAILABLE_AI_MODELS[i];
|
|
2450
|
+
const isSelected = i === state.modelSelectionIndex;
|
|
2451
|
+
const isCurrent = m.id === state.aiModel;
|
|
2452
|
+
const prefix = isSelected ? chalk2.cyan.bold(" \u25B6 ") : " ";
|
|
2453
|
+
const curBadge = isCurrent ? chalk2.bgGreen.black.bold(" ACTIVE ") : "";
|
|
2454
|
+
const badgeStr = m.offline ? chalk2.green(`[${m.badge}]`) : chalk2.yellow(`[${m.badge}]`);
|
|
2455
|
+
const nameStr = isSelected ? chalk2.bgCyan.black.bold(` ${m.name} `) : chalk2.bold.white(m.name);
|
|
2456
|
+
content.push(`${prefix}${nameStr} ${badgeStr} ${curBadge}`);
|
|
2457
|
+
content.push(chalk2.dim(` ${m.description}`));
|
|
2458
|
+
content.push("");
|
|
2459
|
+
}
|
|
2460
|
+
content.push(chalk2.dim("Use \u2191/\u2193 or J/K to navigate \xB7 Enter/Space to select \xB7 Esc to cancel"));
|
|
2461
|
+
out.push(...panel("AI MODEL SELECTOR", content, cols, chalk2.cyan, chalk2.dim, Math.max(1, rows - 2)));
|
|
2462
|
+
return out;
|
|
2463
|
+
}
|
|
2209
2464
|
|
|
2210
2465
|
// src/p2p/server.ts
|
|
2211
2466
|
import net from "net";
|
|
2212
2467
|
import tls from "tls";
|
|
2213
|
-
import
|
|
2468
|
+
import fs4 from "fs";
|
|
2214
2469
|
import crypto2 from "crypto";
|
|
2215
2470
|
import chalk3 from "chalk";
|
|
2216
2471
|
|
|
@@ -2355,9 +2610,9 @@ var P2PServer = class {
|
|
|
2355
2610
|
async start() {
|
|
2356
2611
|
if (this.options.auditLog) {
|
|
2357
2612
|
try {
|
|
2358
|
-
|
|
2613
|
+
fs4.mkdirSync(this.options.auditLog, { recursive: true });
|
|
2359
2614
|
const logFile = `${this.options.auditLog}/p2p-audit.log`;
|
|
2360
|
-
this.auditStream =
|
|
2615
|
+
this.auditStream = fs4.createWriteStream(logFile, { flags: "a" });
|
|
2361
2616
|
} catch {
|
|
2362
2617
|
this.log(chalk3.yellow("Warning: could not open audit log"));
|
|
2363
2618
|
}
|
|
@@ -2366,11 +2621,11 @@ var P2PServer = class {
|
|
|
2366
2621
|
const useTLS = !!(this.options.tlsCert && this.options.tlsKey);
|
|
2367
2622
|
if (useTLS) {
|
|
2368
2623
|
const tlsOptions = {
|
|
2369
|
-
key:
|
|
2370
|
-
cert:
|
|
2624
|
+
key: fs4.readFileSync(this.options.tlsKey),
|
|
2625
|
+
cert: fs4.readFileSync(this.options.tlsCert)
|
|
2371
2626
|
};
|
|
2372
2627
|
if (this.options.tlsCA) {
|
|
2373
|
-
tlsOptions.ca =
|
|
2628
|
+
tlsOptions.ca = fs4.readFileSync(this.options.tlsCA);
|
|
2374
2629
|
}
|
|
2375
2630
|
this.server = tls.createServer(tlsOptions, (socket) => this.onConnection(socket));
|
|
2376
2631
|
} else {
|
|
@@ -2638,7 +2893,7 @@ async function startP2PServer(options) {
|
|
|
2638
2893
|
// src/p2p/client.ts
|
|
2639
2894
|
import net2 from "net";
|
|
2640
2895
|
import tls2 from "tls";
|
|
2641
|
-
import
|
|
2896
|
+
import fs5 from "fs";
|
|
2642
2897
|
import crypto3 from "crypto";
|
|
2643
2898
|
import chalk4 from "chalk";
|
|
2644
2899
|
var P2PClient = class {
|
|
@@ -2666,9 +2921,9 @@ var P2PClient = class {
|
|
|
2666
2921
|
async connect() {
|
|
2667
2922
|
if (this.options.auditLog) {
|
|
2668
2923
|
try {
|
|
2669
|
-
|
|
2924
|
+
fs5.mkdirSync(this.options.auditLog, { recursive: true });
|
|
2670
2925
|
const logFile = `${this.options.auditLog}/p2p-client-audit.log`;
|
|
2671
|
-
this.auditStream =
|
|
2926
|
+
this.auditStream = fs5.createWriteStream(logFile, { flags: "a" });
|
|
2672
2927
|
} catch {
|
|
2673
2928
|
console.log(chalk4.yellow("Warning: could not open audit log"));
|
|
2674
2929
|
}
|
|
@@ -2683,7 +2938,7 @@ var P2PClient = class {
|
|
|
2683
2938
|
rejectUnauthorized: !this.options.tlsInsecure
|
|
2684
2939
|
};
|
|
2685
2940
|
if (this.options.tlsCA) {
|
|
2686
|
-
tlsOptions.ca =
|
|
2941
|
+
tlsOptions.ca = fs5.readFileSync(this.options.tlsCA);
|
|
2687
2942
|
}
|
|
2688
2943
|
this.socket = tls2.connect(tlsOptions, () => {
|
|
2689
2944
|
this.running = true;
|
|
@@ -2872,6 +3127,58 @@ function deriveHMACKey2(password) {
|
|
|
2872
3127
|
return crypto3.createHash("sha256").update(password).digest("hex").slice(0, 32);
|
|
2873
3128
|
}
|
|
2874
3129
|
|
|
3130
|
+
// src/plugins/index.ts
|
|
3131
|
+
import fs6 from "fs";
|
|
3132
|
+
import path3 from "path";
|
|
3133
|
+
import { pathToFileURL } from "url";
|
|
3134
|
+
var loadedPlugins = /* @__PURE__ */ new Map();
|
|
3135
|
+
function getPluginsDir() {
|
|
3136
|
+
return path3.join(getConfigDir(), "plugins");
|
|
3137
|
+
}
|
|
3138
|
+
async function loadPlugins() {
|
|
3139
|
+
const pluginsDir = getPluginsDir();
|
|
3140
|
+
if (!fs6.existsSync(pluginsDir)) {
|
|
3141
|
+
try {
|
|
3142
|
+
fs6.mkdirSync(pluginsDir, { recursive: true });
|
|
3143
|
+
} catch {
|
|
3144
|
+
return loadedPlugins;
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
3147
|
+
let files = [];
|
|
3148
|
+
try {
|
|
3149
|
+
files = fs6.readdirSync(pluginsDir).filter((f) => f.endsWith(".js") || f.endsWith(".mjs"));
|
|
3150
|
+
} catch {
|
|
3151
|
+
return loadedPlugins;
|
|
3152
|
+
}
|
|
3153
|
+
for (const file of files) {
|
|
3154
|
+
const filePath = path3.join(pluginsDir, file);
|
|
3155
|
+
try {
|
|
3156
|
+
const fileUrl = pathToFileURL(filePath).href;
|
|
3157
|
+
const mod = await import(`${fileUrl}?t=${Date.now()}`);
|
|
3158
|
+
const plugin = mod.default || mod;
|
|
3159
|
+
if (plugin && typeof plugin.collect === "function") {
|
|
3160
|
+
const name = plugin.name || path3.basename(file, path3.extname(file));
|
|
3161
|
+
if (!loadedPlugins.has(name)) {
|
|
3162
|
+
loadedPlugins.set(name, {
|
|
3163
|
+
plugin: { ...plugin, name },
|
|
3164
|
+
lastRun: 0,
|
|
3165
|
+
lastOutput: { title: plugin.title || name, lines: ["Collecting..."] }
|
|
3166
|
+
});
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
} catch (err) {
|
|
3170
|
+
const name = path3.basename(file, path3.extname(file));
|
|
3171
|
+
loadedPlugins.set(name, {
|
|
3172
|
+
plugin: { name, collect: async () => ({ lines: [`Error loading plugin: ${err.message}`] }) },
|
|
3173
|
+
lastRun: Date.now(),
|
|
3174
|
+
lastOutput: { title: name, lines: [`Failed to load: ${err.message}`] },
|
|
3175
|
+
error: err.message
|
|
3176
|
+
});
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
return loadedPlugins;
|
|
3180
|
+
}
|
|
3181
|
+
|
|
2875
3182
|
// src/live/input.ts
|
|
2876
3183
|
function persistConfig() {
|
|
2877
3184
|
writeConfig({
|
|
@@ -2893,7 +3200,10 @@ function persistConfig() {
|
|
|
2893
3200
|
splashColorScheme: state.splashColorScheme,
|
|
2894
3201
|
splashAnimation: state.splashAnimation,
|
|
2895
3202
|
visiblePanels: { ...state.visiblePanels },
|
|
2896
|
-
notificationsEnabled: state.notificationsEnabled
|
|
3203
|
+
notificationsEnabled: state.notificationsEnabled,
|
|
3204
|
+
dockerModeConfirmed: state.dockerModeConfirmed,
|
|
3205
|
+
aiModel: state.aiModel,
|
|
3206
|
+
aiBackend: state.aiBackend
|
|
2897
3207
|
});
|
|
2898
3208
|
}
|
|
2899
3209
|
function isUpKey(key, str) {
|
|
@@ -2947,6 +3257,24 @@ function handleInputModeKey(str, key) {
|
|
|
2947
3257
|
} else if (selected === "Temperature Unit") {
|
|
2948
3258
|
state.tempUnit = state.tempUnit === "c" ? "f" : "c";
|
|
2949
3259
|
setStatus(`Temperature unit: ${state.tempUnit.toUpperCase()}`);
|
|
3260
|
+
} else if (selected === "AI Model") {
|
|
3261
|
+
const curIdx = AVAILABLE_AI_MODELS.findIndex((m) => m.id === state.aiModel);
|
|
3262
|
+
const nextIdx = (curIdx + 1) % AVAILABLE_AI_MODELS.length;
|
|
3263
|
+
state.aiModel = AVAILABLE_AI_MODELS[nextIdx].id;
|
|
3264
|
+
state.aiBackend = AVAILABLE_AI_MODELS[nextIdx].backend;
|
|
3265
|
+
setStatus(`AI Model: ${AVAILABLE_AI_MODELS[nextIdx].name}`);
|
|
3266
|
+
} else if (selected === "Grid Panel Order") {
|
|
3267
|
+
const layouts = [
|
|
3268
|
+
["mem", "disk", "net"],
|
|
3269
|
+
["disk", "mem", "net"],
|
|
3270
|
+
["net", "mem", "disk"],
|
|
3271
|
+
["mem", "net", "disk"]
|
|
3272
|
+
];
|
|
3273
|
+
const currentStr = state.panelLayout.join(",");
|
|
3274
|
+
const currentIdx = layouts.findIndex((l) => l.join(",") === currentStr);
|
|
3275
|
+
const nextIdx = (currentIdx + 1) % layouts.length;
|
|
3276
|
+
state.panelLayout = layouts[nextIdx];
|
|
3277
|
+
setStatus(`Panel layout: ${state.panelLayout.join(" \u2192 ")}`);
|
|
2950
3278
|
} else {
|
|
2951
3279
|
const toggleKey = getToggleKey(selected);
|
|
2952
3280
|
if (toggleKey) {
|
|
@@ -3028,6 +3356,76 @@ function handleInputModeKey(str, key) {
|
|
|
3028
3356
|
}
|
|
3029
3357
|
return;
|
|
3030
3358
|
}
|
|
3359
|
+
if (state.inputMode === "docker-alert") {
|
|
3360
|
+
if (isEscKey(key, str)) {
|
|
3361
|
+
state.inputMode = null;
|
|
3362
|
+
state.dockerModeConfirmed = true;
|
|
3363
|
+
render();
|
|
3364
|
+
return;
|
|
3365
|
+
}
|
|
3366
|
+
if (isUpKey(key, str)) {
|
|
3367
|
+
state.dockerSelectionIndex = Math.max(0, state.dockerSelectionIndex - 1);
|
|
3368
|
+
render();
|
|
3369
|
+
return;
|
|
3370
|
+
}
|
|
3371
|
+
if (isDownKey(key, str)) {
|
|
3372
|
+
state.dockerSelectionIndex = Math.min(1, state.dockerSelectionIndex + 1);
|
|
3373
|
+
render();
|
|
3374
|
+
return;
|
|
3375
|
+
}
|
|
3376
|
+
if (isEnterKey(key, str) || str === " ") {
|
|
3377
|
+
state.dockerModeConfirmed = true;
|
|
3378
|
+
state.inputMode = null;
|
|
3379
|
+
if (state.dockerSelectionIndex === 0) {
|
|
3380
|
+
state.visiblePanels.battery = false;
|
|
3381
|
+
state.visiblePanels.thermal = false;
|
|
3382
|
+
state.visiblePanels.power = false;
|
|
3383
|
+
state.visiblePanels.gpu = false;
|
|
3384
|
+
state.detailed = false;
|
|
3385
|
+
} else {
|
|
3386
|
+
state.visiblePanels.battery = true;
|
|
3387
|
+
state.visiblePanels.thermal = true;
|
|
3388
|
+
state.visiblePanels.power = true;
|
|
3389
|
+
state.visiblePanels.gpu = true;
|
|
3390
|
+
}
|
|
3391
|
+
invalidateTableCache();
|
|
3392
|
+
invalidateFrame();
|
|
3393
|
+
persistConfig();
|
|
3394
|
+
render();
|
|
3395
|
+
return;
|
|
3396
|
+
}
|
|
3397
|
+
return;
|
|
3398
|
+
}
|
|
3399
|
+
if (state.inputMode === "ai-model") {
|
|
3400
|
+
if (isEscKey(key, str)) {
|
|
3401
|
+
state.inputMode = null;
|
|
3402
|
+
render();
|
|
3403
|
+
return;
|
|
3404
|
+
}
|
|
3405
|
+
if (isUpKey(key, str) || str === "k") {
|
|
3406
|
+
state.modelSelectionIndex = (state.modelSelectionIndex - 1 + AVAILABLE_AI_MODELS.length) % AVAILABLE_AI_MODELS.length;
|
|
3407
|
+
render();
|
|
3408
|
+
return;
|
|
3409
|
+
}
|
|
3410
|
+
if (isDownKey(key, str) || str === "j") {
|
|
3411
|
+
state.modelSelectionIndex = (state.modelSelectionIndex + 1) % AVAILABLE_AI_MODELS.length;
|
|
3412
|
+
render();
|
|
3413
|
+
return;
|
|
3414
|
+
}
|
|
3415
|
+
if (isEnterKey(key, str) || str === " ") {
|
|
3416
|
+
const chosen = AVAILABLE_AI_MODELS[state.modelSelectionIndex];
|
|
3417
|
+
state.aiModel = chosen.id;
|
|
3418
|
+
state.aiBackend = chosen.backend;
|
|
3419
|
+
state.inputMode = null;
|
|
3420
|
+
setStatus(`Active AI Model: ${chosen.name}`, 4e3);
|
|
3421
|
+
invalidateTableCache();
|
|
3422
|
+
invalidateFrame();
|
|
3423
|
+
persistConfig();
|
|
3424
|
+
render();
|
|
3425
|
+
return;
|
|
3426
|
+
}
|
|
3427
|
+
return;
|
|
3428
|
+
}
|
|
3031
3429
|
if (state.inputMode === "signal") {
|
|
3032
3430
|
if (isUpKey(key, str)) {
|
|
3033
3431
|
const idx = state.SIGNAL_OPTIONS.indexOf(state.inputBuffer);
|
|
@@ -3260,6 +3658,8 @@ async function startLive(opts2, splashPromise) {
|
|
|
3260
3658
|
state.termHeight = process.stdout.rows || 24;
|
|
3261
3659
|
state.history.reset();
|
|
3262
3660
|
state.history.setMaxLen(Math.max(20, Math.min(200, state.termWidth - 30)));
|
|
3661
|
+
await loadPlugins().catch(() => {
|
|
3662
|
+
});
|
|
3263
3663
|
const warmupPromise = doWarmup();
|
|
3264
3664
|
if (splashPromise) {
|
|
3265
3665
|
await splashPromise;
|
|
@@ -3358,6 +3758,18 @@ async function startLive(opts2, splashPromise) {
|
|
|
3358
3758
|
render();
|
|
3359
3759
|
return;
|
|
3360
3760
|
}
|
|
3761
|
+
case "C": {
|
|
3762
|
+
state.activePanel = state.activePanel === "containers" ? "grid" : "containers";
|
|
3763
|
+
setStatus(state.activePanel === "grid" ? "Grid view" : "Containers panel");
|
|
3764
|
+
render();
|
|
3765
|
+
return;
|
|
3766
|
+
}
|
|
3767
|
+
case "B": {
|
|
3768
|
+
state.activePanel = state.activePanel === "blender" ? "grid" : "blender";
|
|
3769
|
+
setStatus(state.activePanel === "grid" ? "Grid view" : "Blender panel");
|
|
3770
|
+
render();
|
|
3771
|
+
return;
|
|
3772
|
+
}
|
|
3361
3773
|
}
|
|
3362
3774
|
switch (key.name) {
|
|
3363
3775
|
case "r":
|
|
@@ -3385,6 +3797,11 @@ async function startLive(opts2, splashPromise) {
|
|
|
3385
3797
|
state.customizerIndex = 0;
|
|
3386
3798
|
render();
|
|
3387
3799
|
break;
|
|
3800
|
+
case "m":
|
|
3801
|
+
state.inputMode = "ai-model";
|
|
3802
|
+
state.modelSelectionIndex = Math.max(0, AVAILABLE_AI_MODELS.findIndex((m) => m.id === state.aiModel));
|
|
3803
|
+
render();
|
|
3804
|
+
break;
|
|
3388
3805
|
case "p":
|
|
3389
3806
|
state.paused = !state.paused;
|
|
3390
3807
|
setStatus(state.paused ? "Paused" : "Resumed");
|
|
@@ -3531,7 +3948,7 @@ async function startLive(opts2, splashPromise) {
|
|
|
3531
3948
|
process.once("SIGINT", () => stopLive());
|
|
3532
3949
|
}
|
|
3533
3950
|
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" };
|
|
3951
|
+
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
3952
|
return map[str] ?? null;
|
|
3536
3953
|
}
|
|
3537
3954
|
function cycleTab(direction) {
|
|
@@ -3955,8 +4372,8 @@ async function showSplash(config3) {
|
|
|
3955
4372
|
}
|
|
3956
4373
|
|
|
3957
4374
|
// src/historyCmd.ts
|
|
3958
|
-
import
|
|
3959
|
-
import
|
|
4375
|
+
import fs7 from "fs";
|
|
4376
|
+
import path4 from "path";
|
|
3960
4377
|
import chalk6 from "chalk";
|
|
3961
4378
|
function runHistoryCommand(opts2 = {}) {
|
|
3962
4379
|
const days = opts2.days || 7;
|
|
@@ -3964,11 +4381,11 @@ function runHistoryCommand(opts2 = {}) {
|
|
|
3964
4381
|
console.log(chalk6.bold(`
|
|
3965
4382
|
pyre history \u2014 Past ${days} Days Trend
|
|
3966
4383
|
`));
|
|
3967
|
-
if (!
|
|
4384
|
+
if (!fs7.existsSync(dir)) {
|
|
3968
4385
|
console.log(chalk6.yellow(` No logs directory found at ${dir}`));
|
|
3969
4386
|
return;
|
|
3970
4387
|
}
|
|
3971
|
-
const files =
|
|
4388
|
+
const files = fs7.readdirSync(dir).filter((f) => f.startsWith("pyre-log-") && f.endsWith(".csv"));
|
|
3972
4389
|
if (files.length === 0) {
|
|
3973
4390
|
console.log(chalk6.yellow(` No CSV logs found in ${dir}`));
|
|
3974
4391
|
return;
|
|
@@ -3978,9 +4395,9 @@ function runHistoryCommand(opts2 = {}) {
|
|
|
3978
4395
|
const memPoints = [];
|
|
3979
4396
|
const tempPoints = [];
|
|
3980
4397
|
for (const file of files.sort()) {
|
|
3981
|
-
const fullPath =
|
|
4398
|
+
const fullPath = path4.join(dir, file);
|
|
3982
4399
|
try {
|
|
3983
|
-
const content =
|
|
4400
|
+
const content = fs7.readFileSync(fullPath, "utf-8");
|
|
3984
4401
|
const lines = content.split("\n").slice(1);
|
|
3985
4402
|
for (const line of lines) {
|
|
3986
4403
|
if (!line.trim()) continue;
|
|
@@ -4015,7 +4432,7 @@ function runHistoryCommand(opts2 = {}) {
|
|
|
4015
4432
|
}
|
|
4016
4433
|
|
|
4017
4434
|
// src/diffCmd.ts
|
|
4018
|
-
import
|
|
4435
|
+
import fs8 from "fs";
|
|
4019
4436
|
import chalk7 from "chalk";
|
|
4020
4437
|
function runDiffCommand(file1, file2) {
|
|
4021
4438
|
console.log(chalk7.bold(`
|
|
@@ -4025,8 +4442,8 @@ function runDiffCommand(file1, file2) {
|
|
|
4025
4442
|
console.log(chalk7.dim(` Snapshot B: ${file2}
|
|
4026
4443
|
`));
|
|
4027
4444
|
try {
|
|
4028
|
-
const dataA = JSON.parse(
|
|
4029
|
-
const dataB = JSON.parse(
|
|
4445
|
+
const dataA = JSON.parse(fs8.readFileSync(file1, "utf-8"));
|
|
4446
|
+
const dataB = JSON.parse(fs8.readFileSync(file2, "utf-8"));
|
|
4030
4447
|
const diffNum = (label, valA, valB, unit = "") => {
|
|
4031
4448
|
const delta = valB - valA;
|
|
4032
4449
|
const sign = delta > 0 ? "+" : "";
|
|
@@ -4050,8 +4467,8 @@ function runDiffCommand(file1, file2) {
|
|
|
4050
4467
|
}
|
|
4051
4468
|
|
|
4052
4469
|
// src/xbar.ts
|
|
4053
|
-
import
|
|
4054
|
-
import
|
|
4470
|
+
import fs9 from "fs";
|
|
4471
|
+
import path5 from "path";
|
|
4055
4472
|
import os from "os";
|
|
4056
4473
|
import chalk8 from "chalk";
|
|
4057
4474
|
function generateXbarPlugin() {
|
|
@@ -4062,10 +4479,9 @@ function generateXbarPlugin() {
|
|
|
4062
4479
|
# <xbar.version>v1.0</xbar.version>
|
|
4063
4480
|
# <xbar.author>somalip</xbar.author>
|
|
4064
4481
|
# <xbar.author.github>somalip</xbar.author.github>
|
|
4065
|
-
# <xbar.desc>
|
|
4482
|
+
# <xbar.desc>Cross-platform system monitoring powered by pyre.</xbar.desc>
|
|
4066
4483
|
|
|
4067
4484
|
# Run pyre to get a JSON snapshot
|
|
4068
|
-
# We use node directly to avoid env issues in xbar
|
|
4069
4485
|
DATA=$("${nodePath}" "${pyrePath}" --once --json)
|
|
4070
4486
|
|
|
4071
4487
|
if [ -z "$DATA" ]; then
|
|
@@ -4088,15 +4504,25 @@ echo "\u{1F525} C:\${CPU}% M:\${MEM}% T:\${TEMP}\xB0C | font=Menlo"
|
|
|
4088
4504
|
echo "---"
|
|
4089
4505
|
echo "Open Dashboard | bash='${nodePath}' param1='${pyrePath}' param2='live' terminal=true"
|
|
4090
4506
|
`;
|
|
4091
|
-
const pluginDir = path4.join(os.homedir(), "Library", "Application Support", "xbar", "plugins");
|
|
4092
4507
|
const filename = "pyre-monitor.2s.sh";
|
|
4093
|
-
const outPath =
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
console.log(`
|
|
4508
|
+
const outPath = path5.join(process.cwd(), filename);
|
|
4509
|
+
fs9.writeFileSync(outPath, script);
|
|
4510
|
+
try {
|
|
4511
|
+
fs9.chmodSync(outPath, 493);
|
|
4512
|
+
} catch {
|
|
4513
|
+
}
|
|
4514
|
+
console.log(chalk8.green(`Generated menu bar plugin script at ${outPath}`));
|
|
4515
|
+
if (process.platform === "darwin") {
|
|
4516
|
+
const pluginDir = path5.join(os.homedir(), "Library", "Application Support", "xbar", "plugins");
|
|
4517
|
+
console.log(`To install in xbar or SwiftBar on macOS:`);
|
|
4518
|
+
console.log(chalk8.cyan(` mv ${filename} "${pluginDir}/"`));
|
|
4519
|
+
} else if (process.platform === "linux") {
|
|
4520
|
+
const argosDir = path5.join(os.homedir(), ".config", "argos");
|
|
4521
|
+
console.log(`To install in Argos (GNOME Shell) on Linux:`);
|
|
4522
|
+
console.log(chalk8.cyan(` mkdir -p "${argosDir}" && mv ${filename} "${argosDir}/"`));
|
|
4523
|
+
} else {
|
|
4524
|
+
console.log(chalk8.dim(`Compatible with BitBar, xbar, and Argos shell plugins.`));
|
|
4525
|
+
}
|
|
4100
4526
|
}
|
|
4101
4527
|
|
|
4102
4528
|
// src/fleet.ts
|
|
@@ -4130,36 +4556,49 @@ async function runFleetCommand(hosts) {
|
|
|
4130
4556
|
|
|
4131
4557
|
// src/index.ts
|
|
4132
4558
|
var config2 = readConfig();
|
|
4133
|
-
var pkg = JSON.parse(
|
|
4559
|
+
var pkg = JSON.parse(fs10.readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
|
|
4134
4560
|
var program = new Command();
|
|
4135
|
-
program.name("pyre").version(pkg.version).description("
|
|
4561
|
+
program.name("pyre").version(pkg.version).description("Cross-platform 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").option("--prometheus-port <port>", "Port number for Prometheus exporter (default: 9090)", "9090").option("--speed <n>", "Playback speed multiplier for pyre replay (default: 1)", "1").option("--llm-backend <backend>", "AI backend: builtin (local zero-setup), ollama, or openai (default: builtin)", "builtin").option("--llm-model <model>", "AI model name for pyre explain (default: expert-rules-v1 / llama3.2 / gpt-4o-mini)").option("--api-key <key>", "API key for OpenAI-compatible LLM backend (or set OPENAI_API_KEY env)").option("--alert-slack <url>", "Slack webhook URL for alert notifications").option("--alert-discord <url>", "Discord webhook URL for alert notifications").option("--alert-pushover-token <token>", "Pushover application token for alert notifications").option("--alert-pushover-user <user>", "Pushover user key for alert notifications").option("--alert-ntfy <url>", "ntfy.sh topic URL for alert notifications (e.g. https://ntfy.sh/my-topic)").option("--watchdog-config <file>", "Path to custom watchdog JSON configuration rules").option("--dry-run", "Simulate actions in watchdog mode without executing kills").option("--top <n>", "Number of items to display in netusage command (default: 15)").option("--raw", "Print unparsed output for profiler").option("--sync-dir <dir>", "Custom directory path for cloud profile/config sync").option("--scan", "Perform active network sweep on LAN neighbors for topology map");
|
|
4136
4562
|
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
|
-
|
|
4563
|
+
Commands:
|
|
4564
|
+
live Interactive live dashboard
|
|
4565
|
+
check One-line plain-English health summary
|
|
4566
|
+
pipe Continuous newline-delimited JSON stream for scripting
|
|
4567
|
+
stress Synthetic CPU/GPU load generator
|
|
4568
|
+
ui Launch the live UI dashboard window
|
|
4569
|
+
web Serve an auto-refreshing live web portal
|
|
4570
|
+
ssh <host> Stream live stats from a remote host over SSH
|
|
4571
|
+
fleet <host1> [host2]... Multi-host live dashboard monitoring multiple hosts
|
|
4572
|
+
bench <cmd> Log CPU, memory, network, power draw, and estimate kWh cost
|
|
4573
|
+
benchmark Run a 1-minute CPU benchmark calculating digits of PI
|
|
4574
|
+
anomalies Compute z-score anomalies and print plain-language digest
|
|
4575
|
+
doctor Run system diagnostics
|
|
4576
|
+
extensions System Extensions / Driver inspector
|
|
4577
|
+
brew Package manager health panel (brew/apt/pacman/winget)
|
|
4578
|
+
update Check for pyre-cli updates
|
|
4579
|
+
profile <save|load|list> Atomic configuration profile management
|
|
4580
|
+
config <show|reset> View or reset persistent configuration file
|
|
4581
|
+
history Graph historical resource trends from CSV logs
|
|
4582
|
+
diff <file1> <file2> Compare two saved snapshot files side-by-side
|
|
4583
|
+
info Concise hardware summary
|
|
4584
|
+
completions <shell> Generate shell auto-completion scripts (zsh, bash, fish, powershell)
|
|
4585
|
+
xbar Generate an xbar / SwiftBar menu bar plugin script
|
|
4586
|
+
p2p <server|connect> Start a P2P server or connect to one
|
|
4587
|
+
server Print commands for starting P2P server/client
|
|
4588
|
+
blender Track active Blender render jobs and their progress
|
|
4589
|
+
prometheus Start a Prometheus metrics exporter on localhost
|
|
4590
|
+
smart S.M.A.R.T. disk health report (requires smartmontools)
|
|
4591
|
+
replay <file> Play back a pyre CSV log file in the TUI
|
|
4592
|
+
explain AI-powered plain-English anomaly explanation (Ollama/OpenAI)
|
|
4593
|
+
alert test Send a test notification to all configured alert channels
|
|
4594
|
+
netusage Real-time per-application network usage breakdown
|
|
4595
|
+
serve Lightweight structured JSON REST API server
|
|
4596
|
+
profile-proc <pid> CPU call-tree profiler and hot function sampler
|
|
4597
|
+
watchdog Persistent autonomous monitoring daemon and rules engine
|
|
4598
|
+
build Active compiler and build system tracker
|
|
4599
|
+
topo Network topology mapper and pyre peer auto-discovery
|
|
4600
|
+
sync <push|pull|status> Synchronize config profiles across Macs via iCloud/Drive
|
|
4601
|
+
`);
|
|
4163
4602
|
program.parse(process.argv);
|
|
4164
4603
|
var opts = program.opts();
|
|
4165
4604
|
if (opts.plain || opts.a11y) {
|
|
@@ -4219,11 +4658,11 @@ async function runServerCommand() {
|
|
|
4219
4658
|
console.log();
|
|
4220
4659
|
}
|
|
4221
4660
|
async function main() {
|
|
4222
|
-
const { runSetupWizardIfNeeded } = await import("./wizard-
|
|
4661
|
+
const { runSetupWizardIfNeeded } = await import("./wizard-TD7BGX6O.js");
|
|
4223
4662
|
await runSetupWizardIfNeeded(!opts.wizard);
|
|
4224
4663
|
const cmd = program.args[0];
|
|
4225
4664
|
if (cmd === "check") {
|
|
4226
|
-
const { runCheckCommand } = await import("./checkCmd-
|
|
4665
|
+
const { runCheckCommand } = await import("./checkCmd-RKW6QGDR.js");
|
|
4227
4666
|
await runCheckCommand({
|
|
4228
4667
|
exitCode: !!opts.exitCode,
|
|
4229
4668
|
cpuAlertPct: config2.cpuAlertPct,
|
|
@@ -4233,7 +4672,7 @@ async function main() {
|
|
|
4233
4672
|
return;
|
|
4234
4673
|
}
|
|
4235
4674
|
if (cmd === "pipe") {
|
|
4236
|
-
const { runPipeCommand } = await import("./pipeCmd-
|
|
4675
|
+
const { runPipeCommand } = await import("./pipeCmd-7REDBLBU.js");
|
|
4237
4676
|
const intervalSec = parseFloat(opts.interval) || 1;
|
|
4238
4677
|
const samples = parseInt(opts.samples, 10) || 0;
|
|
4239
4678
|
await runPipeCommand({
|
|
@@ -4256,12 +4695,176 @@ async function main() {
|
|
|
4256
4695
|
return;
|
|
4257
4696
|
}
|
|
4258
4697
|
if (cmd === "extensions") {
|
|
4259
|
-
const { printExtensionsReport } = await import("./extensions-
|
|
4698
|
+
const { printExtensionsReport } = await import("./extensions-XJGHG7X3.js");
|
|
4260
4699
|
await printExtensionsReport();
|
|
4261
4700
|
return;
|
|
4262
4701
|
}
|
|
4702
|
+
if (cmd === "blender") {
|
|
4703
|
+
const { collectBlenderRenders } = await import("./blender-FGH3ASEQ.js");
|
|
4704
|
+
const renders = await collectBlenderRenders();
|
|
4705
|
+
if (!renders.length) {
|
|
4706
|
+
console.log(chalk10.dim("No active Blender renders detected."));
|
|
4707
|
+
return;
|
|
4708
|
+
}
|
|
4709
|
+
const lines = [];
|
|
4710
|
+
lines.push(chalk10.bold(" Blender Render Tracker"));
|
|
4711
|
+
lines.push("");
|
|
4712
|
+
for (const r of renders) {
|
|
4713
|
+
const statusColor = r.status === "rendering" ? chalk10.green : r.status === "finalizing" ? chalk10.yellow : r.status === "loading" ? chalk10.cyan : chalk10.dim;
|
|
4714
|
+
lines.push(` PID: ${r.pid}`);
|
|
4715
|
+
lines.push(` Blend File: ${r.blendFile}`);
|
|
4716
|
+
lines.push(` Engine: ${r.renderEngine}`);
|
|
4717
|
+
lines.push(` Frames: ${r.currentFrame} / ${r.totalFrames} (${r.completionPercent}%)`);
|
|
4718
|
+
lines.push(` Elapsed: ${r.elapsedSec}s`);
|
|
4719
|
+
lines.push(` Status: ${statusColor(r.status)}`);
|
|
4720
|
+
lines.push(` Output: ${r.outputPath}`);
|
|
4721
|
+
if (r.sampleCount !== void 0) lines.push(` Samples: ${r.sampleCount}`);
|
|
4722
|
+
lines.push("");
|
|
4723
|
+
}
|
|
4724
|
+
console.log(lines.join("\n"));
|
|
4725
|
+
return;
|
|
4726
|
+
}
|
|
4727
|
+
if (cmd === "prometheus") {
|
|
4728
|
+
const { runPrometheusServer } = await import("./prometheusCmd-PL64BLN5.js");
|
|
4729
|
+
const prometheusPort = parseInt(opts.prometheusPort, 10) || 9090;
|
|
4730
|
+
const intervalSec = parseFloat(opts.interval) || 15;
|
|
4731
|
+
await runPrometheusServer({ port: prometheusPort, interval: intervalSec, detailed: opts.detailed });
|
|
4732
|
+
return;
|
|
4733
|
+
}
|
|
4734
|
+
if (cmd === "smart") {
|
|
4735
|
+
const { runSmartCommand } = await import("./smartCmd-ODWKSFNG.js");
|
|
4736
|
+
await runSmartCommand();
|
|
4737
|
+
return;
|
|
4738
|
+
}
|
|
4739
|
+
if (cmd === "replay") {
|
|
4740
|
+
const file = program.args[1];
|
|
4741
|
+
if (!file) {
|
|
4742
|
+
console.error(chalk10.red(" \u2716 Usage: pyre replay <logfile.csv>"));
|
|
4743
|
+
process.exit(1);
|
|
4744
|
+
}
|
|
4745
|
+
const { runReplayCommand } = await import("./replayCmd-Z3PYXY7V.js");
|
|
4746
|
+
await runReplayCommand({
|
|
4747
|
+
file,
|
|
4748
|
+
speed: parseFloat(opts.speed) || 1,
|
|
4749
|
+
plain: opts.plain || opts.a11y
|
|
4750
|
+
});
|
|
4751
|
+
return;
|
|
4752
|
+
}
|
|
4753
|
+
if (cmd === "explain") {
|
|
4754
|
+
const { runExplainCommand } = await import("./explainCmd-MF6PIMLW.js");
|
|
4755
|
+
const backend = opts.llmBackend === "openai" ? "openai" : opts.llmBackend === "ollama" ? "ollama" : "builtin";
|
|
4756
|
+
await runExplainCommand({
|
|
4757
|
+
since: opts.since || "7d",
|
|
4758
|
+
backend,
|
|
4759
|
+
model: opts.llmModel,
|
|
4760
|
+
apiKey: opts.apiKey,
|
|
4761
|
+
dir: opts.exportDir
|
|
4762
|
+
});
|
|
4763
|
+
return;
|
|
4764
|
+
}
|
|
4765
|
+
if (cmd === "alert") {
|
|
4766
|
+
const sub = program.args[1];
|
|
4767
|
+
if (sub === "test") {
|
|
4768
|
+
const { sendTestAlert } = await import("./alertChannels-TPSQIJ7S.js");
|
|
4769
|
+
const { writeConfig: writeConfig2 } = await import("./config-GYGJXU2Q.js");
|
|
4770
|
+
if (opts.alertSlack || opts.alertDiscord || opts.alertPushoverToken || opts.alertPushoverUser || opts.alertNtfy) {
|
|
4771
|
+
writeConfig2({
|
|
4772
|
+
slackAlertUrl: opts.alertSlack || config2.slackAlertUrl,
|
|
4773
|
+
discordAlertUrl: opts.alertDiscord || config2.discordAlertUrl,
|
|
4774
|
+
pushoverToken: opts.alertPushoverToken || config2.pushoverToken,
|
|
4775
|
+
pushoverUser: opts.alertPushoverUser || config2.pushoverUser,
|
|
4776
|
+
ntfyUrl: opts.alertNtfy || config2.ntfyUrl
|
|
4777
|
+
});
|
|
4778
|
+
}
|
|
4779
|
+
const testConfig = {
|
|
4780
|
+
slackUrl: opts.alertSlack || config2.slackAlertUrl || void 0,
|
|
4781
|
+
discordUrl: opts.alertDiscord || config2.discordAlertUrl || void 0,
|
|
4782
|
+
pushoverToken: opts.alertPushoverToken || config2.pushoverToken || void 0,
|
|
4783
|
+
pushoverUser: opts.alertPushoverUser || config2.pushoverUser || void 0,
|
|
4784
|
+
ntfyUrl: opts.alertNtfy || config2.ntfyUrl || void 0
|
|
4785
|
+
};
|
|
4786
|
+
const hasAny = Object.values(testConfig).some((v) => !!v);
|
|
4787
|
+
if (!hasAny) {
|
|
4788
|
+
console.log(chalk10.yellow("\n \u26A0 No alert channels configured."));
|
|
4789
|
+
console.log(chalk10.dim(" Use --alert-slack <url>, --alert-discord <url>, --alert-ntfy <url>, etc.\n"));
|
|
4790
|
+
return;
|
|
4791
|
+
}
|
|
4792
|
+
console.log(chalk10.bold("\n \u{1F525} pyre alert test \u2014 firing test notification...\n"));
|
|
4793
|
+
await sendTestAlert(testConfig);
|
|
4794
|
+
console.log(chalk10.green(" \u2714 Test alert sent to all configured channels.\n"));
|
|
4795
|
+
} else {
|
|
4796
|
+
console.log(chalk10.yellow(" Usage: pyre alert test"));
|
|
4797
|
+
}
|
|
4798
|
+
return;
|
|
4799
|
+
}
|
|
4800
|
+
if (cmd === "netusage") {
|
|
4801
|
+
const { runNetUsageCommand } = await import("./netUsageCmd-WKBTW2YG.js");
|
|
4802
|
+
await runNetUsageCommand({
|
|
4803
|
+
top: opts.top ? parseInt(opts.top, 10) : 15,
|
|
4804
|
+
sort: opts.sort === "rx" || opts.sort === "tx" ? opts.sort : "total",
|
|
4805
|
+
json: !!opts.json
|
|
4806
|
+
});
|
|
4807
|
+
return;
|
|
4808
|
+
}
|
|
4809
|
+
if (cmd === "serve") {
|
|
4810
|
+
const { runServeCommand } = await import("./serveCmd-2H5VLF3P.js");
|
|
4811
|
+
const port = parseInt(opts.port, 10) || 8080;
|
|
4812
|
+
await runServeCommand({
|
|
4813
|
+
port,
|
|
4814
|
+
apiKey: opts.apiKey,
|
|
4815
|
+
detailed: opts.detailed
|
|
4816
|
+
});
|
|
4817
|
+
return;
|
|
4818
|
+
}
|
|
4819
|
+
if (cmd === "profile-proc") {
|
|
4820
|
+
const targetPid = parseInt(program.args[1], 10);
|
|
4821
|
+
if (isNaN(targetPid)) {
|
|
4822
|
+
console.error(chalk10.red(" \u2716 Usage: pyre profile-proc <pid> [--duration <sec>]"));
|
|
4823
|
+
process.exit(1);
|
|
4824
|
+
}
|
|
4825
|
+
const { runProfileProcCommand } = await import("./profileProcCmd-N4H6MCBT.js");
|
|
4826
|
+
const durationSec = parseFloat(opts.duration) || 5;
|
|
4827
|
+
await runProfileProcCommand({
|
|
4828
|
+
pid: targetPid,
|
|
4829
|
+
durationSec,
|
|
4830
|
+
raw: !!opts.raw
|
|
4831
|
+
});
|
|
4832
|
+
return;
|
|
4833
|
+
}
|
|
4834
|
+
if (cmd === "watchdog") {
|
|
4835
|
+
const { runWatchdogCommand } = await import("./watchdogCmd-UF53JPZB.js");
|
|
4836
|
+
await runWatchdogCommand({
|
|
4837
|
+
configFile: opts.watchdogConfig,
|
|
4838
|
+
dryRun: !!opts.dryRun,
|
|
4839
|
+
interval: parseFloat(opts.interval) || 2
|
|
4840
|
+
});
|
|
4841
|
+
return;
|
|
4842
|
+
}
|
|
4843
|
+
if (cmd === "build") {
|
|
4844
|
+
const { runBuildCommand } = await import("./buildCmd-CDPZ2ZFA.js");
|
|
4845
|
+
await runBuildCommand({
|
|
4846
|
+
json: !!opts.json
|
|
4847
|
+
});
|
|
4848
|
+
return;
|
|
4849
|
+
}
|
|
4850
|
+
if (cmd === "topo") {
|
|
4851
|
+
const { runTopoCommand } = await import("./topoCmd-DU2HHZNH.js");
|
|
4852
|
+
await runTopoCommand({
|
|
4853
|
+
scan: !!opts.scan,
|
|
4854
|
+
json: !!opts.json
|
|
4855
|
+
});
|
|
4856
|
+
return;
|
|
4857
|
+
}
|
|
4858
|
+
if (cmd === "sync") {
|
|
4859
|
+
const subcommand = program.args[1] || "status";
|
|
4860
|
+
const { runSyncCommand } = await import("./syncCmd-A2W3QB3S.js");
|
|
4861
|
+
await runSyncCommand(subcommand, {
|
|
4862
|
+
syncDir: opts.syncDir
|
|
4863
|
+
});
|
|
4864
|
+
return;
|
|
4865
|
+
}
|
|
4263
4866
|
if (cmd === "brew") {
|
|
4264
|
-
const { printBrewHealthReport } = await import("./brewHealth-
|
|
4867
|
+
const { printBrewHealthReport } = await import("./brewHealth-P2GW5Y5C.js");
|
|
4265
4868
|
await printBrewHealthReport();
|
|
4266
4869
|
return;
|
|
4267
4870
|
}
|
|
@@ -4273,7 +4876,7 @@ async function main() {
|
|
|
4273
4876
|
if (cmd === "profile") {
|
|
4274
4877
|
const sub = program.args[1];
|
|
4275
4878
|
const name = program.args[2];
|
|
4276
|
-
const { saveProfile, loadProfile, listProfiles } = await import("./config-
|
|
4879
|
+
const { saveProfile, loadProfile, listProfiles } = await import("./config-GYGJXU2Q.js");
|
|
4277
4880
|
if (sub === "save" && name) {
|
|
4278
4881
|
saveProfile(name);
|
|
4279
4882
|
console.log(chalk10.green(` \u2714 Profile '${name}' saved successfully.`));
|
|
@@ -4327,13 +4930,13 @@ async function main() {
|
|
|
4327
4930
|
}
|
|
4328
4931
|
if (cmd === "web") {
|
|
4329
4932
|
if (opts.install) {
|
|
4330
|
-
const { installLaunchdAgent } = await import("./service-
|
|
4933
|
+
const { installLaunchdAgent } = await import("./service-Q74BBILO.js");
|
|
4331
4934
|
const port = parseInt(opts.port || "3000", 10) || 3e3;
|
|
4332
4935
|
installLaunchdAgent(port);
|
|
4333
4936
|
return;
|
|
4334
4937
|
}
|
|
4335
4938
|
if (opts.uninstall) {
|
|
4336
|
-
const { uninstallLaunchdAgent } = await import("./service-
|
|
4939
|
+
const { uninstallLaunchdAgent } = await import("./service-Q74BBILO.js");
|
|
4337
4940
|
uninstallLaunchdAgent();
|
|
4338
4941
|
return;
|
|
4339
4942
|
}
|
|
@@ -4364,7 +4967,7 @@ async function main() {
|
|
|
4364
4967
|
const cfg = readConfig();
|
|
4365
4968
|
console.log(JSON.stringify(cfg, null, 2));
|
|
4366
4969
|
} else if (sub === "reset") {
|
|
4367
|
-
|
|
4970
|
+
fs10.rmSync(CONFIG_FILE, { force: true });
|
|
4368
4971
|
console.log(chalk10.green("Config reset to defaults"));
|
|
4369
4972
|
} else {
|
|
4370
4973
|
console.log("Usage: pyre config <show|reset>");
|
|
@@ -4380,7 +4983,7 @@ async function main() {
|
|
|
4380
4983
|
return;
|
|
4381
4984
|
}
|
|
4382
4985
|
if (cmd === "doctor") {
|
|
4383
|
-
const { runDoctor, printDoctorReport } = await import("./doctor-
|
|
4986
|
+
const { runDoctor, printDoctorReport } = await import("./doctor-GYHFTNZQ.js");
|
|
4384
4987
|
const checks = await runDoctor();
|
|
4385
4988
|
printDoctorReport(checks);
|
|
4386
4989
|
return;
|
|
@@ -4390,7 +4993,7 @@ async function main() {
|
|
|
4390
4993
|
return;
|
|
4391
4994
|
}
|
|
4392
4995
|
if (cmd === "completions") {
|
|
4393
|
-
const { printCompletions } = await import("./completions-
|
|
4996
|
+
const { printCompletions } = await import("./completions-JKYEJNGR.js");
|
|
4394
4997
|
const shell = program.args[1] || "zsh";
|
|
4395
4998
|
printCompletions(shell);
|
|
4396
4999
|
return;
|
|
@@ -4409,6 +5012,11 @@ async function main() {
|
|
|
4409
5012
|
if (opts.webhookUrl) config2.webhookUrl = opts.webhookUrl;
|
|
4410
5013
|
if (opts.alertCmd) config2.alertCmd = opts.alertCmd;
|
|
4411
5014
|
if (opts.exportDir) config2.exportDir = opts.exportDir;
|
|
5015
|
+
if (opts.alertSlack) config2.slackAlertUrl = opts.alertSlack;
|
|
5016
|
+
if (opts.alertDiscord) config2.discordAlertUrl = opts.alertDiscord;
|
|
5017
|
+
if (opts.alertPushoverToken) config2.pushoverToken = opts.alertPushoverToken;
|
|
5018
|
+
if (opts.alertPushoverUser) config2.pushoverUser = opts.alertPushoverUser;
|
|
5019
|
+
if (opts.alertNtfy) config2.ntfyUrl = opts.alertNtfy;
|
|
4412
5020
|
const tempUnit = opts.tempUnit === "f" ? "f" : "c";
|
|
4413
5021
|
if (cmd === "live" || !isExportMode() && !opts.once && cmd !== "p2p") {
|
|
4414
5022
|
if (!process.stdout.isTTY && cmd !== "live") {
|
|
@@ -4461,7 +5069,7 @@ async function main() {
|
|
|
4461
5069
|
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
5070
|
console.log(output);
|
|
4463
5071
|
if (opts.out) {
|
|
4464
|
-
|
|
5072
|
+
fs10.writeFileSync(opts.out, stripAnsi(output));
|
|
4465
5073
|
console.log(chalk10.dim(`
|
|
4466
5074
|
Saved to ${opts.out}`));
|
|
4467
5075
|
}
|
|
@@ -4645,11 +5253,11 @@ async function runP2PConnect() {
|
|
|
4645
5253
|
}
|
|
4646
5254
|
async function runInfoCommand() {
|
|
4647
5255
|
const data = await collectAll({ detailed: true });
|
|
4648
|
-
const { getDisplayInfo, getTimeMachineStatus } = await import("./collectors-
|
|
5256
|
+
const { getDisplayInfo, getTimeMachineStatus } = await import("./collectors-H43PN4PS.js");
|
|
4649
5257
|
const displays = await getDisplayInfo();
|
|
4650
5258
|
const tmStatus = await getTimeMachineStatus();
|
|
4651
5259
|
const lines = [];
|
|
4652
|
-
lines.push(chalk10.bold(
|
|
5260
|
+
lines.push(chalk10.bold(` pyre info \u2014 System Information (${data.header.os})`));
|
|
4653
5261
|
lines.push("");
|
|
4654
5262
|
lines.push(` Hostname: ${data.header.hostname}`);
|
|
4655
5263
|
lines.push(` OS: ${data.header.os}`);
|
|
@@ -4674,13 +5282,15 @@ async function runInfoCommand() {
|
|
|
4674
5282
|
lines.push(` \u2022 ${d.name}: ${d.resolution}${d.isMain ? " (Main)" : ""}${d.connectionType ? ` [${d.connectionType}]` : ""}`);
|
|
4675
5283
|
}
|
|
4676
5284
|
}
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
5285
|
+
if (process.platform === "darwin") {
|
|
5286
|
+
lines.push("");
|
|
5287
|
+
lines.push(chalk10.cyan.bold(" \u23F1 Time Machine Backup:"));
|
|
5288
|
+
if (!tmStatus.configured) {
|
|
5289
|
+
lines.push(" \u2022 Not configured");
|
|
5290
|
+
} else {
|
|
5291
|
+
lines.push(` \u2022 Status: ${tmStatus.running ? chalk10.yellow(`Backup in progress (${tmStatus.percent || 0}%)`) : chalk10.green("Idle")}`);
|
|
5292
|
+
lines.push(` \u2022 Latest Backup: ${tmStatus.latestBackup}`);
|
|
5293
|
+
}
|
|
4684
5294
|
}
|
|
4685
5295
|
console.log(lines.join("\n"));
|
|
4686
5296
|
}
|
|
@@ -4724,7 +5334,7 @@ async function runWebCommand() {
|
|
|
4724
5334
|
return;
|
|
4725
5335
|
}
|
|
4726
5336
|
if (url === "/metrics") {
|
|
4727
|
-
const { formatPrometheusMetrics } = await import("./prometheus-
|
|
5337
|
+
const { formatPrometheusMetrics } = await import("./prometheus-5BL5PVLC.js");
|
|
4728
5338
|
const data = await collectAll({ detailed: true });
|
|
4729
5339
|
res.writeHead(200, { "Content-Type": "text/plain; version=0.0.4; charset=utf-8" });
|
|
4730
5340
|
res.end(formatPrometheusMetrics(data));
|
|
@@ -4758,9 +5368,9 @@ async function runWebCommand() {
|
|
|
4758
5368
|
res.end(getDashboardHtml());
|
|
4759
5369
|
} else if (url === "/setup") {
|
|
4760
5370
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
4761
|
-
const indexPath =
|
|
4762
|
-
if (
|
|
4763
|
-
res.end(
|
|
5371
|
+
const indexPath = path6.join(process.cwd(), "index.html");
|
|
5372
|
+
if (fs10.existsSync(indexPath)) {
|
|
5373
|
+
res.end(fs10.readFileSync(indexPath, "utf-8"));
|
|
4764
5374
|
} else {
|
|
4765
5375
|
res.end(getDashboardHtml());
|
|
4766
5376
|
}
|
|
@@ -4810,10 +5420,26 @@ Error: Port ${port} is already in use.`));
|
|
|
4810
5420
|
});
|
|
4811
5421
|
});
|
|
4812
5422
|
}
|
|
5423
|
+
async function openBrowserUrl(url) {
|
|
5424
|
+
const { spawn } = await import("child_process");
|
|
5425
|
+
const platform = process.platform;
|
|
5426
|
+
if (platform === "win32") {
|
|
5427
|
+
spawn("cmd.exe", ["/c", "start", "", url], { detached: true, stdio: "ignore" });
|
|
5428
|
+
} else if (platform === "darwin") {
|
|
5429
|
+
spawn("open", [url], { detached: true, stdio: "ignore" });
|
|
5430
|
+
} else {
|
|
5431
|
+
spawn("xdg-open", [url], { detached: true, stdio: "ignore" });
|
|
5432
|
+
}
|
|
5433
|
+
}
|
|
4813
5434
|
async function runUiCommand() {
|
|
4814
5435
|
opts.port = "0";
|
|
4815
5436
|
const port = await runWebCommand();
|
|
4816
|
-
const
|
|
5437
|
+
const url = `http://localhost:${port}`;
|
|
5438
|
+
if (process.platform === "darwin") {
|
|
5439
|
+
const { run } = await import("./run-HOGQDDTA.js");
|
|
5440
|
+
const hasSwift = (await run("which swift 2>/dev/null", "")).trim().length > 0;
|
|
5441
|
+
if (hasSwift) {
|
|
5442
|
+
const swiftScript = `
|
|
4817
5443
|
import Cocoa
|
|
4818
5444
|
import WebKit
|
|
4819
5445
|
|
|
@@ -4915,27 +5541,36 @@ let delegate = AppDelegate()
|
|
|
4915
5541
|
app.delegate = delegate
|
|
4916
5542
|
app.run()
|
|
4917
5543
|
`;
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
5544
|
+
const scriptPath = path6.join(os2.tmpdir(), "pyre-ui.swift");
|
|
5545
|
+
fs10.writeFileSync(scriptPath, swiftScript);
|
|
5546
|
+
const { spawn } = await import("child_process");
|
|
5547
|
+
console.log(chalk10.cyan(`
|
|
4922
5548
|
Launching native UI window...`));
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
5549
|
+
const ui = spawn("swift", [scriptPath], { stdio: "inherit" });
|
|
5550
|
+
ui.on("close", () => {
|
|
5551
|
+
console.log(chalk10.dim("UI window closed, shutting down server..."));
|
|
5552
|
+
process.exit(0);
|
|
5553
|
+
});
|
|
5554
|
+
process.once("SIGINT", () => {
|
|
5555
|
+
ui.kill();
|
|
5556
|
+
process.exit(0);
|
|
5557
|
+
});
|
|
5558
|
+
return;
|
|
5559
|
+
}
|
|
5560
|
+
}
|
|
5561
|
+
console.log(chalk10.cyan(`
|
|
5562
|
+
Opening live UI dashboard at ${url}...`));
|
|
5563
|
+
await openBrowserUrl(url);
|
|
5564
|
+
console.log(chalk10.dim(" Press Ctrl+C to stop server.\n"));
|
|
5565
|
+
await new Promise(() => {
|
|
4931
5566
|
});
|
|
4932
5567
|
}
|
|
4933
5568
|
async function runBenchCommand(benchCmd) {
|
|
4934
5569
|
const { spawn } = await import("child_process");
|
|
4935
5570
|
const logDir = opts.exportDir || "./pyre-exports";
|
|
4936
|
-
const file =
|
|
4937
|
-
if (!
|
|
4938
|
-
const stream =
|
|
5571
|
+
const file = path6.join(logDir, `pyre-bench-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.csv`);
|
|
5572
|
+
if (!fs10.existsSync(logDir)) fs10.mkdirSync(logDir, { recursive: true });
|
|
5573
|
+
const stream = fs10.createWriteStream(file, { flags: "a" });
|
|
4939
5574
|
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
5575
|
console.log(chalk10.bold(`
|
|
4941
5576
|
pyre bench`));
|
|
@@ -4945,7 +5580,7 @@ async function runBenchCommand(benchCmd) {
|
|
|
4945
5580
|
const startTime = Date.now();
|
|
4946
5581
|
let accumulatedWattSeconds = 0;
|
|
4947
5582
|
let powerSamplesCount = 0;
|
|
4948
|
-
const child = spawn("sh", ["-c", benchCmd]);
|
|
5583
|
+
const child = process.platform === "win32" ? spawn("cmd.exe", ["/c", benchCmd], { stdio: "inherit" }) : spawn("sh", ["-c", benchCmd]);
|
|
4949
5584
|
child.stdout.on("data", (d) => process.stdout.write(d));
|
|
4950
5585
|
child.stderr.on("data", (d) => process.stdout.write(d));
|
|
4951
5586
|
const interval = parseFloat(opts.interval) || 2;
|