/** Pure UI helpers for /bin/baretop (preamble; no import). */ /** * @param {string} line * @returns {{ label: string, value: string }[]} */ function bareTopParseMeminfoPairs(line) { const s = String(line || '').trim() const out = [] if (!s) return out const re = /(\w+):\s*(\d+)\s*(\w+)?/g let m while ((m = re.exec(s)) !== null) { out.push({ label: m[1], value: m[2] + (m[3] ? ' ' + m[3] : '') }) } return out } /** * @param {string} line * @returns {{ pairs: { label: string, value: string }[], memTotal?: number, memAvail?: number }} */ function bareTopParseMeminfoMetrics(line) { const pairs = bareTopParseMeminfoPairs(line) let memTotal let memAvail for (const p of pairs) { if (p.label === 'MemTotal') memTotal = parseInt(p.value, 10) if (p.label === 'MemAvailable') memAvail = parseInt(p.value, 10) } return { pairs, memTotal, memAvail } } /** * @param {string} line * @returns {{ one: number, five: number, fifteen: number, running?: number, total?: number } | null} */ function bareTopParseLoadavg(line) { const s = String(line || '').trim() if (!s) return null const parts = s.split(/\s+/) if (parts.length < 3) return null const one = parseFloat(parts[0]) const five = parseFloat(parts[1]) const fifteen = parseFloat(parts[2]) if (!Number.isFinite(one)) return null const slash = parts[3] && parts[3].includes('/') ? parts[3].split('/') : null return { one, five: Number.isFinite(five) ? five : one, fifteen: Number.isFinite(fifteen) ? fifteen : one, running: slash ? parseInt(slash[0], 10) : undefined, total: slash ? parseInt(slash[1], 10) : undefined } } /** * @param {number} pct 0–100 * @param {number} width * @param {boolean} ascii * @param {{ low?: string, mid?: string, hi?: string, reset: string }} pal * @param {boolean} useColor */ function bareTopFormatMeterBar(pct, width, ascii, pal, useColor) { const w = Math.max(2, width | 0) const p = Math.min(100, Math.max(0, Number(pct) || 0)) const fill = Math.round((p / 100) * w) const full = ascii ? '#' : '\u2588' const empty = ascii ? '-' : '\u2591' let s = '' for (let i = 0; i < w; i++) { const on = i < fill if (!useColor) { s += on ? full : empty continue } const c = p >= 95 ? pal.hi || '' : p >= 80 ? pal.mid || '' : on ? pal.low || '' : '' s += c + (on ? full : empty) + (c ? pal.reset : '') } return s } /** * @param {string} text * @param {number} maxLen * @param {boolean} wordBoundary */ function bareTopTruncateCell(text, maxLen, wordBoundary) { const t = String(text ?? '') const m = Math.max(1, maxLen | 0) if (t.length <= m) return t if (!wordBoundary || m < 5) return t.slice(0, m - 1) + '\u2026' let cut = t.lastIndexOf(' ', m - 1) if (cut < m >> 1) cut = m - 1 return t.slice(0, cut).trimEnd() + '\u2026' } /** * @param {unknown} pt process_table.json root * @returns {Array>} */ function bareTopProcessRowsFromTable(pt) { if (!pt || typeof pt !== 'object') return [] const o = /** @type {Record} */ (pt) const arr = o.processes if (!Array.isArray(arr)) return [] return arr.filter((x) => x && typeof x === 'object') } /** * @param {unknown} pt * @param {number} nowMs */ function bareTopSessionUptimeLine(pt, nowMs) { const rows = bareTopProcessRowsFromTable(pt) let boot = 0 for (const r of rows) { if (bareTopProcessPid(r) === 1) { boot = bareTopProcessStartedMs(r) break } } if (!boot) return '' return ' uptime ' + bareTopFormatProcAge(boot, nowMs) + ' (since bare-os-kernel row)' } /** * @param {Record} row */ function bareTopProcessPid(row) { const n = Number(row.pid) return Number.isFinite(n) ? n : 0 } /** * @param {Record} row * @param {'pid'|'name'|'state'|'time'|'nice'|'pri'|'cpu'} key */ /** * @param {Record} row * @param {number} wallMs */ function bareTopEffectiveCpuPct(row, wallMs) { const c = row.cpuPct ?? row.cpu if (typeof c === 'number' && Number.isFinite(c)) return c const delta = row.cpuMsDelta const w = Number(wallMs) || 0 if (typeof delta === 'number' && Number.isFinite(delta) && w > 0) return Math.min(100, Math.max(0, (delta / w) * 100)) return 0 } /** * @param {Record} row * @param {'pid'|'name'|'state'|'time'|'nice'|'pri'|'cpu'} key * @param {number} [wallMs] */ function bareTopProcessSortKey(row, key, wallMs) { if (key === 'name') return String(row.name || row.label || '').toLowerCase() if (key === 'state') return String(row.state || '').toLowerCase() if (key === 'time') return bareTopProcessStartedMs(row) if (key === 'nice') return Number(row.nice ?? row.ni) || 0 if (key === 'pri') return Number(row.pri ?? row.priority) || 0 if (key === 'cpu') return bareTopEffectiveCpuPct(row, wallMs || 0) return bareTopProcessPid(row) } /** * @param {Record} row * @param {string} pfRaw * @param {Record} env * @param {{ taggedPids?: Set, taggedOnly?: boolean }} [opts] */ function bareTopProcessRowMatchesFilter(row, pfRaw, env, opts) { const o = opts && typeof opts === 'object' ? opts : {} const tagged = o.taggedPids const taggedOnly = o.taggedOnly === true const pid = bareTopProcessPid(row) if (taggedOnly && tagged && tagged.size && !tagged.has(pid)) return false const invertBase = env.BARE_TOP_FILTER_INVERT === '1' || env.BARE_TOP_FILTER_INVERT === 'true' let pf = String(pfRaw || '').trim() let invert = invertBase if (pf.startsWith('!')) { pf = pf.slice(1).trim() invert = !invert } if (!pf) return true const regexEnv = env.BARE_TOP_FILTER_REGEX === '1' || env.BARE_TOP_FILTER_REGEX === 'true' const name = String(row.name || row.label || '') const user = String(row.user || row.identity || '') /** @type {RegExp | null} */ let re = null if (regexEnv && pf.length) { try { re = new RegExp(pf, 'i') } catch { re = null } } else if (pf.length >= 2 && pf.charAt(0) === '/') { const end = pf.lastIndexOf('/') if (end > 0) { try { const flags = pf.slice(end + 1) || 'i' re = new RegExp(pf.slice(1, end), flags) } catch { re = null } } } let match = false if (re) match = re.test(name) || re.test(user) else match = name.toLowerCase().includes(pf.toLowerCase()) return invert ? !match : match } /** * @param {Record[]} rows * @param {'pid'|'name'|'state'|'time'|'nice'|'pri'|'cpu'} sortKey * @param {boolean} asc * @param {{ tertiaryPpid?: boolean, sortWallMs?: number }} [opts] */ function bareTopSortProcessRows(rows, sortKey, asc, opts) { const o = opts && typeof opts === 'object' ? opts : {} const tertiaryPpid = o.tertiaryPpid === true const wallMs = Number(o.sortWallMs) || 0 const dir = asc ? 1 : -1 const out = rows.slice() out.sort((a, b) => { const ka = bareTopProcessSortKey(a, sortKey, wallMs) const kb = bareTopProcessSortKey(b, sortKey, wallMs) let c = 0 if (typeof ka === 'number' && typeof kb === 'number') c = ka - kb else c = String(ka).localeCompare(String(kb)) if (c !== 0) return c * dir if (tertiaryPpid) { const pa = Number(a.ppid) || 0 const pb = Number(b.ppid) || 0 if (pa !== pb) return (pa - pb) * dir } return bareTopProcessPid(a) - bareTopProcessPid(b) }) return out } /** * @param {string[]} cells * @param {number[]} widths * @param {('l'|'r')[]} align * @param {number} maxLineLen */ function bareTopFormatFixedColumns(cells, widths, align, maxLineLen) { const parts = [] let total = 0 for (let i = 0; i < cells.length && i < widths.length; i++) { const w = Math.max(1, widths[i] | 0) let c = bareTopTruncateCell(cells[i], w, true) if (c.length > w) c = c.slice(0, w) const pad = w - c.length if (align[i] === 'r') { parts.push(' '.repeat(Math.max(0, pad)) + c) } else { parts.push(c + ' '.repeat(Math.max(0, pad))) } total += w + (i < cells.length - 1 ? 1 : 0) } let line = parts.join(' ') if (line.length > maxLineLen) line = line.slice(0, maxLineLen) return line } /** * @param {number} score 0–100 * @param {number} width * @param {boolean} ascii */ function bareTopHealthBarLine(score, width, ascii) { const w = Math.max(4, width | 0) const p = Math.min(100, Math.max(0, Math.round(Number(score) || 0))) // Floor fill so the bar never reads a higher % than the integer score (round caused ~73% for 72/100). const fill = Math.min(w, Math.floor((p * w) / 100)) const full = ascii ? '#' : '\u2588' const empty = ascii ? '.' : '\u2591' let bar = '' for (let i = 0; i < w; i++) bar += i < fill ? full : empty return String(p) + '/100 ' + bar } /** * @param {string} title * @param {number} cols */ function bareTopSectionRule(title, cols) { const t = String(title || '').trim() const c = Math.max(8, cols | 0) const inner = c - 4 if (inner < 4) return t const pad = inner - t.length if (pad <= 0) return '\u2500 ' + t.slice(0, inner - 2) + ' \u2500' const left = Math.floor(pad / 2) const right = pad - left return ( '\u2500'.repeat(Math.max(1, left)) + ' ' + t + ' ' + '\u2500'.repeat(Math.max(1, right)) ) } /** * Strip C0 controls except tab; keep printable + UTF-8 continuation for display safety. * @param {string} s */ function bareTopSanitizeVisible(s) { let t = String(s) let o = '' for (let i = 0; i < t.length; i++) { const c = t.charCodeAt(i) if (c === 9 || c === 10 || c === 13) o += t[i] else if (c < 32) o += ' ' else o += t[i] } return o } /** * @param {number} scrollTop * @param {number} vis * @param {string[]} wrapped * @returns {string[]} visible slice (copy) */ function bareTopScrollSliceLines(wrapped, scrollTop, vis) { const v = Math.max(1, vis | 0) const s = Math.max(0, scrollTop | 0) return wrapped.slice(s, s + v) } /** * @param {unknown} row * @returns {number} */ function bareTopProcessStartedMs(row) { if (!row || typeof row !== 'object') return 0 const n = Number(/** @type {Record} */ (row).startedAtMs) return Number.isFinite(n) ? n : 0 } /** * @param {number} startedAtMs * @param {number} nowMs */ function bareTopPad2(n) { const x = n | 0 return x < 10 ? '0' + x : String(x) } function bareTopFormatProcAge(startedAtMs, nowMs) { const t0 = Number(startedAtMs) const now = Number(nowMs) if (!Number.isFinite(t0) || t0 <= 0 || !Number.isFinite(now)) return '' let sec = Math.max(0, Math.floor((now - t0) / 1000)) if (sec < 3600) { const m = Math.floor(sec / 60) const s = sec % 60 return m > 0 ? m + ':' + bareTopPad2(s) : String(s) + 's' } const h = Math.floor(sec / 3600) sec %= 3600 const m = Math.floor(sec / 60) const s = sec % 60 return h + ':' + bareTopPad2(m) + ':' + bareTopPad2(s) } /** * Tree order: depth-first by ppid, stable by pid. * @param {Record[]} rows */ function bareTopProcessTreeOrder(rows) { const byPid = new Map() for (const r of rows) { const p = bareTopProcessPid(r) if (p) byPid.set(p, r) } const children = new Map() for (const r of rows) { const ppid = Number(r.ppid) || 0 if (!children.has(ppid)) children.set(ppid, []) children.get(ppid).push(r) } for (const arr of children.values()) { arr.sort((a, b) => bareTopProcessPid(a) - bareTopProcessPid(b)) } /** @type {Record[]} */ const out = [] function walk(pid, depth) { const ch = children.get(pid) if (!ch) return for (const r of ch) { out.push(Object.assign({}, r, { _treeDepth: depth })) walk(bareTopProcessPid(r), depth + 1) } } walk(0, 0) for (const r of rows) { const p = bareTopProcessPid(r) const pp = Number(r.ppid) || 0 if (!byPid.has(pp) && pp === 0 && p) { /* orphan roots not reached */ } } if (out.length === 0) return rows.map((r) => Object.assign({}, r, { _treeDepth: 0 })) return out } /** * @param {string} title * @param {number} cols * @param {boolean} ascii */ function bareTopSectionSeparator(title, cols, ascii) { const rule = bareTopSectionRule(title, cols) if (ascii) return '- ' + String(title || '') + ' ' + '-'.repeat(Math.max(4, cols - title.length - 4)) return rule } /** * @param {unknown} o * @param {number} maxRows * @param {number} keyW */ function bareTopDelegateInflightTableLines(o, maxRows, keyW) { if (!o || typeof o !== 'object') return [] const rec = /** @type {Record} */ (o) const pairs = Object.keys(rec) .map((k) => ({ k, v: Number(rec[k]) || 0 })) .sort((a, b) => b.v - a.v || a.k.localeCompare(b.k)) const lim = Math.min(maxRows, pairs.length) /** @type {string[]} */ const out = [] const kw = Math.max(6, keyW | 0) const vw = 8 for (let i = 0; i < lim; i++) { const { k, v } = pairs[i] const ks = bareTopTruncateCell(k, kw, true) const padK = ' '.repeat(Math.max(0, kw - ks.length)) const vs = String(v).padStart(vw, ' ') out.push(' ' + ks + padK + ' ' + vs) } if (pairs.length > lim) out.push(' … +' + (pairs.length - lim) + ' more keys') return out } /** * @param {unknown} o * @param {number} cols */ /** * @param {Record} snap */ function bareTopLimitsMergedLines(snap) { /** @type {string[]} */ const lines = [] const q = snap && snap.extra && snap.extra.quotas && typeof snap.extra.quotas === 'object' ? /** @type {Record} */ (snap.extra.quotas) : null const r = snap && snap.extra && snap.extra.rlimits && typeof snap.extra.rlimits === 'object' ? /** @type {Record} */ (snap.extra.rlimits) : null function row(prefix, o2, max) { const keys = Object.keys(o2).slice(0, max) for (const k of keys) { const v = o2[k] const s = v != null && typeof v !== 'object' ? String(v) : v && typeof v === 'object' ? '{…}' : '' lines.push(prefix + k + '=' + bareTopTruncateCell(s, 56, false)) } } if (q) { lines.push(' quotas (operator caps — full JSON on catalog tab)') row(' ', q, 8) } if (r) { lines.push(' rlimits (process limits mirror)') row(' ', r, 8) } return lines } /** * @param {unknown} v * @param {number} maxChars */ function bareTopJsonSnippet(v, maxChars) { const m = Math.max(8, maxChars | 0) try { const s = JSON.stringify(v) if (s.length <= m) return s return s.slice(0, Math.max(4, m - 18)) + '\u2026(truncated)' } catch { return '(unserializable)' } } /** * @param {unknown} v * @param {number} maxLen */ function bareTopFormatScalarTerminal(v, maxLen) { const m = Math.max(4, maxLen | 0) if (v == null) return '' const t = typeof v if (t === 'string' || t === 'number' || t === 'boolean' || t === 'bigint') return bareTopTruncateCell(String(v), m, false) if (t === 'object') return bareTopJsonSnippet(v, m) return bareTopTruncateCell(String(v), m, false) } /** * Format integers with grouped thousands (en-US style). * @param {unknown} n */ function bareTopFormatIntGrouped(n) { const x = Number(n) if (!Number.isFinite(x)) return String(n) const neg = x < 0 const i = Math.floor(Math.abs(x)) const s = String(i) const g = [] for (let k = s.length; k > 0; k -= 3) g.push(s.slice(Math.max(0, k - 3), k)) return (neg ? '-' : '') + g.reverse().join(',') } /** * Human duration for millisecond-ish counters (shared UI). * @param {unknown} ms */ function bareTopFormatDurationMs(ms) { const t = Number(ms) if (!Number.isFinite(t) || t < 0) return '' if (t < 1000) return Math.round(t) + 'ms' if (t < 60000) return (t / 1000).toFixed(1) + 's' if (t < 3600000) return Math.floor(t / 60000) + 'm' return Math.floor(t / 3600000) + 'h' } /** * Rx/tx bytes: use global bareTopFormatBytes from baretop-snapshot preamble when bundled. * @param {unknown} n */ function bareTopNetFormatBytes(n) { if (typeof bareTopFormatBytes === 'function') return bareTopFormatBytes(n) const x = Number(n) if (!Number.isFinite(x) || x < 0) return '\u2014' 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 {Record} o */ function bareTopNetOrderedTopKeys(o) { const priority = [ 'schema', 'schemaVersion', 'topicHex', 'peerCount', 'replicationQueueDepth', 'peerFirewallAcceptedTotal', 'peerFirewallRejectedTotal', 'peerFirewallInboundTotal', 'peerFirewallOutboundTotal', 'seedHandshakeError', 'seedRole', 'atMs' ] const seen = new Set() /** @type {string[]} */ const out = [] for (const k of priority) { if (Object.prototype.hasOwnProperty.call(o, k) && o[k] != null) { out.push(k) seen.add(k) } } const rest = Object.keys(o) .filter((k) => !seen.has(k)) .sort((a, b) => a.localeCompare(b)) return out.concat(rest) } /** * @param {unknown} q * @param {number} cols */ function bareTopNetReplicationQueueLines(q, cols) { if (!q || typeof q !== 'object') return [' (null)'] const o = /** @type {Record} */ (q) /** @type {string[]} */ const lines = [] const scalars = [ 'depth', 'pending', 'pendingCount', 'length', 'size', 'backlog', 'queued', 'inFlight', 'lastError', 'error', 'note', 'atMs' ] for (const k of scalars) { if (!Object.prototype.hasOwnProperty.call(o, k)) continue const v = o[k] if (v != null && typeof v !== 'object') lines.push(' ' + k + ': ' + String(v)) } for (const ak of ['items', 'queue', 'entries', 'pendingJobs']) { if (Array.isArray(o[ak])) lines.push(' ' + ak + ': ' + o[ak].length + ' entries') } const nestedKeys = Object.keys(o).filter((k) => { const v = o[k] return v != null && typeof v === 'object' }) for (const nk of nestedKeys.slice(0, 6)) { lines.push(' ' + nk + ': ' + bareTopJsonSnippet(o[nk], Math.max(24, cols - 14))) } if (!lines.length) lines.push(' ' + bareTopJsonSnippet(o, Math.max(40, cols - 6))) return lines } /** * peer_firewall_stats / channel.js firewall shape: acceptedSessionCount, rejectedSessionCount, etc. * @param {unknown} fw * @param {number} cols */ function bareTopNetPeerFirewallLines(fw, cols) { if (!fw || typeof fw !== 'object') return [' (null)'] const o = /** @type {Record} */ (fw) /** @type {string[]} */ const lines = [] const counts = [ 'acceptedSessionCount', 'rejectedSessionCount', 'inboundSessionCount', 'outboundSessionCount' ] for (const k of counts) { if (typeof o[k] === 'number') lines.push(' ' + k + ': ' + bareTopFormatIntGrouped(o[k])) } if (typeof o.error === 'string' && o.error.trim()) lines.push(' error: ' + bareTopTruncateCell(o.error, cols - 8, true)) if (typeof o.note === 'string' && o.note.trim()) lines.push(' note: ' + bareTopTruncateCell(o.note, cols - 8, true)) for (const extra of ['transportBreakdown', 'saturationClass', 'wave9', 'wave10']) { if (o[extra] != null && typeof o[extra] === 'object') lines.push( ' ' + extra + ': ' + bareTopJsonSnippet(o[extra], Math.max(32, cols - 14)) ) } if (!lines.length) lines.push(' ' + bareTopJsonSnippet(o, Math.max(40, cols - 6))) return lines } /** * @param {unknown} b * @param {number} cols */ function bareTopNetBsdBridgeLines(b, cols) { if (!b || typeof b !== 'object') return [' (null)'] const o = /** @type {Record} */ (b) /** @type {string[]} */ const lines = [] for (const k of [ 'schema', 'policyEnv', 'activeBridgedFds', 'bridgeErrors', 'guestSocketCount' ]) { if (!Object.prototype.hasOwnProperty.call(o, k)) continue const v = o[k] if (v != null && typeof v !== 'object') lines.push(' ' + k + ': ' + String(v)) } if (typeof o.note === 'string' && o.note.trim()) lines.push( ' note: ' + bareTopTruncateCell(String(o.note), Math.max(20, cols - 10), true) ) const rest = Object.keys(o).filter( (k) => !['schema', 'policyEnv', 'note', 'activeBridgedFds', 'bridgeErrors', 'guestSocketCount'].includes(k) ) for (const k of rest.slice(0, 8)) { const v = o[k] lines.push( ' ' + k + ': ' + bareTopFormatScalarTerminal(v, Math.max(16, cols - 12)) ) } return lines.length ? lines : [' (empty)'] } /** * @param {unknown} net * @param {unknown} metricsLive */ function bareTopNetHealthOverviewLines(net, metricsLive) { /** @type {string[]} */ const parts = [] const peersMl = metricsLive && typeof metricsLive === 'object' ? Number(/** @type {Record} */ (metricsLive).peers) : NaN if (Number.isFinite(peersMl)) parts.push('peers_ml=' + bareTopFormatIntGrouped(peersMl)) if (net && typeof net === 'object') { const o = /** @type {Record} */ (net) if (o.topicHex != null) parts.push('topic=' + String(o.topicHex).slice(0, 16)) if (typeof o.peerCount === 'number') parts.push('peers_net=' + bareTopFormatIntGrouped(o.peerCount)) const err = o.seedHandshakeError if (err != null && String(err).trim()) parts.push('seedErr=' + bareTopTruncateCell(String(err), 36, true)) const rd = o.replicationQueueDepth if (typeof rd === 'number') parts.push('replQ=' + bareTopFormatIntGrouped(rd)) } if (!parts.length) return [] return [' net ' + parts.join(' ') + ' (tab: network)'] } /** * Consolidated HDMS + DHT line for overview (traffic-light style). * @param {Record} snap */ function bareTopHdmsDhtTrafficLine(snap) { const hd = snap && snap.extra && snap.extra.hdmsHealth && typeof snap.extra.hdmsHealth === 'object' ? /** @type {Record} */ (snap.extra.hdmsHealth) : null const dh = snap && snap.extra && snap.extra.dhtStatus && typeof snap.extra.dhtStatus === 'object' ? /** @type {Record} */ (snap.extra.dhtStatus) : null if (!hd && !dh) return '' function grade(o) { if (!o) return '?' const e = o.error ?? o.err if (e != null && String(e).trim()) return 'R' const ok = o.ok ?? o.healthy ?? o.ready if (ok === false) return 'R' if (ok === true) return 'G' return 'Y' } const bits = [] if (hd) { bits.push('hdms[' + grade(hd) + ']=' + bareTopP2PKvString(hd, 5)) } if (dh) { bits.push('dht[' + grade(dh) + ']=' + bareTopP2PKvString(dh, 5)) } return ' p2p ' + bits.join(' ') } /** * @param {Record} o * @param {number} maxKeys */ function bareTopP2PKvString(o, maxKeys) { return Object.keys(o) .slice(0, maxKeys) .map((k) => { const v = o[k] return k + '=' + (v != null && typeof v !== 'object' ? String(v) : bareTopJsonSnippet(v, 32)) }) .join(' ') } function bareTopP2PStackLines(snap) { /** @type {string[]} */ const out = [] const traffic = bareTopHdmsDhtTrafficLine(/** @type {Record} */ (snap)) if (traffic) { out.push(traffic) return out } const hd = snap && snap.extra && snap.extra.hdmsHealth && typeof snap.extra.hdmsHealth === 'object' ? /** @type {Record} */ (snap.extra.hdmsHealth) : null const dh = snap && snap.extra && snap.extra.dhtStatus && typeof snap.extra.dhtStatus === 'object' ? /** @type {Record} */ (snap.extra.dhtStatus) : null if (hd) out.push(' hdms ' + bareTopTruncateCell(bareTopP2PKvString(hd, 8), 100, false)) if (dh) out.push(' dht ' + bareTopTruncateCell(bareTopP2PKvString(dh, 8), 100, false)) return out } /** * @typedef {{ maxLines?: number, filter?: string, wideTwoCol?: boolean }} BareTopNetTabOpts * @param {unknown} net * @param {number} cols * @param {BareTopNetTabOpts | null} [opts] */ function bareTopNetTabLines(net, cols, opts) { const oopts = opts && typeof opts === 'object' ? opts : {} const maxTotal = Math.max(8, (oopts.maxLines | 0) || 512) const filt = String(oopts.filter || '') .trim() .toLowerCase() const wide = oopts.wideTwoCol === true && cols >= 100 if (!net || typeof net !== 'object') return [' N/A'] const o = /** @type {Record} */ (net) /** @type {string[]} */ const lines = [] function pushFiltered(s) { if (filt && !String(s).toLowerCase().includes(filt)) return lines.push(s) } function pushSection(title) { pushFiltered('') pushFiltered(' --- ' + title + ' ---') } if (Array.isArray(o.interfaces)) { pushSection('interfaces (rx+tx)') const arr = o.interfaces.slice().sort((a, b) => { const ta = a && typeof a === 'object' ? /** @type {Record} */ (a) : null const tb = b && typeof b === 'object' ? /** @type {Record} */ (b) : null const sa = (Number(ta && ta.rxBytes) || 0) + (Number(ta && ta.txBytes) || 0) const sb = (Number(tb && tb.rxBytes) || 0) + (Number(tb && tb.txBytes) || 0) return sb - sa }) const w = Math.max(40, cols - 4) for (const iface of arr.slice(0, 32)) { if (!iface || typeof iface !== 'object') continue const i = /** @type {Record} */ (iface) const nm = String(i.name ?? i.if ?? '?') const rxB = i.rxBytes ?? i.rx const txB = i.txBytes ?? i.tx const rxS = rxB != null && rxB !== '' && Number.isFinite(Number(rxB)) ? bareTopNetFormatBytes(rxB) : String(rxB ?? '\u2014') const txS = txB != null && txB !== '' && Number.isFinite(Number(txB)) ? bareTopNetFormatBytes(txB) : String(txB ?? '\u2014') const extra = Object.keys(i) .filter((k) => !['name', 'if', 'rxBytes', 'txBytes', 'rx', 'tx'].includes(k)) .slice(0, 4) .map((k) => { const v = i[k] return ( k + '=' + (v != null && typeof v !== 'object' ? String(v) : bareTopJsonSnippet(v, 36)) ) }) .join(' ') pushFiltered( ' ' + bareTopTruncateCell(nm, 16, true) + ' rx=' + rxS + ' tx=' + txS + ' ' + bareTopTruncateCell(extra, w - 24, false) ) } } const sectionKeys = new Set([ 'interfaces', 'replicationQueue', 'peerFirewallStats', 'bsdSocketGuestBridge', 'transport', 'udxTuning', 'hyperswarmTuning', 'peerFirewallE2e', 'stagingSlot', 'snapshotHints' ]) pushSection('summary') const ordered = bareTopNetOrderedTopKeys(o) for (const k of ordered) { if (sectionKeys.has(k)) continue const v = o[k] if (v != null && typeof v === 'object') continue pushFiltered( ' ' + k + ': ' + bareTopTruncateCell(bareTopFormatScalarTerminal(v, Math.max(24, cols - 8)), Math.max(20, cols - 6), false) ) } if (o.replicationQueue != null) { pushSection('replicationQueue') for (const ln of bareTopNetReplicationQueueLines(o.replicationQueue, cols)) pushFiltered(ln) } if (o.peerFirewallStats != null) { pushSection('peerFirewallStats') for (const ln of bareTopNetPeerFirewallLines(o.peerFirewallStats, cols)) pushFiltered(ln) } if (o.bsdSocketGuestBridge != null) { pushSection('bsdSocketGuestBridge') for (const ln of bareTopNetBsdBridgeLines(o.bsdSocketGuestBridge, cols)) pushFiltered(ln) } const tun = ['transport', 'udxTuning', 'hyperswarmTuning', 'peerFirewallE2e'] for (const tk of tun) { if (o[tk] == null) continue pushSection(tk) const v = o[tk] if (v != null && typeof v === 'object') { const jo = /** @type {Record} */ (v) const keys = Object.keys(jo).slice(0, 24) for (const kk of keys) { const vv = jo[kk] pushFiltered( ' ' + kk + ': ' + bareTopFormatScalarTerminal(vv, Math.max(16, cols - 10)) ) } } else pushFiltered(' ' + String(v)) } for (const ek of ['stagingSlot', 'snapshotHints']) { if (o[ek] == null) continue pushSection(ek) const v = o[ek] if (v != null && typeof v === 'object') { const jo = /** @type {Record} */ (v) for (const kk of Object.keys(jo).slice(0, 20)) { const vv = jo[kk] pushFiltered( ' ' + kk + ': ' + bareTopFormatScalarTerminal(vv, Math.max(12, cols - 12)) ) } } } if (wide && lines.length > 6) { /** @type {string[]} */ const merged = [] for (let i = 0; i < lines.length; i += 2) { const a = lines[i] || '' const b = lines[i + 1] || '' const half = Math.floor(cols / 2) - 1 merged.push( bareTopTruncateCell(a, half, false) + ' | ' + bareTopTruncateCell(b, half, false) ) } lines.length = 0 lines.push(...merged) } if (lines.length > maxTotal) { const head = lines.slice(0, maxTotal - 1) head.push(' \u2026 (truncated, ' + (lines.length - maxTotal + 1) + ' more lines)') return head } return lines.length ? lines : [' (empty net summary)'] } function bareTopDelegateRateBucketLines(o, cols) { if (!o || typeof o !== 'object') return [] const rec = /** @type {Record} */ (o) const keys = Object.keys(rec).sort() if (!keys.length) return [] /** @type {string[]} */ const out = [] const w = Math.max(40, Math.min(cols - 2, cols)) for (const k of keys) { const v = rec[k] if (Array.isArray(v)) { const nums = v.map((x) => String(x)).join(' ') out.push( ' ' + bareTopTruncateCell(k, Math.min(20, Math.floor(w * 0.35)), true).padEnd(20) + ' ' + bareTopTruncateCell(nums, w - 24, false) ) } else if (v != null && typeof v === 'object') { out.push( ' ' + k + ': ' + bareTopJsonSnippet(v, Math.min(120, w - k.length - 4)) ) } else { out.push(' ' + k + ': ' + String(v)) } } return out } /** * Kernel counter keys emitted by the booter (inventory for dashboards): * initd.unit_failed_final, vfs.readfile.samples, vfs.replication_warm_full_invalidate, * vfs.warm_read_cache_*, security.key_handle_*, audit.append*, operator.corestore_snapshot_hint, * vfs.hyperblobs_dedup_* — see bareOsKernelMetricInc call sites in packages/bare-os-booter. * @param {unknown} counters * @param {unknown} prev * @param {boolean} deltaMode * @param {number} topN * @param {number} colW */ function bareTopKernelCounterLines(counters, prev, deltaMode, topN, colW) { if (!counters || typeof counters !== 'object') return [] const o = /** @type {Record} */ (counters) const po = prev && typeof prev === 'object' ? /** @type {Record} */ (prev) : null const pairs = Object.keys(o) .map((k) => ({ k, v: Number(o[k]) || 0 })) .sort((a, b) => b.v - a.v || a.k.localeCompare(b.k)) const lim = Math.min(Math.max(1, topN | 0), pairs.length) /** @type {string[]} */ const out = [] const wn = Math.max(6, colW | 0) for (let i = 0; i < lim; i++) { const { k, v } = pairs[i] let suf = '' if (deltaMode && po && Object.prototype.hasOwnProperty.call(po, k)) { const d = v - (Number(po[k]) || 0) suf = ' (d ' + (d >= 0 ? '+' : '') + d + ')' } const kt = bareTopTruncateCell(k, wn - 1, true) const kPad = kt + ' '.repeat(Math.max(0, wn - kt.length)) out.push(' ' + kPad + ' ' + String(v) + suf) } if (pairs.length > lim) out.push(' … +' + (pairs.length - lim) + ' counters') return out } /** * @param {unknown} w */ function bareTopWarmReadCacheSummaryLines(w) { if (!w || typeof w !== 'object') return [] const o = /** @type {Record} */ (w) const parts = [] for (const key of ['entries', 'bytes', 'hits', 'misses', 'evictions']) { if (o[key] != null) parts.push(key + '=' + String(o[key])) } if (!parts.length) { const keys = Object.keys(o).slice(0, 8) for (const k of keys) { const v = o[k] if (v != null && typeof v !== 'object') parts.push(k + '=' + String(v)) } } if (!parts.length) return [] return [' ' + parts.join(' ')] } /** * @param {unknown} ipc * @param {number} maxLines */ function bareTopIpcTelemetrySummaryLines(ipc, maxLines) { if (!ipc || typeof ipc !== 'object') return [] const o = /** @type {Record} */ (ipc) /** @type {string[]} */ const lines = [] for (const k of Object.keys(o).slice(0, 24)) { const v = o[k] if (v != null && typeof v !== 'object') { lines.push(' ' + k + ': ' + String(v)) } else if (v && typeof v === 'object' && !Array.isArray(v)) { const sub = /** @type {Record} */ (v) const inner = Object.keys(sub) .slice(0, 6) .map( (sk) => sk + '=' + (sub[sk] != null && typeof sub[sk] !== 'object' ? String(sub[sk]) : bareTopJsonSnippet(sub[sk], 24)) ) .join(' ') lines.push(' ' + k + ': ' + bareTopTruncateCell(inner, 72, true)) } if (lines.length >= maxLines) break } if (lines.length >= maxLines && Object.keys(o).length > maxLines) lines.push(' … (truncated)') return lines } /** * @param {unknown} rep * @param {string} na */ function bareTopReplicationOverviewLines(rep, na) { if (!rep || typeof rep !== 'object') return [] const o = /** @type {Record} */ (rep) /** @type {string[]} */ const lines = [] const sys = o.systemCoreLength const per = o.personalCoreLength const aux = o.auxiliaryDriveCount const pc = o.peerCount if (typeof sys === 'number') lines.push(' sysCoreLen ' + sys) if (typeof per === 'number') lines.push(' perCoreLen ' + per) if (typeof aux === 'number') lines.push(' auxDrives ' + aux) if (typeof pc === 'number') lines.push(' peerCount ' + pc) const sh = o.stallHint if (sh != null && String(sh).trim()) { lines.push(' stallHint ' + String(sh)) const sl = String(sh).trim().toLowerCase() if (sl === 'no_peers' || sl === 'length_unavailable' || sl === 'ok') lines.push( ' (hint is operational — health score treats these as non-fault)' ) } if (!lines.length) lines.push(' ' + na) return lines } /** * @param {unknown} cold * @param {unknown} stdlib * @param {unknown} tel */ function bareTopBootBudgetLines(cold, stdlib, tel) { /** @type {string[]} */ const lines = [] const c = cold && typeof cold === 'object' ? /** @type {Record} */ (cold) : null const s = stdlib && typeof stdlib === 'object' ? /** @type {Record} */ (stdlib) : null if (c) { const ex = c.exceeded === true lines.push( ' coldBoot exceeded=' + (ex ? 'yes' : 'no') + ' wall=' + String(c.wallMs ?? '—') + ' limit=' + String(c.limitMs ?? '—') ) } if (s) { const ex = s.exceeded === true lines.push( ' bareStdlib exceeded=' + (ex ? 'yes' : 'no') + ' wall=' + String(s.wallMs ?? '—') + ' limit=' + String(s.limitMs ?? '—') ) } if (tel && typeof tel === 'object') { const t = /** @type {Record} */ (tel) lines.push( ' policy strict=' + String(!!t.strict) + ' bootPerf=' + String(t.bootPerfJsonPath || '/run/bare-os/boot-perf.json') ) } return lines } /** * @param {unknown} sl */ function bareTopSwarmLifecycleLine(sl) { if (!sl || typeof sl !== 'object') return '' const o = /** @type {Record} */ (sl) const parts = [] for (const k of Object.keys(o).slice(0, 12)) { const v = o[k] if (v != null && typeof v !== 'object') parts.push(k + '=' + v) } if (!parts.length) return '' return ' swarm ' + parts.join(' ') + ' (tab: operator)' } /** * @param {unknown} pt */ function bareTopProcessStateHistogramLines(pt) { const rows = bareTopProcessRowsFromTable(pt) /** @type {Record} */ const h = {} for (const r of rows) { const st = String(r.state || '?') h[st] = (h[st] || 0) + 1 } const parts = Object.keys(h) .sort() .map((k) => k + ':' + h[k]) if (!parts.length) return [] return [' states ' + parts.join(' ')] } /** * @param {unknown} ir */ function bareTopInitdReadinessSummaryLines(ir) { if (!ir || typeof ir !== 'object') return [] const o = /** @type {Record} */ (ir) const units = Array.isArray(o.units) ? o.units : [] let active = 0 let starting = 0 let failed = 0 let other = 0 for (const u of units) { if (!u || typeof u !== 'object') continue const ph = String(/** @type {Record} */ (u).phase || '') if (ph === 'active') active++ else if (ph === 'starting') starting++ else if (ph === 'failed') failed++ else other++ } if (!units.length) return [] return [ ' initd units=' + units.length + ' active=' + active + ' starting=' + starting + ' failed=' + failed + (other ? ' other=' + other : '') ] } /** * @param {unknown} m metrics live * @param {number} nowMs * @param {string} na */ function bareTopMetaCoalesceLine(m, nowMs, na) { if (!m || typeof m !== 'object') return '' const o = /** @type {Record} */ (m) const co = o.coalesceMs const at = o.atMs let age = '' if (typeof at === 'number' && Number.isFinite(at)) { age = ' age=' + Math.max(0, Math.floor(nowMs - at)) + 'ms' } return ( ' metrics coalesceMs=' + (typeof co === 'number' ? co : na) + ' atMs=' + (typeof at === 'number' ? at : na) + age ) } /** * @param {unknown} wbh */ function bareTopWorkerBudgetCompactLine(wbh) { if (!wbh || typeof wbh !== 'object') return '' const o = /** @type {Record} */ (wbh) const parts = [] if (o.wallMsMax != null) parts.push('wallMax=' + String(o.wallMsMax)) if (o.cpuMsMax != null) parts.push('cpuMax=' + String(o.cpuMsMax)) if (!parts.length) return '' return ' workerBudget ' + parts.join(' ') } /** * @param {unknown} cs collaboration session object */ function bareTopProtomuxCollabLine(cs) { if (!cs || typeof cs !== 'object') return '' const o = /** @type {Record} */ (cs) const rx = o.protomuxAppChannelRxTotal const cap = o.protomuxCapChannelRxTotal const parts = [] if (typeof rx === 'number') parts.push('appRx=' + rx) if (typeof cap === 'number') parts.push('capRx=' + cap) if (!parts.length) return '' return ' protomux ' + parts.join(' ') } /** * @param {unknown} pt */ function bareTopLogicalFdAggregateLine(pt) { const rows = bareTopProcessRowsFromTable(pt) let n = 0 for (const r of rows) { const ft = r.fdTable if (Array.isArray(ft)) n += ft.length } if (!n) return '' return ' logicalFds rows=' + n + ' (fdTable entries)' } /** * @param {unknown} hostStats */ function bareTopHostStatsSummaryLine(hostStats) { if (!hostStats || typeof hostStats !== 'object') return '' const o = /** @type {Record} */ (hostStats) const pick = [ 'cpuBusyPct', 'cpuPct', 'memUsedPct', 'rssBytes', 'hostname', 'eventLoopLagMs', 'loopLagMs', 'lagMs' ] const parts = [] for (const k of pick) { if (o[k] != null && typeof o[k] !== 'object') parts.push(k + '=' + String(o[k])) } if (!parts.length) return '' return ' host ' + parts.join(' ') + ' (detail: host tab)' } /** * @param {unknown} resources * @param {number} maxKeys */ function bareTopResourcesSummaryLines(resources, maxKeys) { if (!resources || typeof resources !== 'object') return [] const o = /** @type {Record} */ (resources) const keys = Object.keys(o).slice(0, Math.max(1, maxKeys | 0)) /** @type {string[]} */ const lines = [] for (const k of keys) { const v = o[k] if (v != null && typeof v !== 'object') lines.push(' ' + k + ': ' + String(v).slice(0, 120)) } if (Object.keys(o).length > keys.length) lines.push(' … +' + (Object.keys(o).length - keys.length) + ' keys (full: diagnostics)') return lines } /** * @param {number} peersMl peers from metrics_live * @param {unknown} repLive replicationLive object */ function bareTopPeerMismatchWarningLine(peersMl, repLive) { if (!repLive || typeof repLive !== 'object') return '' const o = /** @type {Record} */ (repLive) const pc = o.peerCount if (typeof pc !== 'number' || !Number.isFinite(peersMl)) return '' if (Math.abs(pc - peersMl) > 2) return ' warn peerCount mismatch metrics peers=' + peersMl + ' replication.peerCount=' + pc return '' } /** * @param {unknown} extraIpc from /proc/bare_os/ipc_backpressure.json */ function bareTopIpcBackpressureProcLine(extraIpc) { if (!extraIpc || typeof extraIpc !== 'object') return '' const o = /** @type {Record} */ (extraIpc) const parts = [] for (const k of Object.keys(o).slice(0, 14)) { const v = o[k] if (typeof v === 'number' && v !== 0) parts.push(k + '=' + bareTopFormatIntGrouped(v)) else if (v === true) parts.push(k) } if (!parts.length) return '' return ' ipcBackpressure(proc) ' + parts.join(' ') } /** * @param {unknown} syscallsParsed */ function bareTopSyscallsOverviewLines(syscallsParsed) { if (!syscallsParsed || typeof syscallsParsed !== 'object') return [] const o = /** @type {Record} */ (syscallsParsed) const ops = o.ops const n = Array.isArray(ops) ? ops.length : 0 return [ ' syscalls schemaVersion=' + String(o.schemaVersion ?? '?') + ' stockOps~' + n ] } /** * @param {string} diskRaw */ function bareTopDiskstatsOverviewLines(diskRaw) { const s = String(diskRaw || '').trim() if (!s) return [] const first = s.split('\n')[0] || '' if (first.length < 6) return [] return [' diskstats ' + bareTopTruncateCell(first, 110, true)] } /** * @param {unknown} dr delegate_red.json */ function bareTopDelegateRedOverviewLines(dr) { if (!dr || typeof dr !== 'object') return [] if (!Object.keys(dr).length) return [] return [ ' delegate_red ' + bareTopTruncateCell(bareTopJsonSnippet(dr, 220), 118, true) ] } /** * @param {unknown} sp security_posture.json */ function bareTopSecurityPostureOverviewLines(sp) { if (!sp || typeof sp !== 'object') return [] const o = /** @type {Record} */ (sp) const parts = [] if (typeof o.schema === 'number') parts.push('schema=' + o.schema) if (o.bootPolicyStrict != null) parts.push('bootStrict=' + String(o.bootPolicyStrict)) if (o.bootPolicyMerged != null) parts.push('bootPol=' + String(o.bootPolicyMerged)) if (o.ctxApiVersion != null) parts.push('ctxApi=' + String(o.ctxApiVersion).slice(0, 20)) return parts.length ? [' security_posture ' + parts.join(' ')] : [] } /** * @param {unknown} bb boot_budget_summary.json */ function bareTopBootBudgetSummaryOverviewLines(bb) { if (!bb || typeof bb !== 'object') return [] const o = /** @type {Record} */ (bb) const parts = [] const crit = o.criticalPathMs ?? o.criticalPathWallMs ?? o.totalWallMs if (typeof crit === 'number') parts.push('critical~' + bareTopFormatDurationMs(crit)) const top = o.slowestUnit ?? o.criticalUnit ?? o.topUnit ?? o.bottleneck if (top != null) parts.push('top=' + String(top).slice(0, 28)) if (o.exceeded === true) parts.push('EXCEEDED') return parts.length ? [' boot_budget_summary ' + parts.join(' ')] : [] } /** * @param {unknown} ph peer_health.json */ function bareTopWorstPeerOverviewLines(ph) { if (!ph || typeof ph !== 'object') return [] const peers = /** @type {Record} */ (ph).peers if (!Array.isArray(peers) || !peers.length) return [] let worst = /** @type {Record | null} */ (null) let worstScore = -1 for (const p of peers) { if (!p || typeof p !== 'object') continue const r = /** @type {Record} */ (p) const err = r.error ?? r.lastError ?? r.status const lat = Number(r.rttMs ?? r.latencyMs ?? r.rtt ?? 0) const sc = err != null && String(err).trim() ? 1e12 : Number.isFinite(lat) ? lat : 0 if (sc > worstScore) { worstScore = sc worst = r } } if (!worst) return [] const id = String( worst.publicKeyHex?.slice?.(0, 14) || worst.publicKey?.slice?.(0, 14) || worst.peerId || worst.id || '?' ) const tail = bareTopJsonSnippet(worst, 72) return [' worstPeer ' + id + ' ' + tail] } /** * @param {unknown} ext extensions.json */ function bareTopExtensionsOverviewLines(ext) { if (!ext || typeof ext !== 'object') return [] const root = /** @type {Record} */ (ext) const list = root.extensions ?? root.names ?? root.list const names = Array.isArray(list) ? list .map((x) => { if (x && typeof x === 'object') return String( /** @type {Record} */ (x).name ?? /** @type {Record} */ (x).id ?? '' ) return String(x) }) .filter(Boolean) : [] const head = names.slice(0, 5).join(', ') return [ ' extensions count=' + names.length + (head ? ' e.g. ' + bareTopTruncateCell(head, 56, true) : '') ] } /** * @param {unknown} cj capabilities.json * @param {unknown} cn capabilities node JSON */ function bareTopCapabilitiesDiffOverviewLines(cj, cn) { if (!cj || typeof cj !== 'object' || !cn || typeof cn !== 'object') return [] const a = /** @type {Record} */ (cj) const b = /** @type {Record} */ (cn) const ka = new Set(Object.keys(a)) const kb = new Set(Object.keys(b)) const onlyA = [...ka].filter((k) => !kb.has(k)).slice(0, 8) const onlyB = [...kb].filter((k) => !ka.has(k)).slice(0, 8) /** @type {string[]} */ const out = [] if (onlyA.length) out.push(' cap onlyInJson: ' + onlyA.join(', ')) if (onlyB.length) out.push(' cap onlyInNode: ' + onlyB.join(', ')) if (!out.length) out.push(' cap seed JSON vs /capabilities: same top-level keys (sampled)') return out } /** * @param {unknown} gitDel * @param {unknown} gitLfs * @param {unknown} prevDel * @param {unknown} prevLfs */ function bareTopGitStatsDeltaOverviewLines(gitDel, gitLfs, prevDel, prevLfs) { function pickNum(o, keys) { if (!o || typeof o !== 'object') return null const r = /** @type {Record} */ (o) for (const k of keys) { const v = r[k] if (typeof v === 'number' && Number.isFinite(v)) return v } return null } /** @type {string[]} */ const out = [] const c1 = pickNum(gitDel, ['delegatesStarted', 'total', 'invocations']) const p1 = pickNum(prevDel, ['delegatesStarted', 'total', 'invocations']) if (c1 != null && p1 != null) out.push(' gitDelegate d=' + (c1 - p1)) const c2 = pickNum(gitLfs, ['pointersResolved', 'resolved', 'total']) const p2 = pickNum(prevLfs, ['pointersResolved', 'resolved', 'total']) if (c2 != null && p2 != null) out.push(' gitLfs d=' + (c2 - p2)) return out } /** * @param {unknown} wb worker_budget.json * @param {unknown} sb sandbox_profile.json */ function bareTopWorkerSandboxOverviewLines(wb, sb) { const parts = [] if (wb && typeof wb === 'object') { const o = /** @type {Record} */ (wb) const wm = Number(o.wallMsMax) const wu = Number(o.wallMsUsed ?? o.wallMsTotal ?? o.usedWallMs) if (Number.isFinite(wm) && wm > 0 && Number.isFinite(wu)) { const pct = Math.min(100, Math.round((wu / wm) * 100)) if (pct >= 85) parts.push('workerBudget~' + pct + '%') } } if (sb && typeof sb === 'object') { const o = /** @type {Record} */ (sb) if (o.denied === true || o.violationCount === true || o.violations) parts.push('sandbox:check') } return parts.length ? [' worker/sandbox ' + parts.join(' ')] : [] } /** Default overview section ids in render order */ var BARE_TOP_OVERVIEW_DEFAULT_SECTIONS = [ 'session', 'pipeline', 'delegates', 'replication', 'nethealth', 'kernel', 'warm', 'ipc', 'boot', 'swarm', 'prochist', 'protomux', 'meta', 'worker', 'initd', 'fdagg', 'delegate_rates', 'fairness', 'subprocess', 'host', 'limits', 'p2p', 'diskio', 'syscallsum', 'delegate_red', 'security', 'bootsum', 'badpeer', 'capdiff', 'extsum', 'gitcounters', 'resources', 'readerr' ] /** * @param {string} raw * @param {boolean} compact * @returns {Set | null} null = use default list */ function bareTopOverviewSectionSet(raw, compact) { const s = String(raw || '').trim() if (!s) { const base = BARE_TOP_OVERVIEW_DEFAULT_SECTIONS.slice() if (compact) { const drop = new Set([ 'fairness', 'subprocess', 'host', 'resources', 'limits', 'p2p', 'diskio', 'syscallsum', 'security', 'bootsum', 'capdiff', 'gitcounters', 'extsum' ]) return new Set(base.filter((x) => !drop.has(x))) } return new Set(base) } const parts = s.split(/[,;]+/).map((x) => x.trim().toLowerCase()).filter(Boolean) return new Set(parts) } /** * @typedef {{ * cols: number, * na: string, * compact: boolean, * sectionFilter: string, * deltaMode: boolean, * prevMetrics: Record | null, * nowMs: number, * asciiSep: boolean, * flattenCap: ((v: unknown, maxLines: number, maxKeys: number) => string[]) | null, * ringProto: number[], * protomuxSpark: boolean, * sparkW: number, * sparkAscii: boolean, * logSpark: boolean, * braille: boolean, * healthDetail: boolean, * healthBreakdown: string, * splitLeftCol: number, * splitMiniProc: boolean, * layoutVersion: string, * sectionsRaw?: string, * sessionWallRing?: number[], * prevSnap?: Record | null * }} BareTopOverviewOpts */ /** * @param {Record} snap * @param {BareTopOverviewOpts} opts * @returns {{ lines: string[], activeSections: string[], sectionRawLineIndex: Record }} */ function bareTopOverviewLines(snap, opts) { const na = opts.na || 'N/A' const cols = Math.max(40, opts.cols | 0) const fc = opts.flattenCap const sectionSet = opts.sectionsRaw != null && String(opts.sectionsRaw).trim() ? bareTopOverviewSectionSet(String(opts.sectionsRaw), false) : bareTopOverviewSectionSet('', opts.compact) const filter = String(opts.sectionFilter || '').trim().toLowerCase() /** @type {string[]} */ const lines = [] /** @type {string[]} */ const active = [] /** @type {Record} */ const sectionRawLineIndex = {} const m = snap.metricsLive const sess = m && typeof m.session === 'object' && m.session ? /** @type {Record} */ (m.session) : {} function want(id) { if (!sectionSet.has(id)) return false if (filter && !id.includes(filter) && !bareTopSectionTitleFor(id).toLowerCase().includes(filter)) return false return true } function pushSec(title, id, bodyLines) { if (!want(id)) return sectionRawLineIndex[id] = lines.length active.push(id) lines.push('') lines.push(bareTopSectionSeparator(title, cols, opts.asciiSep)) for (const ln of bodyLines) lines.push(bareTopSanitizeVisible(ln)) } const ec = Number(sess.execLineCount) || 0 const pb = Number(sess.pipelineBytesTotal) || 0 const wm = Number(sess.execLineWallMsTotal) || 0 if (want('session')) { sectionRawLineIndex.session = lines.length active.push('session') lines.push('') lines.push(bareTopSectionSeparator('Session', cols, opts.asciiSep)) lines.push( bareTopSanitizeVisible( ' execLineCount=' + (sess.execLineCount ?? na) + ' pipelineBytesTotal=' + (sess.pipelineBytesTotal ?? na) ) ) lines.push( bareTopSanitizeVisible(' execLineWallMsTotal=' + (sess.execLineWallMsTotal ?? na)) ) const ringW = opts.sessionWallRing if (ringW && ringW.length && opts.sparkW > 0) { const sp = bareTopSparklineOverview( ringW, Math.min(32, opts.sparkW), opts.sparkAscii, opts.logSpark, opts.braille ) lines.push(bareTopSanitizeVisible(' execLineWallMsTotal spark ' + sp)) } } if (want('pipeline')) { const pg = bareTopPipelineGaugesFromMetrics(m && m.pipeline, cols) pushSec('Pipeline limits', 'pipeline', pg ? [pg] : [' ' + na]) } if (want('delegates')) { const delI = m && m.delegateInflight /** @type {string[]} */ const dl = [] if (delI != null && typeof delI === 'object') { dl.push(...bareTopDelegateInflightTableLines(delI, 20, Math.min(24, Math.floor(cols * 0.35)))) } else dl.push(' ' + na) pushSec('Delegates (inflight)', 'delegates', dl) } const repLive = m && m.replicationLive if (want('replication')) { pushSec('Replication live', 'replication', bareTopReplicationOverviewLines(repLive, na)) } if (want('nethealth')) { const nl = bareTopNetHealthOverviewLines(snap.netSummary, m) if (nl.length) pushSec('Network summary', 'nethealth', nl) } if (want('kernel')) { const prevK = opts.prevMetrics && opts.prevMetrics.kernelCounters ? opts.prevMetrics.kernelCounters : null const kTop = opts.compact ? 12 : 22 const kl = bareTopKernelCounterLines( m && m.kernelCounters, prevK, opts.deltaMode, kTop, Math.min(28, Math.floor(cols * 0.38)) ) pushSec('Kernel counters', 'kernel', kl.length ? kl : [' ' + na]) } if (want('warm')) { const wl = bareTopWarmReadCacheSummaryLines(m && m.warmReadCache) pushSec('Warm read cache', 'warm', wl.length ? wl : [' ' + na]) } if (want('ipc')) { /** @type {string[]} */ const ibody = bareTopIpcTelemetrySummaryLines(m && m.ipcTelemetry, 8).slice() const ipL = bareTopIpcBackpressureProcLine( snap.extra && snap.extra.ipcBackpressure ) if (ipL) ibody.push(ipL) pushSec('IPC telemetry', 'ipc', ibody.length ? ibody : [' ' + na]) } if (want('boot')) { const bl = bareTopBootBudgetLines( m && m.bootBudgetCold, m && m.bootBudgetBareStdlib, m && m.bootBudgetTelemetry ) pushSec('Boot budget', 'boot', bl.length ? bl : [' ' + na]) } if (want('swarm') && m && m.swarmLifecycle) { const sl = bareTopSwarmLifecycleLine(m.swarmLifecycle) pushSec('Swarm lifecycle', 'swarm', sl ? [sl] : [' ' + na]) } const pt = (m && m.processTable && typeof m.processTable === 'object' ? m.processTable : null) || (snap.extra && snap.extra.processTable && typeof snap.extra.processTable === 'object' ? snap.extra.processTable : null) if (want('prochist')) { pushSec('Process states', 'prochist', bareTopProcessStateHistogramLines(pt)) } if (want('protomux')) { const cs = repLive && typeof repLive === 'object' && repLive.collaborationSession && typeof repLive.collaborationSession === 'object' ? repLive.collaborationSession : null /** @type {string[]} */ const pl = [] const cl = bareTopProtomuxCollabLine(cs) if (cl) pl.push(cl) if (opts.protomuxSpark && opts.ringProto && opts.ringProto.length && opts.sparkW > 0) { const sp = bareTopSparklineOverview( opts.ringProto, opts.sparkW, opts.sparkAscii, opts.logSpark, opts.braille ) pl.push(' appRx spark ' + sp) } if (pl.length) pushSec('Protomux / collab', 'protomux', pl) } if (want('meta')) { const ml = bareTopMetaCoalesceLine(m, opts.nowMs, na) if (ml) pushSec('Refresh meta', 'meta', [ml]) } if (want('worker')) { /** @type {string[]} */ const wbod = [] const wl = bareTopWorkerBudgetCompactLine(m && m.workerBudgetHints) if (wl) wbod.push(wl) const ws = bareTopWorkerSandboxOverviewLines( snap.extra && snap.extra.workerBudget, snap.extra && snap.extra.sandboxProfile ) for (const x of ws) wbod.push(x) if (wbod.length) pushSec('Worker budget', 'worker', wbod) } if (want('initd')) { pushSec('Initd readiness', 'initd', bareTopInitdReadinessSummaryLines(m && m.initdReadiness)) } if (want('fdagg')) { const fl = bareTopLogicalFdAggregateLine(pt) if (fl) pushSec('FD summary', 'fdagg', [fl]) } if (want('delegate_rates') && m && m.delegateRateBuckets != null) { pushSec('Delegate rate buckets', 'delegate_rates', bareTopDelegateRateBucketLines(m.delegateRateBuckets, cols)) } if (want('fairness') && snap.fairnessSnapshot && fc) { const fl = fc(snap.fairnessSnapshot, 14, 32) const expl = [ ' Fairness = delegate / job scheduling hints (not host CPU share).' ] pushSec('Fairness', 'fairness', expl.concat(fl)) } if (want('subprocess') && snap.subprocessBridge && fc) { pushSec('Subprocess bridge', 'subprocess', fc(snap.subprocessBridge, 12, 24)) } if (want('limits')) { const ll = bareTopLimitsMergedLines( /** @type {Record} */ (snap) ) if (ll.length) { ll.push( ' note: quotas = operator pipeline caps; rlimits = per-process limits mirror.' ) pushSec('Limits (quotas + rlimits)', 'limits', ll) } } if (want('p2p')) { const pl = bareTopP2PStackLines(/** @type {Record} */ (snap)) if (pl.length) pushSec('P2P / HDMS / DHT', 'p2p', pl) } if (want('diskio') && snap.diskstatsLine) { const dl = bareTopDiskstatsOverviewLines(String(snap.diskstatsLine)) if (dl.length) pushSec('Disk', 'diskio', dl) } if (want('syscallsum')) { const sy = snap.extra && snap.extra.syscalls const sl = bareTopSyscallsOverviewLines( sy && typeof sy === 'object' ? sy : null ) if (sl.length) pushSec('Syscalls proc', 'syscallsum', sl) } if (want('delegate_red')) { const dr = snap.extra && snap.extra.delegateRed const dl = bareTopDelegateRedOverviewLines(dr) if (dl.length) pushSec('Delegate red', 'delegate_red', dl) } if (want('security')) { const sp = snap.extra && snap.extra.securityPosture const sl = bareTopSecurityPostureOverviewLines(sp) if (sl.length) pushSec('Security posture', 'security', sl) } if (want('bootsum')) { const bb = snap.extra && snap.extra.bootBudgetSummary const bl = bareTopBootBudgetSummaryOverviewLines(bb) if (bl.length) pushSec('Boot budget file', 'bootsum', bl) } if (want('badpeer')) { const ph = snap.extra && snap.extra.peerHealth const wl = bareTopWorstPeerOverviewLines(ph) if (wl.length) pushSec('Peer health', 'badpeer', wl) } if (want('capdiff')) { const cj = snap.extra && snap.extra.capabilitiesJson const cn = snap.extra && snap.extra.capabilitiesNode const cl = bareTopCapabilitiesDiffOverviewLines(cj, cn) if (cl.length) pushSec('Capabilities diff', 'capdiff', cl) } if (want('extsum')) { const ex = snap.extra && snap.extra.extensions const el = bareTopExtensionsOverviewLines(ex) if (el.length) pushSec('Extensions', 'extsum', el) } if (want('gitcounters') && opts.deltaMode && opts.prevSnap) { const gl = bareTopGitStatsDeltaOverviewLines( snap.extra && snap.extra.gitDelegateStats, snap.extra && snap.extra.gitLfsPointerStats, opts.prevSnap.extra && opts.prevSnap.extra.gitDelegateStats, opts.prevSnap.extra && opts.prevSnap.extra.gitLfsPointerStats ) if (gl.length) pushSec('Git stats delta', 'gitcounters', gl) } if (want('host') && snap.hostStats) { const hl = bareTopHostStatsSummaryLine(snap.hostStats) if (hl) pushSec('Host stats', 'host', [hl]) } if (want('resources') && snap.resources) { const full = !opts.compact && fc if (full) { pushSec('Resources', 'resources', fc(snap.resources, 20, 40)) } else { pushSec('Resources', 'resources', bareTopResourcesSummaryLines(snap.resources, 10)) } } if ( want('readerr') && (snap.readErr || (Array.isArray(snap.readErrPaths) && snap.readErrPaths.length)) ) { /** @type {string[]} */ const erl = [] if (snap.readErr) { const err = String(snap.readErr) const wrapped = err.split('\n').slice(0, 3) for (const w of wrapped) erl.push(' warn ' + w) if (err.split('\n').length > 3) erl.push(' … (truncated)') } const paths = Array.isArray(snap.readErrPaths) ? snap.readErrPaths : [] for (const p of paths.slice(0, 16)) { erl.push(' read fail ' + bareTopSanitizeVisible(String(p))) } if (erl.length) pushSec('Read errors', 'readerr', erl) } const mismatch = bareTopPeerMismatchWarningLine(Number(m && m.peers) || 0, repLive) if (mismatch && want('replication')) { lines.push(bareTopSanitizeVisible(mismatch)) } if (opts.healthDetail && opts.healthBreakdown) { sectionRawLineIndex.health_detail = lines.length lines.push('') lines.push(bareTopSanitizeVisible(' health detail ' + opts.healthBreakdown)) active.push('health_detail') } return { lines, activeSections: active, sectionRawLineIndex } } function bareTopSectionTitleFor(id) { const map = { session: 'session', pipeline: 'pipeline', delegates: 'delegates', replication: 'replication', kernel: 'kernel', warm: 'warm', ipc: 'ipc', boot: 'boot', swarm: 'swarm', prochist: 'process', protomux: 'protomux', meta: 'meta', worker: 'worker', initd: 'initd', initd_phases: 'initd', fdagg: 'fd', delegate_rates: 'delegate', fairness: 'fairness', subprocess: 'subprocess', host: 'host', resources: 'resources', readerr: 'error', limits: 'limits', p2p: 'p2p', nethealth: 'network', diskio: 'disk', syscallsum: 'syscall', delegate_red: 'delegate', security: 'security', bootsum: 'boot', badpeer: 'peer', capdiff: 'cap', extsum: 'ext', gitcounters: 'git' } return map[id] || id } /** * Pipeline gauges string (shared with TUI). * Duplicates bareTopPipelineGauges from tui when only helpers are tested; TUI keeps full color version. */ function bareTopPipelineGaugesFromMetrics(pl, cols) { if (!pl || typeof pl !== 'object') return '' const o = /** @type {Record} */ (pl) const parts = [] for (const k of Object.keys(o).slice(0, 8)) { const v = o[k] if (typeof v === 'number' && Number.isFinite(v)) { const cap = v > 1000 ? v : 100 const pct = Math.min(100, Math.round((v / cap) * 100)) parts.push(k.slice(0, 10) + '=' + pct + '%') } } return parts.join(' ').slice(0, Math.max(20, cols - 2)) } /** * @param {number[]} values * @param {number} width * @param {boolean} ascii * @param {boolean} logScale * @param {boolean} braille */ function bareTopSparklineOverview(values, width, ascii, logScale, braille) { if (width <= 0) return '' const slice = values.length > width ? values.slice(values.length - width) : values.slice() if (slice.length === 0) return ' '.repeat(width) const vmap = logScale ? slice.map((v) => Math.log1p(Math.max(0, v))) : slice.slice() let min = vmap[0] let max = vmap[0] for (let i = 1; i < vmap.length; i++) { if (vmap[i] < min) min = vmap[i] if (vmap[i] > max) max = vmap[i] } const span = max - min || 1 const uni = '\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588' const br = '\u2800\u2840\u28c0\u28e0\u28f0\u28f4\u28f6\u28ff' const asc = ' .:-=+*#' let out = '' const pad = width - slice.length for (let i = 0; i < pad; i++) out += ascii ? ' ' : '\u2581' for (let i = 0; i < vmap.length; i++) { const v = vmap[i] const t = (v - min) / span let idx = Math.min(7, Math.floor(t * 8)) if (idx < 0) idx = 0 if (ascii) out += asc.charAt(idx) else if (braille) out += br.charAt(idx) else out += uni.charAt(idx) } return out.slice(0, width) }