fix(dashboard): Anthropic API Key entry checks ANTHROPIC_API_KEY, not Claude Code creds; hide deprecated tool-progress env vars (#44286)

Two dashboard fixes:

1. The 'Anthropic API Key' OAuth catalog entry's status fn read
   ~/.claude/.credentials.json (which has its own dedicated claude-code
   entry) and never checked ANTHROPIC_API_KEY at all. It now checks the
   Hermes PKCE file, then the registry env-var order (ANTHROPIC_API_KEY
   -> ANTHROPIC_TOKEN -> CLAUDE_CODE_OAUTH_TOKEN) via get_env_value, so
   keys from .env, the shell, or Bitwarden (injected into the process
   env by load_hermes_dotenv) are all reported, with a '(from Bitwarden)'
   source suffix when applicable.

2. Deprecated HERMES_TOOL_PROGRESS / HERMES_TOOL_PROGRESS_MODE removed
   from OPTIONAL_ENV_VARS so the keys page and setup checklists stop
   offering them. Moved to _EXTRA_ENV_KEYS so .env sanitization and
   reload_env still recognize them for existing users (gateway back-compat
   fallback unchanged).
This commit is contained in:
Teknium 2026-06-11 07:18:15 -07:00 committed by GitHub
parent e24c935cf3
commit 9c051f57c3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 46 additions and 42 deletions

View file

@ -270,6 +270,11 @@ _EXTRA_ENV_KEYS = frozenset({
"IRC_SERVER", "IRC_PORT", "IRC_NICKNAME", "IRC_CHANNEL",
"IRC_USE_TLS", "IRC_SERVER_PASSWORD", "IRC_NICKSERV_PASSWORD",
"TERMINAL_ENV", "TERMINAL_SSH_KEY", "TERMINAL_SSH_PORT",
# Deprecated tool-progress env vars — replaced by display.tool_progress in
# config.yaml. Kept known here so .env sanitization/reload still handle
# them for existing users (gateway reads them as a back-compat fallback),
# without surfacing them in user-facing OPTIONAL_ENV_VARS listings.
"HERMES_TOOL_PROGRESS", "HERMES_TOOL_PROGRESS_MODE",
"WHATSAPP_MODE", "WHATSAPP_ENABLED",
"MATTERMOST_HOME_CHANNEL", "MATTERMOST_HOME_CHANNEL_NAME", "MATTERMOST_REPLY_MODE",
"MATRIX_PASSWORD", "MATRIX_ENCRYPTION", "MATRIX_DEVICE_ID", "MATRIX_HOME_ROOM",
@ -3557,21 +3562,11 @@ OPTIONAL_ENV_VARS = {
},
# HERMES_TOOL_PROGRESS and HERMES_TOOL_PROGRESS_MODE are deprecated —
# now configured via display.tool_progress in config.yaml (off|new|all|verbose).
# Gateway falls back to these env vars for backward compatibility.
"HERMES_TOOL_PROGRESS": {
"description": "(deprecated) Use display.tool_progress in config.yaml instead",
"prompt": "Tool progress (deprecated — use config.yaml)",
"url": None,
"password": False,
"category": "setting",
},
"HERMES_TOOL_PROGRESS_MODE": {
"description": "(deprecated) Use display.tool_progress in config.yaml instead",
"prompt": "Progress mode (deprecated — use config.yaml)",
"url": None,
"password": False,
"category": "setting",
},
# The gateway still falls back to these env vars for backward compatibility,
# so they live in _EXTRA_ENV_KEYS (known to .env sanitization/reload) but
# are intentionally NOT listed here: OPTIONAL_ENV_VARS feeds user-facing
# surfaces (dashboard keys page, setup checklists) and deprecated knobs
# shouldn't be offered there.
"HERMES_PREFILL_MESSAGES_FILE": {
"description": "Path to JSON file with ephemeral prefill messages for few-shot priming",
"prompt": "Prefill messages file path",

View file

@ -4468,22 +4468,27 @@ def _truncate_token(value: Optional[str], visible: int = 6) -> str:
def _anthropic_oauth_status() -> Dict[str, Any]:
"""Combined status across the three Anthropic credential sources we read.
"""Status for the "Anthropic API Key" catalog entry.
Hermes resolves Anthropic creds in this order at runtime:
1. ``~/.hermes/.anthropic_oauth.json`` Hermes-managed PKCE flow
2. ``~/.claude/.credentials.json`` Claude Code CLI credentials (auto)
3. ``ANTHROPIC_TOKEN`` / ``ANTHROPIC_API_KEY`` env vars
The dashboard reports the highest-priority source that's actually present.
Two sources, in priority order:
1. ``~/.hermes/.anthropic_oauth.json`` Hermes-managed PKCE flow (what
this entry's Connect button writes)
2. ``ANTHROPIC_API_KEY`` ``ANTHROPIC_TOKEN`` ``CLAUDE_CODE_OAUTH_TOKEN``
env vars (registry order) from ``.env``, the shell, or an external
secret source like Bitwarden (whose keys are injected into the process
env during ``load_hermes_dotenv()``, so the same check covers them)
Claude Code's ``~/.claude/.credentials.json`` is deliberately NOT read
here it has its own dedicated catalog entry (``claude-code``
``_claude_code_only_status``). Reporting it under the API-key entry
double-counts the token and shadows a real ANTHROPIC_API_KEY.
"""
try:
from agent.anthropic_adapter import (
read_hermes_oauth_credentials,
read_claude_code_credentials,
_HERMES_OAUTH_FILE,
)
except ImportError:
read_claude_code_credentials = None # type: ignore
read_hermes_oauth_credentials = None # type: ignore
_HERMES_OAUTH_FILE = None # type: ignore
@ -4503,29 +4508,33 @@ def _anthropic_oauth_status() -> Dict[str, Any]:
"has_refresh_token": bool(hermes_creds.get("refreshToken")),
}
cc_creds = None
if read_claude_code_credentials:
try:
cc_creds = read_claude_code_credentials()
except Exception:
cc_creds = None
if cc_creds and cc_creds.get("accessToken"):
return {
"logged_in": True,
"source": "claude_code",
"source_label": "Claude Code (~/.claude/.credentials.json)",
"token_preview": _truncate_token(cc_creds.get("accessToken")),
"expires_at": cc_creds.get("expiresAt"),
"has_refresh_token": bool(cc_creds.get("refreshToken")),
}
# Env-var / secret-source path. ``get_env_value`` checks the process
# environment first (where Bitwarden-sourced secrets land) then .env.
env_var_order: tuple = ("ANTHROPIC_API_KEY", "ANTHROPIC_TOKEN", "CLAUDE_CODE_OAUTH_TOKEN")
try:
from hermes_cli.auth import PROVIDER_REGISTRY
env_var_order = PROVIDER_REGISTRY["anthropic"].api_key_env_vars
except (ImportError, KeyError):
pass
try:
from hermes_cli.config import get_env_value
except ImportError:
get_env_value = None # type: ignore
try:
from hermes_cli.env_loader import format_secret_source_suffix
except ImportError:
format_secret_source_suffix = None # type: ignore
env_token = os.getenv("ANTHROPIC_TOKEN") or os.getenv("CLAUDE_CODE_OAUTH_TOKEN")
if env_token:
for var in env_var_order:
value = (get_env_value(var) if get_env_value else None) or os.getenv(var)
if not value:
continue
suffix = format_secret_source_suffix(var) if format_secret_source_suffix else ""
return {
"logged_in": True,
"source": "env_var",
"source_label": "ANTHROPIC_TOKEN environment variable",
"token_preview": _truncate_token(env_token),
"source_label": f"{var}{suffix}",
"token_preview": _truncate_token(value),
"expires_at": None,
"has_refresh_token": False,
}