fix(cli): /config displays the live agent credential, not the env-var constructor seed
This commit is contained in:
@@ -9246,10 +9246,22 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin):
|
||||
# ``self.api_key`` may be a callable (Azure Foundry Entra ID bearer
|
||||
# provider). Never invoke it; just identify the auth surface.
|
||||
from agent.azure_identity_adapter import is_token_provider
|
||||
if is_token_provider(self.api_key):
|
||||
|
||||
# Prefer the LIVE agent's credential when one exists: HermesCLI's
|
||||
# constructor seeds self.api_key from OPENAI/OPENROUTER env vars
|
||||
# before provider resolution runs, so on non-OpenAI providers (Nous,
|
||||
# Anthropic, ...) the constructor value is a different vendor's key
|
||||
# than the one actually authenticating requests. /config displaying
|
||||
# an sk-proj-... OpenAI key next to a Nous base URL was the visible
|
||||
# symptom (full-surface CLI QA sweep, Aug 2026).
|
||||
display_key = self.api_key
|
||||
agent = getattr(self, "agent", None)
|
||||
if agent is not None and getattr(agent, "api_key", None):
|
||||
display_key = agent.api_key
|
||||
if is_token_provider(display_key):
|
||||
api_key_display = "Microsoft Entra ID"
|
||||
elif isinstance(self.api_key, str) and len(self.api_key) > 12:
|
||||
api_key_display = f"{self.api_key[:8]}...{self.api_key[-4:]}"
|
||||
elif isinstance(display_key, str) and len(display_key) > 12:
|
||||
api_key_display = f"{display_key[:8]}...{display_key[-4:]}"
|
||||
else:
|
||||
api_key_display = "Not set!"
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
"""Regression test: /config must show the LIVE agent credential.
|
||||
|
||||
HermesCLI.__init__ seeds ``self.api_key`` from OPENAI_API_KEY /
|
||||
OPENROUTER_API_KEY env vars before provider resolution runs. On any
|
||||
non-OpenAI provider (Nous, Anthropic, ...) the constructor value is a
|
||||
different vendor's key than the one actually used for requests, so
|
||||
``/config`` displayed e.g. an ``sk-proj-...`` OpenAI key next to a Nous
|
||||
base URL. ``show_config`` must prefer ``self.agent.api_key`` when an
|
||||
agent exists.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from types import SimpleNamespace
|
||||
|
||||
from cli import HermesCLI
|
||||
|
||||
|
||||
def _run_show_config(stand_in, capsys):
|
||||
HermesCLI.show_config(stand_in)
|
||||
return capsys.readouterr().out
|
||||
|
||||
|
||||
def _make_stand_in(cli_key, agent_key):
|
||||
agent = SimpleNamespace(api_key=agent_key) if agent_key is not None else None
|
||||
return SimpleNamespace(
|
||||
api_key=cli_key,
|
||||
agent=agent,
|
||||
model="test/model",
|
||||
base_url="https://example.invalid/v1",
|
||||
enabled_toolsets=[],
|
||||
max_turns=5,
|
||||
verbose=False,
|
||||
session_start=datetime(2026, 8, 19, 12, 0, 0),
|
||||
)
|
||||
|
||||
|
||||
class TestShowConfigCredentialSource:
|
||||
def test_prefers_live_agent_key(self, capsys):
|
||||
out = _run_show_config(
|
||||
_make_stand_in(cli_key="sk-proj-WRONGVENDORKEY1234", agent_key="nous-REALKEY-abcdef9876"),
|
||||
capsys,
|
||||
)
|
||||
assert "nous-REA" in out
|
||||
assert "sk-proj-" not in out
|
||||
|
||||
def test_falls_back_to_cli_key_without_agent(self, capsys):
|
||||
out = _run_show_config(
|
||||
_make_stand_in(cli_key="sk-proj-CONSTRUCTORKEY5678", agent_key=None),
|
||||
capsys,
|
||||
)
|
||||
assert "sk-proj-" in out
|
||||
@@ -302,8 +302,8 @@ class TestInlinedDisplayMasks:
|
||||
from pathlib import Path
|
||||
src = (Path(__file__).resolve().parent.parent.parent
|
||||
/ "cli.py").read_text()
|
||||
assert "is_token_provider(self.api_key)" in src, (
|
||||
"cli.HermesCLI.show_config must guard self.api_key via "
|
||||
assert "is_token_provider(display_key)" in src, (
|
||||
"cli.HermesCLI.show_config must guard the displayed key via "
|
||||
"is_token_provider so callable Entra ID providers don't "
|
||||
"crash /config."
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user