pyre-cli 9.5.2 → 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 +73 -16
- package/dist/alertChannels-TPSQIJ7S.js +8 -0
- package/dist/{blender-7UT5J7IP.js → blender-FGH3ASEQ.js} +2 -2
- package/dist/brewHealth-P2GW5Y5C.js +176 -0
- package/dist/buildCmd-CDPZ2ZFA.js +38 -0
- package/dist/{checkCmd-T7O4KXXA.js → checkCmd-RKW6QGDR.js} +9 -11
- package/dist/{chunk-CXL4GYGQ.js → chunk-53O2RGSL.js} +44 -17
- package/dist/{chunk-3TE7MJLS.js → chunk-6UHZP4QP.js} +36 -10
- 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-25I533OF.js → chunk-VIACN2RU.js} +1 -1
- package/dist/chunk-Z2YV5HKV.js +87 -0
- package/dist/chunk-ZLVIVFSO.js +110 -0
- package/dist/{collectors-JB4OGUEF.js → collectors-H43PN4PS.js} +6 -3
- package/dist/{completions-VQMHQCXE.js → completions-JKYEJNGR.js} +34 -1
- package/dist/{config-KS5ZZ3VN.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 +460 -88
- package/dist/ioreport.py +72 -0
- package/dist/netUsageCmd-WKBTW2YG.js +90 -0
- package/dist/{pipeCmd-TYLJHQ5B.js → pipeCmd-7REDBLBU.js} +4 -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-Z3RGA3VB.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/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/render.ts +34 -10
- package/src/formatters/types.ts +2 -0
- package/src/index.ts +251 -36
- package/src/live/input.ts +60 -0
- package/src/live/render.ts +67 -0
- package/src/live/state.ts +5 -0
- package/src/live/types.ts +1 -1
- package/src/monitors/batteryPredictor.ts +143 -0
- package/src/monitors/buildTracker.ts +121 -0
- package/src/monitors/collectors.ts +342 -178
- 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 +15 -0
- 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 +58 -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-N4WJVF5M.js +0 -1184
- package/dist/doctor-UV7TWIHX.js +0 -184
- package/dist/extensions-DVA5YLGH.js +0 -74
- package/dist/service-WTS3NBQU.js +0 -73
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-CXL4GYGQ.js";
|
|
7
2
|
import {
|
|
8
3
|
TAB_BAR_ROW,
|
|
9
4
|
THEMES,
|
|
@@ -15,18 +10,31 @@ import {
|
|
|
15
10
|
panel,
|
|
16
11
|
sparkline,
|
|
17
12
|
visLen
|
|
18
|
-
} from "./chunk-
|
|
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";
|
|
19
26
|
import "./chunk-IM3IQMMT.js";
|
|
20
27
|
import {
|
|
21
28
|
collectAll
|
|
22
|
-
} from "./chunk-
|
|
23
|
-
import "./chunk-
|
|
24
|
-
import "./chunk-
|
|
29
|
+
} from "./chunk-GHRDW646.js";
|
|
30
|
+
import "./chunk-M73HCQD2.js";
|
|
31
|
+
import "./chunk-VIACN2RU.js";
|
|
32
|
+
import "./chunk-MZO5QKUR.js";
|
|
25
33
|
|
|
26
34
|
// src/index.ts
|
|
27
|
-
import
|
|
35
|
+
import fs10 from "fs";
|
|
28
36
|
import os2 from "os";
|
|
29
|
-
import
|
|
37
|
+
import path6 from "path";
|
|
30
38
|
import readline2 from "readline";
|
|
31
39
|
import { Command } from "commander";
|
|
32
40
|
import chalk10 from "chalk";
|
|
@@ -1550,6 +1558,8 @@ var state = {
|
|
|
1550
1558
|
"Splash Animation",
|
|
1551
1559
|
"Notifications",
|
|
1552
1560
|
"Temperature Unit",
|
|
1561
|
+
"AI Model",
|
|
1562
|
+
"Grid Panel Order",
|
|
1553
1563
|
"Toggle CPU",
|
|
1554
1564
|
"Toggle Memory",
|
|
1555
1565
|
"Toggle GPU",
|
|
@@ -1609,7 +1619,10 @@ var state = {
|
|
|
1609
1619
|
readmeScrollOffset: 0,
|
|
1610
1620
|
isDocker: isDockerEnv,
|
|
1611
1621
|
dockerModeConfirmed: config.dockerModeConfirmed || false,
|
|
1612
|
-
dockerSelectionIndex: 0
|
|
1622
|
+
dockerSelectionIndex: 0,
|
|
1623
|
+
aiModel: config.aiModel || "expert-rules-v1",
|
|
1624
|
+
aiBackend: config.aiBackend || "builtin",
|
|
1625
|
+
modelSelectionIndex: 0
|
|
1613
1626
|
};
|
|
1614
1627
|
function setStatus(msg, ms = 3e3) {
|
|
1615
1628
|
state.statusMessage = msg;
|
|
@@ -1938,7 +1951,8 @@ function tableCacheParams() {
|
|
|
1938
1951
|
state.treeView ? "1" : "0",
|
|
1939
1952
|
state.history.version,
|
|
1940
1953
|
JSON.stringify(state.visiblePanels),
|
|
1941
|
-
JSON.stringify(state.panelLayout)
|
|
1954
|
+
JSON.stringify(state.panelLayout),
|
|
1955
|
+
state.aiModel
|
|
1942
1956
|
].join("|");
|
|
1943
1957
|
}
|
|
1944
1958
|
function graphsCacheParams() {
|
|
@@ -2007,6 +2021,11 @@ function renderCustomizerOverlay(scrollOffset, maxVisibleItems) {
|
|
|
2007
2021
|
lines.push(`${prefix}${opt}: ${status}`);
|
|
2008
2022
|
} else if (opt === "Temperature Unit") {
|
|
2009
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 "))}`);
|
|
2010
2029
|
} else {
|
|
2011
2030
|
const toggleKey = getToggleKey(opt);
|
|
2012
2031
|
const isVisible = toggleKey ? toggleKey === "tree" ? state.treeView : state.visiblePanels[toggleKey] !== false : true;
|
|
@@ -2067,6 +2086,10 @@ function render() {
|
|
|
2067
2086
|
writeFrame(renderDockerAlert(cols, rows));
|
|
2068
2087
|
return;
|
|
2069
2088
|
}
|
|
2089
|
+
if (state.inputMode === "ai-model") {
|
|
2090
|
+
writeFrame(renderAiModelSelector(cols, rows));
|
|
2091
|
+
return;
|
|
2092
|
+
}
|
|
2070
2093
|
const lines = [];
|
|
2071
2094
|
const theme = THEMES[state.currentTheme] || THEMES.default;
|
|
2072
2095
|
const params = tableCacheParams();
|
|
@@ -2106,7 +2129,8 @@ function render() {
|
|
|
2106
2129
|
panelLayout: state.panelLayout,
|
|
2107
2130
|
selectedProcessIndex: state.processSelectionIndex,
|
|
2108
2131
|
trackedPid: state.trackedPid,
|
|
2109
|
-
inspectProcess: state.inspectingProcess
|
|
2132
|
+
inspectProcess: state.inspectingProcess,
|
|
2133
|
+
aiModel: state.aiModel
|
|
2110
2134
|
});
|
|
2111
2135
|
_cachedTableData = state.lastData;
|
|
2112
2136
|
_cachedTableParams = cacheParams;
|
|
@@ -2203,7 +2227,8 @@ function footerLine() {
|
|
|
2203
2227
|
["l", state.logging ? "stop log" : "log"],
|
|
2204
2228
|
["f", state.exportFormat],
|
|
2205
2229
|
["+/-", `${state.interval}s`],
|
|
2206
|
-
["r", state.p2pServerRunning ? "stop p2p" : "start p2p"]
|
|
2230
|
+
["r", state.p2pServerRunning ? "stop p2p" : "start p2p"],
|
|
2231
|
+
["m", `ai:${state.aiModel.split("-")[0]}`]
|
|
2207
2232
|
];
|
|
2208
2233
|
const fmt = (list) => list.map(([k, label]) => `${chalk2.hex("#50fa7b").bold(k)} ${chalk2.dim(label)}`).join(chalk2.dim(" | "));
|
|
2209
2234
|
const str1 = fmt(row1);
|
|
@@ -2213,6 +2238,7 @@ function footerLine() {
|
|
|
2213
2238
|
if (state.logging) badges.push(chalk2.red.bold("\u25CF REC"));
|
|
2214
2239
|
if (state.activePanel !== "grid") badges.push(chalk2.cyan.bold(`\u25C9 ${state.activePanel.toUpperCase()}`));
|
|
2215
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}`));
|
|
2216
2242
|
const badgeStr = badges.join(" ") || " ";
|
|
2217
2243
|
return [str1, str2, badgeStr];
|
|
2218
2244
|
}
|
|
@@ -2274,6 +2300,28 @@ function checkAlerts(data) {
|
|
|
2274
2300
|
exec(state.alertCmd, { env: { ...process.env, PYRE_ALERT: alertMsg } }, () => {
|
|
2275
2301
|
});
|
|
2276
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
|
+
}
|
|
2277
2325
|
} else if (!hot && !anomalyTriggered && !watchdogTriggered) {
|
|
2278
2326
|
state.alerted = false;
|
|
2279
2327
|
}
|
|
@@ -2390,6 +2438,29 @@ function renderDockerAlert(cols, rows) {
|
|
|
2390
2438
|
out.push(...panel("DOCKER DETECTED", content, cols, chalk2.red, chalk2.dim, rows - 2));
|
|
2391
2439
|
return out;
|
|
2392
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
|
+
}
|
|
2393
2464
|
|
|
2394
2465
|
// src/p2p/server.ts
|
|
2395
2466
|
import net from "net";
|
|
@@ -3056,6 +3127,58 @@ function deriveHMACKey2(password) {
|
|
|
3056
3127
|
return crypto3.createHash("sha256").update(password).digest("hex").slice(0, 32);
|
|
3057
3128
|
}
|
|
3058
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
|
+
|
|
3059
3182
|
// src/live/input.ts
|
|
3060
3183
|
function persistConfig() {
|
|
3061
3184
|
writeConfig({
|
|
@@ -3078,7 +3201,9 @@ function persistConfig() {
|
|
|
3078
3201
|
splashAnimation: state.splashAnimation,
|
|
3079
3202
|
visiblePanels: { ...state.visiblePanels },
|
|
3080
3203
|
notificationsEnabled: state.notificationsEnabled,
|
|
3081
|
-
dockerModeConfirmed: state.dockerModeConfirmed
|
|
3204
|
+
dockerModeConfirmed: state.dockerModeConfirmed,
|
|
3205
|
+
aiModel: state.aiModel,
|
|
3206
|
+
aiBackend: state.aiBackend
|
|
3082
3207
|
});
|
|
3083
3208
|
}
|
|
3084
3209
|
function isUpKey(key, str) {
|
|
@@ -3132,6 +3257,24 @@ function handleInputModeKey(str, key) {
|
|
|
3132
3257
|
} else if (selected === "Temperature Unit") {
|
|
3133
3258
|
state.tempUnit = state.tempUnit === "c" ? "f" : "c";
|
|
3134
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 ")}`);
|
|
3135
3278
|
} else {
|
|
3136
3279
|
const toggleKey = getToggleKey(selected);
|
|
3137
3280
|
if (toggleKey) {
|
|
@@ -3253,6 +3396,36 @@ function handleInputModeKey(str, key) {
|
|
|
3253
3396
|
}
|
|
3254
3397
|
return;
|
|
3255
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
|
+
}
|
|
3256
3429
|
if (state.inputMode === "signal") {
|
|
3257
3430
|
if (isUpKey(key, str)) {
|
|
3258
3431
|
const idx = state.SIGNAL_OPTIONS.indexOf(state.inputBuffer);
|
|
@@ -3485,6 +3658,8 @@ async function startLive(opts2, splashPromise) {
|
|
|
3485
3658
|
state.termHeight = process.stdout.rows || 24;
|
|
3486
3659
|
state.history.reset();
|
|
3487
3660
|
state.history.setMaxLen(Math.max(20, Math.min(200, state.termWidth - 30)));
|
|
3661
|
+
await loadPlugins().catch(() => {
|
|
3662
|
+
});
|
|
3488
3663
|
const warmupPromise = doWarmup();
|
|
3489
3664
|
if (splashPromise) {
|
|
3490
3665
|
await splashPromise;
|
|
@@ -3622,6 +3797,11 @@ async function startLive(opts2, splashPromise) {
|
|
|
3622
3797
|
state.customizerIndex = 0;
|
|
3623
3798
|
render();
|
|
3624
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;
|
|
3625
3805
|
case "p":
|
|
3626
3806
|
state.paused = !state.paused;
|
|
3627
3807
|
setStatus(state.paused ? "Paused" : "Resumed");
|
|
@@ -4192,8 +4372,8 @@ async function showSplash(config3) {
|
|
|
4192
4372
|
}
|
|
4193
4373
|
|
|
4194
4374
|
// src/historyCmd.ts
|
|
4195
|
-
import
|
|
4196
|
-
import
|
|
4375
|
+
import fs7 from "fs";
|
|
4376
|
+
import path4 from "path";
|
|
4197
4377
|
import chalk6 from "chalk";
|
|
4198
4378
|
function runHistoryCommand(opts2 = {}) {
|
|
4199
4379
|
const days = opts2.days || 7;
|
|
@@ -4201,11 +4381,11 @@ function runHistoryCommand(opts2 = {}) {
|
|
|
4201
4381
|
console.log(chalk6.bold(`
|
|
4202
4382
|
pyre history \u2014 Past ${days} Days Trend
|
|
4203
4383
|
`));
|
|
4204
|
-
if (!
|
|
4384
|
+
if (!fs7.existsSync(dir)) {
|
|
4205
4385
|
console.log(chalk6.yellow(` No logs directory found at ${dir}`));
|
|
4206
4386
|
return;
|
|
4207
4387
|
}
|
|
4208
|
-
const files =
|
|
4388
|
+
const files = fs7.readdirSync(dir).filter((f) => f.startsWith("pyre-log-") && f.endsWith(".csv"));
|
|
4209
4389
|
if (files.length === 0) {
|
|
4210
4390
|
console.log(chalk6.yellow(` No CSV logs found in ${dir}`));
|
|
4211
4391
|
return;
|
|
@@ -4215,9 +4395,9 @@ function runHistoryCommand(opts2 = {}) {
|
|
|
4215
4395
|
const memPoints = [];
|
|
4216
4396
|
const tempPoints = [];
|
|
4217
4397
|
for (const file of files.sort()) {
|
|
4218
|
-
const fullPath =
|
|
4398
|
+
const fullPath = path4.join(dir, file);
|
|
4219
4399
|
try {
|
|
4220
|
-
const content =
|
|
4400
|
+
const content = fs7.readFileSync(fullPath, "utf-8");
|
|
4221
4401
|
const lines = content.split("\n").slice(1);
|
|
4222
4402
|
for (const line of lines) {
|
|
4223
4403
|
if (!line.trim()) continue;
|
|
@@ -4252,7 +4432,7 @@ function runHistoryCommand(opts2 = {}) {
|
|
|
4252
4432
|
}
|
|
4253
4433
|
|
|
4254
4434
|
// src/diffCmd.ts
|
|
4255
|
-
import
|
|
4435
|
+
import fs8 from "fs";
|
|
4256
4436
|
import chalk7 from "chalk";
|
|
4257
4437
|
function runDiffCommand(file1, file2) {
|
|
4258
4438
|
console.log(chalk7.bold(`
|
|
@@ -4262,8 +4442,8 @@ function runDiffCommand(file1, file2) {
|
|
|
4262
4442
|
console.log(chalk7.dim(` Snapshot B: ${file2}
|
|
4263
4443
|
`));
|
|
4264
4444
|
try {
|
|
4265
|
-
const dataA = JSON.parse(
|
|
4266
|
-
const dataB = JSON.parse(
|
|
4445
|
+
const dataA = JSON.parse(fs8.readFileSync(file1, "utf-8"));
|
|
4446
|
+
const dataB = JSON.parse(fs8.readFileSync(file2, "utf-8"));
|
|
4267
4447
|
const diffNum = (label, valA, valB, unit = "") => {
|
|
4268
4448
|
const delta = valB - valA;
|
|
4269
4449
|
const sign = delta > 0 ? "+" : "";
|
|
@@ -4287,8 +4467,8 @@ function runDiffCommand(file1, file2) {
|
|
|
4287
4467
|
}
|
|
4288
4468
|
|
|
4289
4469
|
// src/xbar.ts
|
|
4290
|
-
import
|
|
4291
|
-
import
|
|
4470
|
+
import fs9 from "fs";
|
|
4471
|
+
import path5 from "path";
|
|
4292
4472
|
import os from "os";
|
|
4293
4473
|
import chalk8 from "chalk";
|
|
4294
4474
|
function generateXbarPlugin() {
|
|
@@ -4299,10 +4479,9 @@ function generateXbarPlugin() {
|
|
|
4299
4479
|
# <xbar.version>v1.0</xbar.version>
|
|
4300
4480
|
# <xbar.author>somalip</xbar.author>
|
|
4301
4481
|
# <xbar.author.github>somalip</xbar.author.github>
|
|
4302
|
-
# <xbar.desc>
|
|
4482
|
+
# <xbar.desc>Cross-platform system monitoring powered by pyre.</xbar.desc>
|
|
4303
4483
|
|
|
4304
4484
|
# Run pyre to get a JSON snapshot
|
|
4305
|
-
# We use node directly to avoid env issues in xbar
|
|
4306
4485
|
DATA=$("${nodePath}" "${pyrePath}" --once --json)
|
|
4307
4486
|
|
|
4308
4487
|
if [ -z "$DATA" ]; then
|
|
@@ -4325,15 +4504,25 @@ echo "\u{1F525} C:\${CPU}% M:\${MEM}% T:\${TEMP}\xB0C | font=Menlo"
|
|
|
4325
4504
|
echo "---"
|
|
4326
4505
|
echo "Open Dashboard | bash='${nodePath}' param1='${pyrePath}' param2='live' terminal=true"
|
|
4327
4506
|
`;
|
|
4328
|
-
const pluginDir = path4.join(os.homedir(), "Library", "Application Support", "xbar", "plugins");
|
|
4329
4507
|
const filename = "pyre-monitor.2s.sh";
|
|
4330
|
-
const outPath =
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
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
|
+
}
|
|
4337
4526
|
}
|
|
4338
4527
|
|
|
4339
4528
|
// src/fleet.ts
|
|
@@ -4367,36 +4556,48 @@ async function runFleetCommand(hosts) {
|
|
|
4367
4556
|
|
|
4368
4557
|
// src/index.ts
|
|
4369
4558
|
var config2 = readConfig();
|
|
4370
|
-
var pkg = JSON.parse(
|
|
4559
|
+
var pkg = JSON.parse(fs10.readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
|
|
4371
4560
|
var program = new Command();
|
|
4372
|
-
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");
|
|
4373
4562
|
program.addHelpText("after", `
|
|
4374
4563
|
Commands:
|
|
4375
4564
|
live Interactive live dashboard
|
|
4376
4565
|
check One-line plain-English health summary
|
|
4377
4566
|
pipe Continuous newline-delimited JSON stream for scripting
|
|
4378
4567
|
stress Synthetic CPU/GPU load generator
|
|
4379
|
-
ui Launch the
|
|
4568
|
+
ui Launch the live UI dashboard window
|
|
4380
4569
|
web Serve an auto-refreshing live web portal
|
|
4381
|
-
ssh <host> Stream live stats from a remote
|
|
4382
|
-
fleet <host1> [host2]... Multi-host live dashboard monitoring multiple
|
|
4570
|
+
ssh <host> Stream live stats from a remote host over SSH
|
|
4571
|
+
fleet <host1> [host2]... Multi-host live dashboard monitoring multiple hosts
|
|
4383
4572
|
bench <cmd> Log CPU, memory, network, power draw, and estimate kWh cost
|
|
4384
4573
|
benchmark Run a 1-minute CPU benchmark calculating digits of PI
|
|
4385
4574
|
anomalies Compute z-score anomalies and print plain-language digest
|
|
4386
4575
|
doctor Run system diagnostics
|
|
4387
|
-
extensions System Extensions inspector
|
|
4388
|
-
brew
|
|
4576
|
+
extensions System Extensions / Driver inspector
|
|
4577
|
+
brew Package manager health panel (brew/apt/pacman/winget)
|
|
4389
4578
|
update Check for pyre-cli updates
|
|
4390
4579
|
profile <save|load|list> Atomic configuration profile management
|
|
4391
4580
|
config <show|reset> View or reset persistent configuration file
|
|
4392
4581
|
history Graph historical resource trends from CSV logs
|
|
4393
4582
|
diff <file1> <file2> Compare two saved snapshot files side-by-side
|
|
4394
4583
|
info Concise hardware summary
|
|
4395
|
-
completions <shell> Generate shell auto-completion scripts
|
|
4584
|
+
completions <shell> Generate shell auto-completion scripts (zsh, bash, fish, powershell)
|
|
4396
4585
|
xbar Generate an xbar / SwiftBar menu bar plugin script
|
|
4397
4586
|
p2p <server|connect> Start a P2P server or connect to one
|
|
4398
4587
|
server Print commands for starting P2P server/client
|
|
4399
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
|
|
4400
4601
|
`);
|
|
4401
4602
|
program.parse(process.argv);
|
|
4402
4603
|
var opts = program.opts();
|
|
@@ -4457,11 +4658,11 @@ async function runServerCommand() {
|
|
|
4457
4658
|
console.log();
|
|
4458
4659
|
}
|
|
4459
4660
|
async function main() {
|
|
4460
|
-
const { runSetupWizardIfNeeded } = await import("./wizard-
|
|
4661
|
+
const { runSetupWizardIfNeeded } = await import("./wizard-TD7BGX6O.js");
|
|
4461
4662
|
await runSetupWizardIfNeeded(!opts.wizard);
|
|
4462
4663
|
const cmd = program.args[0];
|
|
4463
4664
|
if (cmd === "check") {
|
|
4464
|
-
const { runCheckCommand } = await import("./checkCmd-
|
|
4665
|
+
const { runCheckCommand } = await import("./checkCmd-RKW6QGDR.js");
|
|
4465
4666
|
await runCheckCommand({
|
|
4466
4667
|
exitCode: !!opts.exitCode,
|
|
4467
4668
|
cpuAlertPct: config2.cpuAlertPct,
|
|
@@ -4471,7 +4672,7 @@ async function main() {
|
|
|
4471
4672
|
return;
|
|
4472
4673
|
}
|
|
4473
4674
|
if (cmd === "pipe") {
|
|
4474
|
-
const { runPipeCommand } = await import("./pipeCmd-
|
|
4675
|
+
const { runPipeCommand } = await import("./pipeCmd-7REDBLBU.js");
|
|
4475
4676
|
const intervalSec = parseFloat(opts.interval) || 1;
|
|
4476
4677
|
const samples = parseInt(opts.samples, 10) || 0;
|
|
4477
4678
|
await runPipeCommand({
|
|
@@ -4494,12 +4695,12 @@ async function main() {
|
|
|
4494
4695
|
return;
|
|
4495
4696
|
}
|
|
4496
4697
|
if (cmd === "extensions") {
|
|
4497
|
-
const { printExtensionsReport } = await import("./extensions-
|
|
4698
|
+
const { printExtensionsReport } = await import("./extensions-XJGHG7X3.js");
|
|
4498
4699
|
await printExtensionsReport();
|
|
4499
4700
|
return;
|
|
4500
4701
|
}
|
|
4501
4702
|
if (cmd === "blender") {
|
|
4502
|
-
const { collectBlenderRenders } = await import("./blender-
|
|
4703
|
+
const { collectBlenderRenders } = await import("./blender-FGH3ASEQ.js");
|
|
4503
4704
|
const renders = await collectBlenderRenders();
|
|
4504
4705
|
if (!renders.length) {
|
|
4505
4706
|
console.log(chalk10.dim("No active Blender renders detected."));
|
|
@@ -4523,8 +4724,147 @@ async function main() {
|
|
|
4523
4724
|
console.log(lines.join("\n"));
|
|
4524
4725
|
return;
|
|
4525
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
|
+
}
|
|
4526
4866
|
if (cmd === "brew") {
|
|
4527
|
-
const { printBrewHealthReport } = await import("./brewHealth-
|
|
4867
|
+
const { printBrewHealthReport } = await import("./brewHealth-P2GW5Y5C.js");
|
|
4528
4868
|
await printBrewHealthReport();
|
|
4529
4869
|
return;
|
|
4530
4870
|
}
|
|
@@ -4536,7 +4876,7 @@ async function main() {
|
|
|
4536
4876
|
if (cmd === "profile") {
|
|
4537
4877
|
const sub = program.args[1];
|
|
4538
4878
|
const name = program.args[2];
|
|
4539
|
-
const { saveProfile, loadProfile, listProfiles } = await import("./config-
|
|
4879
|
+
const { saveProfile, loadProfile, listProfiles } = await import("./config-GYGJXU2Q.js");
|
|
4540
4880
|
if (sub === "save" && name) {
|
|
4541
4881
|
saveProfile(name);
|
|
4542
4882
|
console.log(chalk10.green(` \u2714 Profile '${name}' saved successfully.`));
|
|
@@ -4590,13 +4930,13 @@ async function main() {
|
|
|
4590
4930
|
}
|
|
4591
4931
|
if (cmd === "web") {
|
|
4592
4932
|
if (opts.install) {
|
|
4593
|
-
const { installLaunchdAgent } = await import("./service-
|
|
4933
|
+
const { installLaunchdAgent } = await import("./service-Q74BBILO.js");
|
|
4594
4934
|
const port = parseInt(opts.port || "3000", 10) || 3e3;
|
|
4595
4935
|
installLaunchdAgent(port);
|
|
4596
4936
|
return;
|
|
4597
4937
|
}
|
|
4598
4938
|
if (opts.uninstall) {
|
|
4599
|
-
const { uninstallLaunchdAgent } = await import("./service-
|
|
4939
|
+
const { uninstallLaunchdAgent } = await import("./service-Q74BBILO.js");
|
|
4600
4940
|
uninstallLaunchdAgent();
|
|
4601
4941
|
return;
|
|
4602
4942
|
}
|
|
@@ -4627,7 +4967,7 @@ async function main() {
|
|
|
4627
4967
|
const cfg = readConfig();
|
|
4628
4968
|
console.log(JSON.stringify(cfg, null, 2));
|
|
4629
4969
|
} else if (sub === "reset") {
|
|
4630
|
-
|
|
4970
|
+
fs10.rmSync(CONFIG_FILE, { force: true });
|
|
4631
4971
|
console.log(chalk10.green("Config reset to defaults"));
|
|
4632
4972
|
} else {
|
|
4633
4973
|
console.log("Usage: pyre config <show|reset>");
|
|
@@ -4643,7 +4983,7 @@ async function main() {
|
|
|
4643
4983
|
return;
|
|
4644
4984
|
}
|
|
4645
4985
|
if (cmd === "doctor") {
|
|
4646
|
-
const { runDoctor, printDoctorReport } = await import("./doctor-
|
|
4986
|
+
const { runDoctor, printDoctorReport } = await import("./doctor-GYHFTNZQ.js");
|
|
4647
4987
|
const checks = await runDoctor();
|
|
4648
4988
|
printDoctorReport(checks);
|
|
4649
4989
|
return;
|
|
@@ -4653,7 +4993,7 @@ async function main() {
|
|
|
4653
4993
|
return;
|
|
4654
4994
|
}
|
|
4655
4995
|
if (cmd === "completions") {
|
|
4656
|
-
const { printCompletions } = await import("./completions-
|
|
4996
|
+
const { printCompletions } = await import("./completions-JKYEJNGR.js");
|
|
4657
4997
|
const shell = program.args[1] || "zsh";
|
|
4658
4998
|
printCompletions(shell);
|
|
4659
4999
|
return;
|
|
@@ -4672,6 +5012,11 @@ async function main() {
|
|
|
4672
5012
|
if (opts.webhookUrl) config2.webhookUrl = opts.webhookUrl;
|
|
4673
5013
|
if (opts.alertCmd) config2.alertCmd = opts.alertCmd;
|
|
4674
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;
|
|
4675
5020
|
const tempUnit = opts.tempUnit === "f" ? "f" : "c";
|
|
4676
5021
|
if (cmd === "live" || !isExportMode() && !opts.once && cmd !== "p2p") {
|
|
4677
5022
|
if (!process.stdout.isTTY && cmd !== "live") {
|
|
@@ -4724,7 +5069,7 @@ async function main() {
|
|
|
4724
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 } });
|
|
4725
5070
|
console.log(output);
|
|
4726
5071
|
if (opts.out) {
|
|
4727
|
-
|
|
5072
|
+
fs10.writeFileSync(opts.out, stripAnsi(output));
|
|
4728
5073
|
console.log(chalk10.dim(`
|
|
4729
5074
|
Saved to ${opts.out}`));
|
|
4730
5075
|
}
|
|
@@ -4908,11 +5253,11 @@ async function runP2PConnect() {
|
|
|
4908
5253
|
}
|
|
4909
5254
|
async function runInfoCommand() {
|
|
4910
5255
|
const data = await collectAll({ detailed: true });
|
|
4911
|
-
const { getDisplayInfo, getTimeMachineStatus } = await import("./collectors-
|
|
5256
|
+
const { getDisplayInfo, getTimeMachineStatus } = await import("./collectors-H43PN4PS.js");
|
|
4912
5257
|
const displays = await getDisplayInfo();
|
|
4913
5258
|
const tmStatus = await getTimeMachineStatus();
|
|
4914
5259
|
const lines = [];
|
|
4915
|
-
lines.push(chalk10.bold(
|
|
5260
|
+
lines.push(chalk10.bold(` pyre info \u2014 System Information (${data.header.os})`));
|
|
4916
5261
|
lines.push("");
|
|
4917
5262
|
lines.push(` Hostname: ${data.header.hostname}`);
|
|
4918
5263
|
lines.push(` OS: ${data.header.os}`);
|
|
@@ -4937,13 +5282,15 @@ async function runInfoCommand() {
|
|
|
4937
5282
|
lines.push(` \u2022 ${d.name}: ${d.resolution}${d.isMain ? " (Main)" : ""}${d.connectionType ? ` [${d.connectionType}]` : ""}`);
|
|
4938
5283
|
}
|
|
4939
5284
|
}
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
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
|
+
}
|
|
4947
5294
|
}
|
|
4948
5295
|
console.log(lines.join("\n"));
|
|
4949
5296
|
}
|
|
@@ -4987,7 +5334,7 @@ async function runWebCommand() {
|
|
|
4987
5334
|
return;
|
|
4988
5335
|
}
|
|
4989
5336
|
if (url === "/metrics") {
|
|
4990
|
-
const { formatPrometheusMetrics } = await import("./prometheus-
|
|
5337
|
+
const { formatPrometheusMetrics } = await import("./prometheus-5BL5PVLC.js");
|
|
4991
5338
|
const data = await collectAll({ detailed: true });
|
|
4992
5339
|
res.writeHead(200, { "Content-Type": "text/plain; version=0.0.4; charset=utf-8" });
|
|
4993
5340
|
res.end(formatPrometheusMetrics(data));
|
|
@@ -5021,9 +5368,9 @@ async function runWebCommand() {
|
|
|
5021
5368
|
res.end(getDashboardHtml());
|
|
5022
5369
|
} else if (url === "/setup") {
|
|
5023
5370
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
5024
|
-
const indexPath =
|
|
5025
|
-
if (
|
|
5026
|
-
res.end(
|
|
5371
|
+
const indexPath = path6.join(process.cwd(), "index.html");
|
|
5372
|
+
if (fs10.existsSync(indexPath)) {
|
|
5373
|
+
res.end(fs10.readFileSync(indexPath, "utf-8"));
|
|
5027
5374
|
} else {
|
|
5028
5375
|
res.end(getDashboardHtml());
|
|
5029
5376
|
}
|
|
@@ -5073,10 +5420,26 @@ Error: Port ${port} is already in use.`));
|
|
|
5073
5420
|
});
|
|
5074
5421
|
});
|
|
5075
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
|
+
}
|
|
5076
5434
|
async function runUiCommand() {
|
|
5077
5435
|
opts.port = "0";
|
|
5078
5436
|
const port = await runWebCommand();
|
|
5079
|
-
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 = `
|
|
5080
5443
|
import Cocoa
|
|
5081
5444
|
import WebKit
|
|
5082
5445
|
|
|
@@ -5178,27 +5541,36 @@ let delegate = AppDelegate()
|
|
|
5178
5541
|
app.delegate = delegate
|
|
5179
5542
|
app.run()
|
|
5180
5543
|
`;
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
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(`
|
|
5185
5548
|
Launching native UI window...`));
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
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(() => {
|
|
5194
5566
|
});
|
|
5195
5567
|
}
|
|
5196
5568
|
async function runBenchCommand(benchCmd) {
|
|
5197
5569
|
const { spawn } = await import("child_process");
|
|
5198
5570
|
const logDir = opts.exportDir || "./pyre-exports";
|
|
5199
|
-
const file =
|
|
5200
|
-
if (!
|
|
5201
|
-
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" });
|
|
5202
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");
|
|
5203
5575
|
console.log(chalk10.bold(`
|
|
5204
5576
|
pyre bench`));
|
|
@@ -5208,7 +5580,7 @@ async function runBenchCommand(benchCmd) {
|
|
|
5208
5580
|
const startTime = Date.now();
|
|
5209
5581
|
let accumulatedWattSeconds = 0;
|
|
5210
5582
|
let powerSamplesCount = 0;
|
|
5211
|
-
const child = spawn("sh", ["-c", benchCmd]);
|
|
5583
|
+
const child = process.platform === "win32" ? spawn("cmd.exe", ["/c", benchCmd], { stdio: "inherit" }) : spawn("sh", ["-c", benchCmd]);
|
|
5212
5584
|
child.stdout.on("data", (d) => process.stdout.write(d));
|
|
5213
5585
|
child.stderr.on("data", (d) => process.stdout.write(d));
|
|
5214
5586
|
const interval = parseFloat(opts.interval) || 2;
|