Files
portal/tools/system-health.mjs
T
Дмитрий ccf4108e17 fix(status-md): rename C6 System Health to avoid alert-table collision
Code review noted that the new section heading ## C6: System Health collided
with the existing alert-table row | C6 Chain map sync | for controller C6.
Two things named C6 confuses readers and brain-retro analysis scripts.

Heading is now ## System Health (no prefix). Section position unchanged.

Also tightens weak toContain('2')-style assertions in system-health.test.mjs
to pipe-delimited '| 2 |' form -- prevents false-passes if sort order breaks.

Follow-up to 7314a926.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 10:46:00 +03:00

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`;
}