From f13e06500c251b9666040863355faf41bf1a5a00 Mon Sep 17 00:00:00 2001 From: QiuSW <105186638@qq.com> Date: Sat, 25 Jul 2026 16:53:44 +0800 Subject: [PATCH] docs: define proxy and managed stop tasks --- docs/02-requirements.md | 4 ++++ docs/04-architecture.md | 2 ++ docs/06-tasks.md | 2 ++ docs/07-user-stories.md | 10 +++++++++ docs/08-interaction-checklist.md | 4 ++++ docs/api.md | 4 +++- docs/current-state.md | 4 ++-- docs/tasks/T-304.md | 38 ++++++++++++++++++++++++++++++++ docs/tasks/T-305.md | 35 +++++++++++++++++++++++++++++ docs/ui/browser-manager.html | 31 +++++++++++++++++--------- 10 files changed, 121 insertions(+), 13 deletions(-) create mode 100644 docs/tasks/T-304.md create mode 100644 docs/tasks/T-305.md diff --git a/docs/02-requirements.md b/docs/02-requirements.md index 3a0b1ca..49406bc 100644 --- a/docs/02-requirements.md +++ b/docs/02-requirements.md @@ -15,6 +15,8 @@ | BR-011 | Chub 启动的 Chrome/Edge 必须启用仅限 loopback 的本地 CDP;端口从设置的起始端口分配,空值默认 9666。 | P1 | | BR-012 | 同一 user data dir 被外部浏览器占用时,只能在其 CDP 端点可验证时关联为外部实例;不得接管生命周期。 | P1 | | BR-013 | 用户可以刷新已保存实例的状态,并编辑未运行实例的名称、浏览器类型、user data dir 和启动 URL;刷新和编辑不得扩大到未授权的浏览器进程。 | P1 | +| BR-014 | 用户可以维护无认证代理配置,并在创建或编辑实例时选择代理;代理认证信息不得保存、记录或进入启动参数。 | P1 | +| BR-015 | GUI 实例主操作在 Chub 托管运行态提供优雅停止、在可启动状态提供启动;外部关联或未知外部实例不得获得生命周期控制。 | P1 | ## 验收标准 @@ -30,6 +32,8 @@ - UI 覆盖加载、空、占用、启动失败、关闭中、成功和权限错误。 - 刷新仅检查 Chub 已保存的实例;任务执行中不可重复触发,过期结果不得覆盖已经删除或编辑后的记录。 - 实例编辑支持保存、取消和 Escape;有未保存修改时必须先确认,运行中或外部关联实例不能修改浏览器类型或 user data dir。 +- 代理仅接受无认证的 `scheme://host:port` 端点。删除仍被实例引用的代理必须被拒绝;实例保存后按稳定代理 ID 解析最新端点。 +- 受管运行实例点击主操作后进入关闭中,成功退出后恢复启动入口;停止失败不自动强制终止,也绝不影响其他 Chrome/Edge。 ## 不做 diff --git a/docs/04-architecture.md b/docs/04-architecture.md index 572187d..8cc4594 100644 --- a/docs/04-architecture.md +++ b/docs/04-architecture.md @@ -39,6 +39,8 @@ UI/CLI 不直接操作 `exec.Cmd`、Win32 handle 或数据库。Application 层 - App shutdown:取消监控,不默认终止用户浏览器。 - Refresh:以 UI 传入的保存实例快照为范围,在后台读取 Chub registry、对应 profile 占用证据与已知 loopback CDP 端点,返回状态 DTO。刷新不按浏览器进程名或端口范围枚举;结果以实例 ID 与快照版本关联,UI 丢弃已删除或已编辑行的过期结果。 - Edit:UI 只提交可持久化的实例配置 DTO;运行时 PID、关联端口、占用来源和状态由刷新/启动结果返回,不作为编辑表单保存字段。编辑时保留未公开的非敏感启动选项,活跃或外部关联实例的身份约束字段保持只读。 +- Proxy:本地配置保存稳定 `proxy_id` 指向的代理库项(名称与无认证端点);启动 adapter 在后台解析为当前 `LaunchSpec.ProxyServer` 快照。代理不能含 userinfo,删除被引用项会被拒绝;修改代理只影响后续启动,不影响已运行实例。 +- GUI stop:主操作只对 Chub registry 中可验证的托管实例调用 `StopProfile(userDataDir, false)`,随后有限等待 registry 释放。外部关联、占用和未知实例永远不调用停止接口;超时或失败不自动强制关闭。 Chrome 和 Edge 共用大部分 Chromium 参数,但 executable 默认路径、进程名称和安装方式不同,通过 `BrowserDefinition` 封装差异。CDP 端点只绑定 `127.0.0.1`;端口号和 `/json/version` 是诊断数据,WebSocket URL 不进入持久化、事件或普通 UI 文案。 diff --git a/docs/06-tasks.md b/docs/06-tasks.md index c6ae062..606a7b2 100644 --- a/docs/06-tasks.md +++ b/docs/06-tasks.md @@ -39,6 +39,8 @@ | T-301 | 接入 GUI 启动操作与实例删除确认 | T-204,T-208 | DONE | | T-302 | 本地 CDP 启动、外部关联与实例端口展示 | T-102,T-301 | DONE | | T-303 | 实例状态刷新与编辑对话框 | T-302 | DONE | +| T-304 | 代理配置库、实例选择与安全启动参数传递 | T-303 | DOING | +| T-305 | GUI 受管实例优雅停止与启停状态机 | T-304 | TODO | ## Backlog diff --git a/docs/07-user-stories.md b/docs/07-user-stories.md index 2c7720b..495ba36 100644 --- a/docs/07-user-stories.md +++ b/docs/07-user-stories.md @@ -10,6 +10,8 @@ | US-006 | 用户可以通过 CLI/API 查询、启动和关闭实例。 | P1 | | US-007 | 用户可以看到实例关联的本地调试端口;同目录外部浏览器已启用调试时,Chub 只关联并展示状态。 | P1 | | US-008 | 用户可以刷新已保存实例的运行状态,并通过对话框编辑非运行实例的基本配置而不影响其他浏览器。 | P1 | +| US-009 | 用户可以在设置中维护无认证代理,并在新建或编辑实例时从下拉选择器选择代理。 | P1 | +| US-010 | 用户可以通过实例行的同一主操作启动或优雅停止 Chub 托管的浏览器,而不会关闭外部浏览器。 | P1 | ## 验收场景 @@ -32,3 +34,11 @@ CLI/API 返回稳定错误码和结构化数据;未知 PID、身份不匹配 ### US-008 刷新与编辑实例 给定已保存的实例,用户点击刷新或按 F5 后,Chub 只检查这些实例并更新运行状态、PID、端口与占用来源。用户双击行、按 Enter 或点击编辑图标可以打开编辑对话框;保存名称、浏览器类型、User Data Dir 和启动 URL 后更新本地配置。实际调试端口只读显示。若实例正在运行、启动中或为外部关联,浏览器类型和 User Data Dir 保持不可编辑。Escape 在无修改时关闭对话框,有修改时要求用户选择保存、不保存或继续编辑。 + +### US-009 代理选择 + +给定用户在设置中保存的无认证代理,创建和编辑表单可从“无代理”或已命名代理中选择。保存后实例仅关联代理 ID;启动时使用该 ID 的最新端点。含用户名、密码或 URL 路径的端点被拒绝,仍被引用的代理不能删除。 + +### US-010 受管实例启停 + +给定 Chub 启动且仍可在 registry 中验证的实例,运行态主操作显示停止图标。点击后先显示停止中,再请求浏览器正常退出;成功后恢复启动图标。给定外部已关联或未知占用的同目录浏览器,Chub 不显示停止入口,也不发送关闭请求。 diff --git a/docs/08-interaction-checklist.md b/docs/08-interaction-checklist.md index a6eb160..f1d0b3e 100644 --- a/docs/08-interaction-checklist.md +++ b/docs/08-interaction-checklist.md @@ -9,6 +9,8 @@ | IX-005 | 设置 | 默认路径、日志、数据目录和远程调试起始端口 | 保存成功、路径无效、端口无效、权限拒绝 | P1 | | IX-006 | 本地 CDP 启动 | 从起始端口分配、校验端点、展示实际端口 | 分配中、已关联、端口冲突、端点失效、外部不接管 | P1 | | IX-007 | 实例刷新与编辑 | F5/刷新、双击/Enter/编辑图标、目录选择、保存/取消/Escape | 空闲、刷新中、部分失败、字段错误、脏表单、只读运行态、保存成功 | P1 | +| IX-008 | 代理设置与选择器 | 新增、编辑、删除代理;创建/编辑实例选择代理 | 空列表、无代理、格式错误、重复名称、被引用删除、保存成功 | P1 | +| IX-009 | 实例主启停操作 | 启动图标、停止图标、外部重新检测 | 已退出、启动中、托管运行、停止中、失败恢复、外部关联 | P1 | ## 通用规则 @@ -21,3 +23,5 @@ - 实例列表使用外层边框、文字状态和图标区分归属;端口为空时显示“—”,外部关联时操作区不显示关闭或重启。 - 刷新和编辑是独立命令:刷新为只读状态检查,编辑只保存配置;两者均不接管外部实例。F5 与刷新按钮共用一个去重命令。 - 双击不是唯一入口:单击只选择行,Enter 和编辑图标打开对话框。打开后焦点位于名称,关闭后返回调用行;有未保存编辑时 Escape 先请求确认。 +- 代理选择器必须提供“无代理”和命名项,能用键盘选择;代理端点只展示已校验的无认证 `scheme://host:port`,删除有引用的项时保持当前表单与实例选择不变。 +- 实例主操作根据归属和状态切换启动、停止、重新检测或禁用。停止中禁止重复请求;停止失败恢复原状态并保留焦点。外部关联不显示停止入口。 diff --git a/docs/api.md b/docs/api.md index c27bd77..a45d04a 100644 --- a/docs/api.md +++ b/docs/api.md @@ -8,7 +8,7 @@ MVP 首先提供本地 Go application service 和 CLI;是否增加 loopback HT `LaunchSpec` 使用已分配的 `RemoteDebugPort`;端口分配不属于 UI,也不允许由 `ExtraArgs` 覆盖。`InstanceView` 返回实际关联端口、PID、占用来源和归属。外部关联只公开经 `DevToolsActivePort` 与 `/json/version` 验证的端口,不公开 CDP WebSocket URL。 -UI 编辑使用独立的 `EditableInstance` DTO(`ID`、名称、浏览器类型、User Data Dir、启动 URL),不将运行时 PID、状态、来源或实际远程调试端口写回配置。状态刷新使用 `RefreshInstanceStatus(ctx, []InstanceSnapshot) ([]InstanceStatusResult, error)` 形状:每个结果以 `ID` 和快照版本对应,错误可按行返回;实现只能检查调用方提供的实例范围。 +UI 编辑使用独立的 `EditableInstance` DTO(`ID`、名称、浏览器类型、User Data Dir、启动 URL、`ProxyID`),不将运行时 PID、状态、来源或实际远程调试端口写回配置。`ProxyProfile` DTO 只包含 `ID`、名称和已校验的无认证端点;不提供认证字段。状态刷新使用 `RefreshInstanceStatus(ctx, []InstanceSnapshot) ([]InstanceStatusResult, error)` 形状:每个结果以 `ID` 和快照版本对应,错误可按行返回;实现只能检查调用方提供的实例范围。 ## Application service @@ -26,6 +26,8 @@ type BrowserManager interface { 平台实现通过实例级 `StopProfile(userDataDir, force)` 执行关闭;force 只对已注册并经过身份绑定的实例生效。 +GUI 将优雅停止封装为异步 `InstanceStopper(ctx, InstanceRow) error` adapter;adapter 只接受当前配置行并执行 `force=false`,不暴露平台句柄或允许调用方传递任意 PID。 + 参数数组由 `internal/platform/browser.BuildArgs` 生成;Chrome/Edge executable 由同包 `Discoverer.Resolve` 解析。二者都不接收 shell 命令行字符串。CDP 端点检测和端口分配均通过 platform port 执行,UI 只消费 DTO 结果。 状态刷新和目录选择必须通过后台 adapter 回到 UI 结果通道;UI frame 不直接做进程、文件或 CDP I/O。刷新不构成外部实例接管授权。 diff --git a/docs/current-state.md b/docs/current-state.md index 2d777c1..aaf5f2a 100644 --- a/docs/current-state.md +++ b/docs/current-state.md @@ -3,11 +3,11 @@ ## 快照 - 日期:2026-07-25 -- 阶段:Phase 3 真实实例操作(T-303、T-302、T-301 已完成;T-201 至 T-208 已完成) +- 阶段:Phase 3 真实实例操作(T-304 进行中;T-303、T-302、T-301 已完成;T-201 至 T-208 已完成) - 代码:已建立 Go module `chub`、`cmd/chub` 入口、logging 测试基座、浏览器 domain/application 合约、Chrome/Edge 参数/发现模块、启动 registry、Windows 身份/占用检查、loopback 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-303 已完成 - UI:Gio 双页 Shell 使用左侧“实例/设置”导航;实例页以等宽“新建实例 / 刷新实例状态”命令区开始,窄内容区自动堆叠。列表固定显示实例名称、浏览器类型、用户数据目录、调试端口、状态和紧凑编辑/启动/删除图标,并以外层边框和表头分隔组织。刷新通过后台回调只检查已保存实例,以配置快照丢弃编辑或删除后的过期结果;它结合 Chub registry、指定 profile 的外部占用证据和 loopback CDP 端点更新状态,但不扫描、接管或关闭其他 Chrome/Edge。双击、Enter 或编辑图标打开实例编辑弹层,支持名称、浏览器类型、User Data Dir、启动 URL 和只读实际端口;保存保留未公开启动选项,Escape 对脏表单先请求确认,活跃/外部关联实例锁定身份约束字段。启动通过异步回调接到 Windows 浏览器启动器:未占用目录从已保存的起始端口(空值默认 9666)选择 loopback CDP 端口;同目录外部浏览器只有在 `DevToolsActivePort` 与 CDP 端点可验证时才显示“外部已关联”,且不会接管其生命周期。删除使用确认弹层,只删除 Chub 实例配置而不删除 User Data Dir。新建、编辑和删除实例会保存到本地配置;四个设置路径有独立异步可取消搜索,切换页面后输入和任务状态保留;新建实例表单使用 Label、Windows 原生目录选择器和 Chrome/Edge RadioButton;CLI `list/events` 已可用,`start/stop/restart` 等待 BrowserManager adapter - 浏览器核心:设计参考来自 `D:\OPC\shop_helm\internal\platform\chrome`,尚未复制或接入本项目 -- blocker:无;下一步可接入关闭、重启、退出状态回传和 CLI BrowserManager adapter。 +- blocker:无;当前优先完成代理配置库、实例代理选择与安全启动参数传递(T-304),再接入受管实例的 GUI 优雅停止与启停状态回传(T-305)。 ## 当前目录 diff --git a/docs/tasks/T-304.md b/docs/tasks/T-304.md new file mode 100644 index 0000000..15e610c --- /dev/null +++ b/docs/tasks/T-304.md @@ -0,0 +1,38 @@ +--- +id: T-304 +title: 代理配置库、实例选择与安全启动参数传递 +phase: 3 +deps: [T-303] +status: DOING +created: 2026-07-25 +owner: codex +--- + +## 需求与背景 + +用户需要在“设置”中维护可复用的代理,并在创建或编辑实例时选择其中一项。代理必须在实际启动时转换为 Chromium 的 `--proxy-server` 参数,同时继续满足 Chub 的本地、安全和无秘密持久化边界。 + +## 方案与边界 + +- 配置文件新增稳定 ID 的代理集合;每项仅有名称和无认证代理端点。实例只保存 `proxy_id`,原有非敏感 `LaunchSpec.ProxyServer` 仅用于兼容旧配置。 +- 接受 `http`、`https`、`socks4`、`socks5` 的 `scheme://host:port` 端点;拒绝 userinfo、路径、query、fragment 与空 host/port。代理用户名、密码或其他认证秘密不得保存、记录或进入命令行。 +- 设置页以紧凑的代理列表和就地表单完成新增、修改、删除。删除被实例引用的代理必须被拒绝并说明引用数量,不能隐式清空实例的选择。 +- 新建抽屉与编辑对话框提供“代理”选择器,含“无代理”选项;选择器只暴露名称与端点,不要求用户重复输入代理地址。编辑活动实例时仍可修改后续启动使用的代理选择。 +- 启动 adapter 在后台按 `proxy_id` 从当前配置解析端点,生成一次性的 `LaunchSpec.ProxyServer`;找不到代理时拒绝启动并保留稳定、无秘密的错误说明。UI frame 不做配置或进程 I/O。 +- 代理配置更新后,下次启动立即使用最新端点;不修改已运行浏览器,也不尝试通过 CDP 接管外部实例。 + +## 验收要点 + +- 设置页可新增、编辑和删除未被引用的无认证代理;格式错误、重复名称和删除已引用代理均有就地恢复提示。 +- 新建和编辑实例可在“无代理”和已保存代理间选择;保存、重启后选择保持不变,旧的无 `proxy_id` 配置仍能加载。 +- 选择代理后,Chrome/Edge 启动参数包含一次且仅一次正确的 `--proxy-server=`;无代理时不输出该参数。 +- 配置、事件、错误和测试输出均不包含代理认证信息;含 userinfo 的输入被拒绝,不能写入配置或命令行。 +- 覆盖配置序列化、端点校验、引用保护、实例保存、启动参数解析、选择器键盘操作和 UI 异步边界。 +- `gofmt`、`go test ./...`、`go vet ./...`、`go build -o build/chub.exe ./cmd/chub`、`scripts/smoke-browser.ps1` 与 `scripts/smoke-windows.ps1` 通过。 + +## 执行记录 + +- 状态:DOING +- 变更:待实现。 +- 验证:待执行。 +- 阻塞:无。 diff --git a/docs/tasks/T-305.md b/docs/tasks/T-305.md new file mode 100644 index 0000000..8828b92 --- /dev/null +++ b/docs/tasks/T-305.md @@ -0,0 +1,35 @@ +--- +id: T-305 +title: GUI 受管实例优雅停止与启停状态机 +phase: 3 +deps: [T-304] +status: TODO +created: 2026-07-25 +owner: codex +--- + +## 需求与背景 + +实例列表当前能异步启动已保存实例,但无法从同一入口关闭 Chub 启动的浏览器。用户需要启动按钮在可控运行态切换为停止图标,再次点击时请求浏览器优雅退出,成功后恢复启动图标。 + +## 方案与边界 + +- 复用实例行的主操作位:已退出、失败或可启动状态显示启动图标;Chub 托管运行/调试不可用状态显示停止图标;停止中显示不可重复触发的停止中状态;外部已关联继续显示重新检测,绝不显示生命周期控制。 +- 停止请求通过 UI 持有的异步 `InstanceStopper` adapter 发出。adapter 只调用平台 `StopProfile(userDataDir, false)`,并依赖 Chub registry 的 PID、可执行文件和 user data dir 身份校验;绝不按 Chrome/Edge 进程名批量终止,也不对外部关联实例调用 Stop。 +- 平台成功发出优雅关闭后等待有限时间观察注册实例释放。超时、身份不匹配或权限错误回到原运行状态并显示恢复建议;本任务不自动升级为强制关闭。 +- 成功关闭后清空运行时 PID/端口、显示“已退出”、恢复启动图标并将键盘焦点留在同一行。操作状态同时使用图标、文字和禁用态,不只依赖颜色。 + +## 验收要点 + +- 受管运行实例的主操作为停止图标;点击后立即显示“停止中”,重复点击不会发出第二个停止请求;成功后显示“已退出”和启动图标。 +- 外部已关联、外部占用和未知占用实例不能获得关闭入口;其主操作仅可重新检测或不可用。 +- 停止 adapter 仅接收当前已保存实例行,校验失败、无注册记录、超时和权限失败均不会影响其他浏览器进程,且 UI 恢复到可理解状态。 +- 覆盖成功关闭、失败恢复、重复点击、启动中、外部关联、调试不可用、刷新与停止结果竞争、键盘/触控访问和焦点恢复。 +- `gofmt`、`go test ./...`、`go vet ./...`、`go build -o build/chub.exe ./cmd/chub`、`scripts/smoke-browser.ps1` 与 `scripts/smoke-windows.ps1` 通过。 + +## 执行记录 + +- 状态:TODO +- 变更:待实现。 +- 验证:待执行。 +- 阻塞:无。 diff --git a/docs/ui/browser-manager.html b/docs/ui/browser-manager.html index ca20bb4..b1ea3a1 100644 --- a/docs/ui/browser-manager.html +++ b/docs/ui/browser-manager.html @@ -262,6 +262,7 @@
新建实例时作为默认建议路径;不会移动或删除已有 profile。
日志仅保存必要诊断信息,并过滤敏感参数。
+

代理

仅保存无认证代理;名称可在实例表单中选择。

支持 http、https、socks4、socks5 的 scheme://host:port;不允许用户名、密码或路径。
@@ -273,13 +274,17 @@

关闭浏览器实例?

将请求浏览器正常退出。未保存的网页内容由浏览器自行处理。

-

编辑实例

修改下次启动使用的保存配置。

用于在实例列表中识别此浏览器环境。
浏览器类型
必须是绝对路径;同一目录不能被多个实例同时使用。
仅支持 http/https;留空时启动浏览器默认页。
实际端口由全局起始端口在启动时分配,不能在此固定。
+

编辑实例

修改下次启动使用的保存配置。

用于在实例列表中识别此浏览器环境。
浏览器类型
必须是绝对路径;同一目录不能被多个实例同时使用。
仅支持 http/https;留空时启动浏览器默认页。
选择已保存的无认证代理;修改后下次启动生效。
实际端口由全局起始端口在启动时分配,不能在此固定。

放弃未保存的更改?

可以先保存配置,或放弃本次编辑并关闭对话框。