fix(auth): align Codex OAuth persistence paths (#37517)
* fix(desktop): codex OAuth onboarding now resolves on fresh install The desktop codex device-code worker persisted tokens with a hand-rolled pool.add_entry(), writing only credential_pool.openai-codex. It never set active_provider, so on a fresh install the onboarding setup.runtime_check resolved provider "auto", couldn't detect the Codex OAuth session, and raised "No inference provider configured" — while setup.status (which sniffs the pool) reported configured. The disagreement surfaced as the onboarding banner "Connected, but Hermes still cannot resolve a usable provider." Use the canonical _save_codex_tokens() instead, matching the CLI's `hermes auth add openai-codex` path and the Nous/MiniMax dashboard workers. It writes the providers.openai-codex singleton (setting active_provider) and syncs the pool. * fix(auth): align Codex OAuth persistence paths Ensure desktop and CLI Codex OAuth logins both write the canonical provider state so fresh installs resolve a usable runtime provider. --------- Co-authored-by: teknium1 <127238744+teknium1@users.noreply.github.com>
This commit is contained in:
parent
3e6b68252f
commit
bb0619dbce
6 changed files with 85 additions and 49 deletions
|
|
@ -3370,7 +3370,7 @@ def _sync_codex_pool_entries(
|
|||
entry["last_error_reset_at"] = None
|
||||
|
||||
|
||||
def _save_codex_tokens(tokens: Dict[str, str], last_refresh: str = None) -> None:
|
||||
def _save_codex_tokens(tokens: Dict[str, str], last_refresh: str = None, label: str = None) -> None:
|
||||
"""Save Codex OAuth tokens to Hermes auth store (~/.hermes/auth.json)."""
|
||||
if last_refresh is None:
|
||||
last_refresh = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
||||
|
|
@ -3380,6 +3380,8 @@ def _save_codex_tokens(tokens: Dict[str, str], last_refresh: str = None) -> None
|
|||
state["tokens"] = tokens
|
||||
state["last_refresh"] = last_refresh
|
||||
state["auth_mode"] = "chatgpt"
|
||||
if label and str(label).strip():
|
||||
state["label"] = str(label).strip()
|
||||
_save_provider_state(auth_store, "openai-codex", state)
|
||||
_sync_codex_pool_entries(auth_store, tokens, last_refresh)
|
||||
_save_auth_store(auth_store)
|
||||
|
|
|
|||
|
|
@ -307,28 +307,20 @@ def auth_add_command(args) -> None:
|
|||
return
|
||||
|
||||
if provider == "openai-codex":
|
||||
# Clear any existing suppression marker so a re-link after `hermes auth
|
||||
# remove openai-codex` works without the new tokens being skipped.
|
||||
auth_mod.unsuppress_credential_source(provider, "device_code")
|
||||
creds = auth_mod._codex_device_code_login()
|
||||
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}:device_code",
|
||||
access_token=creds["tokens"]["access_token"],
|
||||
refresh_token=creds["tokens"].get("refresh_token"),
|
||||
base_url=creds.get("base_url"),
|
||||
auth_mod._save_codex_tokens(
|
||||
creds["tokens"],
|
||||
last_refresh=creds.get("last_refresh"),
|
||||
label=label,
|
||||
)
|
||||
pool.add_entry(entry)
|
||||
print(f'Added {provider} OAuth credential #{len(pool.entries())}: "{entry.label}"')
|
||||
pool = load_pool(provider)
|
||||
entry = next((item for item in pool.entries() if item.source == "device_code"), None)
|
||||
shown_label = entry.label if entry is not None else label
|
||||
print(f'Saved {provider} OAuth device-code credentials: "{shown_label}"')
|
||||
return
|
||||
|
||||
if provider == "xai-oauth":
|
||||
|
|
|
|||
|
|
@ -3729,31 +3729,12 @@ def _codex_full_login_worker(session_id: str) -> None:
|
|||
if not access_token:
|
||||
raise RuntimeError("token exchange did not return access_token")
|
||||
|
||||
# Persist via credential pool — same shape as auth_commands.add_command
|
||||
from agent.credential_pool import (
|
||||
PooledCredential,
|
||||
load_pool,
|
||||
AUTH_TYPE_OAUTH,
|
||||
SOURCE_MANUAL,
|
||||
)
|
||||
import uuid as _uuid
|
||||
pool = load_pool("openai-codex")
|
||||
base_url = (
|
||||
os.getenv("HERMES_CODEX_BASE_URL", "").strip().rstrip("/")
|
||||
or DEFAULT_CODEX_BASE_URL
|
||||
)
|
||||
entry = PooledCredential(
|
||||
provider="openai-codex",
|
||||
id=_uuid.uuid4().hex[:6],
|
||||
label="dashboard device_code",
|
||||
auth_type=AUTH_TYPE_OAUTH,
|
||||
priority=0,
|
||||
source=f"{SOURCE_MANUAL}:dashboard_device_code",
|
||||
access_token=access_token,
|
||||
refresh_token=refresh_token,
|
||||
base_url=base_url,
|
||||
)
|
||||
pool.add_entry(entry)
|
||||
from hermes_cli.auth import _save_codex_tokens
|
||||
|
||||
_save_codex_tokens({
|
||||
"access_token": access_token,
|
||||
"refresh_token": refresh_token,
|
||||
})
|
||||
with _oauth_sessions_lock:
|
||||
sess["status"] = "approved"
|
||||
_log.info("oauth/device: openai-codex login completed (session=%s)", session_id)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue