Files
FlowPilot/项目完整链路说明.md
T
2026-05-29 10:09:26 +08:00

1257 lines
94 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 项目完整链路说明
本文档面向 AI 与开发者,目标是让阅读者在最短时间内理解“项目做什么、怎么跑、数据怎么流、功能链路怎么串”,从而在新增功能时不漏逻辑、不误改边界。
使用建议:
1. 先阅读 [项目文件结构说明.md](./项目文件结构说明.md)
2. 再阅读本文
3. 最后阅读 [项目开发规范(AI协作).md](./项目开发规范(AI协作).md)
## 1. 项目目标
这是一个 Chrome 扩展,用于承载多套注册与认证自动化 flow。当前已注册三条主 flow:
- `openai`OpenAI / ChatGPT OAuth 注册、登录、平台绑定与 Plus 扩展链路
- `kiro`AWS Builder ID 注册页 + 桌面授权 + `kiro.rs` 凭据上传链路
- `grok`Grok / xAI 注册页自动化 + `webchat2api` SSO Cookie 远程上传链路
它的核心价值不是“打开一个页面点几个按钮”,而是把下面这些环节串成一条完整可恢复的自动化链路:
- 生成注册邮箱,或在接码开启时申请注册手机号
- 打开 ChatGPT / OpenAI 注册入口
- 提交邮箱/手机号和密码
- 轮询注册验证码(邮箱或短信)
- 填写姓名和生日
- 等待注册成功状态稳定
- 刷新 OAuth 链接并登录(邮箱或手机号)
- 轮询登录验证码(邮箱或短信)
- 自动确认 OAuth 同意页
- 把 localhost 回调提交到 CPA、SUB2API 或 Codex2API
同时,这套扩展现在不再把“来源、步骤、侧边栏显隐、运行态字段”硬编码在单一 OpenAI 链路里,而是统一挂到 `flow registry + source registry + settings schema + step definitions` 这一层。Kiro / Grok 这类新 flow 可以复用共享邮箱服务与 IP 代理服务,但不会复用 OpenAI 的接码、Plus、平台绑定和贡献模式逻辑。
## 2. 核心运行参与者
### 2.1 Sidepanel
[sidepanel/sidepanel.html](./sidepanel/sidepanel.html) + [sidepanel/sidepanel.js](./sidepanel/sidepanel.js) + [sidepanel/account-pool-ui.js](./sidepanel/account-pool-ui.js) + [sidepanel/update-service.js](./sidepanel/update-service.js) + [sidepanel/contribution-content-update-service.js](./sidepanel/contribution-content-update-service.js)
职责:
- 展示配置与步骤状态
- 接收用户输入
- 向后台发送命令
- 接收后台广播并更新 UI
- 动态渲染步骤列表
- 维护 `activeFlowId + targetId` 的双层选择;`openai` flow 继续把 `panelMode` 归一为 legacy integration target`kiro` / `grok` flow 使用各自的 `flows.<flowId>.targetId`
- 按当前 flow capability 决定显隐分组;Kiro flow 只显示来源下拉、`kiro.rs URL / API Key`、共享邮箱服务、共享 IP 代理与 Kiro 运行状态;Grok flow 只显示 `webchat2api` URL / 管理密钥、SSO 提取与上传状态、共享账号密码、共享邮箱服务和共享 IP 代理;二者都不显示 OpenAI 接码 / Plus / 平台绑定配置
- 管理顶部“贡献”按钮与贡献模式主面板;贡献模式本身是 sidepanel 的运行态 UI 模式,不是新的 `panelMode` 来源
- 在贡献模式下复用同一套主自动流程启动,并在面板内展示贡献链路的 `OAUTH / 回调 / 总状态` 三块实时状态
- 在顶部“贡献/使用”按钮下方展示一个非强制的内容更新轻提示;提示来源于 `flowpilot.qlhazycoder.top` 的公开公告 / 教程摘要,用户关闭后仅对当前 `promptVersion` 静默,下次内容版本变化后会重新出现
- sidepanel 首次初始化后会按本地一次性关闭标记决定是否弹出“新手引导”提示;确认后直接打开贡献入口,关闭后仅对该引导静默
- 在 sidepanel 初始化和点击“自动”按钮前刷新一次贡献站公开内容摘要;点击“自动”时会先冻结当时输入或号池锁定的目标轮数,后续刷新、保存和后台状态回灌不能把启动目标改回旧的 1 轮;如果刷新失败,不阻塞主自动流程
-`Auto` 目标轮数达到风险阈值且当前没有配置“节点轮询”能力时,会先弹出风险提醒,避免连续使用单节点跑大量轮次导致更容易命中手机号验证
- 在日志区通过“记录”按钮打开独立的账号记录覆盖层,并展示成功/失败/停止/重试统计与分页列表;同一轮同时出现邮箱和手机号时只显示一条组合记录,按本轮 `accountIdentifierType` 展示主身份,另一种身份作为副行显示
- 查询 GitHub Releases 并展示更新卡片;当前更新服务会区分 `Ultra`、历史 `Pro` 与 legacy `v` 三个版本族,排序时固定以 `Ultra` 为最高正式系列,同时会在读取缓存后重新排序,避免历史 `Pro``v` 版本误显示为比 `Ultra` 更新
- 展示一个单独的“接码”开关、注册方式 `signupMethod` 与“接码平台”下拉;接码平台当前支持 HeroSMS / 5sim / NexSMS。普通模式下开启接码后可把注册方式切到手机号注册,并在 OAuth 登录链路命中手机号登录验证码页时继续复用同一手机号续跑短信验证
- 侧栏在接码卡内提供一个独立运行态“注册手机号”输入框,位于接码订单运行状态下方;第 2 步自动拿到号码后立即回填,用户手动接管手机号注册时也可以直接改写这一个运行态槽位。这个输入框表达账号身份,不等同于接码订单;后续自动拉短信仍依赖 `signupPhoneActivation / signupPhoneCompletedActivation`
- 展示 `Plus 模式` 开关、`账号接入策略``Plus 支付` 配置;`账号接入策略` 固定放在 `Plus 支付` 上方,只提供 `OAuth / 使用会话 JSON 导入` 两种接入方式,会话导入的 CPA / SUB2API 目标由上方 `来源` 自动决定,不支持会话导入的来源会直接禁用为仅 OAuth;支付方式支持 PayPal / GoPay / GPCPayPal 展示账号池下拉与添加按钮,GoPay 展示手机号和 PIN,GPC 只展示卡密输入、购买卡密入口和卡密状态提示;步骤列表会按支付方式和账号接入策略动态切换,普通模式的注册成功等待步骤不再显示或执行;Plus 模式下手机号注册不可用,只允许邮箱注册
- 展示 `执行范围` 配置;当前 UI 只在 codex/openai flow 显示,保存为通用的 `stepExecutionRangeByFlow`,用于指定允许执行的起止步骤。开启后,范围外节点在侧栏中显示为禁用态,手动按钮、手动跳过入口与进度计数只按允许节点计算。
- 为 Hotmail / 2925 账号池复用同一套“添加账号 / 取消添加 / 批量导入 / 收起列表”表单交互;共享的显隐控制放在 `sidepanel/account-pool-ui.js`,各自 manager 只保留 provider 相关字段校验与业务操作
- `Cloudflare Temp Email` 在侧栏中有独立区域,支持“按注册邮箱查信 / 按接收邮箱查信”切换、可编辑域名列表、随机子域名开关、固定子域名开关与子域前缀输入,以及教程/部署入口;域名选择由 `sidepanel/editable-list-picker.js` 统一承接
- `SUB2API` 目标在分组配置下方额外暴露 `账号优先级`;该值会随设置一起持久化,并在后台直连创建账号时原样透传给 SUB2API 管理接口
- 点击“自动”或其他自动化入口时,sidepanel 会附带当前浏览器窗口 ID;后台后续查找、复用和接管标签页时优先锁定在这一个 automation window 内,避免串到其他窗口
### 2.2 Background Service Worker
[background.js](./background.js)
职责:
- 扩展后台入口
- 装配所有模块
- 统一承接 runtime message
- 协调步骤执行
- 管理状态、自动运行、标签页与内容脚本通信
### 2.3 Content Scripts
[content](./content)
职责:
- 在目标网页上执行 DOM 交互
- 读取邮件内容或页面状态
- 将步骤成功/失败状态上报给后台
### 2.4 Helper / Utils / Provider Logic
分布在根目录和 `background/` 下。
职责:
- 抽离第三方邮箱 provider 的纯逻辑
- 抽离邮件匹配与验证码提取
- 抽离共享验证码流程、自动运行流程和运行时基础设施
## 3. 入口与装配关系
### 3.1 扩展入口
[manifest.json](./manifest.json) 声明:
- `background.service_worker = background.js`
- `side_panel.default_path = sidepanel/sidepanel.html`
- 多组内容脚本自动注入规则
### 3.2 背景层装配
[background.js](./background.js) 通过 `importScripts(...)` 依次加载:
- 共享数据与纯工具
- provider 纯逻辑
- 后台桥接层
- 后台共享流程层
- 后台运行时与消息路由层
- 步骤执行模块
因此 `background.js` 现在更像:
- 常量定义中心
- 模块依赖装配器
- 极少量保留函数
- Chrome 事件挂接入口
### 3.3 Flow / Source 注册
[core/flow-kernel/flow-registry.js](./core/flow-kernel/flow-registry.js) 定义 flow、来源、可见设置分组、runtime source 与 driver。
[core/flow-kernel/source-registry.js](./core/flow-kernel/source-registry.js) 负责把 flow runtime source 与共享 mail source 合并成统一来源视图。
[core/flow-kernel/flow-capabilities.js](./core/flow-kernel/flow-capabilities.js) 负责把当前 flow/source 解析成 sidepanel 的能力矩阵。
[core/flow-kernel/settings-schema.js](./core/flow-kernel/settings-schema.js) 负责把旧平铺字段与新的 `flows.*` 配置结构互转。
这意味着:
- flow 是第一层边界,source 是第二层边界
- “显示什么配置”“允许什么步骤”“哪些运行态字段属于哪个 flow” 都不再由 sidepanel 或 background 临时硬编码
- 共享服务目前主要是 `email``proxy`
- OpenAI 的 target capability 还统一决定 Plus 模式下 `账号接入策略` 的可选项:`cpa` 支持 `oauth / cpa_codex_session``sub2api` 支持 `oauth / sub2api_codex_session``codex2api` 当前仅支持 `oauth`
### 3.3.1 Workflow 与运行态基础设施
[core/flow-kernel/workflow-engine.js](./core/flow-kernel/workflow-engine.js) 负责把当前 flow 的 workflow 节点定义变成统一的执行视图,包括:
- 节点默认状态 `pending`
- 当前 workflow 的节点顺序与 `nodeId`
- `next` 后继节点解析
- `getFirstUnfinishedNodeId / getRunningNodeIds / hasSavedProgress` 这类自动运行与恢复入口共享判断
[core/flow-kernel/runtime-state.js](./core/flow-kernel/runtime-state.js) 则负责把 session 运行态整理成统一的结构化视图与 patch 入口:
- `runtimeState`:当前运行中的 node、nodeStatuses、activeRunId 等核心执行态
- `sharedState`automation window、tab registry、最近来源 URL 这类跨 flow 共享运行态
- `serviceState`:代理、邮箱等共享服务运行态
- `flowState`OpenAI / Kiro / Grok 各自的 flow 私有运行态
这两个模块的作用,是把“当前该跑哪些节点”“当前 state 应该怎么看”“前后台恢复时如何合并 patch”从 `background.js` 主入口里抽离出来,避免再次退回到旧的平铺 step 状态机。
### 3.4 步骤注册
[data/step-definitions.js](./data/step-definitions.js) 提供共享步骤元数据,并按当前 `activeFlowId` 输出 flow-specific workflow。`openai` flow 会继续按 `plusPaymentMethod / signupMethod / plusAccountAccessStrategy` 动态解析步骤标题与节点集合:当 Plus 模式为邮箱注册且来源支持时,原本的 OAuth 尾链可以被替换成 `sub2api-session-import``cpa-session-import``kiro` flow 输出固定的 9 节点注册/桌面授权/上传 workflow;`grok` flow 输出固定的 6 节点注册、SSO Cookie 提取与 `webchat2api` 上传 workflow。
[core/flow-kernel/step-registry.js](./core/flow-kernel/step-registry.js) 负责把“workflow node 元数据”映射到“步骤执行器”,同时保留 `flowId / nodeId / displayOrder / executeKey` 等稳定节点元数据。
这意味着:
- 步骤顺序靠 `order`
- 步骤文件名靠语义
- 新增步骤时不需要重命名后续文件
- 非 OpenAI flow 不需要复用旧的数字 step registry;直接按 node workflow 注册即可
### 3.4.1 验证码规则注册
[background/mail-rule-registry.js](./background/mail-rule-registry.js) 负责按当前 `activeFlowId` 选择验证码规则构造器。[flows/openai/mail-rules.js](./flows/openai/mail-rules.js) 提供 OpenAI flow 当前使用的注册验证码 / 登录验证码规则,[flows/kiro/mail-rules.js](./flows/kiro/mail-rules.js) 提供 Kiro / AWS Builder ID 注册验证码与桌面授权验证码规则,[flows/grok/mail-rules.js](./flows/grok/mail-rules.js) 提供 xAI / Grok 注册验证码规则。
这层统一产出:
- `senderFilters / subjectFilters / codePatterns`
- `targetEmail / targetEmailHints`
- `mail2925MatchTargetEmail`
- `maxAttempts / includeBody / filterAfterTimestamp` 这类轮询 payload
它的职责边界是:
- `background/verification-flow.js` 只消费统一规则 payload,不再在每个 provider 或内容脚本里各自硬写一套 OpenAI 验证码过滤条件。
- `background/flow-mail-polling.js` 负责共享的 provider 分派、iCloud / 2925 会话准备、网页邮箱标签页打开与内容脚本轮询;flow runner 只提供 `flowId / nodeId / step / filterAfterTimestamp` 这类业务上下文。
- “邮件从哪里读”由公共轮询调度层和 provider 决定,“哪封邮件算命中验证码”优先由规则注册层决定。
- 新 flow 如需接入邮箱验证码,应优先新增自己的 mail rules builder,而不是继续改写 OpenAI 规则。
### 3.5 日志步骤号链路
日志步骤号不再从日志正文里提取,也不再兼容旧的 `步骤 X` / `Step X` 文本解析。
- 后台统一通过 `addLog(message, level, { step, stepKey })` 写入结构化日志条目。
- 内容脚本通过 `log(message, level, { step, stepKey })` 上报结构化日志,`reportComplete` / `reportError` 的步骤号只走消息字段和日志元数据。
- [sidepanel/sidepanel.js](./sidepanel/sidepanel.js) 只读取 `entry.step` 渲染步骤标签;日志正文只作为正文展示,不参与步骤号判断。
- Plus 模式后半段不再固定只有一条 OAuth 复用尾链;当 `plusAccountAccessStrategy` 选择会话导入时,尾节点会直接变成 `sub2api-session-import``cpa-session-import`,并按当前可见步骤写日志、完成信号和错误信号。只有走 `oauth` 时,才会进入 `oauth-login -> fetch-login-code -> confirm-oauth -> platform-verify` 这组复用节点。
- 平台验证链路只在 OAuth 尾链下出现;CPA / SUB2API / Codex2API 都按当前 `platform-verify` 可见步骤上报。若尾链改为会话导入,则不再经过 `platform-verify`,而是由各自的 session-import 节点直接完成平台接入。
## 4. 状态与存储链路
### 4.1 `chrome.storage.session`
保存运行态:
- 当前 flow 选择:`activeFlowId / flowId`
- `runtimeState` 结构化运行快照,以及它投影出来的顶层兼容字段
- `automationWindowId`:来自 sidepanel 启动 payload 或 sender tab,用于把后续找 tab、复用 tab、接管来源页尽量锁定在同一个浏览器窗口内
- 当前步骤状态
- 当前 sidepanel UI 模式 `contributionMode`
- 贡献 OAuth 会话字段:
- `contributionSessionId`
- `contributionAuthUrl`
- `contributionAuthState`
- `contributionCallbackUrl`
- `contributionStatus`
- `contributionStatusMessage`
- `contributionLastPollAt`
- OAuth 链接
- Kiro 运行态:使用 `kiroRuntime.session / register / webAuth / desktopAuth / upload` 命名空间,保存 Kiro 官方登录入口、Builder ID 注册页状态、Kiro Web 登录态摘要、桌面授权 PKCE 凭据、上传状态和当前 Kiro 会话进度
- Grok 运行态:权威状态位于 `runtimeState.flowState.grok`,包含 `session / register / sso / upload`;顶层 `grokRegisterTabId / grokPageState / grokEmail / grokPassword / grokSsoCookie / grokSsoCookies / grokWebchat2ApiUpload*` 只作为兼容投影,不应当再形成第二套状态机;兼容 flat 字段只有非空值才能覆盖 canonical runtime,避免空旧字段反向覆盖本轮 SSO
- 当前轮冻结后的注册方式 `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 当前运行时选择
- YYDS Mail 当前运行时邮箱 `currentYydsMailInbox`,保存本轮创建得到的最终邮箱地址和临时 Bearer token
- 2925 当前选中的账号 ID `currentMail2925AccountId`(运行时会同步到持久配置,用于重开浏览器后恢复同一个号池账号)
补充:
- `core/flow-kernel/runtime-state.js` 会把会话运行态统一整理成 `runtimeState / sharedState / serviceState / flowState` 四层结构;当前顶层那些 `currentNodeId / nodeStatuses / automationWindowId / ipProxyApplied* / flowId / runId` 等兼容字段,都是这份结构化运行态的投影视图,不应再当成另一套平行状态机维护。
- `automationWindowId` 会同时存在于顶层兼容字段与 `runtimeState.sharedState` 视图中;真正目的是约束自动化只在当前 sidepanel 所在窗口里继续接管标签页,而不是把不同窗口里的同类页面串到一起。
- `content/mail-2925.js` 会把“当前验证码步骤启动会话”对应的已试验证码写入 `chrome.storage.session`,键为 `seen2925CodeState`;同一个 Step 4 / Step 8 会话里试过的验证码不会再次提交,新会话开始时会重置。
### 4.2 `chrome.storage.local`
保存持久配置与账号运行历史:
- 当前 flow 与目标配置:`activeFlowId`、OpenAI 的 `panelMode`(归一到 integration target)、Kiro 的 `flows.kiro.targetId`
- flow-aware 配置树:`flows.openai.*``flows.kiro.*`
- CPA / SUB2API 配置
- SUB2API 持久配置中的 `sub2apiGroupName / sub2apiGroupNames / sub2apiAccountPriority / sub2apiDefaultProxyName`
- Codex2API 配置
- IP 代理持久配置:`ipProxyEnabled`、服务商、模式、API 地址、服务商配置快照、账号列表、固定 Host / Port / Protocol / Username / Password、地区参数、session 与自动切换阈值
- Plus 模式开关 `plusModeEnabled`
- Plus 账号接入策略 `plusAccountAccessStrategy`
- Plus 支付方式 `plusPaymentMethod`GoPay 配置 `gopayPhone / gopayPin`GPC 页面充值配置 `gpcBaseUrl / gpcCardKey / gpcBalance / gpcBalancePayload / gpcBalanceUpdatedAt / gpcBalanceError / gpcRemainingUses / gpcCardStatus / gpcPageStatus / gpcPageStatusText`;其中 GPC 页面地址固定归一为 `https://gpc.qlhazycoder.top`
- PayPal 账号池配置 `paypalAccounts / currentPayPalAccountId`,以及供后台步骤兼容读取的 `paypalEmail / paypalPassword`
- 邮箱 provider 配置
- Cloud Mail / SkyMail API 配置:`cloudMailBaseUrl / cloudMailAdminEmail / cloudMailAdminPassword / cloudMailToken / cloudMailReceiveMailbox / cloudMailDomain / cloudMailDomains`
- YYDS Mail API 配置:`yydsMailApiKey / yydsMailBaseUrl`;reset 会保留这两个持久配置,但清空 `currentYydsMailInbox`,确保下一轮重新创建临时邮箱
- Hotmail 账号池
- 2925 账号池
- 2925 是否启用号池模式 `mail2925UseAccountPool`
- 2925 当前选中的号池账号 ID `currentMail2925AccountId`
- Cloudflare / Temp Email 设置
- Cloudflare Temp Email 细分配置:`cloudflareTempEmailLookupMode / cloudflareTempEmailUseRandomSubdomain / cloudflareTempEmailUseFixedSubdomain / cloudflareTempEmailSubdomainPrefix / cloudflareTempEmailDomain / cloudflareTempEmailDomains`
- 注册方式 `signupMethod`
- 接码开关、接码平台 `phoneSmsProvider`,以及 HeroSMS / 5sim 各自的 API Key、国家/地区、价格上限和平台扩展设置
- iCloud 相关偏好
- LuckMail API 配置
- Kiro 目标配置:`flows.kiro.targetId``flows.kiro.targets["kiro-rs"] = { baseUrl, apiKey }`
- 自动运行默认配置
- 操作间延迟兼容字段 `operationDelayEnabled`:当前会被严格归一为启用;sidepanel 不再暴露真正关闭入口,但内容脚本仍通过 `chrome.storage.local` 监听该字段完成兼容同步
- OAuth 授权后链总超时:不再作为侧栏配置项持久化,只由 `openai + cpa` target capability 自动启用;其他来源与 flow 不启动 OAuth 总预算,仅保留各步骤本地等待超时
- flow 级执行范围配置 `stepExecutionRangeByFlow`:按 flowId 保存,例如 `openai: { enabled: true, fromStep: 3, toStep: 6 }`;侧栏中的 `codex` 会归一到内部默认 flowId `openai`。这是通用配置结构,但当前只给 codex/openai flow 暴露 UI。
- 账号运行历史 `accountRunHistory`(以统一账号标识为主键;邮箱账号保留邮箱主键,手机号注册保留手机号主键;同一轮中后续绑定的邮箱或手机号会写入同一条记录的副身份字段,保存最近一次状态:成功/失败/停止)
- 账号运行历史本地同步 helper 地址
注意:
- `contributionMode` 不属于持久配置,也不参与导入/导出;它只存在于运行态
- `panelMode` 现在只代表 `openai` flow 下的 `cpa | sub2api | codex2api` 三个 legacy 来源;`kiro` flow 使用独立的 `flows.kiro.targetId`
账号运行历史会默认通过 [scripts/hotmail_helper.py](./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 https://flowpilot.qlhazycoder.top/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. `已有认证文件?前往上传` 继续打开 `https://flowpilot.qlhazycoder.top/`
13. `退出贡献模式` 会清理贡献会话相关运行态,`重置` 只重置主流程状态,不会自动退出贡献模式
这条链路的关键边界是:
- 扩展会调用公开贡献 API,但这条调用被折叠进主 10 步自动链,而不是额外分出一条独立“贡献 OAuth 小流程”
- 扩展不会保存 token
- 扩展不会持有任何管理密钥
- 扩展代码中不能直接调用 `/v0/management/*`
- 扩展不会直接访问生产管理面或生产 auth-dir
- 安全边界仍然在服务端;扩展只负责公开 OAuth 贡献前端流程
- 退出贡献模式后,要恢复普通模式 UI,并恢复持久配置中的自定义密码等普通模式偏好
### 4.5 贡献内容更新提示链路
这条链路用于把 `flowpilot.qlhazycoder.top` 上的公开公告 / 使用教程更新,映射成 sidepanel 顶部“贡献/使用”按钮下方的轻提示。
1. 贡献站公开暴露 `GET https://flowpilot.qlhazycoder.top/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](./content/utils.js) 在脚本加载后会发送 `CONTENT_SCRIPT_READY`
后台收到后会:
- 注册当前来源对应的 tab
- 标记 ready
- 冲刷排队命令
### 5.2 队列与重试
[core/flow-kernel/tab-runtime.js](./core/flow-kernel/tab-runtime.js) 负责:
- `queueCommand`
- `flushCommand`
- `sendTabMessageWithTimeout`
- `sendToContentScriptResilient`
- `sendToMailContentScriptResilient`
这保证了:
- 页面切换导致脚本暂时失联时,后台不会立刻误判彻底失败
- 邮箱页或注册页能在注入恢复后继续执行
- 用户点击 Stop 后,标签完成等待、注入后的短暂延迟和内容脚本重试等待会尽快中断,不会因为底层轮询还在 sleep 而继续往下恢复
### 5.3 操作间延迟注入层
[content/operation-delay.js](./content/operation-delay.js) 是内容脚本侧的统一交互节奏层。
当前接入方式:
- 它会在 `content/utils.js` 之后注入到需要真实页面交互的 bundle,例如 `openai-auth``plus-checkout``paypal-flow``gopay-flow`,以及 `mail-2925` 这类仍有页面级点击/清理动作的链路。
- 它主要包装输入、选择、点击、提交与短等待这类页面内操作;当前默认节奏为 2 秒。
- `confirm-oauth``platform-verify` 这类后台节点不走这一层;邮箱 API 轮询、短信 API 轮询、WhatsApp 读码和其他纯后台请求也不应被硬塞进 2 秒延迟。
- `operationDelayEnabled` 这个持久字段现在仍会被内容脚本监听,但对外语义已经等价于“始终启用”,保留它主要是为了兼容旧配置与统一注入同步。
## 6. 手动步骤完整链路
### Step 1
文件:
- [flows/openai/background/steps/open-chatgpt.js](./flows/openai/background/steps/open-chatgpt.js)
- [flows/openai/content/openai-auth.js](./flows/openai/content/openai-auth.js)
流程:
1. 后台打开 ChatGPT 官网并复用/创建注册页标签
2. 等待注册页内容脚本就绪(仅做连接与注入确认)
3. 不在 Step 1 检测或点击注册入口
4. 标记 Step 1 完成
补充:
- 手动点击“跳过步骤 1”时,会级联将步骤 2~5 标记为跳过;其中已完成或正在运行的步骤不会被改写。
### Step 2
文件:
- [flows/openai/background/steps/submit-signup-email.js](./flows/openai/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 > 手动填写的运行态手机号 > 接码平台新申请号码”的顺序决定手机号来源,避免重复买号。内容脚本会通过 `flows/openai/content/phone-country-utils.js` 共享工具按号码最长区号和平台国家名切换手机号国家下拉框,兼容 `+(44)` / `(+44)` / `+44` 等页面文案;必须确认可视下拉按钮的区号已同步后才填写并提交,若仍显示旧国家则停止提交
8. 以当前统一账号标识先写入一条“停止(流程尚未完成)”的记录占位;邮箱账号占位邮箱,phone-only 账号占位手机号
9. 等待身份提交后的真实落地页
10. 如果进入密码页,则继续执行 Step 3
11. 如果直接进入邮箱验证码页或手机验证码页,则自动跳过 Step 3 并进入 Step 4
### Step 3
文件:
- [flows/openai/background/steps/fill-password.js](./flows/openai/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
文件:
- [flows/openai/background/steps/fetch-signup-code.js](./flows/openai/background/steps/fetch-signup-code.js)
- [flows/openai/background/steps/fetch-login-code.js](./flows/openai/background/steps/fetch-login-code.js)
- [background/verification-flow.js](./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 重新执行时,后台会先检查 `openai-auth` 认证页标签是否仍然存在;如果步骤 1 / 2 打开的认证页已经关闭,就会直接提示“请先执行步骤 1 或步骤 2,确保认证页仍然打开并停留在验证码页”,不会先重置后续步骤再报技术错误。
- 当验证码最终提交成功后,后台会异步向 2925 邮箱页发送 `DELETE_ALL_EMAILS`,执行“全选 + 删除”清理剩余邮件,不阻塞主流程。
- 如果 `2925` 邮箱页在轮询期间出现“子邮箱已达上限邮箱”,后台会记录当前时间,把当前 2925 账号禁用 24 小时,自动切到下一个可用账号并完成登录,然后直接报错结束当前尝试;如果 Auto 开启了自动重试,现有控制器会按原逻辑进入下一次尝试。
- Auto 模式下,普通 Step 4 失败仍会沿用当前邮箱回到 Step 1 重新开始当前轮;但若失败原因是 2925 主动要求“结束当前尝试”,则不会再回到 Step 1 重开,而是直接把错误抛给自动重试控制器。
### Step 5
文件:
- [flows/openai/background/steps/fill-profile.js](./flows/openai/background/steps/fill-profile.js)
流程:
1. 生成随机姓名和生日
2. 内容脚本填写资料;如果资料页出现顶部“我同意以下所有各项”总勾选框,会先自动勾选后再提交
3. 内容脚本点击“完成帐户创建”
4. 点击后立即上报 Step 5 完成,不再等待页面结果
5. 自动运行在进入 Step 6 前会等待当前页面完成加载并短暂稳定,避免 Step 5 刚点击提交、后续跳转尚未真正开始时就执行下一步;但仍不在 Step 5 阶段接管 ChatGPT 跳转或 onboarding 跳过
### Step 6
文件:
- [flows/openai/background/steps/wait-registration-success.js](./flows/openai/background/steps/wait-registration-success.js)
流程:
1. 固定等待 20 秒,等待注册成功状态和页面跳转稳定
2. 默认不删除 ChatGPT / OpenAI cookies
3. 如果侧栏第六步 `清 Cookies` 开关已开启,则在等待结束后清理 ChatGPT / OpenAI 相关 cookies
4. 完成后进入后续登录链路
### Step 7
文件:
- [flows/openai/background/steps/oauth-login.js](./flows/openai/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
文件:
- [flows/openai/background/steps/fetch-login-code.js](./flows/openai/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
文件:
- [flows/openai/background/steps/confirm-oauth.js](./flows/openai/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 isnt working / HTTP ERROR 500`,后台会把当前号码视为不可继续使用并结束当前激活,避免继续停留在坏页面上空轮询。
### Step 10
文件:
- [flows/openai/background/steps/platform-verify.js](./flows/openai/background/steps/platform-verify.js)
说明:
- 本节描述的是普通 OAuth 尾链里的 `platform-verify` 节点。
- 如果 Plus 模式下把 `账号接入策略` 切到 `cpa_codex_session``sub2api_codex_session`,尾节点不会进入这里,而是分别走 `cpa-session-import``sub2api-session-import`
流程:
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 仍走 OAuth 尾链时的 `platform-verify` 节点,会直接用 localhost callback 中的 `code / state` 调用 `POST /api/v1/admin/openai/exchange-code`,再用返回的 OpenAI token 信息调用 `POST /api/v1/admin/accounts` 创建 SUB2API 账号。
- SUB2API 主链路不再打开后台页面,也不再依赖 `flows/openai/content/sub2api-panel.js` 的 DOM 自动化;该内容脚本仅保留为旧后台页面路径的兼容能力。
贡献模式补充:
- 贡献模式下,步骤 10 不再打开 CPA 管理页
- 步骤 10 会先尝试提交已捕获的 callback URL,随后轮询公开贡献状态,直到进入最终态
- `auto_approved``manual_review_required` 视为主流程完成;`auto_rejected / expired / error` 视为当前轮失败
## 6.1 Plus 模式链路
Plus 模式通过 `plusModeEnabled` 开启,核心目标是在普通注册资料完成后,先完成 Plus 支付,再根据 `账号接入策略` 决定后半段是继续走 OAuth,还是直接把当前 ChatGPT 已登录会话导入目标平台。
先说明边界:
- Plus 模式不是固定一套步骤数量,前缀会随着 `PayPal / GoPay / GPC` 变化,尾链又会随着 `账号接入策略` 变化。
- Plus 模式只允许邮箱注册;一旦开启 Plus,手机号注册入口会被 capability 层直接禁用。
- `账号接入策略` 只在 `openai` flow、Plus 已开启、且当前是邮箱注册时才生效。
前缀链路按支付方式分三类:
1. `PayPal`
- 创建 Plus Checkout
- 填写账单并提交订单
- PayPal 登录与授权
- 订阅回跳确认
2. `GoPay`
- 打开 GoPay 订阅页
- 等待当前页面完成 GoPay 订阅确认
3. `GPC`
- 打开 GPC 页面并准备:覆盖卡密输入框,读取完整 ChatGPT session 并写入页面
- 启动并等待 GPC 完成:点击 `开始plus充值`,按页面日志判断 `订阅完成`、无试用资格、失败或需要再次启动
支付前缀完成后,尾链有两种模式:
1. `OAuth`
- 继续进入 `oauth-login -> fetch-login-code -> confirm-oauth -> platform-verify`
- `platform-verify` 会按当前来源分别落到 CPA、SUB2API 或 Codex2API 的平台接入逻辑
2. `当前 ChatGPT 会话直导`
- `sub2api_codex_session`:尾链直接切成 `sub2api-session-import`
- `cpa_codex_session`:尾链直接切成 `cpa-session-import`
- 这两条尾链都不再进入 `oauth-login / fetch-login-code / confirm-oauth / platform-verify`
来源能力限制如下:
- `CPA` 支持 `oauth``cpa_codex_session`
- `SUB2API` 支持 `oauth``sub2api_codex_session`
- `Codex2API` 当前只支持 `oauth`
直导尾链的真实行为:
- `sub2api-session-import` 会优先复用 Plus 过程中已经登记的 ChatGPT 标签页;如果没有,再回退到当前活动的 `chatgpt.com / chat.openai.com / openai.com` 已登录标签页。
- `cpa-session-import` 使用同样的标签页选择策略,读取当前会话、accessToken 和可解析的账号信息后,直接调用 CPA 管理接口导入。
- 两个导入尾节点都由后台直接完成,不依赖平台后台页面 DOM 自动化。
隐藏与替换规则:
- 普通模式的 `等待注册成功` 在 Plus 模式下隐藏且不执行。
- 当尾链切到 session import 时,被替换掉的是原本的整段 OAuth 后链,而不是只替换某一个固定编号步骤。
- 只有 `账号接入策略 = oauth` 时,才会出现 `platform-verify`
等待模型:
- Plus Checkout、PayPal、GoPay 手动确认与 GPC 页面流程等待,都使用“可停止的后台等待”模型;等待过程中仍保持 Stop 可打断。
## 6.2 Kiro Flow 链路
Kiro flow 的目标不是复用 OpenAI 注册链,而是独立完成“Kiro 官方登录入口 -> Builder ID 注册 -> Kiro Web 登录态 -> 桌面端 refresh token -> 上传到 `kiro.rs`”这条链路。
当前 Kiro workflow 固定为 9 个节点:
1. `kiro-open-register-page`
2. `kiro-submit-email`
3. `kiro-submit-name`
4. `kiro-submit-verification-code`
5. `kiro-submit-password`
6. `kiro-complete-register-consent`
7. `kiro-start-desktop-authorize`
8. `kiro-complete-desktop-authorize`
9. `kiro-upload-credential`
链路如下:
1. sidepanel 把当前 flow 切到 `kiro`target 固定为 `kiro-rs`
2. 用户填写 `kiro.rs URL / API Key`
3. 步骤 1~6 先进入 Kiro 官方登录页,再完成 Builder ID 注册与 Kiro Web 登录态确认:
- 清理 Kiro / AWS Builder ID 相关 cookies
- 打开 `https://app.kiro.dev/signin`
- 在 Kiro 官方登录页选择 Builder ID 登录入口
- 等待跳转到 AWS Builder ID 邮箱输入页
- 通过共享邮箱服务生成注册邮箱并提交
- 填写姓名、拉取验证码、设置账户密码
- 点击“确认并继续 / 允许访问”
- 等待回到 Kiro Web 登录完成页,确认 `kiroRuntime.webAuth.status = signed_in`
- 保存注册邮箱、注册页状态和 Kiro Web 登录态摘要到 `kiroRuntime.register / webAuth`
4. 步骤 7 必须在 `register.status = completed``webAuth.status = signed_in` 后执行;后台注册桌面 OIDC client,并生成 PKCE 参数:
- 生成 `state / codeVerifier / codeChallenge`
- 生成 localhost callback `redirectUri`
- 构建桌面授权地址并打开授权标签页
5. 步骤 8 在桌面授权页完成浏览器侧授权:
- 处理可能出现的 relogin、OTP、consent
- 监听 `http://127.0.0.1:<port>/oauth/callback?...`
- 校验 callback `state`
-`authorization_code + PKCE` 兑换 `accessToken / refreshToken`
- 保存 `clientId / clientSecret / refreshToken / accessToken``kiroRuntime.desktopAuth`
6. 步骤 9 调用 `kiro.rs` Admin API
- 先探活 `/api/admin/credentials`
- 再用 `x-api-key` 上传桌面端 Builder ID 凭据
- payload 固定包含 `refreshToken / profileArn / clientId / clientSecret / region / authRegion / apiRegion / machineId / email`
- 如果当前启用了共享 IP 代理,也会把 `proxyUrl / proxyUsername / proxyPassword` 一并上传
- 上传结果保存到 `kiroRuntime.upload`
这条链路的关键边界是:
- Kiro 只复用共享账户密码、共享邮箱服务和共享 IP 代理服务,不复用 OpenAI 接码 / Plus / 平台绑定 / 贡献模式链路
- Kiro 自动运行走通用 linear node runner,但执行器、运行态、页面驱动和上传器全部独立在 `background/kiro/*``content/kiro/*`
- Kiro 运行态统一落在 `kiroRuntime` 命名空间,不再散落为多个平铺字段
- Kiro 注册验证码与桌面授权验证码只在 runner 内计算业务时间窗,具体 AWS 邮件匹配规则由 `flows/kiro/mail-rules.js` 提供,provider 分派统一走 `background/flow-mail-polling.js`
- `kiro.rs` 上传固定发送 BuilderId profileArn `arn:aws:codewhisperer:us-east-1:638616132270:profile/AAAACCCCXXXX`machineId 固定按 `sha256("KotlinNativeAPI/" + refreshToken)` 生成
- Kiro 注册页覆盖 `app.kiro.dev` 与 AWS Builder ID 页面;桌面授权页只绑定 AWS authorize 页面,二者由后台按 source 动态注入,避免同域 AWS 页面被错误归到另一条 Kiro 子链路
## 6.3 Grok Flow 链路
Grok flow 的目标是完成 Grok / xAI 注册页自动化,并把本轮提取到的 `sso` Cookie 上传到 `webchat2api` 的远程账号注入接口。它不是 OpenAI 目标来源,也不是公开贡献 / 发布 flow。
链路如下:
1. sidepanel 把当前 flow 切到 `grok`target 固定为 `webchat2api`
2. 步骤 1 打开 Grok / xAI 注册入口,并清理本轮注册页运行态
3. 步骤 2 通过共享邮箱服务生成或复用注册邮箱,并提交到 xAI 登录/注册页
4. 步骤 3 调用 `background/flow-mail-polling.js`,由 `flows/grok/mail-rules.js` 构造 xAI / Grok 验证码规则并轮询邮箱验证码,再交给 Grok 内容脚本提交
5. 步骤 4 填写资料和密码,推进注册完成页状态
6. 步骤 5 先通过 `chrome.cookies.get``x.ai / grok.com / accounts.x.ai` 查找 `sso` Cookie,必要时 fallback 到 Grok 内容脚本读取 `document.cookie`;提取到的当前值保存到 `runtimeState.flowState.grok.sso` 并投影到 `grokSsoCookie / grokSsoCookies`,其中 `grokSsoCookies` 只保留本轮当前 Cookie,不再累积历史列表
7. 步骤 6 由 `flows/grok/background/publisher-webchat2api.js` 读取 `settingsState.flows.grok.targets.webchat2api.baseUrl / apiKey` 和当前 SSO,向 `${origin}/api/remote-account/inject` 发送 Bearer 管理密钥请求,payload 使用 `accounts[{ token, provider:'grok', type:'sso' }] + strategy:'merge'`
8. 上传结果保存到 `runtimeState.flowState.grok.upload` 并投影到 `grokWebchat2ApiUploadStatus / grokWebchat2ApiUploadedAt / grokWebchat2ApiUploadMessage / grokWebchat2ApiTargetUrl`;缺 URL、缺管理密钥或缺 SSO 都必须报错,不允许静默跳过
9. sidepanel 只展示掩码后的 SSO 值与上传状态;复制按钮仅用于排障,不提供本地文件导出;清空动作必须发 `CLEAR_GROK_SSO_COOKIES` 给后台,由 `flows/grok/background/state.js` 构造 patch,并同步清空 upload 状态,不能在 sidepanel 直接写 `chrome.storage`
这条链路的关键边界是:
- Grok 只复用共享账户密码、共享邮箱服务和共享 IP 代理服务,不复用 OpenAI 接码 / Plus / 平台绑定 / 贡献模式链路
- Grok 自动运行走通用 linear node runner,执行器、运行态、页面驱动、邮件规则和 `webchat2api` 上传器全部放在 `flows/grok/*`
- Grok 内容脚本不能全局 patch 浏览器原型;点击事件需要的坐标只能在局部事件参数里补齐
- Grok SSO 上传是 Grok flow 自己的收尾节点,`flows/grok/index.js``publicationTargets` 保持为空,`supportsAccountContribution` 保持为 `false`
- `shared/contribution-registry.js` 的默认发布贡献校验只覆盖声明了发布目标或支持贡献的 flow;Grok 不应被自动纳入贡献 adapter 缺失检查。如果未来要把 Grok 改成正式发布 flow,必须先新增明确的 publication target 与 contribution adapter,再打开对应能力
## 7. 邮箱与 provider 链路
### 7.1 共享邮箱生成补充
#### 7.1.1 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.2 自定义邮箱池链路
本轮新增 `custom-pool` 生成方式,用于把一批已准备好的邮箱按顺序并入现有自动流,而不是继续为每种邮箱页面单独适配一套“新地址生成器”。
当前行为约定:
1. sidepanel 在 `邮箱生成` 中新增 `自定义邮箱池` 选项,并显示多行 `邮箱池` 输入框
2. 输入框中的邮箱会按“每行一个”归一化为数组,写入持久配置 `customEmailPool`
3. Auto 启动前会先保存当前配置;如果当前生成方式是 `custom-pool`,则自动把总轮数锁定为邮箱池长度
4. 后台在每个目标轮次开始前,会按 `targetRun` 从邮箱池读取对应邮箱,并写入当前 `state.email`
5. 同一目标轮次里的失败重试会继续复用该轮邮箱,不会提前跳到下一个
6. 如果当前目标轮次超出了邮箱池数量,后台会直接报错提示数量不一致
7. 自定义邮箱池只负责“本轮注册邮箱分配”,实际收码仍由当前 `mailProvider` 对应的既有链路负责
#### 7.1.3 自定义邮箱服务号池链路
本轮给 `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 既有的手动验证码确认逻辑
`mailProvider = custom` 默认收码方式是 `manual`,也就是继续弹出手动确认,不会自动读取邮箱。只有在侧边栏把 `收码方式` 显式切到 `本地 helper` 后,后台才会通过 `customMailHelperBaseUrl`(默认 `http://127.0.0.1:17374`)调用 [scripts/custom_mail_helper.py](./scripts/custom_mail_helper.py) 的 `/code` 接口轮询验证码;此时才会跳过手动确认弹窗。
### 7.2 共享模块分工
- `managed-alias-utils.js`
统一承接 Gmail / 2925 的:
- 基邮箱解析
- 完整邮箱兼容性判断
- 别名邮箱生成
- UI 文案输出
- `background/registration-email-state.js`
统一维护当前注册邮箱、上一份比较基线邮箱、来源与更新时间,并提供 Duck 生成前解析比较基线、Step 8 清空当前邮箱但保留历史基线,以及 Step 8 `add-email` 写入邮箱时保留手机号身份的共享工具。
- `background/mail-rule-registry.js` + `flows/*/mail-rules.js`
统一构造各 flow 的验证码规则 payload,区分 OpenAI 注册/登录验证码、Kiro 注册/桌面授权验证码与 Grok 注册验证码,并把 `senderFilters / subjectFilters / codePatterns / targetEmailHints / mail2925MatchTargetEmail` 收敛到 flow mail rules 层。
- `background/flow-mail-polling.js`
统一执行 flow 邮箱验证码轮询调度:读取当前邮箱配置,调用 `mailRuleRegistry.buildVerificationPollPayloadForNode`,分派 Hotmail / LuckMail / Cloudflare Temp Email / Cloud Mail / YYDS Mail 这类 API provider,并为 iCloud、2925 和普通网页邮箱准备会话与内容脚本轮询。Kiro 与后续新 flow 不应在各自 runner 内复制 provider 分派逻辑。
- `background/generated-email-helpers.js`
在原有 Duck / Cloudflare / iCloud / Cloudflare Temp Email 生成链路之外,新增 Gmail / 2925 的共享生成接入;Duck 生成前会按“侧栏当前可见邮箱 -> `registrationEmailState.current` -> `registrationEmailState.previous`”的顺序解析比较基线。
- `background/signup-flow-helpers.js`
负责在真正提交注册邮箱前做“复用已有完整邮箱 / 重新生成”的最终决策,并避免把生成阶段已经落库的邮箱再次重复写入运行态。
- `phone-sms/providers/registry.js`
统一维护 HeroSMS / 5sim / NexSMS 的 provider id、显示名、默认顺序与工厂装配入口;sidepanel 与后台优先经这层归一化 provider 值,而不是各自手搓字符串判断。
- `phone-sms/providers/hero-sms.js` / `phone-sms/providers/five-sim.js`
承接已模块化的接码平台协议适配;NexSMS 当前仍保留在 `background/phone-verification-flow.js` 的兼容路径中,但 provider id、UI 顺序和能力入口已经统一走注册表。
- `sidepanel/custom-email-pool-manager.js`
负责自定义邮箱池 / 自定义邮箱服务号池的表单接线、批量解析、自动轮数联动与显隐管理,不再把这部分 UI 细节继续堆在 `sidepanel.js` 主文件里。
- `sidepanel/editable-list-picker.js`
负责可编辑列表 UI 组件;当前用于 SUB2API 分组、PayPal 账号池、Cloudflare 域名列表与 Cloudflare Temp Email 域名选择,不再在 `sidepanel.js` 内各自手搓一套列表交互。
### 7.3 生成邮箱
文件:
- [background/generated-email-helpers.js](./background/generated-email-helpers.js)
支持:
- Duck
- Cloudflare
- Cloudflare Temp Email
- Cloud Mail
- YYDS Mail
- 自定义邮箱池
- iCloud 隐私邮箱
补充:
- 所有生成器在返回邮箱后都会统一调用共享注册邮箱状态持久化入口回写运行态:普通邮箱流等价于 `setEmailState(..., { source })`,而 Step 8 `add-email` 的手机号身份流会在更新 `email / registrationEmailState` 的同时保留 `signupPhone* / accountIdentifier*`,避免 sidepanel 短暂看到手机号被清空。
- Duck 生成链路不会再直接拿“生成前瞬间读到的输入框值”当唯一基线,而是优先使用 sidepanel 当前可见邮箱,再回退到共享的注册邮箱运行态基线,避免生成前后地址对比失效。
#### 7.3.1 Cloud Mail
组成:
- [cloudmail-utils.js](./cloudmail-utils.js)
- [background/cloudmail-provider.js](./background/cloudmail-provider.js)
- [background/verification-flow.js](./background/verification-flow.js)
- [sidepanel/sidepanel.html](./sidepanel/sidepanel.html)
- [sidepanel/sidepanel.js](./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.3.2 YYDS Mail
组成:
- [yyds-mail-utils.js](./yyds-mail-utils.js)
- [background/yyds-mail-provider.js](./background/yyds-mail-provider.js)
- [background/verification-flow.js](./background/verification-flow.js)
- [sidepanel/sidepanel.html](./sidepanel/sidepanel.html)
- [sidepanel/sidepanel.js](./sidepanel/sidepanel.js)
链路:
1. 侧栏在 `Mail = YYDS Mail` 时只展示 `API Key``Base URL`,不展示普通邮箱生成、转发邮箱或账号池配置。
2. 持久配置只保存 `yydsMailApiKey / yydsMailBaseUrl`;本轮真实邮箱和临时 token 只保存在运行态 `currentYydsMailInbox`
3. 手动点击“获取”、Step 3 需要生成邮箱、或 Auto 每轮准备邮箱时,后台通过 `POST /accounts` 创建邮箱,请求头使用 `X-API-Key`,请求体只发送自动生成或用户传入的 `localPart`
4. 创建成功后以后端返回的最终 `address` 为准回写 `email / registrationEmailState`,并把返回的 `token` 写入 `currentYydsMailInbox`Step 8 `add-email` 的手机号身份链路仍通过共享注册邮箱状态持久化保留 `signupPhone* / accountIdentifier*`
5. Step 4 / Step 8 轮询验证码时,`background/verification-flow.js` 会把 `provider = yyds-mail` 路由到后台 provider;后台用临时 token 发送 `Authorization: Bearer <token>` 调用 `/messages``/messages/{id}`,再复用现有验证码匹配、时间窗、排除旧验证码等规则。
6. 平台验证完成后的成功收尾会清空 `currentYydsMailInbox` 与当前邮箱运行态,下一轮重新创建新的 YYDS Mail 邮箱。
7. `reset` 保留 YYDS Mail 的 API Key / Base URL 配置,但清空当前邮箱运行态,避免旧临时 token 被下一轮误用。
### 7.4 Hotmail
组成:
- [hotmail-utils.js](./hotmail-utils.js)
- [microsoft-email.js](./microsoft-email.js)
- [scripts/hotmail_helper.py](./scripts/hotmail_helper.py)
- [sidepanel/hotmail-manager.js](./sidepanel/hotmail-manager.js)
- [sidepanel/account-pool-ui.js](./sidepanel/account-pool-ui.js)
模式:
- API 对接
- 本地 helper
补充:
- 本地 helper 除了收信与验证码读取,还提供账号记录 JSON 快照同步接口。
- 账号运行历史快照会按默认本地 helper 地址自动尝试同步,不再要求用户手动打开独立开关,也不再绑定 Hotmail 的本地助手模式。
- sidepanel 中 Hotmail 账号池的新增表单默认收起,头部通过共享按钮切换“添加账号 / 取消添加”;表单显隐、按钮文案切换、清空与聚焦都复用 `sidepanel/account-pool-ui.js`,不在 Hotmail manager 内重复实现一套。
### 7.5 LuckMail
组成:
- [luckmail-utils.js](./luckmail-utils.js)
- LuckMail 相关后台领域逻辑仍在 [background.js](./background.js)
### 7.6 163 / 163 VIP / 126 网页邮箱
组成:
- [mail-provider-utils.js](./mail-provider-utils.js)
- [content/mail-163.js](./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.7 2925 账号池
组成:
- [mail2925-utils.js](./mail2925-utils.js)
- [background/mail-2925-session.js](./background/mail-2925-session.js)
- [sidepanel/mail-2925-manager.js](./sidepanel/mail-2925-manager.js)
- [sidepanel/account-pool-ui.js](./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.7.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.8 iCloud
组成:
- [icloud-utils.js](./icloud-utils.js)
- [content/icloud-mail.js](./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.9 IP Proxy
组成:
- [background/ip-proxy-core.js](./background/ip-proxy-core.js)
- [background/ip-proxy-provider-711proxy.js](./background/ip-proxy-provider-711proxy.js)
- [sidepanel/ip-proxy-panel.js](./sidepanel/ip-proxy-panel.js)
- [sidepanel/ip-proxy-provider-711proxy.js](./sidepanel/ip-proxy-provider-711proxy.js)
- [docs/ip-proxy-module.md](./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](./docs/ip-proxy-module.md)、当前完整链路说明和结构说明。
## 8. 自动运行完整链路
文件:
- [background/auto-run-controller.js](./background/auto-run-controller.js)
流程:
1. 读取总轮数与模式;sidepanel 在点击“自动”瞬间先冻结目标轮数,后台确认进入同一目标轮数前会忽略旧 active 状态里的过期 `autoRunTotalRuns`
2. 为本轮自动流程分配唯一 `autoRunSessionId`
3. 计算是否从中断点继续
4. 每轮执行前重置必要运行态
- 如果当前 `Mail = 自定义邮箱` 且配置了 `customMailProviderPool`,会先按当前目标轮次把号池中的对应邮箱写回运行态
- 如果当前生成方式是 `custom-pool`,会先按当前目标轮次把邮箱池中的对应邮箱写回运行态
- fresh-attempt reset 会保留 `stepExecutionRangeByFlow`,避免重置运行态时丢失用户设置的执行窗口
- Grok fresh-attempt reset 会清理注册页 runtime、当前 SSO 与 upload 状态,避免下一轮误上传上一轮旧 SSO
5. 执行 `runAutoSequenceFromStep`
- 如果当前 `activeFlowId !== openai`,后台会切到通用 linear node runner,按当前 flow 的 workflow node 顺序执行,并复用统一的完成状态与 idle watchdog
- 如果 `stepExecutionRangeByFlow` 已启用,自动运行只会遍历允许范围内的 workflow node;范围外节点会被跳过,`getFirstUnfinishedNodeId` 与保存进度判断也只统计允许节点
- 手动执行、手动跳过、completion signal 执行和 `executeNodeAndWait` 都会在后台校验执行范围,范围外节点即使被外部消息或旧状态触发,也会直接报错,不会真正执行
- 步骤 7 内部仍保留登录态恢复的有限重试,但 `add-phone / 手机号页` 属于立即跳出的不可重试错误
- 步骤 8 若在验证码提交后进入 `add-phone / 手机号页`,会直接抛出 fatal 错误,不再先标记步骤成功
- 一旦进入步骤 7~10,遇到普通报错且认证流程未进入 `add-phone`,则自动回到步骤 7 无限重开
- 如果命中 `add-phone / 手机号页` 这类 fatal 错误,则不会再做当前轮的内部重试;当开启自动重试/跳过失败时,会直接结束当前轮并继续下一轮,而不是把整条自动流程暂停
- 如果是手动停止,则立即退出自动流程,不会再触发“回到步骤 7 重开”
6. 如果失败,根据设置决定:
- 立即停止
- 当前轮重试
- 下一轮继续
7. 当前轮最终失败时,写入账号记录,并在自动重试链路中累计重试次数
- 手动停止与自动停止会写入“停止”状态(若后续同邮箱成功/失败,会被覆盖为最新状态)
8. 当前轮最终成功时,如果本轮是手机号注册且当前 flow 的全部 workflow node 都已经完成,后台会清空 `signupPhone* / accountIdentifier* / phoneNumber / currentPhoneActivation``email / registrationEmailState / step8VerificationTargetEmail` 等运行态,避免下一轮继续复用上一轮手机号或绑定邮箱
9. 如果配置了线程间隔,则挂计时计划;计时计划会带上当前 `autoRunSessionId`
10. 旧 timer / 旧 alarm / 旧恢复入口只有在 session 仍有效时才允许恢复执行;Stop 会立即使当前 session 失效,防止“停止后旧倒计时又把流程重新拉起”
11. 如果启用 IP 代理,后台会在每轮成功后按 `ipProxyPoolTargetCount` 检查是否需要切换出口;候选代理不足时只记录日志并跳过切换
12. 所有轮次结束后输出汇总,并清空当前 session 标识
## 9. 新增功能时最容易漏掉的地方
### 新增步骤
必须同时检查:
1. [data/step-definitions.js](./data/step-definitions.js)
2. [background/steps](./background/steps)
3. [core/flow-kernel/step-registry.js](./core/flow-kernel/step-registry.js)
4. 如果步骤属于新 flow,还必须同步检查 [core/flow-kernel/flow-registry.js](./core/flow-kernel/flow-registry.js) 与 [core/flow-kernel/settings-schema.js](./core/flow-kernel/settings-schema.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. 如果配置影响步骤执行,还必须检查自动运行、手动执行、手动跳过、完成信号、进度统计和 sidepanel 禁用态是否都按当前 flow / workflow node 解析
7. 结构文档 / 完整链路文档 / 开发规范是否需要更新
## 10. 文档联动规则
修改下列内容时,必须同步更新文档:
- 文件结构变更
更新 [项目文件结构说明.md](./项目文件结构说明.md)
- 运行链路变更
更新 [项目完整链路说明.md](./项目完整链路说明.md)
- 规范、边界、步骤接入方式变更
更新 [项目开发规范(AI协作).md](./项目开发规范(AI协作).md)
## 2026-04 链路补充:认证页共享恢复
- 新增共享恢复层:`flows/openai/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` 做收尾清理。