systemlens 1.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/.env.example +5 -0
- package/LICENSE +21 -0
- package/README.md +160 -0
- package/bin/systemlens.js +127 -0
- package/package.json +73 -0
- package/server.js +106 -0
- package/src/analyzers/cpu.analyzer.js +124 -0
- package/src/analyzers/memory.analyzer.js +107 -0
- package/src/analyzers/process.analyzer.js +136 -0
- package/src/analyzers/spike.detector.js +135 -0
- package/src/classifiers/process.classifier.js +127 -0
- package/src/collectors/cpu.collector.js +48 -0
- package/src/collectors/disk.collector.js +41 -0
- package/src/collectors/memory.collector.js +41 -0
- package/src/collectors/process.collector.js +65 -0
- package/src/engines/ai.engine.js +105 -0
- package/src/engines/explanation.engine.js +348 -0
- package/src/engines/suggestion.engine.js +242 -0
- package/src/history/history.tracker.js +114 -0
- package/src/index.js +130 -0
- package/src/monitor/realtime.monitor.js +145 -0
- package/src/renderer/cli.renderer.js +318 -0
- package/src/utils/constants.js +80 -0
- package/src/utils/helpers.js +110 -0
- package/web/app.js +352 -0
- package/web/index.html +209 -0
- package/web/style.css +886 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// ─── Process Data Collector ──────────────────────────────────────
|
|
2
|
+
import si from 'systeminformation';
|
|
3
|
+
import pidusage from 'pidusage';
|
|
4
|
+
import { TOP_PROCESSES_COUNT } from '../utils/constants.js';
|
|
5
|
+
import { formatBytes } from '../utils/helpers.js';
|
|
6
|
+
|
|
7
|
+
export class ProcessCollector {
|
|
8
|
+
/**
|
|
9
|
+
* Collect top processes by CPU and memory usage
|
|
10
|
+
* @returns {Object} Process list sorted by resource usage
|
|
11
|
+
*/
|
|
12
|
+
async collect() {
|
|
13
|
+
const processes = await si.processes();
|
|
14
|
+
|
|
15
|
+
const allProcs = processes.list || [];
|
|
16
|
+
|
|
17
|
+
// Sort by CPU usage descending
|
|
18
|
+
const byCpu = [...allProcs]
|
|
19
|
+
.sort((a, b) => (b.cpu || 0) - (a.cpu || 0))
|
|
20
|
+
.slice(0, TOP_PROCESSES_COUNT * 2); // Get extra for deduplication
|
|
21
|
+
|
|
22
|
+
// Sort by memory usage descending
|
|
23
|
+
const byMem = [...allProcs]
|
|
24
|
+
.sort((a, b) => (b.mem || 0) - (a.mem || 0))
|
|
25
|
+
.slice(0, TOP_PROCESSES_COUNT * 2);
|
|
26
|
+
|
|
27
|
+
// Merge and deduplicate
|
|
28
|
+
const seenPids = new Set();
|
|
29
|
+
const topProcesses = [];
|
|
30
|
+
|
|
31
|
+
for (const proc of [...byCpu, ...byMem]) {
|
|
32
|
+
if (seenPids.has(proc.pid)) continue;
|
|
33
|
+
seenPids.add(proc.pid);
|
|
34
|
+
|
|
35
|
+
topProcesses.push({
|
|
36
|
+
pid: proc.pid,
|
|
37
|
+
parentPid: proc.parentPid,
|
|
38
|
+
name: proc.name || 'unknown',
|
|
39
|
+
command: proc.command || '',
|
|
40
|
+
cpu: parseFloat((proc.cpu || 0).toFixed(1)),
|
|
41
|
+
mem: parseFloat((proc.mem || 0).toFixed(1)),
|
|
42
|
+
memRss: proc.memRss ? proc.memRss * 1024 : 0, // Convert KB to bytes
|
|
43
|
+
memRssFormatted: proc.memRss ? formatBytes(proc.memRss * 1024) : '0 B',
|
|
44
|
+
state: proc.state || 'unknown',
|
|
45
|
+
started: proc.started || '',
|
|
46
|
+
user: proc.user || 'unknown',
|
|
47
|
+
path: proc.path || '',
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Re-sort the merged list by CPU
|
|
52
|
+
topProcesses.sort((a, b) => b.cpu - a.cpu);
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
total: processes.all || 0,
|
|
56
|
+
running: processes.running || 0,
|
|
57
|
+
sleeping: processes.sleeping || 0,
|
|
58
|
+
blocked: processes.blocked || 0,
|
|
59
|
+
topByCpu: topProcesses.slice(0, TOP_PROCESSES_COUNT),
|
|
60
|
+
topByMemory: [...topProcesses].sort((a, b) => b.mem - a.mem).slice(0, TOP_PROCESSES_COUNT),
|
|
61
|
+
all: topProcesses,
|
|
62
|
+
timestamp: Date.now(),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// ─── AI Explanation Engine ───────────────────────────────────────
|
|
2
|
+
// Enhances rule-based explanations with AI-powered insights
|
|
3
|
+
import OpenAI from 'openai';
|
|
4
|
+
import dotenv from 'dotenv';
|
|
5
|
+
|
|
6
|
+
dotenv.config();
|
|
7
|
+
|
|
8
|
+
export class AIEngine {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.enabled = !!process.env.OPENAI_API_KEY;
|
|
11
|
+
this.client = this.enabled ? new OpenAI({ apiKey: process.env.OPENAI_API_KEY }) : null;
|
|
12
|
+
this.lastAICall = 0;
|
|
13
|
+
this.cooldownMs = 15000; // Rate limit: max 1 AI call per 15 seconds
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Check if AI engine is available
|
|
18
|
+
*/
|
|
19
|
+
isAvailable() {
|
|
20
|
+
return this.enabled && this.client !== null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Generate AI-enhanced explanation
|
|
25
|
+
* @param {Object} analysis - Full system analysis
|
|
26
|
+
* @param {Object} explanation - Rule-based explanation
|
|
27
|
+
* @returns {Object|null} AI insights or null if unavailable/cooldown
|
|
28
|
+
*/
|
|
29
|
+
async enhance(analysis, explanation) {
|
|
30
|
+
if (!this.isAvailable()) return null;
|
|
31
|
+
|
|
32
|
+
// Enforce cooldown to avoid excessive API calls
|
|
33
|
+
const now = Date.now();
|
|
34
|
+
if (now - this.lastAICall < this.cooldownMs) return null;
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
this.lastAICall = now;
|
|
38
|
+
|
|
39
|
+
const prompt = this._buildPrompt(analysis, explanation);
|
|
40
|
+
|
|
41
|
+
const response = await this.client.chat.completions.create({
|
|
42
|
+
model: 'gpt-4o-mini',
|
|
43
|
+
messages: [
|
|
44
|
+
{
|
|
45
|
+
role: 'system',
|
|
46
|
+
content: `You are an expert systems engineer who explains computer behavior to users in simple, clear language. You are SystemLens — a tool that helps people understand why their computer is behaving the way it is. Be concise but insightful. Focus on "why" not just "what". Use analogies when helpful. Never use jargon without explaining it. Your response must be in JSON format with keys: "deepInsight" (a deeper explanation), "prediction" (what might happen next), "proTip" (a power-user tip).`,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
role: 'user',
|
|
50
|
+
content: prompt,
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
max_tokens: 400,
|
|
54
|
+
temperature: 0.7,
|
|
55
|
+
response_format: { type: 'json_object' },
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const content = response.choices[0]?.message?.content;
|
|
59
|
+
if (!content) return null;
|
|
60
|
+
|
|
61
|
+
const parsed = JSON.parse(content);
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
deepInsight: parsed.deepInsight || null,
|
|
65
|
+
prediction: parsed.prediction || null,
|
|
66
|
+
proTip: parsed.proTip || null,
|
|
67
|
+
source: 'ai',
|
|
68
|
+
model: 'gpt-4o-mini',
|
|
69
|
+
};
|
|
70
|
+
} catch (error) {
|
|
71
|
+
// Silently fail — AI is enhancement, not critical
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Build a structured prompt for the AI
|
|
78
|
+
*/
|
|
79
|
+
_buildPrompt(analysis, explanation) {
|
|
80
|
+
const { cpu, memory, process: pa } = analysis;
|
|
81
|
+
|
|
82
|
+
const data = {
|
|
83
|
+
cpuUsage: `${cpu.overall}%`,
|
|
84
|
+
cpuSeverity: cpu.severity.label,
|
|
85
|
+
memoryUsage: `${memory.usedPercent}%`,
|
|
86
|
+
memorySeverity: memory.severity.label,
|
|
87
|
+
totalProcesses: pa.summary.totalProcesses,
|
|
88
|
+
systemHealth: explanation.health.status,
|
|
89
|
+
headline: explanation.headline,
|
|
90
|
+
topIssues: [...cpu.issues, ...memory.issues, ...pa.issues].map(i => i.message).slice(0, 5),
|
|
91
|
+
topProcesses: analysis.rawProcesses?.topByCpu?.slice(0, 5).map(p => ({
|
|
92
|
+
name: p.name,
|
|
93
|
+
cpu: `${p.cpu}%`,
|
|
94
|
+
mem: `${p.mem}%`,
|
|
95
|
+
category: p.classification?.label || 'Unknown',
|
|
96
|
+
})) || [],
|
|
97
|
+
patterns: analysis.patterns ? {
|
|
98
|
+
cpuTrend: analysis.patterns.cpu?.trend,
|
|
99
|
+
memTrend: analysis.patterns.memory?.trend,
|
|
100
|
+
} : null,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
return `Here is the current system state:\n${JSON.stringify(data, null, 2)}\n\nProvide a deeper insight, prediction, and pro tip in JSON format.`;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
// ─── Explanation Engine ──────────────────────────────────────────
|
|
2
|
+
// The CORE DIFFERENTIATOR: converts analysis into human-readable explanations
|
|
3
|
+
// Thinks like a senior engineer explaining to a beginner
|
|
4
|
+
|
|
5
|
+
export class ExplanationEngine {
|
|
6
|
+
/**
|
|
7
|
+
* Generate a full system explanation from all analysis results
|
|
8
|
+
* @param {Object} analysis - Combined analysis results
|
|
9
|
+
* @returns {Object} Human-readable explanation
|
|
10
|
+
*/
|
|
11
|
+
explain(analysis) {
|
|
12
|
+
const { cpu, memory, process: processAnalysis, patterns, devEnvironment } = analysis;
|
|
13
|
+
|
|
14
|
+
const systemHealth = this._assessOverallHealth(cpu, memory);
|
|
15
|
+
const mainExplanation = this._buildMainExplanation(cpu, memory, processAnalysis);
|
|
16
|
+
const causeEffect = this._buildCauseEffect(cpu, memory, processAnalysis);
|
|
17
|
+
const devInsights = this._buildDevInsights(devEnvironment, processAnalysis);
|
|
18
|
+
const patternNarrative = this._buildPatternNarrative(patterns);
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
health: systemHealth,
|
|
22
|
+
headline: this._generateHeadline(cpu, memory, processAnalysis),
|
|
23
|
+
explanation: mainExplanation,
|
|
24
|
+
causeAndEffect: causeEffect,
|
|
25
|
+
devInsights,
|
|
26
|
+
patternNarrative,
|
|
27
|
+
timestamp: Date.now(),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Assess overall system health in human terms
|
|
33
|
+
*/
|
|
34
|
+
_assessOverallHealth(cpu, memory) {
|
|
35
|
+
const cpuLoad = cpu.overall;
|
|
36
|
+
const memUsed = memory.usedPercent;
|
|
37
|
+
|
|
38
|
+
// Combined health score (weighted: CPU 60%, Memory 40%)
|
|
39
|
+
const healthScore = (cpuLoad * 0.6) + (memUsed * 0.4);
|
|
40
|
+
|
|
41
|
+
if (healthScore < 30) {
|
|
42
|
+
return {
|
|
43
|
+
status: 'excellent',
|
|
44
|
+
emoji: '🟢',
|
|
45
|
+
label: 'System Healthy',
|
|
46
|
+
description: 'Your system is running smoothly. All resources are well within normal limits.',
|
|
47
|
+
};
|
|
48
|
+
} else if (healthScore < 55) {
|
|
49
|
+
return {
|
|
50
|
+
status: 'good',
|
|
51
|
+
emoji: '🟡',
|
|
52
|
+
label: 'Lightly Loaded',
|
|
53
|
+
description: 'Your system has some load but is handling it fine. No action needed.',
|
|
54
|
+
};
|
|
55
|
+
} else if (healthScore < 75) {
|
|
56
|
+
return {
|
|
57
|
+
status: 'stressed',
|
|
58
|
+
emoji: '🟠',
|
|
59
|
+
label: 'Under Pressure',
|
|
60
|
+
description: 'Your system is working hard. You may notice some slowness. Consider closing unnecessary apps.',
|
|
61
|
+
};
|
|
62
|
+
} else {
|
|
63
|
+
return {
|
|
64
|
+
status: 'critical',
|
|
65
|
+
emoji: '🔴',
|
|
66
|
+
label: 'System Struggling',
|
|
67
|
+
description: 'Your system is under heavy load. Performance is likely degraded. Immediate action recommended.',
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Generate a concise headline for the current state
|
|
74
|
+
*/
|
|
75
|
+
_generateHeadline(cpu, memory, processAnalysis) {
|
|
76
|
+
const cpuLoad = cpu.overall;
|
|
77
|
+
const memUsed = memory.usedPercent;
|
|
78
|
+
const issues = [...cpu.issues, ...memory.issues, ...processAnalysis.issues];
|
|
79
|
+
|
|
80
|
+
if (issues.length === 0) {
|
|
81
|
+
return '✨ Everything looks great — your system is running efficiently.';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Single dominant process
|
|
85
|
+
if (processAnalysis.primaryCause === 'single_process' && processAnalysis.responsibilities.length > 0) {
|
|
86
|
+
const resp = processAnalysis.responsibilities[0];
|
|
87
|
+
return `⚠️ ${resp.process.name} is dominating your system resources (${resp.process.cpu}% CPU).`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Multiple hogs
|
|
91
|
+
if (processAnalysis.primaryCause === 'multiple_processes') {
|
|
92
|
+
return `⚠️ Several processes are competing for resources, causing system strain.`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Distributed load
|
|
96
|
+
if (processAnalysis.primaryCause === 'distributed') {
|
|
97
|
+
return `⚠️ Many background processes are collectively putting pressure on your system.`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// CPU-specific
|
|
101
|
+
if (cpuLoad >= 80) {
|
|
102
|
+
return `🔴 CPU usage is critically high at ${cpuLoad}% — your system is likely slow right now.`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Memory-specific
|
|
106
|
+
if (memUsed >= 80) {
|
|
107
|
+
return `🔴 Memory is nearly full at ${memUsed}% — your system may start swapping and freezing.`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (cpuLoad >= 60 || memUsed >= 60) {
|
|
111
|
+
return `🟡 System is under moderate load — CPU at ${cpuLoad}%, Memory at ${memUsed}%.`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return `ℹ️ Minor issues detected but system is generally stable.`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Build the main explanation paragraph
|
|
119
|
+
*/
|
|
120
|
+
_buildMainExplanation(cpu, memory, processAnalysis) {
|
|
121
|
+
const paragraphs = [];
|
|
122
|
+
const cpuLoad = cpu.overall;
|
|
123
|
+
const memUsed = memory.usedPercent;
|
|
124
|
+
|
|
125
|
+
// ── Opening context ──
|
|
126
|
+
if (cpuLoad >= 80 && memUsed >= 80) {
|
|
127
|
+
paragraphs.push(
|
|
128
|
+
`Your system is under heavy pressure on both CPU and memory. CPU usage is at ${cpuLoad}% and memory is at ${memUsed}%. This combination means your computer is struggling to keep up — you'll notice slow app launches, delayed responses, and possibly temporary freezes.`
|
|
129
|
+
);
|
|
130
|
+
} else if (cpuLoad >= 80) {
|
|
131
|
+
paragraphs.push(
|
|
132
|
+
`Your CPU is working very hard at ${cpuLoad}% usage. This means your processor — the brain of your computer — is nearly at capacity. You'll likely experience sluggish performance, laggy mouse movements, and slow application responses.`
|
|
133
|
+
);
|
|
134
|
+
} else if (memUsed >= 80) {
|
|
135
|
+
paragraphs.push(
|
|
136
|
+
`Your memory (RAM) is nearly full at ${memUsed}%. RAM is like your computer's short-term workspace — when it fills up, the computer starts using your hard drive as overflow storage (called "swap"), which is dramatically slower. This causes significant slowdowns.`
|
|
137
|
+
);
|
|
138
|
+
} else if (cpuLoad >= 60 || memUsed >= 60) {
|
|
139
|
+
paragraphs.push(
|
|
140
|
+
`Your system is moderately loaded. CPU is at ${cpuLoad}% and memory at ${memUsed}%. This isn't critical, but if more applications are opened, performance could start degrading.`
|
|
141
|
+
);
|
|
142
|
+
} else {
|
|
143
|
+
paragraphs.push(
|
|
144
|
+
`Your system is running efficiently. CPU and memory usage are well within normal limits. Your computer has plenty of capacity for additional work.`
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ── Process responsibility explanation ──
|
|
149
|
+
if (processAnalysis.responsibilities.length > 0) {
|
|
150
|
+
const respExplanations = processAnalysis.responsibilities.map(r => {
|
|
151
|
+
if (r.type === 'single_cpu_dominant') {
|
|
152
|
+
return `The process "${r.process.name}" is the primary resource consumer, using ${r.process.cpu}% of your CPU. ${this._getProcessContext(r.process)}`;
|
|
153
|
+
}
|
|
154
|
+
if (r.type === 'multiple_cpu_hogs') {
|
|
155
|
+
return `Multiple processes are competing for CPU: ${r.processes.map(p => `"${p.name}" (${p.cpu}%)`).join(', ')}. This competition for processor time is slowing everything down.`;
|
|
156
|
+
}
|
|
157
|
+
if (r.type === 'memory_hog') {
|
|
158
|
+
return `"${r.process.name}" is using a large amount of memory (${r.process.memRssFormatted}). ${this._getMemoryContext(r.process)}`;
|
|
159
|
+
}
|
|
160
|
+
return r.message;
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
paragraphs.push(respExplanations.join(' '));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ── Core imbalance explanation ──
|
|
167
|
+
const coreIssue = cpu.issues.find(i => i.type === 'core_imbalance');
|
|
168
|
+
if (coreIssue) {
|
|
169
|
+
paragraphs.push(
|
|
170
|
+
`Interestingly, your CPU cores are not sharing the work evenly. One core is much busier than the others. This usually means a single program is doing intensive work that can't be split across multiple cores — like a single-threaded calculation, file compression, or a tight loop.`
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return paragraphs.join('\n\n');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Build cause-and-effect chains
|
|
179
|
+
*/
|
|
180
|
+
_buildCauseEffect(cpu, memory, processAnalysis) {
|
|
181
|
+
const chains = [];
|
|
182
|
+
|
|
183
|
+
// Browser causing high memory
|
|
184
|
+
const browserIssue = processAnalysis.issues.find(i => i.type === 'browser_heavy');
|
|
185
|
+
if (browserIssue && memory.usedPercent >= 60) {
|
|
186
|
+
const estimatedTabs = browserIssue.estimatedTabs || Math.round(browserIssue.processes.length / 2);
|
|
187
|
+
chains.push({
|
|
188
|
+
cause: `Browser with ~${estimatedTabs} open tabs using significant resources`,
|
|
189
|
+
effect: `Using ${browserIssue.processes.reduce((s, p) => s + p.mem, 0).toFixed(1)}% memory and ${browserIssue.processes.reduce((s, p) => s + p.cpu, 0).toFixed(1)}% CPU`,
|
|
190
|
+
consequence: 'System memory pressure and possible sluggishness across all apps',
|
|
191
|
+
emoji: '🌐',
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Distributed load
|
|
196
|
+
const distributedIssue = processAnalysis.issues.find(i => i.type === 'distributed_load');
|
|
197
|
+
if (distributedIssue) {
|
|
198
|
+
chains.push({
|
|
199
|
+
cause: `${distributedIssue.processes.length} background processes running simultaneously`,
|
|
200
|
+
effect: `Collectively consuming ${distributedIssue.processes.reduce((s, p) => s + p.cpu, 0).toFixed(1)}% CPU`,
|
|
201
|
+
consequence: 'No single process to blame, but the combined load is significant. Like traffic — each car isn\'t the problem, but together they cause gridlock.',
|
|
202
|
+
emoji: '🔄',
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Swap active
|
|
207
|
+
const swapIssue = memory.issues.find(i => i.type === 'swap_active');
|
|
208
|
+
if (swapIssue) {
|
|
209
|
+
chains.push({
|
|
210
|
+
cause: 'RAM is full — too many applications consuming memory',
|
|
211
|
+
effect: 'System is using swap (disk as memory)',
|
|
212
|
+
consequence: 'Significant performance degradation — disk is 100-1000x slower than RAM. You\'ll notice freezes and delays.',
|
|
213
|
+
emoji: '💾',
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// High CPU temp
|
|
218
|
+
const tempIssue = cpu.issues.find(i => i.type === 'cpu_temp_high');
|
|
219
|
+
if (tempIssue) {
|
|
220
|
+
chains.push({
|
|
221
|
+
cause: 'CPU is running at high temperature',
|
|
222
|
+
effect: 'Processor may throttle (reduce speed) to prevent damage',
|
|
223
|
+
consequence: 'Even with available CPU capacity, performance may be capped by thermal limits. Ensure good ventilation.',
|
|
224
|
+
emoji: '🌡️',
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return chains;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Build developer-specific insights
|
|
233
|
+
*/
|
|
234
|
+
_buildDevInsights(devEnvironment, processAnalysis) {
|
|
235
|
+
if (!devEnvironment?.isDevActive) return null;
|
|
236
|
+
|
|
237
|
+
const insights = [];
|
|
238
|
+
|
|
239
|
+
if (devEnvironment.hasHotReload) {
|
|
240
|
+
const devHighCpu = processAnalysis.insights.filter(i => i.type === 'dev_process_high_cpu');
|
|
241
|
+
if (devHighCpu.length > 0) {
|
|
242
|
+
insights.push({
|
|
243
|
+
emoji: '🔥',
|
|
244
|
+
insight: 'Development server with hot reload detected. Hot reload watches for file changes and rebuilds — this is CPU-intensive during saves, especially with large projects.',
|
|
245
|
+
actionable: true,
|
|
246
|
+
});
|
|
247
|
+
} else {
|
|
248
|
+
insights.push({
|
|
249
|
+
emoji: '🔄',
|
|
250
|
+
insight: 'Development server with hot reload is running. Normal CPU usage detected — no concerns.',
|
|
251
|
+
actionable: false,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (devEnvironment.hasNodeServer) {
|
|
257
|
+
insights.push({
|
|
258
|
+
emoji: '🟢',
|
|
259
|
+
insight: 'Node.js development server is running. If CPU usage is unexpectedly high, check for: infinite loops, unoptimized database queries, or memory leaks in your code.',
|
|
260
|
+
actionable: true,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (devEnvironment.hasTests) {
|
|
265
|
+
insights.push({
|
|
266
|
+
emoji: '🧪',
|
|
267
|
+
insight: 'Test runner detected. Test suites can be CPU-intensive, especially with large test files or file-watching mode. This is expected behavior.',
|
|
268
|
+
actionable: false,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (devEnvironment.editorActive) {
|
|
273
|
+
insights.push({
|
|
274
|
+
emoji: '📝',
|
|
275
|
+
insight: `Code editor is active. Modern editors with extensions (TypeScript, ESLint, Prettier) can use significant CPU during indexing and code analysis.`,
|
|
276
|
+
actionable: false,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return insights.length > 0 ? insights : null;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Build pattern narrative from spike detector results
|
|
285
|
+
*/
|
|
286
|
+
_buildPatternNarrative(patterns) {
|
|
287
|
+
if (!patterns) return null;
|
|
288
|
+
|
|
289
|
+
const narratives = [];
|
|
290
|
+
|
|
291
|
+
// CPU patterns
|
|
292
|
+
if (patterns.cpu) {
|
|
293
|
+
if (patterns.cpu.trend === 'rising') {
|
|
294
|
+
narratives.push('📈 CPU usage has been trending upward — something is gradually consuming more processing power.');
|
|
295
|
+
} else if (patterns.cpu.trend === 'falling') {
|
|
296
|
+
narratives.push('📉 CPU usage is decreasing — the system is settling down from a period of high activity.');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
for (const pattern of patterns.cpu.patterns) {
|
|
300
|
+
narratives.push(`⚡ ${pattern.message}`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Memory patterns
|
|
305
|
+
if (patterns.memory) {
|
|
306
|
+
if (patterns.memory.trend === 'rising') {
|
|
307
|
+
narratives.push('📈 Memory usage has been climbing — applications may be accumulating data or leaking memory.');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
for (const pattern of patterns.memory.patterns) {
|
|
311
|
+
narratives.push(`⚡ ${pattern.message}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return narratives.length > 0 ? narratives : null;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Get human-friendly context for a process
|
|
320
|
+
*/
|
|
321
|
+
_getProcessContext(process) {
|
|
322
|
+
const cat = process.classification;
|
|
323
|
+
if (cat.isBrowser) {
|
|
324
|
+
return 'Browsers can be heavy — especially with many tabs, video playback, or web apps like Google Docs.';
|
|
325
|
+
}
|
|
326
|
+
if (cat.isDev) {
|
|
327
|
+
return 'This is a development tool — it may be compiling code, running a dev server, or performing file watching.';
|
|
328
|
+
}
|
|
329
|
+
if (cat.isSystem) {
|
|
330
|
+
return 'This is a system process — it handles internal OS operations. If it\'s consistently high, it may indicate driver issues or system maintenance.';
|
|
331
|
+
}
|
|
332
|
+
return '';
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Get memory context for a process
|
|
337
|
+
*/
|
|
338
|
+
_getMemoryContext(process) {
|
|
339
|
+
const cat = process.classification;
|
|
340
|
+
if (cat.isBrowser) {
|
|
341
|
+
return 'Each browser tab uses its own pool of memory. Tabs with rich content (videos, complex web apps) use more.';
|
|
342
|
+
}
|
|
343
|
+
if (cat.isDev) {
|
|
344
|
+
return 'Development tools often load large project files into memory. Node.js applications may also accumulate memory during development (memory leaks).';
|
|
345
|
+
}
|
|
346
|
+
return 'This process has allocated a large amount of memory. If it continues to grow, it may indicate a memory leak.';
|
|
347
|
+
}
|
|
348
|
+
}
|