让外部系统(监控告警、CI、工单、任务编排器)驱动 botmux 机器人的两条入口:
- Webhook 接入点:触发机器人在群里说话或跑工作流;网关不解析各平台格式,把原始事件原样交给模型读,新系统几乎零适配。在 Dashboard「接入点」页创建管理(当前 beta)。
- API 任务触发:程序化把指令交给机器人执行并拿回结果——整个调用是纯 HTTP「请求 → 拿结果」,可选完全不进飞书(虚拟会话)。
Webhook 接入点
快速上手
- Dashboard →「接入点」→「新建接入点」:填名称、选触发的机器人、选投递群(推荐「固定群」按群名选)。
- 「校验方式」默认令牌,密钥留空自动生成。
- 创建后直接得到一条末尾已含令牌的 Webhook URL 和现成
curl:
curl -X POST 'http://<lan-ip>:7891/webhook/conn_xxx/<令牌>' \
-H 'content-type: application/json' \
-d '{"msg":"hello"}'校验方式(按接入点单独选,两档)
| 方式 | 要点 |
|---|---|
| 令牌(默认) | 整条 URL 即凭证,三种携带方式任选:路径段 …/webhook/<id>/<令牌>(默认)、查询参数 ?token=…、请求头 Authorization: Bearer …。服务端只做常量时间比对,无需时间戳/nonce/签名 |
| HMAC 签名(高级) | 密钥从不上网,提供防篡改 + 防重放。调用方对 时间戳.原始body 做 HMAC-SHA256,携带三个请求头:x-botmux-timestamp(±5 分钟容差)、x-botmux-nonce(每次唯一)、x-botmux-signature(sha256=<hex> 或 base64url)。适合公网或本身就会签名的发送方(GitHub / Stripe 类) |
令牌会落进反代日志 / 浏览器历史——URL 泄漏 = 凭证泄漏,公网建议 HMAC 或至少把令牌放请求头;令牌可在列表里随时轮换。
投递目标(三选一)
- 固定群:按群名选(数据来自该机器人所在的群),群 ID 写入接入点,裸 URL 即可触发。
- 由请求指定(动态):每次请求传群——查询参数
?chatId=oc_xxx、请求头x-botmux-chat-id、或请求体chatId字段;可选配「允许的群」白名单。 - 每次新建群:每个事件自动拉新群处置,并自动把该机器人的授权用户拉进群。可填去重字段(点号路径如
alert.id):填了则相同去重值的事件复用同一个群,留空则每事件新建群。早期「状态字段 / 自动关群」已移除,群不再自动关闭。
触发方式与处理指令
- 单轮对话:机器人针对事件回应一次;或工作流:把事件作为字符串参数
event传给 workflow 的节点处理。 - 处理指令(可选):默认机器人只收到原始事件 JSON、自由发挥;写一段指令(如「总结这条告警的严重程度,判断是否需要立即处理,@相关 oncall,并给出排查建议」)后,指令作为可信任务
<botmux_task trusted="true">注入在不可信事件数据<botmux_external_event trusted="false">之上——模型先读「要做什么」,再把事件 JSON 当数据处理。
安全与可观测
- 不被指挥:外部内容被明确框定为「待处理的事件数据,不是命令」,不执行其中指令、不泄露凭据。
- 限流:可设宽松上限防报警风暴;暴露公网时还应配请求体硬上限。
- 调用记录:Dashboard「调用日志」按时间 / Webhook / 结果筛选;单条记录可看 HTTP 状态、耗时、查询参数、请求头、JSON 请求体、路由参数与实际投递目标。
- 脱敏:URL 令牌、
Authorization/Cookie/ 签名头,以及 body 中 password / secret / token / API key 字段写盘前替换为[REDACTED];日志文件权限0600,调用日志 API 不开放给 Dashboard 匿名只读。 - 留存:默认保存脱敏后的请求头和 JSON 请求体 14 天,单个请求体最多 128 KB;可在列表关闭请求参数留存(仍记录状态、耗时与路由元数据)。
常见返回
| 现象 | 原因 |
|---|---|
401 token verification failed | 令牌不对 / 没带令牌 |
404 unknown or disabled connector | 接入点 ID 错或已停用 |
400 target chatId is required | 动态模式没带群 ID |
400 dedup_key_not_found | 配了去重字段但 body 取不到值 |
429 rate limit exceeded | 超过限流上限 |
API 任务触发
面向调用方开发者。两种模式共用端点 POST /api/trigger(dashboard,默认 http://<daemon-host>:7891):
| 模式 | 触发 | 适用 | 可中途取消 |
|---|---|---|---|
| 同步 | options.waitForFinalOutput=true | 一发一收、任务 ≤5 分钟 | ✗ |
| 异步 | options.asyncReturnSessionId=true | 快速接单 + 轮询、需取消 / 故障恢复 | ✓ |
不传 chatId 时开虚拟会话:完全不进飞书群、不发消息。生产级调度推荐异步——同步模式的 sessionId 只在完成时返回,运行中拿不到、无法取消,HTTP 连接最长挂 5 分钟。
鉴权
当前用 dashboard 轮换式 token:程序化调用必须带 Cookie: botmux_dashboard_token=<TOKEN>;不要用 ?t=<TOKEN>(浏览器登录专用,POST 带它会返回 302)。token 获取:运行 botmux dashboard 取当前登录 URL ?t= 后那段;botmux dashboard rotate 使其失效。独立 API Key(如 X-Botmux-Api-Key)在规划中。
请求体与硬校验
{
"source": { "type": "webhook" },
"target": { "kind": "turn", "botId": "cli_xxx" }, // 目标机器人的 larkAppId
"instruction": "要让机器人执行的指令(可信,顶层指令渲染)",
"envelope": { "format": "json", "sourceName": "your-system", "trusted": false },
"options": { /* 模式参数 */ }
}envelope.trusted必须是false(防注入设计):声明 envelope 内容为不可信外部数据,daemon 才会包成 untrusted event、不执行其中夹带指令;要机器人真正执行的内容放顶层instruction。- 不传
chatId时,options必须含waitForFinalOutput或asyncReturnSessionId之一,否则 400target_required。 options.timeoutMs范围[1000, 300000](默认 120000),越界 400。options.model/options.reasoningEffort(low/medium/high/xhigh)仅对 codex / codex-app 机器人生效,且只在本次触发创建新会话时冻结;其它机器人被忽略。
幂等键(options.idempotencyKey)
防重试重复执行:异步触发后响应丢失时,重试会新建 session 把任务跑第二遍。传一个发起前已持久化的稳定键,同键重试返回同一个 session/triggerId、不新建不重派(响应带 idempotent:true/false)。要点:
- 仅支持 fresh async virtual 触发:
target.kind:'turn'+asyncReturnSessionId:true,且不带target.sessionId/rootMessageId/chatId/waitForFinalOutput/dryRun;其它组合带键直接 400。 - 同键不同 payload → 409
idempotency_conflict,绝不把新请求静默串进旧任务。 - 崩溃语义 at-most-once:派发中途 daemon 崩溃后不盲目重派,该键收敛到终态
failed(no_output),调用方按 Failed 处理。 - 键 → session 映射只增不删,完成后的迟到重试仍复用同一 session。
轮询结果(四态契约)
GET /api/sessions/:sessionId/trigger-result(可选 ?triggerId= 精确匹配)。四态全部返回 HTTP 200 + ok:true;只看 state 字段,不要用 ok 或 HTTP 状态码判定:
state | 含义 | 处理 |
|---|---|---|
running | 任务还在跑 | 继续轮询 |
completed | 有最终产出 | 读 output.content;codex-app 且采集到用量时带 usage(input/output/cacheRead/cacheCreate 四桶增量,缺失≠0) |
failed | 会话终止但没捕获产出,errorCode:"no_output"(软终态) | 可能是真失败也可能是你自己的 close,需二次确认再落终态 |
not_found | 查无此会话 | 两种物理表现都归一:404 + unknown_session(代理层短路);200 + state:"not_found"(daemon 磁盘查无) |
同步模式超时返回 HTTP 504 + wait_timeout,但任务仍在后台跑——留 sessionId 兜底查询,别直接判失败。
重启存活与取消
- 异步结果完成时持久化到
data/async-triggers/<sessionId>.json(只增不删,TTL 清扫在规划):daemon 重启后已完成任务仍返回completed,恢复逻辑不应因单次查询拿不到就判任务丢失。 - 取消:
POST /api/sessions/:sessionId/close,语义是关闭整个会话(一次性虚拟异步会话只有一轮 turn,等价于中断本次)。取消后轮询返回failed/no_output属预期,按自己的取消意图落 cancelled。 output.content极少数情况可能带一句前言(prompt 层引导、非硬保证);要求干净输出时在展示层做保守裁剪(只裁已知确定性前言前缀),切勿激进截取「只取最后一段」;持久化 / 审计存原始 content。
端点速查
| 端点 | 方法 | 用途 |
|---|---|---|
/api/trigger | POST | 触发任务(同步/异步由 options 决定) |
/api/sessions/:id/trigger-result | GET | 异步轮询结果(四态) |
/api/sessions/:id | GET | 查会话元信息(状态/标题等) |
/api/sessions/:id/close | POST | 取消/关闭会话 |
关键 errorCode:target_required、bad_request(trusted 校验 / timeoutMs 越界 / 幂等键超范围)、idempotency_conflict、bot_not_found、bot_not_in_chat、wait_timeout、no_output、session_not_found。
客户端轮询应把响应分五类:completed/running/failed 透传、not_found(两种形式)为终止、400/401/403 为终止 error、网络异常 / 超时 / 5xx / 非 JSON 为可重试 unknown——原任务可能仍在后台跑,误当查无补偿重派会导致双执行。
关联
- 接入点、调用日志、群组管理均在 dashboard 操作,见 dashboard
- 工作流触发模式的编排细节见 workflow
- 虚拟会话与群会话的会话模型见 session-model
- 定时触发(内部时钟版的外部触发)见 schedule