docs(desktop-client): spec v2.1 adds admin-web coordination + 2 LLMs
User feedback integrated:
- LLM monitoring scope: 3 → 5 providers (add Hunyuan + Kimi).
Keep-alive table in §4.4 gets 2 rows (default 8 min heartbeat,
re-evaluate on integration). Block B effort 2-3w → 4-5w; total
22-26w → 24-28w; Plan B scope updated.
- New §6.8 "admin-web coordination" covering:
- Publish modal with *account-level* selection (not platform-level):
one workspace has many accounts per platform (e.g., 5 WeChat);
one job expands to N desktop_tasks each bound to (platform, account).
- Media data dashboards at account-level granularity (overview,
publish history, monitoring results).
- New /api/tenant/* aggregation + SSE event bus (separate channel
from /api/desktop/events).
- Offline degradation UX when client(s) are offline.
- Account data consistency: client is SoT for account existence,
server stores projection; startup resync on upsert/delete.
- georankly:// deep links with installed-client detection fallback.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
现有 `apps/browser-extension/`(WXT MV3 Chrome 扩展,"GEO Publisher")同时承担两类工作:
|
||||
|
||||
1. 向 13 家中国媒体平台做内容分发(微信公众号、头条号、百家号、搜狐号、网易号、ZOL、懂车帝、知乎、企鹅号、简书、掘金、B 站、什么值得买)。
|
||||
2. 监控主流 LLM(豆包、千问、DeepSeek)在给定查询下的输出,判定品牌/KOL 是否被引用。
|
||||
2. 监控主流 LLM(豆包、千问、DeepSeek、混元、Kimi)在给定查询下的输出,判定品牌 是否被引用。
|
||||
|
||||
当前问题:
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
1. **全面替换**,不做"先 monitoring 后 publishing"的分阶段替换。
|
||||
2. **不做向后兼容**。因为是开发版本阶段,服务端协议、DB schema、认证字段按桌面客户端的最佳形态重新设计,不为插件保留旧字段或旧路由。
|
||||
3. **登录走嵌入式 Web View**。用户在 Electron 真实平台登录页里自己完成扫码/密码/短信/滑块;我们只保存 cookie,不保存账号密码。
|
||||
4. **Cookie 本地存储 + OS 级加密**(Electron `safeStorage`,macOS Keychain / Windows DPAPI / Linux libsecret)。服务端**永不存储** Cookie 或任何平台凭据。Linux 上 `safeStorage` 可能退化为 `basic_text`,必须启动时检测并显式降级告警(见 §4.5)。
|
||||
4. **Cookie 本地存储 并加密**(Electron `safeStorage`)。服务端**永不存储** Cookie 或任何平台凭据。Linux 上 `safeStorage` 可能退化为 `basic_text`,必须启动时检测并显式降级告警(见 §4.5)。
|
||||
5. **LLM 监控一律走网页 + CDP 拦截**,不调用任何 LLM 官方 API(成本导向;多账号 = 多配额,线性成本比 token 计费友好)。后端 `llm.api_key` 是后端自用管线,与客户端监控完全解耦。
|
||||
6. **不打包 Playwright**。复用 Electron 自带 Chromium,通过 `webContents.debugger` 直接用 CDP。包体越小越好。
|
||||
7. **支持 5 目标**:Windows x64/arm64、macOS Intel+Apple Silicon、Linux x64/arm64。
|
||||
@@ -182,6 +182,8 @@ type ProxyConfig = {
|
||||
| 豆包 | Doubao | 主动保活 | 8 min + 30 min 探活 |
|
||||
| 千问 | Qwen | 主动保活 | 8 min + 30 min 探活 |
|
||||
| DeepSeek | DeepSeek | 只探活 | 30 min |
|
||||
| 混元 | Hunyuan(腾讯)| 主动保活(SSE / 短期 token,待接入时复测)| 8 min + 30 min 探活 |
|
||||
| Kimi | Moonshot | 主动保活(SSE / 短期 token,待接入时复测)| 8 min + 30 min 探活 |
|
||||
|
||||
实施细节:
|
||||
|
||||
@@ -557,6 +559,111 @@ Web UI 可视化
|
||||
|
||||
**超时处理**:`lease_expires_at` 到期后 scheduler 把 task 放回 `queued`,`active_attempt_id` 置空;若老 client 恢复后再 POST `/result(lease_token=过期值)`,返 409 → client 本地记为 `unknown` 提示人工。
|
||||
|
||||
### 6.8 admin-web 协同(发布 Modal + 媒体数据面板 + 离线降级)
|
||||
|
||||
桌面客户端是执行者与凭据持有者,但用户日常操作的入口仍然是 `apps/admin-web`(SaaS Web 端)。`/api/desktop/*` 面向桌面端(§6.4),`/api/tenant/*` 面向 Web 端;server 内部共用 §6.3 的 RabbitMQ + DB。
|
||||
|
||||
#### 6.8.1 角色分工
|
||||
|
||||
| 角色 | 职责 | 数据主权 |
|
||||
|---|---|---|
|
||||
| **admin-web(Web SaaS)** | 内容编辑、创建任务、展示结果、总览媒体数据 | Task 定义与结果的 SoT |
|
||||
| **desktop-client** | 凭据持有、页面自动化、执行任务、上报结果 | Account existence + cookie 的 SoT |
|
||||
|
||||
#### 6.8.2 发布 Modal 交互流(**账号级**选号,非平台级)
|
||||
|
||||
**核心前提**:一个 workspace 下同一平台通常有**多个账号**(例如 5 个微信公众号 + 3 个头条号 + 2 个知乎号)。Modal 的勾选粒度是**账号**,用户可以挑"平台 A 的账号 1/2/3 + 平台 B 的账号 5"这种跨平台跨账号组合;一个 publish job 展开为 **N 条** `desktop_tasks`,每条一个 `(platform, account)` 组合。
|
||||
|
||||
流程:
|
||||
|
||||
1. 打开:`GET /api/tenant/accounts?kind=publish&workspace_id=...` 拉回 workspace 下所有发布账号。响应按 `(platform, account)` 两层结构组织,每条带:
|
||||
- `online`(由 server 根据该账号所属 `client_id` 的 `last_seen_at < 5 min` 判定)
|
||||
- `health`(`live / expired / captcha / risk`)
|
||||
- `lastPublishAt`(上次成功发布时间)
|
||||
2. Modal UI:
|
||||
- 平台折叠块,标题显示"选中 X / 共 Y"
|
||||
- 每账号一行:`checkbox · displayName · health badge · online badge · 最近发布时间`
|
||||
- 全选 / 反选(按平台或全局)
|
||||
- 选模式(manual / auto)、时间(立刻 / 定时)、素材策略(封面默认 / 指定)
|
||||
3. 提交:`POST /api/tenant/publish-jobs`,body 示例:
|
||||
```json
|
||||
{
|
||||
"title": "2026 Q2 新品发布",
|
||||
"content_ref": "draft:1234",
|
||||
"accounts": [
|
||||
{"account_id": "acc-a1", "mode": "auto"},
|
||||
{"account_id": "acc-a2", "mode": "manual"},
|
||||
{"account_id": "acc-b5", "mode": "auto"}
|
||||
],
|
||||
"schedule_at": null
|
||||
}
|
||||
```
|
||||
server 展开成 3 条 `desktop_tasks(kind=publish)`,共享同一 `job_id`;每条绑定一个 `account_id` 与其所属 `client_id`。
|
||||
4. Modal 切到"进度面板",订阅面向 Web 的 SSE `GET /api/tenant/events?topic=job:<job_id>`;接收每子任务状态变化(`queued → in_progress → waiting_user → succeeded/failed/unknown/aborted`)。
|
||||
5. manual 模式任务进入 `waiting_user` 后,面板上显示"账号 X 等待桌面端审核",带"前往桌面端"按钮 → `georankly://tasks/<id>/review`(未装客户端时跳下载页)。
|
||||
6. 全部终态后 Modal 汇总:每账号结果 URL / 错误码;对失败子任务点"重试" → `POST /api/tenant/publish-jobs/{job_id}/retry?task_ids=...`。
|
||||
|
||||
#### 6.8.3 媒体数据面板(账号级粒度)
|
||||
|
||||
admin-web 的"账号总览 / 发布历史 / 监控结果"三个页面全部按 `(platform, account)` 两层粒度:
|
||||
|
||||
| 页面 | API | 行粒度 |
|
||||
|---|---|---|
|
||||
| **账号总览** | `GET /api/tenant/accounts` | `(platform, platform_uid)` |
|
||||
| **发布历史** | `GET /api/tenant/publish-jobs?...` | job 分组,展开后每行 `(job, account, status)` |
|
||||
| **监控结果** | `GET /api/tenant/monitoring-results?plan_id=&range=` | 每行 `(plan, query, llm_account, answer, hit)` |
|
||||
|
||||
统计卡片:workspace 在线桌面端数、各平台账号健康率、近 7 天发布成功率(**按账号**)、近 7 天监控完成率、过去 24h 异常账号数 / 异常平台数。
|
||||
|
||||
#### 6.8.4 admin-web 新增 / 扩充 API
|
||||
|
||||
```
|
||||
GET /api/tenant/accounts # 带 online + health + lastPublishAt 聚合
|
||||
GET /api/tenant/accounts/{id}
|
||||
GET /api/tenant/clients # 本 workspace 的桌面端在线情况
|
||||
GET /api/tenant/publish-jobs # 发布批次列表
|
||||
GET /api/tenant/publish-jobs/{id} # 批次详情含子任务
|
||||
POST /api/tenant/publish-jobs # 创建(见 §6.8.2 step 3)
|
||||
POST /api/tenant/publish-jobs/{id}/retry # 重跑失败子任务
|
||||
GET /api/tenant/monitoring-results # 支持 filter / range / 按 llm / 按账号
|
||||
GET /api/tenant/events?topic=job:<id>|account:<id>|workspace # 面向 Web 的 SSE
|
||||
```
|
||||
|
||||
**关键**:`/api/tenant/events` 与 `/api/desktop/events`(§6.4)是**两条独立通道**,订阅 topic 不同——前者推 task 状态给 Web UI,后者推 task 分派给 desktop client。
|
||||
|
||||
#### 6.8.5 离线降级 UX
|
||||
|
||||
- **某桌面端离线**(`last_seen_at > 5 min`):
|
||||
- Modal 里所属账号标"离线(上次 X 分钟前)",仍可勾选,顶部提示"任务将在该端上线后自动执行"。
|
||||
- 账号总览 / 监控结果用灰色 badge。
|
||||
- **workspace 所有桌面端都离线**:
|
||||
- Modal 顶部 banner:"当前 workspace 无在线桌面端。任务会排队等待。[下载桌面端]"
|
||||
- 监控计划若连续 N 次未执行,站内通知 + 邮件给 workspace owner。
|
||||
- **桌面端重新上线**:
|
||||
- desktop-client 启动 `POST /api/desktop/clients/heartbeat` + §6.8.6 resync;queued 任务通过 SSE 被自然分派。
|
||||
- admin-web 的 SSE 收到 `client_online` 事件自动刷新 badge,不需 reload。
|
||||
|
||||
#### 6.8.6 Account 数据一致性(client ↔ server)
|
||||
|
||||
**client 是 account existence 的 SoT,server 存投影**:
|
||||
|
||||
- 登录完成:client `POST /api/desktop/accounts`(upsert,以 `(workspace_id, platform, platform_uid)` 为键,唯一索引见 §6.6)。
|
||||
- 健康 / 昵称变化:client `PATCH /api/desktop/accounts/{id}`。
|
||||
- 删除:client `DELETE /api/desktop/accounts/{id}` → server 级联把该账号未完成的 task 置为 `aborted`。
|
||||
- **启动时 resync**:client `GET /api/desktop/accounts?client=self` 比对本地清单;server 有而本地没的(用户在别处删过)→ 本地删;反之 upsert;`platform_uid` 冲突走 §6.6 唯一索引处理。
|
||||
|
||||
admin-web 看到"账号不存在"(Modal 里某账号消失)= client 已删;秒级一致性延迟可接受。
|
||||
|
||||
#### 6.8.7 深链接 `georankly://`
|
||||
|
||||
| 深链 | 场景 | Fallback |
|
||||
|---|---|---|
|
||||
| `georankly://accounts` | "前往桌面端管理账号" | 未装 → 下载页 |
|
||||
| `georankly://accounts/add?platform=wechat` | "一键添加某平台账号" | 同上 |
|
||||
| `georankly://tasks/<id>/review` | Manual 模式等审核 | 同上 |
|
||||
|
||||
admin-web 点深链前先查 `/api/tenant/clients` 判断是否有该 workspace 的在线桌面端;没有直接弹下载引导,不让用户点空链接。
|
||||
|
||||
---
|
||||
|
||||
## 7. 错误处理与可观测性
|
||||
@@ -676,7 +783,7 @@ GitHub Actions,runner 矩阵:`macos-14 (arm64)` + `macos-13 (x64)` 合并 un
|
||||
|
||||
### 9.3 集成
|
||||
|
||||
用 Playwright `_electron.launch` 驱动 Electron,对 **本地 fake server + 本地 fake 平台**(HTTP server 喂 fixture SSE)。13+3 适配器全跑一遍,并行约 3–5 分钟。
|
||||
用 Playwright `_electron.launch` 驱动 Electron,对 **本地 fake server + 本地 fake 平台**(HTTP server 喂 fixture SSE)。13+5 适配器全跑一遍,并行约 3–5 分钟。
|
||||
|
||||
### 9.4 集成 · 协议
|
||||
|
||||
@@ -717,7 +824,7 @@ GitHub Actions,runner 矩阵:`macos-14 (arm64)` + `macos-13 (x64)` 合并 un
|
||||
|
||||
| 阶段 | 受众 | 签名 | 退出门槛 |
|
||||
|---|---|---|---|
|
||||
| **Alpha(内部)** | 开发 + QA(≤5 人)| Mac 公证 / Win 无签名 / Linux 裸 AppImage | 13+3 适配器 fake + 真实账号成功率 ≥ 90%;lease 协议在混沌测试下无脏写 |
|
||||
| **Alpha(内部)** | 开发 + QA(≤5 人)| Mac 公证 / Win 无签名 / Linux 裸 AppImage | 13+5 适配器 fake + 真实账号成功率 ≥ 90%;lease 协议在混沌测试下无脏写 |
|
||||
| **Beta(邀请制)** | 公司内部 + 合作客户(≤30 人)| 同上 | 连续 7 天无 P0;保活命中率 > 95%;patch 通道应急修复跑通 ≥ 1 次;autoUpdater smoke 每 release 全过 |
|
||||
| **GA 候选** | 放开注册 | **Win 签名必须到位** | 安装成功率 > 99%;crash rate < 0.1%/session |
|
||||
|
||||
@@ -741,7 +848,7 @@ GitHub Actions,runner 矩阵:`macos-14 (arm64)` + `macos-13 (x64)` 合并 un
|
||||
| 块 | 范围 | 估时 |
|
||||
|---|---|---|
|
||||
| **A. Desktop runtime 基座** | Main 进程、session-registry、view-pool、vault、lease-manager、rate-limiter、circuit-breaker、crash-recovery、tracing(Alpha 版)、transport (SSE + pull + RabbitMQ presence) | 3–4 周 |
|
||||
| **B. LLM 监控适配器** | Doubao + Qwen + **DeepSeek(从零)** | 2–3 周 |
|
||||
| **B. LLM 监控适配器** | Doubao + Qwen + **DeepSeek(从零)** + **混元(从零)** + **Kimi(从零)** | 4–5 周 |
|
||||
| **C. 已有真实发布能力的平台迁移** | 约 6 家(头条 / 百家 / 搜狐 / 知乎 / 企鹅 / 简书)从 chrome.* 迁到 Electron 抽象 | 3 周 |
|
||||
| **D. Detect-only 平台从零实现 publish** | 7 家(微信公众号 / 掘金 / 懂车帝 / 什么值得买 / 网易 / B 站 / ZOL)| 5–7 周 |
|
||||
| **E. 服务端协议 + DB + sqlc 重生 + Service 层重构** | `/api/desktop/*`、租约、presence、patch manifest | 2 周 |
|
||||
@@ -750,7 +857,7 @@ GitHub Actions,runner 矩阵:`macos-14 (arm64)` + `macos-13 (x64)` 合并 un
|
||||
| **H. packages/ui-shared 抽取 + desktop renderer** | — | 1.5 周 |
|
||||
| **I. 测试基建(fake server + fake 平台 + replay)** | — | 1.5 周 |
|
||||
|
||||
合计约 **22–26 工程周**。这份 spec 因此**应拆 3 个 plan**(见 §12)。
|
||||
合计约 **24–28 工程周**(LLM 从 3 家扩到 5 家增加约 2 周)。这份 spec 因此**应拆 3 个 plan**(见 §12)。
|
||||
|
||||
### 10.4 三层回滚
|
||||
|
||||
@@ -803,13 +910,13 @@ GitHub Actions,runner 矩阵:`macos-14 (arm64)` + `macos-13 (x64)` 合并 un
|
||||
|
||||
### Plan B · 广度铺开(10–12 周,Beta 上限)
|
||||
|
||||
- 块 B(Qwen + DeepSeek 从零)
|
||||
- 块 B(Qwen + DeepSeek + 混元 + Kimi 从零)
|
||||
- 块 C(其余 5 家已有真实发布能力的平台迁移)
|
||||
- 块 D(7 家 detect-only 从零实现 publish)
|
||||
- 块 F 完整:Win/Linux 全架构 + 签名(Win 签名到位)+ autoUpdater 完整
|
||||
- 块 I 补齐:多适配器回归 + 冒烟 Playbook
|
||||
|
||||
**退出目标**:13+3 适配器全量可用,autoUpdater smoke 每 release 全过,5 arch × 3 OS 全量打包签名。
|
||||
**退出目标**:13+5 适配器全量可用,autoUpdater smoke 每 release 全过,5 arch × 3 OS 全量打包签名。
|
||||
|
||||
### Plan C · 运维闭环(4–5 周,GA 上限)
|
||||
|
||||
@@ -824,6 +931,10 @@ GitHub Actions,runner 矩阵:`macos-14 (arm64)` + `macos-13 (x64)` 合并 un
|
||||
|
||||
## 13. 变更记录
|
||||
|
||||
- **v2.1(2026-04-18)**:用户增量反馈后的小修订。
|
||||
- **LLM 监控 3 家 → 5 家**:新增混元(Tencent Hunyuan)、Kimi(Moonshot),§4.4 保活表增行(两家默认走 8 min 主动保活,实际节奏接入时复测);§10.3 块 B 估时 2–3 周 → 4–5 周;总工期 22–26 周 → 24–28 周;§12 Plan B 范围更新。
|
||||
- **新增 §6.8「admin-web 协同」**:覆盖发布 Modal 账号级多选语义、媒体数据面板(账号级粒度)、`/api/tenant/*` 聚合与 SSE 通道、离线降级 UX、Account 数据一致性(client = existence SoT / server = 投影)、`georankly://` 深链接。明确 `/api/tenant/events` 与 `/api/desktop/events` 是两条独立 SSE 通道。
|
||||
- **发布 Modal 选号粒度固化为账号级**(非平台级):一个 job 展开为 N 条 desktop_tasks,每条绑定一个 `(platform, account)`。
|
||||
- **v2(2026-04-18)**:Claude + Codex + Gemini 三方交叉审查后的修订版。主要修订:
|
||||
- **C1** 修正 CDP 流式 SSE 抓取方法(§5.2),改为按 EventSource / fetch-stream 分流;维护 Electron Chromium CDP 版本矩阵。
|
||||
- **C2** Patch 通道默认改为**纯数据 DSL**(无 JS 执行),可执行代码作为备用通道并限定在 `utilityProcess + isolated-vm` 沙箱;引入 TUF 风格 manifest + patch_seq + 根密钥轮换 + 撤销机制(§5.5)。
|
||||
|
||||
Reference in New Issue
Block a user