feat: add MCP runtime health dashboard to web UI (Closes #430)
Adds read-only /runtime and /api/runtime routes showing active profile, identity, config model, git sync vs remote master, shell health, workflow/schema hashes, and stale-runtime warnings with #420 guidance. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
"""HTML views for MCP runtime health (#430)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import html
|
||||
|
||||
from webui.runtime_health import FileHash, RuntimeSnapshot
|
||||
|
||||
|
||||
def _hash_rows(items: tuple[FileHash, ...]) -> str:
|
||||
if not items:
|
||||
return "<p class='muted'>No hashes available.</p>"
|
||||
rows = []
|
||||
for item in items:
|
||||
digest = item.sha256 or item.error or "unavailable"
|
||||
rows.append(
|
||||
"<tr>"
|
||||
f"<td>{html.escape(item.label)}</td>"
|
||||
f"<td><code>{html.escape(item.path)}</code></td>"
|
||||
f"<td><code>{html.escape(digest[:16] if item.sha256 else digest)}</code></td>"
|
||||
"</tr>"
|
||||
)
|
||||
return (
|
||||
"<table class='registry'><thead><tr>"
|
||||
"<th>Artifact</th><th>Path</th><th>SHA-256</th>"
|
||||
"</tr></thead><tbody>"
|
||||
f"{''.join(rows)}</tbody></table>"
|
||||
)
|
||||
|
||||
|
||||
def render_runtime_page(snapshot: RuntimeSnapshot) -> str:
|
||||
error_block = ""
|
||||
if snapshot.fetch_error:
|
||||
error_block = (
|
||||
'<div class="stub"><p><strong>Runtime snapshot incomplete:</strong> '
|
||||
f"{html.escape(snapshot.fetch_error)}</p></div>"
|
||||
)
|
||||
|
||||
identity = snapshot.authenticated_username or "unresolved"
|
||||
if snapshot.identity_error:
|
||||
identity = f"unresolved ({snapshot.identity_error})"
|
||||
|
||||
stale_block = ""
|
||||
if snapshot.stale_runtime_warning:
|
||||
stale_block = (
|
||||
'<div class="stub"><p><strong>Stale runtime warning:</strong> '
|
||||
f"{html.escape(snapshot.stale_runtime_warning)}</p></div>"
|
||||
)
|
||||
|
||||
shell = snapshot.shell_health or {}
|
||||
shell_summary = (
|
||||
f"shell_use_allowed={shell.get('shell_use_allowed')} · "
|
||||
f"failures={shell.get('consecutive_spawn_failures')} · "
|
||||
f"hard_stopped={shell.get('hard_stopped')}"
|
||||
)
|
||||
|
||||
return (
|
||||
"<h2>Runtime health</h2>"
|
||||
f"<p class='meta'>Project <code>{html.escape(snapshot.project_id)}</code> · "
|
||||
f"host <code>{html.escape(snapshot.host)}</code> · "
|
||||
f"repo root <code>{html.escape(snapshot.repo_root)}</code></p>"
|
||||
f"{error_block}"
|
||||
f"{stale_block}"
|
||||
"<h3>Active profile</h3>"
|
||||
"<table class='detail'>"
|
||||
f"<tr><th>Profile</th><td><code>{html.escape(snapshot.profile_name)}</code></td></tr>"
|
||||
f"<tr><th>Role kind</th><td>{html.escape(snapshot.role_kind)}</td></tr>"
|
||||
f"<tr><th>Identity</th><td>{html.escape(identity)}</td></tr>"
|
||||
f"<tr><th>Config model</th><td>{html.escape(snapshot.config_model)}</td></tr>"
|
||||
f"<tr><th>Profile mode</th><td>{html.escape(snapshot.profile_mode)} "
|
||||
f"({html.escape(snapshot.profile_source)})</td></tr>"
|
||||
"</table>"
|
||||
"<h3>Server code sync</h3>"
|
||||
"<table class='detail'>"
|
||||
f"<tr><th>Local HEAD</th><td><code>{html.escape(snapshot.repo_sha or 'unknown')}</code></td></tr>"
|
||||
f"<tr><th>Remote {html.escape(snapshot.remote)}/{html.escape('master')}</th>"
|
||||
f"<td><code>{html.escape(snapshot.remote_master_sha or 'unknown')}</code></td></tr>"
|
||||
f"<tr><th>Commits behind</th><td>{snapshot.commits_behind_master if snapshot.commits_behind_master is not None else 'unknown'}</td></tr>"
|
||||
"</table>"
|
||||
"<h3>Shell health</h3>"
|
||||
f"<p class='meta'>{html.escape(shell_summary)}</p>"
|
||||
f"<p class='muted'>{html.escape(str(shell.get('safe_next_action') or ''))}</p>"
|
||||
"<h3>Workflow hashes</h3>"
|
||||
f"{_hash_rows(snapshot.workflow_hashes)}"
|
||||
"<h3>Schema hashes</h3>"
|
||||
f"{_hash_rows(snapshot.schema_hashes)}"
|
||||
"<h3>Restart / reload</h3>"
|
||||
"<p class='muted'>MVP is read-only — restart MCP servers from your IDE/operator "
|
||||
"workflow. Related issue: <code>#420</code>. Guidance: "
|
||||
f"<code>{html.escape(snapshot.restart_guidance)}</code></p>"
|
||||
"<p class='muted'>This page does not expose tokens or perform MCP restarts.</p>"
|
||||
)
|
||||
Reference in New Issue
Block a user