docs(desktop-client): spec v2.3 from architect-level review
Third-round Claude+Codex+Gemini architect review surfaced foundation-
level issues that prior local patches missed. Codex verdict was
Needs-rework; Gemini verdict was Ready-with-caveats. v2.3 lands all
2 Critical + 9 Warning + 3 Info.
Critical:
- C1 workspace-as-first-class: Prior drafts added a WorkspaceScope
middleware, but Codex verified code: Actor/Claims carry only tenant_id,
cache_support keys scope to tenant, workspaces table migration does
not exist, tenant_monitoring_quotas tracks primary_installation_id.
Add a new Plan 0 (Workspace foundation uplift, 2-3w, blocks Plan A)
covering workspaces + memberships migrations, claims extension,
cache-key rewrite, and the full monitoring-domain migration checklist
(new §6.1.1). §2 hard-constraint 8 rewritten; new hard-constraint 9
reserves credential_holder / account_home naming for future evolution.
- C2 CDP stream-capture path was still inconsistent: Fetch.continueResponse
+ takeResponseBodyAsStream cannot chain per CDP semantics. v2.3 moves
EventSource to Network.eventSourceMessageReceived, fetch-stream to
Network.streamResourceContent + dataReceived (Chromium >=120, OK on
Electron 41), and reserves Fetch only for must-mutate scenarios
without chained continueResponse. Plan A now requires a provider x
transport spike matrix (0.5w) before any adapter work.
Warning:
- W1 Patch-channel minimal loop (G1) promoted from Plan C to Plan B,
so Beta exit criteria are satisfiable.
- W2 waiting_user decoupled from lease: becomes a parked state that
releases the lease, survives hours of human review, and is resumed
via POST /tasks/{id}/lease?from_parked=true.
- W3 credential_holder/account_home evolution seam formalized.
- W4 §6.1.1 lists 8 monitoring-domain migration items that were
previously hidden inside "sqlc rename".
- W5 Deep-link nonce becomes a one-time opaque code bound to
target_client_id, Redis-jti consumed on verify.
- W6 §10.3 block H raised from 1.5w to 2.5-3w after Gemini verified
PublishArticleModal's accounts[0] assumption needs a real rewrite.
- W7 MarkdownPreview extracted into packages/ui-shared so Web preview
and Desktop manual review use the same renderer.
- W8 packages/http-client gains a standardized SseClient (backoff +
Last-Event-ID + snapshot + dedup), shared by both apps.
- W9 Trace viewer now runs inside a CSP-sandboxed iframe with hash
routing for file:// loads; trace data flows in via postMessage.
Info:
- I1 §14 adapter map appendix; Hunyuan renamed to Yuanbao with the
correct entry domain (yuanbao.tencent.com).
- I2 UNKNOWN tasks gain a reconcile workflow (mark-succeeded /
mark-failed / ignore / recreate), plus weekly in-app nudge.
- I3 New §15 listing the 5 Mermaid diagrams Plan A must produce
(sequence, lease state machine, account resync, multi-instance
topology, component tree).
Total effort re-estimated from 24-28w to 28-33w; split expands from
3 plans to 4 (Plan 0 -> A -> B -> C).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -36,7 +36,9 @@
|
||||
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。
|
||||
8. **一个平台账号只绑定一个 workspace**(不支持跨租户共享账号)。该约束必须落到 DB 唯一约束与 API 级冲突校验,不依赖客户端自觉(见 §6.6 `platform_accounts` 的 `platform_uid` 字段 + 唯一索引)。
|
||||
8. **一个平台账号只绑定一个 workspace**(不支持跨租户共享账号)。该约束必须落到 DB 唯一约束与 API 级冲突校验,不依赖客户端自觉(见 §6.6 `platform_accounts` 的 `platform_uid` 字段 + 唯一索引)。**前置条件**:workspace 必须先升成后端一等安全边界(见 §12 Plan 0);仅靠新增 middleware 不够,因为现有代码里 `Actor/Claims/cache/monitoring` 多处仍按 tenant 粒度(v2.2 三轮审查发现的底座问题)。
|
||||
9. **演进缝预留**:短期 `一账号 = 一 workspace = 一台 client` 是产品边界,不是代码硬写;DB 列名采用 `credential_holder_workspace_id` / `account_home_client_id` 风格而非 `owner_*`,为将来"共享运营账号池 / 桌面端主备"留演进位置(见 §6.6 DDL 注释)。
|
||||
10. 要求最小 electron 体积包
|
||||
|
||||
---
|
||||
|
||||
@@ -95,9 +97,14 @@ apps/desktop-client/
|
||||
### 3.3 关键复用(更务实的口径)
|
||||
|
||||
**可复用**:
|
||||
|
||||
- `packages/shared-types`:平台枚举、任务类型、DTO。需要**扩展**:加入租约、platform_uid、patch manifest 等新类型。
|
||||
- `packages/http-client`:HTTP 基座直接用。
|
||||
- **新建 `packages/ui-shared`**:从 `apps/admin-web` 抽出**通用组件、i18n、样式、utils**。admin-web 与 desktop-client 两边都只消费共享包。Renderer 是一个极简 Vite 项目,不是克隆+删页面。
|
||||
- `packages/http-client`:HTTP 基座直接用;**v2.3 新增** `SseClient` 类——内置指数退避重连、`Last-Event-ID` 处理、首帧 snapshot 消费、事件去重。Web 和 Desktop 两端共用。[apps/admin-web/src/lib/api.ts](apps/admin-web/src/lib/api.ts) 现有的简陋 `subscribeSSE` 迁移到这里并补能力。
|
||||
- **新建 `packages/ui-shared`**:从 `apps/admin-web` 抽出**通用组件、i18n、样式、utils**。admin-web 与 desktop-client 两边都只消费共享包。Renderer 是一个极简 Vite 项目,不是克隆+删页面。**必须抽出**的组件:
|
||||
- `MarkdownPreview.vue` + `markdown-it` 渲染器(让 Web 发布前预览和 Desktop manual-review 用同一套渲染,避免"Web 看到的"和"桌面端审核看到的"不一致)。
|
||||
- `AccountCard.vue` / `TagMultiSelect.vue` / `HealthBadge.vue`(账号管理 + 发布 Modal 共用)。
|
||||
- `SseSubscription.vue` composable(封装 `SseClient` 给 Vue 用)。
|
||||
- i18n 文案、主题变量、toast/dialog 系统。
|
||||
- 老适配器里的**选择器字符串**和**业务流程描述**("打开编辑器 → 填标题 → 粘正文 → 上传封面 → 点发布"这种语义)。
|
||||
|
||||
**不可复用、必须重写或新建**:
|
||||
@@ -185,8 +192,8 @@ type ProxyConfig = {
|
||||
| 豆包 | Doubao | 主动保活 | 8 min + 30 min 探活 |
|
||||
| 千问 | Qwen | 主动保活 | 8 min + 30 min 探活 |
|
||||
| DeepSeek | DeepSeek | 只探活 | 30 min |
|
||||
| 混元 | Hunyuan(腾讯)· 登录方式:微信扫码 / QQ / 邮箱(微信涉及 `open.weixin.qq.com` iframe + 本地 loopback 检查)| 接入时按实际 transport 复测;默认主动保活 | 8 min + 30 min 探活 |
|
||||
| Kimi | Moonshot · 登录方式:微信扫码 / 手机号快捷登录。实测请求是 `POST /apiv2/kimi.gateway.chat.v1.ChatService/Chat`(gRPC-Web binary-framed,**非 EventSource**,parser 需先 framing 解包,见 §5.2) | 接入时按实际 transport 复测;默认主动保活 | 8 min + 30 min 探活 |
|
||||
| **元宝**(腾讯)| Yuanbao · 入口 `yuanbao.tencent.com`(**不是** hunyuan.tencent.com,后者是开发者/模型页不是对话入口)· 登录方式:微信扫码 / QQ / 邮箱(微信涉及 `open.weixin.qq.com` iframe + 本地 loopback 检查)| 接入时按 §5.2 spike 矩阵复测;默认主动保活 | 8 min + 30 min 探活 |
|
||||
| Kimi | Moonshot · 入口 `www.kimi.com` · 登录方式:微信扫码 / 手机号快捷登录。实测请求是 `POST /apiv2/kimi.gateway.chat.v1.ChatService/Chat`(gRPC-Web binary-framed,**非 EventSource**,parser 需先 framing 解包,见 §5.2) | 接入时按 §5.2 spike 矩阵复测;默认主动保活 | 8 min + 30 min 探活 |
|
||||
|
||||
实施细节:
|
||||
|
||||
@@ -241,20 +248,35 @@ interface MonitorAdapter {
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 监控适配器:流式 SSE 抓取技术路径(核心修正)
|
||||
### 5.2 监控适配器:流式 SSE 抓取技术路径(**先 spike 后定案**)
|
||||
|
||||
**不要用** `Network.responseReceivedExtraInfo + getResponseBody`——前者是响应头/Cookie 元信息,后者只能在响应整段收完后取完整 body,**对长连接 SSE 拿不到流式增量**。
|
||||
**v2.2 review 发现 `Fetch.takeResponseBodyAsStream` 与 `Fetch.continueResponse` 在 CDP 语义上互斥**:前者独占响应 body,后者让页面继续消费。因此**不能**写成"连续调用链"。v2.3 改为"分流 + 实测矩阵 + spike gate"。
|
||||
|
||||
**按传输类型分流**:
|
||||
**总方针**:不同平台用不同 domain,**不劫持能不劫持**(劫持会阻塞页面继续跑),只在必须看响应 body 时才用 Fetch 劫持。
|
||||
|
||||
| 页面使用的机制 | 正确的 CDP 抓法 |
|
||||
|---|---|
|
||||
| `new EventSource(url)` | `Network.enable` → `Network.eventSourceMessageReceived` 监听每条 message |
|
||||
| `fetch(url)` + `text/event-stream` + streaming body | `Fetch.enable({patterns})` → `Fetch.requestPaused` → `Fetch.continueResponse` → `Fetch.takeResponseBodyAsStream` + `IO.read` 流式拼接 |
|
||||
| `fetch(url)` + chunked transfer + 自定义协议 | 同上(或 Chromium ≥ 120 的 `Network.streamResourceContent + Network.dataReceived`,**需先确认 Electron 打包的 Chromium 版本支持**)|
|
||||
| **gRPC-Web / binary-framed fetch-stream**(如 Kimi `/apiv2/kimi.gateway.chat.v1.ChatService/Chat`)| 同 `Fetch.enable + takeResponseBodyAsStream + IO.read`,但 **provider parser 必须先做 gRPC-Web framing 解包**(5-byte 前缀:1B compressed flag + 4B message length)再按 protobuf 解 payload。正规化成 MonitorResult 前建议引入 `framer.ts` 公共工具 |
|
||||
**按传输类型分流**(每条路线的可用性需在 Plan A 先做 **spike 矩阵**确认):
|
||||
|
||||
**Chromium 版本兼容矩阵**由 `docs/superpowers/specs/electron-chromium-cdp-matrix.md`(本 spec 落地后新建)维护;每次升 Electron 大版本检查一次上面这些 domain 是否仍可用。
|
||||
| 页面使用的机制 | 推荐 CDP 路径 | 约束 |
|
||||
|---|---|---|
|
||||
| `new EventSource(url)` | `Network.enable` → `Network.eventSourceMessageReceived` 监听每条 message | 仅监听、不劫持。最干净。 |
|
||||
| `fetch(url)` + `text/event-stream` + streaming body | `Network.enable` → `Network.streamResourceContent(requestId)` + `Network.dataReceived` 事件流式拼接 | **Chromium ≥ 120**(Electron 41+ 满足)。仅监听、不阻断页面。|
|
||||
| `fetch(url)` + chunked transfer + 自定义协议 | 同上 `streamResourceContent + dataReceived` | 同上 |
|
||||
| **gRPC-Web / binary-framed fetch-stream**(Kimi `/apiv2/kimi.gateway.chat.v1.ChatService/Chat`)| 同上 `streamResourceContent + dataReceived`,拿到 raw bytes 后 **provider parser 先做 gRPC-Web framing 解包**(5-byte 前缀:1B compressed flag + 4B message length)再解 payload | 引入 `adapters/_shared/framer.ts` 公共工具 |
|
||||
| 必须**劫持 / 替换** 响应(一般不需要,只在改写响应或注入时)| `Fetch.enable({patterns})` → `Fetch.requestPaused` → `Fetch.fulfillRequest`(**不再调 continueResponse**);若需读 body 再处理,用 `Fetch.getResponseBody`(**等响应结束**)后统一返回 | `takeResponseBodyAsStream` 后页面**不能继续正常消费该响应**,故仅用于"必须替换响应"场景,监控场景不选 |
|
||||
|
||||
**Spike 矩阵(Plan A 必须先跑)**:
|
||||
|
||||
```
|
||||
provider × transport × Electron 版本 × domain 可用性 × 实际效果
|
||||
豆包 千问 DeepSeek 元宝/混元 Kimi
|
||||
EventSource ? ? ? ? ?
|
||||
streamResource ? ? ? ? ?
|
||||
dataReceived ? ? ? ? ?
|
||||
```
|
||||
|
||||
每个 cell 在开工前用 `electron/chrome://devtools` 实跑一次登录后的真实聊天请求,记录实际的 request 种类与可抓到的 body 行为。**没有 spike 结果就不能定任何 adapter 的实现细节**。
|
||||
|
||||
**Chromium/CDP 兼容矩阵文件**:`docs/superpowers/specs/electron-chromium-cdp-matrix.md`(Plan A 阶段新建),记录每次升 Electron 大版本的 domain 可用性回归结果。截至 2026-04-18 Electron stable 41.2.0(Chromium 146),上述三类 domain 全部可用。
|
||||
|
||||
实施步骤:
|
||||
|
||||
@@ -291,11 +313,17 @@ type PublishState =
|
||||
```
|
||||
|
||||
**状态机与租约的绑定**(见 §6 协议):
|
||||
- 进入 `filling / readyToReview / submitting` 时,适配器必须每 60 s 调 `lease-manager.extend(lease_token)` 续租。
|
||||
- 进入 `readyToReview` 时,lease-manager 同步把服务端任务切到 `waiting_user` 状态,服务端 lease 续期 = 30 min(长于默认 10 min),避免等用户审核过程中被抢任务。
|
||||
|
||||
- 进入 `filling / submitting` 时,适配器必须每 60 s 调 `lease-manager.extend(lease_token)` 续租(lease TTL 默认 10 min)。
|
||||
- 进入 `readyToReview` 时,**人工审核可能持续几分钟到几小时**,不能用 lease 续期表达。lease-manager **释放当前 lease**,把服务端任务切到 **`waiting_user` parked state**:
|
||||
- 任务归属仍绑定原 `target_account_id + target_client_id`,**不会被其它 client 抢走**(因为 target_client_id 路由规则)。
|
||||
- 任务**不持有活动 lease**(`active_attempt_id = null`),server 不会因 lease 超时回滚状态。
|
||||
- 用户点"发布"后,客户端**重新获取 lease**(`POST /tasks/{id}/lease` 带 `from_parked=true`),进入 `submitting`。
|
||||
- 用户跨重启恢复:同样的路径,因为 parked state 持久在 DB。
|
||||
- 服务端可选设置 "parked 超过 N 小时则自动 abort" 的 fallback(默认 24h,可配置)。
|
||||
- 用户取消或 signal abort → `aborted`。
|
||||
- adapter 抛异常 → `failed`,带错误码。
|
||||
- 进程崩溃 → 启动时按 §7.5 分派为 `unknown`。
|
||||
- 进程崩溃 → 启动时按 §7.5 分派为 `unknown`(仅限 publish 任务的 `submitting` 阶段)。
|
||||
|
||||
文件上传:`protocol.handle('file')` + CDP `Input.dispatchDragEvent` 或直接喂 `<input type=file>`,不经服务端中转。
|
||||
|
||||
@@ -351,7 +379,10 @@ type PublishState =
|
||||
|
||||
- 结构化日志(pino JSON)滚动写入 `userData/logs/`。
|
||||
- 任务失败时自动截屏 3–5 张 + 存 Network 响应摘要。
|
||||
- 本地内置极简 viewer(Vue 单页,离线),能读 `.trace.json` 文件。
|
||||
- 本地内置极简 viewer(Vue 单页,离线),能读 `.trace.json` 文件。**加载与隔离要求**:
|
||||
- Viewer Vite 构建用 **hash 路由**(`createWebHashHistory`),避免 `file://` 协议下 history 路由 404。
|
||||
- Viewer 跑在 **独立的 sandboxed `<iframe>`**(CSP `sandbox="allow-scripts"`,不开 `allow-same-origin`),挂在主 renderer 的"开发者"菜单下;trace 数据通过 `postMessage` 从主进程 → 主 renderer → iframe 单向传递。
|
||||
- 防御目标:trace 里如果夹带了恶意 HTML/script(比如平台响应里注入的脚本),也无法从 iframe 逃到主 renderer 或主进程。
|
||||
- 脱敏:Cookie / Set-Cookie / Authorization / x-xsrf-token / password / 手机号 / email → `<redacted>`。
|
||||
- 存储:`userData/traces/<accountId>/<taskId>.trace.zip`,配额默认 **50 份** 或 **500 MB**(先到先清)。
|
||||
- **默认不上传服务端**;用户手动"发送诊断包"才上传。
|
||||
@@ -376,6 +407,23 @@ type PublishState =
|
||||
- `server/internal/tenant/transport/*.go` handler
|
||||
- 对应 repo 层单元测试
|
||||
|
||||
#### 6.1.1 监控域存量改造清单(不是 rename 能完成的)
|
||||
|
||||
v2.2 review 发现监控子系统存量耦合比插件本身深。Plan A/B 前必须 spike 这张表:
|
||||
|
||||
| 层次 | 文件 / 表 | 现状 | 改造动作 |
|
||||
|---|---|---|---|
|
||||
| 迁移 | `server/migrations/20260410103000_create_monitoring_tables.up.sql` | `tenant_monitoring_quotas` 有 `collection_mode='plugin'` + `primary_installation_id` | 改 `collection_mode='desktop'` + `primary_client_id`(引用 `desktop_clients`);加 `workspace_id` 列 |
|
||||
| Repo | `server/internal/tenant/repository/queries/monitoring*.sql` | 多处 JOIN `plugin_installations` | 全量改 JOIN `desktop_clients` + 加 `workspace_id` 条件 |
|
||||
| Service | `server/internal/tenant/app/monitoring_service.go` / `monitoring_callback_service.go` | 直接查 `plugin_installations`、按 tenant 选 primary installation | 改查 `desktop_clients`;`primary` 概念在 workspace 粒度(Plan 0 完成后)|
|
||||
| Transport | `server/internal/tenant/transport/monitoring_handler.go` | `/api/plugin/monitoring/*` callback | 对应 `/api/desktop/monitoring/*`(或合并进 `/api/desktop/tasks/*` result 回写,具体在 Plan A spike 决定)|
|
||||
| Projection | `monitoring_query_results` / `monitoring_callback_logs` | 按 installation_id 索引 | 按 `(workspace_id, client_id)` 重索引;旧数据因开发版本无需保留,直接 drop |
|
||||
| Dashboard | admin-web 监控结果页 | 按 tenant 汇总 | 改 workspace 汇总 |
|
||||
| Seed | `server/cmd/dev-seed/main.go` 等 | 生成 plugin_installation 示例 | 生成 desktop_client 示例 |
|
||||
| 定时清理 | (目前没有 tombstone / parked 清理)| — | 新建:parked > 24h 的任务 auto-abort,tombstone > 30 天的 account 清理 |
|
||||
|
||||
**行动**:Plan A 启动前用 0.5 周做一次 impact spike,核对以上每一行都有具体 owner、估时、依赖;**没有确认就不开工**。监控域存量改造是 Plan A 工期的隐藏成本。
|
||||
|
||||
### 6.2 认证
|
||||
|
||||
| Token | 代表 | 发放 | 用途 |
|
||||
@@ -436,10 +484,10 @@ POST /api/desktop/clients/metrics # 轻量非敏感聚合指标
|
||||
# 事件 & 任务租约
|
||||
GET /api/desktop/events # SSE, client_token
|
||||
POST /api/desktop/tasks/lease # {task_id?} 原子领取;kind=publish|monitor 过滤
|
||||
POST /api/desktop/tasks/{id}/extend # body: {lease_token},续租
|
||||
POST /api/desktop/tasks/{id}/pause # body: {lease_token, reason: 'waiting_user'|'captcha'}
|
||||
POST /api/desktop/tasks/{id}/resume # body: {lease_token}
|
||||
POST /api/desktop/tasks/{id}/cancel # body: {lease_token}
|
||||
POST /api/desktop/tasks/{id}/extend # body: {lease_token},续租(短时占用用)
|
||||
POST /api/desktop/tasks/{id}/park # body: {lease_token, reason: 'waiting_user'|'captcha'} 释放 lease、状态 → waiting_user
|
||||
POST /api/desktop/tasks/{id}/lease?from_parked=true # 从 parked 重新领取(不走 queue,指定回到 target_client)
|
||||
POST /api/desktop/tasks/{id}/cancel # body: {lease_token}(或无 lease 时带 client_token 由 target_client 主动取消)
|
||||
POST /api/desktop/tasks/{id}/result # body: {lease_token, status, payload}
|
||||
POST /api/desktop/tasks/{id}/trace # 用户主动上传诊断包
|
||||
|
||||
@@ -460,7 +508,20 @@ POST /api/tenant/publish-jobs
|
||||
|
||||
**幂等契约**:所有写操作(lease/extend/pause/resume/cancel/result)**必须携带 `lease_token`**;服务端校验 `lease_token` 与当前 `active_attempt_id` 绑定,否则返 409 Conflict。客户端收到 409 = 租约已失效,**停止动作 + 提示人工对账**。
|
||||
|
||||
**中间件要求**:`/api/desktop/*` 沿用现有 `TenantScope` 即可(因为每个 client 只属于一个 workspace);`/api/tenant/events`、`/api/tenant/accounts`、`/api/tenant/publish-jobs` 必须**新增 `WorkspaceScope` middleware**——当前 `server/internal/shared/middleware/tenant_scope.go` 只注入 `tenant_id`,不足以隔离同租户下不同 workspace 的事件订阅/数据访问。`WorkspaceScope` 从 User JWT 的 `workspace_id` claim 或 URL path 派生,**永不接受 query 参数自由选择**;所有涉及 workspace 归属的查询在 middleware 里强制注入 `workspace_id = :actor_workspace_id` 过滤条件。
|
||||
**中间件与 workspace 一等化要求**(**先决条件**,不是实施细节):
|
||||
|
||||
v2.2 review 发现现有 `server/internal/shared/auth/claims.go` 只有 `user_id/tenant_id/role`,`server/internal/shared/middleware/tenant_scope.go` 只注入 `tenant_id`,`server/internal/tenant/app/cache_support.go` workspace cache key 按 tenantID 生成,仓库**没有 workspaces 表迁移**,监控表 `tenant_monitoring_quotas` 按 tenant 存 `primary_installation_id`。**单加一个 `WorkspaceScope` middleware 解决不了底座问题**。
|
||||
|
||||
正确顺序:**Plan 0(§12)必须先完成 workspace 底座一等化**——
|
||||
|
||||
1. `workspaces` 表 + `workspace_memberships` 表 DB 迁移。
|
||||
2. `Actor/Claims` 结构扩 `workspace_id`;`client_token` 也绑定 `workspace_id`(稳定维度)。
|
||||
3. `TenantScope` 之上新加 `WorkspaceScope` middleware,派生 topic/过滤条件从**服务端 Actor 派生**,不接受 query 参数自由选择。
|
||||
4. 所有 repo 层 query 注入 `workspace_id = :actor_workspace_id`。
|
||||
5. `cache_support.go` 的所有 workspace-sensitive key 加 `workspace_id`。
|
||||
6. 监控域 `tenant_monitoring_quotas` / `monitoring_service.go` / `monitoring_callback_service.go` 迁到 workspace 粒度(见 §6.1 子节"监控域存量改造清单")。
|
||||
|
||||
只有 Plan 0 完成后,`/api/desktop/*` 和 `/api/tenant/*` 的 workspace 隔离承诺才能真正落地。
|
||||
|
||||
### 6.5 服务端边界
|
||||
|
||||
@@ -587,8 +648,12 @@ Client POST /tasks/lease → DB atomic claim →
|
||||
↓
|
||||
Client 执行(filling→submitting),每 60s POST /extend
|
||||
↓
|
||||
如果 manual 等审核 → POST /pause(reason=waiting_user) → lease_expires_at=+30min
|
||||
审核通过 → POST /resume
|
||||
如果 manual 进入审核 → POST /park(lease_token, reason=waiting_user)
|
||||
→ server 释放 lease(active_attempt_id=null),状态置 waiting_user
|
||||
→ 任务仍绑定 target_client_id,不会被抢;不承担 lease 超时压力
|
||||
审核通过 → client POST /tasks/{id}/lease?from_parked=true 重新领取
|
||||
→ 获得新 attempt_id + 新 lease_token,进 submitting
|
||||
parked 超时(默认 24h)→ server 自动 abort,状态变 aborted
|
||||
↓
|
||||
POST /result(lease_token, status=succeeded|failed) → server 校验 lease_token 后存结果
|
||||
↓
|
||||
@@ -782,7 +847,7 @@ admin-web 侧 "Modal 里账号消失" = client 已走完 DELETE 路径且 tombst
|
||||
|---|---|---|---|
|
||||
| `georankly://accounts` | "前往桌面端管理账号" | — | 未装 → 下载页 |
|
||||
| `georankly://accounts/add?platform=wechat` | "一键添加某平台账号" | platform | 同上 |
|
||||
| `georankly://tasks/<id>/review?nonce=<jwt>` | Manual 模式等审核 | **必须带 nonce**:server 签发的短期 JWT(TTL 60 s,含 `task_id + actor_id + workspace_id`),客户端打开深链时先回调 `POST /api/desktop/links/verify` 换取可访问凭证;防止任意网页构造深链唤起本地敏感页 | 同上 |
|
||||
| `georankly://tasks/<id>/review?nonce=<opaque_code>` | Manual 模式等审核 | **必须带一次性 nonce**:server 签发的**短期 opaque code**(不是 JWT——避免把 claims 放进 URL)。TTL 60 s,后端存入 Redis 带 `jti`;绑定 `(task_id, target_client_id, actor_user_id)`。客户端打开深链时先回调 `POST /api/desktop/links/verify` 换取访问凭证:服务端校验 `target_client_id == 当前 client_token 的 client_id` 且 nonce 未被消费过,通过后**立即 delete jti 失效**。同机恶意进程即使抓到 URL 也无法重放(不同 client_id / 已消费)。 | 同上 |
|
||||
|
||||
**点击交互链路**(解决跨浏览器行为差异 + 协议未注册 fallback):
|
||||
|
||||
@@ -847,8 +912,21 @@ user clicks deep link in admin-web
|
||||
|
||||
**Publish 任务**(不可逆副作用,保守处理):
|
||||
|
||||
- **未进入 `submitting` 阶段**(即还在 `filling` 或 `readyToReview`):重新 ack → 把 state 重置到 `filling` 从头跑,因为还没点过"发布"按钮。
|
||||
- **已进入 `submitting`**:**放弃任务,标 `unknown`**。UI 提示"上次崩溃时尚不确定是否发布成功,请去平台确认后手动标注 succeeded 或重新创建任务"。避免重复发布。
|
||||
- **未进入 `submitting` 阶段**(即还在 `filling` 或 `waiting_user` parked):重新 ack → 把 state 重置到 `filling` 从头跑,因为还没点过"发布"按钮。
|
||||
- **已进入 `submitting`**:**放弃任务,标 `unknown`**。避免重复发布。
|
||||
|
||||
**UNKNOWN 任务的 UI 对账工作流**(不只是"提示一下"就完事):
|
||||
|
||||
UNKNOWN 任务在 admin-web "事件中心"和"发布历史"页有专门区块,黄色高亮 + 顶部固定。每条带一个 Action Dropdown:
|
||||
|
||||
| 用户动作 | 后端行为 | 语义 |
|
||||
|---|---|---|
|
||||
| **"已确认发布成功"** | `POST /tasks/{id}/reconcile {status: 'succeeded', external_url}` | 把状态坐实为 `succeeded`,计入成功统计 |
|
||||
| **"已确认发布失败"** | `POST /tasks/{id}/reconcile {status: 'failed', reason}` | 状态改 `failed`,允许触发 retry |
|
||||
| **"忽略此任务"** | `POST /tasks/{id}/reconcile {status: 'aborted'}` | 状态改 `aborted`,从待办清单撤离 |
|
||||
| **"重新创建任务"** | `POST /api/tenant/publish-jobs` 复制原 payload | 原任务保留 UNKNOWN 作为审计记录,新建一条跑 |
|
||||
|
||||
**关键**:UNKNOWN 不会自动消失——用户必须显式选一项。超过 7 天没处理的 UNKNOWN 任务每周触发一次站内通知给 workspace owner。
|
||||
|
||||
---
|
||||
|
||||
@@ -969,7 +1047,7 @@ GitHub Actions,runner 矩阵:`macos-14 (arm64)` + `macos-13 (x64)` 合并 un
|
||||
| 阶段 | 受众 | 签名 | 退出门槛 |
|
||||
|---|---|---|---|
|
||||
| **Alpha(内部)** | 开发 + QA(≤5 人)| Mac 公证 / Win 无签名 / Linux 裸 AppImage | 13+5 适配器 fake + 真实账号成功率 ≥ 90%;lease 协议在混沌测试下无脏写 |
|
||||
| **Beta(邀请制)** | 公司内部 + 合作客户(≤30 人)| 同上 | 连续 7 天无 P0;保活命中率 > 95%;patch 通道应急修复跑通 ≥ 1 次;autoUpdater smoke 每 release 全过 |
|
||||
| **Beta(邀请制)** | 公司内部 + 合作客户(≤30 人)| 同上 | 连续 7 天无 P0;保活命中率 > 95%;**patch 通道最小闭环(Plan B 块 G1)已上线,应急修复跑通 ≥ 1 次**;autoUpdater smoke 每 release 全过 |
|
||||
| **GA 候选** | 放开注册 | **Win 签名必须到位** | 安装成功率 > 99%;crash rate < 0.1%/session |
|
||||
|
||||
客户端和服务端同加 `channel`(alpha/beta/stable),更新按 channel 拉不同 `latest-*.yml`。"关于"里可切。
|
||||
@@ -985,23 +1063,24 @@ GitHub Actions,runner 矩阵:`macos-14 (arm64)` + `macos-13 (x64)` 合并 un
|
||||
|
||||
如果未来发现有内部测试账号遗留在老表里:手动导出账号元数据后 drop 老表,不写自动迁移。
|
||||
|
||||
### 10.3 工作量重估(9 块)
|
||||
### 10.3 工作量重估(10 块)
|
||||
|
||||
粗估(单人工程周):
|
||||
|
||||
| 块 | 范围 | 估时 |
|
||||
|---|---|---|
|
||||
| **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(从零)** + **混元(从零)** + **Kimi(从零)** | 4–5 周 |
|
||||
| **0. Workspace 底座一等化**(v2.3 新增 · Plan 0)| `workspaces` + `workspace_memberships` 表迁移;`Actor/Claims` 加 `workspace_id`;`WorkspaceScope` middleware;所有 repo query 注入 `workspace_id`;`cache_support.go` key 改 workspace 粒度;监控域存量改造清单(§6.1.1)完整迁 | **2–3 周(阻塞 Plan A)** |
|
||||
| **A. Desktop runtime 基座** | Main 进程、session-registry、view-pool、vault、lease-manager(含 parked state)、rate-limiter、circuit-breaker、crash-recovery、tracing(Alpha 版)、transport (SSE + pull + RabbitMQ presence) | 3–4 周 |
|
||||
| **B. LLM 监控适配器** | Doubao + Qwen + **DeepSeek(从零)** + **元宝(从零)** + **Kimi(从零)**;**前置 § 5.2 spike 矩阵 0.5 周** | 4.5–5.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 周 |
|
||||
| **E. 服务端协议 + DB + sqlc 重生 + Service 层重构** | `/api/desktop/*`、租约(含 park/lease-from-parked)、presence、patch manifest、reconcile | 2.5 周 |
|
||||
| **F. 打包 5 arch + Mac 公证 + autoUpdater + updater smoke** | — | 1.5 周 |
|
||||
| **G. Patch 通道(DSL 解释器 + sandbox 备用 + 签名链)** | — | 2 周 |
|
||||
| **H. packages/ui-shared 抽取 + desktop renderer** | — | 1.5 周 |
|
||||
| **I. 测试基建(fake server + fake 平台 + replay)** | — | 1.5 周 |
|
||||
| **G. Patch 通道** | G1(**最小闭环**:服务端签名 manifest + 客户端 Ed25519 验签 + DSL 解释器 + revoke + rollback)前移到 Plan B,1.5 周;G2(备用隔离执行通道 + 签名链全套 TUF 要素 + 密钥轮换)留在 Plan C,1 周 | 2.5 周(拆 1.5 + 1)|
|
||||
| **H. packages/ui-shared 抽取 + desktop renderer** | 抽 `MarkdownPreview / AccountCard / TagMultiSelect / HealthBadge / SseSubscription composable` 等;**PublishArticleModal 重构为账号级多选(原 accounts[0] 假设要彻底改)**;i18n、主题 | **2.5–3 周(v2.2 review 低估了 1–1.5 周)** |
|
||||
| **I. 测试基建** | fake server + fake 平台 + replay + autoUpdater 核心逻辑单测 | 1.5 周 |
|
||||
|
||||
合计约 **24–28 工程周**(LLM 从 3 家扩到 5 家增加约 2 周)。这份 spec 因此**应拆 3 个 plan**(见 §12)。
|
||||
合计约 **28–33 工程周**(v2.3 新增 Plan 0 前置 + Modal 重构工期修正 + G 拆前后期)。这份 spec 因此**拆 4 个 plan**(见 §12)。
|
||||
|
||||
### 10.4 三层回滚
|
||||
|
||||
@@ -1039,42 +1118,75 @@ GitHub Actions,runner 矩阵:`macos-14 (arm64)` + `macos-13 (x64)` 合并 un
|
||||
|
||||
## 12. Plan 拆分建议
|
||||
|
||||
本 spec 包含约 24–28 周的工作量,应按以下 3 个独立 plan 执行,每个 plan 都能独立 ship:
|
||||
本 spec 包含约 28–33 周的工作量,拆 **4 个独立 plan** 执行。**Plan 0 是 Plan A 的硬前置**(workspace 底座不一等化,后面所有协议/鉴权/缓存/监控承诺都无法真正落地)。
|
||||
|
||||
### Plan A · 骨架穿透(5–6 周,Alpha 上限)
|
||||
### Plan 0 · Workspace 底座一等化(2–3 周 · 阻塞 Plan A)
|
||||
|
||||
- 块 A(Desktop runtime 基座)
|
||||
- 块 E(服务端协议 + DB)
|
||||
- 块 H(ui-shared + renderer)
|
||||
- **1 个** 监控适配器(Doubao)+ **1 个** 发布适配器(头条号,它已有 publish 能力)
|
||||
v2.2 review 发现现有代码里 workspace 不是后端一等安全边界(`Actor/Claims` 只有 tenant_id、`cache_support.go` key 按 tenant、没有 workspaces 表迁移、监控表按 tenant)。这些是横切式改造,不能作为局部补丁塞进 Plan A。
|
||||
|
||||
- `workspaces` + `workspace_memberships` 表迁移
|
||||
- `Actor/Claims` 结构扩 `workspace_id`;User JWT 也加
|
||||
- `TenantScope` 之上新加 `WorkspaceScope` middleware
|
||||
- 所有 repo layer query 显式注入 `workspace_id = :actor_workspace_id`
|
||||
- `cache_support.go` workspace-sensitive key 加 `workspace_id`
|
||||
- **监控域存量改造清单**(§6.1.1)全量迁:`tenant_monitoring_quotas` → 加 workspace_id + 改 `primary_client_id`;`monitoring_service.go` / `monitoring_callback_service.go` / `monitoring_handler.go` 全量改造
|
||||
- admin-web 现有页面的 API 调用补 workspace context
|
||||
|
||||
**退出目标**:现有 admin-web 页面全部在 workspace 粒度正常工作;tenant_monitoring 按 workspace 隔离;`/api/tenant/*` 所有路由挂上 `WorkspaceScope`;repo 单元测试覆盖 cross-workspace 防穿透用例。
|
||||
|
||||
### Plan A · 桌面端骨架穿透(5–6 周,Alpha 上限)
|
||||
|
||||
- 块 A(Desktop runtime 基座,含 parked state 实现)
|
||||
- 块 E(服务端协议 + DB + reconcile endpoint)
|
||||
- 块 H(ui-shared 抽取 + renderer + **PublishArticleModal 重构为账号级多选**)
|
||||
- **§5.2 spike 矩阵(0.5 周)先做再定适配器实现**
|
||||
- **1 个** 监控适配器(Doubao)+ **1 个** 发布适配器(头条号,已有 publish 能力)
|
||||
- 块 I(测试基建)
|
||||
- 块 F 但**仅 Mac 公证**
|
||||
- 块 F **仅 Mac 公证**
|
||||
|
||||
**退出目标**:端到端能跑一条 Doubao 监控任务 + 一条头条发布任务,Mac 公证版可安装,lease 协议在 fake 多实例下无脏写。
|
||||
**退出目标**:端到端跑通一条 Doubao 监控任务 + 一条头条发布任务(含 manual 模式 parked → resume);Mac 公证版可安装;lease 协议在 fake 多实例 + 混沌测试下无脏写;UNKNOWN 任务的 reconcile 工作流闭环。
|
||||
|
||||
### Plan B · 广度铺开(10–12 周,Beta 上限)
|
||||
### Plan B · 广度铺开(12–14 周,Beta 上限)
|
||||
|
||||
- 块 B(Qwen + DeepSeek + 混元 + Kimi 从零)
|
||||
- 块 B(Qwen + DeepSeek + 元宝 + Kimi 从零)
|
||||
- 块 C(其余 5 家已有真实发布能力的平台迁移)
|
||||
- 块 D(7 家 detect-only 从零实现 publish)
|
||||
- **块 G1 · Patch 通道最小闭环**(服务端签名 manifest + 客户端 Ed25519 验签 + DSL 解释器 + revoke + rollback,1.5 周)——前移到此,满足 §10.1 Beta 退出门槛"patch 通道应急修复跑通 ≥ 1 次"
|
||||
- 块 F 完整:Win/Linux 全架构 + 签名(Win 签名到位)+ autoUpdater 完整
|
||||
- 块 I 补齐:多适配器回归 + 冒烟 Playbook
|
||||
|
||||
**退出目标**:13+5 适配器全量可用,autoUpdater smoke 每 release 全过,5 arch × 3 OS 全量打包签名。
|
||||
**退出目标**:13+5 适配器全量可用;autoUpdater smoke 每 release 全过;5 arch × 3 OS 全量打包签名;patch 通道应急修复在 Beta 期至少跑通一次。
|
||||
|
||||
### Plan C · 运维闭环(4–5 周,GA 上限)
|
||||
### Plan C · 运维闭环与高级能力(4–5 周,GA 上限)
|
||||
|
||||
- 块 G(Patch 通道 DSL + 沙箱 + 签名链)
|
||||
- Phase 2 Trace 基建(CDP 全事件流 + Playwright 兼容导出)
|
||||
- Metrics dashboard + 报警规则
|
||||
- 回滚剧本与演练
|
||||
- **块 G2 · Patch 通道高级能力**:备用隔离执行通道(utilityProcess + isolated-vm)、根密钥轮换、TUF 风格完整 manifest(1 周)
|
||||
- Phase 2 Trace 基建(CDP 全事件流 + 2 fps 截屏 rolling buffer + Playwright Trace Viewer 兼容导出)
|
||||
- Metrics dashboard + 报警规则(crash rate / SSE 保持时长 / adapter 日成功率 / patch 覆盖率)
|
||||
- 回滚剧本与混沌演练
|
||||
- 账号共享 / 桌面端主备的演进位置预留(`credential_holder` 抽象,§2 硬约束 9)
|
||||
|
||||
**退出目标**:patch 热更经过混沌演练,trace 能被外部 Playwright tooling 打开,运营指标全套接入报警。
|
||||
**退出目标**:patch 热更经过混沌演练;trace 能被外部 Playwright tooling 打开;运营指标全套接入报警;回滚剧本有实战演练记录。
|
||||
|
||||
---
|
||||
|
||||
## 13. 变更记录
|
||||
|
||||
- **v2.3(2026-04-18)**:Claude + Codex + Gemini 第三轮(架构师视角)交叉审查后的修订。**Codex verdict: Needs-rework**,**Gemini verdict: Ready-with-caveats**——两家合计 2 Critical + 9 Warning + 3 Info。按"全修进 spec + 新增 Plan 0 前置改造"方案落地。
|
||||
- **C1 · Workspace 升成后端一等安全边界**:v2.2 的"新增 WorkspaceScope middleware"是局部补丁,实际需要 JWT claims / repo query / cache / 监控域全链条改造。新增 **Plan 0 · Workspace 底座一等化**(§12,2–3 周,阻塞 Plan A),覆盖 `workspaces` + `workspace_memberships` 表迁移、`Actor/Claims` 加 `workspace_id`、所有 repo query 显式注入 workspace_id、`cache_support.go` key 改粒度、§6.1.1 监控域存量改造清单 8 行。§2 硬约束 8 重写 + 新增硬约束 9(演进缝:`credential_holder` / `account_home` 命名预留)。
|
||||
- **C2 · §5.2 CDP 路线改 "先 spike 后定案"**:v2.2 里 `Fetch.continueResponse` + `Fetch.takeResponseBodyAsStream` 连续调用语义互斥(CDP 文档明文:"Only available in response stage" + "the request can't be continued as is")。v2.3 改为:EventSource 走 `Network.eventSourceMessageReceived`;fetch-stream 优先 `Network.streamResourceContent + Network.dataReceived`(Chromium ≥ 120 已满足);仅"必须劫持/替换"才用 Fetch 且**不再连续调** continueResponse。Plan A 前置 **provider × transport 实测矩阵 spike**(0.5 周),结果入 `electron-chromium-cdp-matrix.md`。
|
||||
- **W1 · Plan 依赖顺序调整**:Beta 退出门槛依赖 patch 通道,但 patch 通道在 Plan C——依赖矛盾。v2.3 把 **块 G 拆为 G1(最小闭环:签名 manifest + Ed25519 验签 + DSL 解释器 + revoke + rollback,1.5 周)前移到 Plan B**,G2(备用隔离执行 + 根密钥轮换 + TUF 全要素,1 周)留在 Plan C。§10.1 Beta 门槛相应加备注。
|
||||
- **W2 · `waiting_user` 从 lease 拆成 parked state**:lease 适合短时占有,不适合 2 小时人审。v2.3 在 `readyToReview` 进入时 **释放 lease**(`active_attempt_id=null`)、状态置 `waiting_user`、任务仍绑定 `target_client_id` 不被抢;审核通过时 client 走 `POST /tasks/{id}/lease?from_parked=true` 重新领取新 attempt;parked 超 24h 自动 abort。§5.3 / §6.4 / §6.7 同步,删 `/pause` + `/resume`,改 `/park` + `/lease?from_parked`。
|
||||
- **W3 · 演进缝预留** → §2 硬约束 9 明写 `credential_holder` / `account_home` 抽象;Plan C 含演进位置预留。
|
||||
- **W4 · 监控域存量改造清单** → §6.1.1 新小节,列 8 行改造盘点(迁移 / Repo / Service / Transport / Projection / Dashboard / Seed / 定时清理)。
|
||||
- **W5 · 深链 nonce 一次性消费 + client 绑定**:改为短期 opaque code(非 JWT),Redis 存 jti,绑 `(task_id, target_client_id, actor_user_id)`,`/links/verify` 成功后立即 delete 失效;同机重放被 client_id mismatch 拦下(§6.8.7)。
|
||||
- **W6 · PublishArticleModal 重构**:Gemini 实测 `apps/admin-web/src/components/PublishArticleModal.vue` 的 `accountCards` 只取 `accounts[0]`——要改成账号级多选需彻底重写。§10.3 块 H 工期 1.5 → 2.5–3 周。
|
||||
- **W7 · `MarkdownPreview.vue` 抽 `packages/ui-shared`**:Web 发布前预览与 Desktop manual-review 用同一渲染器,避免不一致(§3.3)。
|
||||
- **W8 · `packages/http-client` 新增 `SseClient` 类**:标准化指数退避重连 + `Last-Event-ID` + 首帧 snapshot + 事件去重;两端共用(§3.3)。
|
||||
- **W9 · Trace Viewer sandboxed iframe + hash 路由**:`createWebHashHistory` 适配 `file://` 加载;viewer 跑在 CSP sandbox iframe 中,trace 数据通过 `postMessage` 传入,隔离恶意 DOM(§5.6)。
|
||||
- **I1 · LLM 命名 & 产品入口修正**:**Hunyuan → Yuanbao 元宝**(`yuanbao.tencent.com` 才是对话入口,不是 `hunyuan.tencent.com`);新增 **§14 适配器映射表附录**(adapter_id / 公司 / 模型家族 / 产品名 / 官方域名 / 登录方式 / transport 观察)。
|
||||
- **I2 · UNKNOWN 任务 UI 对账工作流**:§7.5 加 `POST /tasks/{id}/reconcile` 端点 + 四种用户动作(已成功 / 已失败 / 忽略 / 重新创建);超 7 天未处理触发站内通知。
|
||||
- **I3 · 新增 §15「需要补的图」**:列 5 张 Mermaid 图(全链路时序 / lease 状态机 / Account CAS resync / 多实例部署 / 前端组件层级),Plan A 期补齐放 `diagrams/`。
|
||||
- **工期重估**:§10.3 从 9 块扩到 10 块(加块 0);总工期 24–28 周 → **28–33 周**(Plan 0 新增 2–3 周 + 块 H 工期修正 +1 周 + G 拆前后期)。§12 从 3 plan 拆成 4 plan。
|
||||
- **v2.2(2026-04-18)**:Claude + Codex + Gemini 对 v2.1 的二轮交叉审查后的修订。修复 4 Critical + 9 Warning。
|
||||
- **C1 多账号路由语义落 schema**:§6.6 `desktop_tasks` 新增一等列 `job_id / target_account_id / target_client_id / platform` + 相关索引;新建 `desktop_publish_jobs` 聚合表;§6.3 SSE 只向 `target_client_id` 推送,lease 仅允许目标 client 原子领取;错 client 会被服务端拒绝。
|
||||
- **C2 Account 一致性 CAS + Tombstone**:§4.1 Account 增 `syncVersion / deletedAt / tags`;§6.6 `platform_accounts` 增 `sync_version / deleted_at / tags` 列与条件唯一索引;§6.8.6 全文重写为 CAS(`if_sync_version`)+ tombstone(soft delete)协议 + resync 三方合并规则 + 并发案例表;新增 `POST /api/tenant/accounts/{id}/request-delete` 由 Web 写删除意图、client 坐实的两段式删除。
|
||||
@@ -1117,7 +1229,33 @@ GitHub Actions,runner 矩阵:`macos-14 (arm64)` + `macos-13 (x64)` 合并 un
|
||||
|
||||
---
|
||||
|
||||
## 14. 术语表
|
||||
## 14. 附录:LLM 适配器映射表(I1)
|
||||
|
||||
截至 2026-04-18 的对外产品入口与传输观察:
|
||||
|
||||
| adapter_id | 公司 | 模型家族 | 消费端产品名 | 官方域名 | 登录方式 | 观察到的 transport |
|
||||
|---|---|---|---|---|---|---|
|
||||
| `doubao` | 字节跳动 | Doubao / 云雀 | 豆包 | `www.doubao.com` | 微信扫码 / 手机号 / 抖音 | fetch-stream(event-stream)· 已有实测基础 |
|
||||
| `qwen` | 阿里 | Qwen / 通义千问 | 通义 | `tongyi.aliyun.com` | 支付宝 / 钉钉 / 手机号 | fetch-stream,带 `bx-ua / x-xsrf-token` 等短期 token |
|
||||
| `deepseek` | DeepSeek | DeepSeek | DeepSeek | `chat.deepseek.com` | 邮箱 / 手机号 / 微信 / Google | 待 spike(旧插件是 default placeholder)|
|
||||
| `yuanbao` | 腾讯 | Hunyuan 混元 | 元宝 | `yuanbao.tencent.com`(**不是** hunyuan.tencent.com)| 微信扫码 / QQ / 邮箱(微信涉及 loopback 检查)| 待 spike |
|
||||
| `kimi` | Moonshot | Kimi | Kimi | `www.kimi.com` | 微信扫码 / 手机号快捷登录 | **gRPC-Web binary-framed**(`POST /apiv2/kimi.gateway.chat.v1.ChatService/Chat`),需 framer 解包 |
|
||||
|
||||
这些条目在 Plan A spike 矩阵后更新为稳定值并记入 `docs/superpowers/specs/electron-chromium-cdp-matrix.md`。
|
||||
|
||||
## 15. 附录:需要补的图(Plan A 期补齐)
|
||||
|
||||
这是 v2.2 / v2.3 review 共同指出的文档短板,前端 + 新人理解链路需要**视觉化**:
|
||||
|
||||
1. **全链路时序图**(Mermaid sequenceDiagram)——Web 点击发布 → Server 入库 → SSE 通知 → Desktop 领取 → 适配器执行 → 结果回传 → Web 更新,含正常流、离线补领流、租约冲突流、parked / resume 流。
|
||||
2. **Lease 状态机图**(Mermaid stateDiagram-v2)——`queued → in_progress → waiting_user(parked) → in_progress → succeeded / failed / unknown / aborted`。
|
||||
3. **Account CAS + Tombstone resync 时序图**(Mermaid sequenceDiagram)——client/server/Web 三方,含"Web 写 delete_requested + client 坐实"的两段式删除。
|
||||
4. **多实例部署架构图**——RabbitMQ fanout + 多个 server 实例 + Redis presence registry + 多个 desktop client。
|
||||
5. **前端组件层级图**(admin-web 与 desktop renderer 共享 `packages/ui-shared` 的依赖关系)。
|
||||
|
||||
这些图由 Plan A 期负责 spec 维护的同学用 Mermaid 补,放在 `docs/superpowers/specs/diagrams/` 目录,并在本 spec 相关章节里用 `` 引用。
|
||||
|
||||
## 16. 术语表
|
||||
|
||||
- **Patch 通道**:仅下发适配器的声明式数据与受限 DSL(默认)或隔离沙箱执行的纯函数(备用),分钟级生效,不重启。
|
||||
- **Release 通道**:完整二进制 + 差分升级,通过 electron-updater。
|
||||
|
||||
Reference in New Issue
Block a user