pyre-cli 10.3.0 → 10.4.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 +41 -0
- package/dist/{alertChannels-TPSQIJ7S.js → alertChannels-MNX4XH6J.js} +1 -0
- package/dist/{anomaliesCmd-25OFKYPK.js → anomaliesCmd-HHTY5LYL.js} +2 -0
- package/dist/benchGitCmd-HIXUC5EK.js +87 -0
- package/dist/{benchmarkCmd-A3ETK6VY.js → benchmarkCmd-B7DLP2VC.js} +2 -0
- package/dist/{blender-FGH3ASEQ.js → blender-6ZX5SPYC.js} +3 -1
- package/dist/{brewHealth-P2GW5Y5C.js → brewHealth-VFW33QB3.js} +1 -0
- package/dist/budgetCmd-TGO3RYLN.js +11 -0
- package/dist/{buildCmd-CDPZ2ZFA.js → buildCmd-E7KUCFYG.js} +3 -1
- package/dist/{checkCmd-WQAHAUWO.js → checkCmd-QLYTP77Y.js} +5 -3
- package/dist/chunk-3RG5ZIWI.js +10 -0
- package/dist/{chunk-VIACN2RU.js → chunk-FYCBGUTW.js} +41 -19
- package/dist/chunk-JH7P6KLN.js +143 -0
- package/dist/{chunk-QM5L43EK.js → chunk-LDJB5V4N.js} +5 -1
- package/dist/chunk-NMGQVRCA.js +111 -0
- package/dist/chunk-OCL7P7N7.js +936 -0
- package/dist/{chunk-23YWCI3O.js → chunk-QEH4LR6P.js} +28 -686
- package/dist/{chunk-KVUMOA6C.js → chunk-RKPGPVTO.js} +77 -4
- package/dist/chunk-UF3XQ3R3.js +3800 -0
- package/dist/{collectors-JDN5Y7EV.js → collectors-V7SCIIDH.js} +5 -3
- package/dist/{completions-ZVTNE52B.js → completions-FTPXDSR2.js} +44 -3
- package/dist/{config-J4YMADEF.js → config-5WW5M63G.js} +2 -1
- package/dist/containersCmd-FSMYOBG2.js +84 -0
- package/dist/{doctor-LHFW4UJP.js → doctor-O4DIAMHK.js} +3 -2
- package/dist/{explainCmd-MF6PIMLW.js → explainCmd-7PBPA7O5.js} +1 -0
- package/dist/{extensions-XJGHG7X3.js → extensions-QQEV7U3D.js} +1 -0
- package/dist/{firebaseSync-ON3RZ6KX.js → firebaseSync-LRN67UTV.js} +2 -0
- package/dist/flightRecorder-GJIAQOXC.js +162 -0
- package/dist/index.js +736 -4125
- package/dist/inspectCmd-5FXJBJTK.js +137 -0
- package/dist/menu-AJFGDLBP.js +1233 -0
- package/dist/monitors/platform/windows-ui.ps1 +31 -29
- package/dist/{netUsageCmd-67IBZZYN.js → netUsageCmd-JP6YKBPF.js} +7 -5
- package/dist/optimizeCmd-WZKLFYZU.js +1110 -0
- package/dist/{pipeCmd-2N4JPKYE.js → pipeCmd-CPK6FVKB.js} +5 -3
- package/dist/playbookCmd-FRROIEXR.js +145 -0
- package/dist/{profileProcCmd-N4H6MCBT.js → profileProcCmd-5Y3733E7.js} +64 -13
- package/dist/{prometheus-5BL5PVLC.js → prometheus-CYNUR3QT.js} +1 -0
- package/dist/{prometheusCmd-LEUM5WCZ.js → prometheusCmd-G2CSEZRQ.js} +5 -3
- package/dist/{replayCmd-Z3PYXY7V.js → replayCmd-ODB5SPOQ.js} +25 -6
- package/dist/{run-HOGQDDTA.js → run-UJQYT2TO.js} +1 -0
- package/dist/{serveCmd-L7LVJOZN.js → serveCmd-KDPWLVQI.js} +5 -3
- package/dist/{service-7GIX3YNC.js → service-LIYYR3UI.js} +1 -0
- package/dist/{smartCmd-ODWKSFNG.js → smartCmd-2BJJ37GV.js} +130 -5
- package/dist/statuslineCmd-EUKYRR2G.js +75 -0
- package/dist/{stressCmd-MFXMFXYX.js → stressCmd-GKHGY26X.js} +2 -0
- package/dist/{syncCmd-42IRF633.js → syncCmd-MIBFKX24.js} +16 -5
- package/dist/topoCmd-RTXQJ6XA.js +189 -0
- package/dist/{updateCheck-52X6LJG5.js → updateCheck-LJHEMG3G.js} +2 -0
- package/dist/{watchdogCmd-6GR4UN6B.js → watchdogCmd-XM6OPTKM.js} +41 -6
- package/dist/{wizard-JQMQWFAW.js → wizard-PRDOJROQ.js} +2 -1
- package/package.json +2 -2
- package/src/benchGitCmd.ts +110 -0
- package/src/budgetCmd.ts +199 -0
- package/src/completions.ts +42 -3
- package/src/containersCmd.ts +106 -0
- package/src/doctor.ts +86 -6
- package/src/easy_features.test.ts +83 -0
- package/src/flightRecorder.ts +214 -0
- package/src/index.ts +181 -8
- package/src/inspectCmd.ts +165 -0
- package/src/live/input.ts +70 -3
- package/src/live/render.ts +1 -1
- package/src/live/state.ts +2 -1
- package/src/menu/commands.ts +754 -0
- package/src/menu/index.ts +358 -0
- package/src/menu/render.ts +272 -0
- package/src/menu/types.ts +63 -0
- package/src/menu.test.ts +110 -0
- package/src/monitors/blender.ts +49 -19
- package/src/monitors/buildTracker.ts +72 -54
- package/src/monitors/platform/windows-ui.ps1 +31 -29
- package/src/monitors/platform/windows.ts +613 -280
- package/src/optimizeCmd.ts +1248 -0
- package/src/platform.test.ts +135 -0
- package/src/playbookCmd.ts +156 -0
- package/src/power_features.test.ts +251 -0
- package/src/profileProcCmd.ts +68 -21
- package/src/replayCmd.ts +15 -6
- package/src/smartCmd.ts +149 -10
- package/src/state/config.ts +13 -0
- package/src/statuslineCmd.ts +87 -0
- package/src/syncCmd.ts +12 -0
- package/src/topoCmd.ts +83 -50
- package/src/watchdogCmd.ts +40 -3
- package/src/xbar.ts +195 -17
- package/dist/chunk-M73HCQD2.js +0 -95
- package/dist/topoCmd-DZ3YS5IL.js +0 -148
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import "./chunk-IM3IQMMT.js";
|
|
2
2
|
import {
|
|
3
3
|
collectAll
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-QEH4LR6P.js";
|
|
5
|
+
import "./chunk-NMGQVRCA.js";
|
|
6
|
+
import "./chunk-FYCBGUTW.js";
|
|
7
|
+
import "./chunk-OCL7P7N7.js";
|
|
7
8
|
import "./chunk-MZO5QKUR.js";
|
|
9
|
+
import "./chunk-3RG5ZIWI.js";
|
|
8
10
|
|
|
9
11
|
// src/pipeCmd.ts
|
|
10
12
|
async function runPipeCommand(opts = {}) {
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getConfigDir
|
|
3
|
+
} from "./chunk-LDJB5V4N.js";
|
|
4
|
+
import "./chunk-3RG5ZIWI.js";
|
|
5
|
+
|
|
6
|
+
// src/playbookCmd.ts
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { spawnSync } from "child_process";
|
|
10
|
+
import chalk from "chalk";
|
|
11
|
+
var BUILTIN_PLAYBOOKS = [
|
|
12
|
+
{
|
|
13
|
+
id: "audit",
|
|
14
|
+
name: "Full System Health Audit",
|
|
15
|
+
description: "Run health check, diagnostic doctor, drive S.M.A.R.T., and package manager audit",
|
|
16
|
+
category: "audit",
|
|
17
|
+
steps: [
|
|
18
|
+
{ name: "System Health Check", command: "check" },
|
|
19
|
+
{ name: "Doctor Diagnostics", command: "doctor" },
|
|
20
|
+
{ name: "Drive S.M.A.R.T. Health", command: "smart", allowFailure: true },
|
|
21
|
+
{ name: "Package Manager Health", command: "brew", allowFailure: true }
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "clean",
|
|
26
|
+
name: "Complete Mac Maintenance & Cleanup",
|
|
27
|
+
description: "Quit long-idle background apps, purge inactive memory, clean caches, flush DNS",
|
|
28
|
+
category: "maintenance",
|
|
29
|
+
steps: [
|
|
30
|
+
{ name: "Quit Idle Apps", command: "optimize apps --idle 2h -y" },
|
|
31
|
+
{ name: "Purge Inactive Memory", command: "optimize memory -y" },
|
|
32
|
+
{ name: "Clean Application Caches", command: "optimize caches --clean -y" },
|
|
33
|
+
{ name: "Flush DNS Resolver Cache", command: "optimize dns -y" }
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: "triage",
|
|
38
|
+
name: "Performance Spike & Incident Triage",
|
|
39
|
+
description: "Detect historical anomalies, inspect active network usage, and audit drivers",
|
|
40
|
+
category: "triage",
|
|
41
|
+
steps: [
|
|
42
|
+
{ name: "Z-Score Anomaly Digest", command: "anomalies --since 1d" },
|
|
43
|
+
{ name: "Per-Application Network Usage", command: "netusage --top 10" },
|
|
44
|
+
{ name: "System Extensions Audit", command: "extensions", allowFailure: true }
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: "bench",
|
|
49
|
+
name: "Quick Compute & Storage Baseline",
|
|
50
|
+
description: "Execute CPU Pi benchmark and query physical disk health",
|
|
51
|
+
category: "bench",
|
|
52
|
+
steps: [
|
|
53
|
+
{ name: "CPU Pi Benchmark", command: "benchmark" },
|
|
54
|
+
{ name: "Physical Disk Health", command: "smart", allowFailure: true }
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
];
|
|
58
|
+
function getCustomPlaybooks() {
|
|
59
|
+
const filePath = path.join(getConfigDir(), "playbooks.json");
|
|
60
|
+
if (!fs.existsSync(filePath)) return [];
|
|
61
|
+
try {
|
|
62
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
63
|
+
const parsed = JSON.parse(raw);
|
|
64
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
65
|
+
} catch {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function getAllPlaybooks() {
|
|
70
|
+
return [...BUILTIN_PLAYBOOKS, ...getCustomPlaybooks()];
|
|
71
|
+
}
|
|
72
|
+
async function runPlaybook(playbookId) {
|
|
73
|
+
const all = getAllPlaybooks();
|
|
74
|
+
const playbook = all.find((p) => p.id === playbookId || p.name.toLowerCase() === playbookId.toLowerCase());
|
|
75
|
+
if (!playbook) {
|
|
76
|
+
console.error(chalk.red(`
|
|
77
|
+
\u2716 Unknown playbook: '${playbookId}'`));
|
|
78
|
+
console.log(chalk.dim(` Available playbooks: ${all.map((p) => p.id).join(", ")}
|
|
79
|
+
`));
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
console.log(chalk.bold(`
|
|
83
|
+
\u{1F525} pyre playbook: ${chalk.hex("#ff6a39")(playbook.name)}`));
|
|
84
|
+
console.log(chalk.dim(` ${playbook.description}`));
|
|
85
|
+
console.log(chalk.dim(` Steps (${playbook.steps.length}):`));
|
|
86
|
+
playbook.steps.forEach((step, idx) => {
|
|
87
|
+
console.log(chalk.dim(` ${idx + 1}. ${step.name} (pyre ${step.command})`));
|
|
88
|
+
});
|
|
89
|
+
console.log(chalk.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
|
|
90
|
+
const scriptPath = process.argv[1];
|
|
91
|
+
const startTime = Date.now();
|
|
92
|
+
let stepIndex = 1;
|
|
93
|
+
let allSuccess = true;
|
|
94
|
+
for (const step of playbook.steps) {
|
|
95
|
+
console.log(chalk.bold.cyan(`
|
|
96
|
+
\u25B6 [${stepIndex}/${playbook.steps.length}] ${step.name} ...`));
|
|
97
|
+
console.log(chalk.dim(` $ pyre ${step.command}
|
|
98
|
+
`));
|
|
99
|
+
const tokens = step.command.trim().split(/\s+/).filter(Boolean);
|
|
100
|
+
const result = spawnSync(process.argv[0], [scriptPath, ...tokens], {
|
|
101
|
+
stdio: "inherit",
|
|
102
|
+
env: process.env
|
|
103
|
+
});
|
|
104
|
+
if (result.status !== 0 && !step.allowFailure) {
|
|
105
|
+
console.log(chalk.red(`
|
|
106
|
+
\u2716 Step '${step.name}' failed with exit code ${result.status}`));
|
|
107
|
+
allSuccess = false;
|
|
108
|
+
break;
|
|
109
|
+
} else if (result.status !== 0 && step.allowFailure) {
|
|
110
|
+
console.log(chalk.yellow(`
|
|
111
|
+
\u26A0 Step '${step.name}' failed but marked as optional \u2014 continuing...`));
|
|
112
|
+
} else {
|
|
113
|
+
console.log(chalk.green(`
|
|
114
|
+
\u2714 Step '${step.name}' completed.`));
|
|
115
|
+
}
|
|
116
|
+
stepIndex++;
|
|
117
|
+
}
|
|
118
|
+
const durationSec = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
119
|
+
console.log(chalk.dim("\n \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
120
|
+
if (allSuccess) {
|
|
121
|
+
console.log(chalk.bold.green(` \u2714 Playbook '${playbook.name}' completed successfully in ${durationSec}s
|
|
122
|
+
`));
|
|
123
|
+
} else {
|
|
124
|
+
console.log(chalk.bold.red(` \u2716 Playbook '${playbook.name}' aborted after ${durationSec}s
|
|
125
|
+
`));
|
|
126
|
+
}
|
|
127
|
+
return allSuccess;
|
|
128
|
+
}
|
|
129
|
+
function listPlaybooks() {
|
|
130
|
+
const all = getAllPlaybooks();
|
|
131
|
+
console.log(chalk.bold("\n \u{1F525} pyre Playbooks & Macro Recipes:\n"));
|
|
132
|
+
for (const p of all) {
|
|
133
|
+
console.log(` \u2022 ${chalk.bold.hex("#ff6a39")(p.id.padEnd(12))} ${p.name}`);
|
|
134
|
+
console.log(chalk.dim(` ${p.description}`));
|
|
135
|
+
console.log(chalk.dim(` Steps: ${p.steps.map((s) => s.command).join(" \u2794 ")}
|
|
136
|
+
`));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
export {
|
|
140
|
+
BUILTIN_PLAYBOOKS,
|
|
141
|
+
getAllPlaybooks,
|
|
142
|
+
getCustomPlaybooks,
|
|
143
|
+
listPlaybooks,
|
|
144
|
+
runPlaybook
|
|
145
|
+
};
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runPowerShell
|
|
3
|
+
} from "./chunk-MZO5QKUR.js";
|
|
4
|
+
import "./chunk-3RG5ZIWI.js";
|
|
5
|
+
|
|
1
6
|
// src/profileProcCmd.ts
|
|
2
|
-
import { execFile } from "child_process";
|
|
7
|
+
import { execFile, execSync } from "child_process";
|
|
3
8
|
import { promisify } from "util";
|
|
4
9
|
import chalk from "chalk";
|
|
5
10
|
var execFileAsync = promisify(execFile);
|
|
@@ -9,9 +14,20 @@ async function runProfileProcCommand(opts) {
|
|
|
9
14
|
console.error(chalk.red("\n \u2716 Error: Valid PID is required. Usage: pyre profile-proc <pid> [--duration <sec>]\n"));
|
|
10
15
|
process.exit(1);
|
|
11
16
|
}
|
|
17
|
+
let processAlive = false;
|
|
12
18
|
try {
|
|
13
19
|
process.kill(pid, 0);
|
|
20
|
+
processAlive = true;
|
|
14
21
|
} catch {
|
|
22
|
+
if (process.platform === "win32") {
|
|
23
|
+
try {
|
|
24
|
+
const out = execSync(`tasklist /fi "PID eq ${pid}" /nh`, { stdio: "pipe" }).toString();
|
|
25
|
+
processAlive = out.includes(String(pid));
|
|
26
|
+
} catch {
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (!processAlive) {
|
|
15
31
|
console.error(chalk.red(`
|
|
16
32
|
\u2716 Error: Process with PID ${pid} is not running or accessible.
|
|
17
33
|
`));
|
|
@@ -22,7 +38,38 @@ async function runProfileProcCommand(opts) {
|
|
|
22
38
|
`));
|
|
23
39
|
let sampleOutput = "";
|
|
24
40
|
let usedFallback = false;
|
|
25
|
-
if (process.platform === "
|
|
41
|
+
if (process.platform === "win32") {
|
|
42
|
+
try {
|
|
43
|
+
const psScript = `
|
|
44
|
+
$p1 = Get-Process -Id ${pid} -ErrorAction SilentlyContinue
|
|
45
|
+
if (-not $p1) { exit 1 }
|
|
46
|
+
$cpu1 = $p1.TotalProcessorTime.TotalSeconds
|
|
47
|
+
$start = Get-Date
|
|
48
|
+
Start-Sleep -Seconds ${durationSec}
|
|
49
|
+
$p2 = Get-Process -Id ${pid} -ErrorAction SilentlyContinue
|
|
50
|
+
$cpu2 = if ($p2) { $p2.TotalProcessorTime.TotalSeconds } else { $cpu1 }
|
|
51
|
+
$elapsed = ((Get-Date) - $start).TotalSeconds
|
|
52
|
+
$cpuPct = if ($elapsed -gt 0) { [math]::Round((($cpu2 - $cpu1) / $elapsed) * 100, 1) } else { 0 }
|
|
53
|
+
|
|
54
|
+
Write-Output "Process: $($p1.ProcessName) (PID $($p1.Id))"
|
|
55
|
+
Write-Output "Path: $($p1.Path)"
|
|
56
|
+
Write-Output "Average CPU Utilization: $cpuPct%"
|
|
57
|
+
Write-Output "Working Set (RAM): $([math]::Round($p1.WorkingSet64 / 1MB, 1)) MB"
|
|
58
|
+
Write-Output "Threads Total: $($p1.Threads.Count)"
|
|
59
|
+
Write-Output ""
|
|
60
|
+
Write-Output "Call graph / Active Threads:"
|
|
61
|
+
$p1.Threads | Select-Object -First 25 | ForEach-Object {
|
|
62
|
+
$state = $_.ThreadState
|
|
63
|
+
$wait = $_.WaitReason
|
|
64
|
+
$time = [math]::Round($_.TotalProcessorTime.TotalSeconds, 3)
|
|
65
|
+
Write-Output " + [TID $($_.Id)] CPU: $($time)s | State: $state | WaitReason: $wait"
|
|
66
|
+
}
|
|
67
|
+
`;
|
|
68
|
+
sampleOutput = await runPowerShell(psScript, "", (durationSec + 8) * 1e3);
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (!sampleOutput && process.platform === "darwin") {
|
|
26
73
|
try {
|
|
27
74
|
const { stdout } = await execFileAsync("sample", [String(pid), String(durationSec), "-file", "/dev/stdout"], {
|
|
28
75
|
timeout: (durationSec + 5) * 1e3,
|
|
@@ -33,13 +80,17 @@ async function runProfileProcCommand(opts) {
|
|
|
33
80
|
sampleOutput = err.stdout || "";
|
|
34
81
|
}
|
|
35
82
|
}
|
|
36
|
-
if (!sampleOutput || sampleOutput.length <
|
|
83
|
+
if (!sampleOutput || sampleOutput.length < 50) {
|
|
37
84
|
usedFallback = true;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
85
|
+
if (process.platform !== "win32") {
|
|
86
|
+
try {
|
|
87
|
+
const { stdout } = await execFileAsync("ps", ["-M", "-p", String(pid), "-o", "pid,thread,pcpu,pmem,state,time,command"]);
|
|
88
|
+
sampleOutput = stdout;
|
|
89
|
+
} catch {
|
|
90
|
+
sampleOutput = `Process PID ${pid} active. Deep stack sampling requires elevated permissions or user-owned process.`;
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
sampleOutput = `Process PID ${pid} active. Profiling finished.`;
|
|
43
94
|
}
|
|
44
95
|
}
|
|
45
96
|
if (raw) {
|
|
@@ -52,10 +103,10 @@ async function runProfileProcCommand(opts) {
|
|
|
52
103
|
const stackLines = [];
|
|
53
104
|
let inCallGraph = false;
|
|
54
105
|
for (const line of lines) {
|
|
55
|
-
if (line.includes("Analysis of sampling") || line.includes("Process:") || line.includes("Path:") || line.includes("Identifier:")) {
|
|
106
|
+
if (line.includes("Analysis of sampling") || line.includes("Process:") || line.includes("Path:") || line.includes("Identifier:") || line.includes("Average CPU Utilization:") || line.includes("Working Set") || line.includes("Threads Total:")) {
|
|
56
107
|
headerLines.push(line.trim());
|
|
57
108
|
}
|
|
58
|
-
if (line.includes("Call graph:") || line.includes("+ Line Coverage")) {
|
|
109
|
+
if (line.includes("Call graph:") || line.includes("Call graph / Active Threads:") || line.includes("+ Line Coverage")) {
|
|
59
110
|
inCallGraph = true;
|
|
60
111
|
continue;
|
|
61
112
|
}
|
|
@@ -69,12 +120,12 @@ async function runProfileProcCommand(opts) {
|
|
|
69
120
|
}
|
|
70
121
|
if (headerLines.length > 0) {
|
|
71
122
|
console.log(chalk.dim(" Process Info:"));
|
|
72
|
-
for (const h of headerLines.slice(0,
|
|
123
|
+
for (const h of headerLines.slice(0, 6)) {
|
|
73
124
|
console.log(` ${chalk.cyan(h)}`);
|
|
74
125
|
}
|
|
75
126
|
console.log();
|
|
76
127
|
}
|
|
77
|
-
console.log(chalk.bold(" Top Call Stacks & Hot
|
|
128
|
+
console.log(chalk.bold(" Top Call Stacks & Hot Threads:"));
|
|
78
129
|
console.log(chalk.dim(" " + "\u2500".repeat(70)));
|
|
79
130
|
if (stackLines.length === 0) {
|
|
80
131
|
for (const l of lines.slice(0, 20)) {
|
|
@@ -92,7 +143,7 @@ async function runProfileProcCommand(opts) {
|
|
|
92
143
|
}
|
|
93
144
|
}
|
|
94
145
|
} else {
|
|
95
|
-
console.log(chalk.yellow(" Note:
|
|
146
|
+
console.log(chalk.yellow(" Note: Operating system permissions prevented deep call-graph unwind."));
|
|
96
147
|
console.log(chalk.dim(" Showing process thread snapshot:\n"));
|
|
97
148
|
for (const l of sampleOutput.split("\n")) {
|
|
98
149
|
if (l.trim()) console.log(` ${l}`);
|
|
@@ -4,10 +4,12 @@ import {
|
|
|
4
4
|
import "./chunk-IM3IQMMT.js";
|
|
5
5
|
import {
|
|
6
6
|
collectAll
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-QEH4LR6P.js";
|
|
8
|
+
import "./chunk-NMGQVRCA.js";
|
|
9
|
+
import "./chunk-FYCBGUTW.js";
|
|
10
|
+
import "./chunk-OCL7P7N7.js";
|
|
10
11
|
import "./chunk-MZO5QKUR.js";
|
|
12
|
+
import "./chunk-3RG5ZIWI.js";
|
|
11
13
|
|
|
12
14
|
// src/prometheusCmd.ts
|
|
13
15
|
import http from "http";
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import "./chunk-3RG5ZIWI.js";
|
|
2
|
+
|
|
1
3
|
// src/replayCmd.ts
|
|
2
4
|
import fs from "fs";
|
|
3
5
|
import readline from "readline";
|
|
@@ -65,14 +67,15 @@ function renderFrame(row, idx, total, speed, paused, cols) {
|
|
|
65
67
|
}
|
|
66
68
|
lines.push("");
|
|
67
69
|
lines.push(" " + divider);
|
|
68
|
-
const progWidth = 30;
|
|
70
|
+
const progWidth = Math.min(40, Math.max(20, cols - 30));
|
|
71
|
+
const pct = total > 1 ? idx / (total - 1) * 100 : 0;
|
|
69
72
|
const progress = total > 1 ? Math.round(idx / (total - 1) * progWidth) : 0;
|
|
70
|
-
const progressBar = chalk.cyan("\
|
|
73
|
+
const progressBar = chalk.cyan("\u2501".repeat(progress)) + chalk.bold.yellow("\u25C6") + chalk.dim("\u2500".repeat(Math.max(0, progWidth - progress)));
|
|
71
74
|
const icon = paused ? chalk.yellow("\u23F8") : chalk.green("\u25B6");
|
|
72
75
|
const spStr = speed === 1 ? "1\xD7" : `${speed}\xD7`;
|
|
73
|
-
lines.push(` ${icon} ${progressBar}
|
|
76
|
+
lines.push(` ${icon} [${progressBar}] ${chalk.bold(`${pct.toFixed(0)}%`)} (${idx + 1}/${total}) ${chalk.dim(spStr)}`);
|
|
74
77
|
lines.push("");
|
|
75
|
-
lines.push(chalk.dim(" Space/p=pause ]/[=speed \u2190/\u2192=step q=quit"));
|
|
78
|
+
lines.push(chalk.dim(" Space/p=pause ]/[=speed \u2190/\u2192=step 0-9=jump % Home/End=seek q=quit"));
|
|
76
79
|
return lines;
|
|
77
80
|
}
|
|
78
81
|
async function runReplayCommand(opts) {
|
|
@@ -132,12 +135,28 @@ async function runReplayCommand(opts) {
|
|
|
132
135
|
return;
|
|
133
136
|
}
|
|
134
137
|
if (key.name === "right") {
|
|
135
|
-
frameIdx = Math.min(frameIdx + 1, rows.length - 1);
|
|
138
|
+
frameIdx = Math.min(frameIdx + (key.shift ? 10 : 1), rows.length - 1);
|
|
136
139
|
renderCurrent();
|
|
137
140
|
return;
|
|
138
141
|
}
|
|
139
142
|
if (key.name === "left") {
|
|
140
|
-
frameIdx = Math.max(frameIdx - 1, 0);
|
|
143
|
+
frameIdx = Math.max(frameIdx - (key.shift ? 10 : 1), 0);
|
|
144
|
+
renderCurrent();
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (key.name === "home" || str === "0") {
|
|
148
|
+
frameIdx = 0;
|
|
149
|
+
renderCurrent();
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (key.name === "end") {
|
|
153
|
+
frameIdx = rows.length - 1;
|
|
154
|
+
renderCurrent();
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (str >= "1" && str <= "9") {
|
|
158
|
+
const frac = parseInt(str, 10) / 10;
|
|
159
|
+
frameIdx = Math.min(rows.length - 1, Math.round(frac * (rows.length - 1)));
|
|
141
160
|
renderCurrent();
|
|
142
161
|
return;
|
|
143
162
|
}
|
|
@@ -10,10 +10,12 @@ import {
|
|
|
10
10
|
collectProcesses,
|
|
11
11
|
collectSystem,
|
|
12
12
|
collectThermal
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
13
|
+
} from "./chunk-QEH4LR6P.js";
|
|
14
|
+
import "./chunk-NMGQVRCA.js";
|
|
15
|
+
import "./chunk-FYCBGUTW.js";
|
|
16
|
+
import "./chunk-OCL7P7N7.js";
|
|
16
17
|
import "./chunk-MZO5QKUR.js";
|
|
18
|
+
import "./chunk-3RG5ZIWI.js";
|
|
17
19
|
|
|
18
20
|
// src/serveCmd.ts
|
|
19
21
|
import http from "http";
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runPowerShell
|
|
3
|
+
} from "./chunk-MZO5QKUR.js";
|
|
4
|
+
import "./chunk-3RG5ZIWI.js";
|
|
5
|
+
|
|
1
6
|
// src/smartCmd.ts
|
|
2
7
|
import { execFile } from "child_process";
|
|
3
8
|
import { promisify } from "util";
|
|
@@ -11,6 +16,62 @@ async function runCmd(cmd, args, timeoutMs = 5e3) {
|
|
|
11
16
|
return "";
|
|
12
17
|
}
|
|
13
18
|
}
|
|
19
|
+
function parseWindowsPhysicalDiskCsv(csvRaw) {
|
|
20
|
+
const lines = csvRaw.split("\n").filter((l) => l.trim().length > 0);
|
|
21
|
+
if (lines.length < 2) return [];
|
|
22
|
+
const headers = lines[0].split(",").map((h) => h.trim().replace(/^"|"$/g, ""));
|
|
23
|
+
const disks = [];
|
|
24
|
+
for (const line of lines.slice(1)) {
|
|
25
|
+
const values = line.split(",").map((v) => v.trim().replace(/^"|"$/g, ""));
|
|
26
|
+
const row = {};
|
|
27
|
+
headers.forEach((h, i) => {
|
|
28
|
+
row[h] = values[i] || "";
|
|
29
|
+
});
|
|
30
|
+
const deviceId = row.DeviceId || row.DeviceID || "0";
|
|
31
|
+
const friendlyName = row.FriendlyName || row.Model || "Physical Disk";
|
|
32
|
+
const serialNumber = row.SerialNumber || "";
|
|
33
|
+
const mediaType = row.MediaType || row.InterfaceType || "";
|
|
34
|
+
const healthStatus = (row.HealthStatus || row.Status || "").toUpperCase();
|
|
35
|
+
const sizeBytes = parseFloat(row.Size) || 0;
|
|
36
|
+
let health = "Unknown";
|
|
37
|
+
if (healthStatus.includes("HEALTHY") || healthStatus.includes("OK") || healthStatus.includes("PASSED")) {
|
|
38
|
+
health = "PASSED";
|
|
39
|
+
} else if (healthStatus.includes("UNHEALTHY") || healthStatus.includes("ERROR") || healthStatus.includes("FAILED")) {
|
|
40
|
+
health = "FAILED";
|
|
41
|
+
}
|
|
42
|
+
const capacity = sizeBytes > 0 ? sizeBytes >= 1e12 ? `${(sizeBytes / 1e12).toFixed(1)} TB` : sizeBytes >= 1e9 ? `${(sizeBytes / 1e9).toFixed(0)} GB` : `${(sizeBytes / 1e6).toFixed(0)} MB` : void 0;
|
|
43
|
+
disks.push({
|
|
44
|
+
device: `\\\\.\\PhysicalDrive${deviceId}`,
|
|
45
|
+
model: friendlyName,
|
|
46
|
+
serialNumber,
|
|
47
|
+
health,
|
|
48
|
+
protocol: mediaType,
|
|
49
|
+
capacity
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return disks;
|
|
53
|
+
}
|
|
54
|
+
function parseWindowsReliabilityCsv(csvRaw) {
|
|
55
|
+
const lines = csvRaw.split("\n").filter((l) => l.trim().length > 0);
|
|
56
|
+
if (lines.length < 2) return {};
|
|
57
|
+
const headers = lines[0].split(",").map((h) => h.trim().replace(/^"|"$/g, ""));
|
|
58
|
+
const values = lines[1].split(",").map((v) => v.trim().replace(/^"|"$/g, ""));
|
|
59
|
+
const row = {};
|
|
60
|
+
headers.forEach((h, i) => {
|
|
61
|
+
row[h] = values[i] || "";
|
|
62
|
+
});
|
|
63
|
+
const temp = parseFloat(row.Temperature);
|
|
64
|
+
const wear = parseFloat(row.Wear);
|
|
65
|
+
const poh = parseFloat(row.PowerOnHours);
|
|
66
|
+
const readErrors = parseFloat(row.ReadErrorsTotal);
|
|
67
|
+
const writeErrors = parseFloat(row.WriteErrorsTotal);
|
|
68
|
+
return {
|
|
69
|
+
tempC: Number.isFinite(temp) && temp > 0 && temp < 120 ? temp : void 0,
|
|
70
|
+
percentUsed: Number.isFinite(wear) && wear >= 0 && wear <= 100 ? wear : void 0,
|
|
71
|
+
powerOnHours: Number.isFinite(poh) && poh >= 0 ? poh : void 0,
|
|
72
|
+
reallocatedSectors: (Number.isFinite(readErrors) ? readErrors : 0) + (Number.isFinite(writeErrors) ? writeErrors : 0)
|
|
73
|
+
};
|
|
74
|
+
}
|
|
14
75
|
async function getPhysicalDisks() {
|
|
15
76
|
const scanRaw = await runCmd("smartctl", ["--scan", "--json"]);
|
|
16
77
|
if (scanRaw) {
|
|
@@ -31,7 +92,18 @@ async function getPhysicalDisks() {
|
|
|
31
92
|
return ["/dev/sda"];
|
|
32
93
|
}
|
|
33
94
|
if (process.platform === "win32") {
|
|
34
|
-
|
|
95
|
+
try {
|
|
96
|
+
const raw2 = await runPowerShell(
|
|
97
|
+
`Get-PhysicalDisk -ErrorAction SilentlyContinue | Select-Object -ExpandProperty DeviceId`,
|
|
98
|
+
""
|
|
99
|
+
);
|
|
100
|
+
const ids = raw2.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
101
|
+
if (ids.length > 0) {
|
|
102
|
+
return ids.map((id) => `\\\\.\\PhysicalDrive${id}`);
|
|
103
|
+
}
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
return ["\\\\.\\PhysicalDrive0"];
|
|
35
107
|
}
|
|
36
108
|
const raw = await runCmd("diskutil", ["list", "-plist"]);
|
|
37
109
|
if (!raw) return ["/dev/disk0"];
|
|
@@ -88,6 +160,55 @@ async function collectSmartctlDisk(device) {
|
|
|
88
160
|
return null;
|
|
89
161
|
}
|
|
90
162
|
}
|
|
163
|
+
async function collectWindowsDiskFallback(device) {
|
|
164
|
+
const match = device.match(/PhysicalDrive(\d+)/i);
|
|
165
|
+
const diskNumber = match ? match[1] : "0";
|
|
166
|
+
try {
|
|
167
|
+
const rawDisk = await runPowerShell(
|
|
168
|
+
`Get-PhysicalDisk -DeviceId '${diskNumber}' -ErrorAction SilentlyContinue | Select-Object DeviceId, FriendlyName, SerialNumber, MediaType, HealthStatus, Size | ConvertTo-Csv -NoTypeInformation`,
|
|
169
|
+
""
|
|
170
|
+
);
|
|
171
|
+
const parsed = parseWindowsPhysicalDiskCsv(rawDisk);
|
|
172
|
+
if (parsed.length > 0) {
|
|
173
|
+
const info = parsed[0];
|
|
174
|
+
info.device = device;
|
|
175
|
+
try {
|
|
176
|
+
const rawReliability = await runPowerShell(
|
|
177
|
+
`Get-StorageReliabilityCounter -Disk (Get-Disk -Number ${diskNumber} -ErrorAction SilentlyContinue) -ErrorAction SilentlyContinue | Select-Object Temperature, Wear, PowerOnHours, ReadErrorsTotal, WriteErrorsTotal | ConvertTo-Csv -NoTypeInformation`,
|
|
178
|
+
""
|
|
179
|
+
);
|
|
180
|
+
const rel = parseWindowsReliabilityCsv(rawReliability);
|
|
181
|
+
if (rel.tempC !== void 0) info.tempC = rel.tempC;
|
|
182
|
+
if (rel.percentUsed !== void 0) info.percentUsed = rel.percentUsed;
|
|
183
|
+
if (rel.powerOnHours !== void 0) info.powerOnHours = rel.powerOnHours;
|
|
184
|
+
if (rel.reallocatedSectors !== void 0) info.reallocatedSectors = rel.reallocatedSectors;
|
|
185
|
+
} catch {
|
|
186
|
+
}
|
|
187
|
+
return info;
|
|
188
|
+
}
|
|
189
|
+
} catch {
|
|
190
|
+
}
|
|
191
|
+
try {
|
|
192
|
+
const rawWmi = await runPowerShell(
|
|
193
|
+
`Get-CimInstance Win32_DiskDrive -Filter "Index = ${diskNumber}" -ErrorAction SilentlyContinue | Select-Object Model, SerialNumber, Status, Size | ConvertTo-Csv -NoTypeInformation`,
|
|
194
|
+
""
|
|
195
|
+
);
|
|
196
|
+
const lines = rawWmi.split("\n").filter((l) => l.trim());
|
|
197
|
+
if (lines.length >= 2) {
|
|
198
|
+
const values = lines[1].split(",").map((s) => s.trim().replace(/^"|"$/g, ""));
|
|
199
|
+
const sizeBytes = parseFloat(values[3]) || 0;
|
|
200
|
+
return {
|
|
201
|
+
device,
|
|
202
|
+
model: values[0] || "Physical Disk",
|
|
203
|
+
serialNumber: values[1] || "",
|
|
204
|
+
health: values[2]?.toUpperCase().includes("OK") ? "PASSED" : "Unknown",
|
|
205
|
+
capacity: sizeBytes >= 1e12 ? `${(sizeBytes / 1e12).toFixed(1)} TB` : `${(sizeBytes / 1e9).toFixed(0)} GB`
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
} catch {
|
|
209
|
+
}
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
91
212
|
async function collectDiskutilFallback(device) {
|
|
92
213
|
const raw = await runCmd("diskutil", ["info", device]);
|
|
93
214
|
if (!raw) return null;
|
|
@@ -118,8 +239,9 @@ async function runSmartCommand() {
|
|
|
118
239
|
const smartctlVersion = await runCmd("smartctl", ["--version"], 2e3);
|
|
119
240
|
const hasSmartctl = smartctlVersion.includes("smartmontools");
|
|
120
241
|
if (!hasSmartctl) {
|
|
121
|
-
console.log(chalk.yellow(" \u26A0 smartctl not found. Showing
|
|
122
|
-
|
|
242
|
+
console.log(chalk.yellow(" \u26A0 smartctl not found. Showing native storage health metrics."));
|
|
243
|
+
const installHint = process.platform === "win32" ? "winget install smartmontools" : process.platform === "linux" ? "sudo apt install smartmontools" : "brew install smartmontools";
|
|
244
|
+
console.log(chalk.dim(" For full SMART data: ") + chalk.cyan(installHint) + "\n");
|
|
123
245
|
}
|
|
124
246
|
const devices = await getPhysicalDisks();
|
|
125
247
|
if (devices.length === 0) {
|
|
@@ -129,7 +251,8 @@ async function runSmartCommand() {
|
|
|
129
251
|
for (const device of devices) {
|
|
130
252
|
let info = null;
|
|
131
253
|
if (hasSmartctl) info = await collectSmartctlDisk(device);
|
|
132
|
-
if (!info) info = await
|
|
254
|
+
if (!info && process.platform === "win32") info = await collectWindowsDiskFallback(device);
|
|
255
|
+
if (!info && process.platform === "darwin") info = await collectDiskutilFallback(device);
|
|
133
256
|
if (!info) continue;
|
|
134
257
|
console.log(chalk.bold(` ${info.device}`));
|
|
135
258
|
console.log(` Model: ${info.model}`);
|
|
@@ -158,9 +281,11 @@ async function runSmartCommand() {
|
|
|
158
281
|
console.log();
|
|
159
282
|
}
|
|
160
283
|
if (!hasSmartctl) {
|
|
161
|
-
console.log(chalk.dim(" Install smartmontools for
|
|
284
|
+
console.log(chalk.dim(" Install smartmontools for complete SMART attribute dump.\n"));
|
|
162
285
|
}
|
|
163
286
|
}
|
|
164
287
|
export {
|
|
288
|
+
parseWindowsPhysicalDiskCsv,
|
|
289
|
+
parseWindowsReliabilityCsv,
|
|
165
290
|
runSmartCommand
|
|
166
291
|
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import {
|
|
2
|
+
collectAll
|
|
3
|
+
} from "./chunk-QEH4LR6P.js";
|
|
4
|
+
import "./chunk-NMGQVRCA.js";
|
|
5
|
+
import "./chunk-FYCBGUTW.js";
|
|
6
|
+
import "./chunk-OCL7P7N7.js";
|
|
7
|
+
import "./chunk-MZO5QKUR.js";
|
|
8
|
+
import "./chunk-3RG5ZIWI.js";
|
|
9
|
+
|
|
10
|
+
// src/statuslineCmd.ts
|
|
11
|
+
async function collectStatuslineMetrics() {
|
|
12
|
+
const data = await collectAll();
|
|
13
|
+
const cpuUsage = data.cpu.usage;
|
|
14
|
+
const memUsage = data.memory.usagePercent;
|
|
15
|
+
const tempC = data.cpu.temperature;
|
|
16
|
+
const batteryPct = data.battery ? data.battery.percent : void 0;
|
|
17
|
+
const batteryCharging = data.battery ? data.battery.isCharging : void 0;
|
|
18
|
+
return {
|
|
19
|
+
cpuUsage,
|
|
20
|
+
memUsage,
|
|
21
|
+
tempC,
|
|
22
|
+
batteryPct,
|
|
23
|
+
batteryCharging
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function formatStatusline(m, format) {
|
|
27
|
+
const cpu = Math.round(m.cpuUsage);
|
|
28
|
+
const mem = Math.round(m.memUsage);
|
|
29
|
+
const temp = m.tempC !== void 0 ? `${Math.round(m.tempC)}\xB0C` : "";
|
|
30
|
+
const batt = m.batteryPct !== void 0 ? `${m.batteryCharging ? "\u26A1" : ""}${m.batteryPct}%` : "";
|
|
31
|
+
switch (format) {
|
|
32
|
+
case "json":
|
|
33
|
+
return JSON.stringify({
|
|
34
|
+
cpu: m.cpuUsage,
|
|
35
|
+
mem: m.memUsage,
|
|
36
|
+
temp: m.tempC,
|
|
37
|
+
battery: m.batteryPct,
|
|
38
|
+
charging: m.batteryCharging
|
|
39
|
+
});
|
|
40
|
+
case "tmux": {
|
|
41
|
+
const parts = [];
|
|
42
|
+
parts.push(`#[fg=cyan]CPU: ${cpu}%`);
|
|
43
|
+
parts.push(`#[fg=magenta]RAM: ${mem}%`);
|
|
44
|
+
if (temp) parts.push(`#[fg=yellow]${temp}`);
|
|
45
|
+
if (batt) parts.push(`#[fg=green]${batt}`);
|
|
46
|
+
return parts.join(" #[fg=dim]| ");
|
|
47
|
+
}
|
|
48
|
+
case "pango": {
|
|
49
|
+
const parts = [];
|
|
50
|
+
parts.push(`<span color="#22d3ee">CPU: ${cpu}%</span>`);
|
|
51
|
+
parts.push(`<span color="#a78bfa">RAM: ${mem}%</span>`);
|
|
52
|
+
if (temp) parts.push(`<span color="#fb923c">${temp}</span>`);
|
|
53
|
+
if (batt) parts.push(`<span color="#34d399">${batt}</span>`);
|
|
54
|
+
return parts.join(" | ");
|
|
55
|
+
}
|
|
56
|
+
case "starship":
|
|
57
|
+
case "plain":
|
|
58
|
+
default: {
|
|
59
|
+
const parts = [`\u{1F525} ${cpu}%`, `${mem}%`];
|
|
60
|
+
if (temp) parts.push(temp);
|
|
61
|
+
if (batt) parts.push(batt);
|
|
62
|
+
return parts.join(" | ");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function runStatuslineCommand(format = "plain") {
|
|
67
|
+
const metrics = await collectStatuslineMetrics();
|
|
68
|
+
const output = formatStatusline(metrics, format);
|
|
69
|
+
process.stdout.write(output + "\n");
|
|
70
|
+
}
|
|
71
|
+
export {
|
|
72
|
+
collectStatuslineMetrics,
|
|
73
|
+
formatStatusline,
|
|
74
|
+
runStatuslineCommand
|
|
75
|
+
};
|