From 10c108f1876bcef52a179c5137d891d720fad3bb Mon Sep 17 00:00:00 2001 From: Cornna <96944678+ymylive@users.noreply.github.com> Date: Wed, 3 Jun 2026 22:53:28 +0800 Subject: [PATCH] fix(auth): fall back from empty codex profile state --- hermes_cli/auth.py | 35 ++++++++-- .../hermes_cli/test_auth_profile_fallback.py | 70 +++++++++++++++++++ 2 files changed, 98 insertions(+), 7 deletions(-) diff --git a/hermes_cli/auth.py b/hermes_cli/auth.py index 7bde989ff3797..2721cc485911f 100644 --- a/hermes_cli/auth.py +++ b/hermes_cli/auth.py @@ -1128,6 +1128,20 @@ def _save_auth_store(auth_store: Dict[str, Any]) -> Path: return auth_file +def _codex_provider_state_has_usable_tokens(state: Dict[str, Any]) -> bool: + tokens = state.get("tokens") + if not isinstance(tokens, dict): + return False + access_token = tokens.get("access_token") + refresh_token = tokens.get("refresh_token") + return ( + isinstance(access_token, str) + and bool(access_token.strip()) + and isinstance(refresh_token, str) + and bool(refresh_token.strip()) + ) + + def _load_provider_state(auth_store: Dict[str, Any], provider_id: str) -> Optional[Dict[str, Any]]: """Return a provider's persisted state. @@ -1143,6 +1157,18 @@ def _load_provider_state(auth_store: Dict[str, Any], provider_id: str) -> Option if isinstance(providers, dict): state = providers.get(provider_id) if isinstance(state, dict): + if provider_id != "openai-codex" or _codex_provider_state_has_usable_tokens(state): + return dict(state) + global_store = _load_global_auth_store() + if global_store: + global_providers = global_store.get("providers") + if isinstance(global_providers, dict): + global_state = global_providers.get(provider_id) + if ( + isinstance(global_state, dict) + and _codex_provider_state_has_usable_tokens(global_state) + ): + return dict(global_state) return dict(state) # Read-only fallback to the global-root auth store (profile mode only; @@ -3654,13 +3680,8 @@ def _pool_codex_access_token() -> str: the original AuthError). """ try: - with _auth_store_lock(): - auth_store = _load_auth_store() - pool = auth_store.get("credential_pool") - if not isinstance(pool, dict): - return "" - entries = pool.get("openai-codex") - if not isinstance(entries, list): + entries = read_credential_pool("openai-codex") + if not entries: return "" def _entry_usable(entry: Dict[str, Any]) -> bool: diff --git a/tests/hermes_cli/test_auth_profile_fallback.py b/tests/hermes_cli/test_auth_profile_fallback.py index 5210404c40ea5..41e1d52902601 100644 --- a/tests/hermes_cli/test_auth_profile_fallback.py +++ b/tests/hermes_cli/test_auth_profile_fallback.py @@ -319,6 +319,54 @@ def test_load_provider_state_profile_wins_over_global(profile_env): assert state["access_token"] == "profile-token" +def test_load_provider_state_codex_empty_tokens_falls_back_to_global(profile_env): + from hermes_cli.auth import _load_auth_store, _load_provider_state + + _write(profile_env["global"] / "auth.json", _make_auth_store(providers={ + "openai-codex": { + "tokens": { + "access_token": "global-codex-token", + "refresh_token": "global-refresh", + }, + "last_refresh": "2026-06-03T00:00:00Z", + }, + })) + _write(profile_env["profile"] / "auth.json", _make_auth_store(providers={ + "openai-codex": {"tokens": {}}, + })) + + auth_store = _load_auth_store() + state = _load_provider_state(auth_store, "openai-codex") + assert state is not None + assert state["tokens"]["access_token"] == "global-codex-token" + + +def test_load_provider_state_codex_profile_valid_tokens_shadow_global(profile_env): + from hermes_cli.auth import _load_auth_store, _load_provider_state + + _write(profile_env["global"] / "auth.json", _make_auth_store(providers={ + "openai-codex": { + "tokens": { + "access_token": "global-codex-token", + "refresh_token": "global-refresh", + }, + }, + })) + _write(profile_env["profile"] / "auth.json", _make_auth_store(providers={ + "openai-codex": { + "tokens": { + "access_token": "profile-codex-token", + "refresh_token": "profile-refresh", + }, + }, + })) + + auth_store = _load_auth_store() + state = _load_provider_state(auth_store, "openai-codex") + assert state is not None + assert state["tokens"]["access_token"] == "profile-codex-token" + + def test_load_provider_state_returns_none_when_neither_has_it(profile_env): from hermes_cli.auth import _load_auth_store, _load_provider_state @@ -352,6 +400,28 @@ def test_load_provider_state_classic_mode_no_fallback(tmp_path, monkeypatch): assert _load_provider_state(auth_store, "anthropic") is None +def test_resolve_codex_runtime_credentials_profile_empty_provider_uses_global_pool(profile_env): + from hermes_cli.auth import resolve_codex_runtime_credentials + + _write(profile_env["global"] / "auth.json", _make_auth_store(pool={ + "openai-codex": [ + { + "source": "device_code", + "access_token": "global-pool-token", + "refresh_token": "global-pool-refresh", + "auth_type": "oauth", + }, + ], + })) + _write(profile_env["profile"] / "auth.json", _make_auth_store(providers={ + "openai-codex": {"tokens": {}}, + }, pool={})) + + resolved = resolve_codex_runtime_credentials() + assert resolved["api_key"] == "global-pool-token" + assert resolved["source"] == "credential_pool" + + def test_load_provider_state_malformed_global_does_not_break_profile(profile_env): """A corrupt global auth.json must not break profile reads.""" (profile_env["global"] / "auth.json").write_text("{not valid json")