Files
Gitea-Tools/webui/runtime_views.py
T
sysadminandClaude Opus 4.8 4429113dae 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]>
2026-07-07 15:18:45 -04:00

92 lines
3.8 KiB
Python

"""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>"
)