Files
FlowPilot/docs/多注册流程来源与驱动注册设计.md
T
2026-05-21 07:21:34 +08:00

248 lines
7.6 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.
# 多注册流程来源与驱动注册设计
本文解决的是另一个会在第二个 flow 接入时立刻爆炸的问题:当前项目虽然有 `tab-runtime`,但“来源 source 是谁、该注入什么脚本、URL family 怎么判定、localhost callback 应该清谁”仍然带着明显的 OpenAI 单流程假设。
## 1. 当前源码里的真实耦合
### 1.1 `signup-page` 其实不是抽象 source,而是 OpenAI Auth 标签页别名
目前这些地方都把 OpenAI Auth / Consent / Add-phone 页面统称为 `signup-page`
- `content/utils.js``detectScriptSource()`
- `background/navigation-utils.js``matchesSourceUrlFamily()`
- `core/flow-kernel/tab-runtime.js` 的注册表、冲突清理、callback 清理
这说明当前的 source 体系不是“可扩展的来源注册表”,而是“OpenAI 时代遗留的 source 命名”。
### 1.2 `content/utils.js` 的默认回退不安全
当前 `detectScriptSource()` 在无法识别页面时会回退成 `vps-panel`。这对单流程时期问题不大,但多 flow 下会出现两个风险:
1. 新 flow 页面被误识别成 `vps-panel`
2. `PING / READY / COMPLETE` 日志和标签注册打到错误 source
### 1.3 `tab-runtime` 的 localhost cleanup 直接写死 `registry['signup-page']`
`closeLocalhostCallbackTabs()` 现在默认把匹配 callback 的残留 tab 从 `signup-page` 名下清掉。将来只要第二个 flow 也有 callback,或者 callback 不是来自 OpenAI Auth 页,这里就会清错或漏清。
### 1.4 manifest 的静态注入列表目前基本围绕 OpenAI
`manifest.json` 里的静态内容脚本匹配域名,目前只有 OpenAI Auth 页是明确的 flow 页面入口。第二个 flow 如果沿用这套做法,后续只会不断在 manifest 里继续加分散规则,缺少统一 source 定义中心。
## 2. 设计目标
后续必须拆成两层注册表:
1. `sourceRegistry`:管理“页面来源与标签页生命周期”
2. `driverRegistry`:管理“这个来源页面由哪个 content driver 负责,支持哪些命令”
这两层不要再混成一个 `signup-page` 字符串。
## 3. `sourceRegistry` 设计
建议 source 用稳定 ID,而不是模糊业务词。
```js
sourceRegistry = {
'openai-auth': {
flowId: 'openai',
kind: 'flow-page',
label: 'OpenAI 认证页',
hostPatterns: [
'https://auth.openai.com/*',
'https://auth0.openai.com/*',
'https://accounts.openai.com/*',
],
familyMatcher: 'openai-auth-family',
injectFiles: [
'content/activation-utils.js',
'content/utils.js',
'content/operation-delay.js',
'flows/openai/content/auth-page-recovery.js',
'flows/openai/content/phone-country-utils.js',
'flows/openai/content/phone-auth.js',
'flows/openai/content/openai-auth.js',
],
cleanupScopes: ['oauth-localhost-callback'],
},
'mail-gmail': {
flowId: null,
kind: 'mail-provider',
label: 'Gmail',
hostPatterns: ['https://mail.google.com/*'],
familyMatcher: 'gmail-family',
injectFiles: ['content/activation-utils.js', 'content/utils.js', 'content/gmail-mail.js'],
},
'panel-sub2api': {
flowId: 'openai',
kind: 'panel-page',
label: 'SUB2API 后台',
dynamicOnly: true,
familyMatcher: 'sub2api-panel-family',
injectFiles: ['content/utils.js', 'flows/openai/content/sub2api-panel.js'],
},
};
```
### 3.1 source 里至少要有这些字段
- `id`
- `flowId`
- `kind`
- `label`
- `hostPatterns`
- `familyMatcher`
- `injectFiles`
- `readyPolicy`
- `cleanupScopes`
- `tabReusePolicy`
### 3.2 source 不等于 flow
一个 flow 会有多个 source
- `openai-entry`
- `openai-auth`
- `openai-plus-checkout`
- `openai-paypal`
- `openai-gopay`
- `panel-sub2api`
同样,一个 source 也可能不属于具体 flow,例如:
- `mail-gmail`
- `mail-163`
- `mail-2925`
- `mail-inbucket`
## 4. `driverRegistry` 设计
`driverRegistry` 解决的是“页面上能做什么”,不是“标签页如何复用”。
```js
driverRegistry = {
'openai-auth': {
sourceId: 'openai-auth',
driverId: 'flows/openai/content/openai-auth',
commands: [
'OPEN_SIGNUP',
'SUBMIT_SIGNUP_IDENTIFIER',
'SUBMIT_PASSWORD',
'SUBMIT_PROFILE',
'SUBMIT_LOGIN_CODE',
'SUBMIT_PHONE_CODE',
'DETECT_AUTH_STATE',
],
},
'mail-gmail': {
sourceId: 'mail-gmail',
driverId: 'content/gmail-mail',
commands: ['POLL_MAILBOX', 'OPEN_MESSAGE', 'DELETE_MESSAGE'],
},
};
```
这样做的好处是:
- `tab-runtime` 只关心 source
- workflow / mail service 只关心 driver capability
- 后续换内容脚本实现时,不需要改 tab 生命周期代码
## 5. `signup-page` 的迁移策略
`signup-page` 不能直接继续作为未来通用 source 名字。
建议迁移方式:
1. 新增正式 source`openai-auth`
2.`sourceRegistry.aliases` 里临时保留:
- `signup-page -> openai-auth`
3. `getSourceLabel()``matchesSourceUrlFamily()``ensureContentScriptReadyOnTab()` 全部优先读注册表
4. 等 OpenAI 全链路迁完后,再删除 `signup-page` 别名
## 6. localhost callback cleanup 怎么改
当前 callback 清理逻辑最大的问题是“只知道 callback URL,不知道 callback 属于哪个 flow source”。
建议改成:
```js
callbackRegistry = {
'oauth-localhost-callback': {
ownerSourceId: 'openai-auth',
matcher: isLocalhostOAuthCallbackUrl,
clearOwnerTabOnClose: true,
},
};
```
`closeLocalhostCallbackTabs()` 的输入要升级为:
- `cleanupScope`
- `callbackUrl`
- `ownerSourceId`
而不是默认拿 `signup-page`
## 7. `content/utils.js` 的改法
### 7.1 禁止默认回退成 `vps-panel`
新的规则应该是:
-`window.__MULTIPAGE_SOURCE` 时,以注入 source 为准
- 没有显式 source 时,只在静态 host map 里做白名单匹配
- 仍无法识别时返回 `unknown-source`
`unknown-source` 不参与 tab 注册,不自动 `reportReady()`,只记录诊断日志。
### 7.2 child frame ready 规则也要转到注册表
当前 `shouldReportReadyForFrame()` 里写死了多个 source 名称。后续应改成 source metadata
- `readyPolicy: 'top-frame-only'`
- `readyPolicy: 'allow-child-frame'`
## 8. manifest / 动态注入策略
原则上以后由 `sourceRegistry` 作为单一事实来源,manifest 只是实现载体。
建议约定:
- 稳定公共页面可继续走静态 `content_scripts`
- 变动大、域名多、只在运行期打开的页面改走 `chrome.scripting.executeScript`
- 每个 source 明确自己的 `hostPatterns``injectFiles`
- 新增 flow 时,不允许直接在业务代码里散写 `injectFiles = [...]`
## 9. 与网络策略的关系
`sourceRegistry` 只负责页面来源,不负责代理策略;但每个 flow 应同时提供自己的 `networkProfile`,至少包含:
- `guardDomains`
- `probeEndpoints`
- `failCloseDomains`
否则 `ip-proxy-core` 仍会继续写死 `chatgpt.com / openai.com`
## 10. 第一阶段迁移顺序
1. 引入 `sourceRegistry / driverRegistry` 与旧 source alias。
2.`getSourceLabel()``matchesSourceUrlFamily()``ensureContentScriptReadyOnTab()` 改成读注册表。
3. 去掉 `detectScriptSource()``vps-panel` 默认回退。
4.`closeLocalhostCallbackTabs()`,不再写死 `signup-page`
5. 第二个 flow 接入时,严格要求先注册 source 和 driver,再写步骤逻辑。
## 11. 本文对应解决的缺口
本文主要补齐以下缺口:
- `signup-page` 实际是 OpenAI source,不是通用注册页
- source URL family / content ready / localhost callback cleanup 没有统一注册表
- manifest 与动态注入没有统一入口
- `content/utils.js` 的默认 source 回退会污染未来 flow