远程连接
Spark Work 远程连接(Telegram / 飞书):连接配置、配对流程、本地 webhook(127.0.0.1:32178)、内置命令(/help /sessions /models 等)、启动项集成。
远程连接让你从手机或另一台设备继续与本地 SparkWork 里的 Agent 对话。当前真实支持 Telegram、飞书、QQ、微信 ClawBot 四个通道:Telegram 与微信走本机 HTTP 长轮询, 飞书与 QQ 走官方 WebSocket 长连接,均不需要公网地址;同时保留一个本机 HTTP webhook 服务,用于本地调试与自建网关。微信使用扫码授权,通过 iLink 收发文字消息;旧版 wechat-claw 自建网关协议仍保留兼容。所有配置存在 app_settings 的 remote-connections/data 里, 配对、隔离与转发逻辑在 apps/desktop/src/main/services/RemoteConnectionService.ts。
1. 支持的通道
| 通道 | 必填凭据 | 连接方式 | 搭建入口 |
|---|---|---|---|
telegram Telegram | botToken | 本机 getUpdates 长轮询(不需要公网 webhook) | https://t.me/BotFather |
feishu 飞书机器人 | appId、appSecret | @larksuiteoapi/node-sdk 官方 WebSocket 长连接 | https://open.feishu.cn/page/openclaw?form=multiAgent |
qq QQ 机器人 | qqBotAppId、qqBotSecret | 官方 WebSocket 网关(api.sgroup.qq.com / bots.qq.com) | https://q.qq.com/#/app/bot |
wechat 微信 ClawBot | 扫码授权后由 SparkWork 自动保存机器人凭据 | iLink HTTP 长轮询与文字消息回复(回复使用微信上下文令牌) | 设置 → 远程连接 → 微信机器人 → 扫码授权 |
wechat-claw 微信 Claw | clawEndpoint、clawAccessToken | 自建网关协议:本地 webhook 收消息,出站调 <endpoint>/send(失败回退 /message) | 旧版自建网关连接;新连接请使用 wechat 微信 ClawBot |
可以同时配置并启用多个连接(每个 Telegram 连接是独立的 botToken 与独立轮询); 界面可新建 Telegram、飞书、QQ、微信四种通道。微信连接通过扫码创建,不需要手动填写凭据。 此实现对接腾讯开源 ClawBot 通道插件使用的 iLink HTTP 协议,不依赖安装 OpenClaw;该协议文档注明 它来自客户端实现分析,不能视为完整的服务端公开契约。当前微信通道支持文字消息,暂不支持图片、语音、视频与文件。
2. 配置模型:字段、默认值与存储位置
存储位置是 app_settings 的 remote-connections 分类、key 为 data,值形如 {global, connections}。
global(全局运行设置),括号内是代码里的默认值:
enabled(true):总开关,关闭时运行时会整体停止。requirePairing(true):未配对用户的消息不会被处理。allowQrPairing(true):是否允许生成二维码配对负载。pairingTtlMinutes(10):配对码有效期。localWebhookPort(32178):本机 HTTP 服务端口。publicBaseUrl(未设置):需要外部回调时使用的基础地址。
connection(每条连接):
| 字段 | 说明 |
|---|---|
id / channel / name / enabled | 身份与开关;id 形如 remote-xxx |
status | disabled / draft / pending-pairing / connected / error(界面文案:已停用 / 草稿 / 等待配对 / 已连接 / 错误) |
credentials | 按通道取不同字段(见上一节的必填项) |
commandPrefix | 命令前缀,默认 /(Telegram 会同步为 bot command) |
allowedUserIds / allowedChatIds | 允许名单(最多各 200 项) |
routeBindings | 按「外部聊天」各自保存的默认会话/项目/Provider/模型/Agent/权限模式/推理强度 |
defaultSessionId / defaultWorkspaceId / defaultProviderProfileId / defaultModelId / defaultAgentId / defaultPermissionMode / defaultReasoningEffort | 连接级默认值;有 routeBinding 时以聊天级为准 |
allowSharedSession | 是否允许与其他远程连接共享同一会话(默认关闭) |
telegramCommands / qqCommands | 要同步到 Telegram 命令菜单 / QQ 指令面板的命令名清单(默认 14 条,最多 80 条) |
capabilities | 能力开关,见下表 |
pairing / pairedDevices | 当前配对码与已配对设备(remoteUserId、displayName、channelThreadId、pairedAt、lastSeenAt) |
lastConnectedAt / lastError | 最近连接时间与最近错误(「测试配置」会写 lastError) |
能力开关(capabilities)的界面标签与默认值:
| 字段 | 界面标签 | 默认 |
|---|---|---|
sendMessages | 发送消息到会话 | true |
switchModel | 切换模型 / Provider | true |
switchSession | 切换会话 | true |
switchAgent | 切换 Agent | true |
manageWorkspace | 查看工作区 | true |
runCommands | 运行内置命令 | true |
approvePermissions | 允许在原始远程聊天中用审批码执行 /approve(仅本次)或 /deny | true |
observeDesktop | 观察桌面 | true |
controlDesktop | 控制桌面 | false |
useInternalBrowser | 使用内置浏览器窗口 | false |
transferFiles | 传输文件 | false |
manageRuntime | 管理运行时 | true |
dangerousActions | 高危动作确认 | false |
安全提醒:远程连接的凭据(bot token / AppSecret 等)以明文 JSON存在 app_settings 里,不在系统的 Keychain/vault 中;界面用的是普通文本输入框,已保存的值会明文显示,不做掩码。 共享这台机器数据库的人可以看到这些凭据。
3. 本地运行时与两个 HTTP 端点
- 主进程注册远程连接 IPC 时会启动运行时:先跑
startRuntime(要求global.enabled为真),再按每条启用连接启动对应通道的接收端 (Telegram / 微信轮询 / 飞书与 QQ 长连接)。 - 本机 HTTP 服务监听
127.0.0.1:<localWebhookPort>,默认 32178; 如果端口被占用(EADDRINUSE),会退化为让操作系统分配随机端口, 真实端口通过运行态接口回传并在设置页顶部显示。 GET /remote/health:返回{ok: true, ...运行态}。POST /remote/webhook/:channel/:connectionId:找不到连接返回 404; 否则按渠道解析请求体(Telegram 支持callback_query与message; 飞书支持 URL 校验 challenge;QQ 走 dispatch 事件;其余按通用chatId+text结构解析)。- 运行态查询用
remote:runtime-status,返回running、port、localBaseUrl、polling[](Telegram)与longConnections[](飞书/QQ,带lastError)。 - IPC 全景:
remote:list/remote:save/remote:delete/remote:test/remote:create-bot-draft/remote:generate-pairing/remote:confirm-pairing/remote:command-catalog/remote:execute-command/remote:runtime-status;配置变化会通过stream:remote:changed推给界面。
4. 配对:6 位数字码与 QR 负载
- 在设置页填好凭据并保存(保存后状态通常是「草稿」或「等待配对」)。
- 点「生成配对码」或「生成二维码配对」:调用
remote:generate-pairing, 服务端生成 6 位数字码(randomInt(100000, 999999)),有效期由pairingTtlMinutes决定(默认 10 分钟),并把连接状态置为pending-pairing、自动启用该连接。 - QR 负载格式是
spark-agent://remote-pair?connectionId=..&channel=..&code=..&expiresAt=..; 界面上的「复制配对命令」复制的则是/bind <code>。 - 在外部聊天里发送
/bind <code>(正则接受 6~12 位大写字母数字,实际生成为 6 位数字)。 服务端校验码相等且未过期,然后把该聊天写入pairedDevices、清掉pairing、 状态置为connected,并在同通道回复结果。 - 也可以走
remote:confirm-pairing手动确认(设置页的配对区支持手工填入远程用户 ID/名称), 适合自建网关等拿不到入站消息的场景。 - 重新生成配对码会让旧码立即失效;
requirePairing为真时,未配对聊天发来的消息会被直接忽略。
5. 内置命令清单(按能力分组)
命令目录来自服务端的 COMMAND_CATALOG,可通过 remote:command-catalog 查询;每条命令都绑定一个能力开关,能力关闭时执行会返回被拒绝的说明。 前缀默认是 /,可用 commandPrefix 改。
| 用到的能力 | 命令 |
|---|---|
system | /start、/help、/status |
sendMessages | /send <message> |
switchSession | /sessions [all|idle|running|error] [页码]、/use-session <序号|名称|sessionId>、/new-session [序号|名称|workspaceId] |
switchModel | /models [页码]、/use-model <序号|名称|modelId>、/providers [页码]、/use-provider <序号|名称|providerProfileId>、/channels [页码]、/use-channel <序号|名称|渠道ID> |
switchAgent | /agents [页码]、/use-agent <序号|名称|agentId> |
manageWorkspace | /workspaces [页码]、/projects [页码]、/use-project <序号|名称|项目ID>、/open-workspace <path>、/add-project <path> |
manageRuntime | /reasoning、/use-reasoning <minimal|low|medium|high|xhigh|max>、/progress、/queue、/history、/cancel、/stop |
approvePermissions | /permissions、/use-permission <manual|auto|plan|full>、/approve <审批码>、/deny <审批码> |
observeDesktop | /screen、/windows |
controlDesktop(默认关闭) | /focus <序号|窗口标题>、/click <x> <y>、/type <text>、/hotkey <keys> |
dangerousActions(默认关闭) | /confirm <code> |
新连接默认同步 14 条命令到平台原生菜单:start、help、status、projects、sessions、new-session、channels、models、agents、reasoning、permissions、progress、queue、cancel。Telegram 走 setMyCommands(清单为空时改调 deleteMyCommands); QQ 注册为「指令面板」,单条面板名超过 14 字符会被跳过。
6. 入站消息:命令、普通消息与会话隔离
已配对聊天的消息按下面的顺序处理:
- 以命令前缀开头的消息 → 走内置命令目录;先检查该命令所需能力是否开启, 再执行会话/项目/模型/Agent/推理/权限等操作。Telegram 与 QQ 支持分页与按钮式选择, 点击按钮会原位更新消息(Telegram 用
editMessageText)而不是刷屏。 - 普通消息 → 包装成一次远程 turn 后提交给
SessionService。 包装内容是【本轮远程渠道:X】+ 渠道约束 + 文件能力说明(开启「传输文件」时要求用mcp__spark_files__present_files提交真实文件,未开启时明确禁止声称已发送文件); 会话与桌面端界面里只显示你的原话,不显示这段内置说明。 - 新聊天没有绑定会话 → 用
defaultWorkspaceId(或该聊天的 routeBinding) 在对应项目里新建会话;没有项目时创建「不使用项目」的会话, 并把这个绑定写回该聊天的 routeBinding。 - 新建会话的权限模式:优先用连接/聊天配置的
defaultPermissionMode, 否则按适配器取默认的自动审批档(Claudeclaude-auto、Codexcodex-auto-review、Sparkspark-auto)。 「完全访问」档仍需在连接里显式配置,并且高危操作仍受权限层保护。 - 配置了默认 Provider / 模型 / Agent / 推理强度时,这些值在发送时应用; 每条远程回复只会发回本轮来源渠道,历史消息里提到的其他渠道不会改变发送目标。
会话隔离的真实规则:
- 同一条机器人下的私聊与群聊是不同聊天(
connectionId+externalId组成唯一路由键),各自保存默认会话、项目、模型与选择状态,互不覆盖。 - 一个会话已被其他连接/聊天绑定时,新连接不能直接复用:
/use-session会返回 「会话已绑定到其他远程聊天」或「会话已被其他远程连接占用」。 - 只有所有相关连接都显式开启「跨连接共享会话」(
allowSharedSession)时, 才允许共享;/status会明示「会话隔离:独立 / 与 X、Y 显式共享」。 - 定时任务的结果只在「该会话恰好由一个已启用聊天拥有」时回传 (
resolveScheduledRemoteRoute:路由数不等于 1 就返回 null,不猜接收者)。
7. 平台运行时要点
Telegram
- 接收:
getUpdates长轮询,timeout=25,allowed_updates=["message","callback_query"];按 offset 递增确认。 - 收到已配对用户消息后优先给原消息加 👀 反应(
setMessageReaction); 群组禁用反应时静默跳过,不影响后续处理。 - 输入状态:长任务期间每 4 秒续期一次
sendChatAction(typing); 正文以「草稿」形式在同一条消息里增量编辑,编辑节流 1 秒,单条最长 3900 字符(超出保留尾部)。 - Markdown 会转换为 Telegram HTML 富文本,解析失败自动回退纯文本; 出站文本按 3900 字符分片发送。
- 图片:入站图片经
getFile下载到{userData}/attachments/remote/telegram/<日期>/后作为附件提交给会话; 出站先尝试sendPhoto(本地文件走 multipart 直传),被拒或失败时改用sendDocument,直传失败还会经临时存储中转。
飞书
- 用 App ID / App Secret 换
tenant_access_token(带缓存与过期时间)。 - 回复走
im/v1/messages,receive_id_type由会话 id 形态推导 (chat_id/open_id);消息体是 interactive 卡片,文本按 10000 字符分片。 - 卡片按钮通过
card.action.trigger事件处理;请在飞书应用后台订阅该事件和im.message.receive_v1。卡片动作事件中的聊天 ID 位于context.open_chat_id。 - 飞书原生机器人菜单在开放平台的「应用功能 → 机器人 → 自定义菜单」中配置;动作设为「发送消息」, 命令会由 SparkWork 解析。连接设置不会自动覆盖飞书应用后台已有的菜单。
- 收到配对消息后给源消息加
Typing表情反应。 - 图片:入站从消息资源接口下载并提交会话;出站先上传到
im/v1/images, 再用image_key发送原生图片消息。
- 鉴权:
POST https://bots.qq.com/app/getAppAccessToken取access_token, 后续请求头是Authorization: QQBot <token>。 - 长连接:先取网关地址再连 WebSocket,支持 Resume;当服务端拒绝订阅(
op:9或关闭码 4013/4014)时会逐级降低 intents 后重连。 - 消息 API:频道用
/channels/<id>/messages,群聊用/v2/groups/<id>/messages,单聊用/v2/users/<id>/messages; 文本msg_type: 0,按字节分片(频道 1900、群聊/单聊 1000),优先被动回复(引用msg_id+ 递增msg_seq), 超窗后退化为主动消息。 - 图片:入站从腾讯图片 CDN 下载;频道出站用 multipart 的
file_image字段, 单聊/群聊先上传拿到file_info再发msg_type: 7富媒体消息。 - 配对的 QQ 机器人会把
qqCommands同步为指令面板(超 14 字符的面板名跳过)。
工具触发权限审批时,已启用「远程审批权限」的连接会把审批码发回发起该轮任务的聊天。发送 /approve <审批码> 只批准这一次,发送 /deny <审批码> 拒绝;审批码绑定到原始连接和聊天, 其他聊天不能使用。审批超时或会话取消也会向该聊天返回结果。
Telegram、飞书与 QQ 三个渠道的入站图片都在配对鉴权之后下载,统一限制 20 MB,并按文件头(magic bytes)校验, 只接受 PNG / JPEG / WebP;不合法会报「仅支持 PNG、JPEG 或 WebP 图片」。 出站图片如果给的是网络 URL,会先做公开地址校验再下载。此能力需要连接里开启「传输文件」。
8. 设置 UI 结构
- 入口:设置 → 远程连接(左栏「生态」分组)。页面说明文案是 「通过 Telegram、飞书、QQ、微信从远程桌面或移动端与 SparkWork 通信」。
- 运行态条:左侧显示
localBaseUrl(未启动时显示「本地 webhook 服务未启动」), 下方提示「N 个渠道已启用,远程消息会进入各聊天独立绑定的会话」或「启用任一渠道后,远程消息才会被接收」; 右上角徽标显示「运行中 / 未运行」与「已连接 / 总数」。右侧有「刷新」。 - 平台入口卡:Telegram / 飞书 / QQ / 微信四个图标卡;微信入口创建草稿并显示授权二维码,其他平台入口会打开对应控制台。
- 连接列表卡:平台图标、名称、通道、状态标签、启用状态、已配对设备数、默认会话。
- 编辑模态:宽 980、高度
min(68dvh, 680px),左侧段导航固定五段——基础、凭证、授权、配对、命令;右侧可滚动内容,底部固定操作栏:删除、取消、测试配置、保存连接(未保存过的新连接不能测试/生成配对)。 - 窄窗口下自动切单列布局。
注意:远程连接页不控制开机自启。开机自启动在设置 → 通用页(开关文案「开机自启动 · 登录系统后自动启动 SparkWork」), 底层走 app:get-startup-settings / app:set-startup-settings(openAtLogin + openAsHidden),平台不支持时会提示「当前系统环境不支持读取登录项」。
9. 一键创建 Bot
每个通道都有 remote:create-bot-draft:创建一个草稿连接(status: draft), 返回 consoleUrl 与 instructions,需要时同时打开控制台。 真实返回内容:
| 通道 | 默认名称 | 控制台地址 | 要点摘录 |
|---|---|---|---|
telegram | Telegram Bot | https://t.me/BotFather | 创建 bot 复制 token → 填回 SparkWork → 生成配对码发给 bot |
feishu | 飞书机器人 | https://open.feishu.cn/page/openclaw?form=multiAgent | 用 openclaw 快捷入口创建自建应用并预选机器人能力,复制 App ID / App Secret |
qq | QQ 机器人 | https://q.qq.com/#/app/bot | 在 QQ 开放平台申请「群聊」与「单聊」消息能力;单聊加好友即可,群聊需 @机器人;保存后命令自动注册为指令面板 |
wechat | 微信机器人 | 二维码授权(无外部控制台) | 扫码确认后由 SparkWork 保存凭据;再生成配对码并在微信中发送 /bind 配对码 |
wechat-claw | 微信 Claw | 远程连接文档页 | 旧版自建网关协议,仅兼容已配置的连接 |
Telegram、飞书与 QQ 的平台侧授权、审核与复制凭据仍然要你手动完成;微信入口会创建本地草稿并直接开始扫码授权,其他平台入口会打开对应控制台。 保存后务必点「测试配置」:它会校验必填字段是否齐全, 缺少字段会把状态置为 error、停用连接,并写入 lastError(形如「缺少字段:appId, appSecret」)。
10. 常见坑与排查
- 需要公网 IP 吗?不需要。Telegram 与微信使用本机出站长轮询,飞书/QQ 使用官方 WebSocket 长连接;本地 HTTP 服务只监听
127.0.0.1,用于本地调试与自建网关。 - 发消息没反应:先看设置页顶部是否「运行中」,再看该连接状态是不是
connected;如果状态是pending-pairing,说明还没完成/bind。QQ 群聊还需要 @机器人(或走指令面板)。 - 「会话已绑定到其他远程聊天」:这是有意的隔离。选择另一个会话、
/new-session新建,或在所有相关连接里都开启「跨连接共享会话」。 - 配对码过期:默认 10 分钟,重新生成即可(旧码立即失效)。
- 图片发不出去 / 收不到:确认连接里开启了「传输文件」能力; 入站图片限 20 MB 且只支持 PNG/JPEG/WebP;出站图片被平台拒绝时会自动降级(Telegram 图片→文件、 飞书上传失败会报错、QQ 大图可用临时 URL 中转)。
- 改了
localWebhookPort但 URL 还是老的: 端口被占用时会退化成随机端口,以运行态回传的localBaseUrl为准;改端口后要重启运行时 (保存配置会触发运行态同步)。 - 想让远程只能发消息、不能切模型/看桌面:在「授权」段关掉对应能力开关即可—— 命令目录里每条命令都绑定能力,关闭后会直接返回拒绝说明。
- 安全提醒:凭据在 SQLite 里是明文;
requirePairing建议保持开启; 桌面控制类能力(controlDesktop、dangerousActions)默认关闭,开启前请确认你信任这些聊天的使用者。 - 微信收不到非文字消息:当前微信 ClawBot 接入只处理文字消息;图片、语音、视频与文件尚未接入。 旧版
wechat-claw通道仍用于已经配置的自建网关连接。
速查表
- 可新建通道
- telegram / feishu / qq / wechat(旧 wechat-claw 网关连接继续兼容)
- 配置存储
- app_settings → remote-connections/data(凭据为明文 JSON)
- 本地服务
- 127.0.0.1:32178(占用则随机端口),仅本机可访问
- 端点
- GET /remote/health · POST /remote/webhook/:channel/:connectionId
- 配对
- 6 位数字码,默认 10 分钟;QR 负载 spark-agent://remote-pair?connectionId&channel&code&expiresAt
- 命令前缀
- 默认 /(commandPrefix 可改)
- 默认同步命令
- start help status projects sessions new-session channels models agents reasoning permissions progress queue cancel
- 图片限制
- 20 MB,仅 PNG / JPEG / WebP,需要开启「传输文件」能力
- 桌面控制
- observeDesktop 默认开;controlDesktop / useInternalBrowser / transferFiles / dangerousActions 默认关
- 运行态接口
- remote:runtime-status(polling[] 与 longConnections[])
- 变化推送
- stream:remote:changed
常见问题
展开 6 条常见问题
- 需要公网 IP 或反向代理吗?
不需要。Telegram 与微信用本机出站长轮询,飞书与 QQ 用官方 WebSocket 长连接;本地 HTTP 服务只监听 127.0.0.1(默认 32178,端口占用时退化为随机端口),供本地调试与自建网关使用。
- 配对码怎么生成、多久过期?
在设置页点「生成配对码」或「生成二维码配对」,服务端生成 6 位数字码,有效期由 global.pairingTtlMinutes 决定(默认 10 分钟)。在外部聊天发送 /bind 加配对码完成配对,也可以用手动确认入口。
- 一个 Telegram 机器人能同时给多个人用吗?
可以,但每个聊天是独立路由(connectionId + externalId),各自绑定自己的会话、项目、模型与选择状态。只有所有相关连接都开启「跨连接共享会话」时才会共用一个会话。
- 远程会话默认什么权限?
优先用连接或聊天级的 defaultPermissionMode;未配置时按适配器取自动审批档:Claude claude-auto、Codex codex-auto-review、Spark spark-auto。完全访问档需要显式配置,且高危操作仍受权限层保护。
- 凭据存在哪里?安全吗?
远程连接的 botToken / AppSecret 以明文 JSON 存在 SQLite 的 app_settings(remote-connections/data)里,不在系统 Keychain 或加密 vault 中,设置页用的是普通文本输入框,已保存的值会明文显示。请确保只有你信任的人能访问这台机器的用户数据目录。
- 微信机器人怎么连接?
在设置 → 远程连接创建微信机器人并扫码授权;SparkWork 使用 iLink 长轮询收发文字消息,不需要 OpenClaw 运行时或公网地址。授权后保存并启用连接,再生成配对码并在微信 ClawBot 对话中发送 /bind 配对码。