fix(gateway): key the messaging fold on the profile name, not the home basename

_platform_payloads passed own_home.name — the directory basename, '.hermes'
for the shipped default root or any custom HERMES_HOME name — into
profile_platforms_from_multiplexer, whose fold only matches a profile NAME.
Only secondaries under profiles/<name> have basename == profile id, so the
default home's re-key came back empty and /api/messaging/platforms kept
reading 'Restart needed' forever while the multiplexer served its flat-keyed
adapters (#123088 follow-up; /api/status already passed 'default'
explicitly).

Resolve the name with profile_name_for_home, and add a regression test whose
fixture home is literally named '.hermes'.
This commit is contained in:
liuhao1024
2026-09-27 06:03:27 +08:00
committed by Teknium
parent f373cff9d5
commit 80c8043a54
2 changed files with 45 additions and 4 deletions

View File

@@ -22,8 +22,8 @@ from typing import Any, Optional
from fastapi import APIRouter, HTTPException
from gateway.status import (
multiplexer_liveness_for_profile, profile_platforms_from_multiplexer, resolve_gateway_liveness,
retained_gateway_state)
multiplexer_liveness_for_profile, profile_name_for_home, profile_platforms_from_multiplexer,
resolve_gateway_liveness, retained_gateway_state)
from hermes_cli._subprocess_compat import windows_hide_flags
from hermes_cli.config import OPTIONAL_ENV_VARS, get_env_path
from hermes_constants import get_process_hermes_home
@@ -294,7 +294,7 @@ def _platform_payloads(scoped_dir: Optional[Path], entries) -> list[dict[str, An
# profile's standalone days outranks nothing: only a record proving a live own gateway does —
# the same rung order ``resolve_gateway_liveness`` uses (own runtime PID before the multiplexer),
# so the two surfaces cannot disagree. Unscoped, the profile is the process's own home (a pooled
# ``hermes --profile X serve``); the default home resolves to a name the multiplexer never serves.
# ``hermes --profile X serve``).
own_home = scoped_dir if scoped_dir is not None else get_process_hermes_home()
if (
runtime is None
@@ -302,7 +302,16 @@ def _platform_payloads(scoped_dir: Optional[Path], entries) -> list[dict[str, An
):
served = multiplexer_liveness_for_profile(own_home)
if served is not None:
runtime = {**served[1], "platforms": profile_platforms_from_multiplexer(served[1], own_home.name)}
# The fold keys on the profile NAME rung 4 just proved served; ``own_home.name`` is only
# the directory basename (".hermes" or any custom HERMES_HOME name for the default root,
# the profile id only for secondaries under profiles/<name>) — the default's flat keys
# then never matched and the card read "Restart needed" forever (#123088).
runtime = {
**served[1],
"platforms": profile_platforms_from_multiplexer(
served[1], profile_name_for_home(own_home) or "default"
),
}
return [_messaging_platform_payload(entry, env_on_disk, runtime, scoped=scoped_dir is not None, profile_home=scoped_dir)
for entry in entries]

View File

@@ -89,6 +89,38 @@ def test_default_profile_keeps_its_flat_adapters_when_rekeyed(served_root):
assert "telegram" not in plats # that entry belongs to alpha
def test_messaging_card_for_the_default_home_rekeys_by_the_profile_not_the_dirname(tmp_path, monkeypatch):
"""The multiplexer fold keys on the profile NAME; ``_platform_payloads`` used to pass
``own_home.name`` — the directory basename, ``.hermes`` for the shipped default root or any
custom ``HERMES_HOME`` name, equal to the profile id only for secondaries under
``profiles/<name>``. On the default home the fold came back empty and the Channels card read
"Restart needed" forever while the multiplexer served its flat-keyed adapters (#123088)."""
import gateway.status as status
from hermes_cli.web_routers import messaging
root = tmp_path / ".hermes" # the shipped default root's literal name — never equal to "default"
root.mkdir()
(root / "config.yaml").write_text("gateway: {multiplex_profiles: true}\n", encoding="utf-8")
record = {
"pid": os.getpid(), "hermes_home": str(root), "gateway_state": "running",
"served_profiles": ["default", "route-runner"],
"platforms": {"telegram": {"state": "connected"}},
}
monkeypatch.setenv("HERMES_HOME", str(root))
import hermes_constants
monkeypatch.setattr(hermes_constants, "_default_hermes_root_memo", None)
# A launch-service gateway's record fails the own-rung argv check (inline ``-c``), so the card
# falls through to the multiplexer rung — mirrored here by an absent own record.
monkeypatch.setattr(messaging, "read_runtime_status", lambda *a, **k: None)
monkeypatch.setattr(messaging, "multiplexer_liveness_for_profile", lambda home: (os.getpid(), record))
monkeypatch.setattr(status, "_read_process_cmdline", lambda pid: "hermes gateway run")
monkeypatch.setattr(messaging, "_platform_enablement", lambda *a, **k: (True, True, None))
entry = {"id": "telegram", "name": "Telegram", "description": "", "docs_url": "", "env_vars": [],
"required_env": []}
[payload] = messaging._platform_payloads(None, [entry]) # unscoped: the dashboard's own home
assert payload["gateway_running"] is True
assert payload["state"] == "connected", payload
def test_messaging_card_for_a_served_profile_reads_connected_not_restart_needed(served_root, monkeypatch):
from hermes_cli.web_routers import messaging
monkeypatch.setattr(messaging, "_platform_enablement", lambda *a, **k: (True, True, None))