feat: auto-fetch codex login otp from mailbox

This commit is contained in:
gameloader
2026-03-18 22:48:46 +08:00
parent 8ba8b1404b
commit 3ca69eb5d3
2 changed files with 49 additions and 2 deletions

View File

@@ -198,12 +198,14 @@ class CodexOAuthHTTPFlow:
otp: str = "", otp: str = "",
workspace_id: str = "", workspace_id: str = "",
use_browser: bool = False, use_browser: bool = False,
mailbox: dict | None = None,
) -> None: ) -> None:
self.authorize_url = authorize_url self.authorize_url = authorize_url
self.email = email self.email = email
self.password = password self.password = password
self.otp = otp self.otp = otp
self.workspace_id = workspace_id self.workspace_id = workspace_id
self.mailbox = mailbox
self.auth_base = "https://auth.openai.com" self.auth_base = "https://auth.openai.com"
self.proxy = load_proxy() self.proxy = load_proxy()
self.http = HTTPClient(self.proxy) self.http = HTTPClient(self.proxy)
@@ -304,13 +306,36 @@ class CodexOAuthHTTPFlow:
print(f"[otp] sending email code via {send_url}") print(f"[otp] sending email code via {send_url}")
return self._api_json("GET", send_url, referer=referer) 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]: def _validate_email_otp(self, referer: str) -> dict[str, Any]:
validate_url = f"{self.auth_base}/api/accounts/email-otp/validate" validate_url = f"{self.auth_base}/api/accounts/email-otp/validate"
resend_url = f"{self.auth_base}/api/accounts/email-otp/resend" resend_url = f"{self.auth_base}/api/accounts/email-otp/resend"
while True: while True:
code = (self.otp or input("Email OTP (or type 'resend'): ").strip()).strip() if self.otp:
code = self.otp.strip()
self.otp = "" 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: if not code:
continue continue

View File

@@ -103,6 +103,26 @@ def cmd_codex_login(args):
authorize_url = args.authorize_url or DEFAULT_AUTHORIZE_URL 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}") print(f"Starting Codex OAuth flow for {email}")
flow = CodexOAuthHTTPFlow( flow = CodexOAuthHTTPFlow(
authorize_url=authorize_url, authorize_url=authorize_url,
@@ -110,6 +130,7 @@ def cmd_codex_login(args):
password=password, password=password,
otp=args.otp or "", otp=args.otp or "",
workspace_id=args.workspace_id or "", workspace_id=args.workspace_id or "",
mailbox=mailbox,
) )
try: try:
callback_url = flow.run() 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("--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("--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("--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 return parser