From f8aaaeda2113fa094b1bcf9365bc4bf55a13d453 Mon Sep 17 00:00:00 2001 From: QiuSW <105186638@qq.com> Date: Wed, 22 Jul 2026 14:51:11 +0800 Subject: [PATCH] chore: establish chub go foundation --- .gitignore | 9 + AGENTS.md | 5 + README.md | 7 + cmd/chub/main.go | 18 ++ docs/00-ai-start-here.md | 30 +++ docs/01-vision.md | 21 ++ docs/02-requirements.md | 29 +++ docs/03-tech-stack.md | 23 ++ docs/04-architecture.md | 41 +++ docs/05-coding-rules.md | 14 + docs/06-tasks.md | 33 +++ docs/07-user-stories.md | 24 ++ docs/08-interaction-checklist.md | 18 ++ docs/README.md | 21 ++ docs/agent-context.json | 12 + docs/agent-context.md | 7 + docs/agent-context.schema.json | 12 + docs/api.md | 68 +++++ docs/current-state.md | 34 +++ docs/design/README.md | 5 + docs/tasks/README.md | 3 + docs/tasks/T-001.md | 34 +++ docs/tasks/_template.md | 27 ++ docs/ui/README.md | 38 +++ docs/ui/browser-manager.html | 312 +++++++++++++++++++++++ go.mod | 4 + internal/platform/logging/logger.go | 15 ++ internal/platform/logging/logger_test.go | 22 ++ 28 files changed, 886 insertions(+) create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 README.md create mode 100644 cmd/chub/main.go create mode 100644 docs/00-ai-start-here.md create mode 100644 docs/01-vision.md create mode 100644 docs/02-requirements.md create mode 100644 docs/03-tech-stack.md create mode 100644 docs/04-architecture.md create mode 100644 docs/05-coding-rules.md create mode 100644 docs/06-tasks.md create mode 100644 docs/07-user-stories.md create mode 100644 docs/08-interaction-checklist.md create mode 100644 docs/README.md create mode 100644 docs/agent-context.json create mode 100644 docs/agent-context.md create mode 100644 docs/agent-context.schema.json create mode 100644 docs/api.md create mode 100644 docs/current-state.md create mode 100644 docs/design/README.md create mode 100644 docs/tasks/README.md create mode 100644 docs/tasks/T-001.md create mode 100644 docs/tasks/_template.md create mode 100644 docs/ui/README.md create mode 100644 docs/ui/browser-manager.html create mode 100644 go.mod create mode 100644 internal/platform/logging/logger.go create mode 100644 internal/platform/logging/logger_test.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eb6176a --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +build/ +dist/ +*.exe +*.log +*.db +*.db-* +.vscode/ +.idea/ + diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..7f30cad --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,5 @@ +# Chub Agent 规则 + +开始工作前必须阅读 [`docs/00-ai-start-here.md`](docs/00-ai-start-here.md)、[`docs/05-coding-rules.md`](docs/05-coding-rules.md)、[`docs/current-state.md`](docs/current-state.md) 和当前任务文件。 + +需求、架构、API 和验收文档是编码约束。所有进程控制必须限制在用户明确授权的实例范围内;禁止按进程名批量终止系统中的全部 Chrome/Edge。 diff --git a/README.md b/README.md new file mode 100644 index 0000000..d9e0756 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# Chub 浏览器实例管理工具 + +Chub 是面向 Windows 桌面和本地自动化场景的 Chrome/Edge 实例启动与生命周期管理工具。它支持指定浏览器可执行文件、`user-data-dir`、profile、启动 URL、代理和额外参数,并安全管理由工具启动的实例。 + +当前处于文档与交互原型阶段,尚未开始生产代码实现。 + +文档入口:[`docs/README.md`](docs/README.md)。MVP 闭环是“配置实例 -> 启动 -> 查看状态 -> 优雅关闭/强制关闭/重启”。首版不保存密码、Cookie、Token,不做平台自动登录和 RPA。 diff --git a/cmd/chub/main.go b/cmd/chub/main.go new file mode 100644 index 0000000..bb92dd7 --- /dev/null +++ b/cmd/chub/main.go @@ -0,0 +1,18 @@ +package main + +import ( + "context" + "fmt" + "os" + + "chub/internal/platform/logging" +) + +const version = "0.1.0-dev" + +func main() { + ctx := context.Background() + logger := logging.New(os.Stderr) + logger.InfoContext(ctx, "chub starting", "version", version) + fmt.Printf("Chub %s\n", version) +} diff --git a/docs/00-ai-start-here.md b/docs/00-ai-start-here.md new file mode 100644 index 0000000..367ddb3 --- /dev/null +++ b/docs/00-ai-start-here.md @@ -0,0 +1,30 @@ +# AI 开发入口 + +Chub 是 Windows 本地 Chrome/Edge 实例管理工具。第一版只做:配置并启动浏览器实例、查看工具管理的实例、检测 profile 占用、优雅关闭/强制关闭/重启和状态事件。 + +## 必读顺序 + +1. 根目录 `AGENTS.md` +2. [`01-vision.md`](01-vision.md) +3. [`02-requirements.md`](02-requirements.md) +4. [`03-tech-stack.md`](03-tech-stack.md) +5. [`04-architecture.md`](04-architecture.md) +6. [`05-coding-rules.md`](05-coding-rules.md) +7. 与任务相关的 [`api.md`](api.md)、[`07-user-stories.md`](07-user-stories.md) 和 [`08-interaction-checklist.md`](08-interaction-checklist.md) +8. [`06-tasks.md`](06-tasks.md)、任务文件和 [`current-state.md`](current-state.md) + +## 固定开工流程 + +1. 确认当前目录为 `D:\OPC\chub`。 +2. 检查 Git 状态和最近提交,不覆盖已有未提交改动。 +3. 读取当前状态,确认基线和 blocker。 +4. 领取一个依赖已完成的 TODO 任务,编辑任务状态为 DOING。 +5. 完成代码、测试和任务中列出的 Windows smoke 后再标记 DONE。 +6. 将验证结果写入任务文件,并更新 `current-state.md`。 + +## 关键边界 + +- 只管理本工具启动并注册的实例;外部实例默认只检测,不接管。 +- 禁止执行 `taskkill /IM chrome.exe`、`/IM msedge.exe` 或等价的全局终止。 +- 不把密码、Cookie、Token 或代理认证信息写入配置、日志、命令行或事件。 +- UI 不执行 I/O;浏览器启动、进程查询和关闭均在后台任务中完成。 diff --git a/docs/01-vision.md b/docs/01-vision.md new file mode 100644 index 0000000..2ba8c29 --- /dev/null +++ b/docs/01-vision.md @@ -0,0 +1,21 @@ +# 产品愿景 + +Chub 为 Windows 用户提供一个可复用的浏览器实例控制层,将 Chrome/Edge 的可执行文件、`user-data-dir`、profile、启动参数和生命周期统一管理。 + +## 用户 + +- 需要同时运行多个隔离浏览器环境的运营和测试人员。 +- 需要脚本可靠启动和回收浏览器的开发人员。 +- 需要桌面化观察浏览器状态的维护人员。 + +## 原则 + +1. 实例身份优先:PID、命令行、浏览器类型和 user data dir 必须能互相校验。 +2. 安全关闭优先:先优雅关闭,明确确认后才强制终止。 +3. 外部进程不误伤:不按进程名全局操作。 +4. 参数透明:展示实际启动参数,但隐藏敏感信息。 +5. 本地优先:MVP 不需要云服务和平台账号。 + +## 非目标 + +首版不保存凭证、不自动登录、不绕过验证码、不做反指纹/防关联、不做平台 RPA、不承诺接管任意外部浏览器、不做远程主机进程管理。 diff --git a/docs/02-requirements.md b/docs/02-requirements.md new file mode 100644 index 0000000..da45d0e --- /dev/null +++ b/docs/02-requirements.md @@ -0,0 +1,29 @@ +# 产品需求 + +| ID | 需求 | 优先级 | +| --- | --- | --- | +| BR-001 | 支持 Chrome 和 Edge,允许自动发现或手动指定 executable。 | P0 | +| BR-002 | 启动配置支持 `user-data-dir`、`profile-directory`、URL、代理、headless 和额外参数。 | P0 | +| BR-003 | 启动前检测目标 user data dir 是否占用,禁止同一环境重复启动。 | P0 | +| BR-004 | 实例列表显示浏览器类型、PID、user data dir、启动时间和状态。 | P0 | +| BR-005 | 支持优雅关闭、强制关闭和重启,并明确操作影响。 | P0 | +| BR-006 | 监控启动失败、异常退出、退出码和进程消失。 | P0 | +| BR-007 | 启动参数按数组传递,不通过 shell 拼接。 | P0 | +| BR-008 | 只允许管理工具注册且经过身份校验的 PID。 | P0 | +| BR-009 | 支持本地配置持久化,绝不保存浏览器凭证。 | P1 | +| BR-010 | 提供 CLI 或本地 API,供脚本启动、查询和关闭实例。 | P1 | +| BR-011 | 可选启用本地 CDP,用于读取窗口/tab 信息;默认关闭。 | P2 | + +## 验收标准 + +- 两个不同 user data dir 可以并行启动,互不阻塞。 +- 同一 user data dir 的第二次启动被拒绝,并显示已知 PID/来源。 +- 关闭操作不会影响其他 Chrome/Edge 实例。 +- 工具重启后,无法确认身份的外部进程只能报告 UNKNOWN,不得被关闭。 +- 异常退出后状态在合理时间内变为 EXITED/FAILED。 +- 参数中不出现密码、Cookie、Token 或代理 userinfo。 +- UI 覆盖加载、空、占用、启动失败、关闭中、成功和权限错误。 + +## 不做 + +全局杀 Chrome/Edge、自动登录、页面操作、验证码处理、平台 API 集成、远程控制和跨用户权限提升。 diff --git a/docs/03-tech-stack.md b/docs/03-tech-stack.md new file mode 100644 index 0000000..2a0c998 --- /dev/null +++ b/docs/03-tech-stack.md @@ -0,0 +1,23 @@ +# 技术栈与验证 + +| 层 | 选择 | 约束 | +| --- | --- | --- | +| 核心 | Go 1.23(当前基线) | 标准库优先,Windows API 通过小型适配层封装;升级到 Go 1.25+ 需重新验证构建。 | +| Windows UI | Go Gio(版本待锁定) | UI 线程不执行进程和文件 I/O;实现前制作 HTML 原型。 | +| 持久化 | SQLite 或 JSON(MVP 前决策) | 配置不保存密码、Cookie、Token。 | +| 进程 | `os/exec` + Windows API | 参数数组启动;Job Object、窗口消息和句柄按职责封装。 | +| API | 本地 CLI,后续可选 loopback HTTP/WebSocket | 默认只监听本机。 | + +## 验证命令 + +建立代码后同步并执行: + +```powershell +go test ./... +go vet ./... +go build ./cmd/chub +``` + +Windows smoke 至少覆盖真实 Chrome/Edge 临时 user data dir、双实例、重复占用、优雅关闭、强制关闭和异常退出。 + +新增依赖必须说明版本、许可证、Windows 兼容性和打包影响。核心启动和身份校验优先使用标准库和 Win32 API。 diff --git a/docs/04-architecture.md b/docs/04-architecture.md new file mode 100644 index 0000000..375aa76 --- /dev/null +++ b/docs/04-architecture.md @@ -0,0 +1,41 @@ +# 架构设计 + +```text +Gio UI / CLI + ↓ DTO + commands +Application BrowserManager + ↓ ports +Platform Windows + ├─ executable discovery + ├─ launcher + ├─ process identity / query + ├─ graceful close / force kill + ├─ Job Object + └─ profile occupancy inspector + ↓ +Chrome / Edge processes +``` + +UI/CLI 不直接操作 `exec.Cmd`、Win32 handle 或数据库。Application 层维护实例状态和授权边界;Platform 层只提供系统能力。 + +## 实例身份 + +每个实例至少记录 `instance_id`、browser kind、executable、root PID、user data dir、profile directory、非敏感参数摘要、创建时间、状态和退出码。 + +进程身份校验至少比较 PID 存活、executable、命令行中的规范化 `--user-data-dir` 和注册记录。无法确认身份时只能报告 UNKNOWN,不允许关闭。 + +## 生命周期 + +`CREATED -> STARTING -> RUNNING -> STOPPING -> EXITED`;启动失败为 `FAILED`,无法确认的外部实例为 `UNKNOWN`。 + +- Start:规范化路径和参数,检查 user data dir,占用检查通过后注册 pending,再启动。 +- Stop:校验身份,优先窗口关闭/浏览器协议,超时后经明确授权强制终止。 +- Force stop:使用实例 Job Object 或已校验进程树,不按名称全局终止。 +- Restart:旧实例退出或完成超时处理后重新执行同一配置。 +- App shutdown:取消监控,不默认终止用户浏览器。 + +Chrome 和 Edge 共用大部分 Chromium 参数,但 executable 默认路径、进程名称和安装方式不同,通过 `BrowserDefinition` 封装差异。 + +## 风险 + +Chromium 可能复用已有进程;浏览器是多进程架构;管理员权限进程可能无法查询;强制终止可能损坏 profile;CDP 端口开放到非 loopback 会形成控制风险,MVP 默认关闭。 diff --git a/docs/05-coding-rules.md b/docs/05-coding-rules.md new file mode 100644 index 0000000..6c1b06b --- /dev/null +++ b/docs/05-coding-rules.md @@ -0,0 +1,14 @@ +# 编码规则 + +1. 启动参数使用 `exec.Command(executable, args...)`,不拼 shell 字符串。 +2. 关闭前必须校验 PID、executable 和 user data dir;无法确认则拒绝。 +3. 禁止 `taskkill /IM chrome.exe`、`/IM msedge.exe` 或等效全局终止。 +4. 默认不接管外部实例;接管能力必须新增需求、安全评审和测试。 +5. 日志、错误、事件和持久化不得包含密码、Cookie、Token、代理认证或完整秘密参数。 +6. 用户路径必须规范化并有绝对路径、权限和 Windows 长路径测试。 +7. I/O 方法接收 `context.Context`,取消管理任务不能隐式杀死浏览器。 +8. 每个实例最多一个监控 goroutine;退出后释放 registry 和句柄。 +9. 不在 UI frame 或事件回调中执行阻塞 I/O;共享状态必须有明确所有权。 +10. 错误使用稳定 code 或 typed error,底层 cause 只进入诊断日志。 +11. UI 的加载、空、失败、确认和恢复路径必须写入交互清单。 +12. API 使用 DTO,不暴露平台句柄、SQL 行或原始系统堆栈。 diff --git a/docs/06-tasks.md b/docs/06-tasks.md new file mode 100644 index 0000000..d0a23b5 --- /dev/null +++ b/docs/06-tasks.md @@ -0,0 +1,33 @@ +# 任务路线图 + +状态:TODO、DOING、DONE、BLOCKED。未启用协作服务时以 `docs/tasks/T-<编号>.md` 为单任务权威。 + +## Phase 0:地基 + +| ID | 任务 | 依赖 | 状态 | +| --- | --- | --- | --- | +| T-001 | 建立 Go module、命令入口、日志和测试基座 | - | TODO | +| T-002 | 固化 BrowserDefinition、LaunchSpec、错误和事件合约 | T-001 | TODO | +| T-003 | 实现可测试的参数构造和 Chrome/Edge 发现 | T-002 | TODO | + +## Phase 1:进程核心 + +| ID | 任务 | 依赖 | 状态 | +| --- | --- | --- | --- | +| T-101 | 实现启动、registry、Wait 和 profile 占用检测 | T-003 | TODO | +| T-102 | 实现 Windows 进程身份查询和外部实例保守识别 | T-101 | TODO | +| T-103 | 实现优雅关闭、强制关闭和 Job Object 进程树 | T-102 | TODO | +| T-104 | Chrome/Edge 双实例、异常退出和权限失败 smoke | T-103 | TODO | + +## Phase 2:产品外壳 + +| ID | 任务 | 依赖 | 状态 | +| --- | --- | --- | --- | +| T-201 | HTML 原型、Gio 外壳和实例列表/表单 | T-002 | TODO | +| T-202 | 本地配置持久化和实例恢复 | T-101 | TODO | +| T-203 | CLI 合约和本地事件推送 | T-103 | TODO | +| T-204 | UI 全状态验收与 Windows 打包 smoke | T-201,T-202,T-203 | TODO | + +## Backlog + +CDP tab/window 管理、导入导出启动配置、进程资源监控、多用户权限和远程主机管理。 diff --git a/docs/07-user-stories.md b/docs/07-user-stories.md new file mode 100644 index 0000000..65189b2 --- /dev/null +++ b/docs/07-user-stories.md @@ -0,0 +1,24 @@ +# 用户故事 + +| ID | 用户故事 | 优先级 | +| --- | --- | --- | +| US-001 | 用户可以创建 Chrome/Edge 实例配置,并指定 user data dir、profile 和 URL。 | P0 | +| US-002 | 用户可以启动两个不同隔离环境,并看到各自 PID 和运行状态。 | P0 | +| US-003 | 用户可以发现同一 user data dir 已被占用,避免重复启动。 | P0 | +| US-004 | 用户可以优雅关闭实例;无响应时在确认后强制终止。 | P0 | +| US-005 | 用户可以重启指定实例,并确认旧实例已经退出。 | P0 | +| US-006 | 用户可以通过 CLI/API 查询、启动和关闭实例。 | P1 | + +## 验收场景 + +### US-003 占用保护 + +给定一个已运行的 Chrome/Edge,使用相同规范化 user data dir 启动时,系统拒绝第二次启动,显示已知 PID 和占用来源,并保留原配置。 + +### US-004 安全关闭 + +用户点击关闭后按钮进入 STOPPING;浏览器正常退出则显示 EXITED;等待超时后只显示强制终止确认,不自动扩大关闭范围。 + +### US-006 CLI/API + +CLI/API 返回稳定错误码和结构化数据;未知 PID、身份不匹配和权限不足必须拒绝操作并给出恢复建议。 diff --git a/docs/08-interaction-checklist.md b/docs/08-interaction-checklist.md new file mode 100644 index 0000000..d48d290 --- /dev/null +++ b/docs/08-interaction-checklist.md @@ -0,0 +1,18 @@ +# 交互清单 + +| ID | 页面/组件 | 交互 | 状态覆盖 | 优先级 | +| --- | --- | --- | --- | --- | +| IX-001 | 实例列表 | 新建、启动、刷新、查看详情 | 加载、空、运行、退出、失败 | P0 | +| IX-002 | 启动表单 | 浏览器类型、路径、user data dir、URL、参数校验 | 默认、校验错误、提交中、成功 | P0 | +| IX-003 | 实例详情 | 优雅关闭、强制终止、重启 | 运行中、关闭中、超时确认、权限错误 | P0 | +| IX-004 | 占用提示 | 查看 PID/来源并重试 | 被占用、身份未知、已释放 | P0 | +| IX-005 | 设置 | 默认路径、日志和数据目录 | 保存成功、路径无效、权限拒绝 | P1 | + +## 通用规则 + +- 异步操作不阻塞窗口线程,提交中禁用重复按钮。 +- 危险操作说明影响范围,默认焦点在取消;Escape 可取消。 +- 进程状态同时使用文字、图标和颜色,不只用颜色表达。 +- 关闭完成后焦点回到实例行;失败时保留表单和用户输入。 +- 验证 960x640、600x500、100%–200% 缩放、键盘、深色和高对比度回退。 +- 不依赖悬停或触控手势完成关键任务。 diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..75e4414 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,21 @@ +# 项目文档导航 + +Chub 是 Windows 本地 Chrome/Edge 实例管理工具,首版闭环是“配置实例 -> 启动 -> 查看状态 -> 关闭/重启”。 + +| 文档 | 职责 | +| --- | --- | +| [`00-ai-start-here.md`](00-ai-start-here.md) | agent 阅读顺序和开工流程 | +| [`01-vision.md`](01-vision.md) | 产品目标、用户和非目标 | +| [`02-requirements.md`](02-requirements.md) | 功能范围、优先级和验收 | +| [`03-tech-stack.md`](03-tech-stack.md) | 技术选型和验证命令 | +| [`04-architecture.md`](04-architecture.md) | 分层、模块、进程模型和数据流 | +| [`05-coding-rules.md`](05-coding-rules.md) | 安全、Windows、Go 和 UI 规则 | +| [`06-tasks.md`](06-tasks.md) | 阶段路线图和任务池 | +| [`07-user-stories.md`](07-user-stories.md) | 用户故事与验收场景 | +| [`08-interaction-checklist.md`](08-interaction-checklist.md) | 页面状态、交互和无障碍 | +| [`api.md`](api.md) | 本地模块、CLI、事件和错误合约 | +| [`current-state.md`](current-state.md) | 当前代码现实和下一步 | +| [`agent-context.json`](agent-context.json) | 机器可读上下文路由 | +| [`design/README.md`](design/README.md) | HTML 原型输入和交接规则 | +| [`ui/README.md`](ui/README.md) | Chub UI 原型、评审场景和 Gio 交接 | +| [`tasks/README.md`](tasks/README.md) | 一任务一文件约定 | diff --git a/docs/agent-context.json b/docs/agent-context.json new file mode 100644 index 0000000..871fc2c --- /dev/null +++ b/docs/agent-context.json @@ -0,0 +1,12 @@ +{ + "schema":"docs/agent-context.schema.json", + "schema_version":1, + "bootstrap":["AGENTS.md","docs/00-ai-start-here.md","docs/05-coding-rules.md","docs/current-state.md"], + "routes":{ + "documentation":["README.md","docs/README.md","docs/01-vision.md","docs/02-requirements.md","docs/07-user-stories.md","docs/08-interaction-checklist.md"], + "browser":["docs/02-requirements.md","docs/03-tech-stack.md","docs/04-architecture.md","docs/05-coding-rules.md","docs/api.md"], + "ui":["docs/07-user-stories.md","docs/08-interaction-checklist.md","docs/ui/README.md","docs/04-architecture.md"], + "deploy":["docs/03-tech-stack.md","docs/current-state.md"] + }, + "tasks":{"roadmap":"docs/06-tasks.md","directory":"docs/tasks/","template":"docs/tasks/_template.md"} +} diff --git a/docs/agent-context.md b/docs/agent-context.md new file mode 100644 index 0000000..aa13a2f --- /dev/null +++ b/docs/agent-context.md @@ -0,0 +1,7 @@ +# Agent 上下文清单 + +`agent-context.json` 是按任务类型选择文档的机器可读清单,Schema 位于 `agent-context.schema.json`。 + +首次接入读取 bootstrap;日常任务再读取对应 route 和唯一任务文件。需求以 `02-requirements.md` 为准,架构/API 以 `04-architecture.md` 与 `api.md` 为准,代码行为以测试和真实 Windows smoke 为准。 + +本项目未启用 Gitea 协调。任务状态以 `docs/tasks/T-<编号>.md` 的 frontmatter 和 `06-tasks.md` 为准;外部状态不可用时可以继续已领取任务,但不得猜测其他 agent 的写路径。 diff --git a/docs/agent-context.schema.json b/docs/agent-context.schema.json new file mode 100644 index 0000000..cb00103 --- /dev/null +++ b/docs/agent-context.schema.json @@ -0,0 +1,12 @@ +{ + "$schema":"https://json-schema.org/draft/2020-12/schema", + "type":"object", + "required":["schema","schema_version","bootstrap","routes","tasks"], + "properties":{ + "schema":{"const":"docs/agent-context.schema.json"}, + "schema_version":{"const":1}, + "bootstrap":{"type":"array","items":{"type":"string"},"minItems":1}, + "routes":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}, + "tasks":{"type":"object","required":["roadmap","directory","template"]} + } +} diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..7b6aa4f --- /dev/null +++ b/docs/api.md @@ -0,0 +1,68 @@ +# API / 模块合约 + +MVP 首先提供本地 Go application service 和 CLI;是否增加 loopback HTTP/WebSocket 在 T-203 决策。时间使用 RFC3339,路径使用规范化绝对路径。 + +## 核心类型 + +```go +type BrowserKind string +const ( + BrowserChrome BrowserKind = "chrome" + BrowserEdge BrowserKind = "edge" +) + +type LaunchSpec struct { + Kind BrowserKind + Executable string + UserDataDir string + ProfileDirectory string + TargetURL string + ProxyServer string + Headless bool + ExtraArgs []string +} + +type InstanceView struct { + ID string + Kind BrowserKind + PID int + UserDataDir string + Status string + ExitCode *int +} +``` + +## Application service + +```go +type BrowserManager interface { + Start(ctx context.Context, spec LaunchSpec) (InstanceView, error) + List(ctx context.Context) ([]InstanceView, error) + Get(ctx context.Context, id string) (InstanceView, error) + Stop(ctx context.Context, id string, force bool) error + Restart(ctx context.Context, id string) (InstanceView, error) +} +``` + +`Stop(force=false)` 只执行优雅关闭;强制关闭必须由明确用户动作或 CLI `--force` 触发。 + +## CLI 目标形状 + +```text +chub start --browser chrome --user-data-dir [--profile-directory ] [--url ] +chub list +chub stop [--force] +chub restart +``` + +退出码:0 成功,2 参数错误,3 profile 占用,4 实例不存在,5 身份校验失败,6 系统权限/进程错误。 + +## 事件 + +| 事件 | 触发 | 负载 | +| --- | --- | --- | +| `browser.started` | 新实例完成启动 | InstanceView | +| `browser.status.changed` | 状态变化 | instance_id、status、pid、error_code | +| `browser.exited` | 进程退出 | instance_id、exit_code | + +事件禁止携带密码、Cookie、Token、完整代理认证信息或原始系统堆栈。 diff --git a/docs/current-state.md b/docs/current-state.md new file mode 100644 index 0000000..0ab2992 --- /dev/null +++ b/docs/current-state.md @@ -0,0 +1,34 @@ +# 当前实现状态 + +## 快照 + +- 日期:2026-07-22 +- 阶段:文档与交互原型准备 +- 代码:已建立 Go module `chub`、`cmd/chub` 入口和 logging 测试基座,T-001 已完成 +- UI:尚未实现;P0 页面需要先制作 HTML 原型 +- 浏览器核心:设计参考来自 `D:\OPC\shop_helm\internal\platform\chrome`,尚未复制或接入本项目 +- blocker:无;下一个任务为 T-002 + +## 当前目录 + +| 路径 | 状态 | 说明 | +| --- | --- | --- | +| `docs/` | 已建立 | Harness Coding 项目文档 | +| `docs/ui/` | 已建立 | Chub HTML 交互原型 | +| `docs/tasks/` | 已建立 | 一任务一文件目录 | +| `cmd/` | 待建 | Go 命令入口 | +| `cmd/chub/` | 已建立 | Go 命令入口和版本输出 | +| `internal/platform/logging/` | 已建立 | 可注入的结构化 JSON 日志 | +| `internal/` | 部分建立 | 后续扩展 application/domain/platform 实现 | + +## 基线命令 + +当前已具备最小可运行入口: + +```text +go test ./... +go vet ./... +go build -o build/chub.exe ./cmd/chub +``` + +`shop_helm` 仅是参考项目,不是本项目代码目录。当前基线已通过 T-001 的测试、vet、构建和启动验证。 diff --git a/docs/design/README.md b/docs/design/README.md new file mode 100644 index 0000000..a5f44bf --- /dev/null +++ b/docs/design/README.md @@ -0,0 +1,5 @@ +# 设计原型输入约定 + +P0 UI 在正式 Gio 实现前制作单文件 HTML 原型。原型只用假数据,标注“PROTOTYPE - 仅供交互确认”,不连接真实进程、配置或浏览器。 + +原型用于枚举页面控件;加载、空、错误、确认、取消、键盘和无障碍行为以 `08-interaction-checklist.md` 为准。原型确认后,将控件和状态映射到 IX 条目,再按 `04-architecture.md` 实现,禁止逐字复制 HTML 到生产代码。 diff --git a/docs/tasks/README.md b/docs/tasks/README.md new file mode 100644 index 0000000..d71bb2b --- /dev/null +++ b/docs/tasks/README.md @@ -0,0 +1,3 @@ +# 一任务一文件 + +任务文件使用 `T-<编号>.md`,包含 frontmatter:`id`、`title`、`phase`、`deps`、`status`、`created`、`owner`。每轮只领取一个依赖已完成的 TODO;验证完成后标记 DONE,并在任务文件记录变更、命令、结果和残余风险。 diff --git a/docs/tasks/T-001.md b/docs/tasks/T-001.md new file mode 100644 index 0000000..f814049 --- /dev/null +++ b/docs/tasks/T-001.md @@ -0,0 +1,34 @@ +--- +id: T-001 +title: 建立 Go module、命令入口、日志和测试基座 +phase: 0 +deps: [] +status: DONE +created: 2026-07-22 +owner: codex +--- + +## 需求与背景 + +Chub 目前只有产品、架构和 UI 原型文档,需要建立可编译、可测试、可继续扩展的 Go 工程基线。 + +## 方案与边界 + +- 建立 module `chub` 和 `cmd/chub` 命令入口。 +- 增加可注入输出的结构化 JSON logger 和单元测试。 +- 固定当前可验证的 Go 1.23 基线;UI、浏览器进程和持久化不在本任务实现。 + +## 验收要点 + +- `go test ./...` 通过。 +- `go vet ./...` 通过。 +- `go run ./cmd/chub` 输出版本并正常退出。 +- `go build -o build/chub.exe ./cmd/chub` 通过。 + +## 执行记录 + +- 状态:DONE +- 变更:建立 `chub` Go module、`cmd/chub` 入口、可注入 JSON logger、logger 单元测试和 `.gitignore`;基线固定为 Go 1.23。 +- 验证:`go test ./...`、`go vet ./...`、`go build -o build/chub.exe ./cmd/chub`、`go run ./cmd/chub` 均通过。 +- 阻塞: +- 残余风险: diff --git a/docs/tasks/_template.md b/docs/tasks/_template.md new file mode 100644 index 0000000..7865545 --- /dev/null +++ b/docs/tasks/_template.md @@ -0,0 +1,27 @@ +--- +id: T-XXX +title: 一句话任务名 +phase: 0 +deps: [] +status: TODO +created: 2026-07-22 +owner: +--- + +## 需求与背景 + +## 方案与边界 + +## 验收要点 + +- 自动化命令: +- Windows smoke: +- 安全边界: + +## 执行记录 + +- 状态: +- 变更: +- 验证: +- 阻塞: +- 残余风险: diff --git a/docs/ui/README.md b/docs/ui/README.md new file mode 100644 index 0000000..93d4f2b --- /dev/null +++ b/docs/ui/README.md @@ -0,0 +1,38 @@ +# Chub UI 原型 + +原型文件:[`browser-manager.html`](browser-manager.html) + +## 用途 + +该文件用于确认 Chub 的信息架构、实例列表密度、启动配置表单、详情抽屉和危险操作反馈,不代表 Gio 的最终控件、窗口材质、DPI 或无障碍实现已经完成。 + +## 评审场景 + +1. 默认实例列表:运行中、启动中、被占用、已退出四种状态同时可见。 +2. 点击“新建实例”:检查浏览器类型、executable、user data dir、profile、URL、代理和无头模式字段。 +3. 留空 user data dir 提交:观察字段级错误和输入保留。 +4. 点击运行中实例:查看 PID、路径、启动地址和操作入口。 +5. 点击“关闭实例”:确认对话框默认提供优雅关闭;重启需要先等待旧实例退出。 +6. 使用搜索、浏览器筛选、状态筛选和刷新状态。 +7. 切换深色主题,并在窄窗口查看导航和表单重排。 +8. 点击“设置”:检查 Chrome/Edge 路径、默认 user data dir、日志目录、运行行为,以及保存/取消反馈。 + +## 当前原型假设 + +- 顶级导航只有“实例、设置”;活动不再是独立模块,作为实例详情中的“最近活动”时间线显示。 +- 详情和新建配置使用右侧抽屉,不创建文档标签。 +- 选择实例后,右侧详情同时承载当前状态、操作、配置摘要和最近活动;不要求用户在实例与活动页面之间切换。 +- 设置作为独立顶级页面,采用显式提交;取消只恢复本次未保存的表单修改。 +- 外部实例显示占用证据,但不提供接管或强制关闭入口。 +- 原型中的“确认”只模拟事件反馈,不启动真实浏览器或改变文件。 + +## Gio 交接方向 + +| 原型元素 | Gio 适配意图 | +| --- | --- | +| 顶级导航 | 稳定 page ID + 持久选中状态 | +| 实例列表 | 虚拟化列表;实例 ID 而非行号作为身份 | +| 启动表单 | 持久 editor/选择状态,显式提交,字段错误保留输入 | +| 详情抽屉 | 自定义辅助面板,关闭后焦点返回实例行 | +| 确认对话框 | 模态层限制背景交互,Escape 取消,危险操作不默认聚焦 | +| Toast/状态区 | 非模态状态反馈;异步结果通过统一事件触发重绘 | diff --git a/docs/ui/browser-manager.html b/docs/ui/browser-manager.html new file mode 100644 index 0000000..c40b5d1 --- /dev/null +++ b/docs/ui/browser-manager.html @@ -0,0 +1,312 @@ + + + + + + Chub · 浏览器实例管理原型 + + + +
PROTOTYPE · 仅供交互确认,非生产实现
+
+ + +
+
+
Browser instances

浏览器实例

启动、观察并安全管理 Chrome 与 Edge 环境

+
+
+ +
+ +
+
全部实例
4 已配置
+
运行中
1 稳定
+
启动中
1 请稍候
+
需处理
1 profile 占用
+
+ +
+
+
浏览器实例列表
实例状态PIDuser data dir最近启动操作
没有匹配的实例尝试清除筛选,或新建一个浏览器实例。
+ +
+
+
+
+
+

浏览器与目录

这些设置用于新建实例的默认值;已有实例的配置不会被静默修改。

+

浏览器路径

+
留空或选择文件后,Chub 启动时会校验路径可访问。
+
Chrome 与 Edge 可分别设置,也可以继续使用自动发现。
+
+

默认目录

+
新建实例时作为默认建议路径;不会移动或删除已有 profile。
+
日志仅保存必要诊断信息,并过滤敏感参数。
+
+
+
+ +
+
+
+
+ +
+ +

关闭浏览器实例?

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

+
+ + + + diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a25e9b2 --- /dev/null +++ b/go.mod @@ -0,0 +1,4 @@ +module chub + +go 1.23 + diff --git a/internal/platform/logging/logger.go b/internal/platform/logging/logger.go new file mode 100644 index 0000000..6f26851 --- /dev/null +++ b/internal/platform/logging/logger.go @@ -0,0 +1,15 @@ +package logging + +import ( + "io" + "log/slog" +) + +// New creates the process logger. Callers provide the sink so tests and the +// command entry point do not need to share global logging state. +func New(w io.Writer) *slog.Logger { + if w == nil { + w = io.Discard + } + return slog.New(slog.NewJSONHandler(w, &slog.HandlerOptions{Level: slog.LevelInfo})) +} diff --git a/internal/platform/logging/logger_test.go b/internal/platform/logging/logger_test.go new file mode 100644 index 0000000..7739cf8 --- /dev/null +++ b/internal/platform/logging/logger_test.go @@ -0,0 +1,22 @@ +package logging + +import ( + "bytes" + "strings" + "testing" +) + +func TestNewWritesStructuredLog(t *testing.T) { + var output bytes.Buffer + logger := New(&output) + logger.Info("test event", "component", "test") + + value := output.String() + if !strings.Contains(value, `"msg":"test event"`) || !strings.Contains(value, `"component":"test"`) { + t.Fatalf("log output = %q", value) + } +} + +func TestNewNilWriterDoesNotPanic(t *testing.T) { + New(nil).Info("discarded") +}