25 KiB
设置页 → 远程连接 — Channels 渠道接入 (F-CHAN)
本文档覆盖「设置 → 远程连接」页面 Channels Tab 的全部功能,包括渠道卡片总览、各 IM 渠道配置(Telegram/Lark/DingTalk/WeChat/WeCom)、Agent/模型选择、配对授权通用流程、扩展渠道支持。 基于静态代码分析和动态 UI 验证综合整理。
WebUI Tab 相关内容见 webui.md。
(F-WEBUI-12) Channels 页面总览与渠道卡片结构 [已实现]
用户故事:作为用户,我希望在"远程连接"设置页面中浏览所有支持的 IM 渠道,了解每个渠道的状态,并展开配置面板。
正常流程(用户视角):
- 用户进入「设置 → 远程连接」页面,切换到 "Channels" Tab(桌面端);或直接看到 Channels 内容(非桌面端/WebUI 浏览器访问)
- 页面顶部显示标题"Channels"、功能描述文本("Connect Telegram, Lark, and DingTalk to interact with AionUi from IM apps."),以及 2 步引导提示
- 下方为渠道卡片列表(Arco Design Collapse),每个渠道一张可折叠卡片
- 卡片 header 包含:渠道图标/logo、渠道名、描述、连接状态徽标(Connected/未连接)、Switch 启用开关
- 默认所有卡片折叠(
collapseKeys默认true);点击卡片 header 展开详细配置面板 - Switch 开关可直接启用/禁用渠道,无需展开卡片
渠道列表与排序:
| 顺序 | 渠道 | pluginId | 状态 |
|---|---|---|---|
| 1 | Telegram | telegram_default |
active |
| 2 | Lark / Feishu | lark_default |
active |
| 3 | DingTalk | dingtalk_default |
active |
| 4 | weixin_default |
active | |
| 5 | WeCom | wecom_default |
active |
| 6+ | 扩展渠道 | 动态(extensionMeta) |
active |
| 末尾 | Slack | - | coming_soon |
| 末尾 | Discord | - | coming_soon |
说明:Slack/Discord 若已被扩展渠道实现(extensionTypeSet 包含 slack/discord),则隐藏对应 coming_soon 占位卡片。
异常情况:
channel.getPluginStatus.invoke()失败:仅记录日志,渠道状态保持初始值(未启用/未连接)channel.pluginStatusChanged.on()事件推送:实时更新对应渠道的状态(启用/连接/botUsername 等)
验收标准:
- 页面显示标题、描述和引导提示
- 所有渠道按指定顺序排列
- 卡片默认折叠,点击可展开/收起
- Switch 开关可在卡片 header 直接操作
- 渠道状态(Connected/未连接)实时更新
- 非桌面端直接显示 Channels 内容(无 Tab 切换)
- Slack/Discord 显示"coming soon"占位文案
- 已被扩展实现的 coming_soon 渠道自动隐藏
(F-WEBUI-13) Telegram 渠道配置 [已实现]
用户故事:作为用户,我希望通过输入 Telegram Bot Token 连接 Telegram,并管理通过 Telegram 与 AionUi 交互的授权用户。
前置条件:用户已在 Telegram @BotFather 创建 Bot 并获取 Token
正常流程(用户视角):
- 展开 Telegram 卡片,显示配置面板
- Bot Token 输入:Password 类型输入框(240px 宽),带可见性切换,placeholder
123456:ABC-DEF... - 点击"Test"按钮测试连接
- 测试成功:toast 提示"Connected! Bot: @xxx",自动启用插件(
enablePlugin),刷新状态 - 启用成功后 header 的 Switch 变为 ON,状态徽标变为 Connected
- 下方出现"Next Steps"引导卡片(4 步操作指引),引导用户在 Telegram 搜索 Bot 发送消息触发配对
- 用户在 Telegram 发送消息后,"Pending Pairing Requests"区域实时出现配对请求(通过
channel.pairingRequested.on事件推送) - 点击"Approve"批准配对,用户移入"Authorized Users"列表
- 授权后 Token 输入框和 Test 按钮变为 disabled(
tokenLockedtooltip:"请先关闭渠道并删除所有授权用户后才能修改配置")
异常情况:
- Token 为空点击 Test:warning toast "Please enter a bot token"
- 测试失败(Token 无效/网络错误):error toast 显示后端错误信息或"Connection failed"
- 启用无 Token(Switch 直接打开):warning toast "Please enter a bot token first"
- 批准配对失败:error toast
- 拒绝配对:info toast "Pairing rejected"
- 撤销用户失败:error toast
验收标准:
- Bot Token 输入框支持 Password 可见性切换
- Test 按钮测试成功后自动启用插件
- 启用后显示 4 步操作引导
- 实时接收并显示配对请求
- 可批准/拒绝配对请求
- 授权用户列表显示用户名、平台、授权时间
- 可撤销已授权用户
- 有授权用户时 Token 和 Test 按钮禁用
- 配对请求和授权用户列表支持手动刷新
(F-WEBUI-14) Lark / 飞书渠道配置 [已实现]
用户故事:作为用户,我希望通过配置飞书应用凭据连接 Lark/飞书,与 AionUi 进行 IM 对话。
前置条件:用户已在飞书开放平台创建应用并获取 App ID 和 App Secret
正常流程(用户视角):
- 展开 Lark 卡片,显示配置面板
- 必填字段:
- App ID(Input,必填,红色
*标记) - App Secret(Input.Password,必填,红色
*标记)
- App ID(Input,必填,红色
- 可选字段(默认折叠,点击"Show optional fields"展开):
- Encrypt Key(Input.Password)
- Verification Token(Input.Password)
- 右下角有蓝色链接"飞书开放平台文档",打开
https://open.feishu.cn/document/develop-an-echo-bot/introduction - 点击"Test & Connect"按钮测试连接
- 测试成功:toast "Connected to Lark API!",自动启用插件(传入 appId + appSecret + encryptKey? + verificationToken?)
- 配对/授权用户管理流程与 Telegram 相同(见 F-WEBUI-13)
异常情况:
- App ID 或 App Secret 为空点击 Test:warning toast "Please enter App ID and App Secret",空字段标红(
touched状态) - 凭据无效:error toast 显示后端错误信息
- 启用无凭据(Switch 直接打开):warning toast "Please configure Lark credentials first"
- 连接状态变化:
pluginStatusChanged事件实时更新(Connected/Error/Connecting 带颜色编码徽标)
验收标准:
- App ID 和 App Secret 为必填项,带红色
* - 可选字段默认折叠,可展开
- Test & Connect 测试成功后自动启用
- 飞书文档链接可正常打开
- 空字段提交时表单标红
- 配对/授权用户管理功能正常
- 连接状态实时更新
(F-WEBUI-15) DingTalk 渠道配置 [已实现]
用户故事:作为用户,我希望通过配置钉钉应用凭据连接 DingTalk,与 AionUi 进行 IM 对话。
前置条件:用户已在钉钉开放平台创建应用并获取 Client ID 和 Client Secret
正常流程(用户视角):
- 展开 DingTalk 卡片,显示配置面板
- 必填字段:
- Client ID(Input,必填,红色
*标记) - Client Secret(Input.Password,必填,红色
*标记)
- Client ID(Input,必填,红色
- 右下角有蓝色链接"DingTalk setup guide",打开 GitHub Wiki 配置指南
- 点击"Test & Connect"按钮测试连接
- 测试成功后自动启用插件
- 配对/授权用户管理流程与 Telegram 相同(见 F-WEBUI-13)
异常情况:
- 凭据为空:warning toast "Please configure DingTalk credentials first"
- 测试/启用失败:error toast 显示错误信息
验收标准:
- Client ID 和 Client Secret 为必填项
- Test & Connect 测试成功后自动启用
- GitHub Wiki 链接可正常打开
- 配对/授权用户管理功能正常
(F-WEBUI-16) WeChat 渠道配置(扫码登录) [已实现]
用户故事:作为用户,我希望通过微信扫码登录连接 WeChat 渠道,无需手动输入复杂凭据。
正常流程(用户视角):
- 展开 WeChat 卡片,显示配置面板
- 初始状态(idle):显示"Scan to Login"按钮
- 用户点击按钮,进入二维码加载状态(
loading_qr) - Electron 桌面端:
- 调用
window.electronAPI.weixinLoginStart()启动登录 - 通过
weixinLoginOnQR事件接收 QR 码(base64 data URL) - 显示
<img>标签渲染 QR 码(160x160px)
- 调用
- WebUI 浏览器端:
- 通过
EventSource('/api/channel/weixin/login')SSE 接收事件 - 通过
qr事件接收 QR 码数据(raw ticket string) - 使用
QRCodeSVG组件渲染 QR 码(160x160px)
- 通过
- 用户用微信扫描二维码
- 扫描后状态变为
scanned:显示 Spin loading + "Scanned, waiting for confirmation..." - 确认后自动调用
enableWeixinPlugin(accountId, botToken)启用渠道 - 状态变为
connected:显示绿色对钩 + "Connected" + "Disconnect"按钮 - 配对/授权用户管理流程与 Telegram 相同(见 F-WEBUI-13)
登录状态机:
idle → loading_qr → showing_qr → scanned → connected
↑ ↓ ↓ ↓
←─── (error/expired) ←───┴──────────┘
异常情况:
- QR 码过期/too many 尝试:warning toast "QR code expired, please try again",回到 idle
- EventSource error 事件(WebUI):error toast "WeChat login failed",回到 idle
- Electron IPC 错误(非 Aborted):error toast "WeChat login failed"
- 用户取消(Aborted):静默回到 idle,不显示错误
- enablePlugin 失败:error toast,回到 idle
- Disconnect 失败:error toast
验收标准:
- Electron 端通过 IPC 接收 QR 码(base64 img)
- WebUI 端通过 EventSource SSE 接收 QR 码(QRCodeSVG 渲染)
- 扫码后显示"等待确认"loading 状态
- 登录成功后显示 Connected 状态 + Disconnect 按钮
- QR 码过期有明确提示,可重新发起
- 断开连接(Disconnect)可正常禁用渠道
- 组件卸载时 EventSource 正确关闭(无连接泄漏)
(F-WEBUI-17) WeCom 渠道配置(WebSocket 长连接) [已实现]
用户故事:作为用户,我希望通过配置企业微信机器人 ID 和密钥连接 WeCom 渠道,使用 WebSocket 长连接模式无需回调 URL。
前置条件:用户已在企业微信管理后台创建应用并获取 Bot ID 和 Secret
正常流程(用户视角):
- 展开 WeCom 卡片,显示配置面板
- 顶部橙色提示横幅说明:WeCom 使用 WebSocket 长连接模式,无需配置回调 URL
- 必填字段:
- Bot ID(Input,必填,红色
*标记,有授权用户时 disabled) - Secret(Input.Password,必填,红色
*标记,有授权用户时 disabled)
- Bot ID(Input,必填,红色
- 右下角有蓝色链接"企业微信开发文档",打开
https://developer.work.weixin.qq.com/document/path/101463 - 点击"Save & Enable"按钮(区别于其他渠道的 "Test & Connect")
- 保存并启用成功:toast "WeCom channel enabled",刷新状态
- 配对/授权用户管理流程与 Telegram 相同(见 F-WEBUI-13)
异常情况:
- Bot ID 或 Secret 为空:warning toast "Please enter Bot ID and Secret",字段标红
- 启用无凭据(Switch 直接打开):warning toast "Please save Token and EncodingAESKey first"
- 保存/启用失败:error toast 显示错误信息
验收标准:
- 顶部显示 WebSocket 长连接模式说明横幅
- Bot ID 和 Secret 为必填项
- Save & Enable 按钮一步保存并启用
- 企业微信文档链接可正常打开
- 有授权用户时凭据输入框禁用
- 配对/授权用户管理功能正常
(F-WEBUI-18) 渠道 Agent 选择 [已实现]
用户故事:作为用户,我希望为每个 IM 渠道独立选择使用的 Agent,以便不同渠道使用不同的 AI 后端。
正常流程(用户视角):
- 每个渠道配置面板内有"Agent"选择行
- 点击 Dropdown 按钮展开 Agent 列表(来自
acpConversation.getAvailableAgents.invoke(),过滤掉 preset Agent) - 默认选中"Gemini CLI"(
{ backend: 'gemini' }) - 选择新 Agent 后:
- 本地状态更新
ConfigStorage.set('assistant.{platform}.agent', agent)持久化channel.syncChannelSettings.invoke({ platform, agent })同步到后端- toast "Agent switched successfully"
- 页面重新打开时从 ConfigStorage 恢复之前的选择
异常情况:
- Agent 列表加载失败:默认显示 "Gemini CLI" 作为唯一选项
- 持久化失败:error toast "Failed to save"
- syncChannelSettings 失败:仅 console.warn,不影响本地选择
验收标准:
- 每个渠道有独立的 Agent Dropdown
- Agent 列表从后端加载,过滤 preset
- 切换 Agent 有成功 toast
- 选择持久化,重新打开页面恢复
- Agent 列表为空时显示 "Gemini CLI" 兜底
(F-WEBUI-19) 渠道默认模型选择 [已实现]
用户故事:作为用户,我希望为每个 IM 渠道独立设置默认使用的 AI 模型,以便控制不同渠道的回复质量和速度。
前置条件:当前 Agent 为 Gemini 兼容类型(backend === 'gemini' 或 'aionrs')
正常流程(用户视角):
- 每个渠道配置面板内有"Default Model"选择行
- 使用
GeminiModelSelector组件渲染模型下拉选择 - 选择模型后:
ConfigStorage.set('assistant.{platform}.defaultModel', { id, useModel })持久化channel.syncChannelSettings.invoke({ platform, agent, model })同步到后端- toast "Model switched successfully"
- 页面重新打开时自动恢复保存的模型(
useChannelModelSelectionhook,最多重试 5 次匹配 provider)
条件渲染:
- Agent 为 Gemini/aionrs 类型:正常显示模型选择器
- Agent 为其他类型(如 Custom Agent):显示灰色文案"Automatically follow the model when CLI is running",选择器禁用
异常情况:
- 保存的 Provider ID 已失效(provider 被删除):重试最多 5 次后放弃,使用默认模型
- 模型保存失败:error toast "Failed to save model"
验收标准:
- 每个渠道有独立的模型选择
- 仅 Gemini 兼容 Agent 显示模型选择器
- 非 Gemini Agent 显示禁用状态 + 提示文案
- 模型选择持久化,重新打开页面恢复
- Provider 失效时有重试机制(最多 5 次)
(F-WEBUI-20) 渠道配对与用户授权(通用流程) [已实现]
用户故事:作为用户,我希望通过配对码机制控制哪些 IM 用户可以与我的 AionUi 交互,保证安全性。
适用渠道:Telegram、Lark、DingTalk、WeChat、WeCom(全部 5 个已实现渠道共享此流程)
正常流程(用户视角):
- 渠道启用且已连接后,IM 用户向 Bot 发送消息
- 首次消息触发配对请求,AionUi 通过
channel.pairingRequested.on()实时推送到设置页面 - "Pending Pairing Requests"区域显示配对卡片:
- 用户显示名(
displayName,未知则显示"Unknown User") - 配对码(
code)+ 复制按钮 - 剩余时间(
expiresAt,单位 min) - "Approve"按钮(主色调)+ "Reject"按钮(红色危险态)
- 用户显示名(
- 点击 Approve:
channel.approvePairing.invoke({ code })→ 成功 toast → 用户移入"Authorized Users" channel.userAuthorized.on()实时推送,自动从 pending 列表移除并添加到 authorized 列表- "Authorized Users"区域显示:
- 用户显示名
- 平台类型 + 授权时间
- "Revoke access"删除按钮(红色危险态)
- 点击 Revoke:
channel.revokeUser.invoke({ userId })→ 成功 toast → 用户从列表移除
凭据锁定机制:当 authorizedUsers.length > 0 时,凭据输入框和测试按钮变为 disabled,tooltip 提示"请先关闭渠道并删除所有授权用户后才能修改配置"。此规则适用于 Telegram(Token)、Lark(AppID/Secret)、DingTalk(ClientID/Secret)、WeCom(BotID/Secret)。
异常情况:
- 加载配对/授权列表失败:仅日志,空列表
- 批准配对失败:error toast 显示后端错误
- 拒绝配对失败:error toast
- 撤销用户失败:error toast
- 实时事件去重:已存在相同 code/id 的不重复添加
验收标准:
- 配对请求实时显示(无需手动刷新)
- 可批准/拒绝配对请求
- 批准后用户实时出现在授权列表
- 可撤销已授权用户
- 有授权用户时凭据/测试按钮禁用 + tooltip 提示
- 配对请求显示剩余时间
- 配对请求/授权列表均有手动刷新按钮
- 空列表显示 Empty 占位组件
(F-WEBUI-21) 扩展渠道支持 [已实现]
用户故事:作为扩展开发者,我希望通过扩展 manifest 注册自定义 IM 渠道,使用动态表单配置凭据。
正常流程(用户视角/开发者视角):
- 扩展通过
extensionMeta声明credentialFields和configFields,定义表单字段 - 页面加载时,扩展渠道从
getPluginStatus返回的非内置类型中识别 - 扩展渠道卡片在 5 个内置渠道之后、coming_soon 渠道之前显示
- 卡片 header 显示蓝色 "ext" 徽标(
isExtension: true) - 展开后根据
extensionMeta的字段 schema 动态渲染表单
支持的字段类型:
| 类型 | 渲染组件 | 说明 |
|---|---|---|
text |
Input |
普通文本输入 |
password |
Input[type=password] |
密码输入 |
select |
Select |
下拉选择,options 来自 schema |
number |
InputNumber |
数值输入 |
boolean |
Switch |
开关切换 |
启用前校验:遍历 credentialFields 中 required: true 的字段,若有未填写的(空字符串或 undefined),warning toast 提示"Please fill required field: {fieldLabel}"
特殊处理 — ext-wecom-bot 扩展:当扩展类型为 ext-wecom-bot 时,显示橙色提示横幅说明回调 URL(本机/局域网/公网),包含 WebUI 状态(localUrl/networkUrl)。
异常情况:
- 扩展无配置字段:显示
extensionMeta.description或 "No extra configuration required." - 启用缺少必填字段:warning toast,不提交
- 启用/禁用失败:error toast
验收标准:
- 扩展渠道在内置渠道之后显示
- 卡片显示蓝色 "ext" 徽标
- 5 种字段类型正确渲染
- 必填字段校验生效
- 字段默认值从
field.default初始化 - ext-wecom-bot 显示回调 URL 信息横幅
附录 A:IPC 通信链路(Channels Tab)
┌──────────────────────────────────────────────────────────────────────┐
│ ChannelModalContent.tsx (Channels Tab): │
│ 渠道状态: │
│ ├─ channel.getPluginStatus.invoke() [加载全部渠道状态] │
│ └─ channel.pluginStatusChanged.on() [实时监听状态变更] │
│ 渠道启停: │
│ ├─ channel.enablePlugin.invoke({ pluginId, config }) [启用] │
│ ├─ channel.disablePlugin.invoke({ pluginId }) [禁用] │
│ └─ channel.testPlugin.invoke({ pluginId, token, extraConfig? }) │
│ 配对授权: │
│ ├─ channel.getPendingPairings.invoke() [加载待配对] │
│ ├─ channel.getAuthorizedUsers.invoke() [加载已授权] │
│ ├─ channel.approvePairing.invoke({ code }) [批准] │
│ ├─ channel.rejectPairing.invoke({ code }) [拒绝] │
│ ├─ channel.revokeUser.invoke({ userId }) [撤销] │
│ ├─ channel.pairingRequested.on() [实时配对请求] │
│ └─ channel.userAuthorized.on() [实时授权通知] │
│ Agent/模型: │
│ ├─ acpConversation.getAvailableAgents.invoke() [加载 Agent 列表] │
│ ├─ channel.syncChannelSettings.invoke({ platform, agent, model }) │
│ └─ ConfigStorage.get/set('assistant.{platform}.agent/defaultModel')│
│ WeChat 登录: │
│ ├─ electronAPI.weixinLoginStart() [Electron IPC] │
│ ├─ electronAPI.weixinLoginOnQR/OnScanned/OnDone [IPC 事件] │
│ └─ EventSource('/api/channel/weixin/login') [WebUI SSE] │
└──────────────────────────────────────────────────────────────────────┘
附录 B:Toast 通知汇总
| 触发操作 | Toast 类型 | i18n Key |
|---|---|---|
| 渠道插件启用成功 | success | settings.assistant.pluginEnabled / settings.{platform}.pluginEnabled |
| 渠道插件禁用成功 | success | settings.assistant.pluginDisabled / settings.{platform}.pluginDisabled |
| 渠道插件启用/禁用失败 | error | settings.assistant.enableFailed / settings.{platform}.enableFailed |
| 连接测试成功 | success | settings.assistant.connectionSuccess / settings.lark.connectionSuccess |
| 连接测试失败 | error | settings.assistant.connectionFailed / 后端错误信息 |
| Token 为空测试 | warning | settings.assistant.tokenRequired |
| 凭据为空(Lark/DingTalk/WeCom) | warning | settings.{platform}.credentialsRequired |
| 配对批准成功 | success | settings.assistant.pairingApproved |
| 配对拒绝 | info | settings.assistant.pairingRejected |
| 用户撤销成功 | success | settings.assistant.userRevoked |
| Agent 切换成功 | success | settings.assistant.agentSwitched |
| 模型切换成功 | success | settings.assistant.modelSwitched |
| 模型保存失败 | error | settings.assistant.modelSaveFailed |
| WeChat 登录过期 | warning | settings.weixin.loginExpired |
| WeChat 登录失败 | error | settings.weixin.loginError |
| WeCom 保存启用成功 | success | settings.wecom.pluginEnabled |
附录 C:已知局限汇总
| # | 功能点 | 局限描述 | 来源 |
|---|---|---|---|
| 1 | F-WEBUI-13 | Telegram Token 锁定机制依赖 authorizedUsers.length > 0,如果后端授权数据丢失但 Token 实际在用中,用户可能误修改 Token 导致断连 |
R2 初稿 |
| 2 | F-WEBUI-16 | WeChat 扫码登录的 EventSource(WebUI 模式)在组件卸载时关闭,但 Electron IPC 模式中 weixinLoginStart 是 await Promise,组件卸载不会取消该 Promise |
R2 初稿 |
| 3 | F-WEBUI-18 | Agent 列表通过 acpConversation.getAvailableAgents.invoke() 加载,每个渠道独立调用,无缓存共享(5 个渠道 = 5 次相同 IPC 调用) |
R2 初稿 |
| 4 | F-WEBUI-19 | useChannelModelSelection 恢复保存的 provider 时最多重试 5 次,provider 被删除后静默降级为默认模型,无用户提示 |
R2 初稿 |
| 5 | F-WEBUI-20 | 配对请求的 expiresAt 由后端生成,UI 显示剩余时间为 Math.ceil((expiresAt - Date.now()) / 60000),无自动倒计时刷新(显示值仅在组件渲染时计算) |
R2 初稿 |
| 6 | F-WEBUI-17 | WeCom "Save & Enable" 按钮 warning 提示 "Please save Token and EncodingAESKey first" 与实际字段名 (Bot ID / Secret) 不一致 | R2 初稿 |
| 7 | F-WEBUI-21 | 扩展渠道字段值存储在组件 state(extensionFieldValues),页面离开后丢失;已启用的扩展字段值不从后端恢复显示 |
R2 初稿 |