@vrv-platform/vrv
v0.1.1
Published
OpenClaw VRV channel: HTTP entry for external messages, reply via Channel outbound (pushApi)
Readme
vrv-channel 插件
最小化 OpenClaw 渠道插件:通过官方 API 注册 HTTP 入口接收外部消息,OpenClaw 回复后通过 Channel 插件的 outbound 向外推送到业主侧的 HTTP 接口(pushApi)。
快速上手
部署和更新插件: 1.配置回调地址
openclaw config set channels.vrv '{
"enabled": true,
"pushApi": "http://39.99.149.198:8100/ai-app/api/v1/clawdbot/callback?clientId=vrvclowd",
}' --json- 在openclaw的插件目录创建文件夹vrv
- 把源码上传到此文件夹
- 安装此本地插件,重启网关
openclaw plugins install -l /root/openclaw-assistant/openclaw/extensions/vrv
openclaw gateway restart源码安装的服务器:
cd /home/openclaw
pnpm openclaw plugins install -l /home/openclaw/extensions/vrv
pnpm openclaw gateway restart- 日志和验证
滚动查看最新日志
openclaw logs --followcurl测试
curl -X POST "http://121.43.129.161:18789/vrv/message" -H "Content-Type: application/json" -d '{"text":"今天天气怎样"}'
curl -X POST "http://192.168.203.201:18789/vrv/message" -H "Content-Type: application/json" -d '{"text":"今天天气怎样"}'
curl -X POST "http://114.55.81.36:18789/vrv/message" -H "Content-Type: application/json" -d '{"text":"今天天气怎样"}'插件如何工作(分层视角)
1. OpenClaw 边界层(index.ts)
- 插件入口:
index.ts只做两件事:api.registerChannel({ plugin: vrvPlugin }):注册 vrv 渠道(出站能力)。api.registerHttpHandler(handleVrvHttpRequest):注册 HTTP handler(入站能力)。
- 这里完全只依赖
openclaw/plugin-sdk,不关心你业主系统的具体逻辑。
2. HTTP 入站层(monitor.ts)
- HTTP 入口:使用
api.registerHttpHandler(handleVrvHttpRequest)注册函数(req, res) => Promise<boolean>。 - 路径:
POST /vrv/message:- 非
/vrv/message的请求返回false,交给其他插件处理; - 非
POST的请求返回405。
- 非
- 请求体:JSON,例如
{ "text": "用户消息内容", "to": "可选目标ID", "sessionKey": "可选会话键" }
其中:text:必填,用户发给 Agent 的自然语言内容;to:可选,业主侧定义的“用户或会话 ID”(默认"default");sessionKey:可选,会话键,用于区分多轮对话(默认等于to)。
- OpenClaw 调用流程:
- 从
runtime.config解析出 vrv 账户配置(主要是pushApi)。 - 用
runtime.channel.routing.resolveAgentRoute解析出要走哪个 Agent + Session:channel: "vrv"peer: { kind: "dm", id: to }
- 用
runtime.channel.reply.formatAgentEnvelope/finalizeInboundContext组装上下文ctx:- 包含
From/To/SessionKey/Provider/Surface/OriginatingChannel等字段。
- 包含
- 调用
runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({ ctx, cfg, dispatcherOptions }):- 由 OpenClaw 调度 Agent 执行;
- 通过
dispatcherOptions.deliver把 Agent 产出的回复交给下一层。
- 从
3. 业主侧出站层(channel.ts / sendVrvReply)
Channel outbound:
vrvPlugin.outbound.sendText只是一个“适配器”,它会:- 根据
ctx.accountId/ctx.cfg调用resolveVrvAccount取出pushApi; - 调用
sendVrvReply({ cfg, to, text, sessionKey, accountId })。
- 根据
业主逻辑的核心只有一处:
sendVrvReply:- 从账户配置中拿到
pushApi; - 用
fetch(pushApi, { method: "POST", body: JSON.stringify({ reply, to, sessionKey }) })推送到你的 HTTP 服务; - 日志统一走
console.log/console.error。
- 从账户配置中拿到
pushApi 请求体:POST 的 JSON 形如
{ "reply": "Agent 回复内容", "to": "目标ID", "sessionKey": "会话键" }。
总结:第 1、2 层完全是 OpenClaw 的集成代码;第 3 层只关心“给哪个 URL 发什么 JSON”,把业务逻辑压缩在
sendVrvReply一处,便于今后替换为 MQ、内部网关等实现。
配置
在 OpenClaw 配置中增加渠道 vrv,并设置回复推送地址,例如:
channels:
vrv:
enabled: true
pushApi: "https://your-server.com/webhook/reply"多账户时可在 channels.vrv.accounts.<accountId>.pushApi 下为不同 account 配置不同 pushApi。
环境变量回退:若主配置中未设置 channels.vrv.pushApi,插件会读取环境变量 VRV_PUSH_API 或 OPENCLAW_VRV_PUSH_API 作为 pushApi(便于未改主配置时快速测试)。例如:
export VRV_PUSH_API="https://webhook.site/197b1f39-b58a-4db5-88d9-8e55ab3a1677"
# 再启动 OpenClaw通过 curl 测试
前置:OpenClaw 网关已启动,且配置了 channels.vrv.pushApi(用于接收 Agent 回复的 webhook 地址)。将下面的 GATEWAY_URL 换成你的网关地址(如 http://localhost:3000)。
1. 测试入站:向 OpenClaw 发送一条消息
# 最简:只发文本
curl -X POST "${GATEWAY_URL}/vrv/message" \
-H "Content-Type: application/json" \
-d '{"text":"你好"}'
# 带目标与会话(可选)
curl -X POST "http://121.43.129.161:18789/vrv/message" \
-H "Content-Type: application/json" \
-d '{"text":"今天天气怎样","to":"user-001","sessionKey":"session-001"}'成功时返回 200 且 body 为 {"ok":true}。若返回 4xx,body 中会有 error 说明(如缺少 text、未配置 pushApi 等)。
2. 测试出站:确认回复被推到 pushApi
需要有一个能接收 POST 的 URL 作为 pushApi,才能看到 Agent 的回复被推过去。
方式 A:用 webhook.site 收回复(无需自建服务)
- 打开 https://webhook.site,复制给你的唯一 URL(如
https://webhook.site/xxx-xxx-xxx)。 - 在 OpenClaw 配置里设置:
channels: vrv: enabled: true pushApi: "https://webhook.site/你的唯一ID" - 重启/重载 OpenClaw 后,用上面的 curl 发一条消息。
- 在 webhook.site 页面应看到一条 POST 请求,body 为 JSON:
{"reply":"...","to":"...","sessionKey":"..."},其中reply为 Agent 的回复内容。
方式 B:用本地 nc 收回复(看原始请求)
# 终端 1:在 9999 端口监听一次 HTTP(只收一条就退出)
nc -l 9999
# 配置里 pushApi 填 http://你的本机IP:9999(网关能访问到的地址)
# 若网关也在本机,可填 http://127.0.0.1:9999然后另开终端用 curl 向 /vrv/message 发消息;终端 1 会收到一次 POST 请求(含 HTTP 头和 JSON body)。
方式 C:用 Python 起一个最小 HTTP 服务收回复
# 终端 1:监听 9999,打印收到的 POST body
python3 -c "
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
class H(BaseHTTPRequestHandler):
def do_POST(self):
n = int(self.headers.get('Content-Length', 0))
body = self.rfile.read(n)
self.send_response(200)
self.end_headers()
self.wfile.write(b'{}')
print('Received POST:', json.loads(body))
HTTPServer(('0.0.0.0', 9999), H).serve_forever()
"配置 pushApi: "http://127.0.0.1:9999"(或网关能访问的地址),再用 curl 发消息,终端会打印收到的 {"reply":..., "to":..., "sessionKey":...}。
3. 常见错误对应的 curl 响应
| 现象 | 可能原因 |
|------|----------|
| 405 Method Not Allowed | 未用 POST 或路径不是 /vrv/message |
| 400 + "error":"missing or empty text" | body 里没有 text 或为空 |
| 400 + "error":"invalid payload" | body 不是合法 JSON 或不是对象 |
| 503 + "error":"vrv pushApi not configured" | 未配置 channels.vrv.pushApi |
| 503 + "error":"gateway routing or reply dispatcher not available" | 网关/Agent 未就绪或未正确加载插件 |
| 503 + "error":"vrv pushApi not configured" | 未配置 pushApi:见下方「解决 pushApi 未配置」 |
解决 "vrv pushApi not configured"
在 OpenClaw 的配置文件(如 config.yaml 或环境/控制台里使用的配置)中增加 vrv 渠道并填写 pushApi,然后重启或重载 OpenClaw:
channels:
vrv:
enabled: true
pushApi: "https://webhook.site/你的唯一ID" # 或你的接收回复的 URL- 若用 webhook.site:打开 https://webhook.site,复制给你的唯一 URL,填到
pushApi。 - 若用本机服务:填
http://你的服务器IP:端口(网关能访问到的地址,例如http://121.43.129.161:9999)。
保存配置并重启 OpenClaw 后,再用 curl 发消息即可。
目录结构
vrv/
├── openclaw.plugin.json # 插件元信息与 configSchema
├── index.ts # 入口:register 中 registerChannel + registerHttpHandler
├── README.md # 本说明
└── src/
├── runtime.ts # 保存 api.runtime,供 handler 与 channel 使用
├── accounts.ts # vrv 账户解析(list/resolve/default)
├── channel.ts # Channel 插件定义与 outbound(sendText → POST pushApi)
└── monitor.ts # HTTP handler:解析 /vrv/message,构造 ctx,调用 dispatchReplyWithBufferedBlockDispatcher与官方 API 的对应关系
- 注册 HTTP 入口:使用官方
api.registerHttpHandler(handleVrvHttpRequest),在 handler 内自行判断 path 与 method,符合则处理并返回true,否则返回false交给其他插件或网关。 - 接收外部消息:在 handler 中把请求体转成 OpenClaw 入站上下文,并调用
core.channel.reply.dispatchReplyWithBufferedBlockDispatcher,即按官方推荐方式把“外部消息”交给 OpenClaw 处理。 - 回复通过 Channel 发出:通过同一插件的 Channel 定义中的
outbound.sendText,以及在dispatcherOptions.deliver中调用相同的推送逻辑(POST 到 pushApi),实现“OpenClaw 回复后通过 Channel 插件向外发送或调用外部 API”。
参考
- OpenClaw 文档:https://docs.openclaw.ai
- 参考实现:本仓库中
pluginCodeForReference/googlechat(HTTP handler + Channel)、pluginCodeForReference/clawdbot-feishu(Channel + outbound)
