feat(infra): commit live infra-audit/cluster work to reconcile git with prod
This work (network_hosts inventory + infra_audit MCP tool, /api/cluster + Sacred Valley cluster card, topbar cluster-health pill + SW self-heal) was built in an earlier session and DEPLOYED to CT 311 as alpha.24–26, but was never committed to git — prod was running code absent from the repo. Commits it as-is (already prod-validated) so git matches the live state, and restores its alpha.24/25/26 CHANGELOG entries. Files are disjoint from the fold-in work; both now ship together under alpha.27. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,29 @@ import { el, mount, clear } from '../dom.js';
|
||||
import { navigate } from '../router.js';
|
||||
import { on } from '../state.js';
|
||||
import { toggleSidebar, toggleRail } from './chrome.js';
|
||||
import { api } from '../api.js';
|
||||
|
||||
// Cluster health → topbar pill. Returns [status, label, title].
|
||||
function classifyCluster(c) {
|
||||
if (!c || c.error) return ['unknown', 'cluster ?', 'Cluster status unavailable'];
|
||||
if (!c.quorate) return ['down', 'no quorum', 'Cluster has LOST quorum'];
|
||||
if ((c.nodes_online ?? 0) < (c.nodes_total ?? 0)) return ['down', 'node down', `${c.nodes_online}/${c.nodes_total} nodes online`];
|
||||
if (c.ha && c.ha.services_error > 0) return ['warn', 'HA issue', `${c.ha.services_error} HA service(s) in error`];
|
||||
return ['ok', 'healthy', `Quorate · ${c.nodes_online}/${c.nodes_total} nodes · HA ok`];
|
||||
}
|
||||
|
||||
function startClusterHealth(pill, labelEl) {
|
||||
async function tick() {
|
||||
let c = null;
|
||||
try { c = await api.get('/api/cluster'); } catch { c = { error: 'fetch' }; }
|
||||
const [status, label, title] = classifyCluster(c);
|
||||
pill.className = 'icon-btn cluster-health status-' + status;
|
||||
pill.title = title;
|
||||
labelEl.textContent = label;
|
||||
}
|
||||
tick();
|
||||
setInterval(tick, 30000);
|
||||
}
|
||||
|
||||
function captureModal() {
|
||||
const root = document.getElementById('modal-root');
|
||||
@@ -37,17 +60,24 @@ export function renderTopbar(root) {
|
||||
|
||||
const bell = el('button', { class: 'icon-btn', onclick: () => navigate('/inbox') }, 'Inbox');
|
||||
|
||||
const chLabel = el('span', { class: 'ch-label' }, '…');
|
||||
const clusterPill = el('button', { class: 'icon-btn cluster-health status-unknown', title: 'Cluster health', onclick: () => navigate('/sacred-valley') },
|
||||
el('span', { class: 'dot' }), chLabel);
|
||||
|
||||
mount(root,
|
||||
el('button', { class: 'chrome-toggle', title: 'Toggle menu', onclick: toggleSidebar }, '☰'),
|
||||
el('div', { class: 'brand' }, 'VOID'),
|
||||
el('button', { class: 'icon-btn', onclick: captureModal }, '+ Capture'),
|
||||
el('div', { class: 'topbar-search' }, searchInput),
|
||||
el('div', { class: 'topbar-spacer' }),
|
||||
clusterPill,
|
||||
bell,
|
||||
el('button', { class: 'chrome-toggle', title: 'Toggle companion chat', onclick: toggleRail }, '◆'),
|
||||
el('button', { class: 'icon-btn', onclick: () => alert('Agent-switching ships post-Plan-2.') }, 'Owner')
|
||||
);
|
||||
|
||||
startClusterHealth(clusterPill, chLabel);
|
||||
|
||||
on('pending-count', (n) => {
|
||||
const old = bell.querySelector('.badge');
|
||||
if (old) old.remove();
|
||||
|
||||
Reference in New Issue
Block a user