# 项目完整链路说明

本文档面向 AI 与开发者，目标是让阅读者在最短时间内理解“项目做什么、怎么跑、数据怎么流、功能链路怎么串”，从而在新增功能时不漏逻辑、不误改边界。

使用建议：

1. 先阅读 [项目文件结构说明.md](c:/Users/projectf/Downloads/codex注册扩展/项目文件结构说明.md)
2. 再阅读本文
3. 最后阅读 [项目开发规范（AI协作）.md](c:/Users/projectf/Downloads/codex注册扩展/项目开发规范（AI协作）.md)

## 1. 项目目标

这是一个 Chrome 扩展，用于自动执行一整套 OpenAI / ChatGPT OAuth 注册与登录流程。

它的核心价值不是“打开一个页面点几个按钮”，而是把下面这些环节串成一条完整可恢复的自动化链路：

- 生成注册邮箱，或在接码开启时申请注册手机号
- 打开 ChatGPT / OpenAI 注册入口
- 提交邮箱/手机号和密码
- 轮询注册验证码（邮箱或短信）
- 填写姓名和生日
- 等待注册成功状态稳定
- 刷新 OAuth 链接并登录（邮箱或手机号）
- 轮询登录验证码（邮箱或短信）
- 自动确认 OAuth 同意页
- 把 localhost 回调提交到 CPA、SUB2API 或 Codex2API

## 2. 核心运行参与者

### 2.1 Sidepanel

[sidepanel/sidepanel.html](c:/Users/projectf/Downloads/codex注册扩展/sidepanel/sidepanel.html) + [sidepanel/sidepanel.js](c:/Users/projectf/Downloads/codex注册扩展/sidepanel/sidepanel.js) + [sidepanel/account-pool-ui.js](c:/Users/projectf/Downloads/codex注册扩展/sidepanel/account-pool-ui.js) + [sidepanel/update-service.js](c:/Users/projectf/Downloads/codex注册扩展/sidepanel/update-service.js) + [sidepanel/contribution-content-update-service.js](c:/Users/projectf/Downloads/codex注册扩展/sidepanel/contribution-content-update-service.js)

职责：

- 展示配置与步骤状态
- 接收用户输入
- 向后台发送命令
- 接收后台广播并更新 UI
- 动态渲染步骤列表
- 管理顶部“贡献”按钮与贡献模式主面板；贡献模式本身是 sidepanel 的运行态 UI 模式，不是新的 `panelMode` 来源
- 在贡献模式下复用同一套主自动流程启动，并在面板内展示贡献链路的 `OAUTH / 回调 / 总状态` 三块实时状态
- 在顶部“使用说明”按钮下方展示一个非强制的内容更新轻提示；公开仓库构建默认不再内置第三方公告 / 教程摘要来源，如需启用，需要自行接入对应内容服务
- 在 sidepanel 初始化和点击“自动”按钮前刷新一次贡献站公开内容摘要；点击“自动”时会先冻结当时输入或号池锁定的目标轮数，后续刷新、保存和后台状态回灌不能把启动目标改回旧的 1 轮；如果刷新失败，不阻塞主自动流程
- 在日志区通过“记录”按钮打开独立的账号记录覆盖层，并展示成功/失败/停止/重试统计与分页列表；同一轮同时出现邮箱和手机号时只显示一条组合记录，按本轮 `accountIdentifierType` 展示主身份，另一种身份作为副行显示
- 查询 GitHub Releases 并展示更新卡片；当前更新服务会区分 `Ultra`、历史 `Pro` 与 legacy `v` 三个版本族，排序时固定以 `Ultra` 为最高正式系列，同时会在读取缓存后重新排序，避免历史 `Pro` 或 `v` 版本误显示为比 `Ultra` 更新
- 展示一个单独的“接码”开关、注册方式 `signupMethod` 与“接码平台”下拉；接码平台当前支持 HeroSMS / 5sim / NexSMS。普通模式下开启接码后可把注册方式切到手机号注册，并在 OAuth 登录链路命中手机号登录验证码页时继续复用同一手机号续跑短信验证
- 侧栏在接码卡内提供一个独立运行态“注册手机号”输入框，位于接码订单运行状态下方；第 2 步自动拿到号码后立即回填，用户手动接管手机号注册时也可以直接改写这一个运行态槽位。这个输入框表达账号身份，不等同于接码订单；后续自动拉短信仍依赖 `signupPhoneActivation / signupPhoneCompletedActivation`
- 展示 `Plus 模式` 开关与 Plus 支付方式配置；支付方式支持 PayPal / GoPay / GPC，PayPal 展示账号池下拉与添加按钮，GoPay 展示手机号和 PIN，GPC 展示只读 API 地址、API Key、专用手机号、OTP 渠道、本地 OTP helper 开关与 URL、PIN；GPC 的相关按钮不再内置第三方购买或兑换链接，需要你自行配置服务入口；步骤列表切换为 Plus 模式步骤定义，普通模式的注册成功等待步骤不再显示或执行，登录验证码步骤会移动到 Plus 可见第 11 步
- 为 Hotmail / 2925 账号池复用同一套“添加账号 / 取消添加 / 批量导入 / 收起列表”表单交互；共享的显隐控制放在 `sidepanel/account-pool-ui.js`，各自 manager 只保留 provider 相关字段校验与业务操作

### 2.2 Background Service Worker

[background.js](c:/Users/projectf/Downloads/codex注册扩展/background.js)

职责：

- 扩展后台入口
- 装配所有模块
- 统一承接 runtime message
- 协调步骤执行
- 管理状态、自动运行、标签页与内容脚本通信

### 2.3 Content Scripts

[content](c:/Users/projectf/Downloads/codex注册扩展/content)

职责：

- 在目标网页上执行 DOM 交互
- 读取邮件内容或页面状态
- 将步骤成功/失败状态上报给后台

### 2.4 Helper / Utils / Provider Logic

分布在根目录和 `background/` 下。

职责：

- 抽离第三方邮箱 provider 的纯逻辑
- 抽离邮件匹配与验证码提取
- 抽离共享验证码流程、自动运行流程和运行时基础设施

## 3. 入口与装配关系

### 3.1 扩展入口

[manifest.json](c:/Users/projectf/Downloads/codex注册扩展/manifest.json) 声明：

- `background.service_worker = background.js`
- `side_panel.default_path = sidepanel/sidepanel.html`
- 多组内容脚本自动注入规则

### 3.2 背景层装配

[background.js](c:/Users/projectf/Downloads/codex注册扩展/background.js) 通过 `importScripts(...)` 依次加载：

- 共享数据与纯工具
- provider 纯逻辑
- 后台桥接层
- 后台共享流程层
- 后台运行时与消息路由层
- 步骤执行模块

因此 `background.js` 现在更像：

- 常量定义中心
- 模块依赖装配器
- 极少量保留函数
- Chrome 事件挂接入口

### 3.3 步骤注册

[data/step-definitions.js](c:/Users/projectf/Downloads/codex注册扩展/data/step-definitions.js) 提供共享步骤元数据，并会按 `plusPaymentMethod / signupMethod` 解析当前运行态应显示的步骤标题。  
[background/steps/registry.js](c:/Users/projectf/Downloads/codex注册扩展/background/steps/registry.js) 负责把“步骤元数据”映射到“步骤执行器”。

这意味着：

- 步骤顺序靠 `order`
- 步骤文件名靠语义
- 新增步骤时不需要重命名后续文件

### 3.4 日志步骤号链路

日志步骤号不再从日志正文里提取，也不再兼容旧的 `步骤 X` / `Step X` 文本解析。

- 后台统一通过 `addLog(message, level, { step, stepKey })` 写入结构化日志条目。
- 内容脚本通过 `log(message, level, { step, stepKey })` 上报结构化日志，`reportComplete` / `reportError` 的步骤号只走消息字段和日志元数据。
- [sidepanel/sidepanel.js](c:/Users/projectf/Downloads/codex注册扩展/sidepanel/sidepanel.js) 只读取 `entry.step` 渲染步骤标签；日志正文只作为正文展示，不参与步骤号判断。
- Plus 模式后半段复用普通执行器时，必须先解析当前可见步骤：`oauth-login=10`、`fetch-login-code=11`、`confirm-oauth=12`、`platform-verify=13`，再把该步骤号传入日志、完成信号和内容脚本 payload。
- 平台验证链路中，CPA / SUB2API / Codex2API 都按当前 `platform-verify` 可见步骤上报；SUB2API 和 Codex2API 的协议式分支由后台直接完成 callback 交换与账号创建，不再依赖后台页面内容脚本修正步骤号。

## 4. 状态与存储链路

### 4.1 `chrome.storage.session`

保存运行态：

- 当前步骤状态
- 当前 sidepanel UI 模式 `contributionMode`
- 贡献 OAuth 会话字段：
  - `contributionSessionId`
  - `contributionAuthUrl`
  - `contributionAuthState`
  - `contributionCallbackUrl`
  - `contributionStatus`
  - `contributionStatusMessage`
  - `contributionLastPollAt`
- OAuth 链接
- 当前轮冻结后的注册方式 `resolvedSignupMethod`
- 当前统一账号标识 `accountIdentifierType / accountIdentifier`
- 当前邮箱 / 密码
- 注册邮箱运行态 `registrationEmailState = { current, previous, source, updatedAt }`
- 当前手机号注册运行态 `signupPhoneNumber / signupPhoneActivation / signupPhoneCompletedActivation`
- 侧栏“注册手机号”输入框只同步运行态 `signupPhoneNumber / accountIdentifierType / accountIdentifier`，不属于持久配置，也不参与配置导入导出
- 普通邮箱身份写入会把 `accountIdentifierType / accountIdentifier` 改回邮箱，并清理旧的 `signupPhone*` 注册手机号运行态，避免后续 Step 4 / Step 8 被旧手机号污染而误走短信分支；但 Step 8 `add-email` 这类明确要求保留手机号身份的链路除外
- 邮箱写入统一通过 `setEmailState*` 或共享注册邮箱状态持久化 helper 同步更新 `email` 与 `registrationEmailState`；如果当前邮箱因 Step 8 恢复被清空，但还需要保留上一份 Duck / add-email 对比基线，则只清空 `current`，保留 `previous`；如果当前链路显式要求 `preserveAccountIdentity`，则在写入邮箱的同时保留 `signupPhone* / accountIdentifier*`
- 本轮联动自检重点：
  1. Step 2 一旦自动取到手机号，必须立刻广播并回填侧栏运行态手机号。
  2. sidepanel 重新加载后，必须从当前 state 恢复“注册手机号”文本框，而不是只恢复邮箱。
  3. 用户手动修改“注册手机号”时，只能改运行态身份槽位，不能误写入持久配置或配置导入导出；编辑中的值不能被后台旧 state 覆盖，执行步骤或跳过步骤前必须先保存当前输入框值。
  4. 第 4 步继续依赖当前手机号注册 activation 获取验证码；只有手机号字符串、没有 activation 时，只能作为页面填写来源，不能伪造成自动接码订单。
  5. 显示与身份同步统一以侧栏运行态手机号为准，避免 UI 空白和后台状态脱节。
- 第 8 步固定的验证码页显示邮箱 `step8VerificationTargetEmail`
- 当前手机号验证激活记录 `currentPhoneActivation`
- 可复用的手机号验证激活记录 `reusablePhoneActivation`
- localhost 回调地址
- SUB2API OAuth 运行态：`sub2apiSessionId / sub2apiOAuthState / sub2apiGroupId / sub2apiGroupIds / sub2apiDraftName / sub2apiProxyId`，用于把步骤 7 生成的会话、目标分组与代理选择传给步骤 10 / 13 的后台直连接口。
- 自动运行轮次信息
- 当前自动运行 session 标识 `autoRunSessionId`
- IP 代理运行态：`ipProxyApiPool / ipProxyAccountPool / ipProxyCurrent / ipProxyApplied*`，用于记录当前代理池、当前代理节点、PAC 接管结果、出口检测结果和诊断信息
- 标签注册表
- 最近打开的来源地址
- LuckMail 当前运行时选择
- 2925 当前选中的账号 ID `currentMail2925AccountId`（运行时会同步到持久配置，用于重开浏览器后恢复同一个号池账号）

补充：

- `content/mail-2925.js` 会把“当前验证码步骤启动会话”对应的已试验证码写入 `chrome.storage.session`，键为 `seen2925CodeState`；同一个 Step 4 / Step 8 会话里试过的验证码不会再次提交，新会话开始时会重置。

### 4.2 `chrome.storage.local`

保存持久配置与账号运行历史：

- CPA / SUB2API 配置
- Codex2API 配置
- IP 代理持久配置：`ipProxyEnabled`、服务商、模式、API 地址、服务商配置快照、账号列表、固定 Host / Port / Protocol / Username / Password、地区参数、session 与自动切换阈值
- Plus 模式开关 `plusModeEnabled`
- Plus 支付方式 `plusPaymentMethod`，GoPay 配置 `gopayPhone / gopayPin`，GPC helper 配置 `gopayHelperApiUrl / gopayHelperApiKey / gopayHelperPhoneMode / gopayHelperPhoneNumber / gopayHelperOtpChannel / gopayHelperLocalSmsHelperEnabled / gopayHelperLocalSmsHelperUrl / gopayHelperPin`；其中 GPC API 地址固定归一为 `https://your-gpc-helper-domain.example`
- PayPal 账号池配置 `paypalAccounts / currentPayPalAccountId`，以及供后台步骤兼容读取的 `paypalEmail / paypalPassword`
- 邮箱 provider 配置
- Cloud Mail / SkyMail API 配置：`cloudMailBaseUrl / cloudMailAdminEmail / cloudMailAdminPassword / cloudMailToken / cloudMailReceiveMailbox / cloudMailDomain / cloudMailDomains`
- Hotmail 账号池
- 2925 账号池
- 2925 是否启用号池模式 `mail2925UseAccountPool`
- 2925 当前选中的号池账号 ID `currentMail2925AccountId`
- Cloudflare / Temp Email 设置
- 注册方式 `signupMethod`
- 接码开关、接码平台 `phoneSmsProvider`，以及 HeroSMS / 5sim 各自的 API Key、国家/地区、价格上限和平台扩展设置
- iCloud 相关偏好
- LuckMail API 配置
- 自动运行默认配置
- OAuth 授权后链总超时开关 `oauthFlowTimeoutEnabled`：默认开启；关闭后会立即清空已存在的 OAuth 总预算 deadline，仅保留各步骤本地等待超时
- 账号运行历史 `accountRunHistory`（以统一账号标识为主键；邮箱账号保留邮箱主键，手机号注册保留手机号主键；同一轮中后续绑定的邮箱或手机号会写入同一条记录的副身份字段，保存最近一次状态：成功/失败/停止）
- 账号运行历史本地同步 helper 地址

注意：

- `contributionMode` 不属于持久配置，也不参与导入/导出；它只存在于运行态
- `panelMode` 当前表示 `cpa | sub2api | codex2api` 三个来源；贡献模式仍不是新的来源枚举

账号运行历史会默认通过 [scripts/hotmail_helper.py](c:/Users/projectf/Downloads/codex注册扩展/scripts/hotmail_helper.py) 的本地 helper 地址尝试同步写入 `data/account-run-history.json` 快照文件；如果 helper 没有运行，则静默跳过，不再要求用户先手动打开一个“本地同步”开关。
这条配置链路独立于 `mailProvider` 和 Hotmail 的接码模式。

### 4.3 状态广播

后台通过 runtime message 向 sidepanel 广播：

- `LOG_ENTRY`
- `STEP_STATUS_CHANGED`
- `DATA_UPDATED`
- `AUTO_RUN_STATUS`
- `ICLOUD_LOGIN_REQUIRED`
- `ICLOUD_ALIASES_CHANGED`

IP 代理模块在同步、切换、Change、出口探测和自动运行成功阈值切换后，会通过 `DATA_UPDATED` 广播 `ipProxyApplied*` 与当前代理池字段，sidepanel 只根据广播更新状态卡，不在 UI 层重新推断后台代理结果。

### 4.4 贡献模式链路

贡献模式的目标不是新增一个 provider，而是在 sidepanel 内开启一套“贡献账号”专用 UI，并把公开贡献服务并进原有 10 步主链：

1. 用户点击顶部 `贡献`
2. sidepanel 直接通过 `SET_CONTRIBUTION_MODE` 切换运行态，不再弹确认窗口
3. 进入贡献模式后会强制 `panelMode = cpa`，并临时清空运行态 `customPassword`、禁用运行态账号记录快照同步
4. sidepanel 隐藏 CPA 管理地址、管理密钥、SUB2API 配置、Codex2API 配置、自定义密码等普通模式配置，并禁用来源选择、配置菜单和记录入口
5. 用户点击 `开始贡献` 后，不再单独走一条旁路 OAuth 流程，而是直接复用顶部 `自动` 的同一套主自动流
6. 步骤 1~6 仍按原来的注册自动化执行
7. 当主流程进入步骤 7 时，后台改为调用公开接口 `POST <your-contribution-service>/oauth/api/start` 申请贡献登录地址，而不是去 CPA / SUB2API / Codex2API 来源刷新 OAuth
8. 步骤 7 拿到 `session_id / auth_url / state` 后，继续沿用原有登录链路进入授权页
9. 步骤 9 仍负责捕获 localhost callback；贡献模式下后台也会持续监听导航变化，必要时提前兼容处理 callback
10. 步骤 10 在贡献模式下不再打开 CPA 管理页，而是围绕公开贡献会话做 callback 提交兼容和最终状态确认；当状态进入 `auto_approved / auto_rejected / manual_review_required / expired / error` 时结束
11. 当前服务端如果返回“无需手动提交 callback”，扩展会把它视为兼容成功态，而不是报错
12. `已有认证文件？前往上传` 继续打开 `<your-contribution-service>/`
13. `退出贡献模式` 会清理贡献会话相关运行态，`重置` 只重置主流程状态，不会自动退出贡献模式

这条链路的关键边界是：

- 扩展会调用公开贡献 API，但这条调用被折叠进主 10 步自动链，而不是额外分出一条独立“贡献 OAuth 小流程”
- 扩展不会保存 token
- 扩展不会持有任何管理密钥
- 扩展代码中不能直接调用 `/v0/management/*`
- 扩展不会直接访问生产管理面或生产 auth-dir
- 安全边界仍然在服务端；扩展只负责公开 OAuth 贡献前端流程
- 退出贡献模式后，要恢复普通模式 UI，并恢复持久配置中的自定义密码等普通模式偏好

### 4.5 贡献内容更新提示链路

这条链路原本用于把外部公告 / 使用教程更新，映射成 sidepanel 顶部按钮下方的轻提示。公开仓库构建已默认关闭该外部内容源。

1. 贡献站公开暴露 `GET <your-contribution-service>/api/content-summary`
2. 接口返回 `announcement / tutorial` 两类内容的可见状态、更新时间，以及聚合后的 `promptVersion`
3. `sidepanel/contribution-content-update-service.js` 负责拉取这个摘要，并在本地缓存最近一次可用结果
4. `sidepanel.js` 在 sidepanel 初始化时会先拉一次摘要；如果当前 `promptVersion` 没被本地关闭过，就显示提示
5. 用户点击提示右侧 `X` 后，只会把当前 `promptVersion` 记入 `localStorage`，不会永久关闭整个能力
6. 当公告或教程再次更新，服务端返回新的 `promptVersion`，提示会重新出现
7. 用户点击“自动”按钮时，sidepanel 会先冻结当时输入或号池锁定的目标轮数，再在真正启动自动流程前刷新一次摘要，尽量让提示状态更及时；刷新、保存或后台旧状态回灌期间不会重新读取输入框导致轮数回退
8. 如果这次刷新失败，sidepanel 只记录警告并继续自动流程，不会因为提示链路故障阻塞主功能

这条链路的关键边界是：

- 它只消费公开摘要接口，不接触任何管理接口
- 它只影响顶部轻提示，不改变贡献模式主状态机
- 它使用 `localStorage` 做缓存和关闭版本记录，而不是写入 `chrome.storage.session/local`
- 它是辅助提示链路，不允许反向阻塞自动运行

## 5. 内容脚本通信链路

### 5.1 READY 机制

[content/utils.js](c:/Users/projectf/Downloads/codex注册扩展/content/utils.js) 在脚本加载后会发送 `CONTENT_SCRIPT_READY`。

后台收到后会：

- 注册当前来源对应的 tab
- 标记 ready
- 冲刷排队命令

### 5.2 队列与重试

[background/tab-runtime.js](c:/Users/projectf/Downloads/codex注册扩展/background/tab-runtime.js) 负责：

- `queueCommand`
- `flushCommand`
- `sendTabMessageWithTimeout`
- `sendToContentScriptResilient`
- `sendToMailContentScriptResilient`

这保证了：

- 页面切换导致脚本暂时失联时，后台不会立刻误判彻底失败
- 邮箱页或注册页能在注入恢复后继续执行
- 用户点击 Stop 后，标签完成等待、注入后的短暂延迟和内容脚本重试等待会尽快中断，不会因为底层轮询还在 sleep 而继续往下恢复

## 6. 手动步骤完整链路

### Step 1

文件：

- [background/steps/open-chatgpt.js](c:/Users/projectf/Downloads/codex注册扩展/background/steps/open-chatgpt.js)
- [content/signup-page.js](c:/Users/projectf/Downloads/codex注册扩展/content/signup-page.js)

流程：

1. 后台打开 ChatGPT 官网并复用/创建注册页标签
2. 等待注册页内容脚本就绪（仅做连接与注入确认）
3. 不在 Step 1 检测或点击注册入口
4. 标记 Step 1 完成

补充：

- 手动点击“跳过步骤 1”时，会级联将步骤 2~5 标记为跳过；其中已完成或正在运行的步骤不会被改写。

### Step 2

文件：

- [background/steps/submit-signup-email.js](c:/Users/projectf/Downloads/codex注册扩展/background/steps/submit-signup-email.js)

流程：

1. 解析本轮应使用的注册方式：默认邮箱注册，开启接码且普通模式下可切到手机号注册
2. 打开或复用注册页
3. 后台打开或切回注册页后，会先等待标签页完成加载并额外稳定 3 秒，再连接内容脚本探测入口；这用于避开官网按钮已在 DOM 中但仍处于隐藏态的异步窗口期
4. 内容脚本识别到官网“免费注册 / Sign up / Register”入口后，会先等待 3 秒再点击；如果点击后仍停留在官网入口页、没有进入邮箱或手机号输入页，会再次等待 3 秒后重试，最多额外重试 5 次，且每次真正点击前都会检查 Stop 状态
5. 邮箱注册时，如果注册弹窗默认停留在手机号输入模式，会先尝试点击 `继续使用电子邮件地址登录 / Continue using email address` 一类按钮切回邮箱输入模式
6. 邮箱注册分支：提交邮箱；邮箱输入框识别同时兼容本地化占位与 `aria-label`
7. 手机号注册分支：先点击官网“免费注册”并切换到手机号注册入口，必要时展开更多选项；确认手机号输入页就绪后，按“已有 signup activation > 手动填写的运行态手机号 > 接码平台新申请号码”的顺序决定手机号来源，避免重复买号。内容脚本会通过 `content/phone-country-utils.js` 共享工具按号码最长区号和平台国家名切换手机号国家下拉框，兼容 `+(44)` / `(+44)` / `+44` 等页面文案；必须确认可视下拉按钮的区号已同步后才填写并提交，若仍显示旧国家则停止提交
8. 以当前统一账号标识先写入一条“停止（流程尚未完成）”的记录占位；邮箱账号占位邮箱，phone-only 账号占位手机号
9. 等待身份提交后的真实落地页
10. 如果进入密码页，则继续执行 Step 3
11. 如果直接进入邮箱验证码页或手机验证码页，则自动跳过 Step 3 并进入 Step 4

### Step 3

文件：

- [background/steps/fill-password.js](c:/Users/projectf/Downloads/codex注册扩展/background/steps/fill-password.js)

流程：

1. 生成或读取密码
2. 更新运行态密码
3. 记录账号快照
4. 让内容脚本按当前账号身份填写密码
5. 内容脚本先上报 Step 3 完成信号
6. 上报完成后再异步点击提交，避免页面跳转打断响应通道
7. 延迟提交真正触发前会再次检查 Stop 状态，避免用户已停止时页面仍继续自动提交
8. 后台在真正确认 Step 3 完成前，会额外检查提交后是否切换页面；如果出现认证页 `Try again / 重试` 页面，或 `/email-verification` 上的 `405 / Route Error` 重试页，会先通过共享恢复逻辑最多自动点击 5 次 `重试` 尝试恢复，再继续后续链路
9. Step 3 收尾阶段如果页面切换导致旧内容脚本失联，后台会把单次消息等待收口到当前收尾预算内，优先尽快重试重连；若最终仍未恢复，则输出中文的步骤级错误，而不是直接暴露底层英文通信超时
10. 手机号注册时，如果 Step 3 收尾或 Step 4 准备验证码阶段在密码页检测到 `Incorrect phone number or password`，后台会把它视为当前注册手机号/密码不匹配：清空本轮 `signupPhoneNumber / signupPhoneActivation / signupPhoneCompletedActivation` 和手机号账号身份，回到 Step 1 重新获取号码并重开当前轮，避免继续在密码页重复点击

补充：

- 手机号注册如果在 Step 2 后直接落到短信验证码页、资料页或已登录页，Step 3 会按页面状态自动跳过，不再因为缺少邮箱而失败。
- 手动点击 Step 3 时，侧栏会先判断当前统一账号身份：如果本轮是手机号注册，或运行态已经写入 `accountIdentifierType = phone / signupPhoneNumber`，则直接执行 Step 3，不再尝试生成或校验注册邮箱，避免把手机号注册链路误切回邮箱注册链路。
- 后台 Step 3 会再次按 `resolveSignupMethod` 与统一账号身份兜底校验；手机号注册意图下如果缺少手机号，会明确提示先完成 Step 2 或填写注册手机号，不会回退使用旧邮箱。

### Step 4 / Step 8

文件：

- [background/steps/fetch-signup-code.js](c:/Users/projectf/Downloads/codex注册扩展/background/steps/fetch-signup-code.js)
- [background/steps/fetch-login-code.js](c:/Users/projectf/Downloads/codex注册扩展/background/steps/fetch-login-code.js)
- [background/verification-flow.js](c:/Users/projectf/Downloads/codex注册扩展/background/verification-flow.js)

这两步共享验证码主流程：

1. 先按注册阶段或认证页真实状态分流验证码通道：
   Step 4：邮箱注册走邮箱验证码，手机号注册走短信验证码
   Step 8：真实邮箱验证码页走邮箱验证码，真实手机验证码页才走短信验证码；如果先进入 `add-email`，先绑定邮箱再走邮箱验证码
2. 确定 provider
3. 必要时重发验证码
4. 轮询邮箱、短信 API 或对应 provider 通道
5. 提取验证码
6. 回填页面
7. 若页面拒绝，则重试或回退

补充行为：

- `2925` provider 会关闭 Step 4 / 8 的自动重发间隔 25 秒节流；每次“重新发送验证码”之间，会在邮箱页内部执行一轮固定 15 次的刷新轮询，不再因 OAuth 剩余时间预算而缩短。
- 当 provider 为 `2925` 时，Step 4 会优先直接打开当前 2925 邮箱页，并先比对页面顶部显示的邮箱地址是否与当前目标邮箱一致：如果一致，就直接复用当前已登录页面；如果不一致且启用了 2925 账号池，则会先清理 cookie 再登录当前选中的账号；如果不一致且未启用账号池，则直接复用现有停止逻辑结束流程。Step 8 不再额外承接这套登录态处理。
- `2925` 在执行自动登录后，如果登录页因为跳转或重载导致原内容脚本通信中断，后台不会立刻判失败；而是会等待当前标签页重新加载完成、重新确认内容脚本就绪后，再继续确认是否已经进入收件箱。这段登录恢复窗口当前按 2 分钟控制。
- 普通邮箱仍会携带 `filterAfterTimestamp` 做时间窗筛选；`2925` 在 Step 4 / Step 8 会固定使用“步骤开始时间向前回看 10 分钟”的时间窗，不再做“新旧邮件快照差集”比较，而是每次刷新后直接遍历当前列表中落在该固定时间窗内的匹配邮件。
- 自动重新发送验证码次数现在使用 sidepanel 里的单一“验证码重发”配置；普通邮箱仍按 25 秒间隔节流，Hotmail / 2925 不走这个 25 秒间隔。Step 4 若启用先请求新验证码，会先消耗一次当前步骤的自动重发次数。
- `LuckMail` 在 `/code` 接口暂未拿到新验证码时，会先点击一次认证页“重新发送验证码”，再等待 15 秒后继续轮询 `/code`，避免只空等不触发新邮件。
- `Cloud Mail` 既可以作为邮箱生成器直接创建 `local@domain`，也可以作为收件 provider 轮询 `cloudMailReceiveMailbox`；当 `Mail = Cloud Mail` 且邮箱生成器不是 Cloud Mail 时，会优先轮询配置的接收邮箱，避免错误轮询注册邮箱本身。
- 验证码提交重试上限当前为 15 次；页面明确拒绝验证码时，会在上限内继续拉取新验证码并重提。
- `2925` 内容脚本会把每一封实际打开检测的邮件立即删除；同一验证码步骤启动后，试过的验证码会按“步骤 ID + 启动时间”隔离缓存，不会在本次步骤里重复提交；如果再次遇到相同验证码，对应邮件也会在读取后立即删除，避免后续反复打开。
- `2925` 在 provide 模式下仍保持宽松匹配：只要邮件内容命中 ChatGPT / OpenAI 验证码过滤条件，就会尝试该邮件。
- `2925` 在 receive 模式下会恢复“弱目标邮箱匹配”：只有当邮件里显式出现了其他收件邮箱时才会跳过；如果邮件里没有明确写出邮箱，仍允许继续尝试该验证码。
- 手动点击 Step 4 重新执行时，后台会先检查 `signup-page` 认证页标签是否仍然存在；如果步骤 1 / 2 打开的认证页已经关闭，就会直接提示“请先执行步骤 1 或步骤 2，确保认证页仍然打开并停留在验证码页”，不会先重置后续步骤再报技术错误。
- 当验证码最终提交成功后，后台会异步向 2925 邮箱页发送 `DELETE_ALL_EMAILS`，执行“全选 + 删除”清理剩余邮件，不阻塞主流程。
- 如果 `2925` 邮箱页在轮询期间出现“子邮箱已达上限邮箱”，后台会记录当前时间，把当前 2925 账号禁用 24 小时，自动切到下一个可用账号并完成登录，然后直接报错结束当前尝试；如果 Auto 开启了自动重试，现有控制器会按原逻辑进入下一次尝试。

- Auto 模式下，普通 Step 4 失败仍会沿用当前邮箱回到 Step 1 重新开始当前轮；但若失败原因是 2925 主动要求“结束当前尝试”，则不会再回到 Step 1 重开，而是直接把错误抛给自动重试控制器。

### Step 5

文件：

- [background/steps/fill-profile.js](c:/Users/projectf/Downloads/codex注册扩展/background/steps/fill-profile.js)

流程：

1. 生成随机姓名和生日
2. 内容脚本填写资料；如果资料页出现顶部“我同意以下所有各项”总勾选框，会先自动勾选后再提交
3. 内容脚本点击“完成帐户创建”
4. 点击后立即上报 Step 5 完成，不再等待页面结果
5. 自动运行在进入 Step 6 前会等待当前页面完成加载并短暂稳定，避免 Step 5 刚点击提交、后续跳转尚未真正开始时就执行下一步；但仍不在 Step 5 阶段接管 ChatGPT 跳转或 onboarding 跳过

### Step 6

文件：

- [background/steps/wait-registration-success.js](c:/Users/projectf/Downloads/codex注册扩展/background/steps/wait-registration-success.js)

流程：

1. 固定等待 20 秒，等待注册成功状态和页面跳转稳定
2. 默认不删除 ChatGPT / OpenAI cookies
3. 如果侧栏第六步 `清 Cookies` 开关已开启，则在等待结束后清理 ChatGPT / OpenAI 相关 cookies
4. 完成后进入后续登录链路

### Step 7

文件：

- [background/steps/oauth-login.js](c:/Users/projectf/Downloads/codex注册扩展/background/steps/oauth-login.js)

流程：

1. 按当前来源刷新 OAuth 地址：
   - CPA：打开管理页并读取 OAuth 地址
   - SUB2API：后台直连 SUB2API 管理接口，登录后解析 openai 分组、可选代理，并调用 `/api/v1/admin/openai/generate-auth-url`
   - Codex2API：直接调用后台协议 `/api/admin/oauth/generate-auth-url`
2. 打开最新 OAuth 链接
3. 根据统一账号标识选择登录身份：
   - 邮箱账号：沿用现有邮箱登录链路
   - 手机号账号：优先探测登录页是否存在手机号登录入口，必要时展开更多选项或从邮箱页切到手机号页；进入手机号输入页后会复用 Step 2 的可视国家下拉框同步逻辑，按本轮手机号最长区号切到目标国家，填写后还会读回输入框确认已包含本地号码，避免英国 `+44` 号码仍按默认澳大利亚 `+61` 提交或只提交区号
4. 登录；如果进入密码页且当前有密码，则填写并提交密码；如果当前没有密码但检测到一次性验证码入口，则直接切换到一次性验证码登录
5. 确保真正进入验证码页或登录后续页；手机号登录允许以 `phone-verification` 作为 Step 7 的成功落点，手机号注册后的 OAuth 登录也允许以真实 `add-email` 作为 Step 7 完成并交给 Step 8 接管
6. 如果页面没有可用的手机号登录入口，则 Step 7 直接抛出明确业务错误，不再误进入 Step 8 邮箱轮询
7. 如果未进入验证码页，则按可恢复逻辑最多重试 3 次；但一旦当前失败已经明确进入 `https://auth.openai.com/add-phone`，则立即退出步骤 7 内部重试，不再继续第 2 / 3 次尝试
8. 自动运行一旦进入步骤 7 之后的链路，若后续步骤报错且认证页未进入 `https://auth.openai.com/add-phone`，则统一回到步骤 7 重新开始授权流程
9. CPA 回调阶段固定为步骤 9，不再支持“第七步回调”跳过步骤 7/8

贡献模式补充：

- 贡献模式下，步骤 7 不再从 CPA / SUB2API / Codex2API 来源刷新 OAuth，而是直接调用公开贡献接口 `/oauth/api/start`
- 贡献接口返回的 `auth_url` 会写回运行态 `oauthUrl`，后续步骤 7 / 8 / 9 继续复用现有授权链路

### Step 8

文件：

- [background/steps/fetch-login-code.js](c:/Users/projectf/Downloads/codex注册扩展/background/steps/fetch-login-code.js)

流程：

1. 先确认当前认证页真实状态，而不是只看 `accountIdentifierType`。
2. 当前是 `add-email`：
   - 调用 `resolveSignupEmailForFlow` 按需获取或生成本轮绑定邮箱；手机号注册不会在 Step 2 提前生成邮箱，只有真实进入 `add-email` 时才占用邮箱资源；这里会显式打开 `preserveAccountIdentity`
   - 如果邮箱生成器已经在生成阶段写回运行态，这里不会再重复覆盖 `registrationEmailState`；无论是复用已有邮箱，还是 Duck / Cloudflare / Cloud Mail / iCloud / 自定义邮箱池这类现场生成的新邮箱，都会走共享注册邮箱状态持久化规则保留手机号身份
   - 内容脚本发送 `SUBMIT_ADD_EMAIL` 填写邮箱并提交
   - 页面进入邮箱验证码页后，用绑定邮箱作为 `step8VerificationTargetEmail`
3. 当前是邮箱验证码页：
   - 对非 2925 provider，固定当前验证码页显示邮箱为本次 Step 8 的目标邮箱
   - 打开邮箱页或 API 轮询入口
   - 轮询登录验证码并回填
4. 当前是真实 `phone-verification` 页：
   - 才复用 OpenAI 专属手机号验证码流程，重新激活同一号码并轮询短信验证码
   - 在当前 `phone-verification` 页回填短信验证码
5. 如果登录验证码提交后页面进入 `add-phone / 手机号页`，则邮箱注册模式下交给后续 OAuth 后置手机号验证链路；手机号注册模式不把 `signupPhone*` 身份误当成官方 add-phone 订单
6. 如遇邮箱轮询类失败或显式的 `STEP8_RESTART_STEP7` 恢复错误，则按有限次数回到 Step 7 重试；若仍停在验证码页、手机验证码页或 add-email 页，则优先在当前链路重试
7. 获取到登录验证码后不再触发“刷新 OAuth 并重走 Step 7”的前置回放，直接在当前验证码页提交并继续进入 Step 9

补充：

- 对 `2925 provide` 而言，Step 8 仍不依赖验证码页显示邮箱做收件匹配，而是直接测试所有命中 ChatGPT / OpenAI 过滤条件的邮件。
- 对 `2925 receive` 而言，Step 8 会把当前目标注册邮箱一并传给 2925 内容脚本；只有当邮件里显式写出了其他邮箱时才会跳过，从而在不破坏历史兼容性的前提下，尽量降低误收验证码的概率。
- 对 `custom` provider 而言，Step 8 仍使用手动验证码确认弹窗；弹窗当前额外提供“出现手机号验证”按钮，点击后会直接抛出与真实 `add-phone` 页面一致的 fatal 错误，供 Auto 按既有 add-phone 分支继续下一邮箱。
- 当登录验证码提交后进入 `phone-verification` 页时，内容脚本会显式把该页面识别为“手机验证码页”，避免与邮箱验证码页混淆。
- 手机号注册身份本身不会让 Step 8 自动走短信；只有真实页面状态是 `phone_verification_page` 时才调用短信 helper。手机号注册登录后出现 `add-email` 时，Step 8 走“按需生成邮箱 -> 绑定邮箱 -> 邮箱验证码”链路。
- 如果手机号注册账号在 Step 8 的 `add-email` / 邮箱验证码阶段失败并回退到 Step 7，Step 7 会优先使用当前轮保留的注册手机号身份重新登录，避免误用尚未绑定成功的邮箱登录。
- `email_in_use` 会在 Step 8 当前步骤内仅清空 `registrationEmailState.current / state.email`，保留 `registrationEmailState.previous` 作为上一轮比较基线，再重新打开 `add-email` 获取新邮箱；`max_check_attempts` 不继续点击认证页 `重试`，只恢复当前 Step 8。

### Step 9

文件：

- [background/steps/confirm-oauth.js](c:/Users/projectf/Downloads/codex注册扩展/background/steps/confirm-oauth.js)

流程：

1. 监听 localhost callback
2. 准备 OAuth 同意页；如果页面已进入 `add-phone / phone-verification`，先切入 OpenAI 专属手机号验证流程
3. OpenAI 手机号验证流程会按当前 sidepanel 中保存的接码平台 `phoneSmsProvider` 选择 HeroSMS / 5sim / NexSMS，并使用对应平台的 API Key、国家/地区、价格上限申请或复用号码
4. 后台提交号码后，会按 `currentPhoneActivation.provider` 分发后续查码、完成、取消、ban、reuse：HeroSMS 走原 `getStatus / setStatus` 逻辑，5sim 走 `/v1/user/check|finish|cancel|ban|reuse`
5. 验证码被拒绝或长时间收不到短信时，会按平台决定重发、换号、ban/取消当前激活，必要时把自动流拉回 Step 7
6. 手机号验证完成后，如果认证页偶发进入资料页，内容脚本会复用 Step 5 的姓名生日填写逻辑补齐资料，再继续等待 OAuth 同意页
7. 手机号验证和可选资料页处理完成后，继续等待 OAuth 同意页出现
8. 尝试多轮点击“继续”
9. 一旦捕获 localhost callback，写入状态并完成步骤

补充：

- HeroSMS / 5sim / NexSMS 都归入 OpenAI 接码平台适配层；默认平台是 HeroSMS，5sim 产品码固定为 `openai`，operator 默认 `any`。该适配层暂不进入多注册 flow 的 core 通用服务，后续只有第二个真实 flow 也需要接码时再按实际复用点抽象。
- 号码当前最多复用 3 次成功注册；超过上限后会清空可复用激活记录，下次重新申请新号码。
- 5sim 的国家/地区使用字符串 slug（当前开放 `indonesia / thailand / vietnam`），HeroSMS 仍使用数字国家 ID（当前开放印度尼西亚/泰国/越南）。
- 如果同一个号码在重发短信后仍收不到验证码，后台会按当前平台取消或 ban 激活并换号，而不是把当前号码无限重试下去。
- 如果注册手机号验证码页在点击 `resend` 后跳到 `contact-verification` 的 `This page isn’t working / HTTP ERROR 500`，后台会把当前号码视为不可继续使用并结束当前激活，避免继续停留在坏页面上空轮询。

### Step 10

文件：

- [background/steps/platform-verify.js](c:/Users/projectf/Downloads/codex注册扩展/background/steps/platform-verify.js)

流程：

1. 校验 localhost callback 是否有效
2. 判断当前来源是 CPA、SUB2API 还是 Codex2API
3. CPA 打开相应后台；SUB2API / Codex2API 直接走协议分支，不打开后台页面
4. 提交回调地址或等价的授权码交换请求；SUB2API 会调用 `/api/v1/admin/openai/exchange-code` 交换授权码，再调用 `/api/v1/admin/accounts` 创建账号
5. 仅当出现精确成功徽标，且该徽标不是红色/错误态、页面上也没有同时可见的失败提示时，才判定成功
6. 识别 `认证失败:*`、`认证失败: timeout of 30000ms exceeded`、`回调 URL 提交失败: oauth flow is not pending` 等失败提示并立即报错
7. 完成平台侧验证
8. 追加账号运行历史成功记录，并携带本轮邮箱/手机号组合身份
9. 做成功后的清理与标记

Codex2API 补充：

- 步骤 7 直接调用 `POST /api/admin/oauth/generate-auth-url` 获取 `auth_url / session_id`
- 授权页仍沿用现有 OpenAI 登录、验证码、OAuth 同意页与 localhost callback 主链
- 步骤 10 直接调用 `POST /api/admin/oauth/exchange-code`，用 callback 中的 `code / state` 完成账号创建
- Codex2API 这条来源不新增 panel content script，也不依赖“添加账号 -> OAuth 授权 -> 生成授权链接”页面按钮 DOM

SUB2API 补充：

- 步骤 7 直接调用 `POST /api/v1/auth/login` 登录管理接口，再调用 `/api/v1/admin/groups/all` 校验目标 openai 分组；如果配置了默认代理，会读取 `/api/v1/admin/proxies/all?with_count=true` 并按名称或 ID 选择代理。
- 步骤 7 调用 `POST /api/v1/admin/openai/generate-auth-url` 获取 `auth_url / session_id / state`，并把目标分组 ID、代理 ID、草稿账号名写入运行态。
- 步骤 10 / Plus 第 13 步直接用 localhost callback 中的 `code / state` 调用 `POST /api/v1/admin/openai/exchange-code`，再用返回的 OpenAI token 信息调用 `POST /api/v1/admin/accounts` 创建 SUB2API 账号。
- SUB2API 主链路不再打开后台页面，也不再依赖 `content/sub2api-panel.js` 的 DOM 自动化；该内容脚本仅保留为旧后台页面路径的兼容能力。

贡献模式补充：

- 贡献模式下，步骤 10 不再打开 CPA 管理页
- 步骤 10 会先尝试提交已捕获的 callback URL，随后轮询公开贡献状态，直到进入最终态
- `auto_approved` 与 `manual_review_required` 视为主流程完成；`auto_rejected / expired / error` 视为当前轮失败

## 6.1 Plus 模式链路

Plus 模式通过 `plusModeEnabled` 开启，目标是在普通注册资料完成后，不执行普通模式 Step 6 注册成功等待，而是先完成 Plus 支付链路，再复用现有 OAuth 后半段。

Plus 模式可见步骤：

1. 第 1~5 步：沿用普通注册入口、邮箱、密码、注册验证码、资料填写链路。
2. 第 6 步 `创建 Plus Checkout`：PayPal / GoPay 打开已登录 ChatGPT 页面，通过 `/api/auth/session` 读取 accessToken，再请求 `https://chatgpt.com/backend-api/payments/checkout` 创建 `chatgptplusplan` 的 checkout session。PayPal 使用 `DE / EUR` 与 `https://chatgpt.com/checkout/openai_ie/{checkout_session_id}`；GoPay 使用 `ID / IDR` 与 `https://chatgpt.com/checkout/openai_llc/{checkout_session_id}`；GPC helper 模式改为把 accessToken 和 `gopayHelperPhoneMode` 提交到 `gopayHelperApiUrl` 的 `/api/gp/tasks`，并通过 `gopayHelperApiKey` 发送 `X-API-Key` 认证；自动模式只传 `phone_mode=auto`，手动模式额外传手机号、国家区号和 `otp_channel`，创建后保存 `task_id`；当前默认 GPC API 地址为 `https://your-gpc-helper-domain.example`。
3. 第 7 步 `填写账单并提交订阅`：PayPal / GoPay 仍走 checkout 页面与 Stripe iframe 自动化；GPC helper 模式不再操作 checkout iframe，而是轮询 `/api/gp/tasks/{task_id}`。自动模式只等待远端 `completed` 后继续后续步骤；手动模式根据远端 `api_waiting_for` 依次向 `/otp` 和 `/pin` 提交验证码与 PIN。若侧栏启用本地 OTP helper，后台会轮询 `gopayHelperLocalSmsHelperUrl` 的 `/latest-otp?phone=...&consume=1` 接口，按当前 GPC 手机号读取并消费当前 OTP 通道的验证码；未开启本地 helper 时才弹出手动 OTP 输入框。仓库内置的 `scripts/gpc_sms_helper_macos.py` 是 macOS Messages/本地通知兼容读取实现，其他通道需要提供兼容的本地 `/latest-otp` 或 `/otp` 接口。
4. 仅 PayPal / GoPay 会继续显示第 8 步：该步按当前支付方式显示为 `PayPal 登录与授权` 或 `GoPay 手机验证与授权`，底层 step key 仍为 `paypal-approve`。
5. 第 9 步 `订阅回跳确认` 仅在 PayPal / GoPay 模式下等待授权后回跳到 ChatGPT / OpenAI 页面，页面加载完成后固定等待 1 秒。GPC helper 模式在第 7 步任务完成后会直接进入 Plus 可见第 10 步 OAuth 登录。
6. 第 10 步：复用原 Step 7 OAuth 登录执行器，但状态和日志按 Plus 可见第 10 步记录。
7. 第 11 步：复用原 Step 8 登录验证码执行器，但状态和日志按 Plus 可见第 11 步记录。
8. 第 12 步：复用原 Step 9 OAuth 同意页点击和 localhost callback 捕获执行器，但状态和日志按 Plus 可见第 12 步记录。
9. 第 13 步：复用原 Step 10 CPA / SUB2API / Codex2API 平台回调验证执行器，但状态和日志按 Plus 可见第 13 步记录。

以上复用步骤的日志标签、完成信号和错误信号都以结构化 `step` 为准，不再允许靠日志正文中的固定步骤号修正显示。

隐藏与跳过规则：

- 普通模式 Step 6 `等待注册成功`：Plus 模式下隐藏且不执行，因为 Plus Checkout 创建会直接承接当前 ChatGPT 登录态。
- 原 Step 8 `获取登录验证码`：Plus 模式下移动到可见第 11 步，继续负责登录验证码页的收码与提交。
- 原 Step 9 不能跳过；它负责点击 OAuth 同意页并捕获 `localhostUrl`。
- 原 Step 10 不能跳过；它依赖 `localhostUrl` 完成平台侧账号创建或回调验证。

等待模型：

- Plus Checkout 和 PayPal 专用步骤使用“无限等待但可停止”的后台等待 helper。
- 每次页面加载完成后固定等待 1 秒，再继续下一次输入、点击或状态判断。
- 第一版不实时抓取外部地址网站；地址自动化只依赖本地 seed query 和 checkout 页面内置 Google 地址推荐。

## 7. 邮箱与 provider 链路

### 7.1 2026-04-17 补充：Gmail / 2925 统一别名邮箱链路

本轮将 Gmail 与 `2925 + provide` 的注册邮箱逻辑统一收敛为“共享别名邮箱链路”：

- 两者都先填写“基邮箱”
  - Gmail：`name@gmail.com`
  - 2925（仅 provide 模式）：`name@2925.com`
- 两者都允许两种入口：
  - 点击侧边栏按钮自动生成完整注册邮箱
  - 直接在“注册邮箱”输入框中手动填写完整邮箱
- 当 `mailProvider = 2925` 且 `mail2925Mode = receive` 时，不再走别名基邮箱链路，而是回退到普通“邮箱生成 / 手动填写注册邮箱”路线；2925 仅负责后续收信与登录态管理

当前行为约定：

1. sidepanel 会根据当前 provider 与 `mail2925Mode` 决定是否展示“别名基邮箱”输入框
2. 点击 `获取 / 生成` 时：
   - Gmail 生成 `name+tag@gmail.com`
   - 2925 仅在 provide 模式下生成 `name123456@2925.com`
3. 生成链路与手动保存邮箱都会统一维护 `registrationEmailState = { current, previous, source, updatedAt }`
   - `current` 表示当前准备提交或刚生成的注册邮箱
   - `previous` 表示最近一份仍可作为 Duck / Step 8 恢复对比基线的邮箱
4. Step 2 / Step 3 进入注册流程前，不再只看单一的 `state.email`
   - 如果当前完整邮箱仍与当前基邮箱兼容，则直接复用
   - 如果为空或不兼容，则按当前 provider / mode 重新生成
5. sidepanel 手动点击“获取 / 生成”时，会把输入框当前可见邮箱一并传给后台，优先作为 Duck 新地址比较基线；如果 UI 当前为空，再回退到 `registrationEmailState.current / previous`
6. 保存或执行 Step 3 时，如果手动填写的完整邮箱与当前 Gmail / 2925 provide 基邮箱不兼容，sidepanel 会直接拦截；`2925 receive` 不参与这条兼容性约束
7. auto-run fresh attempt reset 时，会保留：
   - `gmailBaseEmail`
   - `mail2925BaseEmail`

### 7.1.1 2026-04-23 补充：自定义邮箱池链路

本轮新增 `custom-pool` 生成方式，用于把一批已准备好的邮箱按顺序并入现有自动流，而不是继续为每种邮箱页面单独适配一套“新地址生成器”。

当前行为约定：

1. sidepanel 在 `邮箱生成` 中新增 `自定义邮箱池` 选项，并显示多行 `邮箱池` 输入框
2. 输入框中的邮箱会按“每行一个”归一化为数组，写入持久配置 `customEmailPool`
3. Auto 启动前会先保存当前配置；如果当前生成方式是 `custom-pool`，则自动把总轮数锁定为邮箱池长度
4. 后台在每个目标轮次开始前，会按 `targetRun` 从邮箱池读取对应邮箱，并写入当前 `state.email`
5. 同一目标轮次里的失败重试会继续复用该轮邮箱，不会提前跳到下一个
6. 如果当前目标轮次超出了邮箱池数量，后台会直接报错提示数量不一致
7. 自定义邮箱池只负责“本轮注册邮箱分配”，实际收码仍由当前 `mailProvider` 对应的既有链路负责

### 7.1.2 2026-04-23 补充：自定义邮箱服务号池链路

本轮给 `mailProvider = custom` 这条链路补上了“号池式注册邮箱分配”，目标是让“手动验证码模式”也能稳定跑多轮不同邮箱，而不是每轮都手动改一次注册邮箱。

当前行为约定：

1. sidepanel 在 `邮箱服务 = 自定义邮箱` 时，会额外显示 `自定义号池` 文本框
2. 文本框中的邮箱会按“每行一个”归一化为数组，写入持久配置 `customMailProviderPool`
3. 如果当前 `Mail = 自定义邮箱` 且号池不为空，Auto 启动前会把总轮数锁定为号池长度
4. 后台在每个目标轮次开始前，会按 `targetRun` 从 `customMailProviderPool` 读取对应邮箱，并写入当前 `state.email`
5. 只要当前邮箱还没成功认证、也没出现 `add-phone / 手机号验证`，Auto 就会继续复用该邮箱重试，不会提前切到下一个
6. 只有当当前邮箱成功完成整轮，或明确进入 `add-phone / 手机号验证` fatal 分支时，Auto 才会切到号池中的下一个邮箱
7. 如果当前目标轮次超出了号池数量，后台会直接报错提示数量不一致
8. 这条链路只影响“注册邮箱分配”；Step 4 / Step 8 仍然走 `custom` provider 既有的手动验证码确认逻辑

### 7.2 共享模块分工

- `managed-alias-utils.js`
  统一承接 Gmail / 2925 的：
  - 基邮箱解析
  - 完整邮箱兼容性判断
  - 别名邮箱生成
  - UI 文案输出

- `background/registration-email-state.js`
  统一维护当前注册邮箱、上一份比较基线邮箱、来源与更新时间，并提供 Duck 生成前解析比较基线、Step 8 清空当前邮箱但保留历史基线，以及 Step 8 `add-email` 写入邮箱时保留手机号身份的共享工具。

- `background/generated-email-helpers.js`
  在原有 Duck / Cloudflare / iCloud / Cloudflare Temp Email 生成链路之外，新增 Gmail / 2925 的共享生成接入；Duck 生成前会按“侧栏当前可见邮箱 -> `registrationEmailState.current` -> `registrationEmailState.previous`”的顺序解析比较基线。

- `background/signup-flow-helpers.js`
  负责在真正提交注册邮箱前做“复用已有完整邮箱 / 重新生成”的最终决策，并避免把生成阶段已经落库的邮箱再次重复写入运行态。

### 7.1 生成邮箱

文件：

- [background/generated-email-helpers.js](c:/Users/projectf/Downloads/codex注册扩展/background/generated-email-helpers.js)

支持：

- Duck
- Cloudflare
- Cloudflare Temp Email
- Cloud Mail
- 自定义邮箱池
- iCloud 隐私邮箱

补充：

- 所有生成器在返回邮箱后都会统一调用共享注册邮箱状态持久化入口回写运行态：普通邮箱流等价于 `setEmailState(..., { source })`，而 Step 8 `add-email` 的手机号身份流会在更新 `email / registrationEmailState` 的同时保留 `signupPhone* / accountIdentifier*`，避免 sidepanel 短暂看到手机号被清空。
- Duck 生成链路不会再直接拿“生成前瞬间读到的输入框值”当唯一基线，而是优先使用 sidepanel 当前可见邮箱，再回退到共享的注册邮箱运行态基线，避免生成前后地址对比失效。

### 7.1.1 Cloud Mail

组成：

- [cloudmail-utils.js](c:/Users/projectf/Downloads/codex注册扩展/cloudmail-utils.js)
- [background/cloudmail-provider.js](c:/Users/projectf/Downloads/codex注册扩展/background/cloudmail-provider.js)
- [background/verification-flow.js](c:/Users/projectf/Downloads/codex注册扩展/background/verification-flow.js)
- [sidepanel/sidepanel.html](c:/Users/projectf/Downloads/codex注册扩展/sidepanel/sidepanel.html)
- [sidepanel/sidepanel.js](c:/Users/projectf/Downloads/codex注册扩展/sidepanel/sidepanel.js)

链路：

1. 侧栏展示 Cloud Mail 的 API 地址、管理员邮箱、管理员密码、接收邮箱与生成域名配置。
2. 当 `邮箱生成 = Cloud Mail` 时，后台用管理员账号获取 Token，并通过 `/api/public/addUser` 创建随机本地名前缀邮箱。
3. 当 `Mail = Cloud Mail` 且邮箱生成器不是 Cloud Mail 时，Step 4 / Step 8 优先轮询 `cloudMailReceiveMailbox`，用于接住 Duck / Cloudflare / iCloud 等转发来的验证码。
4. 邮件读取统一走 `/api/public/emailList`，返回数据先经 `cloudmail-utils.js` 归一化，再复用现有验证码匹配与时间窗过滤逻辑。
5. 如果 Token 失效，后台会尝试重新登录刷新 Token 后重试当前 API 请求。

### 7.2 Hotmail

组成：

- [hotmail-utils.js](c:/Users/projectf/Downloads/codex注册扩展/hotmail-utils.js)
- [microsoft-email.js](c:/Users/projectf/Downloads/codex注册扩展/microsoft-email.js)
- [scripts/hotmail_helper.py](c:/Users/projectf/Downloads/codex注册扩展/scripts/hotmail_helper.py)
- [sidepanel/hotmail-manager.js](c:/Users/projectf/Downloads/codex注册扩展/sidepanel/hotmail-manager.js)
- [sidepanel/account-pool-ui.js](c:/Users/projectf/Downloads/codex注册扩展/sidepanel/account-pool-ui.js)

模式：

- API 对接
- 本地 helper

补充：

- 本地 helper 除了收信与验证码读取，还提供账号记录 JSON 快照同步接口。
- 账号运行历史快照会按默认本地 helper 地址自动尝试同步，不再要求用户手动打开独立开关，也不再绑定 Hotmail 的本地助手模式。
- sidepanel 中 Hotmail 账号池的新增表单默认收起，头部通过共享按钮切换“添加账号 / 取消添加”；表单显隐、按钮文案切换、清空与聚焦都复用 `sidepanel/account-pool-ui.js`，不在 Hotmail manager 内重复实现一套。

### 7.3 LuckMail

组成：

- [luckmail-utils.js](c:/Users/projectf/Downloads/codex注册扩展/luckmail-utils.js)
- LuckMail 相关后台领域逻辑仍在 [background.js](c:/Users/projectf/Downloads/codex注册扩展/background.js)

### 7.4 163 / 163 VIP / 126 网页邮箱

组成：

- [mail-provider-utils.js](c:/Users/projectf/Downloads/codex注册扩展/mail-provider-utils.js)
- [content/mail-163.js](c:/Users/projectf/Downloads/codex注册扩展/content/mail-163.js)

行为约定：

- `163`、`163 VIP`、`126` 都走同一条“网易网页邮箱”验证码链路。
- sidepanel 只负责切换 provider 与展示登录入口；后台根据 provider 选择对应网页邮箱首页。
- 内容脚本来源统一归类到 `mail-163`，这样 Step 4 / Step 8 继续复用同一套验证码读取与邮件清理逻辑。
- `manifest.json` 需要同时覆盖：
  - `https://mail.163.com/*`
  - `https://webmail.vip.163.com/*`
  - `https://mail.126.com/*`

### 7.5 2925 账号池

组成：

- [mail2925-utils.js](c:/Users/projectf/Downloads/codex注册扩展/mail2925-utils.js)
- [background/mail-2925-session.js](c:/Users/projectf/Downloads/codex注册扩展/background/mail-2925-session.js)
- [sidepanel/mail-2925-manager.js](c:/Users/projectf/Downloads/codex注册扩展/sidepanel/mail-2925-manager.js)
- [sidepanel/account-pool-ui.js](c:/Users/projectf/Downloads/codex注册扩展/sidepanel/account-pool-ui.js)

职责：

- `mail2925-utils.js`
  统一承接 2925 账号池的归一化、冷却期判断、可用账号挑选、导入格式解析与列表更新。
- `background/mail-2925-session.js`
  统一承接 2925 账号池的持久化、当前账号切换、cookie 清理登出、网页登录态确认、自动登录，以及命中“子邮箱已达上限邮箱”后的 24 小时禁用与自动切号。
- `sidepanel/mail-2925-manager.js`
  负责 2925 账号池的新增、导入、切换、手动登录、禁用、清冷却与删除。
- `sidepanel/account-pool-ui.js`
  负责 Hotmail / 2925 账号池共用的新增表单显隐、头部按钮文案切换、清空表单与首字段聚焦；2925 manager 不再单独维护另一套表单开关状态机。

链路：

1. 用户在 sidepanel 的 2925 账号池中保存 `email / password`
2. sidepanel 中会单独展示 `提供邮箱 / 接收邮箱` 模式切换，以及独立的 `2925 号池` 开关 / 当前账号下拉框；这样即使切到 receive 模式，账号池设置也不会被别名基邮箱行一起隐藏
3. 只有当 sidepanel 中的 `mail2925UseAccountPool` 开关开启时，provide 模式下的别名基邮箱才会优先取当前账号池选中的 2925 账号邮箱；关闭时会回退到原来的手填 `mail2925BaseEmail`
4. 手动点击 `登录` 或自动流程进入 Step 4 前，后台会先打开当前 2925 邮箱页，并读取页面顶部当前邮箱地址：如果仍停留在收件箱且顶部邮箱与目标邮箱一致，则直接复用；如果顶部邮箱不一致且启用了号池模式，则先清理 cookie 后登录当前选中的账号；如果顶部邮箱不一致且未启用号池模式，则直接调用现有停止逻辑结束流程；如果页面跳到登录页，则仍然只有号池模式开启时才自动登录
5. 一旦轮询期间出现“子邮箱已达上限邮箱”，后台会先判断是否启用了号池模式：若已启用且还有下一个可用账号，则把当前账号禁用 24 小时并自动切到下一个账号重新登录；若未启用，则直接调用现有停止逻辑结束流程
6. 如果登录页已经识别到账号密码输入框，内容脚本会在填完账号密码后额外等待 1 秒再点击登录；若点击登录后 40 秒内仍未进入收件箱，且当前正处于自动运行中，则后台会直接复用现有 `requestStop()` 停止链路，把整个自动流程停成和用户手动点击“停止”一致的状态；这类情况常见于图片验证、行为验证或其他阻断登录的中间页
7. 如果没有下一个可用账号，或当前未启用号池模式，则不会继续消耗自动重试次数，而是直接复用现有 `requestStop()` 停止链路，把整个自动流程停成和用户手动点击“停止”一致的状态
8. sidepanel 中 2925 账号池的新增表单也走与 Hotmail 相同的共享交互：默认收起，头部按钮切换“添加账号 / 取消添加”，操作行右侧提供“批量导入”，保存成功后自动收起并清空。
9. 当 2925 号池模式开启时，当前选中的号池邮箱会同步回写到同一个 `mail2925BaseEmail` 字段；因此用户切换号池账号后，即使再次关闭号池模式，也会直接沿用刚才选中的邮箱作为手动基邮箱，无需重新输入。

### 7.5.1 2925 双模式维护约定

这是后续维护 `2925` 时最容易被重新打散的一段链路，建议按下面的职责边界理解：

- UI 层：
  - `sidepanel/sidepanel.html`
    - 负责展示 `提供邮箱 / 接收邮箱`
    - 负责展示独立的 `2925 号池` 配置行
    - 不负责决定“2925 是否参与别名邮箱生成”
  - `sidepanel/sidepanel.js`
    - 只负责把当前 `mail2925Mode`、号池开关、当前账号选择同步到 state / runtime message
    - 只负责用共享规则决定当前文案、显隐和前端校验
    - 不应再次复制一套“provide / receive 对应什么行为”的业务判断

- 共享规则层：
  - `managed-alias-utils.js`
    - 负责 `mail2925Mode` 归一化
    - 负责判断 `2925` 当前是否属于“别名邮箱 provider”
    - 负责保证 `2925` 只有在 `provide` 模式下才进入共享别名邮箱链路
    - 如果未来要新增第三种模式，应该先改这里，再改 sidepanel / background 接线

- 注册邮箱生成层：
  - `background/generated-email-helpers.js`
    - 只负责根据当前 provider / mode 选择“共享别名邮箱”还是“普通邮箱生成器”
    - 不负责定义 `provide / receive` 的语义本身
  - `background/signup-flow-helpers.js`
    - 只负责真正提交前的“复用已有邮箱 / 重新生成邮箱”
    - 这里看到的 `isGeneratedAliasProvider(state)` 已经包含 `mail2925Mode` 语义，不应该再手写 `state.mail2925Mode === 'provide'`

- 收信与验证码层：
  - `background/verification-flow.js`
    - 负责决定是否给 2925 内容脚本下发 `mail2925MatchTargetEmail`
    - 当前约定：仅 `receive` 模式开启弱目标邮箱匹配
  - `content/mail-2925.js`
    - 负责真正执行“弱目标邮箱匹配”
    - 当前约定：只有邮件里显式出现了其他邮箱才跳过；若邮件里没有写邮箱，仍允许继续尝试
    - 这样做的目的，是在不破坏历史兼容性的前提下，降低 receive 模式误收验证码的概率

- 账号池与登录态层：
  - `background/mail-2925-session.js`
    - 不关心 `provide / receive` 哪个负责生成注册邮箱
    - 只关心当前是否允许自动登录、是否需要切号、是否命中上限、是否需要停止自动流程
    - 因此后续如果只改别名生成语义，不应把这层重新卷入 provider 判定

后续如果再改 `2925`，建议最少按下面顺序自检：

1. `managed-alias-utils.js` 是否仍是 `provide / receive` 的唯一共享语义来源
2. `sidepanel/sidepanel.js` 是否只做接线，没有复制业务规则
3. `background/generated-email-helpers.js` / `background/signup-flow-helpers.js` 是否仍然只做调度，不重复写模式语义
4. `background/verification-flow.js` 与 `content/mail-2925.js` 的“弱目标邮箱匹配”是否保持同步
5. `项目文件结构说明.md` 与当前文件是否已同步更新

### 7.6 iCloud

组成：

- [icloud-utils.js](c:/Users/projectf/Downloads/codex注册扩展/icloud-utils.js)
- [content/icloud-mail.js](c:/Users/projectf/Downloads/codex注册扩展/content/icloud-mail.js)

配置：

- `icloudHostPreference` 只决定 iCloud 登录、别名管理和 iCloud Mail 收件箱 Host。
- `icloudFetchMode` 决定生成注册邮箱时复用已有 Hide My Email 别名，还是始终创建新别名。
- `icloudTargetMailboxType = icloud-inbox` 时，Step 4 / Step 8 直接打开 iCloud Mail 收件箱轮询验证码。
- `icloudTargetMailboxType = forward-mailbox` 时，注册邮箱仍由 iCloud Hide My Email 生成，但 Step 4 / Step 8 改为打开 `icloudForwardMailProvider` 指定的转发目标邮箱收码；当前支持 `qq / 163 / 163-vip / 126 / gmail`。
- Step 8 `add-email` 里无论 iCloud 最终是复用未使用别名，还是新建并保留 Hide My Email 别名，选中的邮箱都会先走共享注册邮箱状态持久化；当当前账号主身份仍是手机号时，这一步不会先广播清空手机号再恢复。

Hide My Email 获取与管理链路：

1. 后台优先通过 `setup.icloud.com*/setup/ws/1/validate` 解析 `premiummailsettings.url`，并记录最近可用服务节点。
2. 如果后台请求受 401 / 403 / 409 / 421 / 429 / 5xx、CORS、超时或地址空间限制影响，会优先尝试在已打开或自动创建的 `www.icloud.com*/mail/` 页面主上下文中重放请求。
3. `maildomainws` 请求会补充 `clientBuildNumber / clientMasteringNumber / clientId / dsid` 查询参数，写请求使用 `text/plain;charset=UTF-8`，以兼容 iCloud 网页端接口。
4. 别名列表加载成功后会缓存到 `icloudAliasCache`；遇到短暂上下文波动时，列表展示会按“最近缓存 -> 历史缓存 -> 本地已用/保留记录”回退。
5. 生成新别名后若 `reserve` 返回鉴权或网络异常，会先回查列表确认候选别名是否已经创建；必要时刷新服务节点重试一次，最后才进入可复用别名回退或暂停等待用户处理。
6. 自动运行获取 iCloud 别名时，检测到会话或网络上下文异常后会停止重复消耗获取重试次数，转入等待邮箱状态，避免连续请求放大 iCloud 风控或上下文抖动。

维护约定：

- iCloud 转发目标邮箱 provider 的 label、URL、source、inject 配置统一由 `mail-provider-utils.js` 输出。
- `background.js` 只根据 `icloudTargetMailboxType` 选择 iCloud Mail 收件箱或共享转发邮箱配置，不再在主文件内硬编码各 provider 地址。
- `sidepanel/sidepanel.js` 只负责展示、保存和回显目标邮箱类型 / 转发邮箱 provider，不重复定义 provider 业务清单。
- iCloud 登录态提示必须区分“真实未登录”和“已登录但请求上下文波动”；不能把所有 401 / 403 / 421 都直接提示为未登录。
- iCloud 别名缓存只用于短暂失败回退，不应改变最终的已用 / 保留状态来源；状态仍以 `manualAliasUsage`、`preservedAliases` 与最新线上列表合并后的结果为准。

### 7.7 IP Proxy

组成：

- [background/ip-proxy-core.js](c:/Users/projectf/Downloads/codex注册扩展/background/ip-proxy-core.js)
- [background/ip-proxy-provider-711proxy.js](c:/Users/projectf/Downloads/codex注册扩展/background/ip-proxy-provider-711proxy.js)
- [sidepanel/ip-proxy-panel.js](c:/Users/projectf/Downloads/codex注册扩展/sidepanel/ip-proxy-panel.js)
- [sidepanel/ip-proxy-provider-711proxy.js](c:/Users/projectf/Downloads/codex注册扩展/sidepanel/ip-proxy-provider-711proxy.js)
- [docs/ip-proxy-module.md](c:/Users/projectf/Downloads/codex注册扩展/docs/ip-proxy-module.md)

职责边界：

- `background/ip-proxy-core.js` 负责代理条目解析、运行态代理池、PAC 应用与清除、`webRequest.onAuthRequired` 代理鉴权回填、出口探测和失败时的目标站点 fail-close 规则。
- `background/ip-proxy-provider-711proxy.js` 只承接 711Proxy 的账号串参数规则，负责把 `region / session / sessTime` 叠加到最终用户名。
- `background/message-router.js` 只暴露 `REFRESH_IP_PROXY_POOL / SWITCH_IP_PROXY / CHANGE_IP_PROXY_EXIT / PROBE_IP_PROXY_EXIT` 消息，不在路由层复制代理解析逻辑。
- `sidepanel/ip-proxy-panel.js` 负责 UI 状态渲染、表单快照、按钮动作和 711 参数输入联动；`sidepanel/sidepanel.js` 只保留配置保存、事件绑定和广播接线。

基础链路：

1. 用户在 sidepanel 打开 `IP代理`，选择服务和账号模式。
2. 首版只开放 `711proxy + account` 主路径；API 模式和账号列表入口保留但默认禁用。
3. 用户填写固定账号的 Host / Port / Protocol / Username / Password，可选填写地区、session 和时长。
4. 点击 `同步` 后，sidepanel 发送 `REFRESH_IP_PROXY_POOL`，后台解析当前账号为代理池并调用 `chrome.proxy.settings.set` 写入 PAC。
5. 如果代理需要账号密码，后台通过 `webRequest.onAuthRequired` 对代理 407 challenge 回填用户名和密码。
6. PAC 成功应用后，后台关闭 fail-close 规则；如果配置缺失、应用失败或出口探测失败，则开启只覆盖 OpenAI / ChatGPT 目标域的阻断规则，避免误以为已经走代理。
7. `检测出口` 会优先用页面上下文探测公网 IP，再用后台请求兜底，并把出口 IP、地区、探测来源和诊断摘要写回运行态。
8. `下一条` 根据当前代理池 index 切换节点；`Change` 在 711 session 场景下强制重绑代理链路，用于刷新出口。
9. 自动运行成功轮数命中 `ipProxyPoolTargetCount` 时，后台会尝试自动切到下一条代理；只有代理候选数量大于 1 时才执行切换。

维护约定：

- 代理解析、PAC、鉴权和出口检测必须留在后台模块，不应继续堆进 `background.js`。
- UI 层不能凭输入值直接判断“代理已接管”，只能展示后台返回的 `ipProxyApplied*` 状态。
- 新增代理服务商时，应优先新增 provider 规则模块，并让共享解析/运行态继续走 `background/ip-proxy-core.js`。
- 修改代理字段、权限或链路时，需要同步更新 [docs/ip-proxy-module.md](c:/Users/projectf/Downloads/codex注册扩展/docs/ip-proxy-module.md)、当前完整链路说明和结构说明。
## 8. 自动运行完整链路

文件：

- [background/auto-run-controller.js](c:/Users/projectf/Downloads/codex注册扩展/background/auto-run-controller.js)

流程：

1. 读取总轮数与模式；sidepanel 在点击“自动”瞬间先冻结目标轮数，后台确认进入同一目标轮数前会忽略旧 active 状态里的过期 `autoRunTotalRuns`
2. 为本轮自动流程分配唯一 `autoRunSessionId`
3. 计算是否从中断点继续
4. 每轮执行前重置必要运行态
   - 如果当前 `Mail = 自定义邮箱` 且配置了 `customMailProviderPool`，会先按当前目标轮次把号池中的对应邮箱写回运行态
   - 如果当前生成方式是 `custom-pool`，会先按当前目标轮次把邮箱池中的对应邮箱写回运行态
5. 执行 `runAutoSequenceFromStep`
   - 步骤 7 内部仍保留登录态恢复的有限重试，但 `add-phone / 手机号页` 属于立即跳出的不可重试错误
   - 步骤 8 若在验证码提交后进入 `add-phone / 手机号页`，会直接抛出 fatal 错误，不再先标记步骤成功
   - 一旦进入步骤 7~10，遇到普通报错且认证流程未进入 `add-phone`，则自动回到步骤 7 无限重开
   - 如果命中 `add-phone / 手机号页` 这类 fatal 错误，则不会再做当前轮的内部重试；当开启自动重试/跳过失败时，会直接结束当前轮并继续下一轮，而不是把整条自动流程暂停
   - 如果是手动停止，则立即退出自动流程，不会再触发“回到步骤 7 重开”
6. 如果失败，根据设置决定：
   - 立即停止
   - 当前轮重试
   - 下一轮继续
7. 当前轮最终失败时，写入账号记录，并在自动重试链路中累计重试次数
   - 手动停止与自动停止会写入“停止”状态（若后续同邮箱成功/失败，会被覆盖为最新状态）
8. 如果配置了线程间隔，则挂计时计划；计时计划会带上当前 `autoRunSessionId`
9. 旧 timer / 旧 alarm / 旧恢复入口只有在 session 仍有效时才允许恢复执行；Stop 会立即使当前 session 失效，防止“停止后旧倒计时又把流程重新拉起”
10. 如果启用 IP 代理，后台会在每轮成功后按 `ipProxyPoolTargetCount` 检查是否需要切换出口；候选代理不足时只记录日志并跳过切换
11. 所有轮次结束后输出汇总，并清空当前 session 标识

## 9. 新增功能时最容易漏掉的地方

### 新增步骤

必须同时检查：

1. [data/step-definitions.js](c:/Users/projectf/Downloads/codex注册扩展/data/step-definitions.js)
2. [background/steps](c:/Users/projectf/Downloads/codex注册扩展/background/steps)
3. [background/steps/registry.js](c:/Users/projectf/Downloads/codex注册扩展/background/steps/registry.js)
4. 自动运行链路是否需要纳入
5. Step 状态传播和侧边栏展示是否需要适配
6. 测试是否要补

### 新增 provider

必须同时检查：

1. provider 纯工具
2. 后台 provider 调度分支
3. 侧边栏配置项
4. 动态邮箱生成逻辑
5. Step 4 / 7 的验证码流
6. 成功收尾逻辑

如果来源本身提供稳定协议接口，还必须额外判断：

7. 是否可以不打开来源后台页面，直接把步骤 7 / 10 收敛到协议分支

### 新增配置项

必须同时检查：

1. `PERSISTED_SETTING_DEFAULTS`
2. `normalizePersistentSettingValue`
3. 导入导出逻辑
4. sidepanel 表单与状态恢复
5. 是否错误挂靠到无关 provider / manager 配置下
6. 结构文档 / 开发规范是否需要更新

## 10. 文档联动规则

修改下列内容时，必须同步更新文档：

- 文件结构变更  
  更新 [项目文件结构说明.md](c:/Users/projectf/Downloads/codex注册扩展/项目文件结构说明.md)
- 运行链路变更  
  更新 [项目完整链路说明.md](c:/Users/projectf/Downloads/codex注册扩展/项目完整链路说明.md)
- 规范、边界、步骤接入方式变更  
  更新 [项目开发规范（AI协作）.md](c:/Users/projectf/Downloads/codex注册扩展/项目开发规范（AI协作）.md)
## 2026-04 链路补充：认证页共享恢复

- 新增共享恢复层：`content/auth-page-recovery.js`。
- Step 4 在等待注册验证码页时，如果命中认证页 `Try again / 重试` 页，或 `/email-verification` 上的 `405 / Route Error` 重试页，会先通过共享恢复逻辑最多自动点击 5 次 `重试` 尝试恢复，再继续回到密码页重提和验证码页确认流程。
- 但如果 Step 4 的认证重试页正文中出现 `user_already_exists`，则会直接视为“当前用户已存在”：不点击 `重试`，不再回到步骤 1 重开当前轮，而是立即结束当前轮；开启自动重试时直接进入下一轮。
- Step 7 在识别到登录超时报错页时，会先通过共享恢复逻辑最多自动点击 5 次 `重试` 尝试恢复当前页面；恢复成功后会优先按当前页面状态继续当前登录流程，例如直接续跑邮箱页或密码页；只有仍未恢复到可继续状态时，才按原有可恢复失败逻辑重跑 Step 7。
- Step 7 在首次识别到登录验证码页后，不会立刻把步骤判定为成功；还会额外做一轮“收尾确认”，确保页面稳定停留在登录验证码页。如果只是短暂进入验证码页、随后又掉进登录重试页，则会先走共享恢复逻辑，再按既有可恢复失败逻辑重跑 Step 7。
- Step 7 的这轮收尾确认是主要责任边界；Step 8 默认建立在“登录验证码页已经由 Step 7 稳定确认”的前提上，只在后台入口保留防御性回退判断，不替代 Step 7 收尾。
- Step 8 如果发现认证页已经进入登录超时报错/重试页，会直接报错并回到 Step 7 重新开始，而不是在 Step 8 内部点击 `重试`。
- Step 8 的登录重试页判定也覆盖 `/email-verification` 上的 `405 / Route Error`，避免这类页面被误当成普通未知页。
- 任意认证页重试页如果正文中出现 `max_check_attempts`，会被视为 Cloudflare 风控触发：后台立刻完全停止流程，侧边栏会复用现有确认弹窗提示等待 15~30 分钟后再试，避免继续刷新或反复重试加重风控，确认按钮显示为“我知道了”。
- Step 9 在点击 OAuth 同意页 `继续` 后，会持续等待页面跳转；若点击后命中认证页重试页，则直接报错，不会在 Step 9 内部点击 `重试`。
## 2026-04-21 2925 邮件时间窗补充

- `2925` 在 Step 4 / Step 8 现在会携带固定的步骤开始时间窗口，实际筛选下限为“步骤开始时间向前回看 10 分钟”。
- 为了保留这段固定时间窗内已经到达的验证码邮件，后台不再在轮询开始前预先清空 2925 邮箱。
- `2925` 验证码最终提交成功后，后台仍会异步发送 `DELETE_ALL_EMAILS` 做收尾清理。
