diff --git a/plugins/platforms/raft/adapter.py b/plugins/platforms/raft/adapter.py index 0a8b1a359b0..b44b2b6520e 100644 --- a/plugins/platforms/raft/adapter.py +++ b/plugins/platforms/raft/adapter.py @@ -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 --agent --profile-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=( diff --git a/tests/gateway/test_raft_adapter.py b/tests/gateway/test_raft_adapter.py index 174d18d5fff..1ea3869e29b 100644 --- a/tests/gateway/test_raft_adapter.py +++ b/tests/gateway/test_raft_adapter.py @@ -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"]