From 456377f704355a1a10bef24af5761b56406196e2 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Wed, 2 Sep 2026 21:49:33 -0700 Subject: [PATCH] refactor(hermes_cli): compact docstrings (keep WHY/invariants); final AST-neutral layout pass --- hermes_cli/setup_summary.py | 18 +++----- hermes_cli/setup_tts.py | 6 +-- hermes_cli/setup_whatsapp_cloud.py | 30 ++++-------- hermes_cli/sqlite_safe_read.py | 74 ++++++++++-------------------- hermes_cli/sqlite_util.py | 16 ++----- hermes_cli/status.py | 7 ++- hermes_cli/status_auth.py | 12 ++--- hermes_cli/terminal_breadcrumbs.py | 26 ++++------- 8 files changed, 60 insertions(+), 129 deletions(-) diff --git a/hermes_cli/setup_summary.py b/hermes_cli/setup_summary.py index 810a5064b0..934597d52d 100644 --- a/hermes_cli/setup_summary.py +++ b/hermes_cli/setup_summary.py @@ -17,16 +17,14 @@ _TTS_SUMMARY_ROWS = { "mistral": ("Mistral Voxtral", ("MISTRAL_API_KEY",)), "gemini": ("Google Gemini", ("GEMINI_API_KEY", "GOOGLE_API_KEY")), "neutts": ("NeuTTS", "neutts", "run 'hermes setup tts'"), - "kittentts": ("KittenTTS", "kittentts", "run 'hermes setup tts'"), -} + "kittentts": ("KittenTTS", "kittentts", "run 'hermes setup tts'")} _TTS_SUMMARY_DEFAULT = ("Edge TTS", ()) _STT_SUMMARY_ROWS = { "openai": ("OpenAI", ("VOICE_TOOLS_OPENAI_KEY", "OPENAI_API_KEY")), "groq": ("Groq Whisper", ("GROQ_API_KEY",)), "elevenlabs": ("ElevenLabs Scribe", ("ELEVENLABS_API_KEY",)), "xai": ("xAI", ()), - "deepinfra": ("DeepInfra", ("DEEPINFRA_API_KEY",)), -} + "deepinfra": ("DeepInfra", ("DEEPINFRA_API_KEY",))} _STT_SUMMARY_DEFAULT = ("Local Whisper", "faster_whisper", "run 'hermes tools' → Speech-to-Text") # Browser "missing" hint keyed by the configured provider; anything else gets the generic hint. @@ -34,8 +32,7 @@ _BROWSER_MISSING_HINTS = { "Browserbase": "npm install -g agent-browser and set BROWSERBASE_API_KEY/BROWSERBASE_PROJECT_ID", "Browser Use": "npm install -g agent-browser and set BROWSER_USE_API_KEY", "Camofox": "CAMOFOX_URL", - "Local browser": "npm install -g agent-browser && agent-browser install --with-deps", -} + "Local browser": "npm install -g agent-browser && agent-browser install --with-deps"} _BROWSER_MISSING_DEFAULT = "npm install -g agent-browser, set CAMOFOX_URL, or configure Browser Use or Browserbase" _WEB_MISSING = ("EXA_API_KEY, PARALLEL_API_KEY, FIRECRAWL_API_KEY/FIRECRAWL_API_URL, TAVILY_API_KEY, " "KEENABLE_API_KEY, or SEARXNG_URL") @@ -50,17 +47,14 @@ _EDIT_WIZARD_ROWS = ( ("hermes setup model", " Change model/provider"), ("hermes setup terminal", " Change terminal backend"), ("hermes setup gateway", " Configure messaging"), - ("hermes setup tools", " Configure tool providers"), -) + ("hermes setup tools", " Configure tool providers")) _EDIT_CONFIG_ROWS = ( ("hermes config", " View current settings"), ("hermes config edit", " Open config in your editor"), - ("hermes config set ", ""), -) + ("hermes config set ", "")) _READY_ROWS = ( ("hermes", " Start chatting"), ("hermes gateway", " Start messaging gateway"), - ("hermes doctor", " Check for issues"), -) + ("hermes doctor", " Check for issues")) def _voice_provider_status(kind: str, provider: str, rows: dict, default: tuple) -> tuple: diff --git a/hermes_cli/setup_tts.py b/hermes_cli/setup_tts.py index b4bce8e997..eda2ba48d7 100644 --- a/hermes_cli/setup_tts.py +++ b/hermes_cli/setup_tts.py @@ -121,8 +121,7 @@ _TTS_PROVIDER_CHOICES = [ ("mistral", "Mistral Voxtral TTS (multilingual, native Opus, needs API key)"), ("gemini", "Google Gemini TTS (30 prebuilt voices, prompt-controllable, needs API key)"), ("neutts", "NeuTTS (local on-device, free, ~300MB model download)"), - ("kittentts", "KittenTTS (local on-device, free, lightweight ~25-80MB ONNX)"), -] + ("kittentts", "KittenTTS (local on-device, free, lightweight ~25-80MB ONNX)")] # Short label = menu label minus its parenthetical ("Edge TTS", "Mistral Voxtral TTS", ...). _TTS_PROVIDER_LABELS = {key: label.split(" (")[0] for key, label in _TTS_PROVIDER_CHOICES} # provider -> (env vars that satisfy it, env var to save, prompt, success line, pre-prompt hint) @@ -147,8 +146,7 @@ _TTS_LOCAL_PROVIDERS = { "kittentts": ("kittentts", "KittenTTS", ("KittenTTS is lightweight (~25-80MB, CPU-only, no API key required).", "Voices: Jasper, Bella, Luna, Bruno, Rosie, Hugo, Kiki, Leo"), - "Install KittenTTS now?", _install_kittentts_deps), -} + "Install KittenTTS now?", _install_kittentts_deps)} def _tts_api_key_step(selected: str) -> str: diff --git a/hermes_cli/setup_whatsapp_cloud.py b/hermes_cli/setup_whatsapp_cloud.py index 87ba8263ea..814cce7236 100644 --- a/hermes_cli/setup_whatsapp_cloud.py +++ b/hermes_cli/setup_whatsapp_cloud.py @@ -69,8 +69,7 @@ _FOREIGN_TOKEN_PREFIXES = ( (("xoxb-", "xoxp-"), "That's a Slack token, not a Meta WhatsApp access token. " "Meta tokens start with 'EAA'."), (("ghp_", "gho_"), "That's a GitHub token, not a Meta WhatsApp access " - "token. Meta tokens start with 'EAA'."), -) + "token. Meta tokens start with 'EAA'.")) def _validate_app_secret(value: str) -> tuple[bool, Optional[str]]: @@ -110,12 +109,8 @@ def _validate_access_token(value: str) -> tuple[bool, Optional[str]]: # --- Prompt helpers def _prompt(message: str, default: Optional[str] = None, secret: bool = False) -> str: - """Read one line of input. Returns "" on EOF / Ctrl+C / empty input. - - ``default`` is shown but NOT auto-applied on empty input: callers handle "kept existing" - explicitly so a real value is distinguishable from a display preview (masked secrets). - ``secret=True`` reads via ``getpass`` so credentials are not echoed or left in scrollback. - """ + """Read one line; "" on EOF / Ctrl+C / empty. ``default`` is shown but NOT auto-applied so a + real value stays distinguishable from a masked preview; ``secret`` reads via ``getpass``.""" try: suffix = f" [{default}]" if default else "" if secret and sys.stdin.isatty(): @@ -131,11 +126,8 @@ def _prompt(message: str, default: Optional[str] = None, secret: bool = False) - def _prompt_validated( message: str, validator, *, current: Optional[str] = None, help_text: Optional[str] = None, secret: bool = False) -> Optional[str]: - """Repeat the prompt until the user enters a valid value or aborts. - - Returns the validated value, or None if the user gave up (empty response after an error, or - Ctrl+C). ``current`` is shown as a default for re-runs of the wizard with existing config. - """ + """Repeat the prompt until a valid value or the user gives up (None: empty answer, Ctrl+C). + ``current`` is shown as the default on wizard re-runs.""" if help_text: for line in help_text.strip().splitlines(): print(f" {line}") @@ -223,8 +215,7 @@ _CREDENTIAL_STEPS = ( "If 'Show' doesn't appear, you may need Admin role on the app.\n" "It's a 32-character lowercase hex string.\n\n" "Without the App Secret, inbound webhook POSTs are refused\n" - "with HTTP 503 (we can't verify they actually came from Meta)."), -) + "with HTTP 503 (we can't verify they actually came from Meta).")) # Optional step-4 IDs: (prompt label, env var, validator, help text). _OPTIONAL_ID_STEPS = ( @@ -236,8 +227,7 @@ _OPTIONAL_ID_STEPS = ( "WhatsApp Business Account ID. Found in: App Dashboard →\n" "WhatsApp → API Setup, near the top — 'WhatsApp Business\n" "Account ID'. Numeric, ~15+ digits.\n" - "Not required for messaging — useful for analytics."), -) + "Not required for messaging — useful for analytics.")) def _credential_step(step) -> tuple[Optional[str], bool]: @@ -263,11 +253,7 @@ def _credential_step(step) -> tuple[Optional[str], bool]: def run_whatsapp_cloud_setup() -> int: - """Interactive wizard for the WhatsApp Cloud API adapter. - - Returns 0 on full success, 1 on user abort, 2 on partial completion (some fields written but the - user bailed before finishing). - """ + """Interactive wizard for the WhatsApp Cloud API adapter. Returns 0 on success, 1 on abort.""" from hermes_cli.config import get_env_value, save_env_value _lines( "", "⚕ WhatsApp Business Cloud API Setup", "=" * 50, "", diff --git a/hermes_cli/sqlite_safe_read.py b/hermes_cli/sqlite_safe_read.py index 4e3acf166b..a8a6d12aba 100644 --- a/hermes_cli/sqlite_safe_read.py +++ b/hermes_cli/sqlite_safe_read.py @@ -1,10 +1,9 @@ """Lock-safe inspection of SQLite database files. -POSIX advisory locks are cancelled **process-wide** by ``close()`` on *any* file descriptor for -that file, so a bare ``open(db_path, "rb") ... close()`` on a **live** database silently drops -every lock SQLite holds on it from this process -- including the EXCLUSIVE lock a ``VACUUM`` is -holding while it rewrites the whole file, and the RESERVED lock of an in-flight ``BEGIN IMMEDIATE``. -This module tracks live connections so raw reads only happen when none exist. +POSIX advisory locks are cancelled **process-wide** by ``close()`` on *any* fd for that file, so a +bare ``open(db_path, "rb") ... close()`` on a live database drops every lock SQLite holds from this +process (a VACUUM's EXCLUSIVE lock, an in-flight BEGIN IMMEDIATE's RESERVED lock). This module +tracks live connections so raw reads happen only when none exist. """ from __future__ import annotations @@ -27,11 +26,8 @@ _live_connections: dict[str, int] = {} class UntrackableConnectionError(RuntimeError): - """A connection to a probe-able database could not be tracked. - - Raised rather than silently returning an untracked connection: on these paths tracking is part - of the correctness contract, not an optimisation. - """ + """A connection to a probe-able database could not be tracked. Raised rather than returning an + untracked connection: on these paths tracking is part of the correctness contract.""" class LiveConnectionError(RuntimeError): @@ -59,11 +55,8 @@ def _canonical_db_path(conn: sqlite3.Connection) -> Optional[str]: def track_connection(path: Path | str) -> None: - """Record that this process now holds a connection to *path*. - - Prefer :func:`connect_tracked`; this exists for callers that manage their own connection - objects, and for tests. - """ + """Record that this process holds a connection to *path* (prefer :func:`connect_tracked`; this + is for callers managing their own connection objects, and for tests).""" with _live_lock: _track_key(_key(path)) @@ -118,12 +111,9 @@ _tracked_factory_cache: dict[type, type] = {} def _tracking_factory(factory: type) -> type: - """Return *factory* augmented with untrack-on-close. - - Callers legitimately pass their own ``Connection`` subclasses (tests simulate FTS5-less or - pragma-failing runtimes); refusing them or leaving them untracked would quietly unguard the - database, so the tracking ``close()`` is mixed into the caller's class instead. - """ + """Return *factory* augmented with untrack-on-close. Callers legitimately pass their own + ``Connection`` subclasses (tests simulate FTS5-less or pragma-failing runtimes); leaving them + untracked would quietly unguard the database, so the tracking ``close()`` is mixed in.""" if factory is sqlite3.Connection: return TrackedConnection if issubclass(factory, _TrackingMixin): @@ -138,14 +128,10 @@ def _tracking_factory(factory: type) -> type: def connect_tracked( path: Path | str, *, tracking_path: Path | str | None = None, connect_fn=None, **kwargs, ) -> sqlite3.Connection: - """``sqlite3.connect`` that registers the connection for the lifetime of the fd. - - Use for any connection to a database whose file might otherwise be byte-probed (``state.db``, - ``kanban.db``). The registration is released automatically on ``close()``. - - The open and the registration happen together under ``_live_lock``, so a concurrent - :func:`read_header_bytes_preopen` cannot slip between them and cancel this connection's locks. - """ + """``sqlite3.connect`` that registers the connection for the lifetime of the fd (released on + ``close()``). Use for any database that might be byte-probed (``state.db``, ``kanban.db``). + Open and registration happen together under ``_live_lock`` so a concurrent + :func:`read_header_bytes_preopen` cannot slip between them and cancel this connection's locks.""" opener = connect_fn if connect_fn is not None else sqlite3.connect kwargs["factory"] = _tracking_factory(kwargs.get("factory", sqlite3.Connection)) @@ -201,13 +187,9 @@ def page_count_bytes(conn: sqlite3.Connection) -> Optional[int]: def file_length_matches_header(conn: sqlite3.Connection) -> Optional[bool]: - """Whether the file on disk is at least as long as the header claims ("torn extend" check). - - Never opens the database file: the header side comes from ``PRAGMA page_count`` over *conn*, - the on-disk side from ``stat()``. In WAL mode a freshly committed page may still live in the - ``-wal`` file so the main file legitimately lags; callers must treat this as advisory unless - the database is in a rollback journal mode. - """ + """Whether the file on disk is at least as long as the header claims ("torn extend" check), + without opening the file (PRAGMA over *conn* + ``stat()``). Advisory in WAL mode: a freshly + committed page may still live in ``-wal`` so the main file legitimately lags.""" path_str = _canonical_db_path(conn) if path_str is None: return None @@ -221,13 +203,10 @@ def file_length_matches_header(conn: sqlite3.Connection) -> Optional[bool]: def read_header_bytes_preopen(path: Path | str, *, length: int = 100, force: bool = False) -> Optional[bytes]: - """Read the first *length* bytes of *path* -- only when no connection is live. - - This is the ONLY sanctioned byte-level read of a database file, restricted to first-open - validation (real SQLite database? zeroed? overwritten?). The registry check and the - ``open``/``read``/``close`` run together under ``_live_lock`` so a connection cannot be opened - between deciding "nothing is live" and closing this descriptor. - """ + """Read the first *length* bytes of *path* -- only when no connection is live. The ONLY + sanctioned byte-level read of a database file, for first-open validation (real SQLite? zeroed? + overwritten?). Check and open/read/close run together under ``_live_lock`` so a connection + cannot be opened between deciding "nothing is live" and closing this descriptor.""" with _live_lock: if not force and _key(path) in _live_connections: logger.debug( @@ -244,12 +223,9 @@ def read_header_bytes_preopen(path: Path | str, *, length: int = 100, force: boo @contextlib.contextmanager def offline_file_access(path: Path | str, *, what: str = "read"): - """Hold the connection-lifecycle lock across a raw read of a database file. - - Checking :func:`has_live_connection` and *then* doing raw I/O is a check/use race: a connection - opened in between would have its POSIX locks cancelled by the raw ``close()``. The lock is - held only for the raw I/O, never across caller work on an open connection. - """ + """Hold the connection-lifecycle lock across a raw read of a database file: checking + :func:`has_live_connection` and *then* doing raw I/O is a check/use race (a connection opened + in between loses its POSIX locks to the raw ``close()``). Held only for the raw I/O.""" with _live_lock: if _key(path) in _live_connections: raise LiveConnectionError( diff --git a/hermes_cli/sqlite_util.py b/hermes_cli/sqlite_util.py index 920fad1ff0..bd5e7779b4 100644 --- a/hermes_cli/sqlite_util.py +++ b/hermes_cli/sqlite_util.py @@ -7,12 +7,8 @@ import sqlite3 def add_column_if_missing(conn: sqlite3.Connection, table: str, column: str, ddl: str) -> bool: - """``ALTER TABLE ADD COLUMN ``, idempotent across races. - - Returns True when this call added the column; swallows the ``duplicate column name`` error a - concurrent migrator may have caused. ``column`` is the human-readable name, ``ddl`` the - actual definition. - """ + """``ALTER TABLE
ADD COLUMN ``, idempotent across races: True when this call added + it, False on the ``duplicate column name`` a concurrent migrator caused.""" try: conn.execute(f"ALTER TABLE {table} ADD COLUMN {ddl}") return True @@ -24,12 +20,8 @@ def add_column_if_missing(conn: sqlite3.Connection, table: str, column: str, ddl @contextlib.contextmanager def write_txn(conn: sqlite3.Connection): - """An IMMEDIATE write transaction: at most one concurrent writer wins. - - The explicit ROLLBACK is guarded so a SQLite auto-rollback (no active transaction left under EIO - / lock contention / corruption) cannot shadow the original exception with a spurious rollback - error. - """ + """An IMMEDIATE write transaction. The explicit ROLLBACK is guarded so a SQLite auto-rollback + (no transaction left under EIO / contention / corruption) cannot shadow the original error.""" conn.execute("BEGIN IMMEDIATE") try: yield conn diff --git a/hermes_cli/status.py b/hermes_cli/status.py index 9cbf7be6a6..5e06920225 100644 --- a/hermes_cli/status.py +++ b/hermes_cli/status.py @@ -123,16 +123,15 @@ _PLATFORMS = { # name -> (token env var, home-channel env var or None) "Weixin": ("WEIXIN_ACCOUNT_ID", "WEIXIN_HOME_CHANNEL"), "BlueBubbles": ("BLUEBUBBLES_SERVER_URL", "BLUEBUBBLES_HOME_CHANNEL"), "QQBot": ("QQ_APP_ID", "QQ_HOME_CHANNEL"), - "Yuanbao": ("YUANBAO_APP_ID", "YUANBAO_HOME_CHANNEL"), -} + "Yuanbao": ("YUANBAO_APP_ID", "YUANBAO_HOME_CHANNEL")} # Gateway manager label when the runtime snapshot is unavailable, keyed by platform. _GATEWAY_FALLBACK = {"linux": ("unknown", "systemd/manual"), "darwin": ("unknown", "launchd")} class _StatusContext: - """State shared across section renderers: config, --deep, and the Nous login facts - the Auth Providers section derives that the Nous Tool Gateway section needs later.""" + """Shared by section renderers: config, --deep, and the Nous login facts Auth Providers + derives for the later Nous Tool Gateway section.""" def __init__(self, deep: bool): self.deep, self.config = deep, {} diff --git a/hermes_cli/status_auth.py b/hermes_cli/status_auth.py index 25fdd222b6..de23c0fcad 100644 --- a/hermes_cli/status_auth.py +++ b/hermes_cli/status_auth.py @@ -65,14 +65,12 @@ _API_KEYS: dict[str, str | tuple[str, ...]] = { "Browserbase": "BROWSERBASE_API_KEY", # Optional — direct credentials only "FAL": "FAL_KEY", "ElevenLabs": "ELEVENLABS_API_KEY", - "GitHub": "GITHUB_TOKEN", -} + "GitHub": "GITHUB_TOKEN"} # OAuth detail rows: (label, status key, formatter, gate) — see _oauth_block. _FILE_REFRESH_ROWS = ( ("Auth file:", "auth_store", None, None), - ("Refreshed:", "last_refresh", _format_iso_timestamp, None), ("Error:", "error", None, False), -) + ("Refreshed:", "last_refresh", _format_iso_timestamp, None), ("Error:", "error", None, False)) _OAUTH_BLOCKS = ( # (row name, auth getter, login hint, detail rows) @@ -85,8 +83,7 @@ _OAUTH_BLOCKS = ( ("Region:", "region", None, True), ("Access exp:", "expires_at", None, None), ("Error:", "error", None, False))), - ("xAI OAuth", "get_xai_oauth_auth_status", "hermes auth add xai-oauth", _FILE_REFRESH_ROWS), -) + ("xAI OAuth", "get_xai_oauth_auth_status", "hermes auth add xai-oauth", _FILE_REFRESH_ROWS)) _APIKEY_PROVIDERS = { "Z.AI / GLM": ("GLM_API_KEY", "ZAI_API_KEY", "Z_AI_API_KEY"), @@ -94,8 +91,7 @@ _APIKEY_PROVIDERS = { "StepFun Step Plan": ("STEPFUN_API_KEY",), "MiniMax": ("MINIMAX_API_KEY",), "MiniMax (China)": ("MINIMAX_CN_API_KEY",), - "DeepInfra": ("DEEPINFRA_API_KEY",), -} + "DeepInfra": ("DEEPINFRA_API_KEY",)} def _render_api_keys(ctx): diff --git a/hermes_cli/terminal_breadcrumbs.py b/hermes_cli/terminal_breadcrumbs.py index aee7699237..ed05eb4dda 100644 --- a/hermes_cli/terminal_breadcrumbs.py +++ b/hermes_cli/terminal_breadcrumbs.py @@ -1,10 +1,6 @@ -"""Per-terminal session breadcrumbs for ``hermes -c`` / ``--continue``. - -Everything here is strictly best-effort: no function raises, and when no stable terminal identity -can be derived (no tty and no known multiplexer env var) breadcrumbs are skipped entirely and ``-c`` -falls back to the existing latest-session behavior. Gated by ``session.terminal_continue`` in -config.yaml (default true). -""" +"""Per-terminal session breadcrumbs for ``hermes -c`` / ``--continue``. Strictly best-effort: no +function raises; without a stable terminal identity (no tty, no known multiplexer env var) ``-c`` +falls back to latest-session. Gated by ``session.terminal_continue`` (default true).""" from __future__ import annotations @@ -78,11 +74,8 @@ def _prune_stale(directory: Path, now: float) -> None: def write_breadcrumb(session_id: str, cwd: Optional[str] = None) -> None: - """Record that this terminal's live session is ``session_id``. - - Synchronous, best-effort, never raises. No-op when the feature is disabled, the session id is - empty, or no terminal identity exists. - """ + """Record that this terminal's live session is ``session_id``. Never raises; no-op when the + feature is disabled, the session id is empty, or no terminal identity exists.""" try: if not session_id or not is_enabled(): return @@ -120,12 +113,9 @@ def read_breadcrumb() -> Optional[dict]: def resolve_breadcrumb_session() -> Optional[str]: - """Resolve a bare ``-c`` for this terminal, or ``None`` to fall back. - - Returns the breadcrumb's session id only when it still exists in the session DB, projected - forward through the compression chain so the resume lands on the live tip rather than a dead - compressed parent (same projection as ``main._resolve_session_by_name_or_id``). - """ + """Resolve a bare ``-c`` for this terminal, or ``None`` to fall back. The breadcrumb's session + id counts only if it still exists in the DB, projected through the compression chain so the + resume lands on the live tip (same projection as ``main._resolve_session_by_name_or_id``).""" if not is_enabled(): return None crumb = read_breadcrumb()