feat(desktop): 接线 X (Twitter) hook provider - #1230
Conversation
|
| Filename | Overview |
|---|---|
| apps/desktop/src/main/hook-control/manager.ts | 新增表驱动的 X provider lane,并扩展路由、能力协商、绑定、偏好和外部动作处理。 |
| apps/desktop/src/main/hook-control/ipc.ts | 将 provider IPC 泛化至 Telegram 与 X,并让连接标识解析对未知 provider 采取拒绝策略。 |
| apps/desktop/src/main/hook-control/xDeepLink.ts | 为 X OAuth 授权地址和账号主页地址增加独立的严格校验边界。 |
| apps/desktop/src/main/endpointManifestCache.ts | 将 X Hook 端点纳入跨区共享服务白名单,使 CN 构建可使用 Global 域的离线缓存端点。 |
| apps/desktop/src/renderer/components/settings/HookConnectionsSection.tsx | 提炼 provider-neutral 设置卡并接入 X 渠道的开关、绑定动作和工作目录配置。 |
| apps/desktop/src/renderer/components/settings/HookWorkspacePrefsEditor.tsx | 将渠道偏好读取、写入和推送隔离逻辑扩展至 X provider。 |
Sequence Diagram
sequenceDiagram
participant U as 用户
participant UI as Desktop 设置页
participant Main as Hook Control
participant X as X Hook Server
participant Browser as 系统浏览器
U->>UI: 开启 X 渠道
UI->>Main: setProviderEnabled(x, true)
Main->>X: WebSocket + JWT / hello(provider:x)
X-->>Main: provider.bind.state
U->>UI: 发起账号绑定
UI->>Main: providerBindStart(x)
Main->>X: provider.bind.start
X-->>Main: pending + OAuth URL
Main->>Browser: 校验后打开 X OAuth URL
X-->>Main: confirmed binding
Main-->>UI: 推送 X 连接与绑定状态
X->>Main: "task.dispatch(source=x)"
Main->>Main: 校验 source 与 x: externalKey 一致
Main-->>X: task.ack / 任务结果
Reviews (3): Last reviewed commit: "test(desktop): 补 X 渠道设置卡与 prefs 路由镜像用例" | Re-trigger Greptile
There was a problem hiding this comment.
Pull request overview
本 PR 在现有 Slack/Telegram hook-control provider-neutral 状态机基础上,为 Desktop 客户端接入第三个渠道 X (Twitter) hook provider,并同步补齐共享端点清单、会话来源枚举、渠道提示(note)与 Mobile 渲染,使 X 渠道可在端点清单放量后通过设置页完成 OAuth 绑定并参与任务派发/会话展示。
Changes:
- 在 desktop hook-control 中新增 X lane 配置、provider 路由与 IPC 形状扩展(含 open action / prefs / binding cache / session source 等)。
- 新增并接入 X OAuth2(PKCE) 授权链接与 profile 链接的严格校验(shell.openExternal 前的 main 边界校验),并补齐单测。
- 扩展 maker-shared 端点清单键、lizi-mcps 渠道路由 note、i18n 与 mobile/desktop UI 渲染以展示 X 来源与图标。
Reviewed changes
Copilot reviewed 37 out of 38 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/maker-shared/src/clientEndpoints.ts | 端点清单新增 xHookWsUrl key 与 wss: 协议白名单。 |
| packages/maker-shared/src/tests/clientEndpoints.test.ts | 端点清单解析用例补齐 xHookWsUrl。 |
| packages/lizi-mcps/src/cindy_feishuBotMcpServer.ts | 增加 X 会话渠道 note,并纳入 NOTE_BY_SOURCE 路由契约。 |
| packages/lizi-mcps/src/tests/feishuBotChannelRouting.test.ts | 覆盖 X note 的路由拼接与不串扰断言。 |
| apps/mobile/src/session/MessageRenderer.tsx | hookSource 标题展示补 X(Telegram/X/Slack)。 |
| apps/mobile/src/session/messageNormalize.ts | hookSource.im 扩展为 slack/telegram/x 并 fail-closed 解析。 |
| apps/mobile/src/tests/hookSourceRendering.test.ts | 对应更新渲染字符串断言以覆盖 X。 |
| apps/desktop/src/test/vitest/clientEndpointsFixture.ts | desktop 端点 fixture 补 xHookWsUrl。 |
| apps/desktop/src/shared/sessionSource.ts | desktop session source 白名单与 normalize 逻辑补 x。 |
| apps/desktop/src/shared/hookControlIpc.ts | HookProvider/视图形状扩展:新增 x provider 字段与 provider-neutral 类型别名。 |
| apps/desktop/src/renderer/vite-env.d.ts | renderer 侧 ElectronAPI 类型扩展:provider 形参放宽至 `telegram |
| apps/desktop/src/renderer/i18n/locales/zh-CN/common.json | 新增 hook.x.* 文案块、providerX 名称,并同步 Telegram key 结构对齐。 |
| apps/desktop/src/renderer/i18n/locales/ko/common.json | 同步新增/更新 X 与 Telegram 对应文案 key。 |
| apps/desktop/src/renderer/i18n/locales/ja/common.json | 同步新增/更新 X 与 Telegram 对应文案 key。 |
| apps/desktop/src/renderer/i18n/locales/en/common.json | 同步新增/更新 X 与 Telegram 对应文案 key。 |
| apps/desktop/src/renderer/components/settings/HookWorkspacePrefsEditor.tsx | provider-neutral prefs 读写泛化到 Telegram/X(按 bindingId 归属)。 |
| apps/desktop/src/renderer/components/settings/HookConnectionsSection.tsx | 设置页渠道卡抽象为 provider-neutral 渲染函数,并新增 X 卡。 |
| apps/desktop/src/renderer/components/settings/tests/HookWorkspacePrefsEditor.test.tsx | fixture 补 hook.x 视图字段以覆盖新形状。 |
| apps/desktop/src/renderer/components/settings/tests/HookConnectionsSection.test.tsx | IPC mock/断言从 Telegram 专属 open action 切到 provider-neutral open action,并更新 i18n key。 |
| apps/desktop/src/renderer/components/chat/XIcon.tsx | 新增 X 图标组件(currentColor)。 |
| apps/desktop/src/renderer/components/chat/HookTaskCard.tsx | 任务卡 IM 图标/标签补 X。 |
| apps/desktop/src/renderer/tests/automationGeneratedSessions.test.ts | desktop 可见会话 source 合同断言补 x。 |
| apps/desktop/src/preload/preload.ts | preload API 调整为 provider-neutral IPC 形参(provider: telegram/x)。 |
| apps/desktop/src/main/localDb/schema.ts | 本地 DB session source 枚举补 x(类型层)。 |
| apps/desktop/src/main/localDb/ipc/sessions.ts | setSessionSourceInDb 放宽为 `telegram |
| apps/desktop/src/main/hook-control/xDeepLink.ts | 新增 X OAuth2(PKCE) 授权 URL 与 profile URL 的严格校验实现。 |
| apps/desktop/src/main/hook-control/workspaceProviderSourceStore.ts | workspace provider source store 持久化白名单补 x。 |
| apps/desktop/src/main/hook-control/store.ts | store 增加 xEnabled/xBindingCache 槽位并泛化 provider binding cache 写入接口。 |
| apps/desktop/src/main/hook-control/session-runner.ts | 会话创建链路补 vendorOptions.source='x' 与 session source 持久化。 |
| apps/desktop/src/main/hook-control/outbound.ts | prompt note 平台名与格式约束补 X(纯文本回帖提示)。 |
| apps/desktop/src/main/hook-control/manager.ts | 新增 xConfig lane,provider 路由/动作/prefs/binding cache/快照字段扩展到 X。 |
| apps/desktop/src/main/hook-control/ipc.ts | IPC handler 扩展到 provider-neutral(telegram/x)形参,并新增 shell.openExternal 的 X URL 校验。 |
| apps/desktop/src/main/hook-control/dispatcher.ts | 派发入参展示字段对 X 渠道按 Telegram 同口径取 channelName。 |
| apps/desktop/src/main/hook-control/tests/xDeepLink.test.ts | 新增 X URL 校验的正向/负向覆盖用例。 |
| apps/desktop/src/main/hook-control/tests/workspaceProviderSourceStore.test.ts | 覆盖 workspaceProviderSourceStore 的 X 渠道隔离与读写。 |
| apps/desktop/src/main/hook-control/tests/transport-manager.test.ts | hook-control 状态机测试扩展:store 槽位/边界路由/open action 改名适配等。 |
| apps/desktop/src/main/hook-control/tests/store.test.ts | store 默认态与账号隔离用例更新为 provider binding cache 泛化接口与 X 槽位。 |
| apps/desktop/src/main/hook-control/tests/outbound.test.ts | outbound prompt note 用例覆盖 X 平台名与纯文本约束。 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2d04e22fb6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
X 机器人第 4 步(计划见 #691):desktop 侧接入独立部署的 x-hook-server,与 Telegram 同走 provider-neutral 状态机。 - 协议: cindy-protocol 指针 bump 至主干 2520a40(含 'x' provider 常量与 provider:x 能力旗标;服务端 x-hook-server 已同步消费,新增的 turn.reopen 等帧靠能力协商向后兼容,desktop 不宣告即不参与)。 - hook-control: 新增 x lane 配置(端点 xHookWsUrl、requiredFeatures 含 provider:x、绑定缓存、OAuth 授权 URL 校验);顺手参数化三处 Telegram 写死的洞 —— openProviderAction(provider, action)、IPC 六个 provider-neutral handler 从 payload 取 provider、connectionId 后缀解析 不再二分兜底(未登记 provider 不得落到 Slack 的开关与端点)。 providerForTaskDispatch / providerForExternalKey 增加 x: 前缀路由, source/key 错配 fail-closed。 - 安全边界: 新增 xDeepLink.ts —— X OAuth2 (PKCE) 授权 URL 的精确校验 (host/path/参数键集/S256, 拒凭证/端口/fragment/参数走私), 与 t.me 校验彼此独立不复用; profile URL 仅接受裸 handle。 - store: xEnabled / xBindingCache 账号分区槽位, setProviderBindingCache 泛化; workspaceProviderSourceStore 的 persist 白名单补 'x'(新用例先行 抓到该洞: 缺了会让 x 的目录级来源偏好静默失效)。 - 绑定 UI: 设置页 x 渠道卡与 Telegram 卡同构渲染(renderProviderCard, 动作按钮完全由 binding.actions 数据驱动, X 无加群按钮自然不渲染); HookWorkspacePrefsEditor 泛化为 neutral provider 语义(x prefs 按 bindingId 归属)。端点清单刻意不加 xHookWsUrl 条目 —— 缺失解析为空串 即卡不可见, 这就是灰度/回滚开关, 部署后改 CDN 正本即可。 - 会话链路: sessions.source 支持 'x'(SQLite 无 CHECK 约束, 零 migration) 并进侧边栏可见集; X 纯文本回帖的 prompt note; vendorOptions.source='x' 与 lizi-mcps NOTE_BY_SOURCE 的隐式契约两侧同补; 任务卡 X 图标与标签; mobile 渲染与 normalize 三处补 x。 - i18n: 四语言 hook.x.* 整块 + providerX + chat 描述; Telegram 的 openTelegram key 更名 openApp(与 x 块结构平行)。 验证: desktop typecheck 0 错; 全量 test:unit 除 3 个基线预存失败 (makerSendToSessionOrdering, 于干净 main 基线同样复现, 与本 diff 无关, 其抽取的源文件本 PR 未触碰)外全绿; hook-control 345 / 设置组件 122 / mobile 33 / clientEndpoints 69 全过; check:i18n-glossary 通过。 双模式复用既有 themed 组件与语义 token, 未实机目检(如实登记)。 Signed-off-by: Dash <dashhuang@gmail.com>
- ipc.ts getConnection: connectionId 末段解析 provider,未登记值返回 null(dispatcher 按连接不存在拒绝,fail closed),不再兜底回落 Slack - manager.ts: ProviderOpenAction 注释改用现名 openProviderAction - endpointManifestCache: xHookWsUrl 登记进 CROSS_REGION_ENDPOINT_KEYS (x-hook 与 slack/telegram 同款跨区单部署),CN 清单合规测试补 X 端点, 避免 CN 构建离线缓存回退把 X 端点判为不可信 Signed-off-by: Dash <dashhuang@gmail.com>
2d04e22 to
c8a3510
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 39 out of 40 changed files in this pull request and generated no new comments.
Suppressed comments (2)
apps/desktop/src/renderer/components/settings/tests/HookConnectionsSection.test.tsx:115
- 这里新增了 X provider 字段与 provider-neutral 渲染分支,但本文件的用例仍只覆盖 Slack/Telegram。建议补充至少一条 X 卡用例,验证:当 x.url 已配置且 enabled/available 时卡可见;pending/confirmed 状态下按钮按 actions 渲染;点击“openApp/openBot”等动作会调用 openProviderAction('x', );且 X 不应渲染 add-to-group 按钮(除非 actions 明确包含)。否则 X 卡逻辑回归很难被现有测试捕捉。
x: {
enabled: false,
url: '',
status: 'disabled',
lastError: null,
apps/desktop/src/renderer/components/settings/tests/HookWorkspacePrefsEditor.test.tsx:45
- useHookWorkspacePrefs 已泛化为 neutral provider(Telegram / X)并通过 IPC 传 provider 参数、用
${provider}:${bindingId}做 ready identity,但本测试集目前只覆盖 Telegram 的 provider prefs 流。建议补充 X 的镜像用例(例如 X 已连接且 binding confirmed 时应调用 getProviderWorkspacePrefs('x'),且仅接受 provider==='x' 且 bindingId 匹配的推送/响应),避免后续改动把 X 误路由到 Telegram 或 Slack prefs 线。
x: {
enabled: false,
url: '',
status: 'disabled',
lastError: null,
available: false,
capabilityPending: false,
binding: null,
},
Copilot review 建议:X 卡此前只有 fixture 覆盖,无行为用例。补上:
- 端点清单缺 xHookWsUrl 时 X 卡整体不渲染(灰度语义)
- 绑定 pending 时 OAuth 深链动作可见,openApp 调 openProviderAction('x','connect')
- confirmed 动作按 wire actions 数据驱动,X 不渲染 add_to_group
- prefs hook 按 provider='x' 拉取,跨 provider / 跨 bindingId 推送不落进 X 线
Signed-off-by: Dash <dashhuang@gmail.com>
|
Copilot 复审中两条被抑制的测试覆盖建议已在 |
MagicLizi
left a comment
There was a problem hiding this comment.
Heavy review 通过。navigation-escape 的唯一出口 openXUrl 经过 validateXExternalUrl 严格白名单(HTTPS-only、x.com 精确匹配、参数集固定、S256 PKCE、16 条否决测试);schema 扩枚举无需 migration;preload 只做 type widening 不暴露新 IPC;所有路由 fail-closed。安全软命中(测试里的 user:pw@x.com)确认为否决用例桩。无 P0/P1。
|
合了,X hook provider 接线完成。validateXExternalUrl 那套 16 条否决用例很扎实,fail-closed 的路由和 PKCE 强制都在位——以后接 provider 照着这个模子来就对了。 |
这次改了什么
摘要
X (Twitter) 机器人链路的第 4 步:desktop 客户端接线
xhook provider。用户在设置页「IM 机器人 · 官方」栏可开启 X 渠道、完成 X 账号 OAuth 绑定,之后在 X 上 @提及 Cindy bot 即可向本机派发任务(服务端 x-hook-server 已随 xindong/cindy-server#211 合入,产品调研与实施顺序见 #691)。X 走与 Telegram 相同的 provider-neutral 状态机(#695 泛化重构的表驱动 lane),本 PR 的主体是「追加一行 lane 配置 + 打通若干 Telegram 写死的洞」,并顺手把这些洞参数化,第三个 provider 不用再踩一遍。要点:
2520a40(含'x'provider 常量与provider:x能力旗标)。服务端 x-hook-server 已消费同源常量;指针相对 main 此前的a9a34e6多出的 turn.reopen 帧走能力协商,desktop 不宣告即不参与,无兼容影响。main 侧同学在a9a34e6bump 时预留的「X 落地时放宽ClientHookProvider」口子,本 PR 按其预期落位。xConfiglane;providerForTaskDispatch/providerForExternalKey增加x:前缀路由,source/key 错配 fail-closed;参数化三处 Telegram 写死点——openProviderAction(provider, action)、六个 provider-neutral IPC handler 从 payload 取 provider、connectionId后缀解析不再二分兜底(未登记 provider 不得读到 Slack 的开关与端点)。xDeepLink.ts——X OAuth2 (PKCE) 授权 URL 的精确校验(https + 精确 host/path + 参数键集恰好匹配 + S256,拒凭证/端口/fragment/重复与未知参数),与 t.me 校验彼此独立不复用;bot profile URL 仅接受裸 handle。xEnabled/xBindingCache账号分区槽位;setProviderBindingCache泛化;workspaceProviderSourceStore的 persist 白名单补'x'(新增用例先行抓到:缺了会让 x 的目录级模型来源偏好静默失效)。renderProviderCard,动作按钮完全由binding.actions数据驱动——X 无加群概念,add_to_group不下发即不渲染);HookWorkspacePrefsEditor泛化为 neutral provider 语义(x prefs 按 bindingId 归属)。sessions.source支持'x'(SQLite 该列无 CHECK 约束,drizzle enum 仅类型层,零 migration)并进侧边栏可见集;X 纯文本回帖的 prompt note;vendorOptions.source='x'与 lizi-mcpsNOTE_BY_SOURCE的隐式契约两侧同补;任务卡 X 图标与标签;mobile 渲染/normalize 三处补 x。clientEndpoints新增xHookWsUrl(wss:白名单,纯增可选字段不 bump schemaVersion);仓内config/endpoint*.json刻意不加条目——清单缺失该 key 解析为空串,设置卡即不可见,这就是灰度/回滚开关,x-hook-server 部署后改 CDN 正本即可放量。hook.x.*整块 +providerX显示名 + chat 描述补 X;Telegram 的openTelegramkey 更名openApp与 x 块结构平行。变更类型
feat新功能fix缺陷修复refactor/perf重构或性能优化docs/test/chore文档、测试或工程维护范围
xHookWsUrl前无任何可见变化(x 卡隐藏);放量后设置页出现 X 渠道卡,绑定后 X 会话进侧边栏、任务卡带 X 图标UI 变化
设置页「IM 机器人 · 官方」栏新增 X 渠道卡,与 Telegram 卡同构(收起行 = 状态徽章 + 绑定摘要 + 开关;展开区 = 授权动作 + 工作目录映射);聊天任务卡新增 X 图标(
XIcon,currentColor随主题)。因端点未放量,当前构建不可见,未附截图;实机截图待部署后在放量 PR 里补。--settings-badge-*,文本--text-secondary/tertiary、错误--error-fg,无硬编码色,Light/Dark 双模式同源实现);§4 Component Stylings · Cards & Containers(复用ImChannelSettingsCard手风琴卡与既有 pill 按钮样式,未新增容器样式);§7 Do's and Don'ts(不引入新交互模式,动作按钮按服务端下发的binding.actions数据驱动)。双模式为同一 themed 实现,未实机目检(端点未放量,卡在当前环境不可见),按 DESIGN.md 双模式交付门槛如实登记。怎么验证的
自动验证
新增测试:
xDeepLink.test.ts(授权 URL 16 条负向用例 + profile URL);x 路由 source/key 错配 fail-closed;workspaceProviderSourceStorex 渠道隔离(先行抓到 persist 白名单洞);outboundX 平台名与纯文本格式约束;lizi-mcps x 渠道 note 契约;store x 槽位与账号分区;设置组件与 prefs fixture 补 x。手工验证
不涉及:x-hook-server 尚未部署、端点未放量,本机无法端到端连通;UI 卡在端点为空时按设计隐藏(该隐藏行为由
HookConnectionsSection可见性 gate 的既有测试覆盖)。未执行的验证
风险
风险分类
影响与回滚
xHookWsUrl时零行为变化(x lane 端点为空不建连、卡不可见);协议指针 bump 相对 main 仅前进一个 docs+帧定义 commit(turn.reopen),desktop 不宣告该能力,对现有 Slack/Telegram 连接无影响。sessions.source枚举为纯增(无 SQL 约束),slack-hook.json配置为纯增字段,旧版本读到多余字段自动忽略。xHookWsUrl即整体下线 x 渠道(卡隐藏、不建连),无需发版;代码回滚 revert 本 PR 即可,无持久化残留需要清理(xEnabled/xBindingCache字段旧版本解析时按未知字段忽略)。openXUrl在shell.openExternal前经validateXExternalUrl精确校验(host/path/参数集),与 Telegram 的 t.me 校验同级但独立,负向用例覆盖凭证/端口/fragment/参数走私。提交前检查
git commit -s,见 DCO)