// lib/host/resources.js — reads CT 311's own /proc + statfs. Node 22 fs.statfs. import { readFile } from 'node:fs/promises'; import { statfs } from 'node:fs/promises'; async function cpuSample() { const line = (await readFile('/proc/stat', 'utf8')).split('\n')[0]; // "cpu u n s i ..." const v = line.trim().split(/\s+/).slice(1).map(Number); const idle = v[3] + (v[4] || 0); const total = v.reduce((a, b) => a + b, 0); return { idle, total }; } export async function snapshot() { // CPU%: two samples ~100ms apart. const a = await cpuSample(); await new Promise(r => setTimeout(r, 100)); const b = await cpuSample(); const dTotal = b.total - a.total, dIdle = b.idle - a.idle; const cpu_pct = dTotal > 0 ? Math.round((1 - dIdle / dTotal) * 100) : 0; // Memory from /proc/meminfo (kB). const mem = Object.fromEntries( (await readFile('/proc/meminfo', 'utf8')).split('\n').filter(Boolean).map(l => { const [k, val] = l.split(':'); return [k.trim(), parseInt(val) * 1024]; }) ); const total = mem.MemTotal, avail = mem.MemAvailable ?? mem.MemFree; const memOut = { total, used: total - avail, pct: Math.round((1 - avail / total) * 100) }; // Disk for / via statfs. const fs = await statfs('/'); const dTotalB = fs.blocks * fs.bsize, dFree = fs.bavail * fs.bsize; const disk = { total: dTotalB, free: dFree, pct: Math.round((1 - dFree / dTotalB) * 100) }; // Net totals from /proc/net/dev (sum non-lo interfaces). let rx = 0, tx = 0; for (const l of (await readFile('/proc/net/dev', 'utf8')).split('\n')) { const m = l.match(/^\s*([^:]+):\s+(\d+)(?:\s+\d+){7}\s+(\d+)/); if (m && m[1].trim() !== 'lo') { rx += Number(m[2]); tx += Number(m[3]); } } return { cpu_pct, mem: memOut, disk, net: { rx_bytes: rx, tx_bytes: tx }, at: Date.now() }; }