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.
Files changed (94) hide show
  1. package/README.md +84 -14
  2. package/dist/alertChannels-TPSQIJ7S.js +8 -0
  3. package/dist/blender-FGH3ASEQ.js +7 -0
  4. package/dist/brewHealth-P2GW5Y5C.js +176 -0
  5. package/dist/buildCmd-CDPZ2ZFA.js +38 -0
  6. package/dist/{checkCmd-EWFNLBWE.js → checkCmd-RKW6QGDR.js} +10 -11
  7. package/dist/{chunk-RVGKEJXT.js → chunk-53O2RGSL.js} +47 -17
  8. package/dist/{chunk-MOAEGMFP.js → chunk-6UHZP4QP.js} +204 -46
  9. package/dist/chunk-GHRDW646.js +2506 -0
  10. package/dist/{prometheus-LFYHF6VO.js → chunk-HT4VQ4OH.js} +1 -0
  11. package/dist/chunk-M73HCQD2.js +95 -0
  12. package/dist/chunk-MZO5QKUR.js +32 -0
  13. package/dist/chunk-VIACN2RU.js +133 -0
  14. package/dist/chunk-Z2YV5HKV.js +87 -0
  15. package/dist/chunk-ZLVIVFSO.js +110 -0
  16. package/dist/{collectors-WD6VKH6M.js → collectors-H43PN4PS.js} +8 -2
  17. package/dist/{completions-VQMHQCXE.js → completions-JKYEJNGR.js} +34 -1
  18. package/dist/{config-4ZLF4HTS.js → config-GYGJXU2Q.js} +9 -1
  19. package/dist/doctor-GYHFTNZQ.js +387 -0
  20. package/dist/explainCmd-MF6PIMLW.js +222 -0
  21. package/dist/extensions-XJGHG7X3.js +134 -0
  22. package/dist/index.js +841 -206
  23. package/dist/ioreport.py +72 -0
  24. package/dist/netUsageCmd-WKBTW2YG.js +90 -0
  25. package/dist/{pipeCmd-7THXEGDH.js → pipeCmd-7REDBLBU.js} +5 -3
  26. package/dist/profileProcCmd-N4H6MCBT.js +107 -0
  27. package/dist/prometheus-5BL5PVLC.js +6 -0
  28. package/dist/prometheusCmd-PL64BLN5.js +93 -0
  29. package/dist/replayCmd-Z3PYXY7V.js +164 -0
  30. package/dist/run-HOGQDDTA.js +8 -0
  31. package/dist/serveCmd-2H5VLF3P.js +153 -0
  32. package/dist/service-Q74BBILO.js +181 -0
  33. package/dist/smartCmd-ODWKSFNG.js +166 -0
  34. package/dist/syncCmd-A2W3QB3S.js +126 -0
  35. package/dist/topoCmd-DU2HHZNH.js +148 -0
  36. package/dist/watchdogCmd-UF53JPZB.js +161 -0
  37. package/dist/{wizard-GVIAHIVV.js → wizard-TD7BGX6O.js} +1 -1
  38. package/install.ps1 +29 -0
  39. package/install.sh +10 -9
  40. package/package.json +7 -3
  41. package/src/ai/models.ts +138 -0
  42. package/src/alertChannels.ts +119 -0
  43. package/src/anomalies.ts +114 -60
  44. package/src/brewHealth.ts +157 -40
  45. package/src/buildCmd.ts +48 -0
  46. package/src/checkCmd.ts +5 -5
  47. package/src/completions.ts +35 -0
  48. package/src/doctor.ts +235 -7
  49. package/src/easy_features.test.ts +40 -0
  50. package/src/explainCmd.ts +261 -0
  51. package/src/extensions.ts +71 -7
  52. package/src/formatters/index.ts +1 -1
  53. package/src/formatters/output.ts +100 -13
  54. package/src/formatters/render.ts +274 -38
  55. package/src/formatters/themes.ts +8 -0
  56. package/src/formatters/types.ts +6 -1
  57. package/src/index.ts +298 -56
  58. package/src/live/input.ts +122 -4
  59. package/src/live/render.ts +98 -0
  60. package/src/live/state.ts +70 -52
  61. package/src/live/types.ts +2 -2
  62. package/src/monitors/batteryPredictor.ts +143 -0
  63. package/src/monitors/blender.ts +144 -0
  64. package/src/monitors/buildTracker.ts +121 -0
  65. package/src/monitors/collectors.ts +558 -147
  66. package/src/monitors/dns_cache.ts +48 -0
  67. package/src/monitors/index.ts +3 -2
  68. package/src/monitors/ioreport.py +72 -0
  69. package/src/monitors/platform/linux.ts +756 -0
  70. package/src/monitors/platform/windows.ts +519 -0
  71. package/src/monitors/run.ts +27 -3
  72. package/src/monitors/smc.ts +21 -1
  73. package/src/monitors/types.ts +112 -14
  74. package/src/netUsageCmd.ts +121 -0
  75. package/src/platform.test.ts +99 -0
  76. package/src/plugins/index.ts +143 -0
  77. package/src/profileProcCmd.ts +133 -0
  78. package/src/prometheusCmd.ts +107 -0
  79. package/src/replayCmd.ts +186 -0
  80. package/src/serveCmd.ts +180 -0
  81. package/src/service.ts +125 -7
  82. package/src/smartCmd.ts +205 -0
  83. package/src/state/config.ts +64 -17
  84. package/src/syncCmd.ts +166 -0
  85. package/src/topoCmd.ts +176 -0
  86. package/src/watchdogCmd.ts +198 -0
  87. package/src/xbar.ts +22 -11
  88. package/dist/brewHealth-F2BXF5M6.js +0 -74
  89. package/dist/chunk-FH5WDJBF.js +0 -15
  90. package/dist/chunk-HGRBBPYX.js +0 -937
  91. package/dist/doctor-L6FZYA7F.js +0 -184
  92. package/dist/extensions-DVA5YLGH.js +0 -74
  93. package/dist/service-WTS3NBQU.js +0 -73
  94. /package/dist/{chunk-SG26Y3WF.js → chunk-IM3IQMMT.js} +0 -0
@@ -0,0 +1,72 @@
1
+ import ctypes, ctypes.util, json, time, sys
2
+
3
+ try:
4
+ libIOReport = ctypes.cdll.LoadLibrary('/usr/lib/libIOReport.dylib')
5
+ CF = ctypes.cdll.LoadLibrary(ctypes.util.find_library('CoreFoundation'))
6
+ CFStringRef = ctypes.c_void_p
7
+ CFDictionaryRef = ctypes.c_void_p
8
+ CFArrayRef = ctypes.c_void_p
9
+ CFIndex = ctypes.c_long
10
+
11
+ CF.CFStringCreateWithCString.restype = CFStringRef
12
+ CF.CFStringCreateWithCString.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_uint32]
13
+ CF.CFStringGetCString.restype = ctypes.c_bool
14
+ CF.CFStringGetCString.argtypes = [CFStringRef, ctypes.c_char_p, CFIndex, ctypes.c_uint32]
15
+ CF.CFDictionaryGetValue.restype = ctypes.c_void_p
16
+ CF.CFDictionaryGetValue.argtypes = [CFDictionaryRef, ctypes.c_void_p]
17
+ CF.CFArrayGetCount.restype = CFIndex
18
+ CF.CFArrayGetCount.argtypes = [CFArrayRef]
19
+ CF.CFArrayGetValueAtIndex.restype = ctypes.c_void_p
20
+ CF.CFArrayGetValueAtIndex.argtypes = [CFArrayRef, CFIndex]
21
+
22
+ def cf_str(t):
23
+ return CF.CFStringCreateWithCString(None, t.encode('utf-8'), 0x08000100)
24
+
25
+ def cf_to_str(c):
26
+ if not c: return ''
27
+ b = ctypes.create_string_buffer(128)
28
+ return b.value.decode('utf-8') if CF.CFStringGetCString(c, b, 128, 0x08000100) else ''
29
+
30
+ libIOReport.IOReportCopyChannelsInGroup.restype = CFDictionaryRef
31
+ libIOReport.IOReportCopyChannelsInGroup.argtypes = [CFStringRef, CFStringRef, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint64]
32
+ libIOReport.IOReportCreateSubscription.restype = ctypes.c_void_p
33
+ libIOReport.IOReportCreateSubscription.argtypes = [ctypes.c_void_p, CFDictionaryRef, ctypes.POINTER(CFDictionaryRef), ctypes.c_uint64, ctypes.c_void_p]
34
+ libIOReport.IOReportCreateSamples.restype = CFDictionaryRef
35
+ libIOReport.IOReportCreateSamples.argtypes = [ctypes.c_void_p, CFDictionaryRef, ctypes.c_void_p]
36
+ libIOReport.IOReportCreateSamplesDelta.restype = CFDictionaryRef
37
+ libIOReport.IOReportCreateSamplesDelta.argtypes = [CFDictionaryRef, CFDictionaryRef, ctypes.c_void_p]
38
+ libIOReport.IOReportChannelGetChannelName.restype = CFStringRef
39
+ libIOReport.IOReportChannelGetChannelName.argtypes = [CFDictionaryRef]
40
+ libIOReport.IOReportChannelGetUnitLabel.restype = CFStringRef
41
+ libIOReport.IOReportChannelGetUnitLabel.argtypes = [CFDictionaryRef]
42
+ libIOReport.IOReportSimpleGetIntegerValue.restype = ctypes.c_int64
43
+ libIOReport.IOReportSimpleGetIntegerValue.argtypes = [CFDictionaryRef, ctypes.c_int32]
44
+
45
+ ch = libIOReport.IOReportCopyChannelsInGroup(cf_str('Energy Model'), None, 0, 0, 0)
46
+ sub_ch = CFDictionaryRef()
47
+ sub = libIOReport.IOReportCreateSubscription(None, ch, ctypes.byref(sub_ch), 0, None)
48
+ s1 = libIOReport.IOReportCreateSamples(sub, sub_ch, None)
49
+ dt = 0.08
50
+ time.sleep(dt)
51
+ s2 = libIOReport.IOReportCreateSamples(sub, sub_ch, None)
52
+ delta = libIOReport.IOReportCreateSamplesDelta(s1, s2, None)
53
+ arr = CF.CFDictionaryGetValue(delta, cf_str('IOReportChannels'))
54
+ cnt = CF.CFArrayGetCount(arr)
55
+ cpu_w = 0.0
56
+ gpu_w = 0.0
57
+ ane_w = 0.0
58
+ for i in range(cnt):
59
+ item = CF.CFArrayGetValueAtIndex(arr, i)
60
+ n = cf_to_str(libIOReport.IOReportChannelGetChannelName(item))
61
+ if n in ('GPU Energy', 'CPU Energy', 'ANE'):
62
+ u = cf_to_str(libIOReport.IOReportChannelGetUnitLabel(item))
63
+ v = libIOReport.IOReportSimpleGetIntegerValue(item, 0)
64
+ sc = 1e9 if u == 'nJ' else (1e6 if u == 'uJ' else 1e3)
65
+ w = round((v / sc) / dt, 2)
66
+ if n == 'CPU Energy': cpu_w = w
67
+ elif n == 'GPU Energy': gpu_w = w
68
+ elif n == 'ANE': ane_w = w
69
+ combined_w = round(cpu_w + gpu_w + ane_w, 2)
70
+ print(json.dumps({'cpuWatts': cpu_w, 'gpuWatts': gpu_w, 'aneWatts': ane_w, 'combinedWatts': combined_w}))
71
+ except Exception:
72
+ print('{}')
@@ -0,0 +1,90 @@
1
+ import {
2
+ formatBytes
3
+ } from "./chunk-6UHZP4QP.js";
4
+ import "./chunk-ZLVIVFSO.js";
5
+ import {
6
+ collectPackets
7
+ } from "./chunk-GHRDW646.js";
8
+ import "./chunk-M73HCQD2.js";
9
+ import "./chunk-VIACN2RU.js";
10
+ import "./chunk-MZO5QKUR.js";
11
+
12
+ // src/netUsageCmd.ts
13
+ import chalk from "chalk";
14
+ async function runNetUsageCommand(opts = {}) {
15
+ const topCount = opts.top || 15;
16
+ const sortKey = opts.sort || "total";
17
+ const packetData = await collectPackets();
18
+ if (!packetData || !packetData.allProcesses || packetData.allProcesses.length === 0) {
19
+ if (opts.json) {
20
+ console.log(JSON.stringify({ processes: [], totalRx: 0, totalTx: 0 }));
21
+ return;
22
+ }
23
+ console.log(chalk.yellow("\n \u26A0 No active per-application network sockets detected."));
24
+ console.log(chalk.dim(" Ensure network activity is present or run with elevated permissions if required.\n"));
25
+ return;
26
+ }
27
+ const appMap = /* @__PURE__ */ new Map();
28
+ for (const proc of packetData.allProcesses) {
29
+ const key = proc.command || `pid-${proc.pid}`;
30
+ const rx = proc.rxBytes || 0;
31
+ const tx = proc.txBytes || 0;
32
+ const conns = proc.connections ? proc.connections.length : 0;
33
+ const existing = appMap.get(key);
34
+ if (existing) {
35
+ existing.rxBytes += rx;
36
+ existing.txBytes += tx;
37
+ existing.totalBytes += rx + tx;
38
+ existing.connections += conns;
39
+ } else {
40
+ appMap.set(key, {
41
+ pid: proc.pid,
42
+ command: key,
43
+ rxBytes: rx,
44
+ txBytes: tx,
45
+ totalBytes: rx + tx,
46
+ connections: conns
47
+ });
48
+ }
49
+ }
50
+ const sorted = Array.from(appMap.values()).sort((a, b) => {
51
+ if (sortKey === "rx") return b.rxBytes - a.rxBytes;
52
+ if (sortKey === "tx") return b.txBytes - a.txBytes;
53
+ return b.totalBytes - a.totalBytes;
54
+ });
55
+ const displayList = sorted.slice(0, topCount);
56
+ if (opts.json) {
57
+ console.log(JSON.stringify({
58
+ totalRx: packetData.rxPackets,
59
+ totalTx: packetData.txPackets,
60
+ activeConnections: packetData.connections,
61
+ processes: displayList
62
+ }, null, 2));
63
+ return;
64
+ }
65
+ console.log(chalk.bold("\n \u{1F525} pyre netusage \u2014 Per-Application Network Traffic\n"));
66
+ const pidW = 8;
67
+ const appW = 28;
68
+ const rxW = 14;
69
+ const txW = 14;
70
+ const totalW = 14;
71
+ const connsW = 8;
72
+ const header = chalk.bold.hex("#f8f8f2").bgHex("#44475a")(" PID ") + chalk.bold.hex("#f8f8f2").bgHex("#44475a")(" APPLICATION ") + chalk.bold.hex("#f8f8f2").bgHex("#44475a")(" DOWN (RX) ") + chalk.bold.hex("#f8f8f2").bgHex("#44475a")(" UP (TX) ") + chalk.bold.hex("#f8f8f2").bgHex("#44475a")(" TOTAL ") + chalk.bold.hex("#f8f8f2").bgHex("#44475a")(" CONNS ");
73
+ console.log(" " + header);
74
+ console.log(" " + chalk.dim("\u2500".repeat(pidW + appW + rxW + txW + totalW + connsW)));
75
+ for (const p of displayList) {
76
+ const pidStr = String(p.pid).padEnd(pidW);
77
+ const appStr = (p.command.length > appW - 2 ? p.command.slice(0, appW - 3) + "\u2026" : p.command).padEnd(appW);
78
+ const rxStr = chalk.green(formatBytes(p.rxBytes).padStart(rxW - 2)) + " ";
79
+ const txStr = chalk.cyan(formatBytes(p.txBytes).padStart(txW - 2)) + " ";
80
+ const totStr = chalk.bold(formatBytes(p.totalBytes).padStart(totalW - 2)) + " ";
81
+ const connsStr = String(p.connections).padStart(connsW - 2);
82
+ console.log(` ${pidStr}${appStr}${rxStr}${txStr}${totStr}${connsStr}`);
83
+ }
84
+ console.log();
85
+ console.log(chalk.dim(` Total active socket connections: ${chalk.bold(String(packetData.connections))} | Showing top ${displayList.length} apps`));
86
+ console.log();
87
+ }
88
+ export {
89
+ runNetUsageCommand
90
+ };
@@ -1,8 +1,10 @@
1
- import "./chunk-SG26Y3WF.js";
1
+ import "./chunk-IM3IQMMT.js";
2
2
  import {
3
3
  collectAll
4
- } from "./chunk-HGRBBPYX.js";
5
- import "./chunk-FH5WDJBF.js";
4
+ } from "./chunk-GHRDW646.js";
5
+ import "./chunk-M73HCQD2.js";
6
+ import "./chunk-VIACN2RU.js";
7
+ import "./chunk-MZO5QKUR.js";
6
8
 
7
9
  // src/pipeCmd.ts
8
10
  async function runPipeCommand(opts = {}) {
@@ -0,0 +1,107 @@
1
+ // src/profileProcCmd.ts
2
+ import { execFile } from "child_process";
3
+ import { promisify } from "util";
4
+ import chalk from "chalk";
5
+ var execFileAsync = promisify(execFile);
6
+ async function runProfileProcCommand(opts) {
7
+ const { pid, durationSec = 5, raw } = opts;
8
+ if (isNaN(pid) || pid <= 0) {
9
+ console.error(chalk.red("\n \u2716 Error: Valid PID is required. Usage: pyre profile-proc <pid> [--duration <sec>]\n"));
10
+ process.exit(1);
11
+ }
12
+ try {
13
+ process.kill(pid, 0);
14
+ } catch {
15
+ console.error(chalk.red(`
16
+ \u2716 Error: Process with PID ${pid} is not running or accessible.
17
+ `));
18
+ process.exit(1);
19
+ }
20
+ console.log(chalk.bold(`
21
+ \u{1F525} pyre profile-proc \u2014 Profiling PID ${pid} for ${durationSec}s...
22
+ `));
23
+ let sampleOutput = "";
24
+ let usedFallback = false;
25
+ if (process.platform === "darwin") {
26
+ try {
27
+ const { stdout } = await execFileAsync("sample", [String(pid), String(durationSec), "-file", "/dev/stdout"], {
28
+ timeout: (durationSec + 5) * 1e3,
29
+ maxBuffer: 10 * 1024 * 1024
30
+ });
31
+ sampleOutput = stdout;
32
+ } catch (err) {
33
+ sampleOutput = err.stdout || "";
34
+ }
35
+ }
36
+ if (!sampleOutput || sampleOutput.length < 100) {
37
+ usedFallback = true;
38
+ try {
39
+ const { stdout } = await execFileAsync("ps", ["-M", "-p", String(pid), "-o", "pid,thread,pcpu,pmem,state,time,command"]);
40
+ sampleOutput = stdout;
41
+ } catch {
42
+ sampleOutput = `Process PID ${pid} active. Deep stack sampling requires elevated permissions or user-owned process.`;
43
+ }
44
+ }
45
+ if (raw) {
46
+ console.log(sampleOutput);
47
+ return;
48
+ }
49
+ if (!usedFallback) {
50
+ const lines = sampleOutput.split("\n");
51
+ const headerLines = [];
52
+ const stackLines = [];
53
+ let inCallGraph = false;
54
+ for (const line of lines) {
55
+ if (line.includes("Analysis of sampling") || line.includes("Process:") || line.includes("Path:") || line.includes("Identifier:")) {
56
+ headerLines.push(line.trim());
57
+ }
58
+ if (line.includes("Call graph:") || line.includes("+ Line Coverage")) {
59
+ inCallGraph = true;
60
+ continue;
61
+ }
62
+ if (inCallGraph) {
63
+ if (line.includes("Total number in stack") || line.includes("Binary Images:")) {
64
+ inCallGraph = false;
65
+ } else if (line.trim().length > 0 && stackLines.length < 35) {
66
+ stackLines.push(line);
67
+ }
68
+ }
69
+ }
70
+ if (headerLines.length > 0) {
71
+ console.log(chalk.dim(" Process Info:"));
72
+ for (const h of headerLines.slice(0, 4)) {
73
+ console.log(` ${chalk.cyan(h)}`);
74
+ }
75
+ console.log();
76
+ }
77
+ console.log(chalk.bold(" Top Call Stacks & Hot Functions:"));
78
+ console.log(chalk.dim(" " + "\u2500".repeat(70)));
79
+ if (stackLines.length === 0) {
80
+ for (const l of lines.slice(0, 20)) {
81
+ if (l.trim()) console.log(` ${l}`);
82
+ }
83
+ } else {
84
+ for (const l of stackLines) {
85
+ if (l.includes("???")) {
86
+ console.log(chalk.dim(` ${l}`));
87
+ } else if (l.includes("+")) {
88
+ console.log(chalk.yellow(` ${l}`));
89
+ } else {
90
+ console.log(` ${l}`);
91
+ }
92
+ }
93
+ }
94
+ } else {
95
+ console.log(chalk.yellow(" Note: Kernel/SIP restriction prevented deep call-graph unwind."));
96
+ console.log(chalk.dim(" Showing process thread snapshot:\n"));
97
+ for (const l of sampleOutput.split("\n")) {
98
+ if (l.trim()) console.log(` ${l}`);
99
+ }
100
+ }
101
+ console.log();
102
+ console.log(chalk.green(` \u2714 Sampling complete for PID ${pid} (${durationSec}s profile duration).`));
103
+ console.log();
104
+ }
105
+ export {
106
+ runProfileProcCommand
107
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ formatPrometheusMetrics
3
+ } from "./chunk-HT4VQ4OH.js";
4
+ export {
5
+ formatPrometheusMetrics
6
+ };
@@ -0,0 +1,93 @@
1
+ import {
2
+ formatPrometheusMetrics
3
+ } from "./chunk-HT4VQ4OH.js";
4
+ import "./chunk-IM3IQMMT.js";
5
+ import {
6
+ collectAll
7
+ } from "./chunk-GHRDW646.js";
8
+ import "./chunk-M73HCQD2.js";
9
+ import "./chunk-VIACN2RU.js";
10
+ import "./chunk-MZO5QKUR.js";
11
+
12
+ // src/prometheusCmd.ts
13
+ import http from "http";
14
+ import os from "os";
15
+ import chalk from "chalk";
16
+ async function runPrometheusServer(opts) {
17
+ const { port, interval, detailed } = opts;
18
+ let latestData = await collectAll({ detailed });
19
+ let lastCollect = Date.now();
20
+ const refreshTimer = setInterval(async () => {
21
+ try {
22
+ latestData = await collectAll({ detailed });
23
+ lastCollect = Date.now();
24
+ } catch {
25
+ }
26
+ }, interval * 1e3);
27
+ const server = http.createServer(async (req, res) => {
28
+ const url = req.url?.split("?")[0] ?? "/";
29
+ if (url === "/metrics") {
30
+ try {
31
+ if (Date.now() - lastCollect > interval * 2e3) {
32
+ latestData = await collectAll({ detailed });
33
+ lastCollect = Date.now();
34
+ }
35
+ const body = formatPrometheusMetrics(latestData);
36
+ res.writeHead(200, {
37
+ "Content-Type": "text/plain; version=0.0.4; charset=utf-8",
38
+ "Content-Length": Buffer.byteLength(body)
39
+ });
40
+ res.end(body);
41
+ } catch (err) {
42
+ res.writeHead(500, { "Content-Type": "text/plain" });
43
+ res.end(`# collection error: ${err instanceof Error ? err.message : String(err)}
44
+ `);
45
+ }
46
+ return;
47
+ }
48
+ if (url === "/health" || url === "/api/health") {
49
+ const body = JSON.stringify({ status: "ok", uptime: os.uptime(), timestamp: (/* @__PURE__ */ new Date()).toISOString() });
50
+ res.writeHead(200, { "Content-Type": "application/json" });
51
+ res.end(body);
52
+ return;
53
+ }
54
+ res.writeHead(404, { "Content-Type": "text/plain" });
55
+ res.end("Not found. Available endpoints: /metrics /health\n");
56
+ });
57
+ server.on("error", (err) => {
58
+ if (err.code === "EADDRINUSE") {
59
+ console.error(chalk.red(` \u2716 Port ${port} is already in use. Try --prometheus-port <other-port>`));
60
+ } else {
61
+ console.error(chalk.red(` \u2716 Server error: ${err.message}`));
62
+ }
63
+ clearInterval(refreshTimer);
64
+ process.exit(1);
65
+ });
66
+ await new Promise((resolve) => server.listen(port, "0.0.0.0", resolve));
67
+ console.log(chalk.bold("\n \u{1F525} pyre \u2014 Prometheus Exporter\n"));
68
+ console.log(` ${chalk.green("\u2714")} Metrics endpoint: ${chalk.cyan(`http://0.0.0.0:${port}/metrics`)}`);
69
+ console.log(` ${chalk.green("\u2714")} Health endpoint: ${chalk.cyan(`http://0.0.0.0:${port}/health`)}`);
70
+ console.log(` ${chalk.dim(`Refresh interval: ${interval}s | Detailed mode: ${detailed ? "on" : "off"}`)}`);
71
+ console.log(`
72
+ ${chalk.dim("Add to prometheus.yml:")}`);
73
+ console.log(chalk.dim(` - job_name: 'pyre'`));
74
+ console.log(chalk.dim(` static_configs:`));
75
+ console.log(chalk.dim(` - targets: ['localhost:${port}']`));
76
+ console.log(`
77
+ ${chalk.dim("Press Ctrl+C to stop.")}
78
+ `);
79
+ const shutdown = () => {
80
+ clearInterval(refreshTimer);
81
+ server.close(() => {
82
+ console.log(chalk.dim("\n Prometheus server stopped."));
83
+ process.exit(0);
84
+ });
85
+ };
86
+ process.on("SIGINT", shutdown);
87
+ process.on("SIGTERM", shutdown);
88
+ await new Promise(() => {
89
+ });
90
+ }
91
+ export {
92
+ runPrometheusServer
93
+ };
@@ -0,0 +1,164 @@
1
+ // src/replayCmd.ts
2
+ import fs from "fs";
3
+ import readline from "readline";
4
+ import chalk from "chalk";
5
+ function parseLogFile(filePath) {
6
+ const content = fs.readFileSync(filePath, "utf-8");
7
+ const rows = [];
8
+ for (const line of content.split("\n").slice(1)) {
9
+ if (!line.trim()) continue;
10
+ const parts = line.split(",");
11
+ if (parts.length < 3) continue;
12
+ const cpuUsage = parseFloat(parts[1]);
13
+ const memUsage = parseFloat(parts[2]);
14
+ if (isNaN(cpuUsage) || isNaN(memUsage)) continue;
15
+ rows.push({
16
+ timestamp: parts[0]?.trim() ?? "",
17
+ cpuUsage,
18
+ memUsage,
19
+ tempC: parts[3] ? parseFloat(parts[3]) || void 0 : void 0,
20
+ rxBytes: parts[4] ? parseFloat(parts[4]) || void 0 : void 0,
21
+ txBytes: parts[5] ? parseFloat(parts[5]) || void 0 : void 0,
22
+ rxPackets: parts[6] ? parseFloat(parts[6]) || void 0 : void 0,
23
+ txPackets: parts[7] ? parseFloat(parts[7]) || void 0 : void 0,
24
+ connections: parts[8] ? parseInt(parts[8], 10) || void 0 : void 0,
25
+ thermalState: parts[9]?.trim() || void 0
26
+ });
27
+ }
28
+ return rows;
29
+ }
30
+ function fmtBytes(bytes) {
31
+ if (bytes >= 1e9) return `${(bytes / 1e9).toFixed(1)} GB`;
32
+ if (bytes >= 1e6) return `${(bytes / 1e6).toFixed(1)} MB`;
33
+ if (bytes >= 1e3) return `${(bytes / 1e3).toFixed(0)} KB`;
34
+ return `${bytes} B`;
35
+ }
36
+ function bar(pct, width = 20) {
37
+ const filled = Math.max(0, Math.min(Math.round(pct / 100 * width), width));
38
+ const color = pct >= 90 ? chalk.red : pct >= 70 ? chalk.yellow : chalk.green;
39
+ return color("\u2588".repeat(filled)) + chalk.dim("\u2591".repeat(width - filled));
40
+ }
41
+ function renderFrame(row, idx, total, speed, paused, cols) {
42
+ const lines = [];
43
+ const divider = chalk.dim("\u2500".repeat(Math.min(cols - 2, 78)));
44
+ lines.push("");
45
+ lines.push(chalk.bold.hex("#ff5500")(" \u{1F525} pyre replay") + " " + chalk.dim(row.timestamp || ""));
46
+ lines.push(" " + divider);
47
+ lines.push("");
48
+ lines.push(` CPU ${bar(row.cpuUsage)} ${String(row.cpuUsage.toFixed(1)).padStart(5)}%`);
49
+ lines.push(` Memory ${bar(row.memUsage)} ${String(row.memUsage.toFixed(1)).padStart(5)}%`);
50
+ if (row.tempC !== void 0 && !isNaN(row.tempC)) {
51
+ const tc = row.tempC >= 90 ? chalk.red : row.tempC >= 70 ? chalk.yellow : chalk.cyan;
52
+ lines.push(` Temp ${tc(`${row.tempC.toFixed(1)}\xB0C`)}`);
53
+ }
54
+ if (row.rxBytes !== void 0 || row.txBytes !== void 0) {
55
+ const rx = row.rxBytes !== void 0 ? fmtBytes(row.rxBytes) : "N/A";
56
+ const tx = row.txBytes !== void 0 ? fmtBytes(row.txBytes) : "N/A";
57
+ lines.push(` Network \u2193 ${chalk.green(rx)} \u2191 ${chalk.blue(tx)}`);
58
+ }
59
+ if (row.connections !== void 0) {
60
+ lines.push(` Conns ${chalk.cyan(String(row.connections))}`);
61
+ }
62
+ if (row.thermalState && row.thermalState !== "Nominal") {
63
+ const sc = row.thermalState === "Critical" ? chalk.red : chalk.yellow;
64
+ lines.push(` Thermal ${sc(row.thermalState)}`);
65
+ }
66
+ lines.push("");
67
+ lines.push(" " + divider);
68
+ const progWidth = 30;
69
+ const progress = total > 1 ? Math.round(idx / (total - 1) * progWidth) : 0;
70
+ const progressBar = chalk.cyan("\u2588".repeat(progress)) + chalk.dim("\u2591".repeat(progWidth - progress));
71
+ const icon = paused ? chalk.yellow("\u23F8") : chalk.green("\u25B6");
72
+ const spStr = speed === 1 ? "1\xD7" : `${speed}\xD7`;
73
+ lines.push(` ${icon} ${progressBar} ${chalk.bold(`${idx + 1}/${total}`)} ${chalk.dim(spStr)}`);
74
+ lines.push("");
75
+ lines.push(chalk.dim(" Space/p=pause ]/[=speed \u2190/\u2192=step q=quit"));
76
+ return lines;
77
+ }
78
+ async function runReplayCommand(opts) {
79
+ const { file, speed: initialSpeed = 1, plain } = opts;
80
+ if (!fs.existsSync(file)) {
81
+ console.error(chalk.red(` \u2716 File not found: ${file}`));
82
+ process.exit(1);
83
+ }
84
+ let rows;
85
+ try {
86
+ rows = parseLogFile(file);
87
+ } catch (err) {
88
+ console.error(chalk.red(` \u2716 Could not parse log file: ${err instanceof Error ? err.message : String(err)}`));
89
+ process.exit(1);
90
+ }
91
+ if (rows.length === 0) {
92
+ console.log(chalk.yellow(" No data rows found in the log file."));
93
+ return;
94
+ }
95
+ if (plain) chalk.level = 0;
96
+ let frameIdx = 0;
97
+ let speed = initialSpeed;
98
+ let paused = false;
99
+ let running = true;
100
+ process.stdin.setRawMode(true);
101
+ process.stdin.resume();
102
+ readline.emitKeypressEvents(process.stdin);
103
+ const cols = process.stdout.columns || 80;
104
+ process.stdout.write("\x1B[?25l\x1B[2J\x1B[H");
105
+ function renderCurrent() {
106
+ const lines = renderFrame(rows[frameIdx], frameIdx, rows.length, speed, paused, cols);
107
+ process.stdout.write("\x1B[H" + lines.map((l) => `\x1B[2K${l}`).join("\r\n") + "\x1B[0J");
108
+ }
109
+ renderCurrent();
110
+ process.stdin.on("keypress", (str, key) => {
111
+ if (!key) return;
112
+ if (key.ctrl && key.name === "c" || key.name === "q") {
113
+ running = false;
114
+ process.stdout.write("\x1B[?25h\x1B[2J\x1B[H");
115
+ process.stdin.setRawMode(false);
116
+ process.stdin.pause();
117
+ process.exit(0);
118
+ }
119
+ if (key.name === "space" || key.name === "p") {
120
+ paused = !paused;
121
+ renderCurrent();
122
+ return;
123
+ }
124
+ if (str === "]") {
125
+ speed = Math.min(speed * 2, 32);
126
+ renderCurrent();
127
+ return;
128
+ }
129
+ if (str === "[") {
130
+ speed = Math.max(speed / 2, 0.125);
131
+ renderCurrent();
132
+ return;
133
+ }
134
+ if (key.name === "right") {
135
+ frameIdx = Math.min(frameIdx + 1, rows.length - 1);
136
+ renderCurrent();
137
+ return;
138
+ }
139
+ if (key.name === "left") {
140
+ frameIdx = Math.max(frameIdx - 1, 0);
141
+ renderCurrent();
142
+ return;
143
+ }
144
+ });
145
+ const BASE_MS = 2e3;
146
+ const tick = () => {
147
+ if (!running) return;
148
+ if (!paused) {
149
+ frameIdx++;
150
+ if (frameIdx >= rows.length) {
151
+ paused = true;
152
+ frameIdx = rows.length - 1;
153
+ }
154
+ renderCurrent();
155
+ }
156
+ setTimeout(tick, BASE_MS / speed);
157
+ };
158
+ setTimeout(tick, BASE_MS / speed);
159
+ await new Promise(() => {
160
+ });
161
+ }
162
+ export {
163
+ runReplayCommand
164
+ };
@@ -0,0 +1,8 @@
1
+ import {
2
+ run,
3
+ runPowerShell
4
+ } from "./chunk-MZO5QKUR.js";
5
+ export {
6
+ run,
7
+ runPowerShell
8
+ };