pyre-cli 9.5.0 → 9.5.2
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 +15 -2
- package/dist/blender-7UT5J7IP.js +7 -0
- package/dist/{checkCmd-ULNENOPZ.js → checkCmd-T7O4KXXA.js} +4 -3
- package/dist/chunk-25I533OF.js +133 -0
- package/dist/{chunk-MOAEGMFP.js → chunk-3TE7MJLS.js} +168 -36
- package/dist/{chunk-RVGKEJXT.js → chunk-CXL4GYGQ.js} +3 -0
- package/dist/{chunk-PV4MXTAJ.js → chunk-N4WJVF5M.js} +289 -23
- package/dist/{collectors-VYUH3UWY.js → collectors-JB4OGUEF.js} +4 -1
- package/dist/{config-4ZLF4HTS.js → config-KS5ZZ3VN.js} +1 -1
- package/dist/{doctor-L6FZYA7F.js → doctor-UV7TWIHX.js} +1 -1
- package/dist/index.js +419 -156
- package/dist/{pipeCmd-BWNMUV2X.js → pipeCmd-TYLJHQ5B.js} +3 -2
- package/dist/{wizard-GVIAHIVV.js → wizard-Z3RGA3VB.js} +1 -1
- package/package.json +1 -1
- package/src/anomalies.ts +114 -60
- package/src/formatters/index.ts +1 -1
- package/src/formatters/output.ts +100 -13
- package/src/formatters/render.ts +240 -28
- package/src/formatters/themes.ts +8 -0
- package/src/formatters/types.ts +4 -1
- package/src/index.ts +53 -26
- package/src/live/input.ts +62 -4
- package/src/live/render.ts +31 -0
- package/src/live/state.ts +65 -52
- package/src/live/types.ts +2 -2
- package/src/monitors/blender.ts +144 -0
- package/src/monitors/collectors.ts +287 -40
- package/src/monitors/dns_cache.ts +48 -0
- package/src/monitors/index.ts +3 -2
- package/src/monitors/smc.ts +27 -0
- package/src/monitors/types.ts +97 -14
- package/src/state/config.ts +6 -0
- /package/dist/{chunk-SG26Y3WF.js → chunk-IM3IQMMT.js} +0 -0
package/src/live/state.ts
CHANGED
|
@@ -23,6 +23,8 @@ export const MENU_OPTIONS = ['Resume Dashboard', 'Settings (Customizer)', 'Readm
|
|
|
23
23
|
|
|
24
24
|
const config = readConfig();
|
|
25
25
|
|
|
26
|
+
const isDockerEnv = fs.existsSync('/.dockerenv') || fs.existsSync('/run/.containerenv');
|
|
27
|
+
|
|
26
28
|
const state = {
|
|
27
29
|
intervalHandle: null as NodeJS.Timeout | null,
|
|
28
30
|
uiIntervalHandle: null as NodeJS.Timeout | null,
|
|
@@ -37,19 +39,21 @@ const state = {
|
|
|
37
39
|
|
|
38
40
|
exportDir: config.exportDir,
|
|
39
41
|
currentTheme: config.theme as ThemeName,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
visiblePanels: {
|
|
43
|
+
cpu: config.visiblePanels.cpu,
|
|
44
|
+
mem: config.visiblePanels.mem,
|
|
45
|
+
gpu: config.visiblePanels.gpu,
|
|
46
|
+
power: config.visiblePanels.power,
|
|
47
|
+
battery: config.visiblePanels.battery,
|
|
48
|
+
thermal: config.visiblePanels.thermal,
|
|
49
|
+
network: config.visiblePanels.network,
|
|
50
|
+
packets: config.visiblePanels.packets,
|
|
51
|
+
tasks: config.visiblePanels.tasks,
|
|
52
|
+
disk: config.visiblePanels.disk,
|
|
53
|
+
process: config.visiblePanels.process,
|
|
54
|
+
containers: config.visiblePanels.containers,
|
|
55
|
+
blender: config.visiblePanels.blender,
|
|
56
|
+
} as VisibleItems,
|
|
53
57
|
panelLayout: config.panelLayout || ['mem', 'disk', 'net'],
|
|
54
58
|
logging: false,
|
|
55
59
|
logStream: null as fs.WriteStream | null,
|
|
@@ -66,34 +70,36 @@ const state = {
|
|
|
66
70
|
processScrollOffset: 0,
|
|
67
71
|
trackedPid: null as number | null,
|
|
68
72
|
inspectingProcess: null as any | null,
|
|
69
|
-
inputMode: null as InputMode,
|
|
73
|
+
inputMode: (isDockerEnv && !config.dockerModeConfirmed) ? 'docker-alert' as InputMode : null as InputMode,
|
|
70
74
|
inputBuffer: '',
|
|
71
75
|
targetPid: '',
|
|
72
76
|
selectedSignal: 'SIGTERM' as typeof SIGNAL_OPTIONS[number],
|
|
73
77
|
treeView: config.treeView,
|
|
74
78
|
SIGNAL_OPTIONS,
|
|
75
79
|
customizerIndex: 0,
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
80
|
+
CUSTOMIZER_OPTIONS: [
|
|
81
|
+
'Theme',
|
|
82
|
+
'Graph Mode',
|
|
83
|
+
'Splash Screen',
|
|
84
|
+
'Splash Color',
|
|
85
|
+
'Splash Animation',
|
|
86
|
+
'Notifications',
|
|
87
|
+
'Temperature Unit',
|
|
88
|
+
'Toggle CPU',
|
|
89
|
+
'Toggle Memory',
|
|
90
|
+
'Toggle GPU',
|
|
91
|
+
'Toggle Power',
|
|
92
|
+
'Toggle Battery',
|
|
93
|
+
'Toggle Thermal',
|
|
94
|
+
'Toggle Network',
|
|
95
|
+
'Toggle Conns',
|
|
96
|
+
'Toggle Tasks',
|
|
97
|
+
'Toggle Disk',
|
|
98
|
+
'Toggle Processes',
|
|
99
|
+
'Toggle Containers',
|
|
100
|
+
'Toggle Blender',
|
|
101
|
+
'Toggle Tree View',
|
|
102
|
+
],
|
|
97
103
|
CPU_ALERT_PCT: config.cpuAlertPct,
|
|
98
104
|
TEMP_ALERT_C: config.tempAlertC,
|
|
99
105
|
watchdogProcess: config.watchdogProcess || '',
|
|
@@ -103,23 +109,25 @@ const state = {
|
|
|
103
109
|
webhookUrl: config.webhookUrl || '',
|
|
104
110
|
alertCmd: config.alertCmd || '',
|
|
105
111
|
alerted: false,
|
|
106
|
-
|
|
112
|
+
activePanel: 'grid' as 'grid' | 'cpu' | 'mem' | 'gpu' | 'power' | 'battery' | 'thermal' | 'network' | 'packets' | 'tasks' | 'disk' | 'process' | 'p2p' | 'anomalies' | 'containers' | 'blender',
|
|
107
113
|
mouseEnabled: config.mouseEnabled,
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
114
|
+
PANEL_TABS: [
|
|
115
|
+
{ id: 'cpu', label: 'CPU', key: '1' },
|
|
116
|
+
{ id: 'mem', label: 'Memory', key: '2' },
|
|
117
|
+
{ id: 'gpu', label: 'GPU', key: '3' },
|
|
118
|
+
{ id: 'power', label: 'Power', key: '4' },
|
|
119
|
+
{ id: 'battery', label: 'Battery', key: '5' },
|
|
120
|
+
{ id: 'thermal', label: 'Thermal', key: '6' },
|
|
121
|
+
{ id: 'network', label: 'Network', key: '7' },
|
|
122
|
+
{ id: 'packets', label: 'Conns', key: '8' },
|
|
123
|
+
{ id: 'tasks', label: 'Tasks', key: '9' },
|
|
124
|
+
{ id: 'disk', label: 'Disk', key: '0' },
|
|
125
|
+
{ id: 'process', label: 'Process', key: 'P' },
|
|
126
|
+
{ id: 'containers', label: 'Containers', key: 'C' },
|
|
127
|
+
{ id: 'p2p', label: 'P2P', key: 'R' },
|
|
128
|
+
{ id: 'anomalies', label: 'Anomalies', key: 'A' },
|
|
129
|
+
{ id: 'blender', label: 'Blender', key: 'B' },
|
|
130
|
+
],
|
|
123
131
|
p2pServer: null as import('../p2p/server.js').P2PServer | null,
|
|
124
132
|
p2pClient: null as import('../p2p/client.js').P2PClient | null,
|
|
125
133
|
p2pServerRunning: false,
|
|
@@ -134,6 +142,9 @@ const state = {
|
|
|
134
142
|
splashAnimation: config.splashAnimation as SplashAnimation,
|
|
135
143
|
menuSelectionIndex: 0,
|
|
136
144
|
readmeScrollOffset: 0,
|
|
145
|
+
isDocker: isDockerEnv,
|
|
146
|
+
dockerModeConfirmed: config.dockerModeConfirmed || false,
|
|
147
|
+
dockerSelectionIndex: 0,
|
|
137
148
|
};
|
|
138
149
|
|
|
139
150
|
function setStatus(msg: string, ms = 3000) {
|
|
@@ -153,10 +164,12 @@ function getToggleKey(opt: string): keyof VisibleItems | null {
|
|
|
153
164
|
'Toggle Battery': 'battery',
|
|
154
165
|
'Toggle Thermal': 'thermal',
|
|
155
166
|
'Toggle Network': 'network',
|
|
156
|
-
'Toggle
|
|
167
|
+
'Toggle Conns': 'packets',
|
|
157
168
|
'Toggle Tasks': 'tasks',
|
|
158
169
|
'Toggle Disk': 'disk',
|
|
159
170
|
'Toggle Processes': 'process',
|
|
171
|
+
'Toggle Containers': 'containers',
|
|
172
|
+
'Toggle Blender': 'blender',
|
|
160
173
|
'Toggle Tree View': 'tree',
|
|
161
174
|
};
|
|
162
175
|
return map[opt] ?? null;
|
package/src/live/types.ts
CHANGED
|
@@ -20,13 +20,13 @@ export interface LiveOptions {
|
|
|
20
20
|
|
|
21
21
|
export type ExportFormat = 'json' | 'csv' | 'tsv' | 'html' | 'md';
|
|
22
22
|
|
|
23
|
-
type InputMode = null | 'filter' | 'kill' | 'signal' | 'customizer' | 'p2p' | 'menu' | 'readme' | 'credits';
|
|
23
|
+
type InputMode = null | 'filter' | 'kill' | 'signal' | 'customizer' | 'p2p' | 'menu' | 'readme' | 'credits' | 'docker-alert';
|
|
24
24
|
|
|
25
25
|
type SortMode = 'cpu' | 'mem' | 'pid' | 'user' | 'command' | 'state' | 'threads' | 'runtime';
|
|
26
26
|
|
|
27
27
|
type GraphMode = 'spark' | 'bar';
|
|
28
28
|
|
|
29
|
-
type ActivePanel = 'grid' | 'cpu' | 'mem' | 'gpu' | 'power' | 'battery' | 'thermal' | 'network' | 'packets' | 'tasks' | 'disk' | 'process' | 'p2p';
|
|
29
|
+
type ActivePanel = 'grid' | 'cpu' | 'mem' | 'gpu' | 'power' | 'battery' | 'thermal' | 'network' | 'packets' | 'tasks' | 'disk' | 'process' | 'p2p' | 'containers' | 'anomalies' | 'blender';
|
|
30
30
|
|
|
31
31
|
export type { InputMode, SortMode, GraphMode, ActivePanel, ThemeName };
|
|
32
32
|
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { run } from './run.js';
|
|
2
|
+
import type { BlenderRenderData } from './types.js';
|
|
3
|
+
|
|
4
|
+
const RENDER_LOG_DIR = '/tmp/blender_render_logs';
|
|
5
|
+
|
|
6
|
+
function blendFileFromArgs(args: string[]): string {
|
|
7
|
+
const nonFlag = args.filter(a => !a.startsWith('-'));
|
|
8
|
+
for (const a of nonFlag) {
|
|
9
|
+
if (a.endsWith('.blend') || a.includes('.blend')) {
|
|
10
|
+
return a.split('/').pop() || a;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return 'unknown.blend';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function engineFromArgs(args: string[]): string {
|
|
17
|
+
const idx = args.indexOf('-E');
|
|
18
|
+
if (idx !== -1 && args[idx + 1]) return args[idx + 1];
|
|
19
|
+
const flag = args.find(a => a.startsWith('-E='));
|
|
20
|
+
if (flag) return flag.slice(3);
|
|
21
|
+
return 'unknown';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function outputPathFromArgs(args: string[]): string {
|
|
25
|
+
const oIdx = args.indexOf('-o');
|
|
26
|
+
if (oIdx !== -1 && args[oIdx + 1]) {
|
|
27
|
+
let p = args[oIdx + 1];
|
|
28
|
+
if (p.endsWith('/')) p = p.slice(0, -1);
|
|
29
|
+
return p.split('/').slice(0, -1).join('/') || '/tmp';
|
|
30
|
+
}
|
|
31
|
+
const fFlag = args.find(a => a.startsWith('-o='));
|
|
32
|
+
if (fFlag) {
|
|
33
|
+
const p = fFlag.slice(3);
|
|
34
|
+
return p.endsWith('/') ? p.slice(0, -1).split('/').slice(0, -1).join('/') || '/tmp' : p.split('/').slice(0, -1).join('/') || '/tmp';
|
|
35
|
+
}
|
|
36
|
+
return '/tmp';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function frameRangeFromArgs(args: string[]): { start: number; end: number } {
|
|
40
|
+
const fIdx = args.indexOf('-f');
|
|
41
|
+
if (fIdx !== -1 && args[fIdx + 1]) {
|
|
42
|
+
const raw = args[fIdx + 1];
|
|
43
|
+
if (raw.includes('..')) {
|
|
44
|
+
const [s, e] = raw.split('..').map(Number);
|
|
45
|
+
if (!isNaN(s) && !isNaN(e)) return { start: s, end: e };
|
|
46
|
+
}
|
|
47
|
+
const n = parseInt(raw, 10);
|
|
48
|
+
if (!isNaN(n)) return { start: n, end: n };
|
|
49
|
+
}
|
|
50
|
+
const sFlag = args.find(a => a.startsWith('-s='));
|
|
51
|
+
const eFlag = args.find(a => a.startsWith('-e='));
|
|
52
|
+
const start = sFlag ? parseInt(sFlag.slice(3), 10) : 1;
|
|
53
|
+
const end = eFlag ? parseInt(eFlag.slice(3), 10) : 250;
|
|
54
|
+
return { start: isNaN(start) ? 1 : start, end: isNaN(end) ? 250 : end };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function parseRenderLog(blendName: string): Promise<{ framesRendered: number; totalFrames: number; startTime: number; samples?: number } | null> {
|
|
58
|
+
const baseName = blendName.replace(/\.blend$/i, '');
|
|
59
|
+
try {
|
|
60
|
+
const entries = (await run(`ls -t "${RENDER_LOG_DIR}" 2>/dev/null | grep "^${baseName}_" | head -5`, '')).trim().split('\n').filter(Boolean);
|
|
61
|
+
for (const entry of entries) {
|
|
62
|
+
const logPath = `${RENDER_LOG_DIR}/${entry}`;
|
|
63
|
+
try {
|
|
64
|
+
const content = await run(`cat "${logPath}" 2>/dev/null`, '', 500);
|
|
65
|
+
if (!content) continue;
|
|
66
|
+
const lines = content.split('\n');
|
|
67
|
+
const rendered = lines.filter(l => /Fra:(\d+)/i.test(l)).length;
|
|
68
|
+
const totalMatch = content.match(/Fra:\s*\d+\s*\/\s*(\d+)/);
|
|
69
|
+
const totalFrames = totalMatch ? parseInt(totalMatch[1], 10) : 0;
|
|
70
|
+
const startMatch = content.match(/^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})/m);
|
|
71
|
+
const startTime = startMatch ? new Date(startMatch[1]).getTime() : Date.now() - 60000;
|
|
72
|
+
const sampleMatch = content.match(/Sample\s*(\d+)/i);
|
|
73
|
+
if (rendered > 0) {
|
|
74
|
+
return { framesRendered: rendered, totalFrames: totalFrames || rendered, startTime, samples: sampleMatch ? parseInt(sampleMatch[1], 10) : undefined };
|
|
75
|
+
}
|
|
76
|
+
} catch {
|
|
77
|
+
// try next log file
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
} catch {
|
|
81
|
+
// ignore
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function collectBlenderRenders(): Promise<BlenderRenderData[]> {
|
|
87
|
+
const renders: BlenderRenderData[] = [];
|
|
88
|
+
try {
|
|
89
|
+
const psRaw = await run("ps -eo pid,pcpu,pmem,state,command 2>/dev/null | grep -iE '[b]lender'", '', 2000);
|
|
90
|
+
if (!psRaw) return renders;
|
|
91
|
+
|
|
92
|
+
const lines = psRaw.split('\n');
|
|
93
|
+
for (const line of lines) {
|
|
94
|
+
const parts = line.trim().match(/\s*(\d+)\s+([\d.]+)\s+([\d.]+)\s+(\S+)\s+(.+)/);
|
|
95
|
+
if (!parts) continue;
|
|
96
|
+
|
|
97
|
+
const pid = parseInt(parts[1], 10);
|
|
98
|
+
const cpuPct = parseFloat(parts[2]);
|
|
99
|
+
const command = parts[5];
|
|
100
|
+
const args = command.split(/\s+/);
|
|
101
|
+
|
|
102
|
+
const isRender = /-b|--background|-f\s/.test(command) || /render/i.test(command);
|
|
103
|
+
if (!isRender && !command.includes('blender')) continue;
|
|
104
|
+
|
|
105
|
+
const blendFile = blendFileFromArgs(args);
|
|
106
|
+
const engine = engineFromArgs(args);
|
|
107
|
+
const outputPath = outputPathFromArgs(args);
|
|
108
|
+
const { start: frameStart, end: frameEnd } = frameRangeFromArgs(args);
|
|
109
|
+
const totalFrames = Math.max(1, frameEnd - frameStart + 1);
|
|
110
|
+
|
|
111
|
+
const logData = await parseRenderLog(blendFile);
|
|
112
|
+
const startTime = logData?.startTime ?? Date.now() - 60000;
|
|
113
|
+
const elapsedSec = Math.max(0, Math.round((Date.now() - startTime) / 1000));
|
|
114
|
+
let currentFrame = logData?.framesRendered ?? 0;
|
|
115
|
+
if (currentFrame === 0 && cpuPct > 0) currentFrame = 1;
|
|
116
|
+
|
|
117
|
+
let status: BlenderRenderData['status'] = 'unknown';
|
|
118
|
+
if (cpuPct > 0 && currentFrame > 0 && currentFrame < totalFrames) status = 'rendering';
|
|
119
|
+
else if (cpuPct > 0 && currentFrame >= totalFrames) status = 'finalizing';
|
|
120
|
+
else if (currentFrame === 0 && cpuPct > 0) status = 'loading';
|
|
121
|
+
else if (cpuPct === 0) status = 'idle';
|
|
122
|
+
|
|
123
|
+
const completionPercent = totalFrames > 0 ? Math.min(100, Math.round((currentFrame / totalFrames) * 100)) : 0;
|
|
124
|
+
|
|
125
|
+
renders.push({
|
|
126
|
+
pid,
|
|
127
|
+
blendFile,
|
|
128
|
+
renderEngine: engine,
|
|
129
|
+
frameStart,
|
|
130
|
+
frameEnd,
|
|
131
|
+
currentFrame,
|
|
132
|
+
totalFrames,
|
|
133
|
+
elapsedSec,
|
|
134
|
+
completionPercent,
|
|
135
|
+
status,
|
|
136
|
+
outputPath,
|
|
137
|
+
sampleCount: logData?.samples,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
} catch {
|
|
141
|
+
// ignore
|
|
142
|
+
}
|
|
143
|
+
return renders;
|
|
144
|
+
}
|