diff --git a/website/docs/user-guide/features/dashboard-plugins.md b/website/docs/user-guide/features/dashboard-plugins.md deleted file mode 100644 index 8d87c031f4..0000000000 --- a/website/docs/user-guide/features/dashboard-plugins.md +++ /dev/null @@ -1,336 +0,0 @@ ---- -sidebar_position: 16 -title: "Dashboard Plugins" -description: "Build custom tabs and extensions for the Hermes web dashboard" ---- - -# Dashboard Plugins - -Dashboard plugins let you add custom tabs to the web dashboard. A plugin can display its own UI, call the Hermes API, and optionally register backend endpoints — all without touching the dashboard source code. - -## Quick Start - -Create a plugin directory with a manifest and a JS file: - -```bash -mkdir -p ~/.hermes/plugins/my-plugin/dashboard/dist -``` - -**manifest.json:** - -```json -{ - "name": "my-plugin", - "label": "My Plugin", - "icon": "Sparkles", - "version": "1.0.0", - "tab": { - "path": "/my-plugin", - "position": "after:skills" - }, - "entry": "dist/index.js" -} -``` - -**dist/index.js:** - -```javascript -(function () { - var SDK = window.__HERMES_PLUGIN_SDK__; - var React = SDK.React; - var Card = SDK.components.Card; - var CardHeader = SDK.components.CardHeader; - var CardTitle = SDK.components.CardTitle; - var CardContent = SDK.components.CardContent; - - function MyPage() { - return React.createElement(Card, null, - React.createElement(CardHeader, null, - React.createElement(CardTitle, null, "My Plugin") - ), - React.createElement(CardContent, null, - React.createElement("p", { className: "text-sm text-muted-foreground" }, - "Hello from my custom dashboard tab!" - ) - ) - ); - } - - window.__HERMES_PLUGINS__.register("my-plugin", MyPage); -})(); -``` - -Refresh the dashboard — your tab appears in the navigation bar. - -## Plugin Structure - -Plugins live inside the standard `~/.hermes/plugins/` directory. The dashboard extension is a `dashboard/` subfolder: - -``` -~/.hermes/plugins/my-plugin/ - plugin.yaml # optional — existing CLI/gateway plugin manifest - __init__.py # optional — existing CLI/gateway hooks - dashboard/ # dashboard extension - manifest.json # required — tab config, icon, entry point - dist/ - index.js # required — pre-built JS bundle - style.css # optional — custom CSS - plugin_api.py # optional — backend API routes -``` - -A single plugin can extend both the CLI/gateway (via `plugin.yaml` + `__init__.py`) and the dashboard (via `dashboard/`) from one directory. - -## Manifest Reference - -The `manifest.json` file describes your plugin to the dashboard: - -```json -{ - "name": "my-plugin", - "label": "My Plugin", - "description": "What this plugin does", - "icon": "Sparkles", - "version": "1.0.0", - "tab": { - "path": "/my-plugin", - "position": "after:skills" - }, - "entry": "dist/index.js", - "css": "dist/style.css", - "api": "plugin_api.py" -} -``` - -| Field | Required | Description | -|-------|----------|-------------| -| `name` | Yes | Unique plugin identifier (lowercase, hyphens ok) | -| `label` | Yes | Display name shown in the nav tab | -| `description` | No | Short description | -| `icon` | No | Lucide icon name (default: `Puzzle`) | -| `version` | No | Semver version string | -| `tab.path` | Yes | URL path for the tab (e.g. `/my-plugin`) | -| `tab.position` | No | Where to insert the tab: `end` (default), `after:`, `before:` | -| `entry` | Yes | Path to the JS bundle relative to `dashboard/` | -| `css` | No | Path to a CSS file to inject | -| `api` | No | Path to a Python file with FastAPI routes | - -### Tab Position - -The `position` field controls where your tab appears in the navigation: - -- `"end"` — after all built-in tabs (default) -- `"after:skills"` — after the Skills tab -- `"before:config"` — before the Config tab -- `"after:cron"` — after the Cron tab - -The value after the colon is the path segment of the target tab (without the leading slash). - -### Available Icons - -Plugins can use any of these Lucide icon names: - -`Activity`, `BarChart3`, `Clock`, `Code`, `Database`, `Eye`, `FileText`, `Globe`, `Heart`, `KeyRound`, `MessageSquare`, `Package`, `Puzzle`, `Settings`, `Shield`, `Sparkles`, `Star`, `Terminal`, `Wrench`, `Zap` - -Unrecognized icon names fall back to `Puzzle`. - -## Plugin SDK - -Plugins don't bundle React or UI components — they use the SDK exposed on `window.__HERMES_PLUGIN_SDK__`. This avoids version conflicts and keeps plugin bundles tiny. - -### SDK Contents - -```javascript -var SDK = window.__HERMES_PLUGIN_SDK__; - -// React -SDK.React // React instance -SDK.hooks.useState // React hooks -SDK.hooks.useEffect -SDK.hooks.useCallback -SDK.hooks.useMemo -SDK.hooks.useRef -SDK.hooks.useContext -SDK.hooks.createContext - -// API -SDK.api // Hermes API client (getStatus, getSessions, etc.) -SDK.fetchJSON // Raw fetch for custom endpoints — handles auth automatically - -// UI Components (shadcn/ui style) -SDK.components.Card -SDK.components.CardHeader -SDK.components.CardTitle -SDK.components.CardContent -SDK.components.Badge -SDK.components.Button -SDK.components.Input -SDK.components.Label -SDK.components.Select -SDK.components.SelectOption -SDK.components.Separator -SDK.components.Tabs -SDK.components.TabsList -SDK.components.TabsTrigger - -// Utilities -SDK.utils.cn // Tailwind class merger (clsx + twMerge) -SDK.utils.timeAgo // "5m ago" from unix timestamp -SDK.utils.isoTimeAgo // "5m ago" from ISO string - -// Hooks -SDK.useI18n // i18n translations -SDK.useTheme // Current theme info -``` - -### Using SDK.fetchJSON - -For calling your plugin's backend API endpoints: - -```javascript -SDK.fetchJSON("/api/plugins/my-plugin/data") - .then(function (result) { - console.log(result); - }) - .catch(function (err) { - console.error("API call failed:", err); - }); -``` - -`fetchJSON` automatically injects the session auth token, handles errors, and parses JSON. - -### Using Existing API Methods - -The `SDK.api` object has methods for all built-in Hermes endpoints: - -```javascript -// Fetch agent status -SDK.api.getStatus().then(function (status) { - console.log("Version:", status.version); -}); - -// List sessions -SDK.api.getSessions(10).then(function (resp) { - console.log("Sessions:", resp.sessions.length); -}); -``` - -## Backend API Routes - -Plugins can register FastAPI routes by setting the `api` field in the manifest. Create a Python file that exports a `router`: - -```python -# plugin_api.py -from fastapi import APIRouter - -router = APIRouter() - -@router.get("/data") -async def get_data(): - return {"items": ["one", "two", "three"]} - -@router.post("/action") -async def do_action(body: dict): - return {"ok": True, "received": body} -``` - -Routes are mounted at `/api/plugins//`, so the above becomes: -- `GET /api/plugins/my-plugin/data` -- `POST /api/plugins/my-plugin/action` - -Plugin API routes bypass session token authentication since the dashboard server only binds to localhost. - -### Accessing Hermes Internals - -Backend routes can import from the hermes-agent codebase: - -```python -from fastapi import APIRouter -from hermes_state import SessionDB -from hermes_cli.config import load_config - -router = APIRouter() - -@router.get("/session-count") -async def session_count(): - db = SessionDB() - try: - count = len(db.list_sessions(limit=9999)) - return {"count": count} - finally: - db.close() -``` - -## Custom CSS - -If your plugin needs custom styles, add a CSS file and reference it in the manifest: - -```json -{ - "css": "dist/style.css" -} -``` - -The CSS file is injected as a `` tag when the plugin loads. Use specific class names to avoid conflicts with the dashboard's existing styles. - -```css -/* dist/style.css */ -.my-plugin-chart { - border: 1px solid var(--color-border); - background: var(--color-card); - padding: 1rem; -} -``` - -You can use the dashboard's CSS custom properties (e.g. `--color-border`, `--color-foreground`) to match the active theme. - -## Plugin Loading Flow - -1. Dashboard loads — `main.tsx` exposes the SDK on `window.__HERMES_PLUGIN_SDK__` -2. `App.tsx` calls `usePlugins()` which fetches `GET /api/dashboard/plugins` -3. For each plugin: CSS `` injected (if declared), JS `