From 89b651b8cbc65decb4e6b296ebbc1a8def5e3fa1 Mon Sep 17 00:00:00 2001 From: AhmetArif0 <147827411+AhmetArif0@users.noreply.github.com> Date: Tue, 2 Jun 2026 21:42:12 +0300 Subject: [PATCH] fix(auth): use _save_xai_oauth_tokens in auth_commands to set active_provider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hermes auth add xai-oauth used pool.add_entry() directly, writing only the credential pool entry without touching providers["xai-oauth"] or setting active_provider in auth.json. On a fresh install _model_section_has_credentials() checks get_active_provider() first; with active_provider unset it fell through to env-var checks, found nothing, and the setup wizard reported "No inference provider configured" even though the OAuth flow had succeeded. Use _save_xai_oauth_tokens() instead — the canonical path that writes the providers.xai-oauth singleton (setting active_provider) and lets the pool seed itself from auth.json via _seed_from_singletons on the next load_pool() call. Mirrors the fix applied to openai-codex in #37517. --- hermes_cli/auth_commands.py | 26 ++++++--------- tests/hermes_cli/test_auth_commands.py | 44 ++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/hermes_cli/auth_commands.py b/hermes_cli/auth_commands.py index 0ca5627627557..336895d3be2d7 100644 --- a/hermes_cli/auth_commands.py +++ b/hermes_cli/auth_commands.py @@ -329,24 +329,18 @@ def auth_add_command(args) -> None: open_browser=not getattr(args, "no_browser", False), manual_paste=bool(getattr(args, "manual_paste", False)), ) - label = (getattr(args, "label", None) or "").strip() or label_from_token( - creds["tokens"]["access_token"], - _oauth_default_label(provider, len(pool.entries()) + 1), - ) - entry = PooledCredential( - provider=provider, - id=uuid.uuid4().hex[:6], - label=label, - auth_type=AUTH_TYPE_OAUTH, - priority=0, - source=f"{SOURCE_MANUAL}:xai_pkce", - access_token=creds["tokens"]["access_token"], - refresh_token=creds["tokens"].get("refresh_token"), - base_url=creds.get("base_url"), + auth_mod._save_xai_oauth_tokens( + creds["tokens"], + discovery=creds.get("discovery"), + redirect_uri=creds.get("redirect_uri", ""), last_refresh=creds.get("last_refresh"), ) - pool.add_entry(entry) - print(f'Added {provider} OAuth credential #{len(pool.entries())}: "{entry.label}"') + pool = load_pool(provider) + entry = next((e for e in pool.entries() if getattr(e, "source", "") == "loopback_pkce"), None) + shown_label = entry.label if entry is not None else label_from_token( + creds["tokens"]["access_token"], _oauth_default_label(provider, 1) + ) + print(f'Saved {provider} OAuth credentials: "{shown_label}"') return if provider == "google-gemini-cli": diff --git a/tests/hermes_cli/test_auth_commands.py b/tests/hermes_cli/test_auth_commands.py index f6fc408a083fb..bca6887a7c8ff 100644 --- a/tests/hermes_cli/test_auth_commands.py +++ b/tests/hermes_cli/test_auth_commands.py @@ -312,6 +312,50 @@ class _Args: assert entry["base_url"] == "https://chatgpt.com/backend-api/codex" +def test_auth_add_xai_oauth_persists_provider_state(tmp_path, monkeypatch): + """hermes auth add xai-oauth must set active_provider so setup detects the provider.""" + monkeypatch.setenv("HERMES_HOME", str(tmp_path / "hermes")) + _write_auth_store(tmp_path, {"version": 1, "providers": {}}) + access_token = "xai-test-access-token" + monkeypatch.setattr( + "hermes_cli.auth._xai_oauth_loopback_login", + lambda **kwargs: { + "tokens": { + "access_token": access_token, + "refresh_token": "xai-refresh-token", + "id_token": "", + "token_type": "Bearer", + }, + "discovery": {"token_endpoint": "https://auth.x.ai/token"}, + "redirect_uri": "http://127.0.0.1:7777/callback", + "base_url": "https://api.x.ai/v1", + "last_refresh": "2026-06-02T10:00:00Z", + "source": "oauth-loopback", + }, + ) + + from hermes_cli.auth_commands import auth_add_command + + class _Args: + provider = "xai-oauth" + auth_type = "oauth" + api_key = None + label = None + timeout = None + no_browser = False + manual_paste = False + + auth_add_command(_Args()) + + payload = json.loads((tmp_path / "hermes" / "auth.json").read_text()) + assert payload["active_provider"] == "xai-oauth" + assert payload["providers"]["xai-oauth"]["tokens"]["access_token"] == access_token + entries = payload["credential_pool"]["xai-oauth"] + entry = next(item for item in entries if item["source"] == "loopback_pkce") + assert entry["source"] == "loopback_pkce" + assert entry["refresh_token"] == "xai-refresh-token" + + def test_auth_remove_reindexes_priorities(tmp_path, monkeypatch): monkeypatch.setenv("HERMES_HOME", str(tmp_path / "hermes")) # Prevent pool auto-seeding from host env vars and file-backed sources