Use shared OS-available memory readings so macOS cache/inactive pages are not counted as used. - Add a core available-memory helper that prefers `process.availableMemory()` and flags `freemem` fallback reliability. - Route core metrics, dashboard system stats, and the dashboard TUI through the shared helper. - Move and expand memory tests across core, dashboard routes, and verification coverage while documenting the telemetry behavior. - Add a patch changeset for the published CLI package. Files changed: .changeset/fix-macos-memory-used.md | 5 ++ docs/architecture.md | 2 +- docs/dashboard-guide.md | 2 +- .../__tests__/available-memory.test.ts | 54 -------------- .../cli/src/commands/dashboard-tui/controller.ts | 37 +--------- .../core/src/__tests__/available-memory.test.ts | 83 ++++++++++++++++++++++ packages/core/src/__tests__/system-metrics.test.ts | 36 +++++++++- packages/core/src/available-memory.ts | 32 +++++++++ packages/core/src/index.ts | 1 + packages/core/src/system-metrics.ts | 9 ++- .../dashboard/src/__tests__/routes-system.test.ts | 27 +++++++ packages/dashboard/src/routes.ts | 7 +- .../src/__tests__/run-verification-command.test.ts | 18 +++++ 13 files changed, 218 insertions(+), 95 deletions(-) Fusion-Task-Id: FN-6834 Fusion-Task-Lineage: 2e651551-6c35-46d1-b517-535758c7ace2
69 lines
2.1 KiB
TypeScript
69 lines
2.1 KiB
TypeScript
import { cpus, totalmem, uptime as getUptime } from "node:os";
|
|
import * as checkDiskSpaceModule from "check-disk-space";
|
|
import { getAvailableMemoryBytes } from "./available-memory.js";
|
|
import type { SystemMetrics } from "./types.js";
|
|
|
|
const checkDiskSpace = ((checkDiskSpaceModule as { default?: unknown }).default ??
|
|
checkDiskSpaceModule) as (directoryPath: string) => Promise<{
|
|
diskPath: string;
|
|
free: number;
|
|
size: number;
|
|
}>;
|
|
|
|
function clamp(value: number, min: number, max: number): number {
|
|
if (!Number.isFinite(value)) return min;
|
|
return Math.min(max, Math.max(min, value));
|
|
}
|
|
|
|
function toNonNegative(value: number): number {
|
|
if (!Number.isFinite(value) || value < 0) {
|
|
return 0;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Collect host-level system metrics for mesh state reporting.
|
|
*/
|
|
export async function collectSystemMetrics(dbPath?: string): Promise<SystemMetrics> {
|
|
const cpuTimes = cpus();
|
|
|
|
let busyTime = 0;
|
|
let totalTime = 0;
|
|
|
|
for (const cpu of cpuTimes) {
|
|
const busy = cpu.times.user + cpu.times.nice + cpu.times.sys;
|
|
const total = busy + cpu.times.idle + cpu.times.irq;
|
|
busyTime += busy;
|
|
totalTime += total;
|
|
}
|
|
|
|
const cpuUsage = totalTime > 0 ? (busyTime / totalTime) * 100 : 0;
|
|
|
|
const memoryTotal = toNonNegative(totalmem());
|
|
/*
|
|
FNXC:SystemMetrics 2026-06-21-13:01:
|
|
Mesh metrics must compute used memory from OS-available memory instead of raw `freemem()` so macOS inactive/cache pages are not incorrectly reported as used.
|
|
*/
|
|
const rawMemoryUsed = memoryTotal - toNonNegative(getAvailableMemoryBytes());
|
|
const memoryUsed = clamp(rawMemoryUsed, 0, memoryTotal);
|
|
|
|
const diskPath = dbPath ?? process.cwd();
|
|
const diskSpace = await checkDiskSpace(diskPath);
|
|
const storageTotal = toNonNegative(diskSpace.size);
|
|
const rawStorageUsed = storageTotal - toNonNegative(diskSpace.free);
|
|
const storageUsed = clamp(rawStorageUsed, 0, storageTotal);
|
|
|
|
const uptime = toNonNegative(getUptime() * 1000);
|
|
|
|
return {
|
|
cpuUsage: clamp(cpuUsage, 0, 100),
|
|
memoryUsed,
|
|
memoryTotal,
|
|
storageUsed,
|
|
storageTotal,
|
|
uptime,
|
|
reportedAt: new Date().toISOString(),
|
|
};
|
|
}
|