feat(hosts): true live vitals strip; agent panel becomes management-only
CI / lint (push) Successful in 2s
CI / unit (push) Successful in 46s
CI / integration (push) Successful in 2m22s
CI / publish (push) Successful in 1m0s

Realizes the chosen host-summary layout: a thin live vitals bar at the very top,
separate from the agent management panel (no more duplicated CPU/MEM/DISK/LOAD).

- New fragment _host_vitals.html + route /plugins/host_agent/vitals/<id>: compact
  CPU / Memory / Disk(/) / Load-per-core (threshold-coloured + sparkline) +
  Pressure + live/stale·version·last-seen. Polled every 15s; renders nothing
  until the agent reports.
- The metric computation (latest snapshot + 6h sparkline query + load/core + PSI)
  moves from host_panel into host_vitals. host_panel slims to management only
  (reg/target/reporting/stale/ansible) and no longer queries metrics; panel.html
  drops the gauge row + pressure block, keeping status + lifecycle actions.
- hosts/detail.html: vitals strip on top (full width, live), then a 2-col
  [Monitors | Agent] grid, Ansible full width below, docker fragment last.

UI only. Templates parse; plugin-template parse test covers the new fragment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Jg27rgypiW2efULXJDtMC
This commit is contained in:
2026-06-20 19:49:01 -04:00
parent a35c369dd4
commit b0d3e83bdd
4 changed files with 91 additions and 55 deletions
+14 -8
View File
@@ -26,12 +26,13 @@
{% set inp = "width:100%;padding:0.4rem 0.65rem;background:var(--bg);border:1px solid var(--border-mid);border-radius:4px;color:var(--text);font-size:0.88rem;" %}
{% set lbl = "font-size:0.75rem;color:var(--text-muted);display:block;margin-bottom:0.2rem;" %}
{# ── Agent vitals + lifecycle (host_agent fragment) — primary, full width on top ── #}
<div hx-get="/plugins/host_agent/panel/{{ host.id }}" hx-trigger="load" hx-swap="innerHTML">
<div class="card"><span style="color:var(--text-muted);font-size:0.85rem;">Loading agent…</span></div>
</div>
{# ── Live vitals strip (host_agent fragment) — full width on top, polled ────── #}
<div id="hv-strip"
hx-get="/plugins/host_agent/vitals/{{ host.id }}"
hx-trigger="load, every 15s"
hx-swap="innerHTML"></div>
{# ── Monitors + Ansible, side by side on wide screens, stacked when narrow ──── #}
{# ── Monitors + Agent, side by side on wide screens, stacked when narrow ────── #}
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(420px,1fr));gap:1rem;align-items:start;margin-bottom:1rem;">
{# ── Monitors ─────────────────────────────────────────────────────────────── #}
@@ -124,8 +125,14 @@
{% endif %}
</div>{# Monitors card #}
{# ── Ansible ──────────────────────────────────────────────────────────────── #}
<div class="card" style="margin-bottom:0;">
{# ── Agent (host_agent management panel) ──────────────────────────────────── #}
<div hx-get="/plugins/host_agent/panel/{{ host.id }}" hx-trigger="load" hx-swap="innerHTML">
<div class="card" style="margin-bottom:0;"><span style="color:var(--text-muted);font-size:0.85rem;">Loading agent…</span></div>
</div>
</div>{# Monitors + Agent grid #}
{# ── Ansible (full width) ─────────────────────────────────────────────────── #}
<div class="card">
<h3 class="section-title">Ansible</h3>
{% if linked_target %}
<div style="display:flex;align-items:center;gap:1rem;padding:0.6rem 0.75rem;background:var(--bg-elevated);border-radius:4px;margin-bottom:1rem;flex-wrap:wrap;">
@@ -211,7 +218,6 @@
</div>
{% endif %}
</div>{# Ansible card #}
</div>{# Monitors + Ansible grid #}
{# ── Docker (docker plugin fragment; renders nothing if the host has none) ──── #}
{% if "docker" in enabled_plugins %}