Files
bare-operating-system/packages/bare-os-coreutils/lib/baretop-snapshot.js
T
2026-04-26 08:28:12 -04:00

928 lines
28 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/** Fetch /proc and ctx snapshots for /bin/baretop (preamble; no import). */
/** EWMA of last fetch wall time (ms); used when `BARE_TOP_FETCH_EWMA=1`. */
var bareTopFetchEwmaMs = -1
/** Last fallback proc batch concurrency (for jitter smoothing). */
var bareTopLastFallbackConc = -1
/** Soft cache for secondary proc/ctx reads (stale-while-refresh). */
var bareTopSecondaryCache = Object.create(null)
/**
* Keep in sync with `bareOsReadBareTopSnapshot` path list in packages/bare-os-booter/index.js
* @type {readonly [string, string][]}
*/
var BARE_TOP_SNAPSHOT_PROC_ENTRIES = [
['index', '/proc/bare_os/index.json'],
['version', '/proc/bare_os/version'],
['hostOs', '/proc/bare_os/host_os.json'],
['debug', '/proc/bare_os/debug.json'],
['sessionStats', '/proc/bare_os/session_stats'],
['quotas', '/proc/bare_os/quotas'],
['capabilitiesJson', '/proc/bare_os/capabilities.json'],
['capabilitiesNode', '/proc/bare_os/capabilities'],
['seedHandshake', '/proc/bare_os/seed_handshake'],
['bootstrap', '/proc/bare_os/bootstrap'],
['provenance', '/proc/bare_os/provenance'],
['snapshotHints', '/proc/bare_os/snapshot_hints.json'],
['stagingSlot', '/proc/bare_os/staging_slot'],
['peerHealth', '/proc/bare_os/peer_health'],
['pearIpc', '/proc/bare_os/pear_ipc.json'],
['pearIpcHealth', '/proc/bare_os/pear_ipc_health.json'],
['pearTrust', '/proc/bare_os/pear_trust.json'],
['replication', '/proc/bare_os/replication'],
['replicationBackpressure', '/proc/bare_os/replication_backpressure.json'],
['swarm', '/proc/bare_os/swarm'],
['syncWindow', '/proc/bare_os/sync_window.json'],
['clock', '/proc/bare_os/clock.json'],
['openssh', '/proc/bare_os/openssh.json'],
['hdmsHealth', '/proc/bare_os/hdms_health.json'],
['hdmsHints', '/proc/bare_os/hdms_hints.json'],
['dhtStatus', '/proc/bare_os/dht_status.json'],
['udxExtended', '/proc/bare_os/udx_extended.json'],
['ipcBackpressure', '/proc/bare_os/ipc_backpressure.json'],
['delegateRed', '/proc/bare_os/delegate_red.json'],
['gitDelegateStats', '/proc/bare_os/git_delegate_stats.json'],
['kernelProgram', '/proc/bare_os/kernel_program.json'],
['gitLfsPointerStats', '/proc/bare_os/git_lfs_pointer_stats.json'],
['workerBudget', '/proc/bare_os/worker_budget.json'],
['sandboxProfile', '/proc/bare_os/sandbox_profile.json'],
['rlimits', '/proc/bare_os/rlimits.json'],
['extensions', '/proc/bare_os/extensions.json'],
['initdDag', '/proc/bare_os/initd_dag.json'],
['bootGraph', '/proc/bare_os/boot_graph.json'],
[
'bootBudgetSummary',
'/proc/bare_os/boot_budget_summary.json'
],
['processTable', '/proc/bare_os/process_table.json'],
['syscalls', '/proc/bare_os/syscalls.json'],
['metricsProm', '/proc/bare_os/metrics.prom'],
['protomuxWire', '/proc/bare_os/protomux.json'],
['securityPosture', '/proc/bare_os/security_posture.json'],
['processIo', '/proc/bare_os/process_io.json'],
['processThreads', '/proc/bare_os/process_threads.json'],
['processMaps', '/proc/bare_os/process_maps.json']
]
/** Subset for BARE_TOP_SNAPSHOT_LITE=1 — keep keys subset of full list above. */
var BARE_TOP_SNAPSHOT_LITE_ENTRIES = [
['index', '/proc/bare_os/index.json'],
['version', '/proc/bare_os/version'],
['hostOs', '/proc/bare_os/host_os.json'],
['replication', '/proc/bare_os/replication'],
['swarm', '/proc/bare_os/swarm'],
['syncWindow', '/proc/bare_os/sync_window.json'],
['processTable', '/proc/bare_os/process_table.json'],
['snapshotHints', '/proc/bare_os/snapshot_hints.json']
]
var BARE_TOP_SNAPSHOT_EXTENDED_HINT_KEYS = [
'meshdrop',
'peerDetails',
'dhtScan',
'swarmDoctor',
'routeSummary',
'holepunchSummary'
]
/** @param {unknown} buf @param {unknown} b4a @returns {string} */
function bareTopBufToString(buf, b4a) {
if (buf == null) return ''
if (typeof buf === 'string') return buf
try {
if (b4a && typeof b4a.toString === 'function') return b4a.toString(buf)
} catch {
/* fall through */
}
try {
return new TextDecoder().decode(
buf instanceof Uint8Array ? buf : new Uint8Array(/** @type {ArrayLike<number>} */ (buf))
)
} catch {
return ''
}
}
/** @param {string} s @returns {unknown} */
function bareTopJsonParse(s) {
const t = String(s || '').trim()
if (!t) return null
try {
return JSON.parse(t)
} catch {
return null
}
}
/**
* @param {Record<string, unknown>} ctx
* @param {string} path
* @returns {Promise<string>}
*/
async function bareTopReadProc(ctx, path) {
const vfs = ctx.vfs
if (!vfs || typeof vfs.readFile !== 'function') return ''
try {
const buf = await vfs.readFile(path)
return bareTopBufToString(buf, ctx.b4a)
} catch {
return ''
}
}
/**
* @param {Record<string, unknown>} ctx
* @param {readonly [string, string][]} entries
* @param {number} concurrency
* @returns {Promise<Record<string, string>>}
*/
/**
* @param {Record<string, unknown>} ctx
* @param {readonly [string, string][]} entries
* @param {number} concurrency
* @param {string[] | null} [errPaths] push failed paths (optional)
*/
async function bareTopReadProcBatch(ctx, entries, concurrency, errPaths) {
/** @type {Record<string, string>} */
const out = {}
const vfs = ctx.vfs
const conc = Math.max(1, Math.min(16, concurrency | 0) || 6)
for (let i = 0; i < entries.length; i += conc) {
const slice = entries.slice(i, i + conc)
await Promise.all(
slice.map(async ([key, path]) => {
if (!vfs || typeof vfs.readFile !== 'function') {
out[key] = ''
return
}
try {
const buf = await vfs.readFile(path)
out[key] = bareTopBufToString(buf, ctx.b4a)
} catch {
out[key] = ''
if (errPaths) errPaths.push(path)
}
})
)
}
return out
}
/**
* Read-through stale cache helper for secondary fields.
* @param {string} key
* @param {number} ttlMs
* @param {() => Promise<unknown>} producer
* @returns {Promise<{ value: unknown, stale: boolean, freshAtMs: number, ttlMs: number }>}
*/
async function bareTopCachedSecondary(key, ttlMs, producer) {
const now = Date.now()
const cur = bareTopSecondaryCache[key]
if (cur && typeof cur === 'object' && now - cur.atMs <= ttlMs) {
return { value: cur.value, stale: false, freshAtMs: cur.atMs, ttlMs }
}
try {
const value = await producer()
bareTopSecondaryCache[key] = { value, atMs: now }
return { value, stale: false, freshAtMs: now, ttlMs }
} catch {
if (cur && typeof cur === 'object') {
return { value: cur.value, stale: true, freshAtMs: cur.atMs, ttlMs }
}
return { value: null, stale: true, freshAtMs: 0, ttlMs }
}
}
/**
* @param {unknown} o
* @param {number} maxChars
* @returns {string}
*/
function bareTopTruncateJsonPretty(o, maxChars) {
const raw = JSON.stringify(o, null, 2)
if (raw.length <= maxChars) return raw
return raw.slice(0, Math.max(0, maxChars - 24)) + '\n… (truncated) …\n'
}
/** @param {number} n @returns {string} */
function bareTopFormatBytes(n) {
const x = Number(n)
if (!Number.isFinite(x) || x < 0) return '—'
if (x < 1024) return String(Math.floor(x)) + ' B'
if (x < 1048576) return (x / 1024).toFixed(1) + ' KiB'
if (x < 1073741824) return (x / 1048576).toFixed(1) + ' MiB'
return (x / 1073741824).toFixed(2) + ' GiB'
}
/** @param {number} ms @returns {string} */
function bareTopFormatDuration(ms) {
const x = Number(ms)
if (!Number.isFinite(x) || x < 0) return '—'
if (x < 1000) return String(Math.floor(x)) + ' ms'
if (x < 60000) return (x / 1000).toFixed(1) + ' s'
if (x < 3600000) return Math.floor(x / 60000) + ' m'
return (x / 3600000).toFixed(1) + ' h'
}
/**
* @param {Record<string, unknown> | null} m metrics live
* @param {number} peers
* @param {string | null | undefined} readErr optional snapshot read failure
* @returns {number} 0100
*/
function bareTopHealthScore(m, peers, readErr) {
return bareTopHealthScoreDetail(m, peers, readErr).score
}
/**
* Subtractive score from 100 (penalty table):
* - read_err: -28
* - no_metrics: -22
* - delegate_inflight: capped combined from active keys (count>0) + total inflight sum; max -12
* - replication stall: -20 when stallHint is a fault token OR replicationStall===true;
* benign hints no_peers | length_unavailable | ok do not penalize (operational, not fault)
* - boot_cold exceeded: -12; boot_stdlib exceeded: -12
* - initd_readiness.failed: up to -18; kernel initd.unit_failed_final: up to -10
* - peer_mismatch (replication.peerCount vs metrics peers): -8 when both finite and |Δ|>2
* - peers_invalid (peers<0): -5
* @param {Record<string, unknown> | null} m
* @param {number} peers
* @param {string | null | undefined} readErr
* @returns {{ score: number, breakdown: string }}
*/
function bareTopHealthScoreDetail(m, peers, readErr) {
let score = 100
/** @type {string[]} */
const parts = ['base=100']
const errS = readErr != null ? String(readErr).trim() : ''
if (errS) {
const pen = 28
score -= pen
parts.push('read_err=-' + pen)
}
const live =
m && typeof m === 'object'
? /** @type {Record<string, unknown>} */ (m)
: null
if (!live) {
const pen = 22
score -= pen
parts.push('no_metrics=-' + pen)
} else {
const delI = live.delegateInflight
if (delI != null && typeof delI === 'object') {
const o = /** @type {Record<string, unknown>} */ (delI)
const keys = Object.keys(o)
let nActive = 0
let sum = 0
for (let i = 0; i < keys.length; i++) {
const v = Math.max(0, Number(o[keys[i]]) || 0)
sum += v
if (v > 0) nActive++
}
const penK = Math.min(10, nActive * 2)
const penS = Math.min(20, sum * 2)
const inflightCap = 12
const inflightTotal = Math.min(inflightCap, penK + penS)
if (inflightTotal > 0) {
score -= inflightTotal
parts.push(
'inflight_-' +
inflightTotal +
'(activeKinds=' +
nActive +
',sum=' +
sum +
')'
)
}
}
const rep =
live.replicationLive && typeof live.replicationLive === 'object'
? /** @type {Record<string, unknown>} */ (live.replicationLive)
: null
const stallExplicit =
rep && rep.replicationStall === true ? true : false
const hintRaw = rep && rep.stallHint
const benignStallHints = new Set([
'',
'ok',
'no_peers',
'length_unavailable',
'0',
'false',
'none',
'off',
'null',
'undefined'
])
if (stallExplicit) {
const pen = 20
score -= pen
parts.push('replication_stall_flag=-' + pen)
} else if (hintRaw != null) {
const hs = String(hintRaw).trim().toLowerCase()
if (hs && !benignStallHints.has(hs)) {
const pen = 20
score -= pen
parts.push('stall_hint=-' + pen + '(' + hs + ')')
}
}
const cold =
live.bootBudgetCold && typeof live.bootBudgetCold === 'object'
? /** @type {Record<string, unknown>} */ (live.bootBudgetCold)
: null
if (cold && cold.exceeded === true) {
score -= 12
parts.push('boot_cold=-12')
}
const std =
live.bootBudgetBareStdlib &&
typeof live.bootBudgetBareStdlib === 'object'
? /** @type {Record<string, unknown>} */ (live.bootBudgetBareStdlib)
: null
if (std && std.exceeded === true) {
score -= 12
parts.push('boot_stdlib=-12')
}
const ir = live.initdReadiness
if (ir && typeof ir === 'object') {
const failed = Number(/** @type {Record<string, unknown>} */ (ir).failed)
if (Number.isFinite(failed) && failed > 0) {
const pen = Math.min(18, failed * 6)
score -= pen
parts.push('initd_failed_' + failed + '=-' + pen)
}
}
const kc = live.kernelCounters
if (kc && typeof kc === 'object') {
const kf = Number(
/** @type {Record<string, unknown>} */ (kc)['initd.unit_failed_final']
)
if (Number.isFinite(kf) && kf > 0) {
const pen = Math.min(10, kf * 2)
score -= pen
parts.push('initd_kc_' + kf + '=-' + pen)
}
}
if (
rep &&
typeof rep.peerCount === 'number' &&
Number.isFinite(rep.peerCount) &&
rep.peerCount >= 0 &&
Number.isFinite(peers) &&
peers >= 0
) {
if (Math.abs(rep.peerCount - peers) > 2) {
score -= 8
parts.push('peer_mismatch=-8')
}
}
}
if (peers < 0) {
score -= 5
parts.push('peers_invalid=-5')
}
if (score < 0) score = 0
if (score > 100) score = 100
parts.push('=>' + score)
return { score, breakdown: parts.join(' ') }
}
/**
* @param {number} prev
* @param {number} value
* @param {number} alpha 01
* @returns {number}
*/
function bareTopEma(prev, value, alpha) {
const a = Math.min(1, Math.max(0, alpha))
return prev * (1 - a) + value * a
}
/**
* @param {Record<string, unknown>} ctx
* @param {Record<string, string>} files
* @param {string} key
* @returns {Record<string, unknown> | null}
*/
function bareTopParsedFile(files, key) {
const t = files[key]
if (!t) return null
const p = bareTopJsonParse(t)
return p && typeof p === 'object'
? /** @type {Record<string, unknown>} */ (p)
: null
}
/**
* @param {Record<string, unknown>} ctx
* @returns {Promise<Record<string, unknown>>}
*/
/** One coalesced operator frame; keep VFS read fan-out bounded (batch size + concurrency). */
async function bareTopFetchSnapshot(ctx, fetchOpts) {
const fetchStart = Date.now()
let readErr = null
const env =
ctx.env && typeof ctx.env === 'object'
? /** @type {Record<string, string>} */ (ctx.env)
: {}
const fo =
fetchOpts && typeof fetchOpts === 'object'
? /** @type {{ forceLite?: boolean, activeTab?: string }} */ (fetchOpts)
: null
const concRaw = parseInt(env.BARE_TOP_FETCH_CONCURRENCY || '8', 10)
const concurrency = Number.isFinite(concRaw) ? concRaw : 8
const fetchEwmaOn =
env.BARE_TOP_FETCH_EWMA === '1' || env.BARE_TOP_FETCH_EWMA === 'true'
const snapshotLite =
fo && fo.forceLite === true
? true
: env.BARE_TOP_SNAPSHOT_LITE === '1' ||
env.BARE_TOP_SNAPSHOT_LITE === 'true'
const snapshotExtendedOn =
env.BARE_TOP_SNAPSHOT_EXTENDED === '1' ||
env.BARE_TOP_SNAPSHOT_EXTENDED === 'true'
const missingSignalsOn =
env.BARE_TOP_MISSING_SIGNALS === '1' ||
env.BARE_TOP_MISSING_SIGNALS === 'true'
const procEntryList = snapshotLite
? BARE_TOP_SNAPSHOT_LITE_ENTRIES
: BARE_TOP_SNAPSHOT_PROC_ENTRIES
const activeTab = String((fo && fo.activeTab) || 'overview').toLowerCase()
const requestedKeys = (() => {
if (snapshotLite) return BARE_TOP_SNAPSHOT_LITE_ENTRIES.map(([k]) => k)
if (activeTab === 'processes')
return [
'index',
'version',
'hostOs',
'processTable',
'sessionStats',
'swarm',
'replication',
'snapshotHints'
]
if (activeTab === 'network')
return [
'index',
'version',
'hostOs',
'swarm',
'replication',
'dhtStatus',
'udxExtended',
'snapshotHints'
]
if (
missingSignalsOn &&
(activeTab === 'network' || activeTab === 'operator' || activeTab === 'diagnostics')
) {
return BARE_TOP_SNAPSHOT_PROC_ENTRIES.map(([k]) => k).concat(
BARE_TOP_SNAPSHOT_EXTENDED_HINT_KEYS
)
}
return BARE_TOP_SNAPSHOT_PROC_ENTRIES.map(([k]) => k)
})()
const snapshotProfile =
snapshotLite || activeTab === 'processes' || activeTab === 'network'
? 'minimal'
: snapshotExtendedOn ||
(missingSignalsOn &&
(activeTab === 'network' ||
activeTab === 'operator' ||
activeTab === 'diagnostics'))
? 'extended'
: 'standard'
/** @type {Record<string, string>} */
let fileTexts = {}
let fastAtMs = 0
let snapshotBatchBytes = 0
/** @type {Record<string, unknown> | null} */
let metricsLiveFromSnap = null
try {
if (typeof ctx.bareOsReadBareTopSnapshot === 'function') {
const r = await ctx.bareOsReadBareTopSnapshot(
snapshotLite
? { lite: true, requestedKeys }
: {
profile: snapshotProfile,
requestedKeys
}
)
if (r && typeof r === 'object') {
const o = /** @type {Record<string, unknown>} */ (r)
const files = o.files
if (files && typeof files === 'object') {
for (const k of Object.keys(files)) {
const v = files[k]
if (typeof v === 'string') fileTexts[k] = v
}
}
const am = o.atMs
if (typeof am === 'number' && Number.isFinite(am)) fastAtMs = am
if (
typeof o.snapshotBytes === 'number' &&
Number.isFinite(o.snapshotBytes) &&
o.snapshotBytes >= 0
) {
snapshotBatchBytes = o.snapshotBytes
}
const mlRaw = o.metricsLiveText
if (typeof mlRaw === 'string' && mlRaw.trim()) {
const mp = bareTopJsonParse(mlRaw)
if (mp && typeof mp === 'object') {
metricsLiveFromSnap = /** @type {Record<string, unknown>} */ (mp)
}
}
}
}
} catch (e) {
readErr = (e && /** @type {{ message?: string }} */ (e).message) || String(e)
}
/** @type {string[]} */
const readErrPaths = []
if (Object.keys(fileTexts).length === 0) {
let useConc = concurrency
if (fetchEwmaOn && bareTopFetchEwmaMs >= 0) {
const e = bareTopFetchEwmaMs
if (e > 900) useConc = Math.max(2, concurrency - 3)
else if (e > 500) useConc = Math.max(2, concurrency - 2)
else if (e > 280) useConc = Math.max(2, concurrency - 1)
}
if (bareTopLastFallbackConc > 0) {
if (useConc > bareTopLastFallbackConc + 1) useConc = bareTopLastFallbackConc + 1
if (useConc < bareTopLastFallbackConc - 1) useConc = bareTopLastFallbackConc - 1
}
bareTopLastFallbackConc = useConc
fileTexts = await bareTopReadProcBatch(
ctx,
procEntryList,
useConc,
readErrPaths
)
}
const atMs = fastAtMs || Date.now()
/** @type {Record<string, unknown> | null} */
let metricsLive = metricsLiveFromSnap
/** @type {Record<string, unknown> | null} */
let resources = null
/** @type {Record<string, unknown> | null} */
let features = null
/** @type {Record<string, unknown> | null} */
let netSummary = null
/** @type {unknown} */
let initdGraph = null
/** @type {Record<string, unknown> | null} */
let fairnessSnapshot = null
/** @type {unknown} */
let subprocessBridge = null
/** @type {Record<string, unknown> | null} */
let hostStats = null
const ttl = {
metricsLive: 300,
resources: activeTab === 'diagnostics' ? 400 : 1500,
features: activeTab === 'features' ? 500 : 2000,
netSummary: activeTab === 'network' ? 300 : 1500,
initdGraph: activeTab === 'initd' ? 400 : 2000,
fairness: activeTab === 'operator' ? 500 : 2000,
subprocess: activeTab === 'diagnostics' ? 600 : 2500,
hostStats: activeTab === 'host' ? 500 : 2000,
memRaw: activeTab === 'mem' ? 350 : 1400,
loadavg: activeTab === 'cpu' ? 350 : 1200,
cpuRaw: activeTab === 'cpu' ? 450 : 1800,
diskstats: activeTab === 'disk' ? 450 : 1800
}
/** @type {Record<string, { stale: boolean, freshAtMs: number, ttlMs: number }>} */
const sectionTtl = {}
try {
if (typeof ctx.bareOsReadProcMetricsLive === 'function') {
const o = ctx.bareOsReadProcMetricsLive()
if (o && typeof o === 'object') metricsLive = /** @type {Record<string, unknown>} */ (o)
}
} catch (e) {
if (!readErr)
readErr = (e && /** @type {{ message?: string }} */ (e).message) || String(e)
}
if (!metricsLive) {
const ml = await bareTopCachedSecondary(
'metrics_live',
ttl.metricsLive,
async () => {
const t = await bareTopReadProc(ctx, '/proc/bare_os/metrics_live.json')
return bareTopJsonParse(t)
}
)
sectionTtl.metricsLive = {
stale: ml.stale,
freshAtMs: ml.freshAtMs,
ttlMs: ml.ttlMs
}
if (ml.value && typeof ml.value === 'object') {
metricsLive = /** @type {Record<string, unknown>} */ (ml.value)
}
}
try {
if (typeof ctx.bareOsGetResourceStatus === 'function') {
const o = ctx.bareOsGetResourceStatus()
if (o && typeof o === 'object') resources = /** @type {Record<string, unknown>} */ (o)
}
} catch {
/* ignore */
}
if (!resources) {
const rs = await bareTopCachedSecondary(
'resources',
ttl.resources,
async () => {
const t = await bareTopReadProc(ctx, '/proc/bare_os_resources')
return bareTopJsonParse(t)
}
)
sectionTtl.resources = { stale: rs.stale, freshAtMs: rs.freshAtMs, ttlMs: rs.ttlMs }
if (rs.value && typeof rs.value === 'object') {
resources = /** @type {Record<string, unknown>} */ (rs.value)
}
}
const fs = await bareTopCachedSecondary(
'features',
ttl.features,
async () => {
let fp = bareTopJsonParse(await bareTopReadProc(ctx, '/proc/bare_os/features'))
if (!fp) fp = bareTopJsonParse(await bareTopReadProc(ctx, '/proc/bare_os_features'))
return fp
}
)
sectionTtl.features = { stale: fs.stale, freshAtMs: fs.freshAtMs, ttlMs: fs.ttlMs }
if (fs.value && typeof fs.value === 'object') {
features = /** @type {Record<string, unknown>} */ (fs.value)
}
const ns = await bareTopCachedSecondary(
'net_summary',
ttl.netSummary,
async () => {
const netT = await bareTopReadProc(ctx, '/proc/bare_os/net_summary.json')
return bareTopJsonParse(netT)
}
)
sectionTtl.netSummary = {
stale: ns.stale,
freshAtMs: ns.freshAtMs,
ttlMs: ns.ttlMs
}
if (ns.value && typeof ns.value === 'object') {
netSummary = /** @type {Record<string, unknown>} */ (ns.value)
}
const ig = await bareTopCachedSecondary(
'initd_graph',
ttl.initdGraph,
async () => {
const initT = await bareTopReadProc(ctx, '/proc/bare_os/initd_graph.json')
return bareTopJsonParse(initT)
}
)
sectionTtl.initdGraph = {
stale: ig.stale,
freshAtMs: ig.freshAtMs,
ttlMs: ig.ttlMs
}
initdGraph = ig.value
if (initdGraph === null) {
const alt = bareTopJsonParse(fileTexts.initdDag || '')
initdGraph = alt
}
const fa = await bareTopCachedSecondary('fairness_snapshot', ttl.fairness, async () => {
if (typeof ctx.bareOsReadDelegateFairnessSnapshot === 'function') {
return ctx.bareOsReadDelegateFairnessSnapshot()
}
return null
})
sectionTtl.fairnessSnapshot = {
stale: fa.stale,
freshAtMs: fa.freshAtMs,
ttlMs: fa.ttlMs
}
if (fa.value && typeof fa.value === 'object') {
fairnessSnapshot = /** @type {Record<string, unknown>} */ (fa.value)
}
const sb = await bareTopCachedSecondary(
'subprocess_bridge',
ttl.subprocess,
async () => {
if (typeof ctx.bareOsReadSubprocessBridgeSnapshot === 'function') {
return ctx.bareOsReadSubprocessBridgeSnapshot()
}
return null
}
)
sectionTtl.subprocessBridge = {
stale: sb.stale,
freshAtMs: sb.freshAtMs,
ttlMs: sb.ttlMs
}
subprocessBridge = sb.value
const hs = await bareTopCachedSecondary('host_stats', ttl.hostStats, async () => {
return ctx.bareOsHostStats || null
})
sectionTtl.hostStats = { stale: hs.stale, freshAtMs: hs.freshAtMs, ttlMs: hs.ttlMs }
if (hs.value && typeof hs.value === 'object') {
hostStats = /** @type {Record<string, unknown>} */ (hs.value)
}
const memC = await bareTopCachedSecondary('proc_meminfo', ttl.memRaw, async () =>
bareTopReadProc(ctx, '/proc/meminfo')
)
sectionTtl.meminfo = {
stale: memC.stale,
freshAtMs: memC.freshAtMs,
ttlMs: memC.ttlMs
}
const memRaw = typeof memC.value === 'string' ? memC.value : ''
let meminfoLine = ''
for (const line of memRaw.split('\n')) {
const L = line.trim()
if (L.startsWith('MemTotal:') || L.startsWith('MemAvailable:')) {
meminfoLine = L
break
}
}
if (!meminfoLine) meminfoLine = memRaw.split('\n')[0]?.trim() || ''
const loadC = await bareTopCachedSecondary('proc_loadavg', ttl.loadavg, async () =>
bareTopReadProc(ctx, '/proc/loadavg')
)
sectionTtl.loadavg = {
stale: loadC.stale,
freshAtMs: loadC.freshAtMs,
ttlMs: loadC.ttlMs
}
const loadavgLine = String(loadC.value || '').split('\n')[0]?.trim() || ''
let cpuLine = ''
let cpuCoreCount = 0
const cpuC = await bareTopCachedSecondary('proc_cpuinfo', ttl.cpuRaw, async () =>
bareTopReadProc(ctx, '/proc/cpuinfo')
)
sectionTtl.cpuinfo = {
stale: cpuC.stale,
freshAtMs: cpuC.freshAtMs,
ttlMs: cpuC.ttlMs
}
const cpuRaw = typeof cpuC.value === 'string' ? cpuC.value : ''
for (const line of cpuRaw.split('\n')) {
if (line.startsWith('model name') || line.startsWith('Model')) {
cpuLine = line.replace(/^[^:]+:\s*/, '').trim().slice(0, 72)
}
const L = line.trim().toLowerCase()
if (L.startsWith('processor') && L.includes(':')) cpuCoreCount++
}
if (!cpuCoreCount) {
const m = cpuRaw.match(/^processor\s*:/gim)
if (m) cpuCoreCount = m.length
}
let swapinfoLine = ''
for (const line of memRaw.split('\n')) {
const L = line.trim()
if (L.startsWith('SwapTotal:') || L.startsWith('SwapFree:')) {
swapinfoLine = swapinfoLine ? swapinfoLine + ' ' + L : L
}
}
const dsC = await bareTopCachedSecondary(
'proc_diskstats',
ttl.diskstats,
async () => bareTopReadProc(ctx, '/proc/diskstats')
)
sectionTtl.diskstats = {
stale: dsC.stale,
freshAtMs: dsC.freshAtMs,
ttlMs: dsC.ttlMs
}
const diskstatsRaw = typeof dsC.value === 'string' ? dsC.value : ''
const diskstatsLine = diskstatsRaw.split('\n')[0]?.trim() || ''
const hostOs = bareTopParsedFile(fileTexts, 'hostOs')
const procIndex =
bareTopParsedFile(fileTexts, 'index') || /** @type {Record<string, unknown>} */ ({})
const procIndexAvailability = (() => {
/** @type {Record<string, boolean>} */
const out = Object.create(null)
const nodes =
procIndex && Array.isArray(procIndex.nodes) ? procIndex.nodes : []
const nodeSet = new Set()
for (const n of nodes) {
if (!n || typeof n !== 'object') continue
const no = /** @type {Record<string, unknown>} */ (n)
if (typeof no.name === 'string') nodeSet.add(no.name)
}
for (const [k, p] of BARE_TOP_SNAPSHOT_PROC_ENTRIES) {
const base = p.split('/').pop() || ''
out[k] =
nodeSet.has(base) ||
nodeSet.has(base.replace(/\.json$/i, '')) ||
(typeof fileTexts[k] === 'string' && fileTexts[k].length > 0)
}
for (const k of BARE_TOP_SNAPSHOT_EXTENDED_HINT_KEYS) {
out[k] = out[k] === true || (typeof fileTexts[k] === 'string' && fileTexts[k].length > 0)
}
return out
})()
const metaAt =
metricsLive && typeof metricsLive.atMs === 'number' ? metricsLive.atMs : atMs
/** @type {Record<string, Record<string, unknown> | null>} */
const extra = {}
for (const [key] of procEntryList) {
if (
key === 'processTable' &&
metricsLive &&
metricsLive.processTable &&
typeof metricsLive.processTable === 'object'
) {
extra[key] = /** @type {Record<string, unknown>} */ (
metricsLive.processTable
)
continue
}
extra[key] = bareTopParsedFile(fileTexts, key)
}
for (const key of BARE_TOP_SNAPSHOT_EXTENDED_HINT_KEYS) {
if (extra[key] == null) extra[key] = bareTopParsedFile(fileTexts, key)
}
extra.procIndex = procIndex
extra.procIndexAvailability = procIndexAvailability
/** @type {Record<string, number>} */
const snapshotBytesByKey = Object.create(null)
for (const k of Object.keys(fileTexts)) {
const v = fileTexts[k]
snapshotBytesByKey[k] = typeof v === 'string' ? v.length : 0
}
const fetchWallMs = Date.now() - fetchStart
bareTopFetchEwmaMs =
bareTopFetchEwmaMs < 0
? fetchWallMs
: bareTopFetchEwmaMs * 0.82 + fetchWallMs * 0.18
const peersN = Number(metricsLive && metricsLive.peers) || 0
const healthD = bareTopHealthScoreDetail(metricsLive, peersN, readErr)
return {
metricsLive,
resources,
features,
initdGraph,
netSummary,
meminfoLine,
swapinfoLine,
loadavgLine,
cpuLine,
cpuCoreCount,
diskstatsLine,
diskstatsRaw,
hostOs,
atMs,
metaAtMs: metaAt,
readErr,
readErrPaths,
fileTexts,
extra,
fairnessSnapshot,
subprocessBridge,
hostStats,
fetchWallMs,
snapshotBatchBytes,
snapshotBytesByKey,
sectionTtl,
snapshotProfile,
snapshotRequestedKeys: requestedKeys,
healthScore: healthD.score,
healthBreakdown: healthD.breakdown,
snapshotLite
}
}