Add Crucible rich terminal renderers and ignore local coverage dirs.

Listen ports table UI and styles; gitignore for spread-kits and cov artifact files.
This commit is contained in:
AetherForge
2026-05-31 01:14:10 -07:00
parent ea6f54ad03
commit babe644c94
3 changed files with 295 additions and 6 deletions

View File

@@ -579,6 +579,137 @@ export default function CruciblePage() {
};
};
// ── Rich terminal renderers ────────────────────────────────────────────
const RichListenPortsTable = ({ d }: { d: RichListenPorts }) => (
<div className="rich-block rich-ports">
<div className="rich-header">
<span className="rich-label">LISTEN PORTS</span>
<span className="rich-count">{d.count} listener{d.count !== 1 ? 's' : ''}</span>
</div>
{d.ports.length === 0 ? (
<div className="rich-empty">No listeners found</div>
) : (
<table className="rich-table">
<thead>
<tr>
<th>PORT</th>
<th>ADDR</th>
<th>PROTO</th>
<th>PROCESS</th>
<th>PID</th>
</tr>
</thead>
<tbody>
{d.ports.map((p, i) => (
<tr key={i} className={p.port === 22 ? 'rich-row-ssh' : p.port < 1024 ? 'rich-row-sys' : ''}>
<td className="rich-port">{p.port}</td>
<td className="rich-addr">{p.addr || '*'}</td>
<td className="rich-proto">{p.proto.toUpperCase()}</td>
<td className="rich-proc">{p.process || '—'}</td>
<td className="rich-pid">{p.pid ?? '—'}</td>
</tr>
))}
</tbody>
</table>
)}
</div>
);
const RichPatchStatusBlock = ({ d }: { d: RichPatchStatus }) => (
<div className="rich-block rich-patch">
<div className="rich-header">
<span className="rich-label">PATCH STATUS</span>
{d.reboot_pending && <span className="rich-tag rich-tag-warn blink-slow">REBOOT REQUIRED</span>}
</div>
<div className="rich-kv-row">
<span className="rich-key">Pending Updates</span>
<span className={`rich-val ${(d.pending_updates ?? 0) > 0 ? 'rich-val-warn' : 'rich-val-ok'}`}>
{d.pending_updates ?? 0}
</span>
</div>
<div className="rich-kv-row">
<span className="rich-key">Last Patch</span>
<span className={`rich-val ${(d.last_patch_days ?? 0) > 60 ? 'rich-val-bad' : (d.last_patch_days ?? 0) > 30 ? 'rich-val-warn' : 'rich-val-ok'}`}>
{d.last_patch ?? '—'}{d.last_patch_days != null ? ` (${d.last_patch_days}d ago)` : ''}
</span>
</div>
<div className="rich-kv-row">
<span className="rich-key">Reboot Pending</span>
<span className={`rich-val ${d.reboot_pending ? 'rich-val-warn' : 'rich-val-ok'}`}>
{d.reboot_pending ? 'YES' : 'NO'}
</span>
</div>
</div>
);
const fwIcon = (on?: boolean) => on ? <span className="rich-val-ok">ON</span> : <span className="rich-val-bad">OFF</span>;
const RichPostureSummaryBlock = ({ d }: { d: RichPostureSummary }) => (
<div className="rich-block rich-posture">
<div className="rich-header">
<span className="rich-label">POSTURE REPORT</span>
{d.posture_score != null && (
<span className={`rich-score ${d.posture_score >= 70 ? 'rich-score-ok' : d.posture_score >= 40 ? 'rich-score-warn' : 'rich-score-bad'}`}>
{d.posture_score} / 100
</span>
)}
</div>
<div className="rich-posture-grid">
<div className="rich-kv-row"><span className="rich-key">Defender</span>
<span className={`rich-val ${d.defender_enabled ? 'rich-val-ok' : 'rich-val-bad'}`}>{d.defender_enabled ? 'ON' : 'OFF'}</span>
</div>
<div className="rich-kv-row"><span className="rich-key">Real-Time Prot</span>
<span className={`rich-val ${d.defender_rtp ? 'rich-val-ok' : 'rich-val-warn'}`}>{d.defender_rtp ? 'ON' : 'OFF'}</span>
</div>
<div className="rich-kv-row"><span className="rich-key">Firewall Domain/Priv/Pub</span>
<span className="rich-val">{fwIcon(d.firewall_domain)} / {fwIcon(d.firewall_private)} / {fwIcon(d.firewall_public)}</span>
</div>
<div className="rich-kv-row"><span className="rich-key">AV Products</span>
<span className="rich-val">{d.av_products?.join(', ') || '—'}</span>
</div>
<div className="rich-kv-row"><span className="rich-key">SSH Listening</span>
<span className={`rich-val ${d.ssh_listening ? 'rich-val-ok' : 'rich-val-bad'}`}>{d.ssh_listening ? 'YES' : 'NO'}</span>
</div>
<div className="rich-kv-row"><span className="rich-key">Agent Elevated</span>
<span className={`rich-val ${d.agent_elevated ? 'rich-val-warn' : 'rich-val-ok'}`}>{d.agent_elevated ? 'YES (ADMIN)' : 'no'}</span>
</div>
{d.last_patch_days != null && (
<div className="rich-kv-row"><span className="rich-key">Last Patch</span>
<span className={`rich-val ${d.last_patch_days > 60 ? 'rich-val-bad' : d.last_patch_days > 30 ? 'rich-val-warn' : 'rich-val-ok'}`}>
{d.last_patch_days}d ago{d.pending_updates ? ` · ${d.pending_updates} pending` : ''}
{d.reboot_pending ? ' · REBOOT!' : ''}
</span>
</div>
)}
</div>
{d.services && d.services.length > 0 && (
<>
<div className="rich-sub-label">SERVICES</div>
<table className="rich-table">
<thead><tr><th>SERVICE</th><th>STATUS</th><th>START</th></tr></thead>
<tbody>
{d.services.map((s, i) => (
<tr key={i}>
<td className="rich-proc">{s.display_name || s.name}</td>
<td className={`rich-svc-status ${s.status === 'running' ? 'rich-val-ok' : 'rich-val-bad'}`}>{s.status.toUpperCase()}</td>
<td className="rich-addr">{s.start_type}</td>
</tr>
))}
</tbody>
</table>
</>
)}
</div>
);
const renderRichData = (d: RichTermData) => {
if (d.type === 'listen_ports') return <RichListenPortsTable d={d} />;
if (d.type === 'patch_status') return <RichPatchStatusBlock d={d} />;
if (d.type === 'posture') return <RichPostureSummaryBlock d={d} />;
return null;
};
// ── Render ─────────────────────────────────────────────────────────────
return (
@@ -929,7 +1060,7 @@ export default function CruciblePage() {
return (
<div
key={line.id}
className={`crucible-term-line ${line.isCmd ? 'cmd-line' : 'out-line'} ${line.success === false ? 'err-line' : ''}`}
className={`crucible-term-line ${line.isCmd ? 'cmd-line' : 'out-line'} ${line.success === false ? 'err-line' : ''} ${line.richData ? 'rich-line' : ''}`}
>
<span className="ctl-agent" style={{ color }}>
{line.agentName.slice(0, 12).padEnd(12)}
@@ -937,7 +1068,11 @@ export default function CruciblePage() {
<span className="ctl-arrow" style={{ color }}>
{line.isCmd ? '▶' : '◀'}
</span>
<span className="ctl-text">{line.text}</span>
{line.richData ? (
<span className="ctl-text ctl-rich">{renderRichData(line.richData)}</span>
) : (
<span className="ctl-text">{line.text}</span>
)}
<span className="ctl-ts">{line.ts.toLocaleTimeString()}</span>
</div>
);