/ hermes_cli / platforms.py
platforms.py
1 """ 2 Shared platform registry for Hermes Agent. 3 4 Single source of truth for platform metadata consumed by both 5 skills_config (label display) and tools_config (default toolset 6 resolution). Import ``PLATFORMS`` from here instead of maintaining 7 duplicate dicts in each module. 8 """ 9 10 from collections import OrderedDict 11 from typing import NamedTuple 12 13 14 class PlatformInfo(NamedTuple): 15 """Metadata for a single platform entry.""" 16 label: str 17 default_toolset: str 18 19 20 # Ordered so that TUI menus are deterministic. 21 PLATFORMS: OrderedDict[str, PlatformInfo] = OrderedDict([ 22 ("cli", PlatformInfo(label="🖥️ CLI", default_toolset="hermes-cli")), 23 ("telegram", PlatformInfo(label="📱 Telegram", default_toolset="hermes-telegram")), 24 ("discord", PlatformInfo(label="💬 Discord", default_toolset="hermes-discord")), 25 ("slack", PlatformInfo(label="💼 Slack", default_toolset="hermes-slack")), 26 ("whatsapp", PlatformInfo(label="📱 WhatsApp", default_toolset="hermes-whatsapp")), 27 ("signal", PlatformInfo(label="📡 Signal", default_toolset="hermes-signal")), 28 ("bluebubbles", PlatformInfo(label="💙 BlueBubbles", default_toolset="hermes-bluebubbles")), 29 ("email", PlatformInfo(label="📧 Email", default_toolset="hermes-email")), 30 ("homeassistant", PlatformInfo(label="🏠 Home Assistant", default_toolset="hermes-homeassistant")), 31 ("mattermost", PlatformInfo(label="💬 Mattermost", default_toolset="hermes-mattermost")), 32 ("matrix", PlatformInfo(label="💬 Matrix", default_toolset="hermes-matrix")), 33 ("dingtalk", PlatformInfo(label="💬 DingTalk", default_toolset="hermes-dingtalk")), 34 ("feishu", PlatformInfo(label="🪽 Feishu", default_toolset="hermes-feishu")), 35 ("wecom", PlatformInfo(label="💬 WeCom", default_toolset="hermes-wecom")), 36 ("wecom_callback", PlatformInfo(label="💬 WeCom Callback", default_toolset="hermes-wecom-callback")), 37 ("weixin", PlatformInfo(label="💬 Weixin", default_toolset="hermes-weixin")), 38 ("qqbot", PlatformInfo(label="💬 QQBot", default_toolset="hermes-qqbot")), 39 ("yuanbao", PlatformInfo(label="🤖 Yuanbao", default_toolset="hermes-yuanbao")), 40 ("webhook", PlatformInfo(label="🔗 Webhook", default_toolset="hermes-webhook")), 41 ("api_server", PlatformInfo(label="🌐 API Server", default_toolset="hermes-api-server")), 42 ("cron", PlatformInfo(label="⏰ Cron", default_toolset="hermes-cron")), 43 ]) 44 45 46 def platform_label(key: str, default: str = "") -> str: 47 """Return the display label for a platform key, or *default*. 48 49 Checks the static PLATFORMS dict first, then the plugin platform 50 registry for dynamically registered platforms. 51 """ 52 info = PLATFORMS.get(key) 53 if info is not None: 54 return info.label 55 # Check plugin registry 56 try: 57 from gateway.platform_registry import platform_registry 58 entry = platform_registry.get(key) 59 if entry: 60 return f"{entry.emoji} {entry.label}" if entry.emoji else entry.label 61 except Exception: 62 pass 63 return default 64 65 66 def get_all_platforms() -> "OrderedDict[str, PlatformInfo]": 67 """Return PLATFORMS merged with any plugin-registered platforms. 68 69 Plugin platforms are appended after builtins. This is the function 70 that tools_config and skills_config should use for platform menus. 71 """ 72 merged = OrderedDict(PLATFORMS) 73 try: 74 from gateway.platform_registry import platform_registry 75 for entry in platform_registry.plugin_entries(): 76 if entry.name not in merged: 77 merged[entry.name] = PlatformInfo( 78 label=f"{entry.emoji} {entry.label}" if entry.emoji else entry.label, 79 default_toolset=f"hermes-{entry.name}", 80 ) 81 except Exception: 82 pass 83 return merged