diff --git a/hermes_cli/commands.py b/hermes_cli/commands.py index 7160c16f9d..b40522a7b3 100644 --- a/hermes_cli/commands.py +++ b/hermes_cli/commands.py @@ -148,8 +148,7 @@ COMMAND_REGISTRY: list[CommandDef] = [ CommandDef("cron", "Manage scheduled tasks", "Tools & Skills", cli_only=True, args_hint="[subcommand]", subcommands=("list", "add", "create", "edit", "pause", "resume", "run", "remove")), - CommandDef("reload", "Reload .env variables into the running session", "Tools & Skills", - cli_only=True), + CommandDef("reload", "Reload .env variables into the running session", "Tools & Skills"), CommandDef("reload-mcp", "Reload MCP servers from config", "Tools & Skills", aliases=("reload_mcp",)), CommandDef("browser", "Connect browser tools to your live Chrome via CDP", "Tools & Skills", diff --git a/tests/test_tui_gateway_server.py b/tests/test_tui_gateway_server.py index 1c8eecf060..dacc55df5b 100644 --- a/tests/test_tui_gateway_server.py +++ b/tests/test_tui_gateway_server.py @@ -3448,3 +3448,39 @@ def test_config_set_indicator_none_keeps_blank_repr(monkeypatch): ) assert "error" in resp assert "unknown indicator: ''" in resp["error"]["message"] + + +# ── reload.env ─────────────────────────────────────────────────────── + + +def test_reload_env_rpc_calls_hermes_cli_reload_env(monkeypatch): + """reload.env mirrors classic CLI's `/reload` — re-reads ~/.hermes/.env + into the gateway process and reports the count of vars updated.""" + calls = {"n": 0} + + def _fake_reload(): + calls["n"] += 1 + return 7 + + fake = types.SimpleNamespace(reload_env=_fake_reload) + with patch.dict(sys.modules, {"hermes_cli.config": fake}): + resp = server.handle_request( + {"id": "1", "method": "reload.env", "params": {}} + ) + + assert resp["result"] == {"updated": 7} + assert calls["n"] == 1 + + +def test_reload_env_rpc_surfaces_errors(monkeypatch): + def _broken(): + raise RuntimeError("env path locked") + + fake = types.SimpleNamespace(reload_env=_broken) + with patch.dict(sys.modules, {"hermes_cli.config": fake}): + resp = server.handle_request( + {"id": "1", "method": "reload.env", "params": {}} + ) + + assert "error" in resp + assert "env path locked" in resp["error"]["message"] diff --git a/tui_gateway/server.py b/tui_gateway/server.py index e5fda15a5c..377ed64a58 100644 --- a/tui_gateway/server.py +++ b/tui_gateway/server.py @@ -3429,6 +3429,26 @@ def _(rid, params: dict) -> dict: return _err(rid, 5015, str(e)) +@method("reload.env") +def _(rid, params: dict) -> dict: + """Re-read ~/.hermes/.env into the gateway process — TUI parity with + classic CLI's ``/reload`` (cli.py). Newly added API keys take effect + on the next agent call without restarting the TUI. + + The credential pool / provider routing for any *already-constructed* + agent does not auto-rebuild — that's the same behaviour as classic + CLI's ``/reload``. Users who want a brand-new credential resolution + should follow with ``/new``. + """ + try: + from hermes_cli.config import reload_env + + count = reload_env() + return _ok(rid, {"updated": int(count)}) + except Exception as e: + return _err(rid, 5015, str(e)) + + _TUI_HIDDEN: frozenset[str] = frozenset( { "sethome", diff --git a/ui-tui/src/__tests__/createSlashHandler.test.ts b/ui-tui/src/__tests__/createSlashHandler.test.ts index db5e37347a..3ec340b8a2 100644 --- a/ui-tui/src/__tests__/createSlashHandler.test.ts +++ b/ui-tui/src/__tests__/createSlashHandler.test.ts @@ -194,6 +194,7 @@ describe('createSlashHandler', () => { ['/browser status', 'browser.manage', { action: 'status', session_id: null }], ['/browser connect', 'browser.manage', { action: 'connect', session_id: null, url: 'http://127.0.0.1:9222' }], ['/reload-mcp', 'reload.mcp', { session_id: null }], + ['/reload', 'reload.env', {}], ['/stop', 'process.stop', {}], ['/fast status', 'config.get', { key: 'fast', session_id: null }], ['/busy status', 'config.get', { key: 'busy' }], diff --git a/ui-tui/src/app/slash/commands/ops.ts b/ui-tui/src/app/slash/commands/ops.ts index 21cd52b341..7353f6fb4d 100644 --- a/ui-tui/src/app/slash/commands/ops.ts +++ b/ui-tui/src/app/slash/commands/ops.ts @@ -2,6 +2,7 @@ import type { BrowserManageResponse, DelegationPauseResponse, ProcessStopResponse, + ReloadEnvResponse, ReloadMcpResponse, RollbackDiffResponse, RollbackListResponse, @@ -89,6 +90,24 @@ export const opsCommands: SlashCommand[] = [ } }, + { + help: 're-read ~/.hermes/.env into the running gateway (CLI parity)', + name: 'reload', + run: (_arg, ctx) => { + ctx.gateway + .rpc('reload.env', {}) + .then( + ctx.guarded(r => { + const n = Number(r.updated ?? 0) + const noun = n === 1 ? 'var' : 'vars' + + ctx.transcript.sys(`reloaded .env (${n} ${noun} updated)`) + }) + ) + .catch(ctx.guardedErr) + } + }, + { help: 'manage browser CDP connection [connect|disconnect|status]', name: 'browser', diff --git a/ui-tui/src/gatewayTypes.ts b/ui-tui/src/gatewayTypes.ts index 8a518e385e..1f43096340 100644 --- a/ui-tui/src/gatewayTypes.ts +++ b/ui-tui/src/gatewayTypes.ts @@ -308,6 +308,10 @@ export interface ReloadMcpResponse { status?: string } +export interface ReloadEnvResponse { + updated?: number +} + export interface ProcessStopResponse { killed?: number }