Files
brain/tools/system-health.mjs
T

59 lines
2.3 KiB
JavaScript

import { execFileSync } from 'child_process';
const CPU_THRESHOLD_SECONDS = 3600; // 1 hour
export function parsePowerShellOutput(csv, now = new Date()) {
const lines = csv.trim().split(/\r?\n/);
if (lines.length < 2) return [];
const header = lines[0].split(',');
const idxPid = header.indexOf('Id');
const idxName = header.indexOf('ProcessName');
const idxCpu = header.indexOf('CPU');
const idxStart = header.indexOf('StartTime');
const procs = [];
for (let i = 1; i < lines.length; i++) {
const cells = lines[i].split(',');
if (cells.length < 4) continue;
const startTime = new Date(cells[idxStart]);
const ageMinutes = Math.max(0, Math.floor((now - startTime) / 60000));
procs.push({
pid: parseInt(cells[idxPid], 10),
name: cells[idxName],
cpuSeconds: parseFloat(cells[idxCpu]),
ageMinutes,
});
}
return procs;
}
export function queryRunningProcesses() {
// PowerShell command: get heavy processes, output as CSV without quotes
const psCmd = "Get-Process | Where-Object {$_.CPU -gt 3600} | Select-Object Id,ProcessName,CPU,StartTime | ConvertTo-Csv -NoTypeInformation | ForEach-Object {$_ -replace '\"',''}";
try {
const out = execFileSync('powershell.exe', [
'-NoProfile', '-NonInteractive',
'-Command',
psCmd,
], { encoding: 'utf-8', timeout: 5000 });
return parsePowerShellOutput(out, new Date());
} catch (e) {
return [];
}
}
export function computeSystemHealthBlock(procs) {
const heavy = (procs || [])
.filter(p => p.cpuSeconds > CPU_THRESHOLD_SECONDS)
.sort((a, b) => b.cpuSeconds - a.cpuSeconds)
.slice(0, 3);
if (heavy.length === 0) {
return `## System Health\n\nДолго работающих процессов нет (порог CPU > 1ч).\n`;
}
const rows = heavy.map(p => {
const hours = (p.cpuSeconds / 3600).toFixed(2);
const ageHours = (p.ageMinutes / 60).toFixed(1);
return `| ${p.pid} | ${p.name} | ${hours}ч | ${ageHours}ч |`;
}).join('\n');
return `## System Health\n\nТоп-3 процессов с CPU > 1ч:\n\n| PID | Имя | CPU-время | Возраст |\n|---|---|---|---|\n${rows}\n\n⚠️ Проверь, не «осиротевшие» ли это процессы от завершённых Claude-сессий.\n`;
}