fix: ignore placeholder provider keys in provider activation checks

Add has_usable_secret() to reject empty, short (<4 char), and common
placeholder API key values (changeme, your_api_key, placeholder, etc.)
throughout the auth/runtime resolution chain.

Update list_available_providers() to use provider-specific auth status
via get_auth_status() instead of resolve_runtime_provider(), preventing
cross-provider key fallback from making providers appear available when
they aren't actually configured.

Preserve keyless custom endpoint support by checking via base URL.

Cherry-picked from PR #2121 by aashizpoudel.
This commit is contained in:
aashizpoudel
2026-03-21 12:55:42 -07:00
committed by Teknium
parent b73d221324
commit f304bc63b8
3 changed files with 60 additions and 26 deletions

View File

@@ -278,6 +278,33 @@ def _try_gh_cli_token() -> Optional[str]:
return None
_PLACEHOLDER_SECRET_VALUES = {
"*",
"**",
"***",
"changeme",
"your_api_key",
"your-api-key",
"placeholder",
"example",
"dummy",
"null",
"none",
}
def has_usable_secret(value: Any, *, min_length: int = 4) -> bool:
"""Return True when a configured secret looks usable, not empty/placeholder."""
if not isinstance(value, str):
return False
cleaned = value.strip()
if len(cleaned) < min_length:
return False
if cleaned.lower() in _PLACEHOLDER_SECRET_VALUES:
return False
return True
def _resolve_api_key_provider_secret(
provider_id: str, pconfig: ProviderConfig
) -> tuple[str, str]:
@@ -297,7 +324,7 @@ def _resolve_api_key_provider_secret(
for env_var in pconfig.api_key_env_vars:
val = os.getenv(env_var, "").strip()
if val:
if has_usable_secret(val):
return val, env_var
return "", ""
@@ -688,7 +715,7 @@ def resolve_provider(
except Exception as e:
logger.debug("Could not detect active auth provider: %s", e)
if os.getenv("OPENAI_API_KEY") or os.getenv("OPENROUTER_API_KEY"):
if has_usable_secret(os.getenv("OPENAI_API_KEY")) or has_usable_secret(os.getenv("OPENROUTER_API_KEY")):
return "openrouter"
# Auto-detect API-key providers by checking their env vars
@@ -701,7 +728,7 @@ def resolve_provider(
if pid == "copilot":
continue
for env_var in pconfig.api_key_env_vars:
if os.getenv(env_var, "").strip():
if has_usable_secret(os.getenv(env_var, "")):
return pid
return "openrouter"

View File

@@ -300,12 +300,15 @@ def list_available_providers() -> list[dict[str, str]]:
# Check if this provider has credentials available
has_creds = False
try:
from hermes_cli.auth import get_auth_status, has_usable_secret
if pid == "custom":
has_creds = bool(_get_custom_base_url())
custom_base_url = _get_custom_base_url() or os.getenv("OPENAI_BASE_URL", "")
has_creds = bool(custom_base_url.strip())
elif pid == "openrouter":
has_creds = has_usable_secret(os.getenv("OPENROUTER_API_KEY", ""))
else:
from hermes_cli.runtime_provider import resolve_runtime_provider
runtime = resolve_runtime_provider(requested=pid)
has_creds = bool(runtime.get("api_key"))
status = get_auth_status(pid)
has_creds = bool(status.get("logged_in") or status.get("configured"))
except Exception:
pass
result.append({

View File

@@ -15,6 +15,7 @@ from hermes_cli.auth import (
resolve_codex_runtime_credentials,
resolve_api_key_provider_credentials,
resolve_external_process_provider_credentials,
has_usable_secret,
)
from hermes_cli.config import load_config
from hermes_constants import OPENROUTER_BASE_URL
@@ -188,12 +189,13 @@ def _resolve_named_custom_runtime(
if not base_url:
return None
api_key = (
(explicit_api_key or "").strip()
or custom_provider.get("api_key", "")
or os.getenv("OPENAI_API_KEY", "").strip()
or os.getenv("OPENROUTER_API_KEY", "").strip()
)
api_key_candidates = [
(explicit_api_key or "").strip(),
str(custom_provider.get("api_key", "") or "").strip(),
os.getenv("OPENAI_API_KEY", "").strip(),
os.getenv("OPENROUTER_API_KEY", "").strip(),
]
api_key = next((candidate for candidate in api_key_candidates if has_usable_secret(candidate)), "")
return {
"provider": "openrouter",
@@ -257,21 +259,23 @@ def _resolve_openrouter_runtime(
# provider (issues #420, #560).
_is_openrouter_url = "openrouter.ai" in base_url
if _is_openrouter_url:
api_key = (
explicit_api_key
or os.getenv("OPENROUTER_API_KEY")
or os.getenv("OPENAI_API_KEY")
or ""
)
api_key_candidates = [
explicit_api_key,
os.getenv("OPENROUTER_API_KEY"),
os.getenv("OPENAI_API_KEY"),
]
else:
# Custom endpoint: use api_key from config when using config base_url (#1760).
api_key = (
explicit_api_key
or (cfg_api_key if use_config_base_url else "")
or os.getenv("OPENAI_API_KEY")
or os.getenv("OPENROUTER_API_KEY")
or ""
)
api_key_candidates = [
explicit_api_key,
(cfg_api_key if use_config_base_url else ""),
os.getenv("OPENAI_API_KEY"),
os.getenv("OPENROUTER_API_KEY"),
]
api_key = next(
(str(candidate or "").strip() for candidate in api_key_candidates if has_usable_secret(candidate)),
"",
)
source = "explicit" if (explicit_api_key or explicit_base_url) else "env/config"