fix(auth): self-heal missing Codex access tokens
Recover Codex singleton auth entries that have a refresh token but no access token by adopting a valid Codex CLI token pair, matching the cron-time failure mode before falling back to the credential pool.
This commit is contained in:
parent
311ff967de
commit
aa0798352a
3 changed files with 112 additions and 17 deletions
|
|
@ -76,6 +76,7 @@ def test_resolve_codex_runtime_credentials_missing_access_token(tmp_path, monkey
|
|||
hermes_home = tmp_path / "hermes"
|
||||
_setup_hermes_auth(hermes_home, access_token="")
|
||||
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
|
||||
monkeypatch.setenv("CODEX_HOME", str(tmp_path / "missing-codex"))
|
||||
|
||||
with pytest.raises(AuthError) as exc:
|
||||
resolve_codex_runtime_credentials()
|
||||
|
|
|
|||
|
|
@ -10,10 +10,12 @@ surfacing a hard 401 — but ONLY for relogin-required failures, never for trans
|
|||
ones (e.g. 429 quota, where the stored token is still valid).
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
import hermes_cli.auth as auth
|
||||
from hermes_cli.auth import AuthError, _refresh_codex_auth_tokens
|
||||
from hermes_cli.auth import AuthError, _refresh_codex_auth_tokens, resolve_codex_runtime_credentials
|
||||
|
||||
STALE = {"access_token": "stale-access", "refresh_token": "stale-refresh"}
|
||||
|
||||
|
|
@ -142,3 +144,65 @@ def test_reraises_when_imported_token_lacks_refresh_token(monkeypatch):
|
|||
|
||||
assert ei.value.code == "invalid_grant"
|
||||
assert saved == {} # nothing was persisted
|
||||
|
||||
|
||||
def test_self_heals_missing_singleton_access_token_from_codex_cli(tmp_path, monkeypatch):
|
||||
"""Exact cron failure path: Hermes auth has refresh_token but missing access_token."""
|
||||
hermes_home = tmp_path / "hermes"
|
||||
codex_home = tmp_path / "codex"
|
||||
hermes_home.mkdir()
|
||||
codex_home.mkdir()
|
||||
(hermes_home / "auth.json").write_text(json.dumps({
|
||||
"version": 1,
|
||||
"providers": {
|
||||
"openai-codex": {
|
||||
"tokens": {"refresh_token": "stale-refresh"},
|
||||
"last_refresh": "2026-06-01T00:00:00Z",
|
||||
"auth_mode": "chatgpt",
|
||||
},
|
||||
},
|
||||
}))
|
||||
(codex_home / "auth.json").write_text(json.dumps({
|
||||
"tokens": {
|
||||
"access_token": "fresh-access",
|
||||
"refresh_token": "fresh-refresh",
|
||||
},
|
||||
}))
|
||||
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
|
||||
monkeypatch.setenv("CODEX_HOME", str(codex_home))
|
||||
|
||||
resolved = resolve_codex_runtime_credentials()
|
||||
|
||||
assert resolved["api_key"] == "fresh-access"
|
||||
assert resolved["source"] == "hermes-auth-store"
|
||||
stored = json.loads((hermes_home / "auth.json").read_text())
|
||||
tokens = stored["providers"]["openai-codex"]["tokens"]
|
||||
assert tokens["access_token"] == "fresh-access"
|
||||
assert tokens["refresh_token"] == "fresh-refresh"
|
||||
|
||||
|
||||
def test_missing_singleton_access_token_reraises_when_codex_cli_half_token(tmp_path, monkeypatch):
|
||||
"""Missing access_token must not be masked by a malformed Codex CLI import."""
|
||||
hermes_home = tmp_path / "hermes"
|
||||
codex_home = tmp_path / "codex"
|
||||
hermes_home.mkdir()
|
||||
codex_home.mkdir()
|
||||
(hermes_home / "auth.json").write_text(json.dumps({
|
||||
"version": 1,
|
||||
"providers": {
|
||||
"openai-codex": {
|
||||
"tokens": {"refresh_token": "stale-refresh"},
|
||||
"auth_mode": "chatgpt",
|
||||
},
|
||||
},
|
||||
}))
|
||||
(codex_home / "auth.json").write_text(json.dumps({
|
||||
"tokens": {"access_token": "fresh-only"},
|
||||
}))
|
||||
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
|
||||
monkeypatch.setenv("CODEX_HOME", str(codex_home))
|
||||
|
||||
with pytest.raises(AuthError) as ei:
|
||||
resolve_codex_runtime_credentials()
|
||||
|
||||
assert ei.value.code == "codex_auth_missing_access_token"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue