From f5b5e95223a20e8f6032dc27a347067935e05b08 Mon Sep 17 00:00:00 2001 From: QiuSW <105186638@qq.com> Date: Sat, 25 Jul 2026 18:07:57 +0800 Subject: [PATCH] docs: define managed browser exit monitoring --- docs/02-requirements.md | 4 +++- docs/04-architecture.md | 1 + docs/06-tasks.md | 1 + docs/07-user-stories.md | 5 ++++ docs/08-interaction-checklist.md | 2 ++ docs/api.md | 4 ++-- docs/current-state.md | 4 ++-- docs/tasks/T-307.md | 37 ++++++++++++++++++++++++++++++ docs/ui/browser-manager.html | 39 ++++++++++++++++++++++++++++++-- 9 files changed, 90 insertions(+), 7 deletions(-) create mode 100644 docs/tasks/T-307.md diff --git a/docs/02-requirements.md b/docs/02-requirements.md index 52d6976..646f19a 100644 --- a/docs/02-requirements.md +++ b/docs/02-requirements.md @@ -7,7 +7,7 @@ | BR-003 | 启动前检测目标 user data dir 是否占用,禁止同一环境重复启动。 | P0 | | BR-004 | 实例列表显示浏览器类型、PID、user data dir、启动时间和状态。 | P0 | | BR-005 | 支持优雅关闭、强制关闭和重启,并明确操作影响。 | P0 | -| BR-006 | 监控启动失败、异常退出、退出码和进程消失。 | P0 | +| BR-006 | 监控 Chub 注册实例的启动失败、异常退出、退出码和进程消失;受管进程意外退出时及时更新 GUI,外部实例只允许刷新检测。 | P0 | | BR-007 | 启动参数按数组传递,不通过 shell 拼接。 | P0 | | BR-008 | 只允许管理工具注册且经过身份校验的 PID。 | P0 | | BR-009 | 支持本地配置持久化,绝不保存浏览器凭证。 | P1 | @@ -35,6 +35,8 @@ - 代理仅接受无认证的 `scheme://host:port` 端点。删除仍被实例引用的代理必须被拒绝;实例保存后按稳定代理 ID 解析最新端点。 - 设置页默认代理输入为空;选择下拉项后回填完整地址,保存更新当前项、未选择时新建。删除未引用项须经确认,确认取消或引用保护不能改变任何实例选择。 - 受管运行实例点击主操作后进入关闭中,成功退出后恢复启动入口;停止失败不自动强制终止,也绝不影响其他 Chrome/Edge。 +- Chub 在当前应用会话中启动的 Chrome/Edge 根进程意外退出后,实例状态立即变为“已退出”,清空运行时 PID/端口,并显示包含实例名称和浏览器类型的可关闭提示;配置与 User Data Dir 保留,关闭提示后启动入口可用。 +- 用户从 Chub 发起优雅停止时,不显示“浏览器已退出”意外退出提示;外部已关联、外部占用、未知占用以及应用关闭后无法继续监控的实例均不弹出此提示。 ## 不做 diff --git a/docs/04-architecture.md b/docs/04-architecture.md index 8cc4594..d464de2 100644 --- a/docs/04-architecture.md +++ b/docs/04-architecture.md @@ -41,6 +41,7 @@ UI/CLI 不直接操作 `exec.Cmd`、Win32 handle 或数据库。Application 层 - Edit:UI 只提交可持久化的实例配置 DTO;运行时 PID、关联端口、占用来源和状态由刷新/启动结果返回,不作为编辑表单保存字段。编辑时保留未公开的非敏感启动选项,活跃或外部关联实例的身份约束字段保持只读。 - Proxy:本地配置保存稳定 `proxy_id` 指向的代理库项(名称与无认证端点);启动 adapter 在后台解析为当前 `LaunchSpec.ProxyServer` 快照。代理不能含 userinfo,删除被引用项会被拒绝;修改代理只影响后续启动,不影响已运行实例。 - GUI stop:主操作只对 Chub registry 中可验证的托管实例调用 `StopProfile(userDataDir, false)`,随后有限等待 registry 释放。外部关联、占用和未知实例永远不调用停止接口;超时或失败不自动强制关闭。 +- 受管退出监控:仅为当前会话由 Chub 启动并获得进程句柄的实例建立一个 `Wait` 监控任务。退出事件携带实例 ID、启动代次和 PID,UI 仅在三者仍匹配且当前未处于 Chub 请求的停止中时,将运行时状态改为“已退出”并排队显示提示;过期事件、外部实例和应用退出后的监控取消均不得生成意外退出提示。 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 4ed16f0..8bafc10 100644 --- a/docs/06-tasks.md +++ b/docs/06-tasks.md @@ -42,6 +42,7 @@ | T-304 | 代理配置库、实例选择与安全启动参数传递 | T-303 | DONE | | T-305 | GUI 受管实例优雅停止与启停状态机 | T-304 | DONE | | T-306 | 精简代理下拉编辑与实例行安全操作布局 | T-304,T-305 | DONE | +| T-307 | 受管浏览器意外退出监控与状态提示 | T-101,T-305,T-306 | DOING | ## Backlog diff --git a/docs/07-user-stories.md b/docs/07-user-stories.md index 3d50ffd..bef0ff5 100644 --- a/docs/07-user-stories.md +++ b/docs/07-user-stories.md @@ -12,6 +12,7 @@ | US-008 | 用户可以刷新已保存实例的运行状态,并通过对话框编辑非运行实例的基本配置而不影响其他浏览器。 | P1 | | US-009 | 用户可以在设置中通过完整代理地址下拉框维护无认证代理,并在新建或编辑实例时从下拉选择器选择代理。 | P1 | | US-010 | 用户可以通过实例行的同一主操作启动或优雅停止 Chub 托管的浏览器,而不会关闭外部浏览器。 | P1 | +| US-011 | 用户手动关闭 Chub 启动的 Chrome/Edge 后,可以立即获知该实例已退出,并重新启动该实例。 | P1 | ## 验收场景 @@ -42,3 +43,7 @@ CLI/API 返回稳定错误码和结构化数据;未知 PID、身份不匹配 ### US-010 受管实例启停 给定 Chub 启动且仍可在 registry 中验证的实例,运行态主操作显示停止图标。点击后先显示停止中,再请求浏览器正常退出;成功后恢复启动图标。给定外部已关联或未知占用的同目录浏览器,Chub 不显示停止入口,也不发送关闭请求。 + +### US-011 受管实例意外退出 + +给定 Chub 在当前会话中启动且处于运行态的 Chrome/Edge,用户关闭最后一个浏览器窗口、浏览器崩溃或进程被外部结束时,Chub 等待该受管根进程句柄并把匹配实例更新为“已退出”。Chub 显示“检测到‘实例名’的 Chrome/Edge 已退出”的可关闭信息提示,说明配置和 User Data Dir 未被删除;关闭提示后焦点回到该实例的启动入口。给定 Chub 已请求优雅停止、外部关联/占用实例、过期启动代次或应用已关闭,Chub 不显示意外退出提示。 diff --git a/docs/08-interaction-checklist.md b/docs/08-interaction-checklist.md index 4627589..9f91927 100644 --- a/docs/08-interaction-checklist.md +++ b/docs/08-interaction-checklist.md @@ -11,6 +11,7 @@ | IX-007 | 实例刷新与编辑 | F5/刷新、双击/Enter/编辑图标、目录选择、保存/取消/Escape | 空闲、刷新中、部分失败、字段错误、脏表单、只读运行态、保存成功 | P1 | | IX-008 | 代理设置与选择器 | 完整地址下拉选择、新增/更新、删除确认;创建/编辑实例选择代理 | 空列表、无代理、格式错误、重复地址、删除取消、被引用删除、保存成功 | P1 | | IX-009 | 实例主启停操作 | 主操作、编辑、删除的固定顺序与间距;启动图标、停止图标、外部重新检测 | 已退出、启动中、托管运行、停止中、失败恢复、外部关联 | P1 | +| IX-010 | 受管浏览器退出提示 | 受管根进程退出后的状态恢复、提示队列和焦点回归 | 意外退出、Chub 请求停止、多个同时退出、过期事件、编辑/确认弹层打开、应用关闭 | P1 | ## 通用规则 @@ -25,3 +26,4 @@ - 双击不是唯一入口:单击只选择行,Enter 和编辑图标打开对话框。打开后焦点位于名称,关闭后返回调用行;有未保存编辑时 Escape 先请求确认。 - 代理选择器必须提供“无代理”和完整地址项,能用键盘选择;设置页默认输入为空,选择项回填地址。删除前先确认完整地址;有引用时保持当前选择、输入与实例选择不变。 - 实例主操作根据归属和状态切换启动、停止、重新检测或禁用,并固定排在编辑和删除之前。停止中禁止重复请求;停止失败恢复原状态并保留焦点。外部关联不显示停止入口;行内图标间距与命中区必须避免误操作。 +- 受管浏览器意外退出是当前会话的异步状态提示,不是用户确认决策:状态先更新为“已退出”,再显示可关闭信息弹层;弹层只含“知道了”,Escape/Enter 均可关闭,关闭后焦点回到对应启动入口。多个退出合并到一个提示;已有编辑、未保存确认或删除确认弹层时排队,不能丢弃输入或叠加模态层。Chub 发起停止、外部实例、代次/PID 不匹配和应用退出取消监控时均不提示。 diff --git a/docs/api.md b/docs/api.md index a45d04a..81944a8 100644 --- a/docs/api.md +++ b/docs/api.md @@ -30,7 +30,7 @@ GUI 将优雅停止封装为异步 `InstanceStopper(ctx, InstanceRow) error` ada 参数数组由 `internal/platform/browser.BuildArgs` 生成;Chrome/Edge executable 由同包 `Discoverer.Resolve` 解析。二者都不接收 shell 命令行字符串。CDP 端点检测和端口分配均通过 platform port 执行,UI 只消费 DTO 结果。 -状态刷新和目录选择必须通过后台 adapter 回到 UI 结果通道;UI frame 不直接做进程、文件或 CDP I/O。刷新不构成外部实例接管授权。 +状态刷新、受管进程退出和目录选择必须通过后台 adapter 回到 UI 结果通道;UI frame 不直接做进程、文件或 CDP I/O。受管退出结果至少包含实例 ID、启动代次、受管 PID、退出码和是否为 Chub 请求停止;UI 以实例 ID、代次和 PID 丢弃过期结果。刷新不构成外部实例接管授权。 ## CLI 当前合约 @@ -52,7 +52,7 @@ chub events | --- | --- | --- | | `browser.started` | 新实例完成启动 | InstanceView | | `browser.status.changed` | 状态变化 | instance_id、status、pid、remote_debug_port、ownership、error_code | -| `browser.exited` | 进程退出 | instance_id、exit_code | +| `browser.exited` | Chub 当前会话受管根进程退出 | instance_id、launch_generation、pid、exit_code、expected_stop | 事件禁止携带密码、Cookie、Token、完整代理认证信息或原始系统堆栈。 diff --git a/docs/current-state.md b/docs/current-state.md index 7ed5ac3..edaa809 100644 --- a/docs/current-state.md +++ b/docs/current-state.md @@ -3,11 +3,11 @@ ## 快照 - 日期:2026-07-25 -- 阶段:Phase 3 真实实例操作(T-301 至 T-306、T-201 至 T-208 已完成) +- 阶段:Phase 3 真实实例操作(T-307 进行中;T-301 至 T-306、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 托管运行或调试不可用时显示停止,在启动/停止中禁用,在外部关联、外部占用或未知占用时仅重新检测;优雅停止通过后台 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 - 浏览器核心:设计参考来自 `D:\OPC\shop_helm\internal\platform\chrome`,尚未复制或接入本项目 -- blocker:无;T-306 已完成,下一项从 Backlog 或后续需求确定。 +- blocker:无;当前执行 T-307 的受管浏览器意外退出监控与提示。 ## 当前目录 diff --git a/docs/tasks/T-307.md b/docs/tasks/T-307.md new file mode 100644 index 0000000..12887d1 --- /dev/null +++ b/docs/tasks/T-307.md @@ -0,0 +1,37 @@ +--- +id: T-307 +title: 受管浏览器意外退出监控与状态提示 +phase: 3 +deps: [T-101, T-305, T-306] +status: DOING +created: 2026-07-25 +owner: codex +--- + +## 需求与背景 + +当前刷新操作可以检查已保存实例的状态,但 Chub 已启动的浏览器被用户手动关闭、崩溃或被外部结束后,GUI 不会立即反馈。用户需要知道对应 Chrome/Edge 已退出,并能安全地重新启动它;这一能力不得变成扫描或接管外部浏览器的通道。 + +## 方案与边界 + +- 仅在当前 Chub 应用会话中,为成功启动并注册的 Chrome/Edge 根进程建立一个后台 `Wait` 监控;每个实例最多一个监控 goroutine。外部已关联、外部占用、未知占用和应用启动前已存在的进程均不监控、不提示。 +- 退出结果必须携带稳定实例 ID、启动代次、受管 PID、退出码和 `expectedStop` 标记。UI 只接受仍与当前运行时记录完全匹配的结果,忽略删除、重启或再次启动后的迟到结果。 +- 监控到意外退出时,先将实例运行时状态改为“已退出”并清空 PID、端口和归属,再排队显示信息提示。提示使用事实性文案“检测到‘实例名’的 Chrome/Edge 已退出”,不声称能区分手动关闭、崩溃或任务管理器结束;说明配置与 User Data Dir 未被删除。 +- 提示只有“知道了”操作,Enter/Escape 都可关闭;关闭后焦点返回对应实例的启动按钮。多个实例同时退出时合并显示,已有编辑、未保存确认或删除确认弹层时延后显示,不能叠加或夺走用户输入。 +- Chub 请求的优雅停止属于预期退出:停止结果与退出监控只产生一次最终状态,不显示意外退出提示。应用关闭时取消 UI 订阅/提示发布,但不能杀死浏览器;`Wait` 取消也不能隐式关闭进程。 +- 运行时 PID、端口、退出码、启动代次和提示队列都不写入实例配置;日志和事件不含代理认证或浏览器私密数据。 + +## 验收要点 + +- 成功启动的受管 Chrome/Edge 进程意外结束后,不经 F5 就在合理时间内将对应实例改为“已退出”,启动操作恢复可用;外部状态与其他实例不变。 +- 退出提示含实例名称、浏览器类型和“配置、User Data Dir 未删除”的恢复说明;关闭、Escape、Enter 后焦点回到该实例启动按钮。多个退出合并为一个提示。 +- 主动停止不弹意外退出提示;停止失败、启动失败、外部关联、PID/代次不匹配、已删除实例、重启前一代退出和窗口关闭后的迟到事件均不改变新状态或弹窗。 +- 监控、CDP/进程等待和结果发送均在后台;UI Frame 仅消费通道结果。没有按进程名枚举、终止或接管 Chrome/Edge 的逻辑。 +- 覆盖受管退出、预期停止、重复/过期退出、启动后快速退出、多个退出、提示关闭/焦点恢复和应用关闭取消;`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/ui/browser-manager.html b/docs/ui/browser-manager.html index dc7ac48..b694661 100644 --- a/docs/ui/browser-manager.html +++ b/docs/ui/browser-manager.html @@ -161,6 +161,8 @@ .dialog-inner h2 { margin: 0; font-size: 19px; } .dialog-inner p { margin: 0; color: var(--muted); line-height: 1.55; } .dialog-warning { padding: 11px 12px; border-radius: 9px; background: var(--danger-soft); color: var(--danger); font-size: 12px; line-height: 1.45; } + .dialog-list { margin: -3px 0 0; padding-left: 20px; color: var(--text); display: grid; gap: 5px; } + .dialog-list li { line-height: 1.45; } .dialog-actions { display: flex; justify-content: flex-end; gap: 8px; } .edit-form { display: grid; gap: 18px; } .edit-form .path-field { grid-template-columns: minmax(0, 1fr) auto; } @@ -218,7 +220,7 @@ -
PROTOTYPE · 仅供交互确认,非生产实现
+
PROTOTYPE · 仅供交互确认,非生产实现 · Ctrl+Shift+E 模拟受管浏览器退出