feat: improve service configuration and runtime diagnostics

This commit is contained in:
2026-09-11 09:38:56 +08:00
parent 945d36eb31
commit bfe349cc10
28 changed files with 1427 additions and 391 deletions
+9 -7
View File
@@ -1,9 +1,11 @@
# Web UI + MCP 使用说明
> 当前版本已实施基础迁移:Token 不按 `AccountIds` 做账号级授权,调用方负责业务隔离;Agent 已校验可信 Host/Origin,并对浏览器写请求执行 CSRF/本机请求头检查。完整的普通用户交互和批次写操作仍按 [普通用户重构指导](WebUI-面向普通用户重构落地指导.md) 分阶段实施。
## 启动
1. 在已登录、未锁定的 Windows 用户会话中运行发布包中的 `WxAgent-Setup.exe`。安装器会创建桌面/开始菜单快捷方式和可选的登录启动项,不需要 PowerShell 或管理员权限。
2. 启动托盘程序后,首次运行会生成远程访问 Token,并在一次性窗口中提供复制按钮。后续通过托盘菜单“访问凭据...”新建或撤销 Token,通过“服务设置...”配置监听和服务参数;不需要手工编辑配置文件。
2. 启动托盘程序后,首次运行会生成唯一的远程访问 Token,并自动打开“服务设置...”窗口。Token 在该窗口中明文展示,可复制或重新生成;不需要单独的凭据窗口,也不支持多 Token。
3. 如需无托盘运行,仍可复制 `docs/webui-mcp-config.example.json` 后使用:
```powershell
@@ -16,9 +18,9 @@ WxAgent.Host.exe serve --config C:\Users\USERNAME\wx-agent\service.json
[{"PrincipalId":"local-read","TokenSha256":"<64-hex-sha256>","Permissions":["read"],"AccountIds":[]}]
```
`AccountIds: []` 不授予任何显式数据库账号范围;需要联系人/群成员等账号范围调用时,必须填入已验证的 account fingerprint。凭据文件应使用当前用户 ACL,禁止提交仓库。
`AccountIds` 仅为旧凭据格式保留,不再作为账号级数据授权边界;`[]` 可以保留。凭据文件应使用当前用户 ACL,禁止提交仓库。调用方仍必须显式携带 account fingerprint 选择数据和目标,Agent 会验证绑定、窗口和目标一致性。托盘模式的 `service.json` 会保存 `AccessToken` 明文,以便服务设置窗口随时展示;该文件同样只应保存在本机。
服务运行后使用托盘菜单中的“服务设置...”配置监听地址、端口、远程访问、队列和监听事件;使用“访问凭据...”新建或撤销 Token,不需要手工编辑 `service.json`/`credentials.json`。若需要保持微信 UI 会话不自动锁屏,在安装器的可选项中启用“防止自动锁屏”;该设置不阻止用户手动锁定。默认只监听 `127.0.0.1:5088`,本机访问控制台不需要 Token,会自动进入。外部监听必须启用远程访问并自行配置防火墙,服务不会自动开放端口。Host/Origin(CORS)限制不再校验;HTTP 不加密 Token、Cookie、消息或附件,不直接暴露公网。
服务运行后通过托盘菜单中的“服务设置...”配置监听地址、端口、远程访问和唯一 Token,不需要手工编辑 `service.json`/`credentials.json`。队列容量、监听会话和监听事件开关属于内部实现/验收策略,不作为用户配置项。若需要保持微信 UI 会话不自动锁屏,在安装器的可选项中启用“防止自动锁屏”;该设置不阻止用户手动锁定。默认只监听 `127.0.0.1:5088`,本机访问控制台不需要 Token,会自动进入。外部监听必须启用远程访问并自行配置防火墙,服务不会自动开放端口;外部监听使用具体 IP,不能使用 `0.0.0.0`/`::`。服务校验可信 Host/Origin;远程浏览器写请求需要 Cookie、CSRF 和可信 Origin,本机浏览器写请求还需要 `X-WxAgent-Local: 1`。HTTP 不加密 Token、Cookie、消息或附件,不直接暴露公网。
远程浏览器访问 `/` 后输入 Token 登录。HTTP/MCP 客户端使用:
@@ -26,22 +28,22 @@ WxAgent.Host.exe serve --config C:\Users\USERNAME\wx-agent\service.json
Authorization: Bearer <TOKEN>
```
MCP Streamable HTTP 地址为 `/mcp`。不要把 Token 放在 URL、MCP session ID、浏览器持久存储或日志中。事件流默认关闭;只有在固定测试会话、受控消息验证和恢复验收完成后才设置 `EnableListenerEvents=true`。真机消息验证最多发送 3 条。
MCP Streamable HTTP 地址为 `/mcp`。不要把 Token 放在 URL、MCP session ID、浏览器持久存储或日志中。事件流默认关闭,是否启用属于内部验收开关,不作为用户配置项;真机消息验证最多发送 3 条。
## 凭据更换与撤销
在托盘“访问凭据...”中创建新 Token 或撤销旧 Token。明文 Token 只在创建时显示一次;服务在每个请求、任务执行和事件批次重新读取凭据。旧 Token、Cookie、SSE/MCP 授权立即失效,不存在重叠窗口。变更后删除旧浏览器会话并重新登录。
在托盘“服务设置...”中查看唯一 Token;点击“重新生成”后保存,旧 Token、Cookie、SSE/MCP 授权立即失效,不存在重叠窗口。服务在每个请求、任务执行和事件批次重新读取凭据。
## 多账号显式绑定
1. 在未锁定的交互式 Windows 会话中读取 `GET /api/v1/accounts` 和 `GET /api/v1/ui-targets`。
2. 用户选择一个数据库 `accountId` 和一个当前窗口 `targetId`(PID+HWND),调用 `POST /api/v1/accounts/bind`;服务会重新读取 UI 微信号/昵称,并与已验证 `contact.db` 身份匹配,重复、过期或不匹配均拒绝。
3. 绑定状态保存在服务数据目录的 `account-bindings.json`。微信进程/窗口消失、身份变化或重启后窗口句柄失效时,必须重新绑定;调用 `POST /api/v1/accounts/unbind` 可主动解除。
4. 会话、可见消息、联系人等操作必须显式携带已绑定 `accountId`;未绑定返回 `AccountNotBound`/`AccountWindowUnavailable`,不会猜测窗口。
4. 会话、可见消息、联系人和任务筛选等操作必须显式携带要查看或操作的 `accountId`;它是调用方的数据/目标选择,不是 Token 授权边界。未绑定返回 `AccountNotBound`/`AccountWindowUnavailable`,不会猜测窗口。任务列表按当前 principal 返回,可用 `accountId` 做视图筛选。
## 只读边界
状态、账号、窗口目标、会话、摘要消息、联系人和任务查询通过 REST 或同一 MCP 工具访问。正文需 `content` 权限。当前写入、@所有人、管理和朋友圈能力保持 disabled;不会把入队、UI 点击或数据库指纹描述成已发送。
状态、账号、窗口目标、会话、摘要消息、联系人和任务查询通过 REST 或同一 MCP 工具访问;任务列表使用 `GET /api/v1/operations` 或 MCP `operations_list`,详情/取消仍按当前 principal 校验。正文需 `content` 权限。当前文本发送、@所有人、管理和朋友圈能力保持 disabled;不会把入队、UI 点击或数据库指纹描述成已发送。
## 回滚
+7 -7
View File
@@ -3,7 +3,7 @@
状态以 `GET /api/v1/capabilities` 为准;`implemented` 不代表 `validated`,数据库指纹也不代表当前 UI 账号绑定。
| 操作 | REST | MCP | 当前状态 | 说明 |
|---|---|---|---|---|
| --- | --- | --- | --- | --- |
| 服务/微信诊断 | `GET /api/v1/status`、`/api/v1/diagnostics` | `agent_status`、`agent_diagnose` | Ready/环境依赖 | 脱敏;服务在线不等于微信可用,不执行恢复或 UI 写操作 |
| 能力清单 | `GET /api/v1/capabilities` | `agent_capabilities` | Ready | 各项 implemented/validated/enabled 分离 |
| 数据库账号发现 | `GET /api/v1/accounts` | `accounts_list` | 只读 | 仅返回指纹和绑定状态,不返回密钥 |
@@ -11,15 +11,15 @@
| 可见会话 | `GET /api/v1/sessions?accountId=...`、`/search`、`/current`;POST `/open`、`/scroll` | `sessions_list`、`sessions_search`、`session_current` | 只读/导航 | 所有 UI 调用必须携带已绑定 accountId;精确匹配拒绝猜测;open/scroll 需 manage 且当前仍待导航验收 |
| 可见消息 | `GET /api/v1/messages?accountId=...` | `messages_read` | 只读 | 必须使用已绑定 accountId;默认摘要;`includeContent` 需要 content 权限 |
| 数据库消息/合并记录 | `GET /api/v1/db/messages`、`/api/v1/db/merged` | `db_messages`、`db_merged` | Implemented but disabled | 仅接受显式已验证账号 fingerprint;只读 SQLCipher,未提供通用 SQL |
| 任务查询/取消 | `/api/v1/operations/{id}` | `operation_get`/`operation_cancel` | Ready | 只允许任务所有者;取消不撤销已发生副作用 |
| 任务查询/取消 | `GET /api/v1/operations`、`/api/v1/operations/{id}`、`POST /api/v1/operations/{id}/cancel` | `operations_list`、`operation_get`、`operation_cancel` | Ready | 按 principal 隔离;`accountId` 仅为调用方视图筛选,不是账号授权;取消不撤销已发生副作用 |
| 事件流 | `GET /api/v1/events` | 暂未注册 | Explicit opt-in | SSE 有界缓存、Last-Event-ID gap、Windows `ListenEventsAsync` 已接入;默认关闭,需受控真机验收后开启 |
| 文本/文件/卡片发送 | — | — | Disabled | 绑定验证已具备,但写能力仍需目标唯一性、写后确认和真机验收;绝不模拟成功 |
| 联系人/群管理、朋友圈 | — | — | Deferred/Disabled | 遵循 `docs/PENDING.md`,需单项授权和真机证据 |
| 单目标文本发送 | `POST /api/v1/operations`,`kind=send-text` | `operation_submit` | Implemented but disabled | 已接入任务/幂等/写后确认契约;`send-text` 保持 disabled,须完成账号绑定、目标唯一性和 Windows 真机验收后才能开放 |
| 文件/卡片发送、联系人/群管理、朋友圈 | — | — | Deferred/Disabled | 遵循 `docs/PENDING.md`,需单项授权和真机证据 |
| 任意 SQL/UI 菜单/shell | — | — | Unsupported | 不提供 |
## 运行边界
- 可通过 `WxAgent.Host serve --config <file>` 启动;安装版使用托盘程序,服务设置和 Token 管理均通过系统 UI 完成。默认监听 `127.0.0.1`,外部 IP 必须显式 `allowExternal: true`。
- 可通过 `WxAgent.Host serve --config <file>` 启动;安装版使用托盘程序,服务设置和唯一 Token 均在同一个系统设置窗口完成。默认监听 `127.0.0.1`,外部 IP 必须显式 `allowExternal: true`。
- 本机回环访问 Web UI、HTTP API 和 Streamable HTTP MCP (`/mcp`) 不需要 Token;远程访问仍使用 Bearer Token,Token 只放 `Authorization`,不放 URL。
- 远程浏览器登录后仅保留短期 HttpOnly SameSite Cookie,写请求需 CSRF;普通日志不记录正文、Token、Cookie、密钥或完整 UI 树。
- 明文 HTTP 不提供传输保密性,只适合可信隔离网络;不应直接暴露公网。
- 远程浏览器登录后仅保留短期 HttpOnly SameSite Cookie,写请求需可信 Host/Origin、CSRF;本机浏览器写请求还需同源本机请求头;普通日志不记录正文、Token、Cookie、密钥或完整 UI 树。
- 明文 HTTP 不提供传输保密性,只适合可信隔离网络;不应直接暴露公网。外部监听必须使用具体 IP,不能使用 `0.0.0.0`/`::` 作为工作台地址。
+2
View File
@@ -1,5 +1,7 @@
# Web UI + MCP 服务开发计划
> 后续 UI 改造入口:[面向普通用户重构落地指导](WebUI-面向普通用户重构落地指导.md)。用户界面以连接 Agent、消息收发、受控群发、添加好友和任务结果为主线;本文保留早期服务规划,当前实现状态请结合源码、功能矩阵和验收记录判断。
>
> 状态:规划,尚未实施或验收。用户已确认目标为“浏览器 Web UI + MCP 服务”。
> 本次交付仅为开发计划,不启动服务、不执行微信写操作。
> 范围变化:原第一阶段排除正式 UI;本文规划其后的控制面阶段。用户已确认 HTTP/Web UI 与 MCP 支持外部访问且共用 Token,不要求 HTTPS,不实现服务端人工审批;不改变 Windows Service、协议及数据库安全边界。
@@ -0,0 +1,330 @@
# Web UI 面向普通用户重构落地指导
> 状态:待实施指导文档,不代表功能已开放或通过验收。
> 产品定位:普通用户连接 Agent 后,可以查看和回复消息、选择对象群发、添加好友、查看执行结果,无需理解 CLI、MCP、UIA 或数据库。
> 本文依据当前工作区源码整理;已有未提交修改,不视为已发布版本。本次仅交付文档,不修改运行代码、不开放写权限、不执行微信写操作。
>
> 待实施决策:认证成功的 Token 可获取 Agent 当前登录账号的数据,不按 `AccountIds` 做账号级授权隔离;调用方负责账号、页面和业务视图隔离。Agent 仍保留能力权限、账号与窗口绑定一致性、目标核验,并补齐 CSRF/Origin 等服务端安全校验。好友申请冷却明确按“发送账号+目标”计算,为 4 小时。这些是目标行为,不代表当前源码已实现。
## 1. 改造结论与范围
当前 UI 是开发者诊断控制台,目标 UI 应是微信业务工作台。改造不是换配色、加卡片,而是把页面组织方式从“后端有什么接口”改成“用户要完成什么事情”。
### 1.1 本轮必须覆盖
1. **连接 Agent**:从 Windows 托盘打开工作台,或在另一台设备打开 Agent 提供的地址并输入连接密码。
2. **消息管理**:选择会话、阅读消息、回复文本、查看发送结果;明确同步范围和时效。
3. **群发**:选择已有联系人/群、编写文本、核对名单、确认发送、查看逐项结果、停止剩余发送。
4. **添加好友**:输入微信支持的查找标识、确认搜索对象、填写验证信息、提交好友申请、查看申请结果。
5. **任务中心**:自动展示当前身份的任务,无需复制粘贴 Operation ID;共享 Token 的调用方共享任务归属,不承诺浏览器用户私有任务。
上述是产品交付范围,不意味着现有后端全部支持。群发定义为用户确认名单后的逐对象发送,不是调用微信协议或承诺存在原生群发接口。添加好友首版做单人申请,不做批量陌生人搜集或批量加人。
### 1.2 本轮不做
- 不引入 CRM、团队客服分配、自动回复机器人、营销自动化、定时发送、多租户、云端中转或多 Agent 并行控制。
- 不承诺读取全部会话、全部历史或离线消息必达;不实现“下一个未读会话”。
- 不顺带开放朋友圈、删除好友、移出群成员等高风险操作。
- 不引入新前端框架、通用工作流引擎、独立消息队列或 Windows Service。
- 不为简化体验而取消账号身份核验、能力权限、写后确认、串行调度和失败诊断;账号之间的业务视图隔离由调用方处理,不能替代服务端目标一致性校验。
本文扩展早期计划中的正式 UI/受控批量发送范围;其余安全及技术约束不变。它不是对 `PENDING.md` 所列暂缓能力的整体放行。
## 2. 当前事实:可以复用什么,还缺什么
基线文件:
- `src/WxAgent.Service/wwwroot/index.html`、`app.js`、`styles.css`
- `src/WxAgent.Service/ServiceHost.cs`、`AgentService.cs`
- `src/WxAgent.Service/OperationQueue.cs`、`OperationStore.cs`
- [功能矩阵](WebUI-MCP-功能矩阵.md)、[验收记录](validation/WebUI-MCP-2026-09-07.md)、[暂缓事项](PENDING.md)
| 领域 | 当前实现 | 改造缺口 |
| --- | --- | --- |
| 页面组织 | 单页纵向排列状态、能力、账号绑定、窗口、会话、联系人、消息、任务查询 | 缺少业务导航、明确主操作和逐步流程 |
| 连接认证 | 本机回环免 Token;远程输入 Token 登录,Cookie + CSRF | 缺少面向用户的启动指引、连接故障处理和就绪状态 |
| 账号选择 | 显式选择数据库账号和微信窗口,界面显示 Fingerprint、PID、HWND | 应显示可识别账号;原始字段移入诊断,身份不明仍禁止绑定 |
| 账号范围 | 当前部分接口仍使用 `AccountIds` 校验,与其他入口不一致 | 目标是不做 Token 账号级授权隔离;统一 REST/MCP、查询/取消/执行及事件入口,保留账号登录状态、绑定与目标一致性校验 |
| 消息 | 列出可见会话和当前可见消息;当前前端没有完整会话点击/回复流程 | 会话导航、正文权限引导、聊天布局、发送接口和结果验证 |
| 联系人/群 | 已有数据库只读分页查询接口;前端加载首批联系人 | 搜索、加载更多、选中名单和稳定目标解析 |
| 导航/历史 | 已有部分路由;导航待验收、数据库消息能力存在禁用项 | 不得把“接口存在”当成“页面可直接使用” |
| 单条发送 | 服务层尚无文本发送入口,功能矩阵标记 Disabled | 接入现有 Windows 业务方法、权限/唯一目标/写后确认、真机验收 |
| 群发/加好友 | 尚无 Web 业务闭环 | 有界批量任务、单人好友申请接口及端到端验收 |
| 任务 | 持久化队列、按 ID 查询/取消、幂等与重启状态处理 | 缺少任务列表、业务结果字段、批量逐项结果;不是只改前端就能补齐 |
| 事件 | 服务端已有 SSE,默认关闭,需显式启用并验收;当前前端主要手动或每 15 秒刷新 | 前端事件接入、断线恢复与缺口提示,不能宣称完整实时收件箱 |
运行时以 `capabilities` 的实际权限/可用状态为准,结合绑定状态和环境状态判断按钮是否可操作。重构后的 `accountId` 是数据和目标选择/一致性字段,不是 Token 的账号授权边界;调用方可以按账号隔离界面,但不能绕过 Agent 的绑定、目标和写操作校验。
## 3. 新信息架构
```text
未连接:连接与使用引导
已连接工作台
├── 消息 默认首页:会话列表、消息区、回复框
├── 群发 新建群发、最近群发结果
├── 通讯录 联系人 / 群、搜索、添加好友
├── 任务 最近任务、状态筛选、逐项结果
└── 设置
├── 连接与账号 Agent 地址、当前微信、重新连接/选择账号
└── 高级设置 权限、能力详情、诊断、MCP、技术标识
```
顶部只保留 Agent 名称/地址、当前微信账号、就绪提示和设置入口。不再用四张技术状态卡占据首页,也不把能力清单当菜单。
桌面消息页结构:
```text
[微信工作台] 当前账号:昵称 [已连接 · 可发送] [设置]
[消息] [群发] [通讯录] [任务]
┌────────────────┬────────────────────────────────┐
│ 搜索会话 │ 会话名称 / 联系人或群 │
│ 会话列表 │ 数据范围、最后更新时间 │
│ 加载更多 │ 消息内容 / 加载失败或空状态 │
│ ├────────────────────────────────┤
│ │ 输入消息…… [发送] │
└────────────────┴────────────────────────────────┘
```
窄屏改为“会话列表 → 消息详情 → 返回”,不硬挤三栏。表单必须有标签、键盘焦点和可读错误提示;成功/失败不能只靠颜色区分。自动刷新不得抢焦点、清空草稿或重置已选名单。
## 4. 关键交互流程
### 4.1 连接 Agent:让用户不必先学习部署术语
**本机路径**:打开 Windows 托盘菜单“打开工作台” → 自动检查连接 → 选择并确认微信账号 → 进入消息页。
**远程路径**:在 Agent 托盘设置中开启外部访问、复制工作台地址 → 另一台设备用浏览器打开该地址 → 输入“连接密码” → 确认账号 → 进入消息页。
- 首版继续由目标 Agent 托管页面,浏览器同源调用 API。不另造中央门户,也不让页面直接跨域连接任意 Agent。
- 远程调用方负责当前账号、页面状态、重连和业务视图隔离;Agent 仍负责 Token、请求来源、CSRF/Origin、绑定和目标一致性校验。
- “连接密码”只是现有访问 Token 的用户界面名称,不改变其高强度随机凭据属性;密码取得/重置位置给出图文指引。
- 复制地址不得包含 Token;不把凭据保存到 URL、localStorage、日志或示例截图。可记住非敏感显示偏好,不自行延长认证有效期。
- 默认仍只监听回环。开启外部访问时说明 HTTP 明文只适合可信隔离网络,不能表述为安全公网访问;不要自动开放公网或放宽 CORS。
- 托盘/设置必须显示并可复制实际可达的工作台地址;监听 `0.0.0.0`/`::` 时不能把通配地址直接当浏览器地址,应提示选择或填写局域网可达地址,并说明防火墙和连通性检查。
- 地址无法打开属于浏览器加载前故障,指引需同时放在托盘设置/使用说明,不能只放在尚未加载的页面。
**账号确认**:优先展示已核验昵称/微信号。只有一个候选也需首次明确确认;多个同名候选用可靠的补充身份区分。身份无法确认时提示去 Windows 打开正确微信并重新检查,不猜测绑定。
**状态用自然语言分层显示**:
| 状态 | 用户文案 / 下一步 |
| --- | --- |
| Agent 不可达 | “无法连接电脑上的 Agent,请确认该电脑开机且 Agent 正在运行。” 提供重试连接 |
| 认证失败/过期 | “连接密码无效或已过期,请到 Agent 设置查看后重新连接。” |
| 微信未就绪 | “请在 Agent 所在电脑登录微信并解锁屏幕。” |
| 未绑定/绑定失效 | “请选择要管理的微信账号。” / “微信账号发生变化,请重新确认。” |
| 可读取但不能写 | “当前只能查看消息。” 在发送入口说明具体限制 |
| 可操作 | “已连接,可以收发消息。” 仅在相应能力和环境都满足时显示 |
切换账号后清除旧会话及消息视图,草稿未发送时先提示;在途请求必须按账号/会话作用域丢弃过期响应,避免旧数据串入新账号。
### 4.2 消息管理:从只读列表到阅读、回复闭环
1. 进入消息页,展示当前账号会话;注明“当前可见会话”,不伪装为全量通讯录或完整收件箱。
2. 选中会话后,通过已验收的导航能力打开目标,再读取对应消息。导航未开放时明确“当前仅能查看微信已打开的会话”,不假切换。
3. 消息区显示时间、发送方、消息类型及正文;正文未授权时给出“未授权查看正文”,不把每条消息打印成枚举值和 JSON。
4. 有 `content` 权限时可按已确认的用户偏好显示正文;不得为了聊天外观而默认扩权。UI 快照和数据库历史需注明来源,未完成一致性验证前不无标识混排。
5. 回复框固定当前账号和目标,用户输入文本后发送。普通回复以点击“发送”为明确提交,不每条增加无意义弹窗;群发、加好友另走确认步骤。
6. 请求受理后展示“排队中/发送中”,只有写后确认成功才标“已发送”;结果无法确认时显示“请到微信核对,勿重复发送”。不宣称对方已收取或已读。
7. 失败保留未发送草稿并给下一步;已提交内容与正在编辑的新草稿分离。禁用重复提交不替代服务端幂等。
发送前后需再次校验账号与目标,不能让执行时微信碰巧打开的聊天替代用户选中目标。联系人数据库 ID、会话 AutomationId 和名称不是同一种标识,转换必须经后端核验,不能前端按名称拼接。
**业务操作不可交错**:账号核验 → 目标导航 → 消息快照读取必须在同一个 UI 调度/跨进程门禁临界区内完成,不能只把各个方法分别排队。发送的账号/目标核验、输入、提交和写后确认同样构成单个执行单元;复用共享内部方法,避免嵌套获取非重入门禁。响应携带实际核验的 `accountId`、目标引用和采集时间;若人工操作或窗口变化导致归属无法确认,读取拒绝返回,已尝试发送则标待核对。两个客户端交错切换会话时,不能把乙会话消息标为甲会话;前端丢弃过期响应不能替代此保证。
正文、联系人字段均按文本渲染,禁止通过 `innerHTML` 注入。附件、引用、@ 和历史检索在文本闭环完成后逐项开放;上传成功不等于发送成功。
**同步规则**:能力允许时接入 SSE;否则明确低频刷新模式。事件缺口、断线或目标变化时重新读取有界快照并显示可能缺失,不承诺补齐所有漏收消息。刷新不得为扫描所有会话而轮流切换微信窗口。
### 4.3 群发:四步向导,不提供参数控制台
1. **选对象**:从当前 Token 可获取账号的联系人/群分页搜索、多选;持续显示已选人数和完整可核对名单。使用稳定 ID 去重,不按昵称去重,调用方负责账号视图隔离。
2. **写内容**:首版纯文本,一个批次相同内容;展示最终发送预览。暂不做 CSV 导入、变量模板、多附件或定时器。
3. **确认**:展示发送账号、联系人/群数量、名单和正文,提示“逐个发送,停止不能撤回已发消息”。用户点击“确认向 N 个对象发送”后才提交。
4. **看进度**:自动跳转任务详情,显示总数、已确认成功、失败、待核对、未执行及当前项;支持“停止剩余发送”。
服务端必须先冻结并校验目标清单,再有界接纳任务;不能由浏览器循环 POST 发送,更不能 `Promise.all` 并发操纵微信。群发动作仍通过现有 UI 调度/跨进程门禁串行执行;前台切换会话也须排队或明确提示占用,不能打断发送步骤。
首版建议默认最多 **20 个对象/批次**,作为待验收的安全容量上限,不是微信允许速率或避免限制的保证。上限、每项超时及发送间隔由 Agent 侧受控配置,界面显示实际限制;禁止随机抖动、换号等规避机制。总耗时不能硬塞进现有单任务最大 300 秒预算,应按逐项预算和批次截止时间明确建模。批次至少持久化父任务与逐项记录:提交时按对象数预留容量;每项有独立状态、幂等键、开始/截止时间和结果证据;取消只在下一个未开始边界生效;父任务汇总不覆盖子项事实,重启时所有活动项按既有重启保护转为取消或待核对。
- 每个接收对象保存独立幂等标识和状态;浏览器断开不取消已受理任务,重新连接后可找回。
- 遇到账号变化、登录/锁屏问题、微信限制提示或发送结果不明,首版停止剩余发送,提示人工核对;不自动重试写动作。
- 取消只阻止尚未执行项,已开始项按实际证据落状态。存在待核对项时不能显示“全部成功”或“全部未发送”。
- Agent 重启不自动续发;沿用既有重启保护。重新发送必须由用户核对后新建任务,不重放整个批次。
### 4.4 添加好友:单人申请,结果不冒充好友关系
入口为“通讯录 → 添加好友”。
1. 输入当前微信及后端实际支持的微信号/手机号等标识,前端与后端均校验;不提前承诺所有查找方式。
2. 显式搜索并展示微信返回的候选身份。没有结果、多个结果、身份无法确认都停止,不能按第一个结果自动申请。
3. 用户选择对象,填写有长度限制的验证信息,预览后点击“发送好友申请”。
4. 在任务中心记录“申请已提交/已是好友/失败/结果待核对”;“申请已提交”必须有对应证据,只有实际核验成为好友后才显示“已添加”,底层返回 `Unconfirmed` 时不能映射成成功。
**冷却与并发规则**:
- 用户已明确冷却范围为 **发送账号+目标稳定身份**,时长 **4 小时**;不限制同一账号向其他对象申请,但所有操作仍需串行、显式确认并遵守微信限制。不得以昵称、查询字符串、临时候选引用或 Token 作为冷却键;重新搜索、手机号/微信号别名应解析为同一目标,无法可靠核验稳定身份时不开放申请。
- 通过认证及能力检查后,先按 principal、账号、操作类型和幂等键查询原请求:相同参数返回原任务,不再次消耗候选或冷却;不同参数返回 `IdempotencyConflict`。这是识别重复请求,不是网络错误后自动重放写动作。
- 新请求须在同一持久化事务内完成有效候选校验/消耗、冷却与活动占用检查、目标占用和任务接纳;一个账号+目标最多有一个排队或执行中的申请。数据库约束兜底,不能只靠前端按钮或进程内检查。占用冲突返回 `FriendRequestInProgress`;队列满或事务失败不消耗候选和占用。
- 执行前重新检查账号、目标、能力和冷却。在真正尝试点击提交前,先持久化 `submissionAttemptedAt` 与 `retryAt = submissionAttemptedAt + 4h`,持久化失败不点击。只有排队/搜索或已确认尚未尝试提交的失败、取消、已是好友,释放占用且不启动冷却;在持久化提交标记后崩溃,即使不确定是否点击,也保留冷却并标待核对。
- 冷却期间新请求返回 `FriendRequestCoolingDown` 和服务端 UTC `retryAt`;重连、刷新、重启、Token 轮换及任务详情清理均不重置冷却。客户端只显示倒计时,不决定到期;检测到时钟回拨时保守拒绝提前到期并提示校时。
- 冷却到期只解除时间门禁,不自动清除 `Unconfirmed`。同账号+目标存在待核对记录时,新任务仍须由用户核对并显式确认,关联原任务记录;禁止自动续发或因到期自动重试。
首版不自动监视对方通过、不批量加人、不绕过验证码/风控/隐私限制。搜索本身可能改变 UI,也必须进入统一调度。出现限制提示即停止并原样归类,用户取消不代表撤回申请。
底层库有相关方法也不代表可直接开放。必须先核对实际签名、搜索与申请是否可分步、返回证据及失败语义;无法支持“先预览再确认”时补齐共享业务层,不做假预览。
### 4.5 任务中心:不用知道任务 ID
自动展示当前 principal 的任务,调用方负责按 `accountId` 做业务视图隔离。共享 Token 的多个浏览器/调用方可以查询和请求停止该身份的任务;筛选不是用户私有隔离,界面使用“当前身份的任务”,不误称“仅我可见”。提供类型/状态筛选和分页;点开即看进度、逐项结果及处理建议。任务 ID、关联 ID 和诊断下载放“技术详情”,支持复制用于反馈。
首版保留现有身份边界:本机免 Token 的 `local` 与远程 Token 的 principal 不同,不自动合并历史任务或跨 principal 取消。切换入口时提示任务来源;本机页面可显式使用同一 Token 登录以找回远程任务。Token 轮换保持 principal 稳定,旧凭据立即失效,新凭据仍可查询原任务;已因撤销停止的任务不自动续发。共享 principal 下的调用方标记只能用于显示筛选,不能当作认证凭据。
| 内部状态 | 用户展示 | 行为约束 |
| --- | --- | --- |
| `Queued` | 等待执行 | 可取消 |
| `Running` | 正在执行 | 可请求停止;不能承诺立即撤销 |
| `Succeeded` | 已完成 | 文本发送和好友申请仍用各自业务结果文案 |
| `Failed` | 未完成 | 显示具体原因;不能默认认定所有失败都可安全重发 |
| `Cancelled` | 已取消/未执行 | 只针对已确认未执行项;批次需保留已执行项结果 |
| `Unconfirmed` | 结果待核对 | 显示核对指引,禁止自动重试 |
批次的“部分完成/已停止”是逐项结果汇总,需新增汇总契约,不伪装成现有单任务状态。任务列表本身只能返回当前 principal 的记录;账号筛选是调用方的业务视图,不作为 Token 账号授权边界。
## 5. 技术实施:沿用现有结构,补真实缺口
### 5.1 前端
- 保留原生 HTML/CSS/JavaScript 和 Agent 静态托管,首版用 hash 导航即可,不新增构建链或前端框架。
- `index.html` 改为连接页与工作台骨架;`styles.css` 实现布局、状态和窄屏适配。
- `app.js` 保留 API/认证入口;当业务代码实际增长时按消息、群发、通讯录、任务拆小模块,不先建通用组件库。
- API 辅助函数保留结构化 `error.code`、HTTP 状态和 `correlationId`,映射“原因 + 下一步”,未知错误仍有通用提示及诊断编号。
- 认证失效统一停止刷新/事件流、清除旧私密视图、进入登录。只读请求允许有界重试;写请求不因网络错误自动重放。
- 请求中的 accountId、目标及幂等键由业务上下文生成并保存到本次操作状态,不要求用户手填;浏览器缓存不是持久任务的事实来源。
- 浏览器使用下述固定请求安全规则;调用方业务隔离不能替代服务端安全门禁,缺少 Origin 不代表请求来自 CLI。
### 5.2 服务与 Windows 业务层
| 位置 | 最小改造 | 禁止事项 |
| --- | --- | --- |
| `ServiceHost.cs` | 新增经过校验的业务路由及分页任务列表;统一 Token、会话、CSRF/Origin 和请求来源校验 | 前端直调任意 UI 菜单、Shell 或 SQL |
| `AgentService.cs` / `IAgentBackend` | 共用能力、账号/目标一致性验证;不按 Token 的 `AccountIds` 做账号授权过滤,调用方负责业务隔离;按已核实库方法补文本发送、好友搜索/申请 | 在路由里另写坐标或复制一套微信操作 |
| `OperationQueue.cs` / `OperationStore.cs` | 补任务列表、业务结果;批次/逐项最小持久化关联及停止语义 | 用浏览器内存保存整个群发进度、重启后自动重放 |
| Windows 后端与共享定位器 | 复用既有方法和跨进程门禁;补目标核验与写后证据 | 给单一路由散落坐标补丁、另开并发点击通道 |
| `AgentTools.cs` | 需要开放 MCP 对应能力时薄适配同一服务操作 | 将普通用户页面做成 MCP 工具清单或放宽 MCP 权限 |
**请求安全规则(待实施,适用于 REST/MCP)**:
| 请求方式 | 服务端必须执行的规则 |
| --- | --- |
| 远程浏览器 Cookie | 登录提交需匹配配置的精确可信 Origin;登录后非 GET/HEAD 请求同时要求有效会话、绑定会话的 CSRF 请求头和可信 Origin,缺失或 `null` Origin 拒绝。读取/事件请求不产生 UI 导航副作用;有 Origin 时同样校验 |
| 本机回环免 Token 浏览器 | 直连回环且可信 Host 才可初始化短期本机会话及 CSRF;初始化提交同样要求可信 Origin。此后浏览器写请求按 Cookie 规则校验,不沿用“回环直接跳过 CSRF”。只读请求可保留直连回环免 Token 路径 |
| 显式 Bearer(CLI/MCP/HTTP 客户端) | 每次验证 Token 和能力,不依赖 Cookie;非浏览器请求可缺少 Origin,有 Origin 则必须可信。Bearer 无效不能回退到 Cookie 或回环身份;无 Origin 的 HTTP 写调用必须提供 Bearer |
所有方式均校验配置的可信 Host/端口;可信 Origin 从服务端配置生成,不能照抄未经核验的 Host、Origin 或转发头。`0.0.0.0`/`::` 不进入允许列表;启用远程地址时同步配置精确地址,不用通配 Origin。GET/HEAD 不执行切会话、搜索输入等改变 UI 的操作,这些流程走受保护的 POST。Fetch Metadata 存在时用于附加拒绝跨站请求,缺失时仍按上表判断,不能因普通局域网 HTTP 缺少该头而跳过校验。
首版不启用可信反向代理模式、不依据 `Forwarded`/`X-Forwarded-*` 授予回环权限;若代理将远程流量转成本机连接,必须禁用回环免 Token 并要求认证,否则该部署不受支持。Host 校验也覆盖只读接口,防止任意域名通过 DNS 重绑定获得本机访问。CSRF、会话和 Token 均不得放入 URL 或日志。
Agent 自己的 `operations.sqlite` 可以保存业务任务元数据;这不改变微信数据库只读边界。尽量只存目标引用、摘要/计数和结果,不保存完整正文、手机号或附件。若可靠执行必须保存敏感任务参数,另行明确最小保留期、访问控制和清理方式,不随日志落盘。
**展示清理与防重保护分开**:
- 任务列表按 `(createdAt, id)` 稳定排序,有界游标分页;终态详情建议默认保留 30 天,活动任务不清理。正文等敏感执行参数在不再需要时优先删除,不随详情保留期延长。
- 现有幂等摘要位于任务表,不能直接删除整行。详情到期后仍保留最小防重记录(principal、账号、操作类型、幂等键、请求摘要、原任务 ID 和终态);旧键相同参数返回原任务最小状态及 `detailsExpired`,不同参数仍拒绝,不能成为新任务。
- 首版在建立可验证的请求到期机制前,不自动淘汰最小幂等记录;数量/存储达到 Agent 配置上限时拒绝接纳新任务并给出维护提示,不静默驱逐后重新执行旧键。R0 固化容量值;这避免无界增长,但保守占用存储。以后若支持删除防重记录,必须先保证过期旧请求返回 `IdempotencyExpired`,不能仅凭客户端可修改的时间戳判断。
- 冷却和待核对保护不随任务详情删除:冷却至少保留至 `retryAt` 且无活动占用,未解决的待核对标记保留至显式核对。清理不能影响 Token 轮换、进程重启后的去重和冷却。
### 5.3 契约补齐清单(标注“基础已接入”的入口仍需能力验收后开放)
| 建议入口 | 用途 | 必须校验/返回 |
| --- | --- | --- |
| `GET /api/v1/operations` | 当前身份的任务列表 | 按当前登录 principal 过滤;可按 `accountId` 筛选但不做 Token 账号授权拒绝;有界分页、摘要,不泄漏其他 principal 的任务 |
| `POST /api/v1/operations`,`kind=send-text` | 单目标文本发送 | **基础已接入但 capability disabled**;accountId、AutomationId 目标引用、text、idempotencyKey、confirmed;受理返回任务 ID,不直接宣称成功;开放前须完成 Windows 真机验收 |
| 同入口,`kind=broadcast-text` | 提交有界群发 | 冻结目标列表、去重、条数/长度限制、显式确认、逐项幂等与批次详情 |
| `POST /api/v1/friend-search` | 好友候选预览 | Token/能力、绑定状态、超时、候选归属当前账号;不发送申请 |
| `POST /api/v1/operations`,`kind=friend-request` | 单人好友申请 | 当前账号绑定的有效候选引用、验证信息、显式确认、幂等键;按 §4.4 原子接纳并执行,发送账号+目标稳定身份冷却 4 小时,返回任务或冷却 `retryAt` |
请求采用明确的分支 DTO,不接受任意 capability 名加任意参数字典执行未知动作。确认绑定实际账号、目标和内容;修改名单/正文后需重新确认。服务端确认字段是请求门禁,不是服务端人工审批,也不能成为绕过权限的凭据。好友搜索返回的候选必须由服务端生成短期有效、绑定账号和 principal 的不可伪造引用;新请求的候选过期、跨账号或已被消费时拒绝。相同幂等键的已受理请求先返回原任务,不因候选后来过期或已消费改成失败。
复用现有 `/operations/{id}` 查询和 `/cancel` 入口。任务详情需扩展业务结果及有界逐项分页,不能返回无界完整群发名单。涉及 UI 的预览/搜索在预计耗时超出普通请求预算时也走任务,不无限挂起 HTTP 请求。
## 6. 分阶段落地与完成门槛
各阶段应形成可独立验证的提交;完成界面骨架不等于完成产品。写能力逐项验收、逐项开启,不一次性翻转所有 disabled 标志。
| 阶段 | 交付 | 验收门槛 / 依赖 |
| --- | --- | --- |
| R0:能力核对 | 核对实际 Windows 方法、导航和写后证据,更新能力差距清单,确定测试账号/目标 | 每个用户动作有后端落点;未实现、未验收、未授权原因分开 |
| R1:工作台与连接 | 新导航、托盘入口文案、账号确认、自然语言状态、诊断下沉 | 普通用户不看 PID/HWND/Fingerprint/JSON 即能连接并确认账号;仍明确只读限制 |
| R2:消息闭环 | 会话选择、消息阅读、文本回复、最小任务列表/详情 | 单条发送唯一目标、幂等、写后确认、取消/超时/断网核对通过;依赖导航和文本写能力验收 |
| R3:受控群发 | 四步向导、批次逐项持久化、进度与停止 | 复用 R2;少量授权对象验证去重、顺序、停止、部分结果、重启不重放 |
| R4:添加好友 | 搜索预览、验证信息、单人申请、真实业务结果 | 专用测试对象获得授权且真机通过;没有获准对象时保留禁用并记录阻塞,不能模拟验收 |
| R5:收尾交付 | 可用性走查、错误/空状态、窄屏和键盘操作、说明与发布回归 | 普通用户完成三条业务主路径;更新矩阵、截图及对应微信版本验收记录 |
R3、R4 可分别评审,但同一桌面的真机 UI 写操作不得并行。R2 之前不要先做高级模板、主题系统或复杂统计图。
## 7. 测试与验收清单
### 7.1 自动检查
复用现有测试项目;本次是指导文档,以下命令在后续代码实施时执行,不代表本次已执行。
```bash
dotnet test tests/WxAgent.Core.Tests -c Release
dotnet build WxAgent.sln -c Release -p:EnableWindowsTargeting=true
```
另运行当前 Service 的实际路由/鉴权测试项目,不用假路由代替集成测试。最少覆盖:
- 任务列表按 principal 隔离、`accountId` 筛选正确;同一 Token 可获取全部已登录账号数据;失效 Token、CSRF/Origin 和未授权正文/写操作仍拒绝。
- 文本边界、群发上限、稳定 ID 去重、同幂等键相同/不同请求、账号目标不匹配。
- 停止未执行项、执行中取消导致待核对、部分完成汇总、重启不重放。
- 好友候选过期/跨账号/多候选拒绝;两个不同幂等键并发申请同账号+目标只有一个可接纳,重新搜索或别名查询不能绕过;同键同参数返回原任务、不同参数冲突。
- 冷却测试覆盖同账号同目标拒绝、不同目标不被此冷却阻止、提交前失败不计时、提交标记持久化后崩溃保守计时;UTC 到期边界、时钟回拨、重启/Token 轮换、`retryAt` 和待核对人工门禁。时钟测试使用可控时间,不需真机等待四小时或重复发送申请。
- 详情清理后旧幂等请求不重新执行、不同参数仍冲突、容量满拒绝新任务;活动任务、未到期冷却和待核对保护不被清理。
- 本机免 Token 与 Token 登录的 principal 切换、共享 Token 的多个调用方任务可见/可取消范围、Token 轮换保留归属但不续发。
- 两客户端交错切会话、搜索和读取:返回实际账号/目标,不能发生甲页面收到乙消息;身份核验与发送之间不能插入其他 Agent UI 操作。
- Cookie 写请求缺失/伪造 CSRF、Origin 缺失/为 null/跨站均拒绝;Fetch Metadata 缺失的合法局域网浏览器仍可操作;伪造 Host、DNS 重绑定式 Host 和转发头不能获得回环权限;有效无 Origin Bearer 客户端正常,无效 Bearer 不回退。
- 前端账号/会话快速切换丢弃旧响应;刷新保留草稿和名单;错误提示在未登录页也可见。
- HTML/脚本样式的消息或昵称只作为文本展示;浏览器存储、诊断及日志中无敏感凭据和完整正文。
### 7.2 Windows 真机验收
每次 Windows 发布仍执行:
```powershell
WxAgent.Host doctor
WxAgent.Host inspect-ui --output artifacts/ui-tree.json
WxAgent.Host smoke
```
然后执行本次功能对应的真机业务 smoke。`inspect-ui` 产物按敏感诊断管理,不直接作为普通用户可下载的原始树。真机连接和上传流程继续遵守 [AGENTS.md](../AGENTS.md),不得删除或改写其中的连接信息。
默认消息测试对象仅限“文件传输助手”“Hao 豪”“吉祥三宝”“消息测试专用群组”;不得为测试群发追加真实联系人。添加好友必须使用用户明确授权的专用未添加账号,没有对象即记为验收阻塞。只读联系人权限不是发送授权。
每份记录包含:代码版本、微信版本、Agent 运行会话、绑定账号核验结果、测试步骤、预期/实际结果、脱敏证据、超时/取消/断网/锁屏表现和剩余限制。基础 `doctor/smoke` 成功不能代替群发/加好友验收。
### 7.3 普通用户验收场景
让未参与开发的人按正常提示完成,不由开发者口头补充技术术语:
- 从托盘打开页面;远程用户按地址和连接密码指引成功连接,选择正确账号。
- 找到指定会话、阅读并回复一条文本;明确知道“已发送”还是“结果待核对”。
- 选择两个获准对象完成一次群发;能核对名单并找到每个对象的结果。
- 在一个包含剩余项的测试批次中请求停止,能区分已发送和未执行。
- 向获准测试账号提交好友申请,理解“申请已提交”不等于“对方已通过”。
- 断线、锁屏、权限不足、账号变化时,能根据提示完成下一步而不是复制 JSON 求助。
- 无需输入任务 ID 即可找回任务;需要求助时能复制诊断编号,不暴露消息正文。
可用性目标:在 Agent 已安装、网络可达、微信已登录且账号准备完成的前提下,首次连接与账号确认力争 3 分钟内完成;这是待测目标,不是已有成绩。
## 8. 文档与发布同步
- 本文负责产品定位、交互路径和实施顺序。账号不按 `AccountIds` 授权、可信 Host/Origin 与浏览器写请求安全已完成基础迁移;普通用户交互、批次写操作和真机验收仍按本计划推进。使用说明和功能矩阵已同步当前基础行为,未通过真机验收的能力不能标记为可用。
- [Web UI + MCP 开发计划](WebUI-MCP-开发计划.md) 保留服务架构与安全约束;早期“尚未实施”和暂定目录属于历史阶段信息,不能覆盖当前源码事实。
- [功能矩阵](WebUI-MCP-功能矩阵.md) 逐项更新已实现/已验收/已开放状态;只在真机通过后标记可用。
- [使用说明](WebUI-MCP-使用说明.md) 在功能落地时按“连接 → 消息 → 群发 → 添加好友 → 查看结果”重排,MCP/Token 技术说明移到高级章节。
- `docs/validation/` 保存分阶段验收记录;`PENDING.md` 继续记录未闭环项。未经批准的测试或缺乏真机证据的功能保持禁用。
**最终完成标准:用户不懂 Agent 内部技术,也能完成业务;执行器仍严格遵守原有安全和真实性约束。仅把技术字段隐藏、把按钮做漂亮,不算本次重构完成。**
+2 -2
View File
@@ -11,7 +11,7 @@
## 已验证
| 能力 | 入口 | 结果 |
|---|---|---|
| --- | --- | --- |
| 服务/微信诊断 | `GET /api/v1/status` | HTTP 200;`serviceOnline=true`、`wechatAvailable=true`、`sessionAvailable=true`、`windowFound=true`,错误为空 |
| 会话列表 | `GET /api/v1/sessions?accountId=...` | HTTP 200;返回可见会话,含 automationId;未携带 accountId 拒绝执行 |
| 会话当前/精确搜索 | `/api/v1/sessions/current?accountId=...`、`/search?accountId=...&exactOnly=true` | 需要显式绑定账号;当前锁屏环境不作为真机成功证据 |
@@ -52,6 +52,6 @@
## 未完成/不宣称
- 未执行发送、联系人/群管理、朋友圈、语音等写操作;`send-text`、`group-at-all` 和 deferred 项保持禁用。
- 未完成第二个账号/第二个窗口隔离、60 分钟/20 条消息监听、断线补齐和多客户端慢消费者真机验收;SSE 与服务端监听源已实现,默认配置 `EnableListenerEvents=false`。自动化高频探针已中止,不作为验收证据;后续真机消息验证上限 3 条。
- 未完成第二个账号/第二个窗口隔离、60 分钟/20 条消息监听、断线补齐和多客户端慢消费者真机验收;SSE 与服务端监听源已实现,内部监听事件开关默认关闭,不作为用户配置项。自动化高频探针已中止,不作为验收证据;后续真机消息验证上限 3 条。
- 未完成外部主机直接访问验收(防火墙未开放);不将 HTTP 鉴权描述为网络加密。
- 远程验证任务已停止并清理;不再自动发送测试消息,后续真机消息验证上限为 3 条。
+1 -4
View File
@@ -2,8 +2,5 @@
"ListenUrl": "http://127.0.0.1:5088",
"AllowExternal": false,
"CredentialFile": "C:/Users/USERNAME/wx-agent/credentials.json",
"DataDirectory": "C:/Users/USERNAME/wx-agent/data",
"QueueCapacity": 100,
"ListenerSession": "文件传输助手",
"EnableListenerEvents": false
"DataDirectory": "C:/Users/USERNAME/wx-agent/data"
}