diff --git a/hermes_cli/proxy_cli.py b/hermes_cli/proxy_cli.py index 83eff52e2d..f5513f8005 100644 --- a/hermes_cli/proxy_cli.py +++ b/hermes_cli/proxy_cli.py @@ -14,7 +14,7 @@ from rich.panel import Panel from rich.table import Table from agent.proxy_sources import iron_proxy as ip -from hermes_cli.config import load_config, save_config +from hermes_cli.config import load_config, load_env, save_config def register_cli(parent_parser: argparse.ArgumentParser) -> None: @@ -29,32 +29,25 @@ def register_cli(parent_parser: argparse.ArgumentParser) -> None: ("--force", dict(action="store_true", help="Re-download even if a managed copy already exists")), ]), ("setup", "Interactive wizard: install + CA + mint tokens + write config", cmd_setup, [ - ("--tunnel-port", dict( - type=int, default=None, - help=f"Override the tunnel port (default {ip._DEFAULT_TUNNEL_PORT})")), - ("--from-bitwarden", dict( - action="store_true", - help="Treat secrets as managed by Bitwarden — discover provider keys " - "from secrets.bitwarden config instead of the current env. Fails " - "loudly if BW is unreachable rather than silently falling back.")), - ("--no-bitwarden", dict( - action="store_true", - help="Explicitly switch credential_source back to env on re-setup " - "(only meaningful when the previous setup used --from-bitwarden).")), - ("--rotate-tokens", dict( - action="store_true", - help="Mint fresh proxy tokens for every provider (default is to " - "preserve tokens for providers that already had one — avoids " - "401-ing already-running sandboxes on re-setup).")), - ("--restart", dict( - dest="restart", action="store_true", default=None, - help="If a daemon is already running, restart it automatically after " - "writing the new config/tokens (non-interactive default on a tty " - "is to ask).")), - ("--no-restart", dict( - dest="restart", action="store_false", - help="Do not restart a running daemon after setup; you'll need to run " - "`hermes egress restart` yourself for changes to take effect.")), + ("--tunnel-port", dict(type=int, default=None, + help=f"Override the tunnel port (default {ip._DEFAULT_TUNNEL_PORT})")), + ("--from-bitwarden", dict(action="store_true", help=( + "Treat secrets as managed by Bitwarden — discover provider keys " + "from secrets.bitwarden config instead of the current env. Fails " + "loudly if BW is unreachable rather than silently falling back."))), + ("--no-bitwarden", dict(action="store_true", help=( + "Explicitly switch credential_source back to env on re-setup " + "(only meaningful when the previous setup used --from-bitwarden)."))), + ("--rotate-tokens", dict(action="store_true", help=( + "Mint fresh proxy tokens for every provider (default is to " + "preserve tokens for providers that already had one — avoids " + "401-ing already-running sandboxes on re-setup)."))), + ("--restart", dict(dest="restart", action="store_true", default=None, help=( + "If a daemon is already running, restart it automatically after " + "writing the new config/tokens (non-interactive default on a tty is to ask)."))), + ("--no-restart", dict(dest="restart", action="store_false", help=( + "Do not restart a running daemon after setup; you'll need to run " + "`hermes egress restart` yourself for changes to take effect."))), ]), ("start", "Start the managed iron-proxy", cmd_start, []), ("stop", "Stop the managed iron-proxy", cmd_stop, []), @@ -62,10 +55,9 @@ def register_cli(parent_parser: argparse.ArgumentParser) -> None: ("reload", "Hot-reload the running daemon's ruleset from proxy.yaml " "(management API — no restart, no dropped connections)", cmd_reload, []), ("status", "Show proxy state and mappings", cmd_status, [ - ("--show-tokens", dict( - action="store_true", - help="Print the proxy tokens (default: redacted prefix only). " - "Beware: tokens may persist in your shell history.")), + ("--show-tokens", dict(action="store_true", help=( + "Print the proxy tokens (default: redacted prefix only). " + "Beware: tokens may persist in your shell history."))), ]), ("disable", "Turn off the proxy integration", cmd_disable, []), ("config", "Print the generated proxy.yaml path", cmd_config, []), @@ -77,11 +69,6 @@ def register_cli(parent_parser: argparse.ArgumentParser) -> None: parser.set_defaults(func=func) -# --------------------------------------------------------------------------- -# Handlers -# --------------------------------------------------------------------------- - - def cmd_install(args: argparse.Namespace) -> int: console = Console() try: @@ -228,13 +215,7 @@ def _setup_mint_tokens(console: Console, args: argparse.Namespace): "credentials inside the sandbox. Egress isolation is INCOMPLETE for these.[/dim]" ) - table = Table(show_header=True, header_style="bold") - table.add_column("Provider env", style="cyan") - table.add_column("Upstream hosts", style="dim") - table.add_column("Proxy token", style="green") - for m in mappings: - table.add_row(m.real_env_name, ", ".join(m.upstream_hosts), _redact_token(m.proxy_token)) - console.print(table) + console.print(_mappings_table(mappings, "Provider env", "Upstream hosts", show_tokens=False)) return mappings @@ -528,14 +509,7 @@ def cmd_status(args: argparse.Namespace) -> int: if mappings: console.print() console.print("[bold]Token mappings[/bold]") - m_table = Table(show_header=True, header_style="bold") - m_table.add_column("Real env", style="cyan") - m_table.add_column("Upstream", style="dim") - m_table.add_column("Proxy token", style="green") - for m in mappings: - tok = m.proxy_token if args.show_tokens else _redact_token(m.proxy_token) - m_table.add_row(m.real_env_name, ", ".join(m.upstream_hosts), tok) - console.print(m_table) + console.print(_mappings_table(mappings, "Real env", "Upstream", show_tokens=args.show_tokens)) if args.show_tokens: console.print( "[yellow]⚠[/yellow] proxy tokens just printed in full — " @@ -582,11 +556,6 @@ def cmd_config(args: argparse.Namespace) -> int: return 0 -# --------------------------------------------------------------------------- -# Helpers -# --------------------------------------------------------------------------- - - def _bitwarden_env_names(console: Console) -> Optional[List[str]]: """Secret names from Bitwarden for ``setup --from-bitwarden``; prints the error and returns ``None`` on any failure so the wizard aborts loudly instead of falling back to the host env. @@ -634,10 +603,6 @@ def _load_env_file_into_environ() -> int: Only fills names not already set (an exported value always wins) and only known provider names, so unrelated secrets are never slurped into the process. """ - try: - from hermes_cli.config import load_env - except ImportError: - return 0 try: file_env = load_env() except Exception: # noqa: BLE001 — best-effort convenience, never fatal @@ -654,6 +619,17 @@ def _load_env_file_into_environ() -> int: return added +def _mappings_table(mappings, env_header: str, hosts_header: str, *, show_tokens: bool) -> Table: + table = Table(show_header=True, header_style="bold") + table.add_column(env_header, style="cyan") + table.add_column(hosts_header, style="dim") + table.add_column("Proxy token", style="green") + for m in mappings: + tok = m.proxy_token if show_tokens else _redact_token(m.proxy_token) + table.add_row(m.real_env_name, ", ".join(m.upstream_hosts), tok) + return table + + def _step(console: Console, n: int, title: str) -> None: console.print() console.print(f"[bold]Step {n}[/bold] {title}") diff --git a/hermes_cli/relaunch.py b/hermes_cli/relaunch.py index 92e98a525a..de02f06381 100644 --- a/hermes_cli/relaunch.py +++ b/hermes_cli/relaunch.py @@ -45,11 +45,8 @@ def _extract_inherited_flags(argv: Sequence[str]) -> list[str]: while i < len(argv): arg = argv[i] if "=" in arg: - key = arg.split("=", 1)[0] - for flag, _ in _INHERITED_FLAGS_TABLE: - if key == flag: - flags.append(arg) - break + if any(arg.split("=", 1)[0] == flag for flag, _ in _INHERITED_FLAGS_TABLE): + flags.append(arg) i += 1 continue