f473be4033
Add an optional auth-token (bearer) field and a custom-headers textarea to
the MCP 'Add server' pane. URL-mode servers are now registered by writing
hermes config directly via the venv's config helpers (save_env_value +
save_config) instead of shelling into the interactive, network-probing
'hermes mcp add --url' flow that hung/504'd on auth challenges.
- Token is stored in ~/.hermes/.env (mode 600) and referenced in config.yaml
as 'Authorization: Bearer ${MCP_<NAME>_API_KEY}' — never plaintext config.
- Registration is non-blocking: no live probe, so a slow/unreachable server
can't hang the request.
- Add a per-server 'Test' button (POST /api/mcp/test -> hermes mcp test).
- Contract tests for the new fields, payload, no-probe path, and test route.
947 lines
44 KiB
HTML
947 lines
44 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<title>Hermes · Operations Console</title>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Big+Shoulders+Stencil+Display:wght@500;700;800&family=JetBrains+Mono:wght@400;500;700&family=Public+Sans:ital,wght@0,300;0,400;0,500;0,600;1,400&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="style.css" />
|
|
</head>
|
|
<body>
|
|
<!-- printer registration crosshairs in the four page corners -->
|
|
<span class="reg reg-tl" aria-hidden="true"></span>
|
|
<span class="reg reg-tr" aria-hidden="true"></span>
|
|
<span class="reg reg-bl" aria-hidden="true"></span>
|
|
<span class="reg reg-br" aria-hidden="true"></span>
|
|
|
|
<!-- ─── Masthead / Index ──────────────────────────────────────────────── -->
|
|
<aside class="nav">
|
|
<header class="masthead">
|
|
<div class="masthead-title">HERMES</div>
|
|
<div class="masthead-sub">OPERATIONS CONSOLE</div>
|
|
<div class="masthead-meta">
|
|
<span>REV.</span><span id="masthead-version">———</span>
|
|
</div>
|
|
<div class="masthead-stamp" aria-hidden="true">
|
|
<span class="stamp-line">▢</span>
|
|
<span>LOCAL · LOOPBACK · :7843</span>
|
|
</div>
|
|
</header>
|
|
|
|
<nav class="dial">
|
|
<button class="dial-item" data-route="providers" data-active>
|
|
<span class="dial-num">01</span><span class="dial-name">Providers</span>
|
|
</button>
|
|
<button class="dial-item" data-route="routing">
|
|
<span class="dial-num">02</span><span class="dial-name">Routing</span>
|
|
</button>
|
|
<button class="dial-item" data-route="skills">
|
|
<span class="dial-num">03</span><span class="dial-name">Skills</span>
|
|
</button>
|
|
<button class="dial-item" data-route="plugins">
|
|
<span class="dial-num">04</span><span class="dial-name">Plugins</span>
|
|
</button>
|
|
<button class="dial-item" data-route="bundles">
|
|
<span class="dial-num">05</span><span class="dial-name">Bundles</span>
|
|
</button>
|
|
<button class="dial-item" data-route="curator">
|
|
<span class="dial-num">06</span><span class="dial-name">Curator</span>
|
|
</button>
|
|
<button class="dial-item" data-route="mcp">
|
|
<span class="dial-num">07</span><span class="dial-name">MCP</span>
|
|
</button>
|
|
<button class="dial-item" data-route="cron">
|
|
<span class="dial-num">08</span><span class="dial-name">Cron</span>
|
|
</button>
|
|
<button class="dial-item" data-route="sessions">
|
|
<span class="dial-num">09</span><span class="dial-name">Sessions</span>
|
|
</button>
|
|
<button class="dial-item" data-route="hooks">
|
|
<span class="dial-num">10</span><span class="dial-name">Hooks</span>
|
|
</button>
|
|
<button class="dial-item" data-route="memory">
|
|
<span class="dial-num">11</span><span class="dial-name">Memory</span>
|
|
</button>
|
|
<button class="dial-item" data-route="kanban">
|
|
<span class="dial-num">12</span><span class="dial-name">Kanban</span>
|
|
</button>
|
|
<button class="dial-item" data-route="webhooks">
|
|
<span class="dial-num">13</span><span class="dial-name">Webhooks</span>
|
|
</button>
|
|
<button class="dial-item" data-route="profiles">
|
|
<span class="dial-num">14</span><span class="dial-name">Profiles</span>
|
|
</button>
|
|
<button class="dial-item" data-route="storage">
|
|
<span class="dial-num">15</span><span class="dial-name">Storage</span>
|
|
</button>
|
|
<button class="dial-item" data-route="config">
|
|
<span class="dial-num">16</span><span class="dial-name">Config</span>
|
|
</button>
|
|
|
|
<div class="dial-divider"><span>VIEW · READ ONLY</span></div>
|
|
|
|
<button class="dial-item" data-route="tools">
|
|
<span class="dial-num">17</span><span class="dial-name">Tools</span>
|
|
</button>
|
|
<button class="dial-item" data-route="insights">
|
|
<span class="dial-num">18</span><span class="dial-name">Insights</span>
|
|
</button>
|
|
<button class="dial-item" data-route="security">
|
|
<span class="dial-num">19</span><span class="dial-name">Security</span>
|
|
</button>
|
|
<button class="dial-item" data-route="system">
|
|
<span class="dial-num">20</span><span class="dial-name">System</span>
|
|
</button>
|
|
|
|
<div class="dial-divider"><span>ACCESS · API KEYS</span></div>
|
|
|
|
<button class="dial-item" data-route="api-users">
|
|
<span class="dial-num">21</span><span class="dial-name">API Users</span>
|
|
</button>
|
|
</nav>
|
|
|
|
<footer class="nav-foot">
|
|
<div class="kv"><span class="kv-k">home</span><span class="kv-v" id="foot-home">·</span></div>
|
|
<div class="kv"><span class="kv-k">conf</span><span class="kv-v">config.yaml</span></div>
|
|
<div class="kv"><span class="kv-k">port</span><span class="kv-v">:7843</span></div>
|
|
</footer>
|
|
</aside>
|
|
|
|
<!-- ─── Main ─────────────────────────────────────────────────────────── -->
|
|
<main class="main">
|
|
|
|
<!-- §01 PROVIDERS ──────────────────────────────────────────────────── -->
|
|
<section class="pane" data-pane="providers" data-active>
|
|
<header class="folio">
|
|
<div class="folio-rail">
|
|
<span class="folio-num">§ 01</span>
|
|
<span class="folio-tag">AUTH · POOLS · FAILOVER</span>
|
|
</div>
|
|
<h1 class="folio-title">Providers</h1>
|
|
<div class="folio-actions">
|
|
<button class="btn-ghost" data-action="refresh">↻ Reload</button>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="grid grid-4" id="provider-grid"></div>
|
|
|
|
<section class="block" data-block="01.E">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 01.E</span>
|
|
<span class="block-name">DeepSeek · API key</span>
|
|
<span class="block-meta">last resort · written to .env</span>
|
|
</header>
|
|
<div class="key-row">
|
|
<input id="deepseek-key" type="password" placeholder="sk-XXXXXXXXXXXXXXXXXX" autocomplete="off" spellcheck="false" />
|
|
<button class="btn-primary" data-action="save-deepseek">Save</button>
|
|
</div>
|
|
<p class="status" id="deepseek-status"></p>
|
|
</section>
|
|
</section>
|
|
|
|
<!-- §02 ROUTING ───────────────────────────────────────────────────── -->
|
|
<section class="pane" data-pane="routing">
|
|
<header class="folio">
|
|
<div class="folio-rail">
|
|
<span class="folio-num">§ 02</span>
|
|
<span class="folio-tag">PRIMARY · FALLBACK CHAIN</span>
|
|
</div>
|
|
<h1 class="folio-title">Routing</h1>
|
|
<div class="folio-actions">
|
|
<button class="btn-ghost" data-action="refresh">↻ Reload</button>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="block" data-block="02.A">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 02.A</span>
|
|
<span class="block-name">Primary route</span>
|
|
<span class="block-meta">provider · model</span>
|
|
</header>
|
|
<div class="primary-row">
|
|
<select id="primary-provider"></select>
|
|
<input id="primary-model" placeholder="claude-opus-4.6" />
|
|
<button class="btn-primary" data-action="set-primary">Commit</button>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="block" data-block="02.B">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 02.B</span>
|
|
<span class="block-name">Fallback chain</span>
|
|
<span class="block-meta">ordered · tried top-to-bottom on failure</span>
|
|
<span class="block-actions">
|
|
<button class="btn-mini" data-action="add-fallback-row">+ Hop</button>
|
|
</span>
|
|
</header>
|
|
<ol class="chain-edit" id="chain-edit"></ol>
|
|
<div class="row gap">
|
|
<button class="btn-primary" data-action="save-chain">Save chain</button>
|
|
<button class="btn" data-action="reset-chain">Discard</button>
|
|
<span class="status" id="chain-status"></span>
|
|
</div>
|
|
</section>
|
|
</section>
|
|
|
|
<!-- §17 TOOLS · view-only ───────────────────────────────────────── -->
|
|
<section class="pane" data-pane="tools">
|
|
<header class="folio">
|
|
<div class="folio-rail">
|
|
<span class="folio-num">§ 17</span>
|
|
<span class="folio-tag">PER-PLATFORM TOOLSETS</span>
|
|
</div>
|
|
<h1 class="folio-title">Tools</h1>
|
|
<div class="folio-actions">
|
|
<button class="btn-ghost" data-action="refresh">↻ Reload</button>
|
|
</div>
|
|
</header>
|
|
<pre class="raw" id="tools-raw">loading…</pre>
|
|
</section>
|
|
|
|
<!-- §03 SKILLS ────────────────────────────────────────────────────── -->
|
|
<section class="pane" data-pane="skills">
|
|
<header class="folio">
|
|
<div class="folio-rail">
|
|
<span class="folio-num">§ 03</span>
|
|
<span class="folio-tag">FILESYSTEM · HUB MARKETPLACE</span>
|
|
</div>
|
|
<h1 class="folio-title">Skills</h1>
|
|
<div class="folio-actions">
|
|
<button class="btn-ghost" data-action="refresh">↻ Reload</button>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="tabs" data-tab-group="skills">
|
|
<button class="tab" data-tab="installed" data-active>Installed <span class="tab-count" id="skills-count">·</span></button>
|
|
<button class="tab" data-tab="discover">Discover · Hermes Hub</button>
|
|
</div>
|
|
|
|
<div class="tab-body" data-tab-pane="skills:installed" data-active>
|
|
<div class="row gap" style="margin-bottom:14px">
|
|
<input id="skills-filter" placeholder="filter by name, tag, category…" style="flex:1" />
|
|
<select id="skills-cat-filter" style="width:200px"><option value="">all categories</option></select>
|
|
</div>
|
|
<div class="cards-grid" id="skills-grid"></div>
|
|
</div>
|
|
|
|
<div class="tab-body" data-tab-pane="skills:discover">
|
|
<div class="row gap" style="margin-bottom:14px">
|
|
<input id="skills-search" placeholder="search the hub (5000+ skills)…" style="flex:1" />
|
|
<select id="skills-source">
|
|
<option value="all">all sources</option>
|
|
<option value="official">official ★</option>
|
|
<option value="skills-sh">skills.sh</option>
|
|
<option value="clawhub">clawhub</option>
|
|
<option value="lobehub">lobehub</option>
|
|
<option value="github">github</option>
|
|
<option value="well-known">well-known</option>
|
|
<option value="browse-sh">browse.sh</option>
|
|
</select>
|
|
<button class="btn-primary" data-action="skills-discover">Search</button>
|
|
</div>
|
|
<div class="cards-grid" id="skills-discover-grid">
|
|
<div class="empty-pane">Pick a registry source and search. Results stream from <code>hermes skills search --json</code>.</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- §07 MCP ───────────────────────────────────────────────────────── -->
|
|
<section class="pane" data-pane="mcp">
|
|
<header class="folio">
|
|
<div class="folio-rail">
|
|
<span class="folio-num">§ 07</span>
|
|
<span class="folio-tag">MODEL CONTEXT PROTOCOL</span>
|
|
</div>
|
|
<h1 class="folio-title">MCP Servers</h1>
|
|
<div class="folio-actions">
|
|
<button class="btn-ghost" data-action="refresh">↻ Reload</button>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="block" data-block="05.A">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 09.A</span>
|
|
<span class="block-name">Connected servers</span>
|
|
</header>
|
|
<ul class="list" id="mcp-list"></ul>
|
|
</section>
|
|
|
|
<section class="block" data-block="05.B">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 07.B</span>
|
|
<span class="block-name">Add server</span>
|
|
<span class="block-meta">URL or stdio command</span>
|
|
</header>
|
|
<div class="mcp-form">
|
|
<input id="mcp-name" placeholder="name (e.g. github)" />
|
|
<input id="mcp-url" placeholder="https://… (url-mode)" />
|
|
<input id="mcp-token" placeholder="auth token / bearer (url-mode, optional)" type="password" autocomplete="off" />
|
|
<input id="mcp-command" placeholder="command (stdio-mode)" />
|
|
<input id="mcp-args" placeholder="args (space-separated)" />
|
|
<textarea id="mcp-headers" rows="2" placeholder="extra headers (optional) — one per line, e.g. X-Api-Key: abc123"></textarea>
|
|
<button class="btn-primary" data-action="add-mcp">Add</button>
|
|
</div>
|
|
<p class="mcp-hint">URL servers are saved instantly without a live probe. The token is stored in <code>.env</code> (never in plaintext config) and sent as <code>Authorization: Bearer …</code>. Use <strong>Test</strong> on a server to verify it connects.</p>
|
|
<pre class="log" id="mcp-log" hidden></pre>
|
|
</section>
|
|
</section>
|
|
|
|
<!-- §06 CRON ──────────────────────────────────────────────────────── -->
|
|
<section class="pane" data-pane="cron">
|
|
<header class="folio">
|
|
<div class="folio-rail">
|
|
<span class="folio-num">§ 08</span>
|
|
<span class="folio-tag">SCHEDULED TASKS</span>
|
|
</div>
|
|
<h1 class="folio-title">Cron</h1>
|
|
<div class="folio-actions">
|
|
<button class="btn-ghost" data-action="refresh">↻ Reload</button>
|
|
</div>
|
|
</header>
|
|
<ul class="list" id="cron-list"></ul>
|
|
</section>
|
|
|
|
<!-- §07 SESSIONS ──────────────────────────────────────────────────── -->
|
|
<section class="pane" data-pane="sessions">
|
|
<header class="folio">
|
|
<div class="folio-rail">
|
|
<span class="folio-num">§ 09</span>
|
|
<span class="folio-tag">TRAJECTORY STORE</span>
|
|
</div>
|
|
<h1 class="folio-title">Sessions</h1>
|
|
<div class="folio-actions">
|
|
<button class="btn-ghost" data-action="refresh">↻ Reload</button>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="block" data-block="07.A">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 09.A</span>
|
|
<span class="block-name">Maintenance</span>
|
|
</header>
|
|
<div class="row gap">
|
|
<span class="status" id="sessions-count">·</span>
|
|
<span class="grow"></span>
|
|
<label class="field-inline">
|
|
<span>older than</span>
|
|
<input id="prune-days" type="number" min="1" value="30" />
|
|
<span>days</span>
|
|
</label>
|
|
<button class="btn-primary" data-action="prune-sessions">Prune</button>
|
|
</div>
|
|
</section>
|
|
|
|
<pre class="raw" id="sessions-raw">loading…</pre>
|
|
</section>
|
|
|
|
<!-- §08 HOOKS ─────────────────────────────────────────────────────── -->
|
|
<section class="pane" data-pane="hooks">
|
|
<header class="folio">
|
|
<div class="folio-rail">
|
|
<span class="folio-num">§ 10</span>
|
|
<span class="folio-tag">SHELL-SCRIPT HOOKS</span>
|
|
</div>
|
|
<h1 class="folio-title">Hooks</h1>
|
|
<div class="folio-actions">
|
|
<button class="btn-ghost" data-action="refresh">↻ Reload</button>
|
|
<button class="btn-ghost" data-action="hooks-doctor">▷ Doctor</button>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="block" data-block="08.A">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 10.A</span>
|
|
<span class="block-name">Configured hooks</span>
|
|
<span class="block-meta">events · matcher · timeout · consent</span>
|
|
</header>
|
|
<pre class="raw" id="hooks-raw">loading…</pre>
|
|
</section>
|
|
|
|
<section class="block" data-block="08.B">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 10.B</span>
|
|
<span class="block-name">Test event</span>
|
|
<span class="block-meta">fire all hooks matching event against synthetic payload</span>
|
|
</header>
|
|
<div class="row gap">
|
|
<select id="hook-event">
|
|
<option>pre_tool_call</option>
|
|
<option>post_tool_call</option>
|
|
<option>pre_llm_call</option>
|
|
<option>post_llm_call</option>
|
|
<option>pre_api_request</option>
|
|
<option>post_api_request</option>
|
|
<option>on_session_start</option>
|
|
<option>on_session_end</option>
|
|
<option>on_session_reset</option>
|
|
<option>subagent_stop</option>
|
|
</select>
|
|
<button class="btn-primary" data-action="hooks-test">Fire test</button>
|
|
</div>
|
|
<pre class="log" id="hooks-log" hidden></pre>
|
|
</section>
|
|
</section>
|
|
|
|
<!-- §04 PLUGINS ───────────────────────────────────────────────────── -->
|
|
<section class="pane" data-pane="plugins">
|
|
<header class="folio">
|
|
<div class="folio-rail">
|
|
<span class="folio-num">§ 04</span>
|
|
<span class="folio-tag">MANIFESTS · GITHUB MARKETPLACE</span>
|
|
</div>
|
|
<h1 class="folio-title">Plugins</h1>
|
|
<div class="folio-actions">
|
|
<button class="btn-ghost" data-action="refresh">↻ Reload</button>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="tabs" data-tab-group="plugins">
|
|
<button class="tab" data-tab="installed" data-active>Installed <span class="tab-count" id="plugins-count">·</span></button>
|
|
<button class="tab" data-tab="discover">Discover · GitHub</button>
|
|
</div>
|
|
|
|
<div class="tab-body" data-tab-pane="plugins:installed" data-active>
|
|
<div class="row gap" style="margin-bottom:14px">
|
|
<input id="plugins-filter" placeholder="filter by name, hook, source…" style="flex:1" />
|
|
<select id="plugins-status-filter" style="width:180px">
|
|
<option value="">all statuses</option>
|
|
<option value="enabled">enabled</option>
|
|
<option value="not enabled">not enabled</option>
|
|
<option value="disabled">disabled</option>
|
|
</select>
|
|
</div>
|
|
<div class="cards-grid" id="plugins-grid"></div>
|
|
</div>
|
|
|
|
<div class="tab-body" data-tab-pane="plugins:discover">
|
|
<div class="row gap" style="margin-bottom:14px">
|
|
<input id="plugins-search" placeholder="search GitHub for Hermes plugins…" style="flex:1" />
|
|
<button class="btn-primary" data-action="plugins-discover">Search</button>
|
|
</div>
|
|
<div class="row gap" style="margin-bottom:14px">
|
|
<input id="plugin-install-target" placeholder="install directly · owner/repo or https git URL" style="flex:1" />
|
|
<button class="btn" data-action="plugin-install-manual">Install</button>
|
|
</div>
|
|
<div class="cards-grid" id="plugins-discover-grid">
|
|
<div class="empty-pane">Press Search to fetch repos tagged <code>topic:hermes-plugin</code> on GitHub.</div>
|
|
</div>
|
|
<pre class="log" id="plugins-log" hidden></pre>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- §05 BUNDLES ───────────────────────────────────────────────────── -->
|
|
<section class="pane" data-pane="bundles">
|
|
<header class="folio">
|
|
<div class="folio-rail">
|
|
<span class="folio-num">§ 05</span>
|
|
<span class="folio-tag">SKILL BUNDLES · SLASH COMMANDS</span>
|
|
</div>
|
|
<h1 class="folio-title">Bundles</h1>
|
|
<div class="folio-actions">
|
|
<button class="btn-ghost" data-action="refresh">↻ Reload</button>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="block" data-block="05.A">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 05.A</span>
|
|
<span class="block-name">Your bundles</span>
|
|
<span class="block-meta">create custom slash-commands grouping multiple skills</span>
|
|
</header>
|
|
<div id="user-bundles" class="cards-grid"></div>
|
|
<div class="row gap" style="margin-top:14px">
|
|
<input id="bundle-name" placeholder="bundle name" style="width:240px" />
|
|
<input id="bundle-skills" placeholder="skills · comma-separated · e.g. github-pr-workflow, claude-code" style="flex:1" />
|
|
<button class="btn-primary" data-action="bundle-create">Create</button>
|
|
</div>
|
|
<pre class="log" id="bundles-log" hidden></pre>
|
|
</section>
|
|
|
|
<section class="block" data-block="05.B">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 05.B</span>
|
|
<span class="block-name">Bundled official skills</span>
|
|
<span class="block-meta">pre-shipped with this Hermes install · <span id="bundled-count">·</span></span>
|
|
</header>
|
|
<div class="row gap" style="margin-bottom:14px">
|
|
<input id="bundles-filter" placeholder="filter by name or category…" style="flex:1" />
|
|
<select id="bundles-cat-filter" style="width:200px"><option value="">all categories</option></select>
|
|
</div>
|
|
<div class="cards-grid" id="bundled-grid"></div>
|
|
</section>
|
|
</section>
|
|
|
|
<!-- §06 CURATOR ───────────────────────────────────────────────────── -->
|
|
<section class="pane" data-pane="curator">
|
|
<header class="folio">
|
|
<div class="folio-rail">
|
|
<span class="folio-num">§ 06</span>
|
|
<span class="folio-tag">SKILL MAINTENANCE DAEMON</span>
|
|
</div>
|
|
<h1 class="folio-title">Curator</h1>
|
|
<div class="folio-actions">
|
|
<button class="btn-ghost" data-action="curator-run">▷ Run now</button>
|
|
<button class="btn-ghost" data-action="curator-pause">⏸ Pause</button>
|
|
<button class="btn-ghost" data-action="curator-resume">▷ Resume</button>
|
|
<button class="btn-ghost" data-action="curator-prune">▷ Prune</button>
|
|
<button class="btn-ghost" data-action="refresh">↻ Reload</button>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="stat-grid" id="curator-stats"></div>
|
|
|
|
<section class="block" data-block="06.A">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 06.A</span>
|
|
<span class="block-name">Schedule</span>
|
|
</header>
|
|
<dl class="kv-list" id="curator-meta"></dl>
|
|
</section>
|
|
|
|
<section class="block" data-block="06.B">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 06.B</span>
|
|
<span class="block-name">Least recently used skills</span>
|
|
<span class="block-meta">candidates for archival</span>
|
|
</header>
|
|
<table class="table" id="curator-recent">
|
|
<thead><tr><th>Skill</th><th>Activity</th><th>Uses</th><th>Last activity</th></tr></thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</section>
|
|
|
|
<section class="block" data-block="06.C">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 06.C</span>
|
|
<span class="block-name">Archived</span>
|
|
<span class="block-meta">recoverable · never auto-deleted</span>
|
|
</header>
|
|
<ul class="list" id="curator-archived"></ul>
|
|
</section>
|
|
</section>
|
|
|
|
<!-- §10 MEMORY ────────────────────────────────────────────────────── -->
|
|
<section class="pane" data-pane="memory">
|
|
<header class="folio">
|
|
<div class="folio-rail">
|
|
<span class="folio-num">§ 11</span>
|
|
<span class="folio-tag">MEMORY.MD · USER.MD · EXTERNAL</span>
|
|
</div>
|
|
<h1 class="folio-title">Memory</h1>
|
|
<div class="folio-actions">
|
|
<button class="btn-ghost" data-action="refresh">↻ Reload</button>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="block" data-block="10.A">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 11.A</span>
|
|
<span class="block-name">External provider</span>
|
|
<span class="block-actions">
|
|
<button class="btn-mini" data-action="memory-off">Disable external</button>
|
|
</span>
|
|
</header>
|
|
<pre class="raw" id="memory-status-raw">loading…</pre>
|
|
</section>
|
|
|
|
<section class="block" data-block="10.B">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 11.B</span>
|
|
<span class="block-name">MEMORY.md</span>
|
|
<span class="block-meta">agent notes · environment facts · conventions</span>
|
|
<span class="block-actions">
|
|
<button class="btn-mini" data-action="save-memory-md" data-file="MEMORY.md">Save</button>
|
|
</span>
|
|
</header>
|
|
<textarea id="memory-md" class="editor" spellcheck="false" placeholder="(empty)"></textarea>
|
|
</section>
|
|
|
|
<section class="block" data-block="10.C">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 11.C</span>
|
|
<span class="block-name">USER.md</span>
|
|
<span class="block-meta">user profile · preferences · communication style</span>
|
|
<span class="block-actions">
|
|
<button class="btn-mini" data-action="save-memory-md" data-file="USER.md">Save</button>
|
|
</span>
|
|
</header>
|
|
<textarea id="user-md" class="editor" spellcheck="false" placeholder="(empty)"></textarea>
|
|
</section>
|
|
</section>
|
|
|
|
<!-- §11 KANBAN ────────────────────────────────────────────────────── -->
|
|
<section class="pane" data-pane="kanban">
|
|
<header class="folio">
|
|
<div class="folio-rail">
|
|
<span class="folio-num">§ 12</span>
|
|
<span class="folio-tag">MULTI-PROFILE TASK BOARD</span>
|
|
</div>
|
|
<h1 class="folio-title">Kanban</h1>
|
|
<div class="folio-actions">
|
|
<button class="btn-ghost" data-action="refresh">↻ Reload</button>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="block" data-block="11.A">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 12.A</span>
|
|
<span class="block-name">Boards</span>
|
|
</header>
|
|
<pre class="raw" id="kanban-boards-raw">loading…</pre>
|
|
</section>
|
|
|
|
<section class="block" data-block="11.B">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 12.B</span>
|
|
<span class="block-name">Tasks</span>
|
|
</header>
|
|
<pre class="raw" id="kanban-tasks-raw">loading…</pre>
|
|
</section>
|
|
|
|
<section class="block" data-block="11.C">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 12.C</span>
|
|
<span class="block-name">Create task</span>
|
|
</header>
|
|
<div class="row gap">
|
|
<input id="kanban-board" placeholder="board slug (optional)" style="width:200px" />
|
|
<input id="kanban-title" placeholder="task title" />
|
|
<button class="btn-primary" data-action="kanban-create">Create</button>
|
|
</div>
|
|
<pre class="log" id="kanban-log" hidden></pre>
|
|
</section>
|
|
</section>
|
|
|
|
<!-- §12 WEBHOOKS ──────────────────────────────────────────────────── -->
|
|
<section class="pane" data-pane="webhooks">
|
|
<header class="folio">
|
|
<div class="folio-rail">
|
|
<span class="folio-num">§ 13</span>
|
|
<span class="folio-tag">EVENT SUBSCRIPTIONS</span>
|
|
</div>
|
|
<h1 class="folio-title">Webhooks</h1>
|
|
<div class="folio-actions">
|
|
<button class="btn-ghost" data-action="refresh">↻ Reload</button>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="block" data-block="12.A">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 13.A</span>
|
|
<span class="block-name">Active subscriptions</span>
|
|
</header>
|
|
<pre class="raw" id="webhooks-raw">loading…</pre>
|
|
</section>
|
|
|
|
<section class="block" data-block="12.B">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 13.B</span>
|
|
<span class="block-name">Add subscription</span>
|
|
</header>
|
|
<div class="row gap">
|
|
<input id="webhook-url" placeholder="https://target.example.com/hook" />
|
|
<input id="webhook-event" placeholder="event filter (optional)" style="width:240px" />
|
|
<button class="btn-primary" data-action="webhook-add">Add</button>
|
|
</div>
|
|
<div class="row gap" style="margin-top:8px">
|
|
<input id="webhook-test-target" placeholder="id or URL to test" />
|
|
<button class="btn" data-action="webhook-test">Send test POST</button>
|
|
</div>
|
|
<pre class="log" id="webhooks-log" hidden></pre>
|
|
</section>
|
|
</section>
|
|
|
|
<!-- §13 PROFILES ──────────────────────────────────────────────────── -->
|
|
<section class="pane" data-pane="profiles">
|
|
<header class="folio">
|
|
<div class="folio-rail">
|
|
<span class="folio-num">§ 14</span>
|
|
<span class="folio-tag">PROFILES · PAIRING</span>
|
|
</div>
|
|
<h1 class="folio-title">Profiles</h1>
|
|
<div class="folio-actions">
|
|
<button class="btn-ghost" data-action="refresh">↻ Reload</button>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="block" data-block="13.A">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 14.A</span>
|
|
<span class="block-name">Profiles</span>
|
|
</header>
|
|
<pre class="raw" id="profiles-raw">loading…</pre>
|
|
<div class="row gap" style="margin-top:14px">
|
|
<input id="profile-name" placeholder="profile name" style="width:240px" />
|
|
<button class="btn" data-action="profile-create">Create</button>
|
|
<button class="btn" data-action="profile-use">Switch to</button>
|
|
<button class="btn-danger" data-action="profile-delete">Delete</button>
|
|
</div>
|
|
<pre class="log" id="profiles-log" hidden></pre>
|
|
</section>
|
|
|
|
<section class="block" data-block="13.B">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 14.B</span>
|
|
<span class="block-name">Pairing codes</span>
|
|
<span class="block-meta">DM user authorization</span>
|
|
<span class="block-actions">
|
|
<button class="btn-mini" data-action="pairing-clear">Clear pending</button>
|
|
</span>
|
|
</header>
|
|
<pre class="raw" id="pairing-raw">loading…</pre>
|
|
<div class="row gap" style="margin-top:14px">
|
|
<input id="pairing-subject" placeholder="code (to approve) or user (to revoke)" />
|
|
<button class="btn" data-action="pairing-approve">Approve</button>
|
|
<button class="btn-danger" data-action="pairing-revoke">Revoke</button>
|
|
</div>
|
|
</section>
|
|
</section>
|
|
|
|
<!-- §14 CONFIG ────────────────────────────────────────────────────── -->
|
|
<section class="pane" data-pane="config">
|
|
<header class="folio">
|
|
<div class="folio-rail">
|
|
<span class="folio-num">§ 16</span>
|
|
<span class="folio-tag">CONFIG.YAML · VIEW · CHECK</span>
|
|
</div>
|
|
<h1 class="folio-title">Config</h1>
|
|
<div class="folio-actions">
|
|
<button class="btn-ghost" data-action="refresh">↻ Reload</button>
|
|
<button class="btn-ghost" data-action="config-check">▷ Check</button>
|
|
<button class="btn-ghost" data-action="config-migrate">▷ Migrate</button>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="block" data-block="14.A">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 16.A</span>
|
|
<span class="block-name">Path</span>
|
|
</header>
|
|
<div class="kv" id="config-path-row" style="font-family:var(--ff-mono);font-size:11px"></div>
|
|
</section>
|
|
|
|
<section class="block" data-block="14.B">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 16.B</span>
|
|
<span class="block-name">Resolved configuration</span>
|
|
<span class="block-meta">merged: file + env + defaults</span>
|
|
</header>
|
|
<pre class="raw" id="config-raw">loading…</pre>
|
|
</section>
|
|
|
|
<section class="block" data-block="14.C">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 16.C</span>
|
|
<span class="block-name">Set key</span>
|
|
<span class="block-meta">dotted path · e.g. compression.threshold</span>
|
|
</header>
|
|
<div class="row gap">
|
|
<input id="config-key" placeholder="e.g. agent.reasoning_effort" />
|
|
<input id="config-value" placeholder="value" />
|
|
<button class="btn-primary" data-action="config-set">Set</button>
|
|
</div>
|
|
<pre class="log" id="config-log" hidden></pre>
|
|
</section>
|
|
</section>
|
|
|
|
<!-- §15 SECURITY ──────────────────────────────────────────────────── -->
|
|
<section class="pane" data-pane="security">
|
|
<header class="folio">
|
|
<div class="folio-rail">
|
|
<span class="folio-num">§ 19</span>
|
|
<span class="folio-tag">SUPPLY-CHAIN AUDIT</span>
|
|
</div>
|
|
<h1 class="folio-title">Security</h1>
|
|
<div class="folio-actions">
|
|
<button class="btn-ghost" data-action="security-run">▷ Run audit</button>
|
|
</div>
|
|
</header>
|
|
<pre class="raw" id="security-raw">Press “▷ Run audit”. Scans venv + plugin deps + pinned MCP servers against OSV.dev.</pre>
|
|
</section>
|
|
|
|
<!-- §16 STORAGE ───────────────────────────────────────────────────── -->
|
|
<section class="pane" data-pane="storage">
|
|
<header class="folio">
|
|
<div class="folio-rail">
|
|
<span class="folio-num">§ 15</span>
|
|
<span class="folio-tag">BACKUP · CHECKPOINTS</span>
|
|
</div>
|
|
<h1 class="folio-title">Storage</h1>
|
|
<div class="folio-actions">
|
|
<button class="btn-ghost" data-action="refresh">↻ Reload</button>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="block" data-block="16.A">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 15.A</span>
|
|
<span class="block-name">Backup archive</span>
|
|
<span class="block-meta">zip of config + skills + sessions + memories</span>
|
|
</header>
|
|
<div class="row gap">
|
|
<input id="backup-label" placeholder="optional label" style="width:280px" />
|
|
<button class="btn-primary" data-action="backup-full">Full backup</button>
|
|
<button class="btn" data-action="backup-quick">Quick snapshot</button>
|
|
</div>
|
|
<pre class="log" id="backup-log" hidden></pre>
|
|
</section>
|
|
|
|
<section class="block" data-block="16.B">
|
|
<header class="block-head">
|
|
<span class="block-id">§ 15.B</span>
|
|
<span class="block-name">Checkpoints store</span>
|
|
<span class="block-meta">shadow git · rollback snapshots</span>
|
|
<span class="block-actions">
|
|
<button class="btn-mini" data-action="checkpoints-prune">Prune</button>
|
|
<button class="btn-danger" data-action="checkpoints-clear">Clear all</button>
|
|
</span>
|
|
</header>
|
|
<pre class="raw" id="checkpoints-raw">loading…</pre>
|
|
</section>
|
|
</section>
|
|
|
|
<!-- §17 INSIGHTS ──────────────────────────────────────────────────── -->
|
|
<section class="pane" data-pane="insights">
|
|
<header class="folio">
|
|
<div class="folio-rail">
|
|
<span class="folio-num">§ 18</span>
|
|
<span class="folio-tag">USAGE · TOKENS · COSTS</span>
|
|
</div>
|
|
<h1 class="folio-title">Insights</h1>
|
|
<div class="folio-actions">
|
|
<label class="field-inline" style="padding-bottom:0">
|
|
<span>window</span>
|
|
<input id="insights-days" type="number" min="1" value="30" />
|
|
<span>days</span>
|
|
</label>
|
|
<button class="btn-ghost" data-action="insights-run">▷ Compute</button>
|
|
</div>
|
|
</header>
|
|
<pre class="raw" id="insights-raw">Press “▷ Compute”. Analyzes session trajectories for token usage, costs, tool patterns.</pre>
|
|
</section>
|
|
|
|
<!-- §18 SYSTEM ────────────────────────────────────────────────────── -->
|
|
<section class="pane" data-pane="system">
|
|
<header class="folio">
|
|
<div class="folio-rail">
|
|
<span class="folio-num">§ 20</span>
|
|
<span class="folio-tag">DIAGNOSTICS · LOGS · UPDATE</span>
|
|
</div>
|
|
<h1 class="folio-title">System</h1>
|
|
<div class="folio-actions">
|
|
<button class="btn-ghost" data-action="run-doctor">▷ Doctor</button>
|
|
<button class="btn-ghost" data-action="refresh">▷ Status</button>
|
|
<button class="btn-ghost" data-action="tail-logs">▷ Tail logs</button>
|
|
<button class="btn-ghost" data-action="prompt-size">▷ Prompt size</button>
|
|
<button class="btn-ghost" data-action="dump">▷ Dump</button>
|
|
<button class="btn-ghost" data-action="portal-info">▷ Portal</button>
|
|
<button class="btn-ghost" data-action="run-update">▷ Update</button>
|
|
</div>
|
|
</header>
|
|
<pre class="raw" id="system-raw">loading…</pre>
|
|
</section>
|
|
|
|
<!-- §21 API USERS ──────────────────────────────────────────────────── -->
|
|
<section class="pane" data-pane="api-users">
|
|
<header class="folio">
|
|
<div class="folio-rail">
|
|
<span class="folio-num">§ 21</span>
|
|
<span class="folio-tag">ACCESS · API KEYS · RATE LIMITS</span>
|
|
</div>
|
|
<h1 class="folio-title">API Users</h1>
|
|
<div class="folio-actions">
|
|
<button class="btn-primary" id="new-api-user-btn">+ New API User</button>
|
|
</div>
|
|
</header>
|
|
|
|
<table id="api-users-table" class="api-user-table">
|
|
<thead>
|
|
<tr>
|
|
<th>User / Status</th>
|
|
<th>API Key (masked)</th>
|
|
<th>Access</th>
|
|
<th>Rate / Month</th>
|
|
<th>Last Used</th>
|
|
<th>Expires</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="api-users-tbody"></tbody>
|
|
</table>
|
|
|
|
<dialog id="api-user-dialog">
|
|
<form method="dialog" id="api-user-form">
|
|
<h2 id="api-user-dialog-title">Create API User</h2>
|
|
<div class="dialog-field">
|
|
<label for="api-user-name">Display Name</label>
|
|
<input id="api-user-name" type="text" required autocomplete="off" />
|
|
</div>
|
|
<div class="dialog-field dialog-field-check">
|
|
<label><input id="api-user-pre" type="checkbox" /> Pre-Hermes access</label>
|
|
</div>
|
|
<div class="dialog-field dialog-field-check">
|
|
<label><input id="api-user-post" type="checkbox" /> Post-Hermes access</label>
|
|
</div>
|
|
<div class="dialog-field">
|
|
<label for="api-user-rpm">Requests per minute</label>
|
|
<input id="api-user-rpm" type="number" min="1" />
|
|
</div>
|
|
<div class="dialog-field">
|
|
<label for="api-user-monthly">Monthly token limit</label>
|
|
<input id="api-user-monthly" type="number" min="1" />
|
|
</div>
|
|
<div class="dialog-field">
|
|
<label for="api-user-expires">Expires at</label>
|
|
<input id="api-user-expires" type="datetime-local" />
|
|
<div class="expires-presets">
|
|
<button type="button" class="btn-mini" data-expires-preset="30d">30 days</button>
|
|
<button type="button" class="btn-mini" data-expires-preset="90d">90 days</button>
|
|
<button type="button" class="btn-mini" data-expires-preset="1y">1 year</button>
|
|
<button type="button" class="btn-mini" data-expires-preset="never">Never</button>
|
|
</div>
|
|
</div>
|
|
<div id="api-user-dialog-error" class="dialog-error"></div>
|
|
<div class="dialog-actions">
|
|
<button type="submit" class="btn-primary" id="api-user-save-btn">Save</button>
|
|
<button type="button" class="btn-ghost" id="api-user-cancel-btn">Cancel</button>
|
|
</div>
|
|
</form>
|
|
</dialog>
|
|
|
|
<dialog id="api-key-reveal-dialog">
|
|
<div class="reveal-body">
|
|
<h2>API Key Created</h2>
|
|
<p class="reveal-warning">This key will only be shown once. Copy it now.</p>
|
|
<div class="reveal-key-wrap">
|
|
<code id="reveal-key-text"></code>
|
|
</div>
|
|
<div class="dialog-actions">
|
|
<button type="button" class="btn-primary" id="reveal-copy-btn">Copy to clipboard</button>
|
|
<button type="button" class="btn-ghost" id="reveal-done-btn">Done</button>
|
|
</div>
|
|
</div>
|
|
</dialog>
|
|
</section>
|
|
</main>
|
|
|
|
<!-- ─── Readout / status bar ─────────────────────────────────────────── -->
|
|
<footer class="readout">
|
|
<span class="rd-tag">OPS</span>
|
|
<span class="rd-sep">│</span>
|
|
<span class="rd-cell"><span class="rd-k">pri</span> <span id="bar-primary">·</span></span>
|
|
<span class="rd-sep">│</span>
|
|
<span class="rd-cell"><span class="rd-k">fbk</span> <span id="bar-chain">·</span></span>
|
|
<span class="rd-sep">│</span>
|
|
<span class="rd-cell"><span class="rd-k">ver</span> <span id="bar-version">·</span></span>
|
|
<span class="rd-sep">│</span>
|
|
<span class="grow"></span>
|
|
<span class="rd-cell rd-msg" id="bar-msg"></span>
|
|
<span class="rd-led" id="bar-led" aria-hidden="true"></span>
|
|
</footer>
|
|
|
|
<script src="app.js"></script>
|
|
</body>
|
|
</html>
|