feat: consolidate 'hermes version' into 'hermes --version', remove the subcommand
'hermes --version' (and -V) now prints the full version report — banner version line with upstream SHA, install directory, authoritative install method, Python and OpenAI SDK versions, and update status — making the separate 'hermes version' subcommand redundant. The subcommand is removed. - _startup_fast.print_fast_version_info() is now THE canonical version printer: static lines print instantly from stdlib probes, then the banner label, install-method resolver, and update check lazy-import after the first line is on screen (each degrades gracefully). - main.py _print_version_info() delegates to it (used by /version in the CLI chat surface and the --version flag path); the old duplicate implementation is deleted. - hermes_cli/subcommands/version.py removed; parser wiring, subcommand sets, console-engine extraction entry, and tests updated. Hermes Console keeps a 'version' command wired to the shared printer. - Termux fast paths now include update status too (previously check_updates=False). - Docs/i18n, CONTRIBUTING, SECURITY, and nix checks updated to 'hermes --version'.
This commit is contained in:
+1
-1
@@ -582,7 +582,7 @@ test(tools): añadir tests unitarios para file_operations
|
|||||||
## Reportar Issues
|
## Reportar Issues
|
||||||
|
|
||||||
- Usa [GitHub Issues](https://github.com/NousResearch/hermes-agent/issues)
|
- Usa [GitHub Issues](https://github.com/NousResearch/hermes-agent/issues)
|
||||||
- Incluye: SO, versión de Python, versión de Hermes (`hermes version`), traza de error completa
|
- Incluye: SO, versión de Python, versión de Hermes (`hermes --version`), traza de error completa
|
||||||
- Incluye pasos para reproducir
|
- Incluye pasos para reproducir
|
||||||
- Verifica los issues existentes antes de crear duplicados
|
- Verifica los issues existentes antes de crear duplicados
|
||||||
- Para vulnerabilidades de seguridad, por favor reporta de forma privada
|
- Para vulnerabilidades de seguridad, por favor reporta de forma privada
|
||||||
|
|||||||
+1
-1
@@ -973,7 +973,7 @@ test(tools): add unit tests for file_operations
|
|||||||
## Reporting Issues
|
## Reporting Issues
|
||||||
|
|
||||||
- Use [GitHub Issues](https://github.com/NousResearch/hermes-agent/issues)
|
- Use [GitHub Issues](https://github.com/NousResearch/hermes-agent/issues)
|
||||||
- Include: OS, Python version, Hermes version (`hermes version`), full error traceback
|
- Include: OS, Python version, Hermes version (`hermes --version`), full error traceback
|
||||||
- Include steps to reproduce
|
- Include steps to reproduce
|
||||||
- Check existing issues before creating duplicates
|
- Check existing issues before creating duplicates
|
||||||
- For security vulnerabilities, please report privately
|
- For security vulnerabilities, please report privately
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ Un informe útil incluye:
|
|||||||
- Una descripción concisa y evaluación de severidad.
|
- Una descripción concisa y evaluación de severidad.
|
||||||
- El componente afectado, identificado por ruta de archivo y rango de líneas
|
- El componente afectado, identificado por ruta de archivo y rango de líneas
|
||||||
(ej. `path/to/file.py:120-145`).
|
(ej. `path/to/file.py:120-145`).
|
||||||
- Detalles del entorno (`hermes version`, SHA del commit, SO, versión de Python).
|
- Detalles del entorno (`hermes --version`, SHA del commit, SO, versión de Python).
|
||||||
- Una reproducción contra `main` o el último release.
|
- Una reproducción contra `main` o el último release.
|
||||||
- Una declaración de qué límite de confianza del §2 se cruza.
|
- Una declaración de qué límite de confianza del §2 se cruza.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ A useful report includes:
|
|||||||
- A concise description and severity assessment.
|
- A concise description and severity assessment.
|
||||||
- The affected component, identified by file path and line range
|
- The affected component, identified by file path and line range
|
||||||
(e.g. `path/to/file.py:120-145`).
|
(e.g. `path/to/file.py:120-145`).
|
||||||
- Environment details (`hermes version`, commit SHA, OS, Python
|
- Environment details (`hermes --version`, commit SHA, OS, Python
|
||||||
version).
|
version).
|
||||||
- A reproduction against `main` or the latest release.
|
- A reproduction against `main` or the latest release.
|
||||||
- A statement of which trust boundary in §2 is crossed.
|
- A statement of which trust boundary in §2 is crossed.
|
||||||
|
|||||||
+64
-11
@@ -73,7 +73,7 @@ def is_termux_env() -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def is_termux_fast_version_argv(argv: list[str]) -> bool:
|
def is_termux_fast_version_argv(argv: list[str]) -> bool:
|
||||||
return argv in (["--version"], ["-V"], ["version"])
|
return argv in (["--version"], ["-V"])
|
||||||
|
|
||||||
|
|
||||||
def is_global_fast_version_argv(argv: list[str]) -> bool:
|
def is_global_fast_version_argv(argv: list[str]) -> bool:
|
||||||
@@ -180,12 +180,43 @@ def read_install_method() -> str | None:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def print_fast_version_info() -> None:
|
def print_fast_version_info(*, check_updates: bool = True) -> None:
|
||||||
from hermes_cli import __release_date__, __version__
|
"""THE canonical ``hermes --version`` output (also used by /version).
|
||||||
|
|
||||||
|
The static lines print instantly from stdlib-only probes; everything
|
||||||
|
heavier (upstream SHA in the version line, authoritative install-method
|
||||||
|
detection, the update-status check) is lazy-imported AFTER the first
|
||||||
|
line is already on screen, so perceived latency stays instant while the
|
||||||
|
output carries the full information that used to require the (removed)
|
||||||
|
``hermes version`` subcommand. Every lazy block degrades gracefully —
|
||||||
|
a broken/heavy import can never take the basic version output down.
|
||||||
|
"""
|
||||||
|
# Line 1: registry-owned banner label (includes "· upstream <sha>" for
|
||||||
|
# git installs). banner.py keeps rich/prompt_toolkit lazy, so this
|
||||||
|
# import is light; fall back to the plain label if anything fails.
|
||||||
|
try:
|
||||||
|
from hermes_cli.banner import format_banner_version_label
|
||||||
|
|
||||||
|
print(format_banner_version_label())
|
||||||
|
except Exception:
|
||||||
|
from hermes_cli import __release_date__, __version__
|
||||||
|
|
||||||
|
print(f"Hermes Agent v{__version__} ({__release_date__})")
|
||||||
|
|
||||||
print(f"Hermes Agent v{__version__} ({__release_date__})")
|
|
||||||
print(f"Install directory: {project_root_str()}")
|
print(f"Install directory: {project_root_str()}")
|
||||||
install_method = read_install_method()
|
|
||||||
|
# Install method: authoritative resolver first (code-scoped stamp →
|
||||||
|
# managed → nix → git → pip; also self-heals poisoned shared-home
|
||||||
|
# 'docker' stamps). Fall back to the cheap stdlib stamp probe only if
|
||||||
|
# the resolver import/run fails.
|
||||||
|
try:
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from hermes_cli.config import detect_install_method
|
||||||
|
|
||||||
|
install_method = detect_install_method(Path(project_root_str()))
|
||||||
|
except Exception:
|
||||||
|
install_method = read_install_method()
|
||||||
if install_method:
|
if install_method:
|
||||||
print(f"Install method: {install_method}")
|
print(f"Install method: {install_method}")
|
||||||
|
|
||||||
@@ -193,17 +224,39 @@ def print_fast_version_info() -> None:
|
|||||||
|
|
||||||
openai_version = read_openai_version()
|
openai_version = read_openai_version()
|
||||||
print(f"OpenAI SDK: {openai_version}" if openai_version else "OpenAI SDK: Not installed")
|
print(f"OpenAI SDK: {openai_version}" if openai_version else "OpenAI SDK: Not installed")
|
||||||
print("Run 'hermes version' for update status.")
|
|
||||||
|
if not check_updates:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Update status (synchronous — acceptable since the user asked for
|
||||||
|
# version info). Bounded by check_for_updates' own subprocess/network
|
||||||
|
# timeouts and its 6-hour cache; any failure prints nothing.
|
||||||
|
try:
|
||||||
|
from hermes_cli.banner import UPDATE_AVAILABLE_NO_COUNT, check_for_updates
|
||||||
|
from hermes_cli.config import recommended_update_command
|
||||||
|
|
||||||
|
behind = check_for_updates()
|
||||||
|
if behind == UPDATE_AVAILABLE_NO_COUNT:
|
||||||
|
print(f"Update available — run '{recommended_update_command()}'")
|
||||||
|
elif behind and behind > 0:
|
||||||
|
commits_word = "commit" if behind == 1 else "commits"
|
||||||
|
print(
|
||||||
|
f"Update available: {behind} {commits_word} behind — "
|
||||||
|
f"run '{recommended_update_command()}'"
|
||||||
|
)
|
||||||
|
elif behind == 0:
|
||||||
|
print("Up to date")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def try_fast_version(argv: list[str] | None = None) -> bool:
|
def try_fast_version(argv: list[str] | None = None) -> bool:
|
||||||
"""Handle ``hermes --version`` before the heavy import wall.
|
"""Handle ``hermes --version`` before the heavy import wall.
|
||||||
|
|
||||||
Termux keeps its historical contract (also accepts the ``version``
|
Only ``--version``/``-V`` (the ``version`` subcommand was removed —
|
||||||
subcommand + the HERMES_TERMUX_DISABLE_FAST_CLI escape hatch). Everywhere
|
``--version`` now carries the full output incl. update status), and
|
||||||
else: only ``--version``/``-V`` (the ``version`` subcommand stays on the
|
never when container mode may need to route the command into the
|
||||||
slow path for full output incl. update check), and never when container
|
container. Termux keeps the HERMES_TERMUX_DISABLE_FAST_CLI escape hatch.
|
||||||
mode may need to route the command into the container.
|
|
||||||
"""
|
"""
|
||||||
if argv is None:
|
if argv is None:
|
||||||
argv = sys.argv[1:]
|
argv = sys.argv[1:]
|
||||||
|
|||||||
@@ -567,6 +567,7 @@ class HermesConsoleEngine:
|
|||||||
|
|
||||||
def _register_defaults(self) -> None:
|
def _register_defaults(self) -> None:
|
||||||
self.register(("status",), "status", "Show Hermes component status.", _status)
|
self.register(("status",), "status", "Show Hermes component status.", _status)
|
||||||
|
self.register(("version",), "version", "Show Hermes version information.", _version)
|
||||||
self.register(("doctor",), "doctor", "Run diagnostics without auto-fix.", _doctor)
|
self.register(("doctor",), "doctor", "Run diagnostics without auto-fix.", _doctor)
|
||||||
self.register(("logs",), "logs [name] [-n N]", "Show recent Hermes logs.", _logs)
|
self.register(("logs",), "logs [name] [-n N]", "Show recent Hermes logs.", _logs)
|
||||||
self.register(("sessions", "list"), "sessions list [--limit N]", "List recent sessions.", _sessions_list)
|
self.register(("sessions", "list"), "sessions list [--limit N]", "List recent sessions.", _sessions_list)
|
||||||
@@ -613,13 +614,6 @@ class HermesConsoleEngine:
|
|||||||
"""Register non-admin CLI commands that are safe for Hermes Console."""
|
"""Register non-admin CLI commands that are safe for Hermes Console."""
|
||||||
|
|
||||||
extracted = {
|
extracted = {
|
||||||
"version": (
|
|
||||||
"hermes_cli.subcommands.version",
|
|
||||||
"build_version_parser",
|
|
||||||
"cmd_version",
|
|
||||||
[()],
|
|
||||||
set(),
|
|
||||||
),
|
|
||||||
"dump": (
|
"dump": (
|
||||||
"hermes_cli.subcommands.dump",
|
"hermes_cli.subcommands.dump",
|
||||||
"build_dump_parser",
|
"build_dump_parser",
|
||||||
@@ -1280,6 +1274,13 @@ def _apply_confirmed_defaults(args: argparse.Namespace) -> None:
|
|||||||
setattr(args, "yes", True)
|
setattr(args, "yes", True)
|
||||||
|
|
||||||
|
|
||||||
|
def _version(_engine: HermesConsoleEngine, args: list[str]) -> str:
|
||||||
|
_expect_no_args(args, "version")
|
||||||
|
from hermes_cli._startup_fast import print_fast_version_info
|
||||||
|
|
||||||
|
return _capture_output(lambda: print_fast_version_info(check_updates=True))
|
||||||
|
|
||||||
|
|
||||||
def _status(_engine: HermesConsoleEngine, args: list[str]) -> str:
|
def _status(_engine: HermesConsoleEngine, args: list[str]) -> str:
|
||||||
_expect_no_args(args, "status")
|
_expect_no_args(args, "status")
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
|
|||||||
+12
-60
@@ -34,7 +34,7 @@ Usage:
|
|||||||
hermes honcho identity # Show AI peer identity representation
|
hermes honcho identity # Show AI peer identity representation
|
||||||
hermes honcho identity <file> # Seed AI peer identity from a file (SOUL.md etc.)
|
hermes honcho identity <file> # Seed AI peer identity from a file (SOUL.md etc.)
|
||||||
hermes honcho migrate # Step-by-step migration guide: OpenClaw native → Hermes + Honcho
|
hermes honcho migrate # Step-by-step migration guide: OpenClaw native → Hermes + Honcho
|
||||||
hermes version Show version
|
hermes --version Show version and update status
|
||||||
hermes update Update to latest version
|
hermes update Update to latest version
|
||||||
hermes uninstall Uninstall Hermes Agent
|
hermes uninstall Uninstall Hermes Agent
|
||||||
hermes acp Run as an ACP server for editor integration
|
hermes acp Run as an ACP server for editor integration
|
||||||
@@ -468,7 +468,6 @@ from hermes_cli.subcommands.import_agent import build_import_agent_parser
|
|||||||
from hermes_cli.subcommands.config import build_config_parser
|
from hermes_cli.subcommands.config import build_config_parser
|
||||||
from hermes_cli.subcommands.skin import build_skin_parser
|
from hermes_cli.subcommands.skin import build_skin_parser
|
||||||
from hermes_cli.subcommands.console import build_console_parser
|
from hermes_cli.subcommands.console import build_console_parser
|
||||||
from hermes_cli.subcommands.version import build_version_parser
|
|
||||||
from hermes_cli.subcommands.update import build_update_parser
|
from hermes_cli.subcommands.update import build_update_parser
|
||||||
from hermes_cli.subcommands.uninstall import build_uninstall_parser
|
from hermes_cli.subcommands.uninstall import build_uninstall_parser
|
||||||
from hermes_cli.subcommands.dashboard import build_dashboard_parser
|
from hermes_cli.subcommands.dashboard import build_dashboard_parser
|
||||||
@@ -5700,58 +5699,14 @@ def cmd_import(args):
|
|||||||
|
|
||||||
|
|
||||||
def _print_version_info(*, check_updates: bool = True) -> None:
|
def _print_version_info(*, check_updates: bool = True) -> None:
|
||||||
from hermes_cli.config import detect_install_method
|
# Single source of truth for version output — shared with the
|
||||||
from hermes_cli.slash_exec import CommandContext, execute_command
|
# `hermes --version` pre-import fast path (the `version` subcommand
|
||||||
|
# was consolidated into `--version`).
|
||||||
# Core version line is registry-owned (shared with the gateway /version);
|
_startup_fast.print_fast_version_info(check_updates=check_updates)
|
||||||
# the install/python/SDK detail below is CLI-only decoration.
|
|
||||||
print(execute_command("version", CommandContext(surface="cli")).text)
|
|
||||||
print(f"Install directory: {PROJECT_ROOT}")
|
|
||||||
print(f"Install method: {detect_install_method(PROJECT_ROOT)}")
|
|
||||||
|
|
||||||
# Show Python version
|
|
||||||
print(f"Python: {sys.version.split()[0]}")
|
|
||||||
|
|
||||||
# Check for key dependencies. Use importlib.metadata rather than
|
|
||||||
# ``import openai`` — the SDK drags in ~800ms of pydantic-backed type
|
|
||||||
# modules just to expose ``__version__``. Metadata lookup is ~2ms.
|
|
||||||
try:
|
|
||||||
from importlib.metadata import version as _pkg_version, PackageNotFoundError
|
|
||||||
|
|
||||||
try:
|
|
||||||
print(f"OpenAI SDK: {_pkg_version('openai')}")
|
|
||||||
except PackageNotFoundError:
|
|
||||||
print("OpenAI SDK: Not installed")
|
|
||||||
except ImportError:
|
|
||||||
print("OpenAI SDK: Not installed")
|
|
||||||
|
|
||||||
if not check_updates:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Show update status (synchronous — acceptable since user asked for version info)
|
|
||||||
try:
|
|
||||||
from hermes_cli.banner import UPDATE_AVAILABLE_NO_COUNT, check_for_updates
|
|
||||||
from hermes_cli.config import recommended_update_command
|
|
||||||
|
|
||||||
behind = check_for_updates()
|
|
||||||
if behind == UPDATE_AVAILABLE_NO_COUNT:
|
|
||||||
print(
|
|
||||||
f"Update available — run '{recommended_update_command()}'"
|
|
||||||
)
|
|
||||||
elif behind and behind > 0:
|
|
||||||
commits_word = "commit" if behind == 1 else "commits"
|
|
||||||
print(
|
|
||||||
f"Update available: {behind} {commits_word} behind — "
|
|
||||||
f"run '{recommended_update_command()}'"
|
|
||||||
)
|
|
||||||
elif behind == 0:
|
|
||||||
print("Up to date")
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def cmd_version(args):
|
def cmd_version(args):
|
||||||
"""Show version."""
|
"""Show version (--version/-V flag)."""
|
||||||
_print_version_info(check_updates=True)
|
_print_version_info(check_updates=True)
|
||||||
|
|
||||||
|
|
||||||
@@ -10198,7 +10153,6 @@ def _coalesce_session_name_args(argv: list) -> list:
|
|||||||
"mcp",
|
"mcp",
|
||||||
"sessions",
|
"sessions",
|
||||||
"insights",
|
"insights",
|
||||||
"version",
|
|
||||||
"update",
|
"update",
|
||||||
"uninstall",
|
"uninstall",
|
||||||
"profile",
|
"profile",
|
||||||
@@ -11644,7 +11598,7 @@ _BUILTIN_SUBCOMMANDS = frozenset(
|
|||||||
"resume",
|
"resume",
|
||||||
"send", "sessions", "setup",
|
"send", "sessions", "setup",
|
||||||
"skin", "skills", "slack", "status", "sync", "tools", "uninstall", "update",
|
"skin", "skills", "slack", "status", "sync", "tools", "uninstall", "update",
|
||||||
"version", "webhook", "whatsapp", "whatsapp-cloud", "worktree", "chat", "secrets", "security",
|
"webhook", "whatsapp", "whatsapp-cloud", "worktree", "chat", "secrets", "security",
|
||||||
"verify",
|
"verify",
|
||||||
# Help-ish invocations — plugin commands not being listed in
|
# Help-ish invocations — plugin commands not being listed in
|
||||||
# top-level --help is an acceptable trade-off for skipping an
|
# top-level --help is an acceptable trade-off for skipping an
|
||||||
@@ -12004,7 +11958,7 @@ def _try_termux_fast_cli_launch() -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
if _is_termux_fast_version_argv(argv):
|
if _is_termux_fast_version_argv(argv):
|
||||||
_print_version_info(check_updates=False)
|
_print_version_info(check_updates=True)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
first = _first_positional_argv()
|
first = _first_positional_argv()
|
||||||
@@ -12022,7 +11976,7 @@ def _try_termux_fast_cli_launch() -> bool:
|
|||||||
args = parser.parse_args(_coalesce_session_name_args(argv))
|
args = parser.parse_args(_coalesce_session_name_args(argv))
|
||||||
|
|
||||||
if getattr(args, "version", False):
|
if getattr(args, "version", False):
|
||||||
_print_version_info(check_updates=False)
|
_print_version_info(check_updates=True)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if getattr(args, "oneshot", None):
|
if getattr(args, "oneshot", None):
|
||||||
@@ -12926,7 +12880,7 @@ def main():
|
|||||||
# own argparse tree. No hardcoded plugin commands in main.py.
|
# own argparse tree. No hardcoded plugin commands in main.py.
|
||||||
#
|
#
|
||||||
# Skipped when the invocation is already targeting a known built-in
|
# Skipped when the invocation is already targeting a known built-in
|
||||||
# subcommand — ``hermes --help``, ``hermes version``, ``hermes logs``,
|
# subcommand — ``hermes --help``, ``hermes logs``,
|
||||||
# etc. This avoids eagerly importing every bundled plugin module
|
# etc. This avoids eagerly importing every bundled plugin module
|
||||||
# (google.cloud.pubsub_v1, aiohttp, grpc, PIL …) which costs
|
# (google.cloud.pubsub_v1, aiohttp, grpc, PIL …) which costs
|
||||||
# 500-650ms on typical installs.
|
# 500-650ms on typical installs.
|
||||||
@@ -13801,10 +13755,8 @@ def main():
|
|||||||
# =========================================================================
|
# =========================================================================
|
||||||
build_claw_parser(subparsers, cmd_claw=cmd_claw)
|
build_claw_parser(subparsers, cmd_claw=cmd_claw)
|
||||||
|
|
||||||
# =========================================================================
|
# NOTE: the `hermes version` subcommand was removed — `hermes --version`
|
||||||
# version command (parser built in hermes_cli/subcommands/version.py)
|
# / `-V` now carries the full output including update status.
|
||||||
# =========================================================================
|
|
||||||
build_version_parser(subparsers, cmd_version=cmd_version)
|
|
||||||
|
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
# update command (parser built in hermes_cli/subcommands/update.py)
|
# update command (parser built in hermes_cli/subcommands/update.py)
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
"""``hermes version`` subcommand parser.
|
|
||||||
|
|
||||||
Extracted verbatim from ``hermes_cli/main.py:main()`` (god-file Phase 2).
|
|
||||||
Handler injected to avoid importing ``main``.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import Callable
|
|
||||||
|
|
||||||
|
|
||||||
def build_version_parser(subparsers, *, cmd_version: Callable) -> None:
|
|
||||||
"""Attach the ``version`` subcommand to ``subparsers``."""
|
|
||||||
# =========================================================================
|
|
||||||
# version command
|
|
||||||
# =========================================================================
|
|
||||||
version_parser = subparsers.add_parser("version", help="Show version information")
|
|
||||||
version_parser.set_defaults(func=cmd_version)
|
|
||||||
+1
-1
@@ -571,7 +571,7 @@ json.dump(sorted(leaf_paths(DEFAULT_CONFIG)), sys.stdout, indent=2)
|
|||||||
echo "PASS: All binaries present"
|
echo "PASS: All binaries present"
|
||||||
|
|
||||||
echo "=== Checking version ==="
|
echo "=== Checking version ==="
|
||||||
${hermes-agent}/bin/hermes version 2>&1 | grep -qi "hermes" || (echo "FAIL: version check"; exit 1)
|
${hermes-agent}/bin/hermes --version 2>&1 | grep -qi "hermes" || (echo "FAIL: version check"; exit 1)
|
||||||
echo "PASS: Version check"
|
echo "PASS: Version check"
|
||||||
|
|
||||||
echo "=== All checks passed ==="
|
echo "=== All checks passed ==="
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
``hermes_cli.main`` skips eager plugin discovery at argparse-setup time
|
``hermes_cli.main`` skips eager plugin discovery at argparse-setup time
|
||||||
when the invocation is clearly targeting a known built-in subcommand.
|
when the invocation is clearly targeting a known built-in subcommand.
|
||||||
This saves 500-650ms on ``hermes --help``, ``hermes version``,
|
This saves 500-650ms on ``hermes --help``, ``hermes --version``,
|
||||||
``hermes logs``, etc., by not importing ``google.cloud.pubsub_v1``,
|
``hermes logs``, etc., by not importing ``google.cloud.pubsub_v1``,
|
||||||
``aiohttp``, ``grpc``, and friends.
|
``aiohttp``, ``grpc``, and friends.
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ from hermes_cli.subcommands.slack import build_slack_parser
|
|||||||
from hermes_cli.subcommands.status import build_status_parser
|
from hermes_cli.subcommands.status import build_status_parser
|
||||||
from hermes_cli.subcommands.uninstall import build_uninstall_parser
|
from hermes_cli.subcommands.uninstall import build_uninstall_parser
|
||||||
from hermes_cli.subcommands.update import build_update_parser
|
from hermes_cli.subcommands.update import build_update_parser
|
||||||
from hermes_cli.subcommands.version import build_version_parser
|
|
||||||
from hermes_cli.subcommands.webhook import build_webhook_parser
|
from hermes_cli.subcommands.webhook import build_webhook_parser
|
||||||
from hermes_cli.subcommands.whatsapp import build_whatsapp_parser
|
from hermes_cli.subcommands.whatsapp import build_whatsapp_parser
|
||||||
|
|
||||||
@@ -67,7 +66,6 @@ SINGLE_HANDLER_CASES = [
|
|||||||
("backup", build_backup_parser, "cmd_backup", ["backup"]),
|
("backup", build_backup_parser, "cmd_backup", ["backup"]),
|
||||||
("import", build_import_cmd_parser, "cmd_import", ["import", "/tmp/x.zip"]),
|
("import", build_import_cmd_parser, "cmd_import", ["import", "/tmp/x.zip"]),
|
||||||
("config", build_config_parser, "cmd_config", ["config"]),
|
("config", build_config_parser, "cmd_config", ["config"]),
|
||||||
("version", build_version_parser, "cmd_version", ["version"]),
|
|
||||||
("update", build_update_parser, "cmd_update", ["update"]),
|
("update", build_update_parser, "cmd_update", ["update"]),
|
||||||
("uninstall", build_uninstall_parser, "cmd_uninstall", ["uninstall"]),
|
("uninstall", build_uninstall_parser, "cmd_uninstall", ["uninstall"]),
|
||||||
("gui", build_gui_parser, "cmd_gui", ["gui"]),
|
("gui", build_gui_parser, "cmd_gui", ["gui"]),
|
||||||
|
|||||||
@@ -287,7 +287,7 @@ When you ask Hermes to review a PR in a repository that has `.agents/checks/`, t
|
|||||||
## Reporting Issues
|
## Reporting Issues
|
||||||
|
|
||||||
- Use [GitHub Issues](https://github.com/NousResearch/hermes-agent/issues)
|
- Use [GitHub Issues](https://github.com/NousResearch/hermes-agent/issues)
|
||||||
- Include: OS, Python version, Hermes version (`hermes version`), full error traceback
|
- Include: OS, Python version, Hermes version (`hermes --version`), full error traceback
|
||||||
- Include steps to reproduce
|
- Include steps to reproduce
|
||||||
- Check existing issues before creating duplicates
|
- Check existing issues before creating duplicates
|
||||||
- For security vulnerabilities, please report privately
|
- For security vulnerabilities, please report privately
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ Setting `addToSystemPackages = true` does two things: puts the `hermes` CLI on y
|
|||||||
:::info
|
:::info
|
||||||
When `container.enable = true` and `addToSystemPackages = true`, **every** `hermes` command on the host automatically routes into the managed container. This means your interactive CLI session runs inside the same environment as the gateway service — with access to all container-installed packages and tools.
|
When `container.enable = true` and `addToSystemPackages = true`, **every** `hermes` command on the host automatically routes into the managed container. This means your interactive CLI session runs inside the same environment as the gateway service — with access to all container-installed packages and tools.
|
||||||
|
|
||||||
- The routing is transparent: `hermes chat`, `hermes sessions list`, `hermes version`, etc. all exec into the container under the hood
|
- The routing is transparent: `hermes chat`, `hermes sessions list`, `hermes --version`, etc. all exec into the container under the hood
|
||||||
- All CLI flags are forwarded as-is
|
- All CLI flags are forwarded as-is
|
||||||
- If the container isn't running, the CLI retries briefly (5s with a spinner for interactive use, 10s silently for scripts) then fails with a clear error — no silent fallback
|
- If the container isn't running, the CLI retries briefly (5s with a spinner for interactive use, 10s silently for scripts) then fails with a clear error — no silent fallback
|
||||||
- For developers working on the hermes codebase, set `HERMES_DEV=1` to bypass container routing and run the local checkout directly
|
- For developers working on the hermes codebase, set `HERMES_DEV=1` to bypass container routing and run the local checkout directly
|
||||||
@@ -191,7 +191,7 @@ systemctl status hermes-agent
|
|||||||
journalctl -u hermes-agent -f
|
journalctl -u hermes-agent -f
|
||||||
|
|
||||||
# If addToSystemPackages is true, test the CLI
|
# If addToSystemPackages is true, test the CLI
|
||||||
hermes version
|
hermes --version
|
||||||
hermes config # shows the generated config
|
hermes config # shows the generated config
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -681,7 +681,7 @@ journalctl --user -u hermes-agent -f
|
|||||||
launchctl list | grep hermes
|
launchctl list | grep hermes
|
||||||
tail -f ~/Library/Logs/hermes-agent.log
|
tail -f ~/Library/Logs/hermes-agent.log
|
||||||
|
|
||||||
hermes version
|
hermes --version
|
||||||
hermes config # shows the configuration that Nix wrote
|
hermes config # shows the configuration that Nix wrote
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -937,7 +937,7 @@ nix build .#checks.x86_64-linux.config-roundtrip # merge script preserves use
|
|||||||
|
|
||||||
| Check | What it tests |
|
| Check | What it tests |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `package-contents` | `hermes` and `hermes-agent` binaries exist and `hermes version` runs |
|
| `package-contents` | `hermes` and `hermes-agent` binaries exist and `hermes --version` runs |
|
||||||
| `entry-points-sync` | Every `[project.scripts]` entry in `pyproject.toml` has a wrapped binary in the Nix package |
|
| `entry-points-sync` | Every `[project.scripts]` entry in `pyproject.toml` has a wrapped binary in the Nix package |
|
||||||
| `cli-commands` | `hermes --help` exposes `gateway` and `config` subcommands |
|
| `cli-commands` | `hermes --help` exposes `gateway` and `config` subcommands |
|
||||||
| `managed-guard` | `HERMES_MANAGED=true hermes config set ...` prints the NixOS error |
|
| `managed-guard` | `HERMES_MANAGED=true hermes config set ...` prints the NixOS error |
|
||||||
@@ -1180,7 +1180,7 @@ nix-store --query --roots $(docker exec hermes-agent readlink /data/current-pack
|
|||||||
| `Cannot save configuration: managed by NixOS` | CLI guards active | Edit `configuration.nix` and `nixos-rebuild switch` |
|
| `Cannot save configuration: managed by NixOS` | CLI guards active | Edit `configuration.nix` and `nixos-rebuild switch` |
|
||||||
| `No adapter available for discord` (or telegram/slack) | Messaging deps missing from the sealed Nix venv | Install `#messaging` variant: `nix profile install ...#messaging`. For NixOS module: `extraDependencyGroups = [ "messaging" ]`. Check `journalctl -u hermes-agent` for `FeatureUnavailable` or `requirements not met` for the underlying error. |
|
| `No adapter available for discord` (or telegram/slack) | Messaging deps missing from the sealed Nix venv | Install `#messaging` variant: `nix profile install ...#messaging`. For NixOS module: `extraDependencyGroups = [ "messaging" ]`. Check `journalctl -u hermes-agent` for `FeatureUnavailable` or `requirements not met` for the underlying error. |
|
||||||
| Container recreated unexpectedly | `extraVolumes`, `extraOptions`, or `image` changed | Expected — writable layer resets. Reinstall packages or use a custom image |
|
| Container recreated unexpectedly | `extraVolumes`, `extraOptions`, or `image` changed | Expected — writable layer resets. Reinstall packages or use a custom image |
|
||||||
| `hermes version` shows old version | Container not restarted | `systemctl restart hermes-agent` |
|
| `hermes --version` shows old version | Container not restarted | `systemctl restart hermes-agent` |
|
||||||
| Permission denied on `/var/lib/hermes` | State dir is `0750 hermes:hermes` | Use `docker exec` or `sudo -u hermes` |
|
| Permission denied on `/var/lib/hermes` | State dir is `0750 hermes:hermes` | Use `docker exec` or `sudo -u hermes` |
|
||||||
| `nix-collect-garbage` removed hermes | GC root missing | Restart the service (preStart recreates the GC root) |
|
| `nix-collect-garbage` removed hermes | GC root missing | Restart the service (preStart recreates the GC root) |
|
||||||
| `no container with name or ID "hermes-agent"` (Podman) | Podman rootful container not visible to regular user | Add passwordless sudo for podman (see [Container Mode](#container-mode) section) |
|
| `no container with name or ID "hermes-agent"` (Podman) | Podman rootful container not visible to regular user | Add passwordless sudo for podman (see [Container Mode](#container-mode) section) |
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ ln -sf "$PWD/venv/bin/hermes" "$PREFIX/bin/hermes"
|
|||||||
### 6. Verify the install
|
### 6. Verify the install
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
hermes version
|
hermes --version
|
||||||
hermes doctor
|
hermes doctor
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ You no longer need to wrap `hermes update` in `screen` or `tmux` to survive a te
|
|||||||
### Checking your current version
|
### Checking your current version
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
hermes version
|
hermes --version
|
||||||
```
|
```
|
||||||
|
|
||||||
Compare against the latest release at the [GitHub releases page](https://github.com/NousResearch/hermes-agent/releases).
|
Compare against the latest release at the [GitHub releases page](https://github.com/NousResearch/hermes-agent/releases).
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ hermes [global-options] <command> [subcommand/options]
|
|||||||
| `hermes desktop` (alias `gui`) | Build and launch the native Electron desktop app. |
|
| `hermes desktop` (alias `gui`) | Build and launch the native Electron desktop app. |
|
||||||
| `hermes profile` | Manage profiles — multiple isolated Hermes instances. |
|
| `hermes profile` | Manage profiles — multiple isolated Hermes instances. |
|
||||||
| `hermes completion` | Print shell completion scripts (bash/zsh/fish). |
|
| `hermes completion` | Print shell completion scripts (bash/zsh/fish). |
|
||||||
| `hermes version` | Show version information. |
|
| `hermes --version` | Show version information. |
|
||||||
| `hermes update` | Pull latest code and reinstall dependencies. `--check` previews without installing; `--backup` takes a pre-pull `HERMES_HOME` snapshot. |
|
| `hermes update` | Pull latest code and reinstall dependencies. `--check` previews without installing; `--backup` takes a pre-pull `HERMES_HOME` snapshot. |
|
||||||
| `hermes uninstall` | Remove Hermes from the system. |
|
| `hermes uninstall` | Remove Hermes from the system. |
|
||||||
|
|
||||||
@@ -1758,7 +1758,7 @@ Additional behavior:
|
|||||||
|
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
|---------|-------------|
|
|---------|-------------|
|
||||||
| `hermes version` | Print version information. |
|
| `hermes --version` | Print version information. |
|
||||||
| `hermes update` | Pull latest changes and reinstall dependencies. |
|
| `hermes update` | Pull latest changes and reinstall dependencies. |
|
||||||
|
|
||||||
| `hermes uninstall [--full] [--gui] [--dry-run] [--yes]` | Remove Hermes, optionally deleting all config/data. `--gui` removes only the desktop Chat GUI, leaving the agent intact; `--full` also deletes config/data; `--dry-run` prints what would be removed without changing anything; `--yes` skips prompts. |
|
| `hermes uninstall [--full] [--gui] [--dry-run] [--yes]` | Remove Hermes, optionally deleting all config/data. `--gui` removes only the desktop Chat GUI, leaving the agent intact; `--full` also deletes config/data; `--dry-run` prints what would be removed without changing anything; `--yes` skips prompts. |
|
||||||
|
|||||||
+1
-1
@@ -237,7 +237,7 @@ fix(security): prevent shell injection in sudo password piping
|
|||||||
## 报告问题
|
## 报告问题
|
||||||
|
|
||||||
- 使用 [GitHub Issues](https://github.com/NousResearch/hermes-agent/issues)
|
- 使用 [GitHub Issues](https://github.com/NousResearch/hermes-agent/issues)
|
||||||
- 请包含:操作系统、Python 版本、Hermes 版本(`hermes version`)、完整错误堆栈
|
- 请包含:操作系统、Python 版本、Hermes 版本(`hermes --version`)、完整错误堆栈
|
||||||
- 包含复现步骤
|
- 包含复现步骤
|
||||||
- 创建前请检查是否已有重复 issue
|
- 创建前请检查是否已有重复 issue
|
||||||
- 安全漏洞请私下报告
|
- 安全漏洞请私下报告
|
||||||
|
|||||||
+4
-4
@@ -127,7 +127,7 @@ services.hermes-agent.environmentFiles = [ "/var/lib/hermes/env" ];
|
|||||||
:::info
|
:::info
|
||||||
当 `container.enable = true` 且 `addToSystemPackages = true` 时,主机上的**所有** `hermes` 命令都会自动路由到托管容器中执行。这意味着你的交互式 CLI 会话在与 gateway 服务相同的环境中运行——可以访问所有容器内安装的包和工具。
|
当 `container.enable = true` 且 `addToSystemPackages = true` 时,主机上的**所有** `hermes` 命令都会自动路由到托管容器中执行。这意味着你的交互式 CLI 会话在与 gateway 服务相同的环境中运行——可以访问所有容器内安装的包和工具。
|
||||||
|
|
||||||
- 路由是透明的:`hermes chat`、`hermes sessions list`、`hermes version` 等命令都会在底层 exec 进容器
|
- 路由是透明的:`hermes chat`、`hermes sessions list`、`hermes --version` 等命令都会在底层 exec 进容器
|
||||||
- 所有 CLI 参数原样转发
|
- 所有 CLI 参数原样转发
|
||||||
- 如果容器未运行,CLI 会短暂重试(交互式使用时显示 5 秒 spinner,脚本中静默等待 10 秒),然后以明确的错误退出——不会静默回退
|
- 如果容器未运行,CLI 会短暂重试(交互式使用时显示 5 秒 spinner,脚本中静默等待 10 秒),然后以明确的错误退出——不会静默回退
|
||||||
- 对于在 hermes 代码库上工作的开发者,设置 `HERMES_DEV=1` 可绕过容器路由,直接运行本地检出版本
|
- 对于在 hermes 代码库上工作的开发者,设置 `HERMES_DEV=1` 可绕过容器路由,直接运行本地检出版本
|
||||||
@@ -171,7 +171,7 @@ systemctl status hermes-agent
|
|||||||
journalctl -u hermes-agent -f
|
journalctl -u hermes-agent -f
|
||||||
|
|
||||||
# 如果 addToSystemPackages 为 true,测试 CLI
|
# 如果 addToSystemPackages 为 true,测试 CLI
|
||||||
hermes version
|
hermes --version
|
||||||
hermes config # 显示生成的配置
|
hermes config # 显示生成的配置
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -763,7 +763,7 @@ nix build .#checks.x86_64-linux.config-roundtrip # 合并脚本保留用户
|
|||||||
|
|
||||||
| 检查 | 测试内容 |
|
| 检查 | 测试内容 |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `package-contents` | `hermes` 和 `hermes-agent` 二进制文件存在且 `hermes version` 可运行 |
|
| `package-contents` | `hermes` 和 `hermes-agent` 二进制文件存在且 `hermes --version` 可运行 |
|
||||||
| `entry-points-sync` | `pyproject.toml` 中 `[project.scripts]` 的每个条目在 Nix 包中都有对应的封装二进制文件 |
|
| `entry-points-sync` | `pyproject.toml` 中 `[project.scripts]` 的每个条目在 Nix 包中都有对应的封装二进制文件 |
|
||||||
| `cli-commands` | `hermes --help` 暴露 `gateway` 和 `config` 子命令 |
|
| `cli-commands` | `hermes --help` 暴露 `gateway` 和 `config` 子命令 |
|
||||||
| `managed-guard` | `HERMES_MANAGED=true hermes config set ...` 打印 NixOS 错误 |
|
| `managed-guard` | `HERMES_MANAGED=true hermes config set ...` 打印 NixOS 错误 |
|
||||||
@@ -967,7 +967,7 @@ nix-store --query --roots $(docker exec hermes-agent readlink /data/current-pack
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `Cannot save configuration: managed by NixOS` | CLI 守卫已激活 | 编辑 `configuration.nix` 并执行 `nixos-rebuild switch` |
|
| `Cannot save configuration: managed by NixOS` | CLI 守卫已激活 | 编辑 `configuration.nix` 并执行 `nixos-rebuild switch` |
|
||||||
| 容器意外重建 | `extraVolumes`、`extraOptions` 或 `image` 发生变更 | 预期行为——可写层重置。重新安装包或使用自定义镜像 |
|
| 容器意外重建 | `extraVolumes`、`extraOptions` 或 `image` 发生变更 | 预期行为——可写层重置。重新安装包或使用自定义镜像 |
|
||||||
| `hermes version` 显示旧版本 | 容器未重启 | `systemctl restart hermes-agent` |
|
| `hermes --version` 显示旧版本 | 容器未重启 | `systemctl restart hermes-agent` |
|
||||||
| `/var/lib/hermes` 权限拒绝 | 状态目录为 `0750 hermes:hermes` | 使用 `docker exec` 或 `sudo -u hermes` |
|
| `/var/lib/hermes` 权限拒绝 | 状态目录为 `0750 hermes:hermes` | 使用 `docker exec` 或 `sudo -u hermes` |
|
||||||
| `nix-collect-garbage` 删除了 hermes | GC root 缺失 | 重启服务(preStart 会重新创建 GC root) |
|
| `nix-collect-garbage` 删除了 hermes | GC root 缺失 | 重启服务(preStart 会重新创建 GC root) |
|
||||||
| `no container with name or ID "hermes-agent"`(Podman) | Podman rootful 容器对普通用户不可见 | 为 podman 添加免密 sudo(参见[容器模式](#container-mode)章节) |
|
| `no container with name or ID "hermes-agent"`(Podman) | Podman rootful 容器对普通用户不可见 | 为 podman 添加免密 sudo(参见[容器模式](#container-mode)章节) |
|
||||||
|
|||||||
+1
-1
@@ -118,7 +118,7 @@ ln -sf "$PWD/venv/bin/hermes" "$PREFIX/bin/hermes"
|
|||||||
### 6. 验证安装
|
### 6. 验证安装
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
hermes version
|
hermes --version
|
||||||
hermes doctor
|
hermes doctor
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -120,7 +120,7 @@ tail -f ~/.hermes/logs/update.log
|
|||||||
### 查看当前版本
|
### 查看当前版本
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
hermes version
|
hermes --version
|
||||||
```
|
```
|
||||||
|
|
||||||
与 [GitHub releases 页面](https://github.com/NousResearch/hermes-agent/releases) 上的最新版本进行比较。
|
与 [GitHub releases 页面](https://github.com/NousResearch/hermes-agent/releases) 上的最新版本进行比较。
|
||||||
|
|||||||
+2
-2
@@ -78,7 +78,7 @@ hermes [global-options] <command> [subcommand/options]
|
|||||||
| `hermes dashboard` | 启动用于管理配置、API 密钥和会话的 Web 控制台。 |
|
| `hermes dashboard` | 启动用于管理配置、API 密钥和会话的 Web 控制台。 |
|
||||||
| `hermes profile` | 管理 profile——多个隔离的 Hermes 实例。 |
|
| `hermes profile` | 管理 profile——多个隔离的 Hermes 实例。 |
|
||||||
| `hermes completion` | 打印 shell 补全脚本(bash/zsh/fish)。 |
|
| `hermes completion` | 打印 shell 补全脚本(bash/zsh/fish)。 |
|
||||||
| `hermes version` | 显示版本信息。 |
|
| `hermes --version` | 显示版本信息。 |
|
||||||
| `hermes update` | 拉取最新代码并重新安装依赖。`--check` 预览而不安装;`--backup` 在拉取前对 `HERMES_HOME` 进行快照。 |
|
| `hermes update` | 拉取最新代码并重新安装依赖。`--check` 预览而不安装;`--backup` 在拉取前对 `HERMES_HOME` 进行快照。 |
|
||||||
| `hermes uninstall` | 从系统中删除 Hermes。 |
|
| `hermes uninstall` | 从系统中删除 Hermes。 |
|
||||||
|
|
||||||
@@ -1246,7 +1246,7 @@ hermes update [--check] [--backup] [--restart-gateway]
|
|||||||
|
|
||||||
| 命令 | 说明 |
|
| 命令 | 说明 |
|
||||||
|---------|-------------|
|
|---------|-------------|
|
||||||
| `hermes version` | 打印版本信息。 |
|
| `hermes --version` | 打印版本信息。 |
|
||||||
| `hermes update` | 拉取最新变更并重新安装依赖。 |
|
| `hermes update` | 拉取最新变更并重新安装依赖。 |
|
||||||
| `hermes uninstall [--full] [--yes]` | 删除 Hermes,可选择删除所有 config/数据。 |
|
| `hermes uninstall [--full] [--yes]` | 删除 Hermes,可选择删除所有 config/数据。 |
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user