mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-10-20 05:15:31 +00:00
fix(gateway): bridge top-level port/host into extra for webhook and api_server
WebhookAdapter and ApiServerAdapter read port/host from config.extra, but PlatformConfig.from_dict only populates extra from the 'extra:' sub-key in the YAML platform section. Top-level keys like port and host are silently ignored, causing the adapter to fall back to DEFAULT_PORT (8644). This causes silent port conflicts in multi-profile setups: a profile that configures 'platforms.webhook.port: 8649' still binds 8644, colliding with the default profile's webhook on the same port. Fix: extend the shared-key bridging loop in load_gateway_config() to bridge top-level port/host/secret into extra for WEBHOOK, MSGRAPH_WEBHOOK, and API_SERVER platforms, following the same pattern already used for dm_policy, allow_from, gateway_restart_notification, and other keys. The extra dict takes precedence: if port is already under 'extra:', the top-level value does not clobber it.
This commit is contained in:
parent
6b1b2e6f0e
commit
74db4bfe68
2 changed files with 126 additions and 0 deletions
|
|
@ -1497,6 +1497,24 @@ def load_gateway_config() -> GatewayConfig:
|
|||
bridged["typing_indicator"] = platform_cfg["typing_indicator"]
|
||||
if "typing_status_text" in platform_cfg:
|
||||
bridged["typing_status_text"] = platform_cfg["typing_status_text"]
|
||||
# Bridge top-level port/host/secret into extra for platforms
|
||||
# whose adapters read these from config.extra (webhook,
|
||||
# msgraph_webhook, api_server). Without this, YAML like:
|
||||
# platforms:
|
||||
# webhook:
|
||||
# enabled: true
|
||||
# port: 8649
|
||||
# silently falls back to the hardcoded DEFAULT_PORT because
|
||||
# PlatformConfig.from_dict only extracts ``extra`` from the
|
||||
# ``extra:`` sub-key, not from the top level.
|
||||
if plat in {Platform.WEBHOOK, Platform.MSGRAPH_WEBHOOK}:
|
||||
for _bridge_key in ("port", "host", "secret"):
|
||||
if _bridge_key in platform_cfg and _bridge_key not in platform_cfg.get("extra", {}):
|
||||
bridged[_bridge_key] = platform_cfg[_bridge_key]
|
||||
if plat == Platform.API_SERVER:
|
||||
for _bridge_key in ("port", "host"):
|
||||
if _bridge_key in platform_cfg and _bridge_key not in platform_cfg.get("extra", {}):
|
||||
bridged[_bridge_key] = platform_cfg[_bridge_key]
|
||||
has_channel_overrides = "channel_overrides" in platform_cfg
|
||||
if has_channel_overrides:
|
||||
raw_overrides = platform_cfg.get("channel_overrides")
|
||||
|
|
|
|||
|
|
@ -1855,6 +1855,114 @@ class TestLoadGatewayConfig:
|
|||
assert Platform.API_SERVER not in config.platforms
|
||||
|
||||
|
||||
class TestWebhookPortBridging:
|
||||
"""Top-level port/host in the YAML platform section must be bridged into
|
||||
PlatformConfig.extra so the webhook/api_server adapters can read them.
|
||||
|
||||
The adapters (WebhookAdapter, ApiServerAdapter) read port/host from
|
||||
``config.extra``, but users naturally write them at the top level of the
|
||||
platform section in config.yaml:
|
||||
|
||||
platforms:
|
||||
webhook:
|
||||
enabled: true
|
||||
host: 0.0.0.0
|
||||
port: 8649
|
||||
|
||||
Without bridging, the port silently falls back to DEFAULT_PORT (8644),
|
||||
causing port conflicts between profiles that configure different ports."""
|
||||
|
||||
def test_webhook_port_bridged_from_toplevel(self, tmp_path, monkeypatch):
|
||||
hermes_home = tmp_path / ".hermes"
|
||||
hermes_home.mkdir()
|
||||
config_path = hermes_home / "config.yaml"
|
||||
config_path.write_text(
|
||||
"platforms:\n"
|
||||
" webhook:\n"
|
||||
" enabled: true\n"
|
||||
" host: 0.0.0.0\n"
|
||||
" port: 8649\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
|
||||
monkeypatch.delenv("WEBHOOK_ENABLED", raising=False)
|
||||
monkeypatch.delenv("WEBHOOK_PORT", raising=False)
|
||||
|
||||
config = load_gateway_config()
|
||||
|
||||
assert Platform.WEBHOOK in config.platforms
|
||||
wh = config.platforms[Platform.WEBHOOK]
|
||||
assert wh.enabled is True
|
||||
assert wh.extra.get("port") == 8649
|
||||
assert wh.extra.get("host") == "0.0.0.0"
|
||||
|
||||
def test_webhook_port_in_extra_not_overwritten_by_toplevel(self, tmp_path, monkeypatch):
|
||||
"""If port is already under extra, the top-level value must not clobber it."""
|
||||
hermes_home = tmp_path / ".hermes"
|
||||
hermes_home.mkdir()
|
||||
config_path = hermes_home / "config.yaml"
|
||||
config_path.write_text(
|
||||
"platforms:\n"
|
||||
" webhook:\n"
|
||||
" enabled: true\n"
|
||||
" extra:\n"
|
||||
" port: 8650\n"
|
||||
" port: 8649\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
|
||||
monkeypatch.delenv("WEBHOOK_ENABLED", raising=False)
|
||||
monkeypatch.delenv("WEBHOOK_PORT", raising=False)
|
||||
|
||||
config = load_gateway_config()
|
||||
|
||||
wh = config.platforms[Platform.WEBHOOK]
|
||||
assert wh.extra.get("port") == 8650
|
||||
|
||||
def test_api_server_port_bridged_from_toplevel(self, tmp_path, monkeypatch):
|
||||
hermes_home = tmp_path / ".hermes"
|
||||
hermes_home.mkdir()
|
||||
config_path = hermes_home / "config.yaml"
|
||||
config_path.write_text(
|
||||
"platforms:\n"
|
||||
" api_server:\n"
|
||||
" enabled: true\n"
|
||||
" host: 127.0.0.1\n"
|
||||
" port: 8648\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
|
||||
monkeypatch.delenv("API_SERVER_ENABLED", raising=False)
|
||||
monkeypatch.delenv("API_SERVER_PORT", raising=False)
|
||||
|
||||
config = load_gateway_config()
|
||||
|
||||
assert Platform.API_SERVER in config.platforms
|
||||
api = config.platforms[Platform.API_SERVER]
|
||||
assert api.extra.get("port") == 8648
|
||||
assert api.extra.get("host") == "127.0.0.1"
|
||||
|
||||
def test_webhook_port_defaults_when_not_configured(self, tmp_path, monkeypatch):
|
||||
"""No port anywhere -> adapter uses its hardcoded DEFAULT_PORT."""
|
||||
hermes_home = tmp_path / ".hermes"
|
||||
hermes_home.mkdir()
|
||||
config_path = hermes_home / "config.yaml"
|
||||
config_path.write_text(
|
||||
"platforms:\n"
|
||||
" webhook:\n"
|
||||
" enabled: true\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
|
||||
monkeypatch.delenv("WEBHOOK_ENABLED", raising=False)
|
||||
monkeypatch.delenv("WEBHOOK_PORT", raising=False)
|
||||
|
||||
config = load_gateway_config()
|
||||
|
||||
wh = config.platforms[Platform.WEBHOOK]
|
||||
assert "port" not in wh.extra or wh.extra.get("port") is None
|
||||
|
||||
|
||||
class TestHomeChannelEnvOverrides:
|
||||
"""Home channel env vars should apply even when the platform was already
|
||||
configured via config.yaml (not just when credential env vars create it)."""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue