feat(capabilities): detect Hermes kanban plugin (foundation for /swarm <-> dashboard sync)
Adds capability detection for the upstream Hermes Agent kanban plugin
mounted at /api/plugins/kanban/. Lays the groundwork for the v2.3.0
work where the workspace's /swarm kanban surface syncs with the
dashboard's SQLite-backed kanban DB.
Changes:
* gateway-capabilities.ts: new probeKanban() probes
/api/plugins/kanban/board on the dashboard URL with a short timeout.
GatewayCapabilities now carries a 'kanban' boolean. Probed once per
PROBE_TTL_MS alongside conductor.
* connection-status.ts: surfaces caps.kanban so client-side feature
gates can react.
* use-feature-capability.ts + feature-gates.ts: 'kanban' is now a
recognized FeatureKey / EnhancedFeature so useFeatureCapability('kanban')
works.
* .env.example: documents HERMES_DASHBOARD_URL (default 127.0.0.1:9119
on current Hermes Agent v0.13+; the legacy 9120 is gone).
Tested locally with hermes-agent main pulled into
/Users/aurora/hermes-dashboard-fresh/repo. Workspace gateway-status
now reports kanban: true when the plugin is mounted, false otherwise.
This commit is contained in:
@@ -103,6 +103,13 @@
|
||||
# STREAM_ACCEPTED_TIMEOUT_MS=120000
|
||||
# STREAM_HANDOFF_TIMEOUT_MS=300000
|
||||
|
||||
# Dashboard URL
|
||||
#
|
||||
# Where Hermes Agent's dashboard is reachable (default: 127.0.0.1:9119).
|
||||
# /api/sessions, the conductor mission API, and the upstream kanban plugin
|
||||
# all live on the dashboard, not the gateway.
|
||||
# HERMES_DASHBOARD_URL=http://127.0.0.1:9119
|
||||
|
||||
# Dashboard API bearer token (optional)
|
||||
#
|
||||
# Preferred over the legacy HTML-scrape token flow. Set this to a dashboard
|
||||
|
||||
@@ -19,6 +19,7 @@ export type FeatureKey =
|
||||
| 'jobs'
|
||||
| 'dashboard'
|
||||
| 'enhancedChat'
|
||||
| 'kanban'
|
||||
|
||||
export type CapabilityState = {
|
||||
/** True when the capability is available right now. */
|
||||
|
||||
@@ -10,6 +10,7 @@ export type EnhancedFeature =
|
||||
| 'jobs'
|
||||
| 'mcp'
|
||||
| 'mcpFallback'
|
||||
| 'kanban'
|
||||
|
||||
const FEATURE_LABELS: Record<EnhancedFeature, string> = {
|
||||
sessions: 'Sessions',
|
||||
@@ -19,6 +20,7 @@ const FEATURE_LABELS: Record<EnhancedFeature, string> = {
|
||||
jobs: 'Jobs',
|
||||
mcp: 'MCP Servers',
|
||||
mcpFallback: 'MCP Servers (config fallback)',
|
||||
kanban: 'Kanban (Hermes plugin)',
|
||||
}
|
||||
|
||||
function normalizeFeature(
|
||||
@@ -32,7 +34,8 @@ function normalizeFeature(
|
||||
normalized === 'config' ||
|
||||
normalized === 'jobs' ||
|
||||
normalized === 'mcp' ||
|
||||
normalized === 'mcpfallback'
|
||||
normalized === 'mcpfallback' ||
|
||||
normalized === 'kanban'
|
||||
) {
|
||||
return normalized === 'mcpfallback' ? 'mcpFallback' : normalized
|
||||
}
|
||||
|
||||
@@ -128,6 +128,7 @@ export const Route = createFileRoute('/api/connection-status')({
|
||||
jobs: caps.jobs,
|
||||
mcp: caps.mcp,
|
||||
conductor: caps.conductor,
|
||||
kanban: caps.kanban,
|
||||
enhancedChat: caps.enhancedChat,
|
||||
dashboard: caps.dashboard.available,
|
||||
},
|
||||
|
||||
@@ -179,6 +179,15 @@ export type EnhancedCapabilities = {
|
||||
* placeholder instead of failing mid-action. See #262.
|
||||
*/
|
||||
conductor: boolean
|
||||
/**
|
||||
* True when the dashboard exposes `/api/plugins/kanban/board` (the native
|
||||
* Hermes kanban plugin shipped upstream). When available, the workspace's
|
||||
* /swarm kanban surface can sync with the dashboard's kanban DB so both
|
||||
* UIs read/write the same SQLite source of truth instead of running
|
||||
* separate stores. When false, the workspace falls back to its local
|
||||
* file-backed swarm-kanban store. See v2.3.0 plan.
|
||||
*/
|
||||
kanban: boolean
|
||||
}
|
||||
|
||||
export type DashboardCapabilities = {
|
||||
@@ -224,6 +233,7 @@ let capabilities: GatewayCapabilities = {
|
||||
mcp: false,
|
||||
mcpFallback: false,
|
||||
conductor: false,
|
||||
kanban: false,
|
||||
dashboard: {
|
||||
available: false,
|
||||
url: CLAUDE_DASHBOARD_URL,
|
||||
@@ -606,6 +616,30 @@ async function probeConductor(dashboardAvailable: boolean): Promise<boolean> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lightweight probe for the upstream Hermes kanban plugin. When the dashboard
|
||||
* exposes `/api/plugins/kanban/board` we assume the kanban plugin is loaded
|
||||
* and the workspace can sync its /swarm kanban surface with the dashboard's
|
||||
* SQLite-backed kanban DB. Mounted by hermes_cli.web_server
|
||||
* `_mount_plugin_api_routes()`. See v2.3.0 plan.
|
||||
*/
|
||||
async function probeKanban(dashboardAvailable: boolean): Promise<boolean> {
|
||||
if (!dashboardAvailable) return false
|
||||
try {
|
||||
const res = await dashboardFetch('/api/plugins/kanban/board', {
|
||||
method: 'GET',
|
||||
signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),
|
||||
})
|
||||
if (res.status === 404 || res.status === 405) return false
|
||||
// The plugin route is unauthenticated by design (loopback-only), so
|
||||
// 200 is the normal success. Some auth setups may return 401 — still
|
||||
// means the route exists.
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Vanilla hermes-agent 0.10.0 satisfies: health, chatCompletions, models, streaming,
|
||||
// sessions, skills, config, jobs. Dashboard-only endpoints (themes/plugins) and the
|
||||
@@ -761,6 +795,7 @@ export async function probeGateway(options?: {
|
||||
|
||||
// Conductor probe runs after dashboard probe.
|
||||
const conductor = await probeConductor(dashboard.available)
|
||||
const kanban = await probeKanban(dashboard.available)
|
||||
|
||||
// Phase 1.5 fallback: when native /api/mcp is missing but the dashboard
|
||||
// exposes `config.mcp_servers` AND we are loopback-only, allow a config
|
||||
@@ -791,6 +826,7 @@ export async function probeGateway(options?: {
|
||||
mcp,
|
||||
mcpFallback,
|
||||
conductor,
|
||||
kanban,
|
||||
dashboard,
|
||||
}
|
||||
lastProbeAt = Date.now()
|
||||
|
||||
Reference in New Issue
Block a user