mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
fix(auth): mirror Nous OAuth credentials to providers.nous on CLI login
`hermes auth add nous --type oauth` only wrote credential_pool.nous, leaving providers.nous empty. When the Nous agent_key's 24h TTL expired, run_agent.py's 401-recovery path called resolve_nous_runtime_credentials (which reads providers.nous), got AuthError "Hermes is not logged into Nous Portal", caught it as logger.debug (suppressed at INFO level), and the agent died with "Non-retryable client error" — no signal to the user that recovery even tried. Introduce persist_nous_credentials() as the single source of truth for Nous device-code login persistence. Both auth_commands (CLI) and web_server (dashboard) now route through it, so pool and providers stay in sync at write time. Why: CLI-provisioned profiles couldn't recover from agent_key expiry, producing silent daily outages 24h after first login. PR #6856/#6869 addressed adjacent issues but assumed providers.nous was populated; this one wasn't being written. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a155b4a159
commit
c096a6935f
5 changed files with 221 additions and 40 deletions
|
|
@ -2159,6 +2159,54 @@ def refresh_nous_oauth_from_state(
|
|||
)
|
||||
|
||||
|
||||
def persist_nous_credentials(
|
||||
creds: Dict[str, Any],
|
||||
*,
|
||||
label: str,
|
||||
source: str,
|
||||
):
|
||||
"""Persist minted Nous OAuth credentials to both auth-store sections.
|
||||
|
||||
Nous credentials are read at runtime from two independent locations:
|
||||
|
||||
- ``credential_pool.nous``: used by the runtime ``pool.select()`` path that
|
||||
services outbound inference requests.
|
||||
- ``providers.nous``: used by ``resolve_nous_runtime_credentials()`` — the
|
||||
singleton-state reader invoked during 401 recovery and dashboard status
|
||||
checks.
|
||||
|
||||
Historically ``hermes auth add nous`` wrote only to the pool while the web
|
||||
dashboard device-code flow wrote to both, so CLI-provisioned profiles
|
||||
failed silently when the recovery path was later consulted. This helper
|
||||
is the single source of truth for CLI/web device-code persistence: both
|
||||
stores are always written together.
|
||||
|
||||
Returns the added :class:`PooledCredential` entry.
|
||||
"""
|
||||
from agent.credential_pool import (
|
||||
PooledCredential,
|
||||
load_pool,
|
||||
AUTH_TYPE_OAUTH,
|
||||
)
|
||||
|
||||
pool = load_pool("nous")
|
||||
entry = PooledCredential.from_dict("nous", {
|
||||
**creds,
|
||||
"label": label,
|
||||
"auth_type": AUTH_TYPE_OAUTH,
|
||||
"source": source,
|
||||
"base_url": creds.get("inference_base_url"),
|
||||
})
|
||||
pool.add_entry(entry)
|
||||
|
||||
with _auth_store_lock():
|
||||
auth_store = _load_auth_store()
|
||||
_save_provider_state(auth_store, "nous", creds)
|
||||
_save_auth_store(auth_store)
|
||||
|
||||
return entry
|
||||
|
||||
|
||||
def resolve_nous_runtime_credentials(
|
||||
*,
|
||||
min_key_ttl_seconds: int = DEFAULT_AGENT_KEY_MIN_TTL_SECONDS,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue