diff --git a/api/routes.py b/api/routes.py
index 5168819d..6a538b20 100644
--- a/api/routes.py
+++ b/api/routes.py
@@ -2183,6 +2183,43 @@ def handle_get(handler, parsed) -> bool:
{"name": get_active_profile_name(), "path": str(get_active_hermes_home())},
)
+ # ── Gateway Status (GET) ──
+ if parsed.path == "/api/gateway/status":
+ import datetime
+ identity_map = _load_gateway_session_identity_map()
+ sessions_path = _gateway_session_metadata_path()
+ running = bool(identity_map)
+ platforms_set: set[str] = set()
+ for meta in identity_map.values():
+ raw = meta.get("raw_source") or meta.get("platform") or ""
+ norm = _normalize_messaging_source(raw)
+ if norm:
+ platforms_set.add(norm)
+ _PLATFORM_LABELS = {
+ "telegram": "Telegram",
+ "discord": "Discord",
+ "slack": "Slack",
+ "web": "Web",
+ "api": "API",
+ }
+ platforms = sorted(
+ [{"name": p, "label": _PLATFORM_LABELS.get(p, p.title())} for p in platforms_set],
+ key=lambda x: x["label"],
+ )
+ last_active = ""
+ if running and sessions_path.exists():
+ try:
+ mtime = sessions_path.stat().st_mtime
+ last_active = datetime.datetime.fromtimestamp(mtime).isoformat()
+ except Exception:
+ pass
+ return j(handler, {
+ "running": running,
+ "platforms": platforms,
+ "last_active": last_active,
+ "session_count": len(identity_map),
+ })
+
# ── MCP Servers (GET) ──
if parsed.path == "/api/mcp/servers":
return _handle_mcp_servers_list(handler)
diff --git a/static/index.html b/static/index.html
index a7834ae3..8e5f30c3 100644
--- a/static/index.html
+++ b/static/index.html
@@ -932,6 +932,12 @@
+
+
+
+
Status of the Hermes gateway (Telegram, Discord, Slack, etc.)
+
Loading…
+
diff --git a/static/panels.js b/static/panels.js
index f633a7f4..eaf4ab2e 100644
--- a/static/panels.js
+++ b/static/panels.js
@@ -3840,11 +3840,33 @@ async function deleteMcpServer(name){
else{showToast((r&&r.error)||t('mcp_delete_failed'));}
}).catch(()=>{showToast(t('mcp_delete_failed'));});
}
+function loadGatewayStatus(){
+ const card=$('gatewayStatusCard');
+ if(!card) return;
+ api('/api/gateway/status').then(r=>{
+ if(!r) return;
+ if(!r.running){
+ card.innerHTML=`
Gateway not running
`;
+ return;
+ }
+ const platformIcons={telegram:'💬',discord:'🎮',slack:'📝',web:'🌐',api:'🔌'};
+ let badges='';
+ if(r.platforms&&r.platforms.length){
+ badges=r.platforms.map(p=>{
+ const icon=platformIcons[p.name]||'📡';
+ return `
${icon} ${esc(p.label)}`;
+ }).join(' ');
+ }
+ const lastActive=r.last_active?`
Last active: ${esc(new Date(r.last_active).toLocaleString())}`:'';
+ const sessionInfo=r.session_count?`
${r.session_count} session${r.session_count!==1?'s':''}`:'';
+ card.innerHTML=`
Running
${badges?`
${badges}
`:''}
${sessionInfo}${lastActive}
`;
+ }).catch(()=>{card.innerHTML=`
Failed to load gateway status
`});
+}
// Load MCP servers when system settings tab opens
const _origSwitchSettings=switchSettingsSection;
switchSettingsSection=function(name){
_origSwitchSettings(name);
- if(name==='system') loadMcpServers();
+ if(name==='system'){loadMcpServers();loadGatewayStatus();}
};
// ── Checkpoints / Rollback ──────────────────────────────────────────────────