fix(cli): guard legacy hermes-agent entrypoint

This commit is contained in:
luyifan
2026-06-29 13:22:25 +08:00
committed by Teknium
parent 2c948e6aa2
commit f80977c0bd
3 changed files with 175 additions and 3 deletions

View File

@@ -430,7 +430,7 @@ build-backend = "setuptools.build_meta"
[project.scripts]
hermes = "hermes_cli.main:main"
hermes-agent = "run_agent:main"
hermes-agent = "run_agent:legacy_cli_main"
hermes-acp = "acp_adapter.entry:main"
[tool.uv]

View File

@@ -12,6 +12,7 @@ try:
except ModuleNotFoundError:
pass # partial `hermes update` — only skips the Windows UTF-8 stdio setup
import argparse
import json
import logging
logger = logging.getLogger(__name__)
@@ -1487,6 +1488,95 @@ def _save_sample_trajectory(agent: "AIAgent", result: dict, user_query: str, mod
print(f"\n⚠️ Failed to save sample: {e}")
def _build_legacy_cli_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
prog="hermes-agent",
description=(
"Legacy Hermes Agent runner. For the primary CLI, use the "
"`hermes` command."
),
add_help=False,
)
parser.add_argument(
"prompt",
nargs="*",
help="Natural language query to run. Prefer --query for scripted use.",
)
parser.add_argument("-h", "--help", action="store_true", dest="show_help")
parser.add_argument("--version", "-V", action="store_true")
parser.add_argument("--query", "-q")
parser.add_argument("--model", default="")
parser.add_argument("--api-key", "--api_key", dest="api_key")
parser.add_argument("--base-url", "--base_url", dest="base_url", default="")
parser.add_argument("--max-turns", "--max_turns", dest="max_turns", type=int, default=10)
parser.add_argument("--enabled-toolsets", "--enabled_toolsets", dest="enabled_toolsets")
parser.add_argument("--disabled-toolsets", "--disabled_toolsets", dest="disabled_toolsets")
parser.add_argument("--list-tools", "--list_tools", dest="list_tools", action="store_true")
parser.add_argument(
"--save-trajectories",
"--save_trajectories",
dest="save_trajectories",
action="store_true",
)
parser.add_argument("--save-sample", "--save_sample", dest="save_sample", action="store_true")
parser.add_argument("--verbose", action="store_true")
parser.add_argument(
"--log-prefix-chars",
"--log_prefix_chars",
dest="log_prefix_chars",
type=int,
default=20,
)
return parser
def legacy_cli_main(argv: Optional[List[str]] = None) -> int:
"""Console-script wrapper for the legacy ``hermes-agent`` entry point.
The packaged console script calls the target function with no arguments.
Pointing it at ``main`` therefore used to start a live model request before
flags like ``--version`` or ``--help`` could be parsed.
"""
parser = _build_legacy_cli_parser()
args = parser.parse_args(argv)
if args.version:
from hermes_cli import __release_date__, __version__
print(f"Hermes Agent v{__version__} ({__release_date__})")
return 0
if args.show_help:
parser.print_help()
return 0
positional_query = " ".join(args.prompt).strip() if args.prompt else None
if args.query and positional_query:
parser.error("pass the query either positionally or via --query, not both")
query = args.query or positional_query
if not query and not args.list_tools:
parser.print_help()
print("\nNo query provided; pass --query to run the legacy agent entry point.")
return 0
main(
query=query,
model=args.model,
api_key=args.api_key,
base_url=args.base_url,
max_turns=args.max_turns,
enabled_toolsets=args.enabled_toolsets,
disabled_toolsets=args.disabled_toolsets,
list_tools=args.list_tools,
save_trajectories=args.save_trajectories,
save_sample=args.save_sample,
verbose=args.verbose,
log_prefix_chars=args.log_prefix_chars,
)
return 0
def main(
query: str = None, model: str = "", api_key: str = None, base_url: str = "", max_turns: int = 10,
enabled_toolsets: str = None, disabled_toolsets: str = None, list_tools: bool = False,
@@ -1557,8 +1647,7 @@ def main(
if __name__ == "__main__":
import fire
fire.Fire(main)
raise SystemExit(legacy_cli_main())
# ---- BEGIN PLUGIN-COMPAT (revert-scheduled; see COMPAT_MANIFEST.md) ----

View File

@@ -0,0 +1,83 @@
from __future__ import annotations
import tomllib
from pathlib import Path
import run_agent
def _raise_if_agent_starts(**_kwargs):
raise AssertionError("legacy metadata commands must not start the agent")
def test_legacy_cli_version_does_not_start_agent(monkeypatch, capsys):
monkeypatch.setattr(run_agent, "main", _raise_if_agent_starts)
assert run_agent.legacy_cli_main(["--version"]) == 0
output = capsys.readouterr().out
assert "Hermes Agent v" in output
def test_legacy_cli_help_does_not_start_agent(monkeypatch, capsys):
monkeypatch.setattr(run_agent, "main", _raise_if_agent_starts)
assert run_agent.legacy_cli_main(["--help"]) == 0
output = capsys.readouterr().out
assert "usage: hermes-agent" in output
def test_legacy_cli_no_args_is_side_effect_free(monkeypatch, capsys):
monkeypatch.setattr(run_agent, "main", _raise_if_agent_starts)
assert run_agent.legacy_cli_main([]) == 0
output = capsys.readouterr().out
assert "usage: hermes-agent" in output
assert "No query provided" in output
def test_legacy_cli_query_dispatches_to_existing_runner(monkeypatch):
calls = {}
def fake_main(**kwargs):
calls.update(kwargs)
monkeypatch.setattr(run_agent, "main", fake_main)
assert run_agent.legacy_cli_main(
[
"--query",
"hello from the legacy entry point",
"--model",
"provider/model",
"--max-turns",
"3",
"--enabled-toolsets",
"web,terminal",
"--verbose",
]
) == 0
assert calls == {
"query": "hello from the legacy entry point",
"model": "provider/model",
"api_key": None,
"base_url": "",
"max_turns": 3,
"enabled_toolsets": "web,terminal",
"disabled_toolsets": None,
"list_tools": False,
"save_trajectories": False,
"save_sample": False,
"verbose": True,
"log_prefix_chars": 20,
}
def test_packaged_hermes_agent_script_uses_safe_wrapper():
repo_root = Path(__file__).resolve().parents[2]
pyproject = tomllib.loads((repo_root / "pyproject.toml").read_text(encoding="utf-8"))
assert pyproject["project"]["scripts"]["hermes-agent"] == "run_agent:legacy_cli_main"