feat(raft): add gateway setup wizard

Add an interactive Raft setup flow for hermes gateway setup. The wizard follows the existing platform adapter setup pattern, persists RAFT_PROFILE to the Hermes env file, preserves an existing profile when the user declines reconfiguration, and registers the flow via setup_fn.

Add focused Raft adapter coverage for saving RAFT_PROFILE, keeping an existing profile, and registering setup_fn.

Signed-off-by: skyzh <skyzh@mail.build>
Signed-off-by: HaoHao <HaoHao@mail.build>
This commit is contained in:
skyzh 2026-06-24 08:39:21 +00:00 committed by Teknium
parent da6d5fcd13
commit cc7d20d683
2 changed files with 73 additions and 0 deletions

View file

@ -754,6 +754,48 @@ def _env_enablement() -> Optional[dict]:
return {"enabled": True}
def interactive_setup() -> None:
"""Interactive ``hermes gateway setup`` flow for the Raft platform.
Lazy-imports CLI helpers so the plugin stays importable in gateway runtime
and test contexts. The flow persists ``RAFT_PROFILE`` to the Hermes env
file so the Raft adapter auto-enables after a gateway restart.
"""
from hermes_cli.cli_output import (
print_header,
print_info,
print_success,
print_warning,
prompt,
prompt_yes_no,
)
from hermes_cli.config import get_env_value, save_env_value
print_header("Raft")
existing_profile = get_env_value("RAFT_PROFILE")
if existing_profile:
print_info(f"Raft: already configured (profile: {existing_profile})")
if not prompt_yes_no("Reconfigure Raft?", False):
print_info(f"Keeping RAFT_PROFILE={existing_profile}.")
return
print_info("Connect Hermes to Raft as an external agent.")
print_info("Create the External Agent in Raft first, then run:")
print_info(" raft agent login --server <server-url> --agent <agent-id> --profile-slug <slug>")
print()
profile = prompt("Raft profile slug", default=existing_profile or "")
if not profile:
print_warning("Raft profile slug is required; skipping Raft setup")
return
save_env_value("RAFT_PROFILE", profile.strip())
print()
print_success("Raft configuration saved")
print_info("Restart the gateway for changes to take effect: hermes gateway restart")
def register(ctx) -> None:
"""Plugin entry point — called by the Hermes plugin system."""
ctx.register_platform(
@ -764,6 +806,7 @@ def register(ctx) -> None:
is_connected=_is_connected,
required_env=["RAFT_PROFILE"],
install_hint="Install the Raft CLI from https://raft.build",
setup_fn=interactive_setup,
env_enablement_fn=_env_enablement,
emoji="🔔",
platform_hint=(

View file

@ -32,6 +32,7 @@ from plugins.platforms.raft.adapter import (
_on_session_end,
_on_session_finalize,
check_raft_requirements,
interactive_setup,
register,
)
from gateway.session import build_session_key
@ -425,6 +426,34 @@ class TestRaftConfig:
assert _is_connected(PlatformConfig(enabled=True, extra={"enabled": True})) is True
assert _is_connected(PlatformConfig(enabled=True, extra={})) is False
def test_interactive_setup_saves_raft_profile(self, monkeypatch, tmp_path, capsys):
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
monkeypatch.delenv("RAFT_PROFILE", raising=False)
monkeypatch.setattr("builtins.input", lambda _prompt: "dev-profile")
interactive_setup()
assert (tmp_path / ".env").read_text(encoding="utf-8") == "RAFT_PROFILE=dev-profile\n"
assert os.environ["RAFT_PROFILE"] == "dev-profile"
out = capsys.readouterr().out
assert "Raft configuration saved" in out
assert "hermes gateway restart" in out
def test_interactive_setup_keeps_existing_profile_when_not_reconfigured(
self, monkeypatch, tmp_path, capsys
):
env_path = tmp_path / ".env"
env_path.write_text("RAFT_PROFILE=existing\n", encoding="utf-8")
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
monkeypatch.setenv("RAFT_PROFILE", "existing")
monkeypatch.setattr("builtins.input", lambda _prompt: "n")
interactive_setup()
assert env_path.read_text(encoding="utf-8") == "RAFT_PROFILE=existing\n"
assert os.environ["RAFT_PROFILE"] == "existing"
assert "Keeping RAFT_PROFILE=existing" in capsys.readouterr().out
def test_register_calls_register_platform(self):
registered = {}
hooks = {}
@ -441,6 +470,7 @@ class TestRaftConfig:
assert registered["name"] == "raft"
assert registered["label"] == "Raft"
assert registered["emoji"] == "🔔"
assert registered["setup_fn"] is interactive_setup
assert "profile show" in registered["platform_hint"]
assert "manual get" in registered["platform_hint"]
assert "--profile" in registered["platform_hint"]