refactor(cli): compact data tables and replace try/except/pass with contextlib.suppress
This commit is contained in:
@@ -23,7 +23,7 @@ import textwrap
|
||||
from collections import deque
|
||||
from dataclasses import dataclass
|
||||
from urllib.parse import unquote, urlparse
|
||||
from contextlib import contextmanager
|
||||
from contextlib import contextmanager, suppress
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from typing import List, Dict, Any, Optional, Mapping
|
||||
@@ -372,95 +372,45 @@ def _mirror_config_to_env(defaults, _file_has_terminal_config):
|
||||
|
||||
def _cli_config_defaults():
|
||||
"""Built-in defaults for every config key the CLI reads (the file overlays these)."""
|
||||
img = "nikolaik/python-nodejs:python3.11-nodejs20"
|
||||
return {
|
||||
"model": {
|
||||
"default": "",
|
||||
"base_url": "",
|
||||
"provider": "auto",
|
||||
},
|
||||
"model": {"default": "", "base_url": "", "provider": "auto"},
|
||||
"terminal": {
|
||||
"env_type": "local",
|
||||
"cwd": ".", # resolved to os.getcwd() at runtime
|
||||
"home_mode": "auto",
|
||||
"lifetime_seconds": 300,
|
||||
"docker_image": "nikolaik/python-nodejs:python3.11-nodejs20",
|
||||
"docker_forward_env": [],
|
||||
"singularity_image": "docker://nikolaik/python-nodejs:python3.11-nodejs20",
|
||||
"modal_image": "nikolaik/python-nodejs:python3.11-nodejs20",
|
||||
"daytona_image": "nikolaik/python-nodejs:python3.11-nodejs20",
|
||||
"docker_volumes": [],
|
||||
"env_type": "local", "cwd": ".", "home_mode": "auto", "lifetime_seconds": 300, # cwd "." -> os.getcwd()
|
||||
"docker_image": img, "docker_forward_env": [], "singularity_image": f"docker://{img}",
|
||||
"modal_image": img, "daytona_image": img, "docker_volumes": [],
|
||||
"docker_mount_cwd_to_workspace": False, # opt-in only: sandbox isolation
|
||||
"docker_shared_container_key": "",
|
||||
},
|
||||
"browser": {
|
||||
"inactivity_timeout": 120,
|
||||
"record_sessions": False,
|
||||
"engine": "auto", # auto (Chrome) | lightpanda | chrome
|
||||
"camofox": {
|
||||
"rewrite_loopback_urls": False,
|
||||
"loopback_host_alias": "host.docker.internal",
|
||||
},
|
||||
},
|
||||
"compression": {
|
||||
"enabled": True,
|
||||
"threshold": 0.50, # fraction of the model's context limit
|
||||
"min_tail_user_messages": 1, # real user messages guaranteed in the tail
|
||||
"inactivity_timeout": 120, "record_sessions": False, "engine": "auto", # auto (Chrome) | lightpanda | chrome
|
||||
"camofox": {"rewrite_loopback_urls": False, "loopback_host_alias": "host.docker.internal"},
|
||||
},
|
||||
# threshold: fraction of the model's context limit; min_tail: real user messages kept in the tail
|
||||
"compression": {"enabled": True, "threshold": 0.50, "min_tail_user_messages": 1},
|
||||
"agent": {
|
||||
"max_turns": 500, # shared with subagents
|
||||
"verbose": False,
|
||||
"system_prompt": "",
|
||||
"prefill_messages_file": "",
|
||||
"reasoning_effort": "",
|
||||
"service_tier": "",
|
||||
"max_turns": 500, "verbose": False, "system_prompt": "", "prefill_messages_file": "", # max_turns shared with subagents
|
||||
"reasoning_effort": "", "service_tier": "",
|
||||
"personalities": {}, # user overrides merged by name over hermes_cli.personality builtins
|
||||
},
|
||||
|
||||
"display": {
|
||||
"compact": False,
|
||||
"resume_display": "full",
|
||||
"resume_exchanges": 10, # /resume recap tuning (see hermes_cli/config.py DEFAULT_CONFIG)
|
||||
"resume_max_user_chars": 300,
|
||||
"resume_max_assistant_chars": 200,
|
||||
"resume_max_assistant_lines": 3,
|
||||
"resume_skip_tool_only": True,
|
||||
"show_reasoning": True, # keep in sync with hermes_cli/config.py DEFAULT_CONFIG
|
||||
"reasoning_full": False,
|
||||
"streaming": True,
|
||||
"busy_input_mode": "interrupt",
|
||||
"persistent_output": True,
|
||||
"persistent_output_max_lines": 200,
|
||||
# /resume recap tuning and show_reasoning: keep in sync with hermes_cli/config.py DEFAULT_CONFIG
|
||||
"resume_display": "full", "resume_exchanges": 10, "resume_max_user_chars": 300,
|
||||
"resume_max_assistant_chars": 200, "resume_max_assistant_lines": 3, "resume_skip_tool_only": True,
|
||||
"show_reasoning": True, "reasoning_full": False, "streaming": True, "busy_input_mode": "interrupt",
|
||||
"persistent_output": True, "persistent_output_max_lines": 200,
|
||||
# Also clear scrollback on redraw/resize recovery; off because users prefer history.
|
||||
"cli_rebuild_scrollback_on_redraw": False,
|
||||
"persist_prompts": True, # one-line summary of resolved modal prompts into scrollback
|
||||
|
||||
"skin": "default",
|
||||
},
|
||||
"clarify": {
|
||||
"timeout": 120, # seconds before a clarify prompt auto-proceeds
|
||||
},
|
||||
"code_execution": {
|
||||
"timeout": 300,
|
||||
"max_tool_calls": 50,
|
||||
},
|
||||
"auxiliary": {
|
||||
"vision": {
|
||||
"provider": "auto",
|
||||
"model": "",
|
||||
"base_url": "",
|
||||
"api_key": "",
|
||||
},
|
||||
},
|
||||
"delegation": {
|
||||
"max_iterations": 45,
|
||||
"model": "", # empty = inherit parent
|
||||
"provider": "", # empty = inherit parent
|
||||
"base_url": "",
|
||||
"api_key": "", # for delegation.base_url (falls back to OPENAI_API_KEY)
|
||||
},
|
||||
"onboarding": {
|
||||
"seen": {}, # first-touch hint flags (agent/onboarding.py), latched once shown
|
||||
},
|
||||
"clarify": {"timeout": 120}, # seconds before a clarify prompt auto-proceeds
|
||||
"code_execution": {"timeout": 300, "max_tool_calls": 50},
|
||||
"auxiliary": {"vision": {"provider": "auto", "model": "", "base_url": "", "api_key": ""}},
|
||||
# delegation: empty model/provider = inherit parent; api_key falls back to OPENAI_API_KEY
|
||||
"delegation": {"max_iterations": 45, "model": "", "provider": "", "base_url": "", "api_key": ""},
|
||||
"onboarding": {"seen": {}}, # first-touch hint flags (agent/onboarding.py), latched once shown
|
||||
}
|
||||
|
||||
|
||||
@@ -703,15 +653,11 @@ def _prepare_deferred_agent_startup() -> None:
|
||||
|
||||
def _flush_logging_and_stdio() -> None:
|
||||
"""Best-effort ``logging.shutdown()`` + stdout/stderr flush before ``os._exit``."""
|
||||
try:
|
||||
with suppress(Exception):
|
||||
logging.shutdown()
|
||||
except Exception:
|
||||
pass
|
||||
for _stream in (sys.stdout, sys.stderr):
|
||||
try:
|
||||
with suppress(Exception):
|
||||
_stream.flush()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _float_env(name: str, default: float) -> float:
|
||||
@@ -759,10 +705,8 @@ def _arm_exit_watchdog(timeout_s: float | None = None, *, from_signal: bool = Fa
|
||||
_flush_logging_and_stdio()
|
||||
os._exit(0)
|
||||
|
||||
try:
|
||||
with suppress(Exception): # never block shutdown on watchdog setup
|
||||
threading.Thread(target=_watchdog, daemon=True, name="exit-watchdog").start()
|
||||
except Exception:
|
||||
pass # best-effort — never block shutdown on watchdog setup
|
||||
|
||||
|
||||
_signal_watchdog_armed = False
|
||||
@@ -783,10 +727,8 @@ def _arm_exit_watchdog_on_shutdown_signal() -> None:
|
||||
base = _exit_watchdog_timeout()
|
||||
if base <= 0:
|
||||
return # explicitly disabled
|
||||
try:
|
||||
with suppress(Exception): # never let the backstop break signal handling
|
||||
_arm_exit_watchdog(timeout_s=base * 2, from_signal=True)
|
||||
except Exception:
|
||||
pass # never let the backstop break signal handling
|
||||
|
||||
|
||||
def _shutdown_agent_memory_provider(agent) -> None:
|
||||
@@ -797,10 +739,8 @@ def _shutdown_agent_memory_provider(agent) -> None:
|
||||
# ~5s drain would cancel it, so give it a bounded head start (watchdog is the backstop).
|
||||
_mm = getattr(agent, '_memory_manager', None)
|
||||
if _mm is not None and hasattr(_mm, 'flush_pending'):
|
||||
try:
|
||||
with suppress(Exception):
|
||||
_mm.flush_pending(timeout=10)
|
||||
except Exception:
|
||||
pass
|
||||
# Forward the agent's transcript so on_session_end hooks see the real conversation;
|
||||
# no-arg fallback for stubs / partially-initialised agents.
|
||||
_session_msgs = getattr(agent, '_session_messages', None)
|
||||
@@ -838,12 +778,9 @@ def _shutdown_cached_aux_clients() -> None:
|
||||
# Ordered teardown steps (attribute names, resolved at call time so tests can patch them)
|
||||
# and the exception class each swallows.
|
||||
_CLEANUP_STEPS = (
|
||||
("_stop_cli_wake_word", Exception),
|
||||
("_cleanup_all_terminals", Exception),
|
||||
("_interrupt_async_delegations", Exception),
|
||||
("_cleanup_all_browsers", Exception),
|
||||
("_shutdown_mcp_servers", BaseException),
|
||||
("_shutdown_cached_aux_clients", Exception),
|
||||
("_stop_cli_wake_word", Exception), ("_cleanup_all_terminals", Exception),
|
||||
("_interrupt_async_delegations", Exception), ("_cleanup_all_browsers", Exception),
|
||||
("_shutdown_mcp_servers", BaseException), ("_shutdown_cached_aux_clients", Exception),
|
||||
)
|
||||
|
||||
|
||||
@@ -862,10 +799,8 @@ def _run_cleanup(*, notify_session_finalize: bool = True):
|
||||
_reset_terminal_input_modes_on_exit()
|
||||
|
||||
for step, swallow in _CLEANUP_STEPS:
|
||||
try:
|
||||
with suppress(swallow):
|
||||
globals()[step]()
|
||||
except swallow:
|
||||
pass
|
||||
if notify_session_finalize:
|
||||
cleanup_session_id = _active_agent_ref.session_id if _active_agent_ref else None
|
||||
if _should_emit_cleanup_session_finalize(cleanup_session_id):
|
||||
@@ -890,11 +825,9 @@ def _should_emit_cleanup_session_finalize(session_id: str | None) -> bool:
|
||||
|
||||
|
||||
def _notify_session_finalize(*, session_id: str | None, platform: str = "cli", reason: str = "shutdown") -> None:
|
||||
try:
|
||||
with suppress(Exception):
|
||||
from hermes_cli.lifecycle import finalize_session
|
||||
finalize_session(session_id=session_id, platform=platform, reason=reason)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _oneshot_agent_and_session(cli):
|
||||
@@ -905,15 +838,13 @@ def _oneshot_agent_and_session(cli):
|
||||
|
||||
def _invoke_interrupted_session_end(agent, session_id, reason: str, **extra) -> None:
|
||||
"""Best-effort ``on_session_end`` hook for a turn cut short (never raises)."""
|
||||
try:
|
||||
with suppress(Exception):
|
||||
from hermes_cli.lifecycle import invoke_hook as _invoke_hook
|
||||
_invoke_hook(
|
||||
"on_session_end", session_id=session_id, completed=False, interrupted=True,
|
||||
model=getattr(agent, "model", None), platform=getattr(agent, "platform", None) or "cli",
|
||||
reason=reason, **extra,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _emit_interrupted_session_end(cli, *, reason: str = "keyboard_interrupt") -> None:
|
||||
@@ -922,18 +853,14 @@ def _emit_interrupted_session_end(cli, *, reason: str = "keyboard_interrupt") ->
|
||||
if agent is None:
|
||||
return
|
||||
|
||||
try:
|
||||
with suppress(Exception):
|
||||
agent.interrupt(reason.replace("_", " "))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if session_id in _handed_off_session_ids: # gateway owns the lifecycle now
|
||||
return
|
||||
if session_id:
|
||||
try:
|
||||
with suppress(Exception):
|
||||
cli.session_id = session_id
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
_invoke_interrupted_session_end(
|
||||
agent, session_id, reason,
|
||||
@@ -1049,12 +976,9 @@ def _reset_terminal_input_modes_on_exit() -> None:
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
with open("/dev/tty", "w", encoding="ascii") as tty:
|
||||
tty.write(_TERMINAL_INPUT_MODE_RESET_SEQ)
|
||||
tty.flush()
|
||||
except Exception:
|
||||
pass
|
||||
with suppress(Exception), open("/dev/tty", "w", encoding="ascii") as tty:
|
||||
tty.write(_TERMINAL_INPUT_MODE_RESET_SEQ)
|
||||
tty.flush()
|
||||
|
||||
|
||||
from hermes_cli.worktree_ops import ( # noqa: F401 (mixins/tests/worktree_gc import via cli)
|
||||
@@ -1302,10 +1226,8 @@ def _query_osc11_background() -> str | None:
|
||||
return f"#{r:02X}{g:02X}{b:02X}"
|
||||
finally:
|
||||
# TCSAFLUSH discards unread input, scrubbing a partial reply before prompt_toolkit reads it.
|
||||
try:
|
||||
with suppress(Exception):
|
||||
termios.tcsetattr(fd, termios.TCSAFLUSH, old)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
drain_deadline = time.monotonic() + 0.05
|
||||
while time.monotonic() < drain_deadline:
|
||||
@@ -1425,11 +1347,9 @@ _install_skin_light_mode_hook()
|
||||
|
||||
|
||||
# Prime the light-mode cache when interactive so OSC 11 happens before prompt_toolkit owns the tty.
|
||||
try:
|
||||
with suppress(Exception):
|
||||
if sys.stdin.isatty() and sys.stdout.isatty():
|
||||
_detect_light_mode()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
class _SkinAwareAnsi:
|
||||
@@ -2168,10 +2088,8 @@ def _bind_prompt_submit_keys(kb, handler, *, multiline_shortcuts_enabled: Option
|
||||
|
||||
def _disable_prompt_toolkit_cpr_warning(app) -> None:
|
||||
"""Let prompt_toolkit fall back from CPR without printing into the prompt."""
|
||||
try:
|
||||
with suppress(Exception):
|
||||
app.renderer.cpr_not_supported_callback = None
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _terminal_may_leak_cpr() -> bool:
|
||||
@@ -2979,10 +2897,8 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin, CLITuiMix
|
||||
print(message, file=sys.stderr) if stderr else self._console_print(f"[bold red]{message}[/]")
|
||||
return False
|
||||
self._active_session_lease = lease
|
||||
try:
|
||||
with suppress(Exception):
|
||||
atexit.register(self._release_active_session)
|
||||
except Exception:
|
||||
pass
|
||||
return True
|
||||
|
||||
def _release_active_session(self) -> None:
|
||||
@@ -3175,50 +3091,24 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin, CLITuiMix
|
||||
# ``_handle_<name>_command(cmd)``. Looked up via getattr at dispatch time so
|
||||
# monkeypatching works. A handler returning False exits the REPL.
|
||||
_SLASH_DISPATCH: dict[str, tuple[str, bool]] = {
|
||||
"exit": ("_cmd_exit", True),
|
||||
"quit": ("_cmd_exit", True),
|
||||
"help": ("_cmd_help", True),
|
||||
"palette": ("_open_command_palette", False),
|
||||
"whoami": ("_handle_whoami_command", False),
|
||||
"profile": ("_handle_profile_command", False),
|
||||
"toolsets": ("show_toolsets", False),
|
||||
"config": ("show_config", False),
|
||||
"redraw": ("_cmd_redraw", True),
|
||||
"clear": ("_cmd_clear", True),
|
||||
"history": ("show_history", False),
|
||||
"title": ("_cmd_title", True),
|
||||
"new": ("_cmd_new", True),
|
||||
"model": ("_handle_model_switch", True),
|
||||
"codex-runtime": ("_handle_codex_runtime", True),
|
||||
"retry": ("_cmd_retry", True),
|
||||
"prompt": ("_handle_prompt_compose_command", True),
|
||||
"undo": ("_cmd_undo", True),
|
||||
"save": ("save_conversation", True),
|
||||
"skills": ("_cmd_skills", True),
|
||||
"platforms": ("_show_gateway_status", False),
|
||||
"status": ("_show_session_status", False),
|
||||
"context": ("_show_context_breakdown", True),
|
||||
"egress": ("_cmd_egress", True),
|
||||
"statusbar": ("_cmd_statusbar", True),
|
||||
"verbose": ("_toggle_verbose", False),
|
||||
"yolo": ("_toggle_yolo", False),
|
||||
"compress": ("_manual_compress", True),
|
||||
"subscription": ("_show_subscription", False),
|
||||
"topup": ("_show_billing", True),
|
||||
"insights": ("_show_insights", True),
|
||||
"update": ("_cmd_update", True),
|
||||
"version": ("_cmd_version", True),
|
||||
"paste": ("_handle_paste_command", False),
|
||||
"reload": ("_cmd_reload", True),
|
||||
"reload-mcp": ("_confirm_and_reload_mcp", True),
|
||||
"reload-skills": ("_cmd_reload_skills", True),
|
||||
"plugins": ("_cmd_plugins", True),
|
||||
"stop": ("_handle_stop_command", False),
|
||||
"agents": ("_handle_agents_command", False),
|
||||
"bg": ("_handle_background_command", True),
|
||||
"queue": ("_cmd_queue", True),
|
||||
"steer": ("_cmd_steer", True),
|
||||
"moa": ("_cmd_moa", True),
|
||||
"exit": ("_cmd_exit", True), "quit": ("_cmd_exit", True), "help": ("_cmd_help", True),
|
||||
"palette": ("_open_command_palette", False), "whoami": ("_handle_whoami_command", False),
|
||||
"profile": ("_handle_profile_command", False), "toolsets": ("show_toolsets", False),
|
||||
"config": ("show_config", False), "redraw": ("_cmd_redraw", True), "clear": ("_cmd_clear", True),
|
||||
"history": ("show_history", False), "title": ("_cmd_title", True), "new": ("_cmd_new", True),
|
||||
"model": ("_handle_model_switch", True), "codex-runtime": ("_handle_codex_runtime", True),
|
||||
"retry": ("_cmd_retry", True), "prompt": ("_handle_prompt_compose_command", True),
|
||||
"undo": ("_cmd_undo", True), "save": ("save_conversation", True), "skills": ("_cmd_skills", True),
|
||||
"platforms": ("_show_gateway_status", False), "status": ("_show_session_status", False),
|
||||
"context": ("_show_context_breakdown", True), "egress": ("_cmd_egress", True),
|
||||
"statusbar": ("_cmd_statusbar", True), "verbose": ("_toggle_verbose", False), "yolo": ("_toggle_yolo", False),
|
||||
"compress": ("_manual_compress", True), "subscription": ("_show_subscription", False),
|
||||
"topup": ("_show_billing", True), "insights": ("_show_insights", True), "update": ("_cmd_update", True),
|
||||
"version": ("_cmd_version", True), "paste": ("_handle_paste_command", False), "reload": ("_cmd_reload", True),
|
||||
"reload-mcp": ("_confirm_and_reload_mcp", True), "reload-skills": ("_cmd_reload_skills", True),
|
||||
"plugins": ("_cmd_plugins", True), "stop": ("_handle_stop_command", False),
|
||||
"agents": ("_handle_agents_command", False), "bg": ("_handle_background_command", True),
|
||||
"queue": ("_cmd_queue", True), "steer": ("_cmd_steer", True), "moa": ("_cmd_moa", True),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
@@ -3488,10 +3378,8 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin, CLITuiMix
|
||||
lambda: self._drain_process_notifications("cli-idle"),
|
||||
self._maybe_fire_loop_tick,
|
||||
):
|
||||
try:
|
||||
with suppress(Exception):
|
||||
step()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _tui_unwrap_input(self, user_input):
|
||||
"""Unwrap ``_VoiceInputMessage`` / ``_SeededQueryMessage`` -> ``(text_or_tuple, is_voice_input, is_seeded_query)``."""
|
||||
@@ -3625,10 +3513,8 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin, CLITuiMix
|
||||
_cprint(f"{_DIM}Voice auto-restart failed: {e}{_RST}")
|
||||
threading.Thread(target=_restart_recording, daemon=True).start()
|
||||
|
||||
try:
|
||||
with suppress(Exception):
|
||||
self._drain_process_notifications("cli-post-turn")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _tui_signal_handler(self, signum, frame):
|
||||
"""SIGHUP/SIGTERM -> graceful shutdown.
|
||||
@@ -3638,10 +3524,8 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin, CLITuiMix
|
||||
``logger.debug`` is guarded: logging is not reentrant-safe and a shutdown race
|
||||
can raise ``KeyError`` inside the handler, bypassing prompt_toolkit's unwind.
|
||||
"""
|
||||
try:
|
||||
with suppress(Exception):
|
||||
logger.debug("Received signal %s, triggering graceful shutdown", signum)
|
||||
except Exception:
|
||||
pass
|
||||
# Arm the backstop IMMEDIATELY: if the unwind wedges, _run_cleanup never arms its own.
|
||||
_arm_exit_watchdog_on_shutdown_signal()
|
||||
if self._agent_running:
|
||||
@@ -3662,17 +3546,13 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin, CLITuiMix
|
||||
|
||||
def _tui_print_startup(self):
|
||||
"""Startup output: light-mode probe, banner, advisories, resume/welcome lines, tips."""
|
||||
try: # light-mode probe before pt grabs the tty (cached, never re-queried mid-render)
|
||||
with suppress(Exception): # light-mode probe before pt grabs the tty (cached)
|
||||
_detect_light_mode()
|
||||
except Exception:
|
||||
pass
|
||||
# Scroll the cursor to the last row so banner, responses and prompt pin to the bottom.
|
||||
try:
|
||||
with suppress(Exception):
|
||||
_term_lines = shutil.get_terminal_size().lines
|
||||
if _term_lines > 2:
|
||||
print("\n" * (_term_lines - 1), end="", flush=True)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.show_banner()
|
||||
self._show_security_advisories()
|
||||
@@ -3715,11 +3595,9 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin, CLITuiMix
|
||||
def _tui_startup_prewarm_and_warnings(self, _welcome_skin):
|
||||
"""Idle-window prewarms (picker cache, agent runtime imports) plus the redaction-off and OpenClaw-residue banners."""
|
||||
# Warm the /model picker cache off-thread (else its first open blocks ~1-2s).
|
||||
try:
|
||||
with suppress(Exception):
|
||||
from hermes_cli.model_switch import prewarm_picker_cache_async
|
||||
prewarm_picker_cache_async()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Pre-import the agent runtime (~1.5s: run_agent + OpenAI SDK) off-thread; the import
|
||||
# lock makes an early submit block on the remaining work rather than redo it.
|
||||
@@ -3735,7 +3613,7 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin, CLITuiMix
|
||||
threading.Thread(target=_prewarm_agent_runtime, name="agent-runtime-prewarm", daemon=True).start()
|
||||
|
||||
# Redaction is ON by default; be loud when the operator turned it off.
|
||||
try:
|
||||
with suppress(Exception):
|
||||
_redact_raw = os.getenv("HERMES_REDACT_SECRETS", "true")
|
||||
if _redact_raw.lower() not in {"1", "true", "yes", "on"}:
|
||||
self._console_print(
|
||||
@@ -3746,8 +3624,6 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin, CLITuiMix
|
||||
"[cyan]security.redact_secrets: true[/] in config.yaml "
|
||||
"to re-enable."
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
# One-time banner when ~/.openclaw/ is left over from a migration.
|
||||
try:
|
||||
from agent.onboarding import (
|
||||
@@ -3769,14 +3645,12 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin, CLITuiMix
|
||||
|
||||
def _tui_startup_background_maintenance(self):
|
||||
"""Best-effort startup passes: curator skill maintenance, personal + org skill sync."""
|
||||
try:
|
||||
with suppress(Exception):
|
||||
from agent.curator import maybe_run_curator
|
||||
maybe_run_curator(
|
||||
idle_for_seconds=float("inf"), # CLI startup = fully idle
|
||||
on_summary=lambda msg: self._console_print(f"[dim #6b7684]💾 {msg}[/]"),
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Skill sync (personal, then org-shared): inert unless the access gate is open
|
||||
# and a sync base URL is configured. The org pull is gated on a real org role on
|
||||
@@ -3787,10 +3661,8 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin, CLITuiMix
|
||||
except Exception:
|
||||
return
|
||||
for pull in (maybe_pull_skills, maybe_pull_org_skills):
|
||||
try:
|
||||
with suppress(Exception):
|
||||
pull()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _tui_build_application(self, layout, kb, style):
|
||||
"""Construct the prompt_toolkit Application for the REPL."""
|
||||
@@ -3962,26 +3834,18 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin, CLITuiMix
|
||||
self._should_exit = True
|
||||
self._pet_stop_anim()
|
||||
# Without this line the terminal sits silent through the whole cleanup window.
|
||||
try:
|
||||
with suppress(Exception):
|
||||
print(f"{_DIM}Shutting down… (finalizing session){_RST}", flush=True)
|
||||
except Exception:
|
||||
pass
|
||||
if self.agent and self._agent_running:
|
||||
try:
|
||||
with suppress(Exception):
|
||||
request_hard_interrupt(self.agent)
|
||||
except Exception:
|
||||
pass
|
||||
if self._voice_recorder:
|
||||
try:
|
||||
with suppress(Exception):
|
||||
self._voice_recorder.shutdown()
|
||||
except Exception:
|
||||
pass
|
||||
self._voice_recorder = None
|
||||
try:
|
||||
with suppress(Exception):
|
||||
from tools.voice_mode import cleanup_temp_recordings
|
||||
cleanup_temp_recordings()
|
||||
except Exception:
|
||||
pass
|
||||
for _unset in (set_sudo_password_callback, set_approval_callback, set_secret_capture_callback):
|
||||
_unset(None)
|
||||
# On SIGHUP/SIGTERM the agent thread may be reaped before its own persistence runs.
|
||||
@@ -4230,25 +4094,19 @@ def _install_single_query_signal_handlers(cli):
|
||||
# and the dispatcher sees 'running' forever, so os._exit(0) (SIGALRM deadman guards
|
||||
# a blocking flush). That skips atexit + the token-drain hook, hence the explicit flush.
|
||||
if os.environ.get("HERMES_KANBAN_TASK"):
|
||||
try:
|
||||
with suppress(Exception):
|
||||
if hasattr(_signal, "SIGALRM"):
|
||||
_signal.signal(_signal.SIGALRM, lambda *_: os._exit(0))
|
||||
_signal.alarm(5)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
with suppress(Exception):
|
||||
_flush_one_shot_session_store(cli)
|
||||
except Exception:
|
||||
pass
|
||||
_flush_logging_and_stdio()
|
||||
os._exit(0)
|
||||
raise KeyboardInterrupt()
|
||||
try:
|
||||
with suppress(Exception): # restricted environments
|
||||
for _name in ("SIGINT", "SIGTERM", "SIGHUP"):
|
||||
if hasattr(_signal, _name):
|
||||
_signal.signal(getattr(_signal, _name), _signal_handler_q)
|
||||
except Exception:
|
||||
pass # restricted environments
|
||||
|
||||
|
||||
def _build_cli_from_args(model, toolsets, provider, reasoning, api_key, base_url, max_turns, run_budget, verbose, compact, resume, checkpoints, pass_session_id, ignore_rules, skills):
|
||||
@@ -4319,11 +4177,9 @@ def _build_cli_from_args(model, toolsets, provider, reasoning, api_key, base_url
|
||||
def _run_legacy_gateway():
|
||||
"""Legacy `cli.py --gateway` entry: arm the startup watchdog (before importing the gateway graph), then run it."""
|
||||
import asyncio
|
||||
try:
|
||||
with suppress(Exception):
|
||||
from hermes_startup_watchdog import arm_startup_watchdog
|
||||
arm_startup_watchdog()
|
||||
except Exception:
|
||||
pass
|
||||
from gateway.run import start_gateway
|
||||
print("Starting Hermes Gateway (messaging platforms)...")
|
||||
asyncio.run(start_gateway())
|
||||
@@ -4514,11 +4370,9 @@ def main(
|
||||
python cli.py -w -q "Fix issue #123" # Single query in worktree
|
||||
"""
|
||||
# UTF-8 stdio on Windows before any print (Rich box-drawing would UnicodeEncodeError on cp1252).
|
||||
try:
|
||||
with suppress(Exception):
|
||||
from hermes_cli.stdio import configure_windows_stdio
|
||||
configure_windows_stdio()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
os.environ["HERMES_INTERACTIVE"] = "1" # terminal_tool: interactive sudo prompts with timeout
|
||||
|
||||
|
||||
Reference in New Issue
Block a user