pyre-cli 9.5.2 → 10.1.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
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart Battery Predictor.
|
|
3
|
+
*
|
|
4
|
+
* Predicts multi-scenario battery longevity by synthesizing:
|
|
5
|
+
* 1. Current instantaneous discharge rate and state of charge.
|
|
6
|
+
* 2. Historical discharge profiles parsed from local CSV log exports.
|
|
7
|
+
* 3. Machine load heuristics (idle vs. current compute vs. sustained high-power).
|
|
8
|
+
*/
|
|
9
|
+
import fs from 'node:fs';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import type { BatteryData, PowerData } from './types.js';
|
|
12
|
+
|
|
13
|
+
export interface BatteryPredictionScenario {
|
|
14
|
+
label: string;
|
|
15
|
+
ratePerHour: number;
|
|
16
|
+
timeRemaining: string;
|
|
17
|
+
hours: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface SmartBatteryPrediction {
|
|
21
|
+
currentLevel: number;
|
|
22
|
+
healthPercentage?: number;
|
|
23
|
+
cycleCount?: number;
|
|
24
|
+
powerWatts?: number;
|
|
25
|
+
scenarios: {
|
|
26
|
+
idle: BatteryPredictionScenario;
|
|
27
|
+
current: BatteryPredictionScenario;
|
|
28
|
+
heavy: BatteryPredictionScenario;
|
|
29
|
+
};
|
|
30
|
+
recommendation: string;
|
|
31
|
+
confidence: 'low' | 'medium' | 'high';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function formatHours(h: number): string {
|
|
35
|
+
if (!isFinite(h) || h <= 0) return 'calculating';
|
|
36
|
+
const hours = Math.floor(h);
|
|
37
|
+
const mins = Math.round((h - hours) * 60);
|
|
38
|
+
if (hours === 0 && mins === 0) return '< 1m';
|
|
39
|
+
if (hours === 0) return `${mins}m`;
|
|
40
|
+
return `${hours}h ${mins}m`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function computeSmartBatteryPrediction(
|
|
44
|
+
battery: BatteryData | null,
|
|
45
|
+
power: PowerData | null,
|
|
46
|
+
logDir: string = './pyre-exports'
|
|
47
|
+
): SmartBatteryPrediction | null {
|
|
48
|
+
if (!battery) return null;
|
|
49
|
+
|
|
50
|
+
const level = battery.level;
|
|
51
|
+
const isCharging = battery.state.toLowerCase().includes('charg');
|
|
52
|
+
|
|
53
|
+
// Baseline assumption for typical MacBook battery Wh capacity
|
|
54
|
+
const assumedCapacityWh = 58;
|
|
55
|
+
|
|
56
|
+
// 1. Current discharge rate in %/hour
|
|
57
|
+
let currentRate = battery.dischargeRatePerHour || 12; // default fallback 12%/h
|
|
58
|
+
if (battery.dischargeRatePerHour === undefined && power && power.combinedWatts) {
|
|
59
|
+
currentRate = (power.combinedWatts / assumedCapacityWh) * 100;
|
|
60
|
+
}
|
|
61
|
+
currentRate = Math.max(2, Math.min(60, currentRate));
|
|
62
|
+
|
|
63
|
+
// 2. Scan historical logs for typical average discharge rate if available
|
|
64
|
+
let historicalSamples: number[] = [];
|
|
65
|
+
try {
|
|
66
|
+
if (fs.existsSync(logDir)) {
|
|
67
|
+
const files = fs.readdirSync(logDir).filter(f => f.startsWith('pyre-') && f.endsWith('.csv')).slice(-3);
|
|
68
|
+
for (const file of files) {
|
|
69
|
+
const content = fs.readFileSync(path.join(logDir, file), 'utf-8');
|
|
70
|
+
const lines = content.split('\n').slice(1);
|
|
71
|
+
for (const line of lines) {
|
|
72
|
+
const parts = line.split(',');
|
|
73
|
+
if (parts.length >= 4) {
|
|
74
|
+
const cpu = parseFloat(parts[1]);
|
|
75
|
+
if (!isNaN(cpu)) {
|
|
76
|
+
// Estimate power based on CPU history
|
|
77
|
+
const estRate = 5 + (cpu / 100) * 20;
|
|
78
|
+
historicalSamples.push(estRate);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
// fallback
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const confidence: 'low' | 'medium' | 'high' = isCharging
|
|
89
|
+
? 'low'
|
|
90
|
+
: historicalSamples.length > 50
|
|
91
|
+
? 'high'
|
|
92
|
+
: battery.dischargeRatePerHour ? 'medium' : 'low';
|
|
93
|
+
|
|
94
|
+
// Compute scenarios
|
|
95
|
+
// Idle load scenario (light browser/reading, ~5-7% per hour)
|
|
96
|
+
const idleRate = 6.0;
|
|
97
|
+
const idleHours = level / idleRate;
|
|
98
|
+
|
|
99
|
+
// Current load scenario
|
|
100
|
+
const currentHours = level / currentRate;
|
|
101
|
+
|
|
102
|
+
// Heavy load scenario (compiling, 3D render, gaming: ~25-35% per hour)
|
|
103
|
+
const heavyRate = Math.max(28.0, currentRate * 1.5);
|
|
104
|
+
const heavyHours = level / heavyRate;
|
|
105
|
+
|
|
106
|
+
let recommendation = 'Power consumption is normal.';
|
|
107
|
+
if (isCharging) {
|
|
108
|
+
recommendation = `Device is currently ${battery.state}. Full capacity health: ${battery.health || 'Good'}.`;
|
|
109
|
+
} else if (currentRate > 25) {
|
|
110
|
+
recommendation = 'High power drain detected. Reducing display brightness and closing heavy background processes can extend battery significantly.';
|
|
111
|
+
} else if (currentRate < 10) {
|
|
112
|
+
recommendation = 'Excellent power efficiency. Machine is running under low-drain conditions.';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
currentLevel: level,
|
|
117
|
+
healthPercentage: battery.maxCapacityPercent,
|
|
118
|
+
cycleCount: battery.cycles,
|
|
119
|
+
powerWatts: battery.powerWatts || power?.combinedWatts,
|
|
120
|
+
scenarios: {
|
|
121
|
+
idle: {
|
|
122
|
+
label: 'Light / Reading Load',
|
|
123
|
+
ratePerHour: idleRate,
|
|
124
|
+
timeRemaining: formatHours(idleHours),
|
|
125
|
+
hours: idleHours,
|
|
126
|
+
},
|
|
127
|
+
current: {
|
|
128
|
+
label: 'Current Activity',
|
|
129
|
+
ratePerHour: Math.round(currentRate * 10) / 10,
|
|
130
|
+
timeRemaining: isCharging ? 'Charging' : formatHours(currentHours),
|
|
131
|
+
hours: currentHours,
|
|
132
|
+
},
|
|
133
|
+
heavy: {
|
|
134
|
+
label: 'Sustained Heavy Compute',
|
|
135
|
+
ratePerHour: Math.round(heavyRate * 10) / 10,
|
|
136
|
+
timeRemaining: formatHours(heavyHours),
|
|
137
|
+
hours: heavyHours,
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
recommendation,
|
|
141
|
+
confidence,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build System Tracker.
|
|
3
|
+
*
|
|
4
|
+
* Scans active processes for compiler invocations (xcodebuild, cargo, rustc,
|
|
5
|
+
* clang, swift-frontend, make, ninja, bazel, go build), tracking compiler
|
|
6
|
+
* parallelism, CPU/RAM usage, and estimated elapsed time.
|
|
7
|
+
*/
|
|
8
|
+
import { run } from './run.js';
|
|
9
|
+
|
|
10
|
+
export interface ActiveBuild {
|
|
11
|
+
tool: 'xcodebuild' | 'cargo' | 'swift' | 'make' | 'ninja' | 'bazel' | 'go' | 'clang' | 'compiler';
|
|
12
|
+
rootPid: number;
|
|
13
|
+
command: string;
|
|
14
|
+
compilerProcesses: number;
|
|
15
|
+
totalCpu: number;
|
|
16
|
+
totalMem: number;
|
|
17
|
+
elapsedSec: number;
|
|
18
|
+
target?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function collectBuilds(): Promise<ActiveBuild[]> {
|
|
22
|
+
try {
|
|
23
|
+
const raw = await run('ps -eo pid,ppid,pcpu,pmem,etime,command -r', '');
|
|
24
|
+
if (!raw) return [];
|
|
25
|
+
|
|
26
|
+
const lines = raw.split('\n').slice(1);
|
|
27
|
+
const builds: Map<string, ActiveBuild> = new Map();
|
|
28
|
+
|
|
29
|
+
const buildToolPatterns: { name: ActiveBuild['tool']; match: RegExp }[] = [
|
|
30
|
+
{ name: 'xcodebuild', match: /xcodebuild/i },
|
|
31
|
+
{ name: 'cargo', match: /\bcargo\s+(build|check|test|run)\b/i },
|
|
32
|
+
{ name: 'swift', match: /\bswift\s+(build|test)\b/i },
|
|
33
|
+
{ name: 'make', match: /\bmake\b/i },
|
|
34
|
+
{ name: 'ninja', match: /\bninja\b/i },
|
|
35
|
+
{ name: 'bazel', match: /\bbazel\b/i },
|
|
36
|
+
{ name: 'go', match: /\bgo\s+(build|test|run)\b/i },
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const compilerMatch = /(clang|swift-frontend|rustc|go tool compile|gcc|g\+\+|ghc)/i;
|
|
40
|
+
|
|
41
|
+
// First pass: identify build roots
|
|
42
|
+
for (const line of lines) {
|
|
43
|
+
if (!line.trim()) continue;
|
|
44
|
+
const parts = line.trim().split(/\s+/);
|
|
45
|
+
if (parts.length < 6) continue;
|
|
46
|
+
|
|
47
|
+
const pid = parseInt(parts[0], 10);
|
|
48
|
+
const cpu = parseFloat(parts[2]) || 0;
|
|
49
|
+
const mem = parseFloat(parts[3]) || 0;
|
|
50
|
+
const etimeStr = parts[4];
|
|
51
|
+
const cmd = parts.slice(5).join(' ');
|
|
52
|
+
|
|
53
|
+
for (const pattern of buildToolPatterns) {
|
|
54
|
+
if (pattern.match.test(cmd)) {
|
|
55
|
+
let elapsedSec = 0;
|
|
56
|
+
const etimeParts = etimeStr.split(':').map(Number);
|
|
57
|
+
if (etimeParts.length === 2) elapsedSec = etimeParts[0] * 60 + etimeParts[1];
|
|
58
|
+
else if (etimeParts.length === 3) elapsedSec = etimeParts[0] * 3600 + etimeParts[1] * 60 + etimeParts[2];
|
|
59
|
+
|
|
60
|
+
builds.set(pattern.name, {
|
|
61
|
+
tool: pattern.name,
|
|
62
|
+
rootPid: pid,
|
|
63
|
+
command: cmd.slice(0, 80),
|
|
64
|
+
compilerProcesses: 1,
|
|
65
|
+
totalCpu: cpu,
|
|
66
|
+
totalMem: mem,
|
|
67
|
+
elapsedSec,
|
|
68
|
+
target: cmd.split(' ').slice(1, 4).join(' '),
|
|
69
|
+
});
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Second pass: associate child compiler processes
|
|
76
|
+
for (const line of lines) {
|
|
77
|
+
if (!line.trim()) continue;
|
|
78
|
+
const parts = line.trim().split(/\s+/);
|
|
79
|
+
if (parts.length < 6) continue;
|
|
80
|
+
|
|
81
|
+
const cpu = parseFloat(parts[2]) || 0;
|
|
82
|
+
const mem = parseFloat(parts[3]) || 0;
|
|
83
|
+
const cmd = parts.slice(5).join(' ');
|
|
84
|
+
|
|
85
|
+
if (compilerMatch.test(cmd)) {
|
|
86
|
+
// If an active build was found, associate this compiler worker
|
|
87
|
+
if (builds.size > 0) {
|
|
88
|
+
const firstBuild = builds.values().next().value;
|
|
89
|
+
if (firstBuild) {
|
|
90
|
+
firstBuild.compilerProcesses += 1;
|
|
91
|
+
firstBuild.totalCpu += cpu;
|
|
92
|
+
firstBuild.totalMem += mem;
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
// Standalone compiler invocation
|
|
96
|
+
const key = 'compiler';
|
|
97
|
+
const existing = builds.get(key);
|
|
98
|
+
if (existing) {
|
|
99
|
+
existing.compilerProcesses += 1;
|
|
100
|
+
existing.totalCpu += cpu;
|
|
101
|
+
existing.totalMem += mem;
|
|
102
|
+
} else {
|
|
103
|
+
builds.set(key, {
|
|
104
|
+
tool: 'compiler',
|
|
105
|
+
rootPid: parseInt(parts[0], 10),
|
|
106
|
+
command: cmd.slice(0, 80),
|
|
107
|
+
compilerProcesses: 1,
|
|
108
|
+
totalCpu: cpu,
|
|
109
|
+
totalMem: mem,
|
|
110
|
+
elapsedSec: 0,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return Array.from(builds.values());
|
|
118
|
+
} catch {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
}
|