Skip to content

Latest commit

 

History

History
649 lines (470 loc) · 11.2 KB

File metadata and controls

649 lines (470 loc) · 11.2 KB

API Reference

Base URL: http://localhost:10272 (or your domain if behind a reverse-proxy)


Authentication

All endpoints marked Auth required require a valid session cookie (hermes...auth).

Login first via POST /api/auth/login to receive the cookie.

Internal endpoints (marked Internal) require the x-hermes-control-secret header matching HERMES_CONTROL_SECRET instead of a cookie.


Endpoints

GET /api/health

Auth required: No

Returns a basic health check.

{
  "ok": true,
  "title": "Hermes Control Interface",
  "auth": true,
  "ws": "/ws"
}

GET /api/auth/status

Auth required: No

Returns the current authentication state.

{
  "authenticated": false,
  "passwordRequired": true,
  "identity": "root@hermes"
}

POST /api/auth/login

Auth required: No

Rate limited: 5 attempts per 15 minutes per IP.

Request body:

{ "password": "***" }

Success response (200):

{ "ok": true }

Sets the hermes...auth cookie.

Failure response (401):

{ "ok": false, "error": "bad password" }

Rate limited response (429):

{
  "ok": false,
  "error": "too many failed attempts, try again in 15 minutes"
}

POST /api/auth/logout

Auth required: No (but does nothing if not authenticated)

Clears the session cookie.

{ "ok": true }

GET /api/profiles

Auth required: Yes

Returns all configured Hermes profiles with their status.

Response:

{
  "ok": true,
  "profiles": [
    { "name": "default", "model": "xiaomi/mimo-v2-pro", "gateway": "stopped", "alias": null, "active": false },
    { "name": "david", "model": "xiaomi/mimo-v2-pro", "gateway": "running", "alias": "david", "active": true },
    { "name": "soci", "model": "gpt-5.4-mini", "gateway": "stopped", "alias": "soci", "active": false }
  ]
}

POST /api/profiles/use

Auth required: Yes (CSRF required)

Sets the active (default) Hermes profile.

Request body:

{ "profile": "david" }

Response:

{ "ok": true, "profile": "david", "output": "..." }

GET /api/gateway/:profile

Auth required: Yes

Returns the systemd service status for a profile's gateway.

Response:

{
  "ok": true,
  "profile": "david",
  "service": "hermes-gateway-david",
  "active": true,
  "enabled": true,
  "status": "● hermes-gateway-david.service - Hermes Gateway - david..."
}

POST /api/gateway/:profile/:action

Auth required: Yes (CSRF required)

Controls a gateway systemd service.

Actions: start, stop, restart, enable, disable

Response:

{
  "ok": true,
  "profile": "david",
  "action": "start",
  "active": true,
  "output": ""
}

GET /api/gateway/:profile/logs

Auth required: Yes

Returns journal logs for a gateway service.

Query parameters:

  • lines (optional) — number of log lines to return (default: 50, max: 500)

Response:

{
  "ok": true,
  "profile": "david",
  "service": "hermes-gateway-david",
  "logs": "Apr 10 23:00:00 vm1 systemd[1]: Started Hermes Gateway - david..."
}

GET /api/explorer

Auth required: Yes

Returns directory trees for configured explorer roots.

Query parameters:

  • root (optional) — key of a specific root to query. If omitted, returns all roots.

Response:

[
  {
    "key": "projects",
    "label": "/root/projects",
    "root": "/root/projects",
    "children": [
      {
        "name": "my-project",
        "path": "/root/projects/my-project",
        "rel": "my-project",
        "type": "dir",
        "depth": 0,
        "children": [...]
      }
    ]
  }
]

GET /api/file

Auth required: Yes

Reads a file.

Query parameters:

  • path — absolute path to the file (must be within an explorer root)

Response (200):

{
  "path": "/root/projects/my-project/file.js",
  "content": "console.log('hello')"
}

Error responses:

  • 400 — path missing or outside allowed roots
  • 404 — file not found
  • 400 — path is a directory

POST /api/file

Auth required: Yes

Writes a file.

Request body:

{
  "path": "/root/projects/my-project/file.js",
  "content": "console.log('updated')"
}

Response (200):

{
  "ok": true,
  "path": "/root/projects/my-project/file.js",
  "bytes": 24
}

POST /api/terminal/exec

Auth required: Yes

Queues a command in the PTY terminal session.

Request body:

{ "command": "ls -la" }

Response:

{
  "ok": true,
  "queued": true,
  "command": "ls -la",
  "cwd": "/root/projects/hermes-control-interface",
  "identity": "root@hermes",
  "ready": true,
  "buffer": "...terminal output...",
  "timestamp": "2026-04-09T00:00:00.000Z"
}

Special commands starting with /cron are handled internally and marked "special": true.


POST /api/cron/:action

Auth required: Yes

Actions: add, list, remove, pause, resume

add request body:

{
  "schedule": "30m",
  "note": "daily report",
  "deliver": "origin"
}

remove request body:

{ "id": "abc123" }

pause / resume request body:

{ "id": "abc123" }

list takes no body.


POST /internal/cron/:action

Auth required: Internal (header-based)

Same as /api/cron/:action but uses x-hermes-control-secret header for authentication. Used by Hermes itself to trigger cron actions internally.


GET /usage / GET /api/usage

Auth required: Yes

Returns system resource usage.

{
  "memUsed": "842 MB",
  "memTotal": "1.96 GB",
  "diskUsed": "38 GB",
  "diskTotal": "49 GB",
  "cpuCores": 2,
  "loadAvg": [0.12, 0.08, 0.05],
  "uptime": 3600,
  "hostname": "vm1"
}

GET /api/layout

Auth required: Yes

Returns the saved dashboard layout.

{
  "ok": true,
  "layout": {
    "updatedAt": "2026-04-09T00:00:00.000Z",
    "panels": [
      { "id": "terminal", "x": 0, "y": 0, "w": 800, "h": 400 }
    ]
  }
}

POST /api/layout

Auth required: Yes

Saves dashboard layout.

Request body:

{
  "panels": [
    { "id": "terminal", "x": 0, "y": 0, "w": 800, "h": 400 }
  ]
}

GET /api/avatar

Auth required: Yes

Returns avatar metadata (URL reference, not the full image data).

{
  "ok": true,
  "url": "/api/avatar/image",
  "custom": true
}

GET /api/avatar/image

Auth required: Yes

Returns the avatar image as raw binary with proper Content-Type and Cache-Control headers. Use this endpoint in <img> tags instead of embedding base64 data.

Response: Raw image bytes (image/jpeg, image/png, or image/webp).

Headers:

  • Cache-Control: private, max-age=3600 (1 hour browser cache)
  • Content-Type: image/* (matches the uploaded format)

POST /api/avatar

Auth required: Yes

Uploads a custom avatar image.

Request body:

{ "dataUrl": "data:image/png;base64,iVBORw0KGgo..." }

Accepted formats: PNG, JPEG, WebP.

Response:

{
  "ok": true,
  "url": "/api/avatar/image",
  "custom": true
}

Triggers a WebSocket snapshot broadcast so connected clients refresh the avatar.


DELETE /api/avatar

Auth required: Yes

Resets avatar to the default photo. Triggers a WebSocket broadcast.


WebSocket (/ws)

Auth required: Yes (via cookie)

Client → Server messages

Ping:

{ "type": "ping" }

Terminal input:

{ "type": "terminal-input", "data": "ls -la\n" }

Terminal resize:

{ "type": "terminal-resize", "cols": 120, "rows": 32 }

Server → Client messages

Dashboard snapshot (sent on connect):

{
  "type": "snapshot",
  "payload": { /* dashboard-state object */ }
}

Live updates:

{
  "type": "snapshot",
  "payload": { /* updated dashboard-state */ }
}

Terminal output (from PTY to client):

{
  "type": "terminal-output",
  "chunk": "root@hermes:~$ ",
  "buffer": "...",
  "ready": true,
  "cwd": "/root/projects/hermes-control-interface",
  "prompt": "root@hermes:/root/projects/hermes-control-interface# "
}

Terminal transcript (sent on reconnect if session was active):

{
  "type": "terminal-transcript",
  "buffer": "...",
  "ready": true,
  "cwd": "...",
  "prompt": "...",
  "cols": 120,
  "rows": 32
}

Pong:

{ "type": "pong", "ts": 1712600000000 }

Office v3 API

GET /api/office/kanban?board=main

Auth required: Yes

Returns the full kanban board for a given board name.

{
  "ok": true,
  "tasks": [{ "id": "t_...", "title": "...", "status": "done", "assignee": "david", ... }],
  "links": [{ "parent_id": "t_...", "child_id": "t_..." }]
}

GET /api/office/kanban/:taskId?board=main

Auth required: Yes

Returns a single task with full detail — runs, events, comments, attachments, links.

{
  "ok": true,
  "task": { "id": "t_...", "title": "...", "status": "done", ... },
  "runs": [{ "id": 1, "status": "done", "outcome": "completed", "summary": "...", "metadata": {...} }],
  "events": [{ "id": 1, "kind": "created", "payload": {...}, "created_at": ... }],
  "comments": [{ "id": 1, "author": "david", "body": "Fixed the issue", "created_at": ... }],
  "attachments": [{ "id": 1, "filename": "report.md", "size": 2048 }],
  "links": [{ "parent_id": "t_...", "child_id": "t_..." }]
}

GET /api/office/kanban/:taskId/workspace-file?board=main&path=file.py

Auth required: Yes

Reads a file from the task's workspace directory. Path traversal protected.

Directory listing (path ends with . or is a directory):

{
  "ok": true,
  "files": [{ "name": "script.py", "size": 15733, "isDir": false, "mtime": ... }],
  "isDir": true
}

File content (max 500KB, auto-detects language):

{
  "ok": true,
  "filename": "script.py",
  "size": 15733,
  "language": "py",
  "content": "#!/usr/bin/env python3\n..."
}

POST /api/office/kanban/:taskId/action?board=main

Auth required: Yes (CSRF required)

Quick actions on tasks. Body: { "action": "...", "assignee": "..." }

Valid actions: unblock, done, reopen, start, reassign

{ "ok": true, "action": "done", "taskId": "t_...", "board": "main" }

GET /api/office/agent-states

Auth required: Yes

Returns agent health grid — model, provider, active/blocked tasks, gateway alive.

{
  "ok": true,
  "agents": [{ "name": "david", "model": "deepseek-v4-pro", "state": "idle", "activeTasks": 0, "blockedTasks": 0 }],
  "timestamp": 1712600000000
}

GET /api/office/events

Auth required: Admin only

Returns live feed events from gateway logs (max 50).

{
  "ok": true,
  "events": [{ "timestamp": "2026-06-03 14:22:10", "agent": "david", "action": "web_search", "emoji": "🔍" }]
}

GET /api/office/summary?board=main

Auth required: Yes

Board-level statistics with agent breakdown and recommendations.

{
  "ok": true,
  "overview": { "total": 19, "running": 1, "blocked": 1, "done": 13 },
  "agents": [{ "name": "david", "active": 1, "done": 5 }],
  "alerts": ["⚠️ 1 task blocked"],
  "recommendations": ["Review blocked task t_..."]
}