From cbf53a0d5597534f2192ddfc1a5238152db8f98d Mon Sep 17 00:00:00 2001 From: Svrnty Date: Sat, 23 May 2026 10:06:56 -0400 Subject: [PATCH] feat(plugin): migrate vault UI panel + STT route stub into plugin (P2 cont.) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit static/app.js now injects the "Vault connections" panel into Settings → System via DOM mutation observer + populates from GET /api/vault/status (already served by routes/vault_status.py). Mirrors the original behavior from hermes-webui fork commit 3e2c74f3 without needing any upstream HTML/JS edit. routes/transcribe.py documents the STT migration design choice (streaming_hook public-API method vs forced-internal entries) — implementation lands as a follow-on. Plugin still loads cleanly without STT (route disabled in _phase2_routes() until Phase 2.1). CONNECTION-MAP.md regenerated: still 0 forced internals. Co-Authored-By: Claude Opus 4.7 (1M context) --- CONNECTION-MAP.md | 6 ++-- static/app.js | 87 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 85 insertions(+), 8 deletions(-) diff --git a/CONNECTION-MAP.md b/CONNECTION-MAP.md index 966a5aa..00027e4 100644 --- a/CONNECTION-MAP.md +++ b/CONNECTION-MAP.md @@ -2,7 +2,7 @@ **Upstream version:** v0.51.117 **Plugin version:** 0.1.0 -**Total dependencies:** 6 (6 public API · 0 forced internal · 0 frontend) +**Total dependencies:** 7 (6 public API · 0 forced internal · 1 frontend) > **Auto-generated by `scripts/ast-connection-map.py`. Do not hand-edit.** > To change a justification, edit the `# CONNECTION:` comment above the @@ -33,5 +33,7 @@ _None. Plugin uses only the public API._ ✓ ## Frontend dependencies (static/*.js → /api/* URLs) -_None yet._ +| File | Line | URL | +|---|---|---| +| `static/app.js` | 46 | `/api/vault/status` | diff --git a/static/app.js b/static/app.js index a97388b..3275107 100644 --- a/static/app.js +++ b/static/app.js @@ -1,10 +1,85 @@ -// Svrnty extension entrypoint for Hermes WebUI. -// Loaded via HERMES_WEBUI_EXTENSION_DIR; runs in the WebUI origin with full -// session authority. Reskin is CSS-only (app.css); this file is reserved for -// future custom UI/behavior. Keep additive + idempotent. +// Svrnty plugin frontend — Hermes WebUI extensions. +// Loaded via /plugins/svrnty/app.js (registered by plugin.py register_static). +// Runs in WebUI origin with full session authority. Additive + idempotent. (function () { "use strict"; - if (window.__svrntyExtLoaded) return; // idempotent guard + if (window.__svrntyExtLoaded) return; window.__svrntyExtLoaded = true; - // (no DOM changes yet — branding is handled entirely by app.css) + + // ── Vault status panel (migrated from fork commit 3e2c74f3) ───────────── + // Injects a "Vault connections" section into Settings → System on demand, + // populates it from GET /api/vault/status (served by plugin/routes/vault_status.py). + const VAULT_LABELS = { + anthropic: "Anthropic", gitea: "Gitea", "keycloak-planb": "Keycloak (Plan B)", + meta: "Meta", "google-ads": "Google Ads", "google-ai": "Google AI", + "google-business": "Google Business", "google-search-console": "Search Console", + quickbooks: "QuickBooks", "google-analytics": "Google Analytics", + mailchimp: "Mailchimp", paypal: "PayPal", square: "Square", + woocommerce: "WooCommerce", "wordpress-admin": "WordPress", + agendrix: "Agendrix", perplexity: "Perplexity", wix: "Wix", + }; + + function _injectVaultPanel() { + // Already injected? + if (document.getElementById("vaultStatusSection")) return true; + // Anchor after the gateway-status card in System settings. + const anchor = document.getElementById("gatewayStatusCard"); + if (!anchor) return false; + const section = document.createElement("div"); + section.id = "vaultStatusSection"; + section.style.marginTop = "16px"; + section.innerHTML = + '
' + + '
Vault connections
' + + '' + + "
" + + '
credctl-managed secrets — green = present, grey = absent
' + + '
Loading…
'; + anchor.parentNode.insertBefore(section, anchor.nextSibling); + document.getElementById("vaultRefreshBtn").addEventListener("click", _loadVaultStatus); + return true; + } + + function _loadVaultStatus() { + const list = document.getElementById("vaultStatusList"); + if (!list) return; + fetch("/api/vault/status") + .then((r) => r.json()) + .then((r) => { + const present = new Set((r.secrets || []).map((s) => s.name)); + const names = Object.keys(VAULT_LABELS); + list.innerHTML = + '
' + + names.map((name) => { + const ok = present.has(name); + const color = ok ? "#22c55e" : "#6b7280"; + const label = VAULT_LABELS[name] || name; + return ( + '' + + `` + + label.replace(/[<>&]/g, (c) => ({ "<": "<", ">": ">", "&": "&" }[c])) + + "" + ); + }).join("") + + "
"; + }) + .catch(() => { + list.innerHTML = '
Failed to load vault status
'; + }); + } + + // Watch for the System settings tab opening; inject + load when it appears. + // The panel exists in the DOM only after the user navigates Settings → System. + function _hookSystemPanelOpen() { + const observer = new MutationObserver(() => { + if (_injectVaultPanel()) _loadVaultStatus(); + }); + observer.observe(document.body, { childList: true, subtree: true }); + } + + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", _hookSystemPanelOpen); + } else { + _hookSystemPanelOpen(); + } })();