feat: add Codex auth login and export flow

Add Codex Auth support in account management so selected accounts can
complete a Codex-compatible OAuth login flow and export usable auth.json
files.

This commit includes:
- account-management UI entrypoints for Codex Auth login and auth.json download
- backend SSE routes for single-account and batch Codex Auth login execution
- persistence of freshly returned Codex-compatible tokens back into the account database
- Codex auth export support for direct auth.json download and batch zip packaging
- tests covering the Codex Auth login flow and export behavior

The OTP verification failure was caused by manually sending a second OTP after
password verification. The flow now reuses the existing proven login path:
login re-entry, password verification, automatic OTP reception, consent page
handling, workspace selection, and OAuth callback exchange.

Successful logins now also persist workspace_id together with the refreshed
Codex-compatible tokens, making later re-export of auth.json possible without
requiring the browser-downloaded file to still exist locally.

Change-Id: I59df518ef4dc05f8bc52c734dd1b738fcb0b7a4e
This commit is contained in:
Solo
2026-03-25 16:31:03 +08:00
parent 1cbb95f91c
commit f4d0327f67
11 changed files with 1266 additions and 13 deletions

View File

@@ -104,7 +104,7 @@ def test_registration_task_fails_over_after_rate_limit(monkeypatch):
attempts = []
class FakeRegistrationEngine:
def __init__(self, email_service, proxy_url=None, callback_logger=None, task_uuid=None):
def __init__(self, email_service, proxy_url=None, callback_logger=None, status_callback=None, task_uuid=None):
self.email_service = email_service
self.phase_history = []
@@ -240,7 +240,7 @@ def test_registration_task_enters_deep_cooldown_after_three_otp_timeouts(monkeyp
current_time = {"value": 1000.0}
class FakeRegistrationEngine:
def __init__(self, email_service, proxy_url=None, callback_logger=None, task_uuid=None):
def __init__(self, email_service, proxy_url=None, callback_logger=None, status_callback=None, task_uuid=None):
self.email_service = email_service
self.phase_history = []
@@ -350,7 +350,7 @@ def test_registration_task_success_clears_email_service_backoff(monkeypatch):
pass
class FakeRegistrationEngine:
def __init__(self, email_service, proxy_url=None, callback_logger=None, task_uuid=None):
def __init__(self, email_service, proxy_url=None, callback_logger=None, status_callback=None, task_uuid=None):
self.email_service = email_service
self.phase_history = [
PhaseResult(
@@ -458,7 +458,7 @@ def test_registration_task_backoff_failures_do_not_get_lost_under_concurrency(mo
peer_started = threading.Event()
class FakeRegistrationEngine:
def __init__(self, email_service, proxy_url=None, callback_logger=None, task_uuid=None):
def __init__(self, email_service, proxy_url=None, callback_logger=None, status_callback=None, task_uuid=None):
self.email_service = email_service
self.phase_history = []