docs: define portable config migration

This commit is contained in:
QiuSW
2026-07-27 11:25:59 +08:00
parent 4e0925d381
commit 206794f78c
9 changed files with 66 additions and 5 deletions
+3 -1
View File
@@ -4,6 +4,8 @@ dist/
*.log *.log
*.db *.db
*.db-* *.db-*
/config.json
/config.json.bak
/.config-*.tmp
.vscode/ .vscode/
.idea/ .idea/
+3 -1
View File
@@ -20,6 +20,7 @@
| BR-016 | 每个已保存实例有独立、持久化的首选远程调试端口;它与运行时实际端口分离,未启动实例仍可在列表看到建议端口。 | P1 | | BR-016 | 每个已保存实例有独立、持久化的首选远程调试端口;它与运行时实际端口分离,未启动实例仍可在列表看到建议端口。 | P1 |
| BR-017 | 用户可以仅通过已验证的 loopback CDP 端点查看 Chub 实例的页面目标(标签页)标题、脱敏 URL 和类型;该功能不得控制页面、关闭标签页或接管外部浏览器。 | P1 | | BR-017 | 用户可以仅通过已验证的 loopback CDP 端点查看 Chub 实例的页面目标(标签页)标题、脱敏 URL 和类型;该功能不得控制页面、关闭标签页或接管外部浏览器。 | P1 |
| BR-018 | Chub 运行时提供一个 Windows 通知区域图标,允许显示主窗口、刷新已保存实例状态和显式退出应用;图标及菜单不得绕过实例授权边界或泄露实例路径、URL、代理等敏感信息。 | P1 | | BR-018 | Chub 运行时提供一个 Windows 通知区域图标,允许显示主窗口、刷新已保存实例状态和显式退出应用;图标及菜单不得绕过实例授权边界或泄露实例路径、URL、代理等敏感信息。 | P1 |
| BR-019 | Chub 默认将本地 `config.json` 保存到正在执行的 `chub.exe` 同级目录;首次使用新位置时安全迁移旧 `%APPDATA%\\chub\\config.json`,新位置既有配置永不被旧配置覆盖。 | P1 |
## 验收标准 ## 验收标准
@@ -46,7 +47,8 @@
- 页面目标列表只显示标题、类型和移除 userinfo、query、fragment 的 URL;不得保存、记录、发布或展示 CDP WebSocket URL,也不得发送导航、关闭、创建或脚本执行命令。 - 页面目标列表只显示标题、类型和移除 userinfo、query、fragment 的 URL;不得保存、记录、发布或展示 CDP WebSocket URL,也不得发送导航、关闭、创建或脚本执行命令。
- 通知区域仅显示一个 Chub 图标;Tooltip 仅含非敏感汇总状态。双击或默认菜单项显示并置前主窗口,刷新命令仍只检查已保存实例,退出命令走明确的应用退出协调流程。 - 通知区域仅显示一个 Chub 图标;Tooltip 仅含非敏感汇总状态。双击或默认菜单项显示并置前主窗口,刷新命令仍只检查已保存实例,退出命令走明确的应用退出协调流程。
- 图标创建失败、Explorer 重启、菜单关闭和应用退出不会阻塞 UI 帧,也不会导致浏览器启动、关闭、接管或按进程名枚举。退出时必须移除图标;外部 Chrome/Edge 永远不因通知区域命令被停止。 - 图标创建失败、Explorer 重启、菜单关闭和应用退出不会阻塞 UI 帧,也不会导致浏览器启动、关闭、接管或按进程名枚举。退出时必须移除图标;外部 Chrome/Edge 永远不因通知区域命令被停止。
- 默认配置路径必须由实际可执行文件路径解析,而非当前工作目录;迁移只在新位置不存在且旧配置存在、可读取、可校验时执行。迁移使用现有原子写入并保留旧配置恢复备份;目标目录不可写或旧配置畸形时,不创建半成品配置、不静默回退或混合两个配置来源,并给出可恢复诊断。
## 不做 ## 不做
全局杀 Chrome/Edge、自动登录、页面操作、验证码处理、平台 API 集成、远程控制和跨用户权限提升;通过 CDP 管理或关闭外部实例。首个 CDP 查看版本不提供物理窗口 ID 或分组,后续仅在经过独立安全评审的 WebSocket CDP 任务中考虑。首个通知区域版本不改变窗口关闭语义、不把关闭按钮隐藏到通知区域,也不在菜单中直接启动、停止、重启或删除实例。 全局杀 Chrome/Edge、自动登录、页面操作、验证码处理、平台 API 集成、远程控制和跨用户权限提升;通过 CDP 管理或关闭外部实例。首个 CDP 查看版本不提供物理窗口 ID 或分组,后续仅在经过独立安全评审的 WebSocket CDP 任务中考虑。首个通知区域版本不改变窗口关闭语义、不把关闭按钮隐藏到通知区域,也不在菜单中直接启动、停止、重启或删除实例。首个便携配置版本不提供 UI 路径选择、自动合并多个 `config.json`、网络同步或安装版的 AppData 回退;可配置路径参数另行评审。
+2
View File
@@ -16,6 +16,7 @@ Platform Windows
├─ loopback CDP target reader (read-only `/json/list`) ├─ loopback CDP target reader (read-only `/json/list`)
├─ remote-debug port allocator ├─ remote-debug port allocator
├─ instance status refresher ├─ instance status refresher
├─ portable config path resolver / legacy migrator
└─ notification-area adapter (Windows Shell) └─ notification-area adapter (Windows Shell)
↓ ↓
Chrome / Edge processes Chrome / Edge processes
@@ -46,6 +47,7 @@ UI/CLI 不直接操作 `exec.Cmd`、Win32 handle 或数据库。Application 层
- 受管退出监控:仅为当前会话由 Chub 启动并获得进程句柄的实例建立一个 `Wait` 监控任务。退出事件携带实例 ID、启动代次和 PID,UI 仅在三者仍匹配且当前未处于 Chub 请求的停止中时,将运行时状态改为“已退出”并排队显示提示;过期事件、外部实例和应用退出后的监控取消均不得生成意外退出提示。 - 受管退出监控:仅为当前会话由 Chub 启动并获得进程句柄的实例建立一个 `Wait` 监控任务。退出事件携带实例 ID、启动代次和 PID,UI 仅在三者仍匹配且当前未处于 Chub 请求的停止中时,将运行时状态改为“已退出”并排队显示提示;过期事件、外部实例和应用退出后的监控取消均不得生成意外退出提示。
- CDP 页面目标查看:UI 仅将当前已保存实例快照交给后台 adapter;adapter 只接受“运行中”或“外部已关联”的有效实际端口,先复核对应 browser kind 的 loopback `/json/version`,再读取同一端口的 `/json/list`。结果 DTO 仅含 target ID、标题、类型和脱敏 URL;UI 以实例 ID、端口和请求代次丢弃过期结果。该链路不建立 WebSocket、不开启页面控制,也不提供物理窗口 ID。 - CDP 页面目标查看:UI 仅将当前已保存实例快照交给后台 adapter;adapter 只接受“运行中”或“外部已关联”的有效实际端口,先复核对应 browser kind 的 loopback `/json/version`,再读取同一端口的 `/json/list`。结果 DTO 仅含 target ID、标题、类型和脱敏 URL;UI 以实例 ID、端口和请求代次丢弃过期结果。该链路不建立 WebSocket、不开启页面控制,也不提供物理窗口 ID。
- 通知区域:Windows platform adapter 在独立原生消息循环中管理一个图标、Tooltip 和原生菜单,向 `cmd/chub` 发送仅含“显示主窗口 / 刷新状态 / 退出应用”的 typed command。应用 coordinator 决定如何显示 Gio 窗口、发起既有刷新或执行退出清理;adapter 不持有浏览器进程句柄,不调用浏览器启动/停止接口。图标状态仅来自非敏感实例计数;Explorer 重启后重新注册,应用结束前删除图标。首版不拦截 Gio 的窗口关闭消息,也不隐藏主窗口。 - 通知区域:Windows platform adapter 在独立原生消息循环中管理一个图标、Tooltip 和原生菜单,向 `cmd/chub` 发送仅含“显示主窗口 / 刷新状态 / 退出应用”的 typed command。应用 coordinator 决定如何显示 Gio 窗口、发起既有刷新或执行退出清理;adapter 不持有浏览器进程句柄,不调用浏览器启动/停止接口。图标状态仅来自非敏感实例计数;Explorer 重启后重新注册,应用结束前删除图标。首版不拦截 Gio 的窗口关闭消息,也不隐藏主窗口。
- 配置定位与迁移:`config.OpenDefault()` 先用 `os.Executable()` 的目录解析目标 `config.json`,不依赖工作目录。目标文件已存在时直接使用它;目标不存在时仅尝试读取旧 `%APPDATA%\\chub\\config.json`,通过现有 decode/normalize 校验后以 Store 原子写入目标,并把旧文件保留为只读恢复备份。迁移失败不会写入目标、不会删除旧文件、不会回退到 AppData 继续运行;应用层只显示可恢复的启动诊断,不显示配置内容。CLI 与 GUI 共用该入口,避免出现不同配置来源。
Chrome 和 Edge 共用大部分 Chromium 参数,但 executable 默认路径、进程名称和安装方式不同,通过 `BrowserDefinition` 封装差异。CDP 端点只绑定 `127.0.0.1`;端口号和 `/json/version` 是诊断数据,WebSocket URL 不进入持久化、事件或普通 UI 文案。`/json/list` 仅用于只读页面目标信息;URL 在 DTO 边界移除 userinfo、query 和 fragment。 Chrome 和 Edge 共用大部分 Chromium 参数,但 executable 默认路径、进程名称和安装方式不同,通过 `BrowserDefinition` 封装差异。CDP 端点只绑定 `127.0.0.1`;端口号和 `/json/version` 是诊断数据,WebSocket URL 不进入持久化、事件或普通 UI 文案。`/json/list` 仅用于只读页面目标信息;URL 在 DTO 边界移除 userinfo、query 和 fragment。
+2 -1
View File
@@ -53,7 +53,8 @@
| --- | --- | --- | --- | | --- | --- | --- | --- |
| T-401 | 已验证实例的 CDP 页面目标只读查看 | T-302,T-303,T-310 | DONE | | T-401 | 已验证实例的 CDP 页面目标只读查看 | T-302,T-303,T-310 | DONE |
| T-402 | Windows 通知区域入口与显式退出协调 | T-303,T-305,T-307 | TODO | | T-402 | Windows 通知区域入口与显式退出协调 | T-303,T-305,T-307 | TODO |
| T-403 | 便携配置路径与安全 legacy 迁移 | T-202,T-309 | TODO |
## Backlog ## Backlog
关闭窗口时隐藏到通知区域与单实例激活、CDP tab/window 管理、导入导出启动配置、进程资源监控、多用户权限和远程主机管理。 关闭窗口时隐藏到通知区域与单实例激活、可配置配置路径与安装版 AppData 策略、CDP tab/window 管理、导入导出启动配置、进程资源监控、多用户权限和远程主机管理。
+5
View File
@@ -15,6 +15,7 @@
| US-011 | 用户手动关闭 Chub 启动的 Chrome/Edge 后,可以立即获知该实例已退出,并重新启动该实例。 | P1 | | US-011 | 用户手动关闭 Chub 启动的 Chrome/Edge 后,可以立即获知该实例已退出,并重新启动该实例。 | P1 |
| US-012 | 用户可以查看已验证运行实例的 CDP 页面目标标题、脱敏 URL 和类型,而不会向浏览器发送控制命令。 | P1 | | US-012 | 用户可以查看已验证运行实例的 CDP 页面目标标题、脱敏 URL 和类型,而不会向浏览器发送控制命令。 | P1 |
| US-013 | 用户可以从 Windows 通知区域快速显示 Chub、刷新已保存实例状态或明确退出应用,而不会在菜单中误操作浏览器实例。 | P1 | | US-013 | 用户可以从 Windows 通知区域快速显示 Chub、刷新已保存实例状态或明确退出应用,而不会在菜单中误操作浏览器实例。 | P1 |
| US-014 | 用户复制或升级 Chub 后,实例配置与程序一同保存在可执行文件目录;首次升级会安全带入旧 AppData 配置且不丢失已有新配置。 | P1 |
## 验收场景 ## 验收场景
@@ -57,3 +58,7 @@ CLI/API 返回稳定错误码和结构化数据;未知 PID、身份不匹配
### US-013 Windows 通知区域入口 ### US-013 Windows 通知区域入口
给定 Chub 正在运行,Windows 通知区域出现一个 Chub 图标;用户双击图标或选择默认“显示 Chub”命令后,主窗口显示并置前。用户选择“刷新实例状态”后,Chub 沿用页面刷新相同的已保存实例范围和后台去重规则。用户选择“退出 Chub”后,应用进入明确退出协调流程并清理图标;该菜单不列出实例,也不提供启动、停止、重启、删除或外部接管。Explorer 重启后图标会恢复,图标失效仅显示可恢复诊断,不影响窗口和浏览器实例。首版关闭主窗口仍按原退出语义处理,不会自动隐藏到通知区域。 给定 Chub 正在运行,Windows 通知区域出现一个 Chub 图标;用户双击图标或选择默认“显示 Chub”命令后,主窗口显示并置前。用户选择“刷新实例状态”后,Chub 沿用页面刷新相同的已保存实例范围和后台去重规则。用户选择“退出 Chub”后,应用进入明确退出协调流程并清理图标;该菜单不列出实例,也不提供启动、停止、重启、删除或外部接管。Explorer 重启后图标会恢复,图标失效仅显示可恢复诊断,不影响窗口和浏览器实例。首版关闭主窗口仍按原退出语义处理,不会自动隐藏到通知区域。
### US-014 便携配置迁移
给定用户从 `build\\chub.exe` 或已发布目录启动 Chub,配置读取和后续保存均使用同级 `config.json`,不受启动脚本或当前工作目录影响。给定目标配置不存在且旧 `%APPDATA%\\chub\\config.json` 合法,Chub 校验旧配置并原子写入目标,旧文件作为恢复备份保留。给定目标配置已存在,Chub 只读取目标,不覆盖、合并或删除它。给定目标目录不可写或旧配置畸形,Chub 不创建新文件、不丢失旧文件,显示可恢复的配置诊断;不得静默切回 AppData 造成后续配置分叉。
+2
View File
@@ -15,6 +15,7 @@
| IX-011 | 实例首选调试端口 | 新建/编辑端口推荐、列表建议/准备/实际/外部文案、启动回退与保存 | 旧配置迁移、重复/非法端口、外部冲突、启动失败、外部关联、过期结果 | P1 | | IX-011 | 实例首选调试端口 | 新建/编辑端口推荐、列表建议/准备/实际/外部文案、启动回退与保存 | 旧配置迁移、重复/非法端口、外部冲突、启动失败、外部关联、过期结果 | P1 |
| IX-012 | CDP 页面目标查看 | 编辑弹层的“查看标签页”、只读目标列表与关闭 | 无可验证端口、读取中、空列表、读取失败、状态变更、Escape/焦点回归 | P1 | | IX-012 | CDP 页面目标查看 | 编辑弹层的“查看标签页”、只读目标列表与关闭 | 无可验证端口、读取中、空列表、读取失败、状态变更、Escape/焦点回归 | P1 |
| IX-013 | Windows 通知区域 | 图标双击/默认显示、原生菜单刷新与退出 | 图标创建失败、Explorer 重启、窗口最小化、菜单关闭、刷新中、应用退出 | P1 | | IX-013 | Windows 通知区域 | 图标双击/默认显示、原生菜单刷新与退出 | 图标创建失败、Explorer 重启、窗口最小化、菜单关闭、刷新中、应用退出 | P1 |
| IX-014 | 配置启动与迁移 | exe 同级配置解析、legacy 配置迁移和可恢复失败反馈 | 新配置、合法 legacy、目标优先、畸形 legacy、目标目录不可写、重复启动 | P1 |
## 通用规则 ## 通用规则
@@ -33,3 +34,4 @@
- 调试端口的“建议/准备/实际/外部”必须同时以文字和数值表达,不能将首选端口称为已监听端口。创建和停止实例编辑使用首选端口输入;运行中、启动中、停止中和外部关联实例只读。推荐仅基于 Chub 保存实例,系统端口复检与 CDP 校验只在后台启动任务执行;回退不得使用其他 Chub 实例的逻辑预留端口。 - 调试端口的“建议/准备/实际/外部”必须同时以文字和数值表达,不能将首选端口称为已监听端口。创建和停止实例编辑使用首选端口输入;运行中、启动中、停止中和外部关联实例只读。推荐仅基于 Chub 保存实例,系统端口复检与 CDP 校验只在后台启动任务执行;回退不得使用其他 Chub 实例的逻辑预留端口。
- CDP 页面目标查看是只读、可关闭的临时对话框:入口仅对已有实际已验证端口的“运行中”或“外部已关联”实例可用;打开后焦点位于关闭按钮,Escape 关闭并回到“查看标签页”入口。读取中、空列表和错误均用文字表达,错误不丢失编辑输入;结果只含标题、类型和脱敏 URL,不能把目标 ID、WebSocket URL 或物理窗口 ID当作控制授权。 - CDP 页面目标查看是只读、可关闭的临时对话框:入口仅对已有实际已验证端口的“运行中”或“外部已关联”实例可用;打开后焦点位于关闭按钮,Escape 关闭并回到“查看标签页”入口。读取中、空列表和错误均用文字表达,错误不丢失编辑输入;结果只含标题、类型和脱敏 URL,不能把目标 ID、WebSocket URL 或物理窗口 ID当作控制授权。
- 通知区域图标是 Windows 原生的辅助入口,不替代页面内可键盘访问的实例操作。图标默认项为“显示 Chub”,双击同效;右键菜单顺序固定为显示、刷新、分隔线、退出。刷新复用页面刷新命令并在进行中禁用重复项;退出是应用级命令,不能伪装为关闭单个浏览器。Tooltip 使用文字汇总,不含实例名称或敏感字段。菜单关闭不改变状态;图标失效或 Explorer 重启后后台重建,不抢焦点。首版不提供“关闭时隐藏到通知区域”。 - 通知区域图标是 Windows 原生的辅助入口,不替代页面内可键盘访问的实例操作。图标默认项为“显示 Chub”,双击同效;右键菜单顺序固定为显示、刷新、分隔线、退出。刷新复用页面刷新命令并在进行中禁用重复项;退出是应用级命令,不能伪装为关闭单个浏览器。Tooltip 使用文字汇总,不含实例名称或敏感字段。菜单关闭不改变状态;图标失效或 Explorer 重启后后台重建,不抢焦点。首版不提供“关闭时隐藏到通知区域”。
- 配置迁移不是用户可编辑表单:应用启动时在 platform 层完成,GUI frame 不读取或写入文件。成功迁移仅以不打扰操作的文本说明来源已更新;失败时保留实例列表输入与当前可见页面,给出“程序目录中的 config.json 无法使用;请检查目录权限或恢复旧配置”的可恢复提示,不能显示完整配置、代理地址或用户路径。无需 HTML 原型,因为不新增可操作控件;需以真实 Windows 文件权限和 CLI/GUI 启动验证。
+10
View File
@@ -69,6 +69,16 @@ type TrayState struct {
原生 adapter 通过结果通道发布 `TrayCommand`,并只接收 `TrayState` 汇总来更新 Tooltip 或图标状态。不得在 DTO 中放入实例名称、路径、URL、代理、PID、CDP 地址或平台句柄;`TrayCommand` 不是实例启动、停止、重启、删除或接管 API。 原生 adapter 通过结果通道发布 `TrayCommand`,并只接收 `TrayState` 汇总来更新 Tooltip 或图标状态。不得在 DTO 中放入实例名称、路径、URL、代理、PID、CDP 地址或平台句柄;`TrayCommand` 不是实例启动、停止、重启、删除或接管 API。
本地配置入口统一为:
```go
func DefaultPath() (string, error) // <dir(os.Executable())>\\config.json
func LegacyPath() (string, error) // %APPDATA%\\chub\\config.json,仅用于一次迁移
func OpenDefault() (*Store, error) // 目标优先;必要时校验并原子迁移 legacy
```
调用方不得把当前工作目录作为配置来源,也不得自行复制、删除或合并 legacy 文件。`OpenDefault` 的迁移是文件级配置迁移,不迁移 `UserDataDir`、日志目录或浏览器文件;目标文件存在、legacy 缺失时均不产生写入。迁移失败返回稳定的配置错误,调用方不得改用 legacy 继续写入。
## CLI 当前合约 ## CLI 当前合约
```text ```text
+3 -2
View File
@@ -3,7 +3,7 @@
## 快照 ## 快照
- 日期:2026-07-27 - 日期:2026-07-27
- 阶段:Phase 4 CDP 只读观察与后台入口(T-301 至 T-310、T-201 至 T-208、T-401 已完成;T-402 已规划) - 阶段:Phase 4 CDP 只读观察与后台入口(T-301 至 T-310、T-201 至 T-208、T-401 已完成;T-402、T-403 已规划)
- 代码:已建立 Go module `chub`、`cmd/chub` 入口、logging 测试基座、浏览器 domain/application 合约、Chrome/Edge 参数/发现模块、启动 registry、Windows 身份/占用检查、loopback CDP 端口分配与端点校验、受限 CDP 目标读取、优雅关闭、Job Object、真实 Chrome/Edge smoke、JSON 配置存储、启动恢复、CLI JSON 合约、应用内事件总线、UI 状态测试和 Windows smoke 脚本,T-001 至 T-003、T-101 至 T-104、T-201 至 T-208、T-301 至 T-310、T-401 已完成 - 代码:已建立 Go module `chub`、`cmd/chub` 入口、logging 测试基座、浏览器 domain/application 合约、Chrome/Edge 参数/发现模块、启动 registry、Windows 身份/占用检查、loopback CDP 端口分配与端点校验、受限 CDP 目标读取、优雅关闭、Job Object、真实 Chrome/Edge smoke、JSON 配置存储、启动恢复、CLI JSON 合约、应用内事件总线、UI 状态测试和 Windows smoke 脚本,T-001 至 T-003、T-101 至 T-104、T-201 至 T-208、T-301 至 T-310、T-401 已完成
- UI:Gio 双页 Shell 使用左侧“实例/设置”导航;实例页以等宽“新建实例 / 刷新实例状态”命令区开始,窄内容区自动堆叠。列表固定显示实例名称、浏览器类型、用户数据目录、调试端口、状态和带边框的操作列;端口列在未启动/已退出时显示“建议 {端口}”、启动中显示“准备 {端口}”、托管运行显示“实际 {端口}”、外部关联显示“外部 {端口}”,避免将持久首选端口误作监听事实。新建表单从设置的起始端口推荐最低未冲突端口,编辑停止实例可修改首选端口;启动后台优先该端口并避开其他实例的首选/实际端口,成功回退后才保存新的首选值。操作顺序固定为启动/停止(或重新检测)、编辑、删除,删除与相邻操作保持更大间距以降低误点。主操作在已退出时启动,在 Chub 托管运行或调试不可用时显示停止,在启动/停止中禁用,在外部关联、外部占用或未知占用时仅重新检测;优雅停止通过后台 adapter 验证 registry 的 PID/profile 身份并等待其释放,绝不按进程名关闭或接管外部 Chrome/Edge。刷新通过后台回调只检查已保存实例,以配置快照丢弃编辑或删除后的过期结果;它结合 Chub registry、指定 profile 的外部占用证据和 loopback CDP 端点更新状态,但不扫描、接管或关闭其他 Chrome/Edge。双击、Enter 或编辑图标打开实例编辑弹层,支持名称、浏览器类型、User Data Dir、启动 URL、首选端口、完整地址代理选择和只读实际端口;保存保留未公开启动选项,Escape 对脏表单先请求确认,活跃/外部关联实例锁定身份约束字段。设置页使用完整代理地址选择器、地址输入及保存/删除操作;选择会回填地址,保存按稳定 `proxyId` 新增或更新,删除未引用代理前必须确认,仍被实例引用的代理会被拒绝。启动通过异步回调接到 Windows 浏览器启动器:未占用目录从已保存的起始端口(空值默认 9666)选择 loopback CDP 端口,并在启动时按已选代理 ID 解析最新的 `--proxy-server` 参数;同目录外部浏览器只有在 `DevToolsActivePort` 与 CDP 端点可验证时才显示“外部已关联”,且不会接管其生命周期。删除使用确认弹层,只删除 Chub 实例配置而不删除 User Data Dir。新建、编辑和删除实例会保存到本地配置;四个设置路径有独立异步可取消搜索,切换页面后输入和任务状态保留;新建实例表单使用 Label、Windows 原生目录选择器和 Chrome/Edge RadioButton;CLI `list/events` 已可用,`start/stop/restart` 等待 BrowserManager adapter - UI:Gio 双页 Shell 使用左侧“实例/设置”导航;实例页以等宽“新建实例 / 刷新实例状态”命令区开始,窄内容区自动堆叠。列表固定显示实例名称、浏览器类型、用户数据目录、调试端口、状态和带边框的操作列;端口列在未启动/已退出时显示“建议 {端口}”、启动中显示“准备 {端口}”、托管运行显示“实际 {端口}”、外部关联显示“外部 {端口}”,避免将持久首选端口误作监听事实。新建表单从设置的起始端口推荐最低未冲突端口,编辑停止实例可修改首选端口;启动后台优先该端口并避开其他实例的首选/实际端口,成功回退后才保存新的首选值。操作顺序固定为启动/停止(或重新检测)、编辑、删除,删除与相邻操作保持更大间距以降低误点。主操作在已退出时启动,在 Chub 托管运行或调试不可用时显示停止,在启动/停止中禁用,在外部关联、外部占用或未知占用时仅重新检测;优雅停止通过后台 adapter 验证 registry 的 PID/profile 身份并等待其释放,绝不按进程名关闭或接管外部 Chrome/Edge。刷新通过后台回调只检查已保存实例,以配置快照丢弃编辑或删除后的过期结果;它结合 Chub registry、指定 profile 的外部占用证据和 loopback CDP 端点更新状态,但不扫描、接管或关闭其他 Chrome/Edge。双击、Enter 或编辑图标打开实例编辑弹层,支持名称、浏览器类型、User Data Dir、启动 URL、首选端口、完整地址代理选择和只读实际端口;保存保留未公开启动选项,Escape 对脏表单先请求确认,活跃/外部关联实例锁定身份约束字段。设置页使用完整代理地址选择器、地址输入及保存/删除操作;选择会回填地址,保存按稳定 `proxyId` 新增或更新,删除未引用代理前必须确认,仍被实例引用的代理会被拒绝。启动通过异步回调接到 Windows 浏览器启动器:未占用目录从已保存的起始端口(空值默认 9666)选择 loopback CDP 端口,并在启动时按已选代理 ID 解析最新的 `--proxy-server` 参数;同目录外部浏览器只有在 `DevToolsActivePort` 与 CDP 端点可验证时才显示“外部已关联”,且不会接管其生命周期。删除使用确认弹层,只删除 Chub 实例配置而不删除 User Data Dir。新建、编辑和删除实例会保存到本地配置;四个设置路径有独立异步可取消搜索,切换页面后输入和任务状态保留;新建实例表单使用 Label、Windows 原生目录选择器和 Chrome/Edge RadioButton;CLI `list/events` 已可用,`start/stop/restart` 等待 BrowserManager adapter
- T-307:本应用会话启动的受管 Chrome/Edge 根进程由单实例后台 `Wait` 监控;意外退出按 ID、启动代次和 PID 验证后立即变为“已退出”,清空运行时 PID/端口,并以可关闭、可合并的提示告知用户。提示关闭后焦点回到启动操作;Chub 请求停止、外部实例、过期事件和应用关闭取消监控均不提示。 - T-307:本应用会话启动的受管 Chrome/Edge 根进程由单实例后台 `Wait` 监控;意外退出按 ID、启动代次和 PID 验证后立即变为“已退出”,清空运行时 PID/端口,并以可关闭、可合并的提示告知用户。提示关闭后焦点回到启动操作;Chub 请求停止、外部实例、过期事件和应用关闭取消监控均不提示。
@@ -13,7 +13,8 @@
- T-310:实例新建页使用外层工作区、字段组件和操作区三级边框;编辑弹层使用可辨外框,并为字段、运行态信息、反馈和操作区添加一致描边。紧凑窗口下沿用原有路径/操作重排,字段状态和键盘交互未改变。 - T-310:实例新建页使用外层工作区、字段组件和操作区三级边框;编辑弹层使用可辨外框,并为字段、运行态信息、反馈和操作区添加一致描边。紧凑窗口下沿用原有路径/操作重排,字段状态和键盘交互未改变。
- T-401:仅在运行中或外部已关联实例已有有效实际端口时,编辑弹层可打开“标签页(CDP 页面目标)”只读模态层。后台先复核 loopback `/json/version` 的浏览器类型,再读取有限的 `/json/list`;只显示 ID、类型、标题和移除 userinfo/query/fragment 的 URL,不暴露 WebSocket 地址、不建立 WebSocket、也不执行浏览器控制。读取、错误、空列表和端口/状态变化都有反馈,Escape/关闭后焦点回到入口。 - T-401:仅在运行中或外部已关联实例已有有效实际端口时,编辑弹层可打开“标签页(CDP 页面目标)”只读模态层。后台先复核 loopback `/json/version` 的浏览器类型,再读取有限的 `/json/list`;只显示 ID、类型、标题和移除 userinfo/query/fragment 的 URL,不暴露 WebSocket 地址、不建立 WebSocket、也不执行浏览器控制。读取、错误、空列表和端口/状态变化都有反馈,Escape/关闭后焦点回到入口。
- T-402:已规划 Windows 通知区域图标、显示主窗口、刷新已保存实例状态和显式退出协调。首版不在托盘菜单中直接操作浏览器,不改变窗口关闭语义,也不实现关闭窗口后隐藏;Windows 原生 Shell adapter、Explorer 重启恢复、图标资源与退出清理将在实施阶段完成。 - T-402:已规划 Windows 通知区域图标、显示主窗口、刷新已保存实例状态和显式退出协调。首版不在托盘菜单中直接操作浏览器,不改变窗口关闭语义,也不实现关闭窗口后隐藏;Windows 原生 Shell adapter、Explorer 重启恢复、图标资源与退出清理将在实施阶段完成。
- blocker:无;下一任务为 T-402。 - T-403:已规划将默认 `config.json` 定位到实际 `chub.exe` 同级目录,并将旧 AppData 文件在目标缺失时无损、原子迁移为新配置。目标配置优先;旧文件保留恢复备份;不自动合并、切回 AppData 或迁移浏览器 User Data Dir。
- blocker:无;当前任务为 T-403。
## 当前目录 ## 当前目录
+36
View File
@@ -0,0 +1,36 @@
---
id: T-403
title: 便携配置路径与安全 legacy 迁移
phase: 4
deps: [T-202, T-309]
status: TODO
created: 2026-07-27
owner: codex
---
## 需求与背景
当前实例、设置和代理统一保存在 `%APPDATA%\\chub\\config.json`。用户需要将配置与正在运行的 `chub.exe` 放在同一目录,以支持复制目录后的独立便携环境。当前 `run.bat` 运行 `build\\chub.exe`,因此其目标配置文件为 `build\\config.json`。
## 方案与边界
- 新增 `config.DefaultPath()`:基于 `os.Executable()` 的目录返回 `<exeDir>\\config.json`;不使用当前工作目录、`PATH` 或启动脚本目录。
- 新增 `config.LegacyPath()` 与 `config.OpenDefault()`。`OpenDefault()` 先解析目标 Store:目标文件存在时绝不读取、覆盖或合并 legacy;目标缺失时,若 legacy 文件存在,则使用现有 Store decode/normalize 校验后原子写入目标。
- 迁移成功后保留旧 AppData 文件为恢复备份,不删除、不重命名、不继续作为活动来源;后续启动始终选择 exe 同级目标。跨卷迁移不使用 `Rename`,避免部分移动或数据丢失。
- legacy 缺失时不创建空目标;legacy 畸形、目标路径解析失败或目标目录不可写时返回稳定错误,不创建半成品文件,不静默回退 AppData。目标文件已存在时,即使 legacy 发生错误也不影响目标读取。
- CLI 和 GUI 都只经 `OpenDefault()` 取得 Store。GUI 启动失败必须记录安全诊断并在现有界面提供可恢复反馈;不得把配置内容、完整配置路径、URL、代理或凭据展示/写入诊断。
- 本任务不改变实例 schema、User Data Dir、日志目录、代理字段、文件加密、自动合并、配置路径选择 UI、CLI `--config` 参数或安装版策略。根目录可能存在 `config.json`,因此将其、`.bak` 和原子写入临时文件加入 `.gitignore`。
## 验收要点
- 自动化命令:覆盖 exe 路径解析、legacy 路径解析、首次合法迁移、目标优先、缺失 legacy、畸形 legacy、目标父目录不可写/不可创建、迁移结果可 reload 和旧文件保留;覆盖 CLI 使用统一默认 Store。执行 `gofmt`、`go test ./...`、`go test -race ./cmd/chub ./internal/platform/config`、`go vet ./...`、`go build -o build/chub.exe ./cmd/chub`。
- Windows smoke:通过 `run.bat` 启动后,`build\\config.json` 由 GUI 创建/更新;将现有 `%APPDATA%\\chub\\config.json` 暂时作为 legacy 后首次启动,确认实例、代理和首选端口已进入 `build\\config.json`,旧文件仍可恢复;从不同当前工作目录启动同一 exe 仍使用同一个目标文件。
- 安全边界:不删除、移动或覆盖用户已有配置;不写入密码、Cookie、Token 或代理认证;迁移错误不导致浏览器启动/关闭或外部实例接管;文件写入保留 0600 权限与现有原子替换路径。
## 执行记录
- 状态:TODO
- 变更:已完成需求、架构、API、交互和迁移决策记录;待实施。
- 验证:待执行。
- 阻塞:无。
- 残余风险:程序安装到不可写目录时 strict portable 策略会拒绝保存;可配置路径/安装版回退留待后续任务。