docs: add token-lite automation flow
This commit is contained in:
Executable
+207
@@ -0,0 +1,207 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Token-lite Codex OAuth onboarding runner skeleton.
|
||||
|
||||
Purpose: keep future Codex2API/OpenAI/5sim onboarding runs script-driven with compact JSONL events.
|
||||
This script intentionally centralizes config parsing, redaction, state classification, and event logging.
|
||||
Sensitive values are never printed directly.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import datetime as dt
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from typing import Any, Dict, Iterable, List, Optional, Tuple
|
||||
|
||||
COUNTRY_META = {
|
||||
"argentina": {"label_zh": "阿根廷", "prefix": "54"},
|
||||
"netherlands": {"label_zh": "荷兰", "prefix": "31"},
|
||||
"indonesia": {"label_zh": "印度尼西亚", "prefix": "62"},
|
||||
"vietnam": {"label_zh": "越南", "prefix": "84"},
|
||||
"england": {"label_zh": "英国", "prefix": "44"},
|
||||
"japan": {"label_zh": "日本", "prefix": "81"},
|
||||
"germany": {"label_zh": "德国", "prefix": "49"},
|
||||
}
|
||||
|
||||
SECRET_KEY_RE = re.compile(r"(KEY|TOKEN|SECRET|PASSWORD|PASS|CODE|CALLBACK)", re.I)
|
||||
DIRECT_PHONE_REJECT = "无法向此电话号码发送验证码"
|
||||
INVALID_PHONE = "电话号码无效"
|
||||
ACCOUNT_DEACTIVATED = "account_deactivated"
|
||||
|
||||
|
||||
def now() -> str:
|
||||
return dt.datetime.now().isoformat(timespec="seconds")
|
||||
|
||||
|
||||
def load_env(path: pathlib.Path) -> Dict[str, str]:
|
||||
env: Dict[str, str] = {}
|
||||
for raw in path.read_text().splitlines():
|
||||
line = raw.strip()
|
||||
if not line or line.startswith("#") or "=" not in line:
|
||||
continue
|
||||
k, v = line.split("=", 1)
|
||||
env[k.strip()] = v.strip().strip('"\'')
|
||||
return env
|
||||
|
||||
|
||||
def redact_key_value(key: str, value: Any) -> Any:
|
||||
if value is None:
|
||||
return None
|
||||
if SECRET_KEY_RE.search(key):
|
||||
return "[REDACTED]" if str(value) else ""
|
||||
if isinstance(value, str) and "code=" in value and "state=" in value:
|
||||
return re.sub(r"code=[^&]+", "code=[REDACTED]", value)
|
||||
return value
|
||||
|
||||
|
||||
def redact_event(event: Dict[str, Any]) -> Dict[str, Any]:
|
||||
return {k: redact_key_value(k, v) for k, v in event.items()}
|
||||
|
||||
|
||||
class RunLog:
|
||||
def __init__(self, run_dir: pathlib.Path):
|
||||
self.run_dir = run_dir
|
||||
self.run_dir.mkdir(parents=True, exist_ok=True)
|
||||
(self.run_dir / "screenshots").mkdir(exist_ok=True)
|
||||
self.events_path = self.run_dir / "events.jsonl"
|
||||
|
||||
def emit(self, phase: str, result: str = "info", **fields: Any) -> None:
|
||||
event = {"ts": now(), "phase": phase, "result": result, **fields}
|
||||
safe = redact_event(event)
|
||||
with self.events_path.open("a") as f:
|
||||
f.write(json.dumps(safe, ensure_ascii=False, separators=(",", ":")) + "\n")
|
||||
print(json.dumps(safe, ensure_ascii=False, separators=(",", ":")), flush=True)
|
||||
|
||||
|
||||
def run(cmd: str, timeout: int = 30, input_text: Optional[str] = None) -> subprocess.CompletedProcess[str]:
|
||||
return subprocess.run(
|
||||
["bash", "-lc", cmd],
|
||||
input=input_text,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
|
||||
def cdp_eval(js: str, match: str = "auth.openai.com", timeout: int = 45) -> str:
|
||||
proc = run(f"CDP_MATCH={shlex.quote(match)} python3 /tmp/cdp_async_eval.py", timeout=timeout, input_text=js)
|
||||
return (proc.stdout or "") + (proc.stderr or "")
|
||||
|
||||
|
||||
def classify_page(raw: str) -> str:
|
||||
if ACCOUNT_DEACTIVATED in raw:
|
||||
return "account_deactivated"
|
||||
if "Cloudflare" in raw or "验证您是真人" in raw or "challenge" in raw.lower():
|
||||
return "cloudflare_or_challenge"
|
||||
if "auth.openai.com/log-in" in raw or "欢迎回来" in raw:
|
||||
return "login_page"
|
||||
if "email-verification" in raw or "检查你的收件箱" in raw:
|
||||
return "email_code_page"
|
||||
if "add-phone" in raw or "电话号码是必填项" in raw:
|
||||
return "add_phone_page"
|
||||
if "重新发送" in raw and ("验证码" in raw or "代码" in raw):
|
||||
return "phone_code_page"
|
||||
if DIRECT_PHONE_REJECT in raw:
|
||||
return "phone_direct_reject"
|
||||
return "unknown"
|
||||
|
||||
|
||||
def country_order(env: Dict[str, str]) -> List[str]:
|
||||
order = [x.strip().lower() for x in env.get("FIVE_SIM_COUNTRY_ORDER", "").split(",") if x.strip()]
|
||||
if order:
|
||||
return order
|
||||
country_id = env.get("FIVE_SIM_COUNTRY_ID", "").strip().lower()
|
||||
if country_id:
|
||||
return [country_id]
|
||||
return ["vietnam"]
|
||||
|
||||
|
||||
def codex2api_origin(env: Dict[str, str]) -> str:
|
||||
raw = env.get("CODEX2API_URL", "").strip().rstrip("/")
|
||||
u = urllib.parse.urlsplit(raw)
|
||||
return f"{u.scheme}://{u.netloc}" if u.scheme and u.netloc else raw
|
||||
|
||||
|
||||
def generate_codex2api_oauth_url(env: Dict[str, str]) -> Dict[str, Any]:
|
||||
origin = codex2api_origin(env)
|
||||
req = urllib.request.Request(
|
||||
origin + "/api/admin/oauth/generate-auth-url",
|
||||
data=b"{}",
|
||||
headers={"X-Admin-Key": env.get("CODEX2API_ADMIN_KEY", ""), "Content-Type": "application/json"},
|
||||
)
|
||||
with urllib.request.urlopen(req, timeout=20) as resp:
|
||||
return json.loads(resp.read().decode())
|
||||
|
||||
|
||||
def open_cdp_url(url: str) -> None:
|
||||
req = urllib.request.Request("http://127.0.0.1:9223/json/new?" + urllib.parse.quote(url, safe=""), method="PUT")
|
||||
with urllib.request.urlopen(req, timeout=10) as resp:
|
||||
resp.read()
|
||||
|
||||
|
||||
def probe_proxy(proxy: str) -> Tuple[bool, str]:
|
||||
if proxy.startswith("socks5://"):
|
||||
curl_proxy = "socks5h://" + proxy[len("socks5://") :]
|
||||
else:
|
||||
curl_proxy = proxy
|
||||
proc = run(f"curl --proxy {shlex.quote(curl_proxy)} --max-time 8 https://api.ipify.org?format=json", timeout=12)
|
||||
ok = proc.returncode == 0 and "ip" in proc.stdout
|
||||
return ok, proc.stdout.strip()[-120:]
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--env", default="~/.hermes/env/codex_oauth_onboarding.env")
|
||||
ap.add_argument("--run-root", default="~/.Hermes/workspace/runs/codex-oauth")
|
||||
ap.add_argument("--print-plan", action="store_true", help="Only print resolved low-token plan/config summary.")
|
||||
args = ap.parse_args()
|
||||
|
||||
env_path = pathlib.Path(args.env).expanduser()
|
||||
env = load_env(env_path)
|
||||
run_id = dt.datetime.now().strftime("%Y%m%d-%H%M%S")
|
||||
log = RunLog(pathlib.Path(args.run_root).expanduser() / run_id)
|
||||
|
||||
countries = country_order(env)
|
||||
proxy = env.get("BROWSER_PROXY_SERVER", "")
|
||||
replacement_limit = int(env.get("PHONE_VERIFICATION_REPLACEMENT_LIMIT") or "3")
|
||||
log.emit(
|
||||
"preflight",
|
||||
"config_loaded",
|
||||
env_path=str(env_path),
|
||||
target_mode=env.get("PANEL_MODE") or env.get("OAUTH_TARGET_MODE") or "codex2api",
|
||||
proxy=proxy,
|
||||
country_order=",".join(countries),
|
||||
replacement_limit=replacement_limit,
|
||||
five_sim_product=env.get("FIVE_SIM_PRODUCT") or env.get("FIVE_SIM_SERVICE") or "openai",
|
||||
operator=env.get("FIVE_SIM_OPERATOR") or "any",
|
||||
)
|
||||
|
||||
if proxy:
|
||||
ok, info = probe_proxy(proxy)
|
||||
log.emit("preflight", "proxy_ok" if ok else "proxy_failed", proxy=proxy, info=info)
|
||||
if not ok:
|
||||
return 2
|
||||
|
||||
if args.print_plan:
|
||||
log.emit("plan", "ready", next="run full automation script implementation/resume")
|
||||
return 0
|
||||
|
||||
log.emit(
|
||||
"not_implemented",
|
||||
"skeleton_only",
|
||||
message="This script currently provides the token-lite logging/config skeleton. Add browser/email/phone step execution before using as the primary runner.",
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -47,7 +47,7 @@ These are confirmed operating rules for BOSS's environment:
|
||||
- The new account password is unified through `CUSTOM_PASSWORD`. If it is empty, stop and ask BOSS to fill it, unless BOSS explicitly allows auto-generation for that run.
|
||||
- ClawEmail creates a fresh mailbox for every run (`CLAWEMAIL_CREATE_PER_RUN=true`). Record every created mailbox locally with outcome classification: pending, success, failed.
|
||||
- Phone/SMS platform has no default. Keep `PHONE_VERIFICATION_ENABLED=false` and `PHONE_SMS_PROVIDER` empty unless BOSS configures one or approves enabling it after a phone verification appears.
|
||||
- If phone verification appears and BOSS has configured 5sim, use **SMS activation for `openai`**, not WhatsApp receive channels. The original extension's 5sim provider buys `/v1/user/buy/activation/{country}/{operator}/openai`, polls `/v1/user/check/{activationId}`, and finishes/cancels the activation. Follow the configured `FIVE_SIM_COUNTRY_ID` first; if it is empty, the extension default is `vietnam`. Do **not** silently switch countries after a rejected number unless BOSS changes config or approves fallback. Before buying/submitting a non-USA number, verify the OpenAI visible country selector is already the expected country (for example `越南 (+84)`); if it reverted to `美国 (+1)`, fix the selector first or the number will be parsed as invalid. Use visible UI paste/click on `add-phone` when possible because CDP-only input mutation can desync React state and revert the country on submit. If a number is **not immediately rejected**, do not cancel it after one polling timeout: poll 5sim, click OpenAI `重新发送`, poll again, and retry resend at least once before canceling/rotating. If changing proxy (for example `1085` → `1083`), expect OpenAI auth session invalidation and restart from a fresh target OAuth URL. Codex2API env URLs may point at `/admin/accounts`; derive the API origin before calling `/api/admin/oauth/generate-auth-url`. See `references/openai-add-phone-5sim-sms-activation-2026-05-10.md`, `references/openai-phone-verification-5sim-sms-2026-05-10.md`, and `references/openai-phone-verification-proxy1083-resend-2026-05-10.md` for observed behavior, resend strategy, proxy-switch recovery, and pitfalls.
|
||||
- If phone verification appears and BOSS has configured 5sim, use **SMS activation for `openai`**, not WhatsApp receive channels. The original extension's 5sim provider buys `/v1/user/buy/activation/{country}/{operator}/openai`, polls `/v1/user/check/{activationId}`, and finishes/cancels the activation. **Country selection priority is `FIVE_SIM_COUNTRY_ORDER` first** (comma-separated list such as `argentina,netherlands,indonesia`), then `FIVE_SIM_COUNTRY_ID`, then the original extension default `vietnam` only if both are empty. For each configured country, rotate numbers up to `PHONE_VERIFICATION_REPLACEMENT_LIMIT`; do **not** silently switch outside the configured order unless BOSS approves. Before buying/submitting a non-USA number, verify the OpenAI visible country selector is already the expected country (for example `阿根廷 (+54)`, `荷兰 (+31)`, `印度尼西亚 (+62)`, or `越南 (+84)`); if it reverted to `美国 (+1)`, fix the selector first or the number will be parsed as invalid. Use visible UI paste/click on `add-phone` when possible because CDP-only input mutation can desync React state and revert the country on submit. If a number is **not immediately rejected**, do not cancel it after one polling timeout: poll 5sim, click OpenAI `重新发送`, poll again, and retry resend at least once before canceling/rotating. If changing proxy (for example `1085` → `1083` or back), expect OpenAI auth session invalidation and restart from a fresh target OAuth URL. Codex2API env URLs may point at `/admin/accounts`; derive the API origin before calling `/api/admin/oauth/generate-auth-url`. If OpenAI returns `account_deactivated` after email verification, stop the current account and start with a new ClawEmail sub-mailbox instead of burning 5sim numbers. See `references/openai-add-phone-5sim-sms-activation-2026-05-10.md`, `references/openai-phone-verification-5sim-sms-2026-05-10.md`, `references/openai-phone-verification-proxy1083-resend-2026-05-10.md`, and `references/codex-oauth-token-lite-automation-flow-2026-05-10.md` for observed behavior, resend strategy, proxy-switch recovery, and pitfalls.
|
||||
- Plus mode uses `PLUS_PAYMENT_METHOD=gopay`. Any paid GoPay action still requires explicit confirmation immediately before payment/approval.
|
||||
- GoPay WhatsApp OTP uses **方案 A / manual checkpoint**: set `GOPAY_OTP_SOURCE=manual`. The extension detects OTP input and opens a side-panel prompt (`requestGoPayOtpInput` / “输入 GoPay 验证码”); BOSS pastes the WhatsApp code, then the extension fills OTP, fills `GOPAY_PIN`, and continues.
|
||||
- `GOPAY_OTP` should normally stay empty before the run. It is only a temporary prefill/cache for the current OTP dialog, not a durable secret.
|
||||
@@ -508,10 +508,12 @@ See `references/openai-clawemail-verification-nondelivery-2026-05-09.md` for the
|
||||
|
||||
See `references/openai-add-phone-5sim-sms-activation-2026-05-10.md` for the phone-verification continuation where OpenAI `add-phone` required SMS, BOSS corrected that WhatsApp receive channels must not be used, and 5sim `activation/*/openai` orders plus visible country selection were identified as the right class of workflow.
|
||||
|
||||
See `references/openai-phone-verification-5sim-sms-2026-05-10.md` for the later detailed retry notes: follow configured country/default `vietnam`, cancel failed 5sim orders before buying new ones, treat OpenAI `无法向此电话号码发送验证码` as a number/provider rejection, and avoid CDP-only mutations that desync the React country selector back to `美国 (+1)`.
|
||||
See `references/openai-phone-verification-5sim-sms-2026-05-10.md` for early detailed retry notes: cancel failed 5sim orders before buying new ones, treat OpenAI `无法向此电话号码发送验证码` as a number/provider rejection, and avoid CDP-only mutations that desync the React country selector back to `美国 (+1)`. **Do not copy the early Vietnam-default assumption when `FIVE_SIM_COUNTRY_ORDER` is set; use the corrected precedence in `references/openai-phone-verification-country-order-account-deactivated-2026-05-10.md`.**
|
||||
|
||||
See `references/openai-add-phone-1083-vietnam-resend-2026-05-10.md` for the follow-up test requested by BOSS: switch browser proxy to `socks5://192.168.2.8:1083`, regenerate Codex2API OAuth URL from the origin API when the auth session expires, continue with configured-country `vietnam` SMS activation, and for non-rejected numbers poll 5sim then click `重新发送` before canceling/retrying.
|
||||
|
||||
See `references/openai-phone-verification-country-order-account-deactivated-2026-05-10.md` for the correction that `FIVE_SIM_COUNTRY_ORDER` (for example `argentina,netherlands,indonesia`) takes precedence over empty `FIVE_SIM_COUNTRY_ID` and default `vietnam`, plus the recovery rule that `account_deactivated` during email verification ends phone attempts for that mailbox.
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
- [ ] Config file exists at `~/.hermes/env/codex_oauth_onboarding.env` with mode-specific fields.
|
||||
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
# Codex OAuth onboarding: token-lite automation flow (2026-05-10)
|
||||
|
||||
Goal: future runs should be script-driven and low-token. The agent should not paste every browser/5sim/mail intermediate state into chat. Use compact status lines, local logs, screenshots only on blockers, and one final summary.
|
||||
|
||||
## Core state machine
|
||||
|
||||
Use one orchestrator script that owns the flow and writes structured JSONL logs under a run directory, e.g.
|
||||
|
||||
```text
|
||||
~/.Hermes/workspace/runs/codex-oauth/<run_id>/
|
||||
run.json
|
||||
events.jsonl
|
||||
screenshots/
|
||||
cdp-targets.json
|
||||
fivesim-orders.jsonl
|
||||
```
|
||||
|
||||
Recommended phases:
|
||||
|
||||
1. `preflight`
|
||||
- Read `~/.hermes/env/codex_oauth_onboarding.env` once.
|
||||
- Redact secrets in all printed output.
|
||||
- Validate selected target mode, Codex2API origin/admin key, ClawEmail profile, browser proxy, and 5sim settings.
|
||||
- Verify proxy TCP + exit IP with short timeouts.
|
||||
2. `browser_start`
|
||||
- Kill only the dedicated CDP Chrome process for port 9223.
|
||||
- Start visible Chrome with a dedicated profile and `--proxy-server`.
|
||||
- Verify CDP `/json/version` and browser target.
|
||||
3. `oauth_start`
|
||||
- Generate a fresh target OAuth URL via target API.
|
||||
- For Codex2API, derive API origin if `CODEX2API_URL` includes `/admin/accounts`.
|
||||
- Open URL through CDP `/json/new`.
|
||||
4. `email_login`
|
||||
- Submit ClawEmail address.
|
||||
- Poll `mail-cli --profile codex-current mail list --fid 1 --limit 50 --json`.
|
||||
- Select latest OpenAI code by timestamp, not message-list order.
|
||||
- Fill code through CDP with React-safe value setter.
|
||||
- If OpenAI returns `account_deactivated`, stop the current account and mark run failed before buying 5sim numbers.
|
||||
5. `phone_verify`
|
||||
- Enter only if URL/text indicates `add-phone`.
|
||||
- Country priority: `FIVE_SIM_COUNTRY_ORDER` -> `FIVE_SIM_COUNTRY_ID` -> `vietnam` fallback only if both empty.
|
||||
- For each country, rotate up to `PHONE_VERIFICATION_REPLACEMENT_LIMIT` numbers.
|
||||
- Before buying/submitting, set visible OpenAI country selector to the intended country and verify it.
|
||||
- Buy SMS activation: `/v1/user/buy/activation/{country}/{operator}/openai`.
|
||||
- Submit local phone digits by visible paste/click or React-safe setter + visible submit.
|
||||
- If direct rejection (`无法向此电话号码发送验证码`), cancel activation and rotate.
|
||||
- If accepted/no direct rejection, poll `/v1/user/check/{activationId}`; if no SMS, click `重新发送`, poll again, retry resend at least once, then cancel/rotate.
|
||||
6. `oauth_callback`
|
||||
- After phone/email success, continue consent screen if present.
|
||||
- Capture/submit callback to target API.
|
||||
- Verify target platform shows account/session.
|
||||
7. `cleanup/report`
|
||||
- Cancel any active 5sim order on failure.
|
||||
- Save screenshot on blocker.
|
||||
- Final response includes: phase, outcome, account email, target session id (safe), current URL class, orders tried (activation IDs + country + outcome), screenshot path if blocker. No secrets/callback codes.
|
||||
|
||||
## Token-minimizing output rules
|
||||
|
||||
- Do not stream full DOM/body text to chat.
|
||||
- Scripts should print compact JSON or single-line events, e.g.
|
||||
|
||||
```json
|
||||
{"phase":"phone_verify","country":"argentina","attempt":1,"activation_id":"100...","result":"direct_reject"}
|
||||
{"phase":"phone_verify","country":"netherlands","attempt":2,"result":"accepted_no_sms_after_resend"}
|
||||
{"phase":"email_login","result":"account_deactivated","screenshot":"/tmp/...png"}
|
||||
```
|
||||
|
||||
- Store verbose outputs in files and only surface paths.
|
||||
- Only send screenshots for blockers or when BOSS asks.
|
||||
- Use `execute_code` or one `terminal` script for loops; avoid repeated tool calls for each 5sim poll.
|
||||
- Avoid `browser_snapshot`/full DOM dumps unless diagnosing a selector bug.
|
||||
- Prefer CDP helper functions that return exact state classes:
|
||||
- `login_page`
|
||||
- `email_code_page`
|
||||
- `add_phone_page`
|
||||
- `phone_code_page`
|
||||
- `account_deactivated`
|
||||
- `cloudflare_or_challenge`
|
||||
- `unknown`
|
||||
|
||||
## Correct 5sim country handling
|
||||
|
||||
Current env can include both:
|
||||
|
||||
```bash
|
||||
FIVE_SIM_COUNTRY_ORDER=argentina,netherlands,indonesia
|
||||
FIVE_SIM_COUNTRY_ID=
|
||||
PHONE_VERIFICATION_REPLACEMENT_LIMIT=3
|
||||
```
|
||||
|
||||
Do **not** ignore `FIVE_SIM_COUNTRY_ORDER`. Country order should be:
|
||||
|
||||
1. Split non-empty `FIVE_SIM_COUNTRY_ORDER` by comma and trim.
|
||||
2. Else use non-empty `FIVE_SIM_COUNTRY_ID`.
|
||||
3. Else fallback to original extension default `vietnam`.
|
||||
|
||||
Known OpenAI Chinese labels/prefixes used in visible country selector:
|
||||
|
||||
```text
|
||||
argentina 阿根廷 (+54)
|
||||
netherlands 荷兰 (+31)
|
||||
indonesia 印度尼西亚 (+62)
|
||||
vietnam 越南 (+84)
|
||||
england 英国 (+44)
|
||||
japan 日本 (+81)
|
||||
germany 德国 (+49)
|
||||
```
|
||||
|
||||
## Known pitfalls from completed run
|
||||
|
||||
- Proxy switch can invalidate OpenAI auth session; always be ready to regenerate target OAuth URL and redo email login.
|
||||
- `CODEX2API_URL` may be a web admin page; use origin for API calls.
|
||||
- CDP-only phone/country changes can desync React state and submit the number under `美国 (+1)`. Verify visible selector before buying/submitting.
|
||||
- A page returning `account_deactivated` after email-code submit means the current account is no longer usable; do not spend 5sim numbers on it.
|
||||
- Clicking/resending on a non-rejected phone may leave the flow at `电子邮件地址已验证`; if continuing same account, reopen current OAuth URL and redo login. If `account_deactivated`, stop.
|
||||
- Chrome stable may require visible profile/UI; keep a visible browser for auth pages.
|
||||
|
||||
## Suggested implementation artifact
|
||||
|
||||
Create or maintain a reusable local script, e.g.
|
||||
|
||||
```text
|
||||
/Users/chick/.Hermes/workspace/scripts/codex_oauth_onboarding_runner.py
|
||||
```
|
||||
|
||||
It should accept flags:
|
||||
|
||||
```bash
|
||||
python3 codex_oauth_onboarding_runner.py \
|
||||
--env ~/.hermes/env/codex_oauth_onboarding.env \
|
||||
--mode codex2api \
|
||||
--proxy-from-env \
|
||||
--run-once \
|
||||
--max-phone-countries-from-env \
|
||||
--screenshot-on-blocker
|
||||
```
|
||||
|
||||
The script should be idempotent enough to resume from `email_login` or `phone_verify` if the browser is already open, but should default to a clean profile per run.
|
||||
|
||||
## Final report template
|
||||
|
||||
```text
|
||||
结果:失败/成功
|
||||
阶段:email_login / phone_verify / oauth_callback
|
||||
邮箱:...
|
||||
代理:socks5://192.168.2.8:1085(出口 IP: ...)
|
||||
目标:Codex2API session_id=...
|
||||
手机验证:argentina[...], netherlands[...], indonesia[...]
|
||||
阻塞原因:account_deactivated / no_sms_after_resend / direct_reject_all / challenge
|
||||
截图:MEDIA:/path/to/screenshot.png
|
||||
下一步:...
|
||||
```
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
# OpenAI phone verification: configured 5sim country order and account-deactivated recovery (2026-05-10)
|
||||
|
||||
## Why this note exists
|
||||
|
||||
During a Codex2API OAuth onboarding continuation, the agent mistakenly looked only at `FIVE_SIM_COUNTRY_ID=` and treated the empty value as permission to fall back to the original extension default `vietnam`. BOSS corrected that the active configuration already had a country allow/order list:
|
||||
|
||||
```bash
|
||||
FIVE_SIM_COUNTRY_ORDER=argentina,netherlands,indonesia
|
||||
FIVE_SIM_COUNTRY_ID=
|
||||
FIVE_SIM_COUNTRY_FALLBACK=
|
||||
PHONE_VERIFICATION_REPLACEMENT_LIMIT=3
|
||||
```
|
||||
|
||||
Future runs must treat `FIVE_SIM_COUNTRY_ORDER` as the primary configuration, not as a secondary hint.
|
||||
|
||||
## Correct country precedence
|
||||
|
||||
Use this order when buying 5sim SMS activation numbers for OpenAI `add-phone`:
|
||||
|
||||
1. If `FIVE_SIM_COUNTRY_ORDER` is non-empty, split it by comma and try countries in that order.
|
||||
2. For each country in the order, try up to `PHONE_VERIFICATION_REPLACEMENT_LIMIT` replacement numbers unless BOSS overrides.
|
||||
3. Only if `FIVE_SIM_COUNTRY_ORDER` is empty, use `FIVE_SIM_COUNTRY_ID`.
|
||||
4. Only if both are empty, use the extension default such as `vietnam`.
|
||||
5. Do not silently switch outside the configured order; ask BOSS or update config first.
|
||||
|
||||
For the observed config, the correct sequence was:
|
||||
|
||||
```text
|
||||
argentina -> netherlands -> indonesia
|
||||
```
|
||||
|
||||
not Vietnam.
|
||||
|
||||
## 5sim/OpenAI retry behavior
|
||||
|
||||
- Use SMS activation only, not WhatsApp:
|
||||
- `GET /v1/user/buy/activation/{country}/{operator}/openai`
|
||||
- `GET /v1/user/check/{activationId}`
|
||||
- `GET /v1/user/cancel/{activationId}` when abandoning.
|
||||
- For direct OpenAI rejection (`无法向此电话号码发送验证码。请稍后重试或使用其他号码。`): cancel that activation and buy a new number in the same configured country until the per-country replacement limit is hit.
|
||||
- For a number that is not directly rejected: keep the activation, poll 5sim, click OpenAI `重新发送` if no SMS arrives, then poll again before canceling.
|
||||
- Always confirm the visible OpenAI country selector matches the 5sim country before buying/submitting. CDP-only field mutation can desync React state and cause the page to parse a non-US number as `美国 (+1)`.
|
||||
|
||||
## Country selector examples from the active config
|
||||
|
||||
Expected visible country labels:
|
||||
|
||||
```text
|
||||
argentina -> 阿根廷 (+54)
|
||||
netherlands -> 荷兰 (+31)
|
||||
indonesia -> 印度尼西亚 (+62)
|
||||
```
|
||||
|
||||
Use visible UI selection or a DOM click on the real option element. Do not mutate hidden state only.
|
||||
|
||||
## Email verification recovery caveat
|
||||
|
||||
After repeated phone-number attempts, canceling a non-rejected activation, or changing proxy, OpenAI may settle back to:
|
||||
|
||||
```text
|
||||
https://auth.openai.com/email-verification
|
||||
电子邮件地址已验证
|
||||
```
|
||||
|
||||
To continue, re-open a fresh Codex2API OAuth URL and complete email login again. When polling ClawEmail:
|
||||
|
||||
- Sort OpenAI mail candidates by message date; do not trust list order alone.
|
||||
- Confirm the code visible in the page input if submission stalls.
|
||||
- If `重新发送电子邮件` does not produce a new code and the page returns:
|
||||
|
||||
```text
|
||||
糟糕,出错了!
|
||||
验证过程中出错 (account_deactivated)。请重试。
|
||||
```
|
||||
|
||||
then the current OpenAI account is deactivated and cannot continue to phone verification. Stop trying phone numbers for that email; start a new ClawEmail sub-mailbox/register flow and then apply the configured country order above.
|
||||
|
||||
## 1085 rollback confirmation
|
||||
|
||||
BOSS later requested switching the browser proxy back from `socks5://192.168.2.8:1083` to `socks5://192.168.2.8:1085` and retrying the same mailbox before giving up. The correct check sequence was:
|
||||
|
||||
1. Patch `BROWSER_PROXY_SERVER=socks5://192.168.2.8:1085` in `~/.hermes/env/codex_oauth_onboarding.env`.
|
||||
2. Verify the LAN proxy before browser work:
|
||||
- `nc -vz -w 2 192.168.2.8 1085`
|
||||
- `curl --proxy socks5h://192.168.2.8:1085 --max-time 8 https://api.ipify.org?format=json`
|
||||
3. Kill the existing `remote-debugging-port=9223` Chrome and relaunch the isolated profile with `--proxy-server=socks5://192.168.2.8:1085`.
|
||||
4. Generate a fresh Codex2API OAuth URL from the API origin, not from `/admin/accounts`.
|
||||
5. Repeat email login and submit the newest OpenAI code.
|
||||
|
||||
Observed result: `1085` was reachable and showed exit IP `13.158.3.8`, but the same mailbox still returned `account_deactivated` after submitting the newest email code. This means proxy rollback did not rescue the mailbox; do not proceed to 5sim for an account that is already deactivated.
|
||||
|
||||
## Session outcome
|
||||
|
||||
The current account reached `account_deactivated` before the corrected country-order phone test could proceed. The learned correction is still valid: future runs must honor `FIVE_SIM_COUNTRY_ORDER=argentina,netherlands,indonesia` before considering defaults.
|
||||
Reference in New Issue
Block a user