/ 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