mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
feat: dashboard plugin system — extend the web UI with custom tabs
Add a plugin system that lets plugins add new tabs to the dashboard.
Plugins live in ~/.hermes/plugins/<name>/dashboard/ alongside any
existing CLI/gateway plugin code.
Plugin structure:
plugins/<name>/dashboard/
manifest.json # name, label, icon, tab config, entry point
dist/index.js # pre-built JS bundle (IIFE, uses SDK globals)
plugin_api.py # optional FastAPI router mounted at /api/plugins/<name>/
Backend (hermes_cli/web_server.py):
- Plugin discovery: scans plugins/*/dashboard/manifest.json from user,
bundled, and project plugin directories
- GET /api/dashboard/plugins — returns discovered plugin manifests
- GET /api/dashboard/plugins/rescan — force re-discovery
- GET /dashboard-plugins/<name>/<path> — serves plugin static assets
with path traversal protection
- Optional API route mounting: imports plugin_api.py and mounts its
router under /api/plugins/<name>/
- Plugin API routes bypass session token auth (localhost-only)
Frontend (web/src/plugins/):
- Plugin SDK exposed on window.__HERMES_PLUGIN_SDK__ — provides React,
hooks, UI components (Card, Badge, Button, etc.), API client,
fetchJSON, theme/i18n hooks, and utilities
- Plugin registry on window.__HERMES_PLUGINS__.register(name, Component)
- usePlugins() hook: fetches manifests, loads JS/CSS, resolves components
- App.tsx dynamically adds nav items and routes for discovered plugins
- Icon resolution via static map of 20 common Lucide icons (no tree-
shaking penalty — bundle only +5KB over baseline)
Example plugin (plugins/example-dashboard/):
- Demonstrates SDK usage: Card components, backend API call, SDK reference
- Backend route: GET /api/plugins/example/hello
Tested: plugin discovery, static serving, API routes, path traversal
blocking, unknown plugin 404, bundle size (400KB vs 394KB baseline).
This commit is contained in:
parent
23a42635f0
commit
01214a7f73
11 changed files with 660 additions and 15 deletions
|
|
@ -9,7 +9,7 @@ declare global {
|
|||
}
|
||||
let _sessionToken: string | null = null;
|
||||
|
||||
async function fetchJSON<T>(url: string, init?: RequestInit): Promise<T> {
|
||||
export async function fetchJSON<T>(url: string, init?: RequestInit): Promise<T> {
|
||||
// Inject the session token into all /api/ requests.
|
||||
const headers = new Headers(init?.headers);
|
||||
const token = window.__HERMES_SESSION_TOKEN__;
|
||||
|
|
@ -192,6 +192,12 @@ export const api = {
|
|||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ name }),
|
||||
}),
|
||||
|
||||
// Dashboard plugins
|
||||
getPlugins: () =>
|
||||
fetchJSON<PluginManifestResponse[]>("/api/dashboard/plugins"),
|
||||
rescanPlugins: () =>
|
||||
fetchJSON<{ ok: boolean; count: number }>("/api/dashboard/plugins/rescan"),
|
||||
};
|
||||
|
||||
export interface PlatformStatus {
|
||||
|
|
@ -432,3 +438,18 @@ export interface ThemeListResponse {
|
|||
themes: Array<{ name: string; label: string; description: string }>;
|
||||
active: string;
|
||||
}
|
||||
|
||||
// ── Dashboard plugin types ─────────────────────────────────────────────
|
||||
|
||||
export interface PluginManifestResponse {
|
||||
name: string;
|
||||
label: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
version: string;
|
||||
tab: { path: string; position: string };
|
||||
entry: string;
|
||||
css?: string | null;
|
||||
has_api: boolean;
|
||||
source: string;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue