From 17451dc77a171e68fc931c6390a47c0d3d15d7e4 Mon Sep 17 00:00:00 2001 From: Hongming Wang Date: Sat, 2 May 2026 02:49:34 -0700 Subject: [PATCH] feat(gateway): add PluginPlatformIdentifier helper for plugin adapters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plugin adapters can't extend the closed Platform enum but BasePlatformAdapter.__init__ requires a Platform-shaped second arg. This helper quacks like a Platform enum value (.value, hashable, equality) so plugin adapters can subclass BasePlatformAdapter cleanly: super().__init__(config, PluginPlatformIdentifier("my-platform")) Distinct hash bucket prevents accidental collision with built-in Platform members in dict keys (defense-in-depth — registration also rejects names that shadow Platform values). --- hermes_cli/plugins.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/hermes_cli/plugins.py b/hermes_cli/plugins.py index b52cfbb2..99b708f9 100644 --- a/hermes_cli/plugins.py +++ b/hermes_cli/plugins.py @@ -840,6 +840,49 @@ def get_plugin_platform_adapter(name: str) -> Optional[tuple]: return get_plugin_manager()._plugin_platform_adapters.get(name) +class PluginPlatformIdentifier: + """Platform-enum-shaped identifier for plugin-registered platforms. + + Built-in adapters pass ``Platform.TELEGRAM`` etc. as the second arg + to ``BasePlatformAdapter.__init__``. Plugin adapters can't extend + the closed Platform enum, so they pass an instance of this class + instead — it quacks like ``Platform`` enough for the base class + (``.value`` attribute, hashable, equality on value). + + Usage in a plugin adapter:: + + from hermes_cli.plugins import PluginPlatformIdentifier + from gateway.platforms.base import BasePlatformAdapter + + class MyAdapter(BasePlatformAdapter): + def __init__(self, config): + super().__init__(config, PluginPlatformIdentifier("my-platform")) + + The ``name`` should match what was passed to + ``ctx.register_platform_adapter(name=..., ...)``. + """ + + __slots__ = ("value",) + + def __init__(self, name: str): + self.value = name + + def __hash__(self) -> int: + # Distinct hash bucket so a plugin platform named "telegram" + # (impossible — registration is rejected — but defensively) + # never collides with Platform.TELEGRAM in dict keys. + return hash(("__plugin_platform__", self.value)) + + def __eq__(self, other: object) -> bool: + return ( + isinstance(other, PluginPlatformIdentifier) + and self.value == other.value + ) + + def __repr__(self) -> str: + return f"PluginPlatformIdentifier({self.value!r})" + + def get_plugin_commands() -> Dict[str, dict]: """Return the full plugin commands dict (name → {handler, description, plugin}).