diff --git a/src/codex_oauth_http_flow.py b/src/codex_oauth_http_flow.py index 592f4fa..e0cbb74 100644 --- a/src/codex_oauth_http_flow.py +++ b/src/codex_oauth_http_flow.py @@ -198,12 +198,14 @@ class CodexOAuthHTTPFlow: otp: str = "", workspace_id: str = "", use_browser: bool = False, + mailbox: dict | None = None, ) -> None: self.authorize_url = authorize_url self.email = email self.password = password self.otp = otp self.workspace_id = workspace_id + self.mailbox = mailbox self.auth_base = "https://auth.openai.com" self.proxy = load_proxy() self.http = HTTPClient(self.proxy) @@ -304,13 +306,36 @@ class CodexOAuthHTTPFlow: print(f"[otp] sending email code via {send_url}") return self._api_json("GET", send_url, referer=referer) + def _fetch_otp_from_mailbox(self) -> str: + """Try to fetch OTP from mailbox automatically.""" + if not self.mailbox: + return "" + from vmail_client import MailClient + mail = MailClient() + print("[otp] auto-fetching OTP from mailbox...") + try: + code = mail.wait_for_otp(self.mailbox, timeout=120, poll=3.0) + print(f" OTP: {code}") + return code + except Exception as e: + print(f"[otp] auto-fetch failed: {e}") + return "" + def _validate_email_otp(self, referer: str) -> dict[str, Any]: validate_url = f"{self.auth_base}/api/accounts/email-otp/validate" resend_url = f"{self.auth_base}/api/accounts/email-otp/resend" while True: - code = (self.otp or input("Email OTP (or type 'resend'): ").strip()).strip() - self.otp = "" + if self.otp: + code = self.otp.strip() + self.otp = "" + elif self.mailbox: + code = self._fetch_otp_from_mailbox() + if not code: + raise FlowError("Failed to auto-fetch OTP from mailbox") + else: + code = input("Email OTP (or type 'resend'): ").strip() + if not code: continue diff --git a/src/main.py b/src/main.py index 20b3df9..fe01e0a 100644 --- a/src/main.py +++ b/src/main.py @@ -103,6 +103,26 @@ def cmd_codex_login(args): authorize_url = args.authorize_url or DEFAULT_AUTHORIZE_URL + # Try to get mailbox token for auto OTP fetching + mailbox = None + if args.mailbox_password: + import httpx + try: + token_resp = httpx.post( + "https://api.mail.tm/token", + json={"address": email, "password": args.mailbox_password}, + timeout=30, + ) + if token_resp.status_code == 200: + mailbox = { + "address": email, + "password": args.mailbox_password, + "token": token_resp.json()["token"], + } + print(f"Mailbox token obtained, will auto-fetch OTP if needed") + except Exception as e: + print(f"Warning: Failed to get mailbox token: {e}") + print(f"Starting Codex OAuth flow for {email}") flow = CodexOAuthHTTPFlow( authorize_url=authorize_url, @@ -110,6 +130,7 @@ def cmd_codex_login(args): password=password, otp=args.otp or "", workspace_id=args.workspace_id or "", + mailbox=mailbox, ) try: callback_url = flow.run() @@ -153,6 +174,7 @@ Examples: p_codex.add_argument("--otp", default="", help="Email OTP code (if already known)") p_codex.add_argument("--workspace-id", dest="workspace_id", default="", help="Workspace ID to select") p_codex.add_argument("--authorize-url", dest="authorize_url", default="", help="Custom authorize URL") + p_codex.add_argument("--mailbox-password", dest="mailbox_password", default="", help="Mailbox password for auto OTP fetching") return parser