mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-31 19:16:29 +00:00
Resolve user-facing versions from SemVer release tags and available local history while retaining raw package metadata for API identity. Stamp Desktop provenance at build time, and expose generic version details for every Desktop install method.
97 lines
4.1 KiB
Python
97 lines
4.1 KiB
Python
"""
|
|
Hermes CLI - Unified command-line interface for Hermes Agent.
|
|
|
|
Provides subcommands for:
|
|
- hermes chat - Interactive chat (same as ./hermes)
|
|
- hermes gateway - Run gateway in foreground
|
|
- hermes gateway start - Start gateway service
|
|
- hermes gateway stop - Stop gateway service
|
|
- hermes setup - Interactive setup wizard
|
|
- hermes status - Show status of all components
|
|
- hermes cron - Manage cron jobs
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
|
|
__version__ = "0.19.0"
|
|
__release_date__ = "2026.7.20"
|
|
# ``v0.19.0`` will exist for future releases. This is the commit count of the
|
|
# transitional CalVer release tag, used only by immutable Nix builds that do
|
|
# not carry git history. `scripts/release.py` updates it for each new SemVer
|
|
# release tag before the release bump commit is made.
|
|
__release_rev_count__ = 16544
|
|
|
|
|
|
def _ensure_utf8():
|
|
"""Force UTF-8 stdout/stderr to prevent UnicodeEncodeError crashes.
|
|
|
|
Several environments select a legacy, non-UTF-8 encoding for the standard
|
|
streams:
|
|
|
|
- Windows services and terminals default to cp1252.
|
|
- Linux hosts with a latin-1 / C / POSIX locale (common on minimal Debian
|
|
installs and Raspberry Pi) select latin-1 or ASCII.
|
|
|
|
The CLI prints box-drawing characters (┌│├└─) and the ⚕ glyph in the setup
|
|
wizard, doctor, and status banners. Encoding those under a non-UTF-8 codec
|
|
raises an unhandled UnicodeEncodeError that crashes the command before it
|
|
can even start — e.g. `hermes setup` on a fresh Pi.
|
|
|
|
This runs at import time so it protects every CLI subcommand, on any
|
|
platform. It re-wraps stdout/stderr as UTF-8 when their encoding is not
|
|
already UTF-8, preferring TextIOWrapper.reconfigure() so the existing
|
|
stream object is fixed in place (cached `sys.stdout` references keep
|
|
working) and falling back to reopening the file descriptor with
|
|
closefd=False (the CPython-recommended safe variant).
|
|
|
|
No-op when the streams are already UTF-8: a healthy UTF-8 system sees no
|
|
stream change and no environment mutation.
|
|
|
|
Note: this is intentionally the earliest, platform-agnostic guard.
|
|
hermes_cli/stdio.py::configure_windows_stdio() runs later from the entry
|
|
points and layers on the Windows-only extras (console code-page flip,
|
|
EDITOR default, PATH augmentation); its stream reconfiguration is a
|
|
harmless idempotent no-op once we have already repaired the streams here.
|
|
"""
|
|
repaired = False
|
|
|
|
for stream_name in ("stdout", "stderr"):
|
|
stream = getattr(sys, stream_name, None)
|
|
if stream is None:
|
|
continue
|
|
try:
|
|
encoding = (getattr(stream, "encoding", "") or "").lower().replace("-", "")
|
|
if encoding == "utf8":
|
|
continue
|
|
|
|
# Preferred: reconfigure the existing TextIOWrapper in place. This
|
|
# preserves object identity so any code already holding a reference
|
|
# to the old sys.stdout benefits from the repair too.
|
|
reconfigure = getattr(stream, "reconfigure", None)
|
|
if callable(reconfigure):
|
|
reconfigure(encoding="utf-8", errors="replace")
|
|
repaired = True
|
|
continue
|
|
|
|
# Fallback: reopen the underlying file descriptor as UTF-8. Used
|
|
# for streams that don't expose reconfigure() (e.g. some wrapped
|
|
# or replaced streams). closefd=False keeps the original fd open.
|
|
new_stream = open(
|
|
stream.fileno(), "w", encoding="utf-8",
|
|
errors="replace", buffering=1, closefd=False,
|
|
)
|
|
setattr(sys, stream_name, new_stream)
|
|
repaired = True
|
|
except (AttributeError, OSError, ValueError):
|
|
pass
|
|
|
|
# Only nudge child processes toward UTF-8 when we actually detected a
|
|
# non-UTF-8 locale. On a healthy UTF-8 host children inherit UTF-8 from the
|
|
# locale already, so leave the environment untouched (minimal footprint).
|
|
if repaired:
|
|
os.environ.setdefault("PYTHONUTF8", "1")
|
|
os.environ.setdefault("PYTHONIOENCODING", "utf-8")
|
|
|
|
|
|
_ensure_utf8()
|